From d1a6209d0b687b485da7ff23b758b5e49cea3501 Mon Sep 17 00:00:00 2001
From: Vladyslav Fenchak
Date: Mon, 28 Feb 2022 14:30:13 +0100
Subject: [PATCH 01/44] ISSUE #149
---
.../minos-graphql-aiohttp/.editorconfig | 21 +
.../plugins/minos-graphql-aiohttp/AUTHORS.md | 15 +
.../plugins/minos-graphql-aiohttp/HISTORY.md | 5 +
.../plugins/minos-graphql-aiohttp/LICENSE | 21 +
.../plugins/minos-graphql-aiohttp/Makefile | 42 +
.../plugins/minos-graphql-aiohttp/README.md | 36 +
.../minos-graphql-aiohttp/RUNTHETESTS.md | 21 +
.../plugins/minos-graphql-aiohttp/SETUP.md | 11 +
.../minos-graphql-aiohttp/docs/Makefile | 20 +
.../minos-graphql-aiohttp/docs/authors.rst | 1 +
.../minos-graphql-aiohttp/docs/conf.py | 197 ++
.../minos-graphql-aiohttp/docs/history.rst | 1 +
.../minos-graphql-aiohttp/docs/index.rst | 18 +
.../minos-graphql-aiohttp/docs/make.bat | 36 +
.../minos-graphql-aiohttp/docs/readme.rst | 4 +
.../docs/runthetests.rst | 1 +
.../minos-graphql-aiohttp/docs/usage.rst | 7 +
.../minos/plugins/graphql_aiohttp/__init__.py | 19 +
.../plugins/graphql_aiohttp/exceptions.py | 7 +
.../graphql_aiohttp/graphql_example.py | 17 +
.../minos/plugins/graphql_aiohttp/handlers.py | 169 ++
.../minos/plugins/graphql_aiohttp/requests.py | 234 +++
.../plugins/graphql_aiohttp/responses.py | 117 ++
.../minos/plugins/graphql_aiohttp/services.py | 44 +
.../plugins/minos-graphql-aiohttp/poetry.lock | 1811 +++++++++++++++++
.../plugins/minos-graphql-aiohttp/poetry.toml | 2 +
.../minos-graphql-aiohttp/pyproject.toml | 56 +
.../plugins/minos-graphql-aiohttp/setup.cfg | 28 +
.../minos-graphql-aiohttp/tests/__init__.py | 0
.../tests/services/commands.py | 36 +
.../tests/services/queries.py | 19 +
.../tests/test_config.yml | 39 +
.../test_rest_aiohttp/test_handlers.py | 131 ++
.../test_rest_aiohttp/test_requests.py | 441 ++++
.../test_rest_aiohttp/test_services.py | 48 +
.../test_plugins/test_rest_aiohttp/utils.py | 100 +
.../minos-graphql-aiohttp/tests/utils.py | 27 +
37 files changed, 3802 insertions(+)
create mode 100644 packages/plugins/minos-graphql-aiohttp/.editorconfig
create mode 100644 packages/plugins/minos-graphql-aiohttp/AUTHORS.md
create mode 100644 packages/plugins/minos-graphql-aiohttp/HISTORY.md
create mode 100644 packages/plugins/minos-graphql-aiohttp/LICENSE
create mode 100644 packages/plugins/minos-graphql-aiohttp/Makefile
create mode 100644 packages/plugins/minos-graphql-aiohttp/README.md
create mode 100644 packages/plugins/minos-graphql-aiohttp/RUNTHETESTS.md
create mode 100644 packages/plugins/minos-graphql-aiohttp/SETUP.md
create mode 100644 packages/plugins/minos-graphql-aiohttp/docs/Makefile
create mode 100644 packages/plugins/minos-graphql-aiohttp/docs/authors.rst
create mode 100755 packages/plugins/minos-graphql-aiohttp/docs/conf.py
create mode 100644 packages/plugins/minos-graphql-aiohttp/docs/history.rst
create mode 100644 packages/plugins/minos-graphql-aiohttp/docs/index.rst
create mode 100644 packages/plugins/minos-graphql-aiohttp/docs/make.bat
create mode 100644 packages/plugins/minos-graphql-aiohttp/docs/readme.rst
create mode 100644 packages/plugins/minos-graphql-aiohttp/docs/runthetests.rst
create mode 100644 packages/plugins/minos-graphql-aiohttp/docs/usage.rst
create mode 100644 packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/__init__.py
create mode 100644 packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/exceptions.py
create mode 100644 packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/graphql_example.py
create mode 100644 packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/handlers.py
create mode 100644 packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/requests.py
create mode 100644 packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/responses.py
create mode 100644 packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/services.py
create mode 100644 packages/plugins/minos-graphql-aiohttp/poetry.lock
create mode 100644 packages/plugins/minos-graphql-aiohttp/poetry.toml
create mode 100644 packages/plugins/minos-graphql-aiohttp/pyproject.toml
create mode 100644 packages/plugins/minos-graphql-aiohttp/setup.cfg
create mode 100644 packages/plugins/minos-graphql-aiohttp/tests/__init__.py
create mode 100644 packages/plugins/minos-graphql-aiohttp/tests/services/commands.py
create mode 100644 packages/plugins/minos-graphql-aiohttp/tests/services/queries.py
create mode 100644 packages/plugins/minos-graphql-aiohttp/tests/test_config.yml
create mode 100644 packages/plugins/minos-graphql-aiohttp/tests/test_plugins/test_rest_aiohttp/test_handlers.py
create mode 100644 packages/plugins/minos-graphql-aiohttp/tests/test_plugins/test_rest_aiohttp/test_requests.py
create mode 100644 packages/plugins/minos-graphql-aiohttp/tests/test_plugins/test_rest_aiohttp/test_services.py
create mode 100644 packages/plugins/minos-graphql-aiohttp/tests/test_plugins/test_rest_aiohttp/utils.py
create mode 100644 packages/plugins/minos-graphql-aiohttp/tests/utils.py
diff --git a/packages/plugins/minos-graphql-aiohttp/.editorconfig b/packages/plugins/minos-graphql-aiohttp/.editorconfig
new file mode 100644
index 000000000..d4a2c4405
--- /dev/null
+++ b/packages/plugins/minos-graphql-aiohttp/.editorconfig
@@ -0,0 +1,21 @@
+# http://editorconfig.org
+
+root = true
+
+[*]
+indent_style = space
+indent_size = 4
+trim_trailing_whitespace = true
+insert_final_newline = true
+charset = utf-8
+end_of_line = lf
+
+[*.bat]
+indent_style = tab
+end_of_line = crlf
+
+[LICENSE]
+insert_final_newline = false
+
+[Makefile]
+indent_style = tab
diff --git a/packages/plugins/minos-graphql-aiohttp/AUTHORS.md b/packages/plugins/minos-graphql-aiohttp/AUTHORS.md
new file mode 100644
index 000000000..30ff94991
--- /dev/null
+++ b/packages/plugins/minos-graphql-aiohttp/AUTHORS.md
@@ -0,0 +1,15 @@
+# Credits
+
+## Development Lead
+
+* Andrea Mucci
+
+## Core Devs
+
+* Sergio Garcia Prado
+* Vladyslav Fenchak
+* Alberto Amigo Alonso
+
+## Contributors
+
+None yet. Why not be the first?
diff --git a/packages/plugins/minos-graphql-aiohttp/HISTORY.md b/packages/plugins/minos-graphql-aiohttp/HISTORY.md
new file mode 100644
index 000000000..1cab554e5
--- /dev/null
+++ b/packages/plugins/minos-graphql-aiohttp/HISTORY.md
@@ -0,0 +1,5 @@
+# History
+
+## 0.0.1 (2022-02-17)
+
+* First release on PyPI.
diff --git a/packages/plugins/minos-graphql-aiohttp/LICENSE b/packages/plugins/minos-graphql-aiohttp/LICENSE
new file mode 100644
index 000000000..4daf85bf2
--- /dev/null
+++ b/packages/plugins/minos-graphql-aiohttp/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2021 Clariteia
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/packages/plugins/minos-graphql-aiohttp/Makefile b/packages/plugins/minos-graphql-aiohttp/Makefile
new file mode 100644
index 000000000..854bc90bc
--- /dev/null
+++ b/packages/plugins/minos-graphql-aiohttp/Makefile
@@ -0,0 +1,42 @@
+.PHONY: docs
+
+lint:
+ poetry run flake8
+
+test:
+ poetry run pytest
+
+coverage:
+ poetry run coverage run -m pytest
+ poetry run coverage report -m
+ poetry run coverage xml
+
+reformat:
+ poetry run black --line-length 120 minos tests
+ poetry run isort minos tests
+
+docs:
+ rm -rf docs/api
+ poetry run $(MAKE) --directory=docs html
+
+release:
+ $(MAKE) dist
+ poetry publish
+
+dist:
+ poetry build
+ ls -l dist
+
+install:
+ poetry install
+
+update:
+ poetry update
+
+check:
+ $(MAKE) install
+ $(MAKE) reformat
+ $(MAKE) lint
+ $(MAKE) test
+ $(MAKE) docs
+ $(MAKE) dist
diff --git a/packages/plugins/minos-graphql-aiohttp/README.md b/packages/plugins/minos-graphql-aiohttp/README.md
new file mode 100644
index 000000000..3be59ccff
--- /dev/null
+++ b/packages/plugins/minos-graphql-aiohttp/README.md
@@ -0,0 +1,36 @@
+
+
+
+
+## minos-graphql-aiohttp
+
+[](https://pypi.org/project/minos-broker-kafka/)
+[](https://minos-framework.github.io/minos-python)
+[](https://github.com/minos-framework/minos-python/blob/main/LICENSE)
+[](https://codecov.io/gh/minos-framework/minos-python)
+[](https://stackoverflow.com/questions/tagged/minos)
+
+## Summary
+
+Minos is a framework which helps you create [reactive](https://www.reactivemanifesto.org/) microservices in Python.
+Internally, it leverages Event Sourcing, CQRS and a message driven architecture to fulfil the commitments of an
+asynchronous environment.
+
+## Documentation
+
+The official API Reference is publicly available at the [GitHub Pages](https://minos-framework.github.io/minos-python).
+
+## Source Code
+
+The source code of this project is hosted at the [GitHub Repository](https://github.com/minos-framework/minos-python).
+
+## Getting Help
+
+For usage questions, the best place to go to is [StackOverflow](https://stackoverflow.com/questions/tagged/minos).
+
+## Discussion and Development
+Most development discussions take place over the [GitHub Issues](https://github.com/minos-framework/minos-python/issues). In addition, a [Gitter channel](https://gitter.im/minos-framework/community) is available for development-related questions.
+
+## License
+
+This project is distributed under the [MIT](https://raw.githubusercontent.com/minos-framework/minos-python/main/LICENSE) license.
diff --git a/packages/plugins/minos-graphql-aiohttp/RUNTHETESTS.md b/packages/plugins/minos-graphql-aiohttp/RUNTHETESTS.md
new file mode 100644
index 000000000..8b5e95b1f
--- /dev/null
+++ b/packages/plugins/minos-graphql-aiohttp/RUNTHETESTS.md
@@ -0,0 +1,21 @@
+Run the tests
+==============
+
+In order to run the tests, please make sure you have the `Docker Engine `_
+and `Docker Compose `_ installed.
+
+Move into tests/ directory
+
+`cd tests/`
+
+Run service dependencies:
+
+`docker-compose up -d`
+
+Install library dependencies:
+
+`make install`
+
+Run tests:
+
+`make test`
diff --git a/packages/plugins/minos-graphql-aiohttp/SETUP.md b/packages/plugins/minos-graphql-aiohttp/SETUP.md
new file mode 100644
index 000000000..8203965c7
--- /dev/null
+++ b/packages/plugins/minos-graphql-aiohttp/SETUP.md
@@ -0,0 +1,11 @@
+Set up a development environment
+=================================
+
+Since we use `poetry` as the default package manager, it must be installed. Please refer to
+`https://python-poetry.org/docs/#installation`.
+
+Run `poetry install` to get the dependencies.
+
+Run `pre-commit install` to set the git checks before commiting.
+
+Make yourself sure you are able to run the tests. Refer to the appropriate section in this guide.
diff --git a/packages/plugins/minos-graphql-aiohttp/docs/Makefile b/packages/plugins/minos-graphql-aiohttp/docs/Makefile
new file mode 100644
index 000000000..86af5d733
--- /dev/null
+++ b/packages/plugins/minos-graphql-aiohttp/docs/Makefile
@@ -0,0 +1,20 @@
+# Minimal makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS =
+SPHINXBUILD = python -msphinx
+SPHINXPROJ = minos_rest_aiohttp
+SOURCEDIR = .
+BUILDDIR = _build
+
+# Put it first so that "make" without argument is like "make help".
+help:
+ @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
+
+.PHONY: help Makefile
+
+# Catch-all target: route all unknown targets to Sphinx using the new
+# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
+%: Makefile
+ @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
diff --git a/packages/plugins/minos-graphql-aiohttp/docs/authors.rst b/packages/plugins/minos-graphql-aiohttp/docs/authors.rst
new file mode 100644
index 000000000..cf16fc494
--- /dev/null
+++ b/packages/plugins/minos-graphql-aiohttp/docs/authors.rst
@@ -0,0 +1 @@
+.. mdinclude:: ../AUTHORS.md
diff --git a/packages/plugins/minos-graphql-aiohttp/docs/conf.py b/packages/plugins/minos-graphql-aiohttp/docs/conf.py
new file mode 100755
index 000000000..b83dc06a1
--- /dev/null
+++ b/packages/plugins/minos-graphql-aiohttp/docs/conf.py
@@ -0,0 +1,197 @@
+#!/usr/bin/env python
+#
+# minos documentation build configuration file, created by
+# sphinx-quickstart on Fri Jun 9 13:47:02 2017.
+#
+# This file is execfile()d with the current directory set to its
+# containing dir.
+#
+# Note that not all possible configuration values are present in this
+# autogenerated file.
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+# If extensions (or modules to document with autodoc) are in another
+# directory, add these directories to sys.path here. If the directory is
+# relative to the documentation root, use os.path.abspath to make it
+# absolute, like shown here.
+#
+import os
+import sys
+
+sys.path.insert(0, os.path.abspath(".."))
+
+import sphinx_rtd_theme
+
+from minos.plugins import (
+ rest_aiohttp,
+)
+
+# -- General configuration ---------------------------------------------
+
+# If your documentation needs a minimal Sphinx version, state it here.
+#
+# needs_sphinx = '1.0'
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
+
+
+extensions = [
+ "sphinxcontrib.apidoc",
+ "sphinx.ext.autodoc",
+ "sphinx_autodoc_typehints",
+ "sphinx.ext.viewcode",
+ "sphinx_rtd_theme",
+ "m2r2",
+]
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ["_templates"]
+
+# The suffix(es) of source filenames.
+# You can specify multiple suffix as a list of string:
+#
+source_suffix = [".rst", ".md"]
+
+# The master toctree document.
+master_doc = "index"
+
+# General information about the project.
+project = "Minos Rest Aiohttp"
+copyright = "2021, Clariteia"
+author = "Clariteia Devs"
+
+# The version info for the project you're documenting, acts as replacement
+# for |version| and |release|, also used in various other places throughout
+# the built documents.
+#
+# The short X.Y version.
+version = rest_aiohttp.__version__
+# The full version, including alpha/beta/rc tags.
+release = rest_aiohttp.__version__
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#
+# This is also used if you do content translation via gettext catalogs.
+# Usually you set "language" from the command line for these cases.
+language = None
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+# This patterns also effect to html_static_path and html_extra_path
+exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = "sphinx"
+
+# If true, `todo` and `todoList` produce output, else they produce nothing.
+todo_include_todos = False
+
+
+# -- Options for HTML output -------------------------------------------
+
+# The theme to use for HTML and HTML Help pages. See the documentation for
+# a list of builtin themes.
+#
+
+html_theme = "sphinx_rtd_theme"
+
+# Theme options are theme-specific and customize the look and feel of a
+# theme further. For a list of options available for each theme, see the
+# documentation.
+#
+
+# html_theme_options = {
+# "codecov_button": True,
+# "description": "Reactive microservices for an asynchronous world",
+# "github_button": True,
+# "github_user": "Clariteia",
+# "github_repo": "plugins",
+# "github_type": "star",
+# }
+
+html_sidebars = {"**": ["about.html", "navigation.html", "searchbox.html"]}
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ["_static"]
+
+
+# -- Options for HTMLHelp output ---------------------------------------
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = "minosdoc"
+
+
+# -- Options for LaTeX output ------------------------------------------
+
+latex_elements = {
+ # The paper size ('letterpaper' or 'a4paper').
+ #
+ # 'papersize': 'letterpaper',
+ # The font size ('10pt', '11pt' or '12pt').
+ #
+ # 'pointsize': '10pt',
+ # Additional stuff for the LaTeX preamble.
+ #
+ # 'preamble': '',
+ # Latex figure (float) alignment
+ #
+ # 'figure_align': 'htbp',
+}
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title, author, documentclass
+# [howto, manual, or own class]).
+latex_documents = [
+ (master_doc, "minos.tex", "Minos Rest Aiohttp Documentation", "Clariteia Devs", "manual"),
+]
+
+
+# -- Options for manual page output ------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [(master_doc, "minos", "Minos Rest Aiohttp Documentation", [author], 1)]
+
+
+# -- Options for Texinfo output ----------------------------------------
+
+# Grouping the document tree into Texinfo files. List of tuples
+# (source start file, target name, title, author,
+# dir menu entry, description, category)
+texinfo_documents = [
+ (
+ master_doc,
+ "minos",
+ "Minos Rest Aiohttp Documentation",
+ author,
+ "minos",
+ "One line description of project.",
+ "Miscellaneous",
+ ),
+]
+
+# "apidoc" extension
+apidoc_module_dir = "../minos"
+apidoc_output_dir = "api"
+apidoc_separate_modules = True
+autodoc_default_options = {
+ "inherited-members": True,
+ "special-members": "__init__",
+ "undoc-members": True,
+}
+
+apidoc_toc_file = False
+apidoc_module_first = True
+apidoc_extra_args = [
+ "--force",
+ "--implicit-namespaces",
+]
+# "autodoc typehints" extension
+
+set_type_checking_flag = True
+typehints_fully_qualified = True
diff --git a/packages/plugins/minos-graphql-aiohttp/docs/history.rst b/packages/plugins/minos-graphql-aiohttp/docs/history.rst
new file mode 100644
index 000000000..d26e5be83
--- /dev/null
+++ b/packages/plugins/minos-graphql-aiohttp/docs/history.rst
@@ -0,0 +1 @@
+.. mdinclude:: ../HISTORY.md
diff --git a/packages/plugins/minos-graphql-aiohttp/docs/index.rst b/packages/plugins/minos-graphql-aiohttp/docs/index.rst
new file mode 100644
index 000000000..c1b30a0fd
--- /dev/null
+++ b/packages/plugins/minos-graphql-aiohttp/docs/index.rst
@@ -0,0 +1,18 @@
+Welcome to Minos Rest Aiohttp's documentation!
+======================================
+
+.. toctree::
+ :maxdepth: 2
+
+ readme
+ runthetests
+ usage
+ api/minos
+ authors
+ history
+
+Indices and tables
+====================
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
diff --git a/packages/plugins/minos-graphql-aiohttp/docs/make.bat b/packages/plugins/minos-graphql-aiohttp/docs/make.bat
new file mode 100644
index 000000000..916674840
--- /dev/null
+++ b/packages/plugins/minos-graphql-aiohttp/docs/make.bat
@@ -0,0 +1,36 @@
+@ECHO OFF
+
+pushd %~dp0
+
+REM Command file for Sphinx documentation
+
+if "%SPHINXBUILD%" == "" (
+ set SPHINXBUILD=python -msphinx
+)
+set SOURCEDIR=.
+set BUILDDIR=_build
+set SPHINXPROJ=minos_rest_aiohttp
+
+if "%1" == "" goto help
+
+%SPHINXBUILD% >NUL 2>NUL
+if errorlevel 9009 (
+ echo.
+ echo.The Sphinx module was not found. Make sure you have Sphinx installed,
+ echo.then set the SPHINXBUILD environment variable to point to the full
+ echo.path of the 'sphinx-build' executable. Alternatively you may add the
+ echo.Sphinx directory to PATH.
+ echo.
+ echo.If you don't have Sphinx installed, grab it from
+ echo.http://sphinx-doc.org/
+ exit /b 1
+)
+
+%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
+goto end
+
+:help
+%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
+
+:end
+popd
diff --git a/packages/plugins/minos-graphql-aiohttp/docs/readme.rst b/packages/plugins/minos-graphql-aiohttp/docs/readme.rst
new file mode 100644
index 000000000..af09813a4
--- /dev/null
+++ b/packages/plugins/minos-graphql-aiohttp/docs/readme.rst
@@ -0,0 +1,4 @@
+Introduction
+**************
+
+.. mdinclude:: ../README.md
diff --git a/packages/plugins/minos-graphql-aiohttp/docs/runthetests.rst b/packages/plugins/minos-graphql-aiohttp/docs/runthetests.rst
new file mode 100644
index 000000000..309db1b96
--- /dev/null
+++ b/packages/plugins/minos-graphql-aiohttp/docs/runthetests.rst
@@ -0,0 +1 @@
+.. mdinclude:: ../RUNTHETESTS.md
diff --git a/packages/plugins/minos-graphql-aiohttp/docs/usage.rst b/packages/plugins/minos-graphql-aiohttp/docs/usage.rst
new file mode 100644
index 000000000..1f662b1ca
--- /dev/null
+++ b/packages/plugins/minos-graphql-aiohttp/docs/usage.rst
@@ -0,0 +1,7 @@
+=====
+Usage
+=====
+
+To use Minos Rest Aiohttp in a project::
+
+ import minos_rest_aiohttp
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/__init__.py b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/__init__.py
new file mode 100644
index 000000000..1a9008f48
--- /dev/null
+++ b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/__init__.py
@@ -0,0 +1,19 @@
+__author__ = "Minos Framework Devs"
+__email__ = "hey@minos.run"
+__version__ = "0.6.0"
+
+from .exceptions import (
+ RestResponseException,
+)
+from .handlers import (
+ RestHandler,
+)
+from .requests import (
+ RestRequest,
+)
+from .responses import (
+ RestResponse,
+)
+from .services import (
+ RestService,
+)
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/exceptions.py b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/exceptions.py
new file mode 100644
index 000000000..e32f39495
--- /dev/null
+++ b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/exceptions.py
@@ -0,0 +1,7 @@
+from minos.networks import (
+ ResponseException,
+)
+
+
+class RestResponseException(ResponseException):
+ """Rest Response Exception class."""
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/graphql_example.py b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/graphql_example.py
new file mode 100644
index 000000000..99bb89a39
--- /dev/null
+++ b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/graphql_example.py
@@ -0,0 +1,17 @@
+import asyncio
+from graphql import (
+ graphql, GraphQLSchema, GraphQLObjectType, GraphQLField, GraphQLString)
+
+
+async def resolve_hello(obj, info):
+ await asyncio.sleep(3)
+ return 'world'
+
+schema = GraphQLSchema(
+ query=GraphQLObjectType(
+ name='RootQueryType',
+ fields={
+ 'hello': GraphQLField(
+ GraphQLString,
+ resolve=resolve_hello)
+ }))
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/handlers.py b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/handlers.py
new file mode 100644
index 000000000..573a751a4
--- /dev/null
+++ b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/handlers.py
@@ -0,0 +1,169 @@
+from __future__ import (
+ annotations,
+)
+
+import logging
+from functools import (
+ cached_property,
+ wraps,
+)
+from inspect import (
+ isawaitable,
+)
+from typing import (
+ Awaitable,
+ Callable,
+ Optional,
+ Union,
+)
+
+from aiohttp import (
+ web,
+)
+
+from minos.common import (
+ MinosConfig,
+ MinosSetup,
+)
+from minos.networks import (
+ REQUEST_USER_CONTEXT_VAR,
+ EnrouteBuilder,
+ ResponseException,
+)
+
+from .requests import (
+ RestRequest,
+)
+from .responses import (
+ RestResponse,
+)
+
+logger = logging.getLogger(__name__)
+
+
+class RestHandler(MinosSetup):
+ """Rest Handler class."""
+
+ def __init__(self, host: str, port: int, endpoints: dict[(str, str), Callable], **kwargs):
+ super().__init__(**kwargs)
+ self._host = host
+ self._port = port
+ self._endpoints = endpoints
+
+ @property
+ def endpoints(self) -> dict[(str, str), Callable]:
+ """Endpoints getter.
+
+ :return: A dictionary value.
+ """
+ return self._endpoints
+
+ @classmethod
+ def _from_config(cls, *args, config: MinosConfig, **kwargs) -> RestHandler:
+ host = config.rest.host
+ port = config.rest.port
+ endpoints = cls._endpoints_from_config(config)
+
+ return cls(host=host, port=port, endpoints=endpoints, **kwargs)
+
+ @staticmethod
+ def _endpoints_from_config(config: MinosConfig, **kwargs) -> dict[(str, str), Callable]:
+ builder = EnrouteBuilder(*config.services, middleware=config.middleware)
+ decorators = builder.get_rest_command_query(config=config, **kwargs)
+ endpoints = {(decorator.url, decorator.method): fn for decorator, fn in decorators.items()}
+ return endpoints
+
+ @property
+ def host(self) -> str:
+ """Get the rest host.
+
+ :return: A ``str`` object.
+ """
+ return self._host
+
+ @property
+ def port(self) -> int:
+ """Get the rest port.
+
+ :return: An integer value.
+ """
+ return self._port
+
+ def get_app(self) -> web.Application:
+ """Return rest application instance.
+
+ :return: A `web.Application` instance.
+ """
+ return self._app
+
+ @cached_property
+ def _app(self) -> web.Application:
+ app = web.Application()
+ self._mount_routes(app)
+ return app
+
+ def _mount_routes(self, app: web.Application):
+ """Load routes from config file."""
+ for (url, method), fn in self._endpoints.items():
+ self._mount_one_route(method, url, fn, app)
+
+ # Load default routes
+ self._mount_system_health(app)
+
+ def _mount_one_route(self, method: str, url: str, action: Callable, app: web.Application) -> None:
+ handler = self.get_callback(action)
+ app.router.add_route(method, url, handler)
+
+ @staticmethod
+ def get_callback(
+ fn: Callable[[RestRequest], Union[Optional[RestResponse], Awaitable[Optional[RestResponse]]]]
+ ) -> Callable[[web.Request], Awaitable[web.Response]]:
+ """Get the handler function to be used by the ``aiohttp`` Controller.
+
+ :param fn: The action function.
+ :return: A wrapper function around the given one that is compatible with the ``aiohttp`` Controller.
+ """
+
+ @wraps(fn)
+ async def _wrapper(request: web.Request) -> web.Response:
+ logger.info(f"Dispatching '{request!s}' from '{request.remote!s}'...")
+
+ request = RestRequest(request)
+ token = REQUEST_USER_CONTEXT_VAR.set(request.user)
+
+ try:
+ response = fn(request)
+ if isawaitable(response):
+ response = await response
+
+ if not isinstance(response, RestResponse):
+ response = RestResponse.from_response(response)
+
+ content = await response.content()
+ content_type = response.content_type
+ status = response.status
+
+ return web.Response(body=content, content_type=content_type, status=status)
+
+ except ResponseException as exc:
+ logger.warning(f"Raised an application exception: {exc!s}")
+ return web.Response(text=str(exc), status=exc.status)
+ except Exception as exc:
+ logger.exception(f"Raised a system exception: {exc!r}")
+ raise web.HTTPInternalServerError()
+ finally:
+ REQUEST_USER_CONTEXT_VAR.reset(token)
+
+ return _wrapper
+
+ def _mount_system_health(self, app: web.Application):
+ """Mount System Health Route."""
+ app.router.add_get("/system/health", self._system_health_handler)
+
+ @staticmethod
+ async def _system_health_handler(request: web.Request) -> web.Response:
+ """System Health Route Handler.
+ :return: A `web.json_response` response.
+ """
+ logger.info(f"Dispatching '{request!s}' from '{request.remote!s}'...")
+ return web.json_response({"host": request.host})
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/requests.py b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/requests.py
new file mode 100644
index 000000000..0b99c93b0
--- /dev/null
+++ b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/requests.py
@@ -0,0 +1,234 @@
+from __future__ import (
+ annotations,
+)
+
+import warnings
+from collections import (
+ defaultdict,
+)
+from collections.abc import (
+ Callable,
+ Iterable,
+)
+from itertools import (
+ chain,
+)
+from typing import (
+ Any,
+ Optional,
+ Union,
+)
+from urllib.parse import (
+ parse_qsl,
+)
+from uuid import (
+ UUID,
+)
+
+from aiohttp.web import Request as AioHttpRequest
+from cached_property import (
+ cached_property,
+)
+
+from minos.common import (
+ AvroDataDecoder,
+ AvroSchemaDecoder,
+ MinosAvroProtocol,
+ import_module,
+)
+from minos.networks import (
+ NotHasParamsException,
+ Request,
+)
+
+
+class RestRequest(Request):
+ """Rest Request class."""
+
+ __slots__ = "raw"
+
+ def __init__(self, raw: AioHttpRequest, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.raw = raw
+
+ @property
+ def raw_request(self) -> AioHttpRequest:
+ """Get the raw request within the instance.
+
+ :return: An ``aiohttp.web.Request`` instance.
+ """
+ warnings.warn(
+ f"'{RestRequest.__name__}s.raw_request' is deprecated in favor of '{RestRequest.__name__}.raw'.",
+ DeprecationWarning,
+ )
+ return self.raw
+
+ def __eq__(self, other: RestRequest) -> bool:
+ return type(self) == type(other) and self.raw == other.raw
+
+ def __repr__(self) -> str:
+ return f"{type(self).__name__}({self.raw!r})"
+
+ @cached_property
+ def user(self) -> Optional[UUID]:
+ """
+ Returns the UUID of the user making the Request.
+ """
+ if "user" not in self.headers:
+ return None
+ return UUID(self.headers["user"])
+
+ @property
+ def headers(self) -> dict[str, str]:
+ """Get the headers of the request.
+
+ :return: A dictionary in which keys are ``str`` instances and values are ``str`` instances.
+ """
+ # noinspection PyTypeChecker
+ return self.raw.headers
+
+ @property
+ def has_content(self) -> bool:
+ """Check if the request has content.
+
+ :return: ``True`` if it has content or ``False`` otherwise.
+ """
+ return self.raw.body_exists
+
+ async def _content(self, type_: Optional[Union[type, str]] = None, **kwargs) -> Any:
+ if "model_type" in kwargs:
+ warnings.warn("The 'model_type' argument is deprecated. Use 'type_' instead", DeprecationWarning)
+ if type_ is None:
+ type_ = kwargs["model_type"]
+
+ data = await self._content_parser()
+ return self._build(data, type_)
+
+ @cached_property
+ def _content_parser(self) -> Callable:
+ mapper = {
+ "application/json": self._raw_json,
+ "application/x-www-form-urlencoded": self._raw_form,
+ "avro/binary": self._raw_avro,
+ "text/plain": self._raw_text,
+ "application/octet-stream": self._raw_bytes,
+ }
+
+ if self.content_type not in mapper:
+ raise ValueError(
+ f"The given Content-Type ({self.content_type!r}) is not supported for automatic content parsing yet."
+ )
+
+ return mapper[self.content_type]
+
+ async def _raw_json(self) -> Any:
+ return await self.raw.json()
+
+ async def _raw_form(self) -> dict[str, Any]:
+ form = await self.raw.json(loads=parse_qsl)
+ return self._parse_multi_dict(form)
+
+ async def _raw_avro(self) -> Any:
+ data = MinosAvroProtocol.decode(await self._raw_bytes())
+ schema = MinosAvroProtocol.decode_schema(await self._raw_bytes())
+
+ type_ = AvroSchemaDecoder(schema).build()
+ return AvroDataDecoder(type_).build(data)
+
+ async def _raw_text(self) -> str:
+ return await self.raw.text()
+
+ async def _raw_bytes(self) -> bytes:
+ return await self.raw.read()
+
+ @property
+ def content_type(self) -> str:
+ """Get the content type.
+
+ :return: A ``str`` value.
+ """
+ return self.raw.content_type
+
+ @property
+ def has_params(self) -> bool:
+ """Check if the request has params.
+
+ :return: ``True`` if it has params or ``False`` otherwise.
+ """
+ sentinel = object()
+ return next(chain(self._raw_url_params, self._raw_query_params), sentinel) is not sentinel
+
+ async def _params(self, type_: Optional[Union[type, str]] = None, **kwargs) -> dict[str, Any]:
+ data = self._parse_multi_dict(chain(self._raw_url_params, self._raw_query_params))
+ return self._build(data, type_)
+
+ async def url_params(self, type_: Optional[Union[type, str]] = None, **kwargs) -> Any:
+ """Get the url params.
+
+ :param type_: Optional ``type`` or ``str`` (classname) that defines the request content type.
+ :param kwargs: Additional named arguments.
+ :return: A dictionary instance.
+ """
+ if not self.has_url_params:
+ raise NotHasParamsException(f"{self!r} has not url params.")
+
+ data = self._parse_multi_dict(self._raw_url_params)
+ return self._build(data, type_)
+
+ @property
+ def has_url_params(self) -> bool:
+ """Check if the request has url params.
+
+ :return: ``True`` if it has url params or ``False`` otherwise.
+ """
+ sentinel = object()
+ return next(iter(self._raw_url_params), sentinel) is not sentinel
+
+ @property
+ def _raw_url_params(self):
+ return self.raw.rel_url.query.items() # pragma: no cover
+
+ async def query_params(self, type_: Optional[Union[type, str]] = None, **kwargs) -> Any:
+ """Get the query params.
+
+
+ :param type_: Optional ``type`` or ``str`` (classname) that defines the request content type.
+ :param kwargs: Additional named arguments.
+ :return: A dictionary instance.
+ """
+ if not self.has_query_params:
+ raise NotHasParamsException(f"{self!r} has not query params.")
+
+ data = self._parse_multi_dict(self._raw_query_params)
+ return self._build(data, type_)
+
+ @property
+ def has_query_params(self) -> bool:
+ # noinspection GrazieInspection
+ """Check if the request has query params.
+
+ :return: ``True`` if it has query params or ``False`` otherwise.
+ """
+ sentinel = object()
+ return next(iter(self._raw_query_params), sentinel) is not sentinel
+
+ @property
+ def _raw_query_params(self):
+ return self.raw.match_info.items() # pragma: no cover
+
+ @staticmethod
+ def _build(data: Any, type_: Union[type, str]) -> Any:
+ if type_ is None:
+ return data
+
+ if isinstance(type_, str):
+ type_ = import_module(type_)
+
+ return AvroDataDecoder(type_).build(data)
+
+ @staticmethod
+ def _parse_multi_dict(raw: Iterable[str, Any]) -> dict[str, Any]:
+ args = defaultdict(list)
+ for k, v in raw:
+ args[k].append(v)
+ return {k: v if len(v) > 1 else v[0] for k, v in args.items()}
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/responses.py b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/responses.py
new file mode 100644
index 000000000..ca082d103
--- /dev/null
+++ b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/responses.py
@@ -0,0 +1,117 @@
+from __future__ import (
+ annotations,
+)
+
+from collections.abc import (
+ Callable,
+)
+from typing import (
+ Any,
+ Awaitable,
+ Optional,
+)
+from urllib.parse import (
+ urlencode,
+)
+
+from cached_property import (
+ cached_property,
+)
+from orjson import (
+ orjson,
+)
+
+from minos.common import (
+ AvroDataEncoder,
+ AvroSchemaEncoder,
+ MinosAvroProtocol,
+ TypeHintBuilder,
+)
+from minos.networks import (
+ Response,
+)
+
+
+class RestResponse(Response):
+ """Rest Response class."""
+
+ def __init__(self, *args, content_type: str = "application/json", **kwargs):
+ super().__init__(*args, **kwargs)
+ self.content_type = content_type
+
+ @classmethod
+ def from_response(cls, response: Optional[Response]) -> RestResponse:
+ """Build a new ``RestRequest`` from another response.
+
+ :param response: The base response.
+ :return: A ``RestResponse`` instance.
+ """
+ if isinstance(response, RestResponse):
+ return response
+
+ if response is None:
+ return RestResponse()
+
+ return RestResponse(response._data)
+
+ # noinspection PyUnusedLocal
+ async def content(self, **kwargs) -> Optional[bytes]:
+ """Raw response content.
+
+ :param kwargs: Additional named arguments.
+ :return: The raw content as a ``bytes`` instance.
+ """
+ if not self.has_content:
+ return None
+ return await self._content_parser()
+
+ @cached_property
+ def _content_parser(self) -> Callable[[], Awaitable[bytes]]:
+ mapper = {
+ "application/json": self._raw_json,
+ "application/x-www-form-urlencoded": self._raw_form,
+ "avro/binary": self._raw_avro,
+ "text/plain": self._raw_text,
+ "application/octet-stream": self._raw_bytes,
+ }
+
+ if self.content_type not in mapper:
+ return self._raw_bytes
+
+ return mapper[self.content_type]
+
+ async def _raw_json(self) -> bytes:
+ return orjson.dumps(self._raw_data)
+
+ async def _raw_form(self) -> bytes:
+ return urlencode(self._raw_data).encode()
+
+ async def _raw_avro(self) -> bytes:
+ type_ = TypeHintBuilder(self._data).build()
+ schema = AvroSchemaEncoder(type_).build()
+ data = AvroDataEncoder(self._data).build()
+
+ return MinosAvroProtocol.encode(data, schema)
+
+ async def _raw_text(self) -> bytes:
+ if not isinstance(self._raw_data, str):
+ raise ValueError(
+ f"Given 'Content-Type' ({self.content_type!r}) is not supported for the given data: {self._raw_data!r}."
+ )
+ return self._raw_data.encode()
+
+ async def _raw_bytes(self) -> bytes:
+ if not isinstance(self._raw_data, bytes):
+ raise ValueError(
+ f"Given 'Content-Type' ({self.content_type!r}) is not supported for the given data: {self._raw_data!r}."
+ )
+ return self._raw_data
+
+ # noinspection PyUnusedLocal
+ @cached_property
+ def _raw_data(self) -> Any:
+ """Raw response content.
+
+ :return: A list of raw items.
+ """
+ return AvroDataEncoder(self._data).build()
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/services.py b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/services.py
new file mode 100644
index 000000000..e9b01883e
--- /dev/null
+++ b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/services.py
@@ -0,0 +1,44 @@
+from aiohttp import (
+ web,
+)
+from aiomisc.service.aiohttp import (
+ AIOHTTPService,
+)
+
+from .handlers import (
+ RestHandler,
+)
+from graphql_server.aiohttp import GraphQLView
+
+from .graphql_example import schema
+
+
+class RestService(AIOHTTPService):
+ """
+ Rest Interface
+
+ Expose REST Interface handler using aiomisc AIOHTTPService.
+
+ """
+
+ def __init__(self, **kwargs):
+ pass
+ #self.handler = RestHandler.from_config(**kwargs)
+ #super().__init__(**(kwargs | {"address": self.handler.host, "port": self.handler.port}))
+
+ async def create_application(self) -> web.Application:
+ """Create the web application.
+
+ :return: A ``web.Application`` instance.
+ """
+ app = web.Application()
+
+ GraphQLView.attach(app, schema=schema, graphiql=True)
+ return app
+ # return self.handler.get_app() # pragma: no cover
+
+
+async def main():
+ web.run_app(RestService.create_application())
+
+
diff --git a/packages/plugins/minos-graphql-aiohttp/poetry.lock b/packages/plugins/minos-graphql-aiohttp/poetry.lock
new file mode 100644
index 000000000..e4f51e8c9
--- /dev/null
+++ b/packages/plugins/minos-graphql-aiohttp/poetry.lock
@@ -0,0 +1,1811 @@
+[[package]]
+name = "aiohttp"
+version = "3.8.1"
+description = "Async http client/server framework (asyncio)"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+aiosignal = ">=1.1.2"
+async-timeout = ">=4.0.0a3,<5.0"
+attrs = ">=17.3.0"
+charset-normalizer = ">=2.0,<3.0"
+frozenlist = ">=1.1.1"
+multidict = ">=4.5,<7.0"
+yarl = ">=1.0,<2.0"
+
+[package.extras]
+speedups = ["aiodns", "brotli", "cchardet"]
+
+[[package]]
+name = "aiomisc"
+version = "15.6.8"
+description = "aiomisc - miscellaneous utils for asyncio"
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+colorlog = "*"
+
+[package.extras]
+aiohttp = ["aiohttp"]
+asgi = ["aiohttp-asgi"]
+carbon = ["aiocarbon (>=0.15,<1.0)"]
+contextvars = ["contextvars (>=2.4,<3.0)"]
+cron = ["croniter (>=0.3.34,<0.4.0)"]
+develop = ["aiocontextvars (==0.2.2)", "aiohttp-asgi", "aiohttp (<4)", "async-timeout", "coveralls", "croniter (>=0.3.34,<0.4.0)", "fastapi", "freezegun (<1.1)", "mypy (>=0.782,<1.0)", "pylava", "pytest", "pytest-cov (>=3.0,<4.0)", "pytest-freezegun (>=0.4.2,<0.5.0)", "pytest-rst", "pytest-subtests", "rich", "setproctitle", "sphinx-autobuild", "sphinx-intl", "sphinx (>=3.5.1)", "timeout-decorator", "tox (>=2.4)", "types-croniter"]
+raven = ["raven-aiohttp"]
+uvloop = ["uvloop (>=0.14,<1)"]
+
+[[package]]
+name = "aiopg"
+version = "1.3.3"
+description = "Postgres integration with asyncio."
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+async-timeout = ">=3.0,<5.0"
+psycopg2-binary = ">=2.8.4"
+
+[package.extras]
+sa = ["sqlalchemy[postgresql_psycopg2binary] (>=1.3,<1.5)"]
+
+[[package]]
+name = "aiosignal"
+version = "1.2.0"
+description = "aiosignal: a list of registered asynchronous callbacks"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+frozenlist = ">=1.1.0"
+
+[[package]]
+name = "alabaster"
+version = "0.7.12"
+description = "A configurable sidebar-enabled Sphinx theme"
+category = "dev"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "async-timeout"
+version = "4.0.2"
+description = "Timeout context manager for asyncio programs"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[[package]]
+name = "atomicwrites"
+version = "1.4.0"
+description = "Atomic file writes."
+category = "dev"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+
+[[package]]
+name = "attrs"
+version = "21.4.0"
+description = "Classes Without Boilerplate"
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+
+[package.extras]
+dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit", "cloudpickle"]
+docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"]
+tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "cloudpickle"]
+tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "cloudpickle"]
+
+[[package]]
+name = "babel"
+version = "2.9.1"
+description = "Internationalization utilities"
+category = "dev"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+
+[package.dependencies]
+pytz = ">=2015.7"
+
+[[package]]
+name = "black"
+version = "22.1.0"
+description = "The uncompromising code formatter."
+category = "dev"
+optional = false
+python-versions = ">=3.6.2"
+
+[package.dependencies]
+click = ">=8.0.0"
+mypy-extensions = ">=0.4.3"
+pathspec = ">=0.9.0"
+platformdirs = ">=2"
+tomli = ">=1.1.0"
+typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""}
+
+[package.extras]
+colorama = ["colorama (>=0.4.3)"]
+d = ["aiohttp (>=3.7.4)"]
+jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"]
+uvloop = ["uvloop (>=0.15.2)"]
+
+[[package]]
+name = "cached-property"
+version = "1.5.2"
+description = "A decorator for caching properties in classes."
+category = "main"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "certifi"
+version = "2021.10.8"
+description = "Python package for providing Mozilla's CA Bundle."
+category = "dev"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "cfgv"
+version = "3.3.1"
+description = "Validate configuration and produce human readable error messages."
+category = "dev"
+optional = false
+python-versions = ">=3.6.1"
+
+[[package]]
+name = "charset-normalizer"
+version = "2.0.12"
+description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
+category = "main"
+optional = false
+python-versions = ">=3.5.0"
+
+[package.extras]
+unicode_backport = ["unicodedata2"]
+
+[[package]]
+name = "click"
+version = "8.0.4"
+description = "Composable command line interface toolkit"
+category = "dev"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+colorama = {version = "*", markers = "platform_system == \"Windows\""}
+
+[[package]]
+name = "colorama"
+version = "0.4.4"
+description = "Cross-platform colored terminal text."
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+
+[[package]]
+name = "colorlog"
+version = "6.6.0"
+description = "Add colours to the output of Python's logging module."
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+colorama = {version = "*", markers = "sys_platform == \"win32\""}
+
+[package.extras]
+development = ["black", "flake8", "mypy", "pytest", "types-colorama"]
+
+[[package]]
+name = "coverage"
+version = "6.3.2"
+description = "Code coverage measurement for Python"
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+
+[package.extras]
+toml = ["tomli"]
+
+[[package]]
+name = "crontab"
+version = "0.23.0"
+description = "Parse and use crontab schedules in Python"
+category = "main"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "dependency-injector"
+version = "4.38.0"
+description = "Dependency injection framework for Python"
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+six = ">=1.7.0,<=1.16.0"
+
+[package.extras]
+aiohttp = ["aiohttp"]
+flask = ["flask"]
+pydantic = ["pydantic"]
+yaml = ["pyyaml"]
+
+[[package]]
+name = "distlib"
+version = "0.3.4"
+description = "Distribution utilities"
+category = "dev"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "docutils"
+version = "0.17.1"
+description = "Docutils -- Python Documentation Utilities"
+category = "dev"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+
+[[package]]
+name = "fastavro"
+version = "1.4.9"
+description = "Fast read/write of AVRO files"
+category = "main"
+optional = false
+python-versions = ">=3.7"
+
+[package.extras]
+codecs = ["python-snappy", "zstandard", "lz4"]
+lz4 = ["lz4"]
+snappy = ["python-snappy"]
+zstandard = ["zstandard"]
+
+[[package]]
+name = "filelock"
+version = "3.6.0"
+description = "A platform independent file lock."
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+
+[package.extras]
+docs = ["furo (>=2021.8.17b43)", "sphinx (>=4.1)", "sphinx-autodoc-typehints (>=1.12)"]
+testing = ["covdefaults (>=1.2.0)", "coverage (>=4)", "pytest (>=4)", "pytest-cov", "pytest-timeout (>=1.4.2)"]
+
+[[package]]
+name = "flake8"
+version = "4.0.1"
+description = "the modular source code checker: pep8 pyflakes and co"
+category = "dev"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+mccabe = ">=0.6.0,<0.7.0"
+pycodestyle = ">=2.8.0,<2.9.0"
+pyflakes = ">=2.4.0,<2.5.0"
+
+[[package]]
+name = "frozenlist"
+version = "1.3.0"
+description = "A list-like structure which implements collections.abc.MutableSequence"
+category = "main"
+optional = false
+python-versions = ">=3.7"
+
+[[package]]
+name = "graphql-core"
+version = "3.2.0"
+description = "GraphQL implementation for Python, a port of GraphQL.js, the JavaScript reference implementation for GraphQL."
+category = "main"
+optional = false
+python-versions = ">=3.6,<4"
+
+[[package]]
+name = "graphql-server"
+version = "3.0.0b5"
+description = "GraphQL Server tools for powering your server"
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+aiohttp = {version = ">=3.8,<4", optional = true, markers = "extra == \"aiohttp\""}
+graphql-core = ">=3.2,<3.3"
+typing-extensions = ">=4,<5"
+
+[package.extras]
+aiohttp = ["aiohttp (>=3.8,<4)"]
+all = ["graphql-core (>=3.2,<3.3)", "typing-extensions (>=4,<5)", "flask (>=1,<2)", "sanic (>=20.3,<21)", "webob (>=1.8.7,<2)", "aiohttp (>=3.8,<4)", "quart (>=0.6.15,<0.15)"]
+dev = ["graphql-core (>=3.2,<3.3)", "typing-extensions (>=4,<5)", "flask (>=1,<2)", "sanic (>=20.3,<21)", "webob (>=1.8.7,<2)", "aiohttp (>=3.8,<4)", "quart (>=0.6.15,<0.15)", "flake8 (>=4,<5)", "isort (>=5,<6)", "black (>=19.10b0)", "mypy (>=0.931,<1)", "check-manifest (>=0.47,<1)", "pytest (>=6.2,<6.3)", "pytest-asyncio (>=0.16,<1)", "pytest-cov (>=3,<4)", "aiohttp (>=3.8,<4)", "Jinja2 (>=2.11,<3)"]
+flask = ["flask (>=1,<2)"]
+quart = ["quart (>=0.6.15,<0.15)"]
+sanic = ["sanic (>=20.3,<21)"]
+test = ["graphql-core (>=3.2,<3.3)", "typing-extensions (>=4,<5)", "flask (>=1,<2)", "sanic (>=20.3,<21)", "webob (>=1.8.7,<2)", "aiohttp (>=3.8,<4)", "quart (>=0.6.15,<0.15)", "pytest (>=6.2,<6.3)", "pytest-asyncio (>=0.16,<1)", "pytest-cov (>=3,<4)", "aiohttp (>=3.8,<4)", "Jinja2 (>=2.11,<3)"]
+webob = ["webob (>=1.8.7,<2)"]
+
+[[package]]
+name = "identify"
+version = "2.4.11"
+description = "File identification library for Python"
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+
+[package.extras]
+license = ["ukkonen"]
+
+[[package]]
+name = "idna"
+version = "3.3"
+description = "Internationalized Domain Names in Applications (IDNA)"
+category = "main"
+optional = false
+python-versions = ">=3.5"
+
+[[package]]
+name = "imagesize"
+version = "1.3.0"
+description = "Getting image size from png/jpeg/jpeg2000/gif file"
+category = "dev"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+
+[[package]]
+name = "importlib-metadata"
+version = "4.11.2"
+description = "Read metadata from Python packages"
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+
+[package.dependencies]
+zipp = ">=0.5"
+
+[package.extras]
+docs = ["sphinx", "jaraco.packaging (>=9)", "rst.linker (>=1.9)"]
+perf = ["ipython"]
+testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pyfakefs", "flufl.flake8", "pytest-perf (>=0.9.2)", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)", "importlib-resources (>=1.3)"]
+
+[[package]]
+name = "iniconfig"
+version = "1.1.1"
+description = "iniconfig: brain-dead simple config-ini parsing"
+category = "dev"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "isort"
+version = "5.10.1"
+description = "A Python utility / library to sort Python imports."
+category = "dev"
+optional = false
+python-versions = ">=3.6.1,<4.0"
+
+[package.extras]
+pipfile_deprecated_finder = ["pipreqs", "requirementslib"]
+requirements_deprecated_finder = ["pipreqs", "pip-api"]
+colors = ["colorama (>=0.4.3,<0.5.0)"]
+plugins = ["setuptools"]
+
+[[package]]
+name = "jinja2"
+version = "3.0.3"
+description = "A very fast and expressive template engine."
+category = "dev"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+MarkupSafe = ">=2.0"
+
+[package.extras]
+i18n = ["Babel (>=2.7)"]
+
+[[package]]
+name = "lmdb"
+version = "1.3.0"
+description = "Universal Python binding for the LMDB 'Lightning' Database"
+category = "main"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "m2r2"
+version = "0.3.2"
+description = "Markdown and reStructuredText in a single file."
+category = "dev"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+docutils = "*"
+mistune = "0.8.4"
+
+[[package]]
+name = "markupsafe"
+version = "2.1.0"
+description = "Safely add untrusted strings to HTML/XML markup."
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+
+[[package]]
+name = "mccabe"
+version = "0.6.1"
+description = "McCabe checker, plugin for flake8"
+category = "dev"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "minos-microservice-common"
+version = "0.5.2"
+description = "The common core of the Minos Framework"
+category = "main"
+optional = false
+python-versions = "^3.9"
+develop = true
+
+[package.dependencies]
+aiomisc = ">=14.0.3,<16.0.0"
+aiopg = "^1.2.1"
+cached-property = "^1.5.2"
+dependency-injector = "^4.32.2"
+fastavro = "^1.4.0"
+lmdb = "^1.2.1"
+orjson = "^3.5.2"
+PyYAML = ">=5.4.1,<7.0.0"
+
+[package.source]
+type = "directory"
+url = "../../core/minos-microservice-common"
+
+[[package]]
+name = "minos-microservice-networks"
+version = "0.5.2"
+description = "The networks core of the Minos Framework"
+category = "main"
+optional = false
+python-versions = "^3.9"
+develop = true
+
+[package.dependencies]
+aiohttp = "^3.7.4"
+aiomisc = ">=14.0.3,<16.0.0"
+aiopg = "^1.2.1"
+crontab = "^0.23.0"
+dependency-injector = "^4.32.2"
+minos-microservice-common = "^0.5.0"
+orjson = "^3.6.5"
+
+[package.source]
+type = "directory"
+url = "../../core/minos-microservice-networks"
+
+[[package]]
+name = "mistune"
+version = "0.8.4"
+description = "The fastest markdown parser in pure Python"
+category = "dev"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "multidict"
+version = "6.0.2"
+description = "multidict implementation"
+category = "main"
+optional = false
+python-versions = ">=3.7"
+
+[[package]]
+name = "mypy-extensions"
+version = "0.4.3"
+description = "Experimental type system extensions for programs checked with the mypy typechecker."
+category = "dev"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "nodeenv"
+version = "1.6.0"
+description = "Node.js virtual environment builder"
+category = "dev"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "orjson"
+version = "3.6.7"
+description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy"
+category = "main"
+optional = false
+python-versions = ">=3.7"
+
+[[package]]
+name = "packaging"
+version = "21.3"
+description = "Core utilities for Python packages"
+category = "dev"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+pyparsing = ">=2.0.2,<3.0.5 || >3.0.5"
+
+[[package]]
+name = "pathspec"
+version = "0.9.0"
+description = "Utility library for gitignore style pattern matching of file paths."
+category = "dev"
+optional = false
+python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
+
+[[package]]
+name = "pbr"
+version = "5.8.1"
+description = "Python Build Reasonableness"
+category = "dev"
+optional = false
+python-versions = ">=2.6"
+
+[[package]]
+name = "platformdirs"
+version = "2.5.1"
+description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+
+[package.extras]
+docs = ["Sphinx (>=4)", "furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)"]
+test = ["appdirs (==1.4.4)", "pytest (>=6)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)"]
+
+[[package]]
+name = "pluggy"
+version = "1.0.0"
+description = "plugin and hook calling mechanisms for python"
+category = "dev"
+optional = false
+python-versions = ">=3.6"
+
+[package.extras]
+dev = ["pre-commit", "tox"]
+testing = ["pytest", "pytest-benchmark"]
+
+[[package]]
+name = "pre-commit"
+version = "2.17.0"
+description = "A framework for managing and maintaining multi-language pre-commit hooks."
+category = "dev"
+optional = false
+python-versions = ">=3.6.1"
+
+[package.dependencies]
+cfgv = ">=2.0.0"
+identify = ">=1.0.0"
+nodeenv = ">=0.11.1"
+pyyaml = ">=5.1"
+toml = "*"
+virtualenv = ">=20.0.8"
+
+[[package]]
+name = "psycopg2-binary"
+version = "2.9.3"
+description = "psycopg2 - Python-PostgreSQL Database Adapter"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[[package]]
+name = "py"
+version = "1.11.0"
+description = "library with cross-python path, ini-parsing, io, code, log facilities"
+category = "dev"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+
+[[package]]
+name = "pycodestyle"
+version = "2.8.0"
+description = "Python style guide checker"
+category = "dev"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+
+[[package]]
+name = "pyflakes"
+version = "2.4.0"
+description = "passive checker of Python programs"
+category = "dev"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+
+[[package]]
+name = "pygments"
+version = "2.11.2"
+description = "Pygments is a syntax highlighting package written in Python."
+category = "dev"
+optional = false
+python-versions = ">=3.5"
+
+[[package]]
+name = "pyparsing"
+version = "3.0.7"
+description = "Python parsing module"
+category = "dev"
+optional = false
+python-versions = ">=3.6"
+
+[package.extras]
+diagrams = ["jinja2", "railroad-diagrams"]
+
+[[package]]
+name = "pytest"
+version = "6.2.5"
+description = "pytest: simple powerful testing with Python"
+category = "dev"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""}
+attrs = ">=19.2.0"
+colorama = {version = "*", markers = "sys_platform == \"win32\""}
+iniconfig = "*"
+packaging = "*"
+pluggy = ">=0.12,<2.0"
+py = ">=1.8.2"
+toml = "*"
+
+[package.extras]
+testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"]
+
+[[package]]
+name = "pytz"
+version = "2021.3"
+description = "World timezone definitions, modern and historical"
+category = "dev"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "pyyaml"
+version = "6.0"
+description = "YAML parser and emitter for Python"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[[package]]
+name = "requests"
+version = "2.27.1"
+description = "Python HTTP for Humans."
+category = "dev"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
+
+[package.dependencies]
+certifi = ">=2017.4.17"
+charset-normalizer = {version = ">=2.0.0,<2.1.0", markers = "python_version >= \"3\""}
+idna = {version = ">=2.5,<4", markers = "python_version >= \"3\""}
+urllib3 = ">=1.21.1,<1.27"
+
+[package.extras]
+socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"]
+use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"]
+
+[[package]]
+name = "six"
+version = "1.16.0"
+description = "Python 2 and 3 compatibility utilities"
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
+
+[[package]]
+name = "snowballstemmer"
+version = "2.2.0"
+description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms."
+category = "dev"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "sphinx"
+version = "4.4.0"
+description = "Python documentation generator"
+category = "dev"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+alabaster = ">=0.7,<0.8"
+babel = ">=1.3"
+colorama = {version = ">=0.3.5", markers = "sys_platform == \"win32\""}
+docutils = ">=0.14,<0.18"
+imagesize = "*"
+importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""}
+Jinja2 = ">=2.3"
+packaging = "*"
+Pygments = ">=2.0"
+requests = ">=2.5.0"
+snowballstemmer = ">=1.1"
+sphinxcontrib-applehelp = "*"
+sphinxcontrib-devhelp = "*"
+sphinxcontrib-htmlhelp = ">=2.0.0"
+sphinxcontrib-jsmath = "*"
+sphinxcontrib-qthelp = "*"
+sphinxcontrib-serializinghtml = ">=1.1.5"
+
+[package.extras]
+docs = ["sphinxcontrib-websupport"]
+lint = ["flake8 (>=3.5.0)", "isort", "mypy (>=0.931)", "docutils-stubs", "types-typed-ast", "types-requests"]
+test = ["pytest", "pytest-cov", "html5lib", "cython", "typed-ast"]
+
+[[package]]
+name = "sphinx-autodoc-typehints"
+version = "1.17.0"
+description = "Type hints (PEP 484) support for the Sphinx autodoc extension"
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+
+[package.dependencies]
+Sphinx = ">=4"
+
+[package.extras]
+testing = ["covdefaults (>=2)", "coverage (>=6)", "diff-cover (>=6.4)", "nptyping (>=1)", "pytest (>=6)", "pytest-cov (>=3)", "sphobjinv (>=2)", "typing-extensions (>=3.5)"]
+type_comments = ["typed-ast (>=1.4.0)"]
+
+[[package]]
+name = "sphinx-rtd-theme"
+version = "1.0.0"
+description = "Read the Docs theme for Sphinx"
+category = "dev"
+optional = false
+python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*"
+
+[package.dependencies]
+docutils = "<0.18"
+sphinx = ">=1.6"
+
+[package.extras]
+dev = ["transifex-client", "sphinxcontrib-httpdomain", "bump2version"]
+
+[[package]]
+name = "sphinxcontrib-apidoc"
+version = "0.3.0"
+description = "A Sphinx extension for running 'sphinx-apidoc' on each build"
+category = "dev"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+pbr = "*"
+Sphinx = ">=1.6.0"
+
+[[package]]
+name = "sphinxcontrib-applehelp"
+version = "1.0.2"
+description = "sphinxcontrib-applehelp is a sphinx extension which outputs Apple help books"
+category = "dev"
+optional = false
+python-versions = ">=3.5"
+
+[package.extras]
+lint = ["flake8", "mypy", "docutils-stubs"]
+test = ["pytest"]
+
+[[package]]
+name = "sphinxcontrib-devhelp"
+version = "1.0.2"
+description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp document."
+category = "dev"
+optional = false
+python-versions = ">=3.5"
+
+[package.extras]
+lint = ["flake8", "mypy", "docutils-stubs"]
+test = ["pytest"]
+
+[[package]]
+name = "sphinxcontrib-htmlhelp"
+version = "2.0.0"
+description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files"
+category = "dev"
+optional = false
+python-versions = ">=3.6"
+
+[package.extras]
+lint = ["flake8", "mypy", "docutils-stubs"]
+test = ["pytest", "html5lib"]
+
+[[package]]
+name = "sphinxcontrib-jsmath"
+version = "1.0.1"
+description = "A sphinx extension which renders display math in HTML via JavaScript"
+category = "dev"
+optional = false
+python-versions = ">=3.5"
+
+[package.extras]
+test = ["pytest", "flake8", "mypy"]
+
+[[package]]
+name = "sphinxcontrib-qthelp"
+version = "1.0.3"
+description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp document."
+category = "dev"
+optional = false
+python-versions = ">=3.5"
+
+[package.extras]
+lint = ["flake8", "mypy", "docutils-stubs"]
+test = ["pytest"]
+
+[[package]]
+name = "sphinxcontrib-serializinghtml"
+version = "1.1.5"
+description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)."
+category = "dev"
+optional = false
+python-versions = ">=3.5"
+
+[package.extras]
+lint = ["flake8", "mypy", "docutils-stubs"]
+test = ["pytest"]
+
+[[package]]
+name = "toml"
+version = "0.10.2"
+description = "Python Library for Tom's Obvious, Minimal Language"
+category = "dev"
+optional = false
+python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
+
+[[package]]
+name = "tomli"
+version = "2.0.1"
+description = "A lil' TOML parser"
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+
+[[package]]
+name = "typing-extensions"
+version = "4.1.1"
+description = "Backported and Experimental Type Hints for Python 3.6+"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[[package]]
+name = "urllib3"
+version = "1.26.8"
+description = "HTTP library with thread-safe connection pooling, file post, and more."
+category = "dev"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4"
+
+[package.extras]
+brotli = ["brotlipy (>=0.6.0)"]
+secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"]
+socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
+
+[[package]]
+name = "virtualenv"
+version = "20.13.2"
+description = "Virtual Python Environment builder"
+category = "dev"
+optional = false
+python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
+
+[package.dependencies]
+distlib = ">=0.3.1,<1"
+filelock = ">=3.2,<4"
+platformdirs = ">=2,<3"
+six = ">=1.9.0,<2"
+
+[package.extras]
+docs = ["proselint (>=0.10.2)", "sphinx (>=3)", "sphinx-argparse (>=0.2.5)", "sphinx-rtd-theme (>=0.4.3)", "towncrier (>=21.3)"]
+testing = ["coverage (>=4)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", "pytest (>=4)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.1)", "pytest-mock (>=2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)", "packaging (>=20.0)"]
+
+[[package]]
+name = "yarl"
+version = "1.7.2"
+description = "Yet another URL library"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+idna = ">=2.0"
+multidict = ">=4.0"
+
+[[package]]
+name = "zipp"
+version = "3.7.0"
+description = "Backport of pathlib-compatible object wrapper for zip files"
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+
+[package.extras]
+docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"]
+testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"]
+
+[metadata]
+lock-version = "1.1"
+python-versions = "^3.9"
+content-hash = "0dbb3f5d394a3212b60abcc7c2b56b0babee5106f012b46671236628363623bc"
+
+[metadata.files]
+aiohttp = [
+ {file = "aiohttp-3.8.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1ed0b6477896559f17b9eaeb6d38e07f7f9ffe40b9f0f9627ae8b9926ae260a8"},
+ {file = "aiohttp-3.8.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7dadf3c307b31e0e61689cbf9e06be7a867c563d5a63ce9dca578f956609abf8"},
+ {file = "aiohttp-3.8.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a79004bb58748f31ae1cbe9fa891054baaa46fb106c2dc7af9f8e3304dc30316"},
+ {file = "aiohttp-3.8.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:12de6add4038df8f72fac606dff775791a60f113a725c960f2bab01d8b8e6b15"},
+ {file = "aiohttp-3.8.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6f0d5f33feb5f69ddd57a4a4bd3d56c719a141080b445cbf18f238973c5c9923"},
+ {file = "aiohttp-3.8.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eaba923151d9deea315be1f3e2b31cc39a6d1d2f682f942905951f4e40200922"},
+ {file = "aiohttp-3.8.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:099ebd2c37ac74cce10a3527d2b49af80243e2a4fa39e7bce41617fbc35fa3c1"},
+ {file = "aiohttp-3.8.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2e5d962cf7e1d426aa0e528a7e198658cdc8aa4fe87f781d039ad75dcd52c516"},
+ {file = "aiohttp-3.8.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:fa0ffcace9b3aa34d205d8130f7873fcfefcb6a4dd3dd705b0dab69af6712642"},
+ {file = "aiohttp-3.8.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:61bfc23df345d8c9716d03717c2ed5e27374e0fe6f659ea64edcd27b4b044cf7"},
+ {file = "aiohttp-3.8.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:31560d268ff62143e92423ef183680b9829b1b482c011713ae941997921eebc8"},
+ {file = "aiohttp-3.8.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:01d7bdb774a9acc838e6b8f1d114f45303841b89b95984cbb7d80ea41172a9e3"},
+ {file = "aiohttp-3.8.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:97ef77eb6b044134c0b3a96e16abcb05ecce892965a2124c566af0fd60f717e2"},
+ {file = "aiohttp-3.8.1-cp310-cp310-win32.whl", hash = "sha256:c2aef4703f1f2ddc6df17519885dbfa3514929149d3ff900b73f45998f2532fa"},
+ {file = "aiohttp-3.8.1-cp310-cp310-win_amd64.whl", hash = "sha256:713ac174a629d39b7c6a3aa757b337599798da4c1157114a314e4e391cd28e32"},
+ {file = "aiohttp-3.8.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:473d93d4450880fe278696549f2e7aed8cd23708c3c1997981464475f32137db"},
+ {file = "aiohttp-3.8.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99b5eeae8e019e7aad8af8bb314fb908dd2e028b3cdaad87ec05095394cce632"},
+ {file = "aiohttp-3.8.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3af642b43ce56c24d063325dd2cf20ee012d2b9ba4c3c008755a301aaea720ad"},
+ {file = "aiohttp-3.8.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c3630c3ef435c0a7c549ba170a0633a56e92629aeed0e707fec832dee313fb7a"},
+ {file = "aiohttp-3.8.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:4a4a4e30bf1edcad13fb0804300557aedd07a92cabc74382fdd0ba6ca2661091"},
+ {file = "aiohttp-3.8.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6f8b01295e26c68b3a1b90efb7a89029110d3a4139270b24fda961893216c440"},
+ {file = "aiohttp-3.8.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:a25fa703a527158aaf10dafd956f7d42ac6d30ec80e9a70846253dd13e2f067b"},
+ {file = "aiohttp-3.8.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:5bfde62d1d2641a1f5173b8c8c2d96ceb4854f54a44c23102e2ccc7e02f003ec"},
+ {file = "aiohttp-3.8.1-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:51467000f3647d519272392f484126aa716f747859794ac9924a7aafa86cd411"},
+ {file = "aiohttp-3.8.1-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:03a6d5349c9ee8f79ab3ff3694d6ce1cfc3ced1c9d36200cb8f08ba06bd3b782"},
+ {file = "aiohttp-3.8.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:102e487eeb82afac440581e5d7f8f44560b36cf0bdd11abc51a46c1cd88914d4"},
+ {file = "aiohttp-3.8.1-cp36-cp36m-win32.whl", hash = "sha256:4aed991a28ea3ce320dc8ce655875e1e00a11bdd29fe9444dd4f88c30d558602"},
+ {file = "aiohttp-3.8.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b0e20cddbd676ab8a64c774fefa0ad787cc506afd844de95da56060348021e96"},
+ {file = "aiohttp-3.8.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:37951ad2f4a6df6506750a23f7cbabad24c73c65f23f72e95897bb2cecbae676"},
+ {file = "aiohttp-3.8.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c23b1ad869653bc818e972b7a3a79852d0e494e9ab7e1a701a3decc49c20d51"},
+ {file = "aiohttp-3.8.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:15b09b06dae900777833fe7fc4b4aa426556ce95847a3e8d7548e2d19e34edb8"},
+ {file = "aiohttp-3.8.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:477c3ea0ba410b2b56b7efb072c36fa91b1e6fc331761798fa3f28bb224830dd"},
+ {file = "aiohttp-3.8.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2f2f69dca064926e79997f45b2f34e202b320fd3782f17a91941f7eb85502ee2"},
+ {file = "aiohttp-3.8.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ef9612483cb35171d51d9173647eed5d0069eaa2ee812793a75373447d487aa4"},
+ {file = "aiohttp-3.8.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:6d69f36d445c45cda7b3b26afef2fc34ef5ac0cdc75584a87ef307ee3c8c6d00"},
+ {file = "aiohttp-3.8.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:55c3d1072704d27401c92339144d199d9de7b52627f724a949fc7d5fc56d8b93"},
+ {file = "aiohttp-3.8.1-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:b9d00268fcb9f66fbcc7cd9fe423741d90c75ee029a1d15c09b22d23253c0a44"},
+ {file = "aiohttp-3.8.1-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:07b05cd3305e8a73112103c834e91cd27ce5b4bd07850c4b4dbd1877d3f45be7"},
+ {file = "aiohttp-3.8.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c34dc4958b232ef6188c4318cb7b2c2d80521c9a56c52449f8f93ab7bc2a8a1c"},
+ {file = "aiohttp-3.8.1-cp37-cp37m-win32.whl", hash = "sha256:d2f9b69293c33aaa53d923032fe227feac867f81682f002ce33ffae978f0a9a9"},
+ {file = "aiohttp-3.8.1-cp37-cp37m-win_amd64.whl", hash = "sha256:6ae828d3a003f03ae31915c31fa684b9890ea44c9c989056fea96e3d12a9fa17"},
+ {file = "aiohttp-3.8.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0c7ebbbde809ff4e970824b2b6cb7e4222be6b95a296e46c03cf050878fc1785"},
+ {file = "aiohttp-3.8.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8b7ef7cbd4fec9a1e811a5de813311ed4f7ac7d93e0fda233c9b3e1428f7dd7b"},
+ {file = "aiohttp-3.8.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c3d6a4d0619e09dcd61021debf7059955c2004fa29f48788a3dfaf9c9901a7cd"},
+ {file = "aiohttp-3.8.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:718626a174e7e467f0558954f94af117b7d4695d48eb980146016afa4b580b2e"},
+ {file = "aiohttp-3.8.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:589c72667a5febd36f1315aa6e5f56dd4aa4862df295cb51c769d16142ddd7cd"},
+ {file = "aiohttp-3.8.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2ed076098b171573161eb146afcb9129b5ff63308960aeca4b676d9d3c35e700"},
+ {file = "aiohttp-3.8.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:086f92daf51a032d062ec5f58af5ca6a44d082c35299c96376a41cbb33034675"},
+ {file = "aiohttp-3.8.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:11691cf4dc5b94236ccc609b70fec991234e7ef8d4c02dd0c9668d1e486f5abf"},
+ {file = "aiohttp-3.8.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:31d1e1c0dbf19ebccbfd62eff461518dcb1e307b195e93bba60c965a4dcf1ba0"},
+ {file = "aiohttp-3.8.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:11a67c0d562e07067c4e86bffc1553f2cf5b664d6111c894671b2b8712f3aba5"},
+ {file = "aiohttp-3.8.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:bb01ba6b0d3f6c68b89fce7305080145d4877ad3acaed424bae4d4ee75faa950"},
+ {file = "aiohttp-3.8.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:44db35a9e15d6fe5c40d74952e803b1d96e964f683b5a78c3cc64eb177878155"},
+ {file = "aiohttp-3.8.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:844a9b460871ee0a0b0b68a64890dae9c415e513db0f4a7e3cab41a0f2fedf33"},
+ {file = "aiohttp-3.8.1-cp38-cp38-win32.whl", hash = "sha256:7d08744e9bae2ca9c382581f7dce1273fe3c9bae94ff572c3626e8da5b193c6a"},
+ {file = "aiohttp-3.8.1-cp38-cp38-win_amd64.whl", hash = "sha256:04d48b8ce6ab3cf2097b1855e1505181bdd05586ca275f2505514a6e274e8e75"},
+ {file = "aiohttp-3.8.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f5315a2eb0239185af1bddb1abf472d877fede3cc8d143c6cddad37678293237"},
+ {file = "aiohttp-3.8.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a996d01ca39b8dfe77440f3cd600825d05841088fd6bc0144cc6c2ec14cc5f74"},
+ {file = "aiohttp-3.8.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:13487abd2f761d4be7c8ff9080de2671e53fff69711d46de703c310c4c9317ca"},
+ {file = "aiohttp-3.8.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea302f34477fda3f85560a06d9ebdc7fa41e82420e892fc50b577e35fc6a50b2"},
+ {file = "aiohttp-3.8.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a2f635ce61a89c5732537a7896b6319a8fcfa23ba09bec36e1b1ac0ab31270d2"},
+ {file = "aiohttp-3.8.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e999f2d0e12eea01caeecb17b653f3713d758f6dcc770417cf29ef08d3931421"},
+ {file = "aiohttp-3.8.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0770e2806a30e744b4e21c9d73b7bee18a1cfa3c47991ee2e5a65b887c49d5cf"},
+ {file = "aiohttp-3.8.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d15367ce87c8e9e09b0f989bfd72dc641bcd04ba091c68cd305312d00962addd"},
+ {file = "aiohttp-3.8.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:6c7cefb4b0640703eb1069835c02486669312bf2f12b48a748e0a7756d0de33d"},
+ {file = "aiohttp-3.8.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:71927042ed6365a09a98a6377501af5c9f0a4d38083652bcd2281a06a5976724"},
+ {file = "aiohttp-3.8.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:28d490af82bc6b7ce53ff31337a18a10498303fe66f701ab65ef27e143c3b0ef"},
+ {file = "aiohttp-3.8.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:b6613280ccedf24354406caf785db748bebbddcf31408b20c0b48cb86af76866"},
+ {file = "aiohttp-3.8.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:81e3d8c34c623ca4e36c46524a3530e99c0bc95ed068fd6e9b55cb721d408fb2"},
+ {file = "aiohttp-3.8.1-cp39-cp39-win32.whl", hash = "sha256:7187a76598bdb895af0adbd2fb7474d7f6025d170bc0a1130242da817ce9e7d1"},
+ {file = "aiohttp-3.8.1-cp39-cp39-win_amd64.whl", hash = "sha256:1c182cb873bc91b411e184dab7a2b664d4fea2743df0e4d57402f7f3fa644bac"},
+ {file = "aiohttp-3.8.1.tar.gz", hash = "sha256:fc5471e1a54de15ef71c1bc6ebe80d4dc681ea600e68bfd1cbce40427f0b7578"},
+]
+aiomisc = [
+ {file = "aiomisc-15.6.8-py3-none-any.whl", hash = "sha256:b65f7f8e50d9791bbb745253e814862880b8a35bf21e4eea545ada9daf89836c"},
+ {file = "aiomisc-15.6.8.tar.gz", hash = "sha256:09024d1f544de98b152dad8f7eb7d02c64457aaaaf03bd6c76a20b73328a3a74"},
+]
+aiopg = [
+ {file = "aiopg-1.3.3-py3-none-any.whl", hash = "sha256:2842dd8741460eeef940032dcb577bfba4d4115205dd82a73ce13b3271f5bf0a"},
+ {file = "aiopg-1.3.3.tar.gz", hash = "sha256:547c6ba4ea0d73c2a11a2f44387d7133cc01d3c6f3b8ed976c0ac1eff4f595d7"},
+]
+aiosignal = [
+ {file = "aiosignal-1.2.0-py3-none-any.whl", hash = "sha256:26e62109036cd181df6e6ad646f91f0dcfd05fe16d0cb924138ff2ab75d64e3a"},
+ {file = "aiosignal-1.2.0.tar.gz", hash = "sha256:78ed67db6c7b7ced4f98e495e572106d5c432a93e1ddd1bf475e1dc05f5b7df2"},
+]
+alabaster = [
+ {file = "alabaster-0.7.12-py2.py3-none-any.whl", hash = "sha256:446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359"},
+ {file = "alabaster-0.7.12.tar.gz", hash = "sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02"},
+]
+async-timeout = [
+ {file = "async-timeout-4.0.2.tar.gz", hash = "sha256:2163e1640ddb52b7a8c80d0a67a08587e5d245cc9c553a74a847056bc2976b15"},
+ {file = "async_timeout-4.0.2-py3-none-any.whl", hash = "sha256:8ca1e4fcf50d07413d66d1a5e416e42cfdf5851c981d679a09851a6853383b3c"},
+]
+atomicwrites = [
+ {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"},
+ {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"},
+]
+attrs = [
+ {file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"},
+ {file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"},
+]
+babel = [
+ {file = "Babel-2.9.1-py2.py3-none-any.whl", hash = "sha256:ab49e12b91d937cd11f0b67cb259a57ab4ad2b59ac7a3b41d6c06c0ac5b0def9"},
+ {file = "Babel-2.9.1.tar.gz", hash = "sha256:bc0c176f9f6a994582230df350aa6e05ba2ebe4b3ac317eab29d9be5d2768da0"},
+]
+black = [
+ {file = "black-22.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1297c63b9e1b96a3d0da2d85d11cd9bf8664251fd69ddac068b98dc4f34f73b6"},
+ {file = "black-22.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2ff96450d3ad9ea499fc4c60e425a1439c2120cbbc1ab959ff20f7c76ec7e866"},
+ {file = "black-22.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e21e1f1efa65a50e3960edd068b6ae6d64ad6235bd8bfea116a03b21836af71"},
+ {file = "black-22.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2f69158a7d120fd641d1fa9a921d898e20d52e44a74a6fbbcc570a62a6bc8ab"},
+ {file = "black-22.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:228b5ae2c8e3d6227e4bde5920d2fc66cc3400fde7bcc74f480cb07ef0b570d5"},
+ {file = "black-22.1.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b1a5ed73ab4c482208d20434f700d514f66ffe2840f63a6252ecc43a9bc77e8a"},
+ {file = "black-22.1.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:35944b7100af4a985abfcaa860b06af15590deb1f392f06c8683b4381e8eeaf0"},
+ {file = "black-22.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:7835fee5238fc0a0baf6c9268fb816b5f5cd9b8793423a75e8cd663c48d073ba"},
+ {file = "black-22.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:dae63f2dbf82882fa3b2a3c49c32bffe144970a573cd68d247af6560fc493ae1"},
+ {file = "black-22.1.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fa1db02410b1924b6749c245ab38d30621564e658297484952f3d8a39fce7e8"},
+ {file = "black-22.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:c8226f50b8c34a14608b848dc23a46e5d08397d009446353dad45e04af0c8e28"},
+ {file = "black-22.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:2d6f331c02f0f40aa51a22e479c8209d37fcd520c77721c034517d44eecf5912"},
+ {file = "black-22.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:742ce9af3086e5bd07e58c8feb09dbb2b047b7f566eb5f5bc63fd455814979f3"},
+ {file = "black-22.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:fdb8754b453fb15fad3f72cd9cad3e16776f0964d67cf30ebcbf10327a3777a3"},
+ {file = "black-22.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5660feab44c2e3cb24b2419b998846cbb01c23c7fe645fee45087efa3da2d61"},
+ {file = "black-22.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:6f2f01381f91c1efb1451998bd65a129b3ed6f64f79663a55fe0e9b74a5f81fd"},
+ {file = "black-22.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:efbadd9b52c060a8fc3b9658744091cb33c31f830b3f074422ed27bad2b18e8f"},
+ {file = "black-22.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8871fcb4b447206904932b54b567923e5be802b9b19b744fdff092bd2f3118d0"},
+ {file = "black-22.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ccad888050f5393f0d6029deea2a33e5ae371fd182a697313bdbd835d3edaf9c"},
+ {file = "black-22.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07e5c049442d7ca1a2fc273c79d1aecbbf1bc858f62e8184abe1ad175c4f7cc2"},
+ {file = "black-22.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:373922fc66676133ddc3e754e4509196a8c392fec3f5ca4486673e685a421321"},
+ {file = "black-22.1.0-py3-none-any.whl", hash = "sha256:3524739d76b6b3ed1132422bf9d82123cd1705086723bc3e235ca39fd21c667d"},
+ {file = "black-22.1.0.tar.gz", hash = "sha256:a7c0192d35635f6fc1174be575cb7915e92e5dd629ee79fdaf0dcfa41a80afb5"},
+]
+cached-property = [
+ {file = "cached-property-1.5.2.tar.gz", hash = "sha256:9fa5755838eecbb2d234c3aa390bd80fbd3ac6b6869109bfc1b499f7bd89a130"},
+ {file = "cached_property-1.5.2-py2.py3-none-any.whl", hash = "sha256:df4f613cf7ad9a588cc381aaf4a512d26265ecebd5eb9e1ba12f1319eb85a6a0"},
+]
+certifi = [
+ {file = "certifi-2021.10.8-py2.py3-none-any.whl", hash = "sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569"},
+ {file = "certifi-2021.10.8.tar.gz", hash = "sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872"},
+]
+cfgv = [
+ {file = "cfgv-3.3.1-py2.py3-none-any.whl", hash = "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426"},
+ {file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"},
+]
+charset-normalizer = [
+ {file = "charset-normalizer-2.0.12.tar.gz", hash = "sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597"},
+ {file = "charset_normalizer-2.0.12-py3-none-any.whl", hash = "sha256:6881edbebdb17b39b4eaaa821b438bf6eddffb4468cf344f09f89def34a8b1df"},
+]
+click = [
+ {file = "click-8.0.4-py3-none-any.whl", hash = "sha256:6a7a62563bbfabfda3a38f3023a1db4a35978c0abd76f6c9605ecd6554d6d9b1"},
+ {file = "click-8.0.4.tar.gz", hash = "sha256:8458d7b1287c5fb128c90e23381cf99dcde74beaf6c7ff6384ce84d6fe090adb"},
+]
+colorama = [
+ {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"},
+ {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"},
+]
+colorlog = [
+ {file = "colorlog-6.6.0-py2.py3-none-any.whl", hash = "sha256:351c51e866c86c3217f08e4b067a7974a678be78f07f85fc2d55b8babde6d94e"},
+ {file = "colorlog-6.6.0.tar.gz", hash = "sha256:344f73204009e4c83c5b6beb00b3c45dc70fcdae3c80db919e0a4171d006fde8"},
+]
+coverage = [
+ {file = "coverage-6.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9b27d894748475fa858f9597c0ee1d4829f44683f3813633aaf94b19cb5453cf"},
+ {file = "coverage-6.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:37d1141ad6b2466a7b53a22e08fe76994c2d35a5b6b469590424a9953155afac"},
+ {file = "coverage-6.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f9987b0354b06d4df0f4d3e0ec1ae76d7ce7cbca9a2f98c25041eb79eec766f1"},
+ {file = "coverage-6.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:26e2deacd414fc2f97dd9f7676ee3eaecd299ca751412d89f40bc01557a6b1b4"},
+ {file = "coverage-6.3.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4dd8bafa458b5c7d061540f1ee9f18025a68e2d8471b3e858a9dad47c8d41903"},
+ {file = "coverage-6.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:46191097ebc381fbf89bdce207a6c107ac4ec0890d8d20f3360345ff5976155c"},
+ {file = "coverage-6.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6f89d05e028d274ce4fa1a86887b071ae1755082ef94a6740238cd7a8178804f"},
+ {file = "coverage-6.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:58303469e9a272b4abdb9e302a780072c0633cdcc0165db7eec0f9e32f901e05"},
+ {file = "coverage-6.3.2-cp310-cp310-win32.whl", hash = "sha256:2fea046bfb455510e05be95e879f0e768d45c10c11509e20e06d8fcaa31d9e39"},
+ {file = "coverage-6.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:a2a8b8bcc399edb4347a5ca8b9b87e7524c0967b335fbb08a83c8421489ddee1"},
+ {file = "coverage-6.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:f1555ea6d6da108e1999b2463ea1003fe03f29213e459145e70edbaf3e004aaa"},
+ {file = "coverage-6.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e5f4e1edcf57ce94e5475fe09e5afa3e3145081318e5fd1a43a6b4539a97e518"},
+ {file = "coverage-6.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7a15dc0a14008f1da3d1ebd44bdda3e357dbabdf5a0b5034d38fcde0b5c234b7"},
+ {file = "coverage-6.3.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21b7745788866028adeb1e0eca3bf1101109e2dc58456cb49d2d9b99a8c516e6"},
+ {file = "coverage-6.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:8ce257cac556cb03be4a248d92ed36904a59a4a5ff55a994e92214cde15c5bad"},
+ {file = "coverage-6.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b0be84e5a6209858a1d3e8d1806c46214e867ce1b0fd32e4ea03f4bd8b2e3359"},
+ {file = "coverage-6.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:acf53bc2cf7282ab9b8ba346746afe703474004d9e566ad164c91a7a59f188a4"},
+ {file = "coverage-6.3.2-cp37-cp37m-win32.whl", hash = "sha256:8bdde1177f2311ee552f47ae6e5aa7750c0e3291ca6b75f71f7ffe1f1dab3dca"},
+ {file = "coverage-6.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:b31651d018b23ec463e95cf10070d0b2c548aa950a03d0b559eaa11c7e5a6fa3"},
+ {file = "coverage-6.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:07e6db90cd9686c767dcc593dff16c8c09f9814f5e9c51034066cad3373b914d"},
+ {file = "coverage-6.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2c6dbb42f3ad25760010c45191e9757e7dce981cbfb90e42feef301d71540059"},
+ {file = "coverage-6.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c76aeef1b95aff3905fb2ae2d96e319caca5b76fa41d3470b19d4e4a3a313512"},
+ {file = "coverage-6.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8cf5cfcb1521dc3255d845d9dca3ff204b3229401994ef8d1984b32746bb45ca"},
+ {file = "coverage-6.3.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8fbbdc8d55990eac1b0919ca69eb5a988a802b854488c34b8f37f3e2025fa90d"},
+ {file = "coverage-6.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ec6bc7fe73a938933d4178c9b23c4e0568e43e220aef9472c4f6044bfc6dd0f0"},
+ {file = "coverage-6.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:9baff2a45ae1f17c8078452e9e5962e518eab705e50a0aa8083733ea7d45f3a6"},
+ {file = "coverage-6.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fd9e830e9d8d89b20ab1e5af09b32d33e1a08ef4c4e14411e559556fd788e6b2"},
+ {file = "coverage-6.3.2-cp38-cp38-win32.whl", hash = "sha256:f7331dbf301b7289013175087636bbaf5b2405e57259dd2c42fdcc9fcc47325e"},
+ {file = "coverage-6.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:68353fe7cdf91f109fc7d474461b46e7f1f14e533e911a2a2cbb8b0fc8613cf1"},
+ {file = "coverage-6.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b78e5afb39941572209f71866aa0b206c12f0109835aa0d601e41552f9b3e620"},
+ {file = "coverage-6.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4e21876082ed887baed0146fe222f861b5815455ada3b33b890f4105d806128d"},
+ {file = "coverage-6.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34626a7eee2a3da12af0507780bb51eb52dca0e1751fd1471d0810539cefb536"},
+ {file = "coverage-6.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1ebf730d2381158ecf3dfd4453fbca0613e16eaa547b4170e2450c9707665ce7"},
+ {file = "coverage-6.3.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd6fe30bd519694b356cbfcaca9bd5c1737cddd20778c6a581ae20dc8c04def2"},
+ {file = "coverage-6.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:96f8a1cb43ca1422f36492bebe63312d396491a9165ed3b9231e778d43a7fca4"},
+ {file = "coverage-6.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:dd035edafefee4d573140a76fdc785dc38829fe5a455c4bb12bac8c20cfc3d69"},
+ {file = "coverage-6.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5ca5aeb4344b30d0bec47481536b8ba1181d50dbe783b0e4ad03c95dc1296684"},
+ {file = "coverage-6.3.2-cp39-cp39-win32.whl", hash = "sha256:f5fa5803f47e095d7ad8443d28b01d48c0359484fec1b9d8606d0e3282084bc4"},
+ {file = "coverage-6.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:9548f10d8be799551eb3a9c74bbf2b4934ddb330e08a73320123c07f95cc2d92"},
+ {file = "coverage-6.3.2-pp36.pp37.pp38-none-any.whl", hash = "sha256:18d520c6860515a771708937d2f78f63cc47ab3b80cb78e86573b0a760161faf"},
+ {file = "coverage-6.3.2.tar.gz", hash = "sha256:03e2a7826086b91ef345ff18742ee9fc47a6839ccd517061ef8fa1976e652ce9"},
+]
+crontab = [
+ {file = "crontab-0.23.0.tar.gz", hash = "sha256:ca79dede9c2f572bb32f38703e8fddcf3427e86edc838f2ffe7ae4b9ee2b0733"},
+]
+dependency-injector = [
+ {file = "dependency-injector-4.38.0.tar.gz", hash = "sha256:bab4c323d822d3fc9936e8eb3c2f5553d75e9efdadac11d5b293a016e31a1477"},
+ {file = "dependency_injector-4.38.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:025eb5f97021663715bff8e01feb83d5b2f66fc17ece1042a194f1aae88c0d85"},
+ {file = "dependency_injector-4.38.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3c61334b196ab767eae43af207764349287d5eb0283d8ed1ab24608121aea35"},
+ {file = "dependency_injector-4.38.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9bf4bfc52015e81c4f17647b7bbbe4e4549f041b3c6635b44a9ecede7594932c"},
+ {file = "dependency_injector-4.38.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7aeba5882b06baf78978f33228e4c44133dada9173e330da68fbccca98520848"},
+ {file = "dependency_injector-4.38.0-cp310-cp310-win32.whl", hash = "sha256:445dbf5324eee215a465d7f3b1965b05e199c31caa09e63abf0f02d982791306"},
+ {file = "dependency_injector-4.38.0-cp310-cp310-win_amd64.whl", hash = "sha256:880edbcb5d810faa0623112e2e652a7bec73d20ce0708d9db998a0a40b62cbb9"},
+ {file = "dependency_injector-4.38.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:cd6f1c130462e7461a43f82fdc0d2ba25b5ef594db823dbd0e57f3d1b7c44f86"},
+ {file = "dependency_injector-4.38.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0be75905f7513886699d3ff5ed9aca233175c03808fc888da2a53b83af0a5d65"},
+ {file = "dependency_injector-4.38.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:8d7f1a7d27de6295ce8b7ca69d1177817bf36c7cbaf73819e4bab04f87c5e962"},
+ {file = "dependency_injector-4.38.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e9c1ff387d7b7d814e9d29a531c6804acc67b1cca578c09abd3590fa7ec6bf06"},
+ {file = "dependency_injector-4.38.0-cp36-cp36m-win32.whl", hash = "sha256:7770efcbc6915dabbb31ea0bdeee1105dabf76e1c8e31a454cb1983dcf19ecf1"},
+ {file = "dependency_injector-4.38.0-cp36-cp36m-win_amd64.whl", hash = "sha256:9e89a9c88317ad237abfb374c410e1466476ffefe6c44f3caeca3dce747a635c"},
+ {file = "dependency_injector-4.38.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cb75cd29c132bfaf03a11a6ac4f459dddb7a6526669543de57d2bb5fddf78def"},
+ {file = "dependency_injector-4.38.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d116c56e7fc3b59b3afa6078163b5f6ff4680ebf27800dd4032de7a04f7ef777"},
+ {file = "dependency_injector-4.38.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9df330ef9e24b6f94e6764d23180350c2fb99785257233ee4738e066b876fa7f"},
+ {file = "dependency_injector-4.38.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7a46639038313f64eca2dc858ac4cd9e0aca5ea21bb005f5d754aadd6446ba4b"},
+ {file = "dependency_injector-4.38.0-cp37-cp37m-win32.whl", hash = "sha256:d0d983b269b657744058b5858afc3c59443db53afe9c3e709e316daa9f9b9454"},
+ {file = "dependency_injector-4.38.0-cp37-cp37m-win_amd64.whl", hash = "sha256:1446df58169c166a5a2d5644ba9eeb3b7f2f679f260596f78d012c58ff140d92"},
+ {file = "dependency_injector-4.38.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:307396f2d9d2f532fe92079a56d40af5fc60dacb1d766e3f9cd04c3802a1c251"},
+ {file = "dependency_injector-4.38.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8dd96b6c454ab86648f57f37b895c1c976b1a82b76f835011f607ee8a78ebc0e"},
+ {file = "dependency_injector-4.38.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:fb44b17e649cabcfd1f370ecfd492ac7a93216776d91954b31598eecb36cdb13"},
+ {file = "dependency_injector-4.38.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ab8ebe728dd9b3be23c40ca5a5dbe195050d9ad35d42d7e9fdaea816f7134584"},
+ {file = "dependency_injector-4.38.0-cp38-cp38-win32.whl", hash = "sha256:1da3bad1452212bab76e87fbf7a71d3675190a1a909f345aaf8bae2fa97b878f"},
+ {file = "dependency_injector-4.38.0-cp38-cp38-win_amd64.whl", hash = "sha256:2918776e88de88be0e2be036261180ca0605c8f64ead43d835ce852f16a9efd2"},
+ {file = "dependency_injector-4.38.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:493be62561218624380d4eca9243a46753444f677217db6292a7b715cf429172"},
+ {file = "dependency_injector-4.38.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a70511db88f84ac4228b27e37e12ea0e04a9c2a32cae3602b9af9a27c0db992"},
+ {file = "dependency_injector-4.38.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5e3e1cfe41a5ada0ff71889563441f538caff0399e41d3ee377b60fa50a858bf"},
+ {file = "dependency_injector-4.38.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:116cc679a2c6d40c6a4f968aefe68535e596e88e96315dbd0d0ad2ff76654e3d"},
+ {file = "dependency_injector-4.38.0-cp39-cp39-win32.whl", hash = "sha256:f703c2c161de067ba9893b56743d24fb4c9dbff92eb504bc082c2d2cfeab4c01"},
+ {file = "dependency_injector-4.38.0-cp39-cp39-win_amd64.whl", hash = "sha256:6821a864d6405dc0d5f794ac1b10da4a8c7e8731c6a0651a9682a0b76f5a5b3e"},
+ {file = "dependency_injector-4.38.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:6c9e8dc91aa5831bd3a58fec1b94ed8c52f78f601f5ab91135b5ad44325beef7"},
+ {file = "dependency_injector-4.38.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2b4eedadef0c84295b70803a79a3ce5a10a59dddd8f306876f6fa6bfc4de8e00"},
+ {file = "dependency_injector-4.38.0-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2e88b5d09c80e20d6b3d985cc360f39a81e748667c20f6bc7eee9f4b832176ed"},
+ {file = "dependency_injector-4.38.0-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b1fcd71ff46e097f4e47917ccdf6aa388b6a6372557f7d9f83db1e879e95f8bb"},
+ {file = "dependency_injector-4.38.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:9633d6366282e83a3f21543c5c78299787948333d9fe6649b020cfac93d8b7ca"},
+]
+distlib = [
+ {file = "distlib-0.3.4-py2.py3-none-any.whl", hash = "sha256:6564fe0a8f51e734df6333d08b8b94d4ea8ee6b99b5ed50613f731fd4089f34b"},
+ {file = "distlib-0.3.4.zip", hash = "sha256:e4b58818180336dc9c529bfb9a0b58728ffc09ad92027a3f30b7cd91e3458579"},
+]
+docutils = [
+ {file = "docutils-0.17.1-py2.py3-none-any.whl", hash = "sha256:cf316c8370a737a022b72b56874f6602acf974a37a9fba42ec2876387549fc61"},
+ {file = "docutils-0.17.1.tar.gz", hash = "sha256:686577d2e4c32380bb50cbb22f575ed742d58168cee37e99117a854bcd88f125"},
+]
+fastavro = [
+ {file = "fastavro-1.4.9-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:2e64a77c529b638e89a879ff0211debfab5b2d114c26a2af29c81f6b013f395a"},
+ {file = "fastavro-1.4.9-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1fc9c95b7c1d59c5a2d29be21075870a122152cf927d84587dafc96da6b2ac3d"},
+ {file = "fastavro-1.4.9-cp310-cp310-win_amd64.whl", hash = "sha256:927fd6148a8dd9646c129c0a0e8571aea829abc3cba04a3d5a4010a866934f4c"},
+ {file = "fastavro-1.4.9-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:000b70c5109a61bdbfddeb2821a506de8f5333f243c608cbced61d44657d6c2f"},
+ {file = "fastavro-1.4.9-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:37a77a1b5347a06416e236c77027c750aaeda29ef8189aa456eb2a2571274b43"},
+ {file = "fastavro-1.4.9-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce2c7747ce958115388872db0756d3eeb0d796084eea9b46dc3758ef32c4d952"},
+ {file = "fastavro-1.4.9-cp37-cp37m-win_amd64.whl", hash = "sha256:d6ccb77604903a0308316e696bb65a8943361af5f757d10985689656c9bce6ed"},
+ {file = "fastavro-1.4.9-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:9a6ada2d6e133a2319438248c2e023b6735747b249c5a79d5f08f9d431e5d226"},
+ {file = "fastavro-1.4.9-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7537e4df7782b03b9761e9338cef9fc7bfcc41100ab93c36c5c60fa568e724a"},
+ {file = "fastavro-1.4.9-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6a9cd6d8471beb4020b4126fd04150ed7295f74ae7234d0dc9205b55c193851e"},
+ {file = "fastavro-1.4.9-cp38-cp38-win_amd64.whl", hash = "sha256:fa9d8b47e0533c84152332ad491bb63bbae76a8a7a0df1caa821e0cbebf0fb70"},
+ {file = "fastavro-1.4.9-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:3759bdc77009ee1e2e76fc9f58b951c05c00a8600ef9ddbff59fee3cb0c9e235"},
+ {file = "fastavro-1.4.9-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b98ef2bdb123b95945aa6d69d6a7d79f211df3274b2dd7786da7852ddec964d0"},
+ {file = "fastavro-1.4.9-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32b804aa6920d80c0e94e1180d480f28f56c4b901849bd80ed180851752b5ce6"},
+ {file = "fastavro-1.4.9-cp39-cp39-win_amd64.whl", hash = "sha256:f9b04acaf06b16218b47985e92d8daa98c1116d58f3cff81a5b3cf39cef9afc0"},
+ {file = "fastavro-1.4.9.tar.gz", hash = "sha256:be3fec387eb2cdc9627060b5ae0690542c687dddc951b63fa11203553769ae5e"},
+]
+filelock = [
+ {file = "filelock-3.6.0-py3-none-any.whl", hash = "sha256:f8314284bfffbdcfa0ff3d7992b023d4c628ced6feb957351d4c48d059f56bc0"},
+ {file = "filelock-3.6.0.tar.gz", hash = "sha256:9cd540a9352e432c7246a48fe4e8712b10acb1df2ad1f30e8c070b82ae1fed85"},
+]
+flake8 = [
+ {file = "flake8-4.0.1-py2.py3-none-any.whl", hash = "sha256:479b1304f72536a55948cb40a32dce8bb0ffe3501e26eaf292c7e60eb5e0428d"},
+ {file = "flake8-4.0.1.tar.gz", hash = "sha256:806e034dda44114815e23c16ef92f95c91e4c71100ff52813adf7132a6ad870d"},
+]
+frozenlist = [
+ {file = "frozenlist-1.3.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d2257aaba9660f78c7b1d8fea963b68f3feffb1a9d5d05a18401ca9eb3e8d0a3"},
+ {file = "frozenlist-1.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4a44ebbf601d7bac77976d429e9bdb5a4614f9f4027777f9e54fd765196e9d3b"},
+ {file = "frozenlist-1.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:45334234ec30fc4ea677f43171b18a27505bfb2dba9aca4398a62692c0ea8868"},
+ {file = "frozenlist-1.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47be22dc27ed933d55ee55845d34a3e4e9f6fee93039e7f8ebadb0c2f60d403f"},
+ {file = "frozenlist-1.3.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:03a7dd1bfce30216a3f51a84e6dd0e4a573d23ca50f0346634916ff105ba6e6b"},
+ {file = "frozenlist-1.3.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:691ddf6dc50480ce49f68441f1d16a4c3325887453837036e0fb94736eae1e58"},
+ {file = "frozenlist-1.3.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bde99812f237f79eaf3f04ebffd74f6718bbd216101b35ac7955c2d47c17da02"},
+ {file = "frozenlist-1.3.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6a202458d1298ced3768f5a7d44301e7c86defac162ace0ab7434c2e961166e8"},
+ {file = "frozenlist-1.3.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b9e3e9e365991f8cc5f5edc1fd65b58b41d0514a6a7ad95ef5c7f34eb49b3d3e"},
+ {file = "frozenlist-1.3.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:04cb491c4b1c051734d41ea2552fde292f5f3a9c911363f74f39c23659c4af78"},
+ {file = "frozenlist-1.3.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:436496321dad302b8b27ca955364a439ed1f0999311c393dccb243e451ff66aa"},
+ {file = "frozenlist-1.3.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:754728d65f1acc61e0f4df784456106e35afb7bf39cfe37227ab00436fb38676"},
+ {file = "frozenlist-1.3.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6eb275c6385dd72594758cbe96c07cdb9bd6becf84235f4a594bdf21e3596c9d"},
+ {file = "frozenlist-1.3.0-cp310-cp310-win32.whl", hash = "sha256:e30b2f9683812eb30cf3f0a8e9f79f8d590a7999f731cf39f9105a7c4a39489d"},
+ {file = "frozenlist-1.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:f7353ba3367473d1d616ee727945f439e027f0bb16ac1a750219a8344d1d5d3c"},
+ {file = "frozenlist-1.3.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:88aafd445a233dbbf8a65a62bc3249a0acd0d81ab18f6feb461cc5a938610d24"},
+ {file = "frozenlist-1.3.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4406cfabef8f07b3b3af0f50f70938ec06d9f0fc26cbdeaab431cbc3ca3caeaa"},
+ {file = "frozenlist-1.3.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8cf829bd2e2956066dd4de43fd8ec881d87842a06708c035b37ef632930505a2"},
+ {file = "frozenlist-1.3.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:603b9091bd70fae7be28bdb8aa5c9990f4241aa33abb673390a7f7329296695f"},
+ {file = "frozenlist-1.3.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:25af28b560e0c76fa41f550eacb389905633e7ac02d6eb3c09017fa1c8cdfde1"},
+ {file = "frozenlist-1.3.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94c7a8a9fc9383b52c410a2ec952521906d355d18fccc927fca52ab575ee8b93"},
+ {file = "frozenlist-1.3.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:65bc6e2fece04e2145ab6e3c47428d1bbc05aede61ae365b2c1bddd94906e478"},
+ {file = "frozenlist-1.3.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:3f7c935c7b58b0d78c0beea0c7358e165f95f1fd8a7e98baa40d22a05b4a8141"},
+ {file = "frozenlist-1.3.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd89acd1b8bb4f31b47072615d72e7f53a948d302b7c1d1455e42622de180eae"},
+ {file = "frozenlist-1.3.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:6983a31698490825171be44ffbafeaa930ddf590d3f051e397143a5045513b01"},
+ {file = "frozenlist-1.3.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:adac9700675cf99e3615eb6a0eb5e9f5a4143c7d42c05cea2e7f71c27a3d0846"},
+ {file = "frozenlist-1.3.0-cp37-cp37m-win32.whl", hash = "sha256:0c36e78b9509e97042ef869c0e1e6ef6429e55817c12d78245eb915e1cca7468"},
+ {file = "frozenlist-1.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:57f4d3f03a18facacb2a6bcd21bccd011e3b75d463dc49f838fd699d074fabd1"},
+ {file = "frozenlist-1.3.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:8c905a5186d77111f02144fab5b849ab524f1e876a1e75205cd1386a9be4b00a"},
+ {file = "frozenlist-1.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b5009062d78a8c6890d50b4e53b0ddda31841b3935c1937e2ed8c1bda1c7fb9d"},
+ {file = "frozenlist-1.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2fdc3cd845e5a1f71a0c3518528bfdbfe2efaf9886d6f49eacc5ee4fd9a10953"},
+ {file = "frozenlist-1.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:92e650bd09b5dda929523b9f8e7f99b24deac61240ecc1a32aeba487afcd970f"},
+ {file = "frozenlist-1.3.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:40dff8962b8eba91fd3848d857203f0bd704b5f1fa2b3fc9af64901a190bba08"},
+ {file = "frozenlist-1.3.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:768efd082074bb203c934e83a61654ed4931ef02412c2fbdecea0cff7ecd0274"},
+ {file = "frozenlist-1.3.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:006d3595e7d4108a12025ddf415ae0f6c9e736e726a5db0183326fd191b14c5e"},
+ {file = "frozenlist-1.3.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:871d42623ae15eb0b0e9df65baeee6976b2e161d0ba93155411d58ff27483ad8"},
+ {file = "frozenlist-1.3.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:aff388be97ef2677ae185e72dc500d19ecaf31b698986800d3fc4f399a5e30a5"},
+ {file = "frozenlist-1.3.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:9f892d6a94ec5c7b785e548e42722e6f3a52f5f32a8461e82ac3e67a3bd073f1"},
+ {file = "frozenlist-1.3.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:e982878792c971cbd60ee510c4ee5bf089a8246226dea1f2138aa0bb67aff148"},
+ {file = "frozenlist-1.3.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:c6c321dd013e8fc20735b92cb4892c115f5cdb82c817b1e5b07f6b95d952b2f0"},
+ {file = "frozenlist-1.3.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:30530930410855c451bea83f7b272fb1c495ed9d5cc72895ac29e91279401db3"},
+ {file = "frozenlist-1.3.0-cp38-cp38-win32.whl", hash = "sha256:40ec383bc194accba825fbb7d0ef3dda5736ceab2375462f1d8672d9f6b68d07"},
+ {file = "frozenlist-1.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:f20baa05eaa2bcd5404c445ec51aed1c268d62600362dc6cfe04fae34a424bd9"},
+ {file = "frozenlist-1.3.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:0437fe763fb5d4adad1756050cbf855bbb2bf0d9385c7bb13d7a10b0dd550486"},
+ {file = "frozenlist-1.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b684c68077b84522b5c7eafc1dc735bfa5b341fb011d5552ebe0968e22ed641c"},
+ {file = "frozenlist-1.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:93641a51f89473837333b2f8100f3f89795295b858cd4c7d4a1f18e299dc0a4f"},
+ {file = "frozenlist-1.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d6d32ff213aef0fd0bcf803bffe15cfa2d4fde237d1d4838e62aec242a8362fa"},
+ {file = "frozenlist-1.3.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:31977f84828b5bb856ca1eb07bf7e3a34f33a5cddce981d880240ba06639b94d"},
+ {file = "frozenlist-1.3.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3c62964192a1c0c30b49f403495911298810bada64e4f03249ca35a33ca0417a"},
+ {file = "frozenlist-1.3.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4eda49bea3602812518765810af732229b4291d2695ed24a0a20e098c45a707b"},
+ {file = "frozenlist-1.3.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:acb267b09a509c1df5a4ca04140da96016f40d2ed183cdc356d237286c971b51"},
+ {file = "frozenlist-1.3.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e1e26ac0a253a2907d654a37e390904426d5ae5483150ce3adedb35c8c06614a"},
+ {file = "frozenlist-1.3.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f96293d6f982c58ebebb428c50163d010c2f05de0cde99fd681bfdc18d4b2dc2"},
+ {file = "frozenlist-1.3.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:e84cb61b0ac40a0c3e0e8b79c575161c5300d1d89e13c0e02f76193982f066ed"},
+ {file = "frozenlist-1.3.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:ff9310f05b9d9c5c4dd472983dc956901ee6cb2c3ec1ab116ecdde25f3ce4951"},
+ {file = "frozenlist-1.3.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d26b650b71fdc88065b7a21f8ace70175bcf3b5bdba5ea22df4bfd893e795a3b"},
+ {file = "frozenlist-1.3.0-cp39-cp39-win32.whl", hash = "sha256:01a73627448b1f2145bddb6e6c2259988bb8aee0fb361776ff8604b99616cd08"},
+ {file = "frozenlist-1.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:772965f773757a6026dea111a15e6e2678fbd6216180f82a48a40b27de1ee2ab"},
+ {file = "frozenlist-1.3.0.tar.gz", hash = "sha256:ce6f2ba0edb7b0c1d8976565298ad2deba6f8064d2bebb6ffce2ca896eb35b0b"},
+]
+graphql-core = [
+ {file = "graphql-core-3.2.0.tar.gz", hash = "sha256:86e2a0be008bfde19ef78388de8a725a1d942a9190ca431c24a60837973803ce"},
+ {file = "graphql_core-3.2.0-py3-none-any.whl", hash = "sha256:0dda7e63676f119bb3d814621190fedad72fda07a8e9ab780bedd9f1957c6dc6"},
+]
+graphql-server = [
+ {file = "graphql-server-3.0.0b5.tar.gz", hash = "sha256:85c12c421e14f339883865d75fe316f626f3f875c70f8c366a93d50c5b1c8a23"},
+]
+identify = [
+ {file = "identify-2.4.11-py2.py3-none-any.whl", hash = "sha256:fd906823ed1db23c7a48f9b176a1d71cb8abede1e21ebe614bac7bdd688d9213"},
+ {file = "identify-2.4.11.tar.gz", hash = "sha256:2986942d3974c8f2e5019a190523b0b0e2a07cb8e89bf236727fb4b26f27f8fd"},
+]
+idna = [
+ {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"},
+ {file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"},
+]
+imagesize = [
+ {file = "imagesize-1.3.0-py2.py3-none-any.whl", hash = "sha256:1db2f82529e53c3e929e8926a1fa9235aa82d0bd0c580359c67ec31b2fddaa8c"},
+ {file = "imagesize-1.3.0.tar.gz", hash = "sha256:cd1750d452385ca327479d45b64d9c7729ecf0b3969a58148298c77092261f9d"},
+]
+importlib-metadata = [
+ {file = "importlib_metadata-4.11.2-py3-none-any.whl", hash = "sha256:d16e8c1deb60de41b8e8ed21c1a7b947b0bc62fab7e1d470bcdf331cea2e6735"},
+ {file = "importlib_metadata-4.11.2.tar.gz", hash = "sha256:b36ffa925fe3139b2f6ff11d6925ffd4fa7bc47870165e3ac260ac7b4f91e6ac"},
+]
+iniconfig = [
+ {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"},
+ {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"},
+]
+isort = [
+ {file = "isort-5.10.1-py3-none-any.whl", hash = "sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7"},
+ {file = "isort-5.10.1.tar.gz", hash = "sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951"},
+]
+jinja2 = [
+ {file = "Jinja2-3.0.3-py3-none-any.whl", hash = "sha256:077ce6014f7b40d03b47d1f1ca4b0fc8328a692bd284016f806ed0eaca390ad8"},
+ {file = "Jinja2-3.0.3.tar.gz", hash = "sha256:611bb273cd68f3b993fabdc4064fc858c5b47a973cb5aa7999ec1ba405c87cd7"},
+]
+lmdb = [
+ {file = "lmdb-1.3.0-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:63cb73fe7ce9eb93d992d632c85a0476b4332670d9e6a2802b5062f603b7809f"},
+ {file = "lmdb-1.3.0-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:abbc439cd9fe60ffd6197009087ea885ac150017dc85384093b1d376f83f0ec4"},
+ {file = "lmdb-1.3.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6260a526e4ad85b1f374a5ba9475bf369fb07e7728ea6ec57226b02c40d1976b"},
+ {file = "lmdb-1.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e568ae0887ae196340947d9800136e90feaed6b86a261ef01f01b2ba65fc8106"},
+ {file = "lmdb-1.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:d6a816954d212f40fd15007cd81ab7a6bebb77436d949a6a9ae04af57fc127f3"},
+ {file = "lmdb-1.3.0-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:fa6439356e591d3249ab0e1778a6f8d8408e993f66dc911914c78208f5310309"},
+ {file = "lmdb-1.3.0-cp35-cp35m-win_amd64.whl", hash = "sha256:c6adbd6f7f9048e97f31a069e652eb51020a81e80a0ce92dbb9810d21da2409a"},
+ {file = "lmdb-1.3.0-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:eefb392f6b5cd43aada49258c5a79be11cb2c8cd3fc3e2d9319a1e0b9f906458"},
+ {file = "lmdb-1.3.0-cp36-cp36m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5a14aca2651c3af6f0d0a6b9168200eea0c8f2d27c40b01a442f33329a6e8dff"},
+ {file = "lmdb-1.3.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cfa4aa9c67f8aee89b23005e98d1f3f32490b6b905fd1cb604b207cbd5755ab"},
+ {file = "lmdb-1.3.0-cp36-cp36m-win_amd64.whl", hash = "sha256:7da05d70fcc6561ac6b09e9fb1bf64b7ca294652c64c8a2889273970cee796b9"},
+ {file = "lmdb-1.3.0-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:008243762decf8f6c90430a9bced56290ebbcdb5e877d90e42343bb97033e494"},
+ {file = "lmdb-1.3.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:17215a42a4b9814c383deabecb160581e4fb75d00198eef0e3cea54f230ffbea"},
+ {file = "lmdb-1.3.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:65334eafa5d430b18d81ebd5362559a41483c362e1931f6e1b15bab2ecb7d75d"},
+ {file = "lmdb-1.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:18c69fabdaf04efaf246587739cc1062b3e57c6ef0743f5c418df89e5e7e7b9b"},
+ {file = "lmdb-1.3.0-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:41318717ab5d15ad2d6d263d34fbf614a045210f64b25e59ce734bb2105e421f"},
+ {file = "lmdb-1.3.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:df2724bad7820114a205472994091097d0fa65a3e5fff5a8e688d123fb8c6326"},
+ {file = "lmdb-1.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ddd590e1c7fcb395931aa3782fb89b9db4550ab2d81d006ecd239e0d462bc41"},
+ {file = "lmdb-1.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:4172fba19417d7b29409beca7d73c067b54e5d8ab1fb9b51d7b4c1445d20a167"},
+ {file = "lmdb-1.3.0-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:2df38115dd9428a54d59ae7c712a4c7cce0d6b1d66056de4b1a8c38718066106"},
+ {file = "lmdb-1.3.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d9103aa4908f0bca43c5911ca067d4e3d01f682dff0c0381a1239bd2bd757984"},
+ {file = "lmdb-1.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:394df860c3f93cfd92b6f4caba785f38208cc9614c18b3803f83a2cc1695042f"},
+ {file = "lmdb-1.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:62ab28e3593bdc318ea2f2fa1574e5fca3b6d1f264686d773ba54a637d4f563b"},
+ {file = "lmdb-1.3.0-pp27-pypy_73-macosx_10_7_x86_64.whl", hash = "sha256:e6a704b3baced9182836c7f77b769f23856f3a8f62d0282b1bc1feaf81a86712"},
+ {file = "lmdb-1.3.0-pp27-pypy_73-win_amd64.whl", hash = "sha256:08f4b5129f4683802569b02581142e415c8dcc0ff07605983ec1b07804cecbad"},
+ {file = "lmdb-1.3.0-pp36-pypy36_pp73-win32.whl", hash = "sha256:f291e3f561f58dddf63a92a5a6a4b8af3a0920b6705d35e2f80e52e86ee238a2"},
+ {file = "lmdb-1.3.0.tar.gz", hash = "sha256:60a11efc21aaf009d06518996360eed346f6000bfc9de05114374230879f992e"},
+]
+m2r2 = [
+ {file = "m2r2-0.3.2-py3-none-any.whl", hash = "sha256:d3684086b61b4bebe2307f15189495360f05a123c9bda2a66462649b7ca236aa"},
+ {file = "m2r2-0.3.2.tar.gz", hash = "sha256:ccd95b052dcd1ac7442ecb3111262b2001c10e4119b459c34c93ac7a5c2c7868"},
+]
+markupsafe = [
+ {file = "MarkupSafe-2.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3028252424c72b2602a323f70fbf50aa80a5d3aa616ea6add4ba21ae9cc9da4c"},
+ {file = "MarkupSafe-2.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:290b02bab3c9e216da57c1d11d2ba73a9f73a614bbdcc027d299a60cdfabb11a"},
+ {file = "MarkupSafe-2.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6e104c0c2b4cd765b4e83909cde7ec61a1e313f8a75775897db321450e928cce"},
+ {file = "MarkupSafe-2.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24c3be29abb6b34052fd26fc7a8e0a49b1ee9d282e3665e8ad09a0a68faee5b3"},
+ {file = "MarkupSafe-2.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:204730fd5fe2fe3b1e9ccadb2bd18ba8712b111dcabce185af0b3b5285a7c989"},
+ {file = "MarkupSafe-2.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d3b64c65328cb4cd252c94f83e66e3d7acf8891e60ebf588d7b493a55a1dbf26"},
+ {file = "MarkupSafe-2.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:96de1932237abe0a13ba68b63e94113678c379dca45afa040a17b6e1ad7ed076"},
+ {file = "MarkupSafe-2.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:75bb36f134883fdbe13d8e63b8675f5f12b80bb6627f7714c7d6c5becf22719f"},
+ {file = "MarkupSafe-2.1.0-cp310-cp310-win32.whl", hash = "sha256:4056f752015dfa9828dce3140dbadd543b555afb3252507348c493def166d454"},
+ {file = "MarkupSafe-2.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:d4e702eea4a2903441f2735799d217f4ac1b55f7d8ad96ab7d4e25417cb0827c"},
+ {file = "MarkupSafe-2.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:f0eddfcabd6936558ec020130f932d479930581171368fd728efcfb6ef0dd357"},
+ {file = "MarkupSafe-2.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ddea4c352a488b5e1069069f2f501006b1a4362cb906bee9a193ef1245a7a61"},
+ {file = "MarkupSafe-2.1.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:09c86c9643cceb1d87ca08cdc30160d1b7ab49a8a21564868921959bd16441b8"},
+ {file = "MarkupSafe-2.1.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a0a0abef2ca47b33fb615b491ce31b055ef2430de52c5b3fb19a4042dbc5cadb"},
+ {file = "MarkupSafe-2.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:736895a020e31b428b3382a7887bfea96102c529530299f426bf2e636aacec9e"},
+ {file = "MarkupSafe-2.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:679cbb78914ab212c49c67ba2c7396dc599a8479de51b9a87b174700abd9ea49"},
+ {file = "MarkupSafe-2.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:84ad5e29bf8bab3ad70fd707d3c05524862bddc54dc040982b0dbcff36481de7"},
+ {file = "MarkupSafe-2.1.0-cp37-cp37m-win32.whl", hash = "sha256:8da5924cb1f9064589767b0f3fc39d03e3d0fb5aa29e0cb21d43106519bd624a"},
+ {file = "MarkupSafe-2.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:454ffc1cbb75227d15667c09f164a0099159da0c1f3d2636aa648f12675491ad"},
+ {file = "MarkupSafe-2.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:142119fb14a1ef6d758912b25c4e803c3ff66920635c44078666fe7cc3f8f759"},
+ {file = "MarkupSafe-2.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b2a5a856019d2833c56a3dcac1b80fe795c95f401818ea963594b345929dffa7"},
+ {file = "MarkupSafe-2.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d1fb9b2eec3c9714dd936860850300b51dbaa37404209c8d4cb66547884b7ed"},
+ {file = "MarkupSafe-2.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:62c0285e91414f5c8f621a17b69fc0088394ccdaa961ef469e833dbff64bd5ea"},
+ {file = "MarkupSafe-2.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fc3150f85e2dbcf99e65238c842d1cfe69d3e7649b19864c1cc043213d9cd730"},
+ {file = "MarkupSafe-2.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f02cf7221d5cd915d7fa58ab64f7ee6dd0f6cddbb48683debf5d04ae9b1c2cc1"},
+ {file = "MarkupSafe-2.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:d5653619b3eb5cbd35bfba3c12d575db2a74d15e0e1c08bf1db788069d410ce8"},
+ {file = "MarkupSafe-2.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:7d2f5d97fcbd004c03df8d8fe2b973fe2b14e7bfeb2cfa012eaa8759ce9a762f"},
+ {file = "MarkupSafe-2.1.0-cp38-cp38-win32.whl", hash = "sha256:3cace1837bc84e63b3fd2dfce37f08f8c18aeb81ef5cf6bb9b51f625cb4e6cd8"},
+ {file = "MarkupSafe-2.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:fabbe18087c3d33c5824cb145ffca52eccd053061df1d79d4b66dafa5ad2a5ea"},
+ {file = "MarkupSafe-2.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:023af8c54fe63530545f70dd2a2a7eed18d07a9a77b94e8bf1e2ff7f252db9a3"},
+ {file = "MarkupSafe-2.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d66624f04de4af8bbf1c7f21cc06649c1c69a7f84109179add573ce35e46d448"},
+ {file = "MarkupSafe-2.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c532d5ab79be0199fa2658e24a02fce8542df196e60665dd322409a03db6a52c"},
+ {file = "MarkupSafe-2.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e67ec74fada3841b8c5f4c4f197bea916025cb9aa3fe5abf7d52b655d042f956"},
+ {file = "MarkupSafe-2.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30c653fde75a6e5eb814d2a0a89378f83d1d3f502ab710904ee585c38888816c"},
+ {file = "MarkupSafe-2.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:961eb86e5be7d0973789f30ebcf6caab60b844203f4396ece27310295a6082c7"},
+ {file = "MarkupSafe-2.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:598b65d74615c021423bd45c2bc5e9b59539c875a9bdb7e5f2a6b92dfcfc268d"},
+ {file = "MarkupSafe-2.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:599941da468f2cf22bf90a84f6e2a65524e87be2fce844f96f2dd9a6c9d1e635"},
+ {file = "MarkupSafe-2.1.0-cp39-cp39-win32.whl", hash = "sha256:e6f7f3f41faffaea6596da86ecc2389672fa949bd035251eab26dc6697451d05"},
+ {file = "MarkupSafe-2.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:b8811d48078d1cf2a6863dafb896e68406c5f513048451cd2ded0473133473c7"},
+ {file = "MarkupSafe-2.1.0.tar.gz", hash = "sha256:80beaf63ddfbc64a0452b841d8036ca0611e049650e20afcb882f5d3c266d65f"},
+]
+mccabe = [
+ {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"},
+ {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"},
+]
+minos-microservice-common = []
+minos-microservice-networks = []
+mistune = [
+ {file = "mistune-0.8.4-py2.py3-none-any.whl", hash = "sha256:88a1051873018da288eee8538d476dffe1262495144b33ecb586c4ab266bb8d4"},
+ {file = "mistune-0.8.4.tar.gz", hash = "sha256:59a3429db53c50b5c6bcc8a07f8848cb00d7dc8bdb431a4ab41920d201d4756e"},
+]
+multidict = [
+ {file = "multidict-6.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b9e95a740109c6047602f4db4da9949e6c5945cefbad34a1299775ddc9a62e2"},
+ {file = "multidict-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ac0e27844758d7177989ce406acc6a83c16ed4524ebc363c1f748cba184d89d3"},
+ {file = "multidict-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:041b81a5f6b38244b34dc18c7b6aba91f9cdaf854d9a39e5ff0b58e2b5773b9c"},
+ {file = "multidict-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5fdda29a3c7e76a064f2477c9aab1ba96fd94e02e386f1e665bca1807fc5386f"},
+ {file = "multidict-6.0.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3368bf2398b0e0fcbf46d85795adc4c259299fec50c1416d0f77c0a843a3eed9"},
+ {file = "multidict-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f4f052ee022928d34fe1f4d2bc743f32609fb79ed9c49a1710a5ad6b2198db20"},
+ {file = "multidict-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:225383a6603c086e6cef0f2f05564acb4f4d5f019a4e3e983f572b8530f70c88"},
+ {file = "multidict-6.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50bd442726e288e884f7be9071016c15a8742eb689a593a0cac49ea093eef0a7"},
+ {file = "multidict-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:47e6a7e923e9cada7c139531feac59448f1f47727a79076c0b1ee80274cd8eee"},
+ {file = "multidict-6.0.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:0556a1d4ea2d949efe5fd76a09b4a82e3a4a30700553a6725535098d8d9fb672"},
+ {file = "multidict-6.0.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:626fe10ac87851f4cffecee161fc6f8f9853f0f6f1035b59337a51d29ff3b4f9"},
+ {file = "multidict-6.0.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:8064b7c6f0af936a741ea1efd18690bacfbae4078c0c385d7c3f611d11f0cf87"},
+ {file = "multidict-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2d36e929d7f6a16d4eb11b250719c39560dd70545356365b494249e2186bc389"},
+ {file = "multidict-6.0.2-cp310-cp310-win32.whl", hash = "sha256:fcb91630817aa8b9bc4a74023e4198480587269c272c58b3279875ed7235c293"},
+ {file = "multidict-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:8cbf0132f3de7cc6c6ce00147cc78e6439ea736cee6bca4f068bcf892b0fd658"},
+ {file = "multidict-6.0.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:05f6949d6169878a03e607a21e3b862eaf8e356590e8bdae4227eedadacf6e51"},
+ {file = "multidict-6.0.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2c2e459f7050aeb7c1b1276763364884595d47000c1cddb51764c0d8976e608"},
+ {file = "multidict-6.0.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d0509e469d48940147e1235d994cd849a8f8195e0bca65f8f5439c56e17872a3"},
+ {file = "multidict-6.0.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:514fe2b8d750d6cdb4712346a2c5084a80220821a3e91f3f71eec11cf8d28fd4"},
+ {file = "multidict-6.0.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:19adcfc2a7197cdc3987044e3f415168fc5dc1f720c932eb1ef4f71a2067e08b"},
+ {file = "multidict-6.0.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b9d153e7f1f9ba0b23ad1568b3b9e17301e23b042c23870f9ee0522dc5cc79e8"},
+ {file = "multidict-6.0.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:aef9cc3d9c7d63d924adac329c33835e0243b5052a6dfcbf7732a921c6e918ba"},
+ {file = "multidict-6.0.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:4571f1beddff25f3e925eea34268422622963cd8dc395bb8778eb28418248e43"},
+ {file = "multidict-6.0.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:d48b8ee1d4068561ce8033d2c344cf5232cb29ee1a0206a7b828c79cbc5982b8"},
+ {file = "multidict-6.0.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:45183c96ddf61bf96d2684d9fbaf6f3564d86b34cb125761f9a0ef9e36c1d55b"},
+ {file = "multidict-6.0.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:75bdf08716edde767b09e76829db8c1e5ca9d8bb0a8d4bd94ae1eafe3dac5e15"},
+ {file = "multidict-6.0.2-cp37-cp37m-win32.whl", hash = "sha256:a45e1135cb07086833ce969555df39149680e5471c04dfd6a915abd2fc3f6dbc"},
+ {file = "multidict-6.0.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6f3cdef8a247d1eafa649085812f8a310e728bdf3900ff6c434eafb2d443b23a"},
+ {file = "multidict-6.0.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0327292e745a880459ef71be14e709aaea2f783f3537588fb4ed09b6c01bca60"},
+ {file = "multidict-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e875b6086e325bab7e680e4316d667fc0e5e174bb5611eb16b3ea121c8951b86"},
+ {file = "multidict-6.0.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:feea820722e69451743a3d56ad74948b68bf456984d63c1a92e8347b7b88452d"},
+ {file = "multidict-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cc57c68cb9139c7cd6fc39f211b02198e69fb90ce4bc4a094cf5fe0d20fd8b0"},
+ {file = "multidict-6.0.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:497988d6b6ec6ed6f87030ec03280b696ca47dbf0648045e4e1d28b80346560d"},
+ {file = "multidict-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:89171b2c769e03a953d5969b2f272efa931426355b6c0cb508022976a17fd376"},
+ {file = "multidict-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:684133b1e1fe91eda8fa7447f137c9490a064c6b7f392aa857bba83a28cfb693"},
+ {file = "multidict-6.0.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fd9fc9c4849a07f3635ccffa895d57abce554b467d611a5009ba4f39b78a8849"},
+ {file = "multidict-6.0.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e07c8e79d6e6fd37b42f3250dba122053fddb319e84b55dd3a8d6446e1a7ee49"},
+ {file = "multidict-6.0.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4070613ea2227da2bfb2c35a6041e4371b0af6b0be57f424fe2318b42a748516"},
+ {file = "multidict-6.0.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:47fbeedbf94bed6547d3aa632075d804867a352d86688c04e606971595460227"},
+ {file = "multidict-6.0.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:5774d9218d77befa7b70d836004a768fb9aa4fdb53c97498f4d8d3f67bb9cfa9"},
+ {file = "multidict-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2957489cba47c2539a8eb7ab32ff49101439ccf78eab724c828c1a54ff3ff98d"},
+ {file = "multidict-6.0.2-cp38-cp38-win32.whl", hash = "sha256:e5b20e9599ba74391ca0cfbd7b328fcc20976823ba19bc573983a25b32e92b57"},
+ {file = "multidict-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:8004dca28e15b86d1b1372515f32eb6f814bdf6f00952699bdeb541691091f96"},
+ {file = "multidict-6.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2e4a0785b84fb59e43c18a015ffc575ba93f7d1dbd272b4cdad9f5134b8a006c"},
+ {file = "multidict-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6701bf8a5d03a43375909ac91b6980aea74b0f5402fbe9428fc3f6edf5d9677e"},
+ {file = "multidict-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a007b1638e148c3cfb6bf0bdc4f82776cef0ac487191d093cdc316905e504071"},
+ {file = "multidict-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:07a017cfa00c9890011628eab2503bee5872f27144936a52eaab449be5eaf032"},
+ {file = "multidict-6.0.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c207fff63adcdf5a485969131dc70e4b194327666b7e8a87a97fbc4fd80a53b2"},
+ {file = "multidict-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:373ba9d1d061c76462d74e7de1c0c8e267e9791ee8cfefcf6b0b2495762c370c"},
+ {file = "multidict-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfba7c6d5d7c9099ba21f84662b037a0ffd4a5e6b26ac07d19e423e6fdf965a9"},
+ {file = "multidict-6.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:19d9bad105dfb34eb539c97b132057a4e709919ec4dd883ece5838bcbf262b80"},
+ {file = "multidict-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:de989b195c3d636ba000ee4281cd03bb1234635b124bf4cd89eeee9ca8fcb09d"},
+ {file = "multidict-6.0.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7c40b7bbece294ae3a87c1bc2abff0ff9beef41d14188cda94ada7bcea99b0fb"},
+ {file = "multidict-6.0.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:d16cce709ebfadc91278a1c005e3c17dd5f71f5098bfae1035149785ea6e9c68"},
+ {file = "multidict-6.0.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:a2c34a93e1d2aa35fbf1485e5010337c72c6791407d03aa5f4eed920343dd360"},
+ {file = "multidict-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:feba80698173761cddd814fa22e88b0661e98cb810f9f986c54aa34d281e4937"},
+ {file = "multidict-6.0.2-cp39-cp39-win32.whl", hash = "sha256:23b616fdc3c74c9fe01d76ce0d1ce872d2d396d8fa8e4899398ad64fb5aa214a"},
+ {file = "multidict-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:4bae31803d708f6f15fd98be6a6ac0b6958fcf68fda3c77a048a4f9073704aae"},
+ {file = "multidict-6.0.2.tar.gz", hash = "sha256:5ff3bd75f38e4c43f1f470f2df7a4d430b821c4ce22be384e1459cb57d6bb013"},
+]
+mypy-extensions = [
+ {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"},
+ {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"},
+]
+nodeenv = [
+ {file = "nodeenv-1.6.0-py2.py3-none-any.whl", hash = "sha256:621e6b7076565ddcacd2db0294c0381e01fd28945ab36bcf00f41c5daf63bef7"},
+ {file = "nodeenv-1.6.0.tar.gz", hash = "sha256:3ef13ff90291ba2a4a7a4ff9a979b63ffdd00a464dbe04acf0ea6471517a4c2b"},
+]
+orjson = [
+ {file = "orjson-3.6.7-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:93188a9d6eb566419ad48befa202dfe7cd7a161756444b99c4ec77faea9352a4"},
+ {file = "orjson-3.6.7-cp310-cp310-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:82515226ecb77689a029061552b5df1802b75d861780c401e96ca6bc8495f775"},
+ {file = "orjson-3.6.7-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3af57ffab7848aaec6ba6b9e9b41331250b57bf696f9d502bacdc71a0ebab0ba"},
+ {file = "orjson-3.6.7-cp310-cp310-manylinux_2_24_aarch64.whl", hash = "sha256:a7297504d1142e7efa236ffc53f056d73934a993a08646dbcee89fc4308a8fcf"},
+ {file = "orjson-3.6.7-cp310-cp310-manylinux_2_24_x86_64.whl", hash = "sha256:5a50cde0dbbde255ce751fd1bca39d00ecd878ba0903c0480961b31984f2fab7"},
+ {file = "orjson-3.6.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d21f9a2d1c30e58070f93988db4cad154b9009fafbde238b52c1c760e3607fbe"},
+ {file = "orjson-3.6.7-cp310-none-win_amd64.whl", hash = "sha256:e152464c4606b49398afd911777decebcf9749cc8810c5b4199039e1afb0991e"},
+ {file = "orjson-3.6.7-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:0a65f3c403f38b0117c6dd8e76e85a7bd51fcd92f06c5598dfeddbc44697d3e5"},
+ {file = "orjson-3.6.7-cp37-cp37m-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:6c47cfca18e41f7f37b08ff3e7abf5ada2d0f27b5ade934f05be5fc5bb956e9d"},
+ {file = "orjson-3.6.7-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:63185af814c243fad7a72441e5f98120c9ecddf2675befa486d669fb65539e9b"},
+ {file = "orjson-3.6.7-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b2da6fde42182b80b40df2e6ab855c55090ebfa3fcc21c182b7ad1762b61d55c"},
+ {file = "orjson-3.6.7-cp37-cp37m-manylinux_2_24_aarch64.whl", hash = "sha256:48c5831ec388b4e2682d4ff56d6bfa4a2ef76c963f5e75f4ff4785f9cf338a80"},
+ {file = "orjson-3.6.7-cp37-cp37m-manylinux_2_24_x86_64.whl", hash = "sha256:913fac5d594ccabf5e8fbac15b9b3bb9c576d537d49eeec9f664e7a64dde4c4b"},
+ {file = "orjson-3.6.7-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:58f244775f20476e5851e7546df109f75160a5178d44257d437ba6d7e562bfe8"},
+ {file = "orjson-3.6.7-cp37-none-win_amd64.whl", hash = "sha256:2d5f45c6b85e5f14646df2d32ecd7ff20fcccc71c0ea1155f4d3df8c5299bbb7"},
+ {file = "orjson-3.6.7-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:612d242493afeeb2068bc72ff2544aa3b1e627578fcf92edee9daebb5893ffea"},
+ {file = "orjson-3.6.7-cp38-cp38-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:539cdc5067db38db27985e257772d073cd2eb9462d0a41bde96da4e4e60bd99b"},
+ {file = "orjson-3.6.7-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6d103b721bbc4f5703f62b3882e638c0b65fcdd48622531c7ffd45047ef8e87c"},
+ {file = "orjson-3.6.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb10a20f80e95102dd35dfbc3a22531661b44a09b55236b012a446955846b023"},
+ {file = "orjson-3.6.7-cp38-cp38-manylinux_2_24_aarch64.whl", hash = "sha256:bb68d0da349cf8a68971a48ad179434f75256159fe8b0715275d9b49fa23b7a3"},
+ {file = "orjson-3.6.7-cp38-cp38-manylinux_2_24_x86_64.whl", hash = "sha256:4a2c7d0a236aaeab7f69c17b7ab4c078874e817da1bfbb9827cb8c73058b3050"},
+ {file = "orjson-3.6.7-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:3be045ca3b96119f592904cf34b962969ce97bd7843cbfca084009f6c8d2f268"},
+ {file = "orjson-3.6.7-cp38-none-win_amd64.whl", hash = "sha256:bd765c06c359d8a814b90f948538f957fa8a1f55ad1aaffcdc5771996aaea061"},
+ {file = "orjson-3.6.7-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:7dd9e1e46c0776eee9e0649e3ae9584ea368d96851bcaeba18e217fa5d755283"},
+ {file = "orjson-3.6.7-cp39-cp39-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:c4b4f20a1e3df7e7c83717aff0ef4ab69e42ce2fb1f5234682f618153c458406"},
+ {file = "orjson-3.6.7-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7107a5673fd0b05adbb58bf71c1578fc84d662d29c096eb6d998982c8635c221"},
+ {file = "orjson-3.6.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a08b6940dd9a98ccf09785890112a0f81eadb4f35b51b9a80736d1725437e22c"},
+ {file = "orjson-3.6.7-cp39-cp39-manylinux_2_24_aarch64.whl", hash = "sha256:f5d1648e5a9d1070f3628a69a7c6c17634dbb0caf22f2085eca6910f7427bf1f"},
+ {file = "orjson-3.6.7-cp39-cp39-manylinux_2_24_x86_64.whl", hash = "sha256:e6201494e8dff2ce7fd21da4e3f6dfca1a3fed38f9dcefc972f552f6596a7621"},
+ {file = "orjson-3.6.7-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:70d0386abe02879ebaead2f9632dd2acb71000b4721fd8c1a2fb8c031a38d4d5"},
+ {file = "orjson-3.6.7-cp39-none-win_amd64.whl", hash = "sha256:d9a3288861bfd26f3511fb4081561ca768674612bac59513cb9081bb61fcc87f"},
+ {file = "orjson-3.6.7.tar.gz", hash = "sha256:a4bb62b11289b7620eead2f25695212e9ac77fcfba76f050fa8a540fb5c32401"},
+]
+packaging = [
+ {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"},
+ {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"},
+]
+pathspec = [
+ {file = "pathspec-0.9.0-py2.py3-none-any.whl", hash = "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a"},
+ {file = "pathspec-0.9.0.tar.gz", hash = "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"},
+]
+pbr = [
+ {file = "pbr-5.8.1-py2.py3-none-any.whl", hash = "sha256:27108648368782d07bbf1cb468ad2e2eeef29086affd14087a6d04b7de8af4ec"},
+ {file = "pbr-5.8.1.tar.gz", hash = "sha256:66bc5a34912f408bb3925bf21231cb6f59206267b7f63f3503ef865c1a292e25"},
+]
+platformdirs = [
+ {file = "platformdirs-2.5.1-py3-none-any.whl", hash = "sha256:bcae7cab893c2d310a711b70b24efb93334febe65f8de776ee320b517471e227"},
+ {file = "platformdirs-2.5.1.tar.gz", hash = "sha256:7535e70dfa32e84d4b34996ea99c5e432fa29a708d0f4e394bbcb2a8faa4f16d"},
+]
+pluggy = [
+ {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"},
+ {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"},
+]
+pre-commit = [
+ {file = "pre_commit-2.17.0-py2.py3-none-any.whl", hash = "sha256:725fa7459782d7bec5ead072810e47351de01709be838c2ce1726b9591dad616"},
+ {file = "pre_commit-2.17.0.tar.gz", hash = "sha256:c1a8040ff15ad3d648c70cc3e55b93e4d2d5b687320955505587fd79bbaed06a"},
+]
+psycopg2-binary = [
+ {file = "psycopg2-binary-2.9.3.tar.gz", hash = "sha256:761df5313dc15da1502b21453642d7599d26be88bff659382f8f9747c7ebea4e"},
+ {file = "psycopg2_binary-2.9.3-cp310-cp310-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:539b28661b71da7c0e428692438efbcd048ca21ea81af618d845e06ebfd29478"},
+ {file = "psycopg2_binary-2.9.3-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e82d38390a03da28c7985b394ec3f56873174e2c88130e6966cb1c946508e65"},
+ {file = "psycopg2_binary-2.9.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:57804fc02ca3ce0dbfbef35c4b3a4a774da66d66ea20f4bda601294ad2ea6092"},
+ {file = "psycopg2_binary-2.9.3-cp310-cp310-manylinux_2_24_aarch64.whl", hash = "sha256:083a55275f09a62b8ca4902dd11f4b33075b743cf0d360419e2051a8a5d5ff76"},
+ {file = "psycopg2_binary-2.9.3-cp310-cp310-manylinux_2_24_ppc64le.whl", hash = "sha256:0a29729145aaaf1ad8bafe663131890e2111f13416b60e460dae0a96af5905c9"},
+ {file = "psycopg2_binary-2.9.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3a79d622f5206d695d7824cbf609a4f5b88ea6d6dab5f7c147fc6d333a8787e4"},
+ {file = "psycopg2_binary-2.9.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:090f3348c0ab2cceb6dfbe6bf721ef61262ddf518cd6cc6ecc7d334996d64efa"},
+ {file = "psycopg2_binary-2.9.3-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:a9e1f75f96ea388fbcef36c70640c4efbe4650658f3d6a2967b4cc70e907352e"},
+ {file = "psycopg2_binary-2.9.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c3ae8e75eb7160851e59adc77b3a19a976e50622e44fd4fd47b8b18208189d42"},
+ {file = "psycopg2_binary-2.9.3-cp310-cp310-win32.whl", hash = "sha256:7b1e9b80afca7b7a386ef087db614faebbf8839b7f4db5eb107d0f1a53225029"},
+ {file = "psycopg2_binary-2.9.3-cp310-cp310-win_amd64.whl", hash = "sha256:8b344adbb9a862de0c635f4f0425b7958bf5a4b927c8594e6e8d261775796d53"},
+ {file = "psycopg2_binary-2.9.3-cp36-cp36m-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:e847774f8ffd5b398a75bc1c18fbb56564cda3d629fe68fd81971fece2d3c67e"},
+ {file = "psycopg2_binary-2.9.3-cp36-cp36m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:68641a34023d306be959101b345732360fc2ea4938982309b786f7be1b43a4a1"},
+ {file = "psycopg2_binary-2.9.3-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3303f8807f342641851578ee7ed1f3efc9802d00a6f83c101d21c608cb864460"},
+ {file = "psycopg2_binary-2.9.3-cp36-cp36m-manylinux_2_24_aarch64.whl", hash = "sha256:e3699852e22aa68c10de06524a3721ade969abf382da95884e6a10ff798f9281"},
+ {file = "psycopg2_binary-2.9.3-cp36-cp36m-manylinux_2_24_ppc64le.whl", hash = "sha256:526ea0378246d9b080148f2d6681229f4b5964543c170dd10bf4faaab6e0d27f"},
+ {file = "psycopg2_binary-2.9.3-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:b1c8068513f5b158cf7e29c43a77eb34b407db29aca749d3eb9293ee0d3103ca"},
+ {file = "psycopg2_binary-2.9.3-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:15803fa813ea05bef089fa78835118b5434204f3a17cb9f1e5dbfd0b9deea5af"},
+ {file = "psycopg2_binary-2.9.3-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:152f09f57417b831418304c7f30d727dc83a12761627bb826951692cc6491e57"},
+ {file = "psycopg2_binary-2.9.3-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:404224e5fef3b193f892abdbf8961ce20e0b6642886cfe1fe1923f41aaa75c9d"},
+ {file = "psycopg2_binary-2.9.3-cp36-cp36m-win32.whl", hash = "sha256:1f6b813106a3abdf7b03640d36e24669234120c72e91d5cbaeb87c5f7c36c65b"},
+ {file = "psycopg2_binary-2.9.3-cp36-cp36m-win_amd64.whl", hash = "sha256:2d872e3c9d5d075a2e104540965a1cf898b52274a5923936e5bfddb58c59c7c2"},
+ {file = "psycopg2_binary-2.9.3-cp37-cp37m-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:10bb90fb4d523a2aa67773d4ff2b833ec00857f5912bafcfd5f5414e45280fb1"},
+ {file = "psycopg2_binary-2.9.3-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:874a52ecab70af13e899f7847b3e074eeb16ebac5615665db33bce8a1009cf33"},
+ {file = "psycopg2_binary-2.9.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a29b3ca4ec9defec6d42bf5feb36bb5817ba3c0230dd83b4edf4bf02684cd0ae"},
+ {file = "psycopg2_binary-2.9.3-cp37-cp37m-manylinux_2_24_aarch64.whl", hash = "sha256:12b11322ea00ad8db8c46f18b7dfc47ae215e4df55b46c67a94b4effbaec7094"},
+ {file = "psycopg2_binary-2.9.3-cp37-cp37m-manylinux_2_24_ppc64le.whl", hash = "sha256:53293533fcbb94c202b7c800a12c873cfe24599656b341f56e71dd2b557be063"},
+ {file = "psycopg2_binary-2.9.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c381bda330ddf2fccbafab789d83ebc6c53db126e4383e73794c74eedce855ef"},
+ {file = "psycopg2_binary-2.9.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:9d29409b625a143649d03d0fd7b57e4b92e0ecad9726ba682244b73be91d2fdb"},
+ {file = "psycopg2_binary-2.9.3-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:183a517a3a63503f70f808b58bfbf962f23d73b6dccddae5aa56152ef2bcb232"},
+ {file = "psycopg2_binary-2.9.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:15c4e4cfa45f5a60599d9cec5f46cd7b1b29d86a6390ec23e8eebaae84e64554"},
+ {file = "psycopg2_binary-2.9.3-cp37-cp37m-win32.whl", hash = "sha256:adf20d9a67e0b6393eac162eb81fb10bc9130a80540f4df7e7355c2dd4af9fba"},
+ {file = "psycopg2_binary-2.9.3-cp37-cp37m-win_amd64.whl", hash = "sha256:2f9ffd643bc7349eeb664eba8864d9e01f057880f510e4681ba40a6532f93c71"},
+ {file = "psycopg2_binary-2.9.3-cp38-cp38-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:def68d7c21984b0f8218e8a15d514f714d96904265164f75f8d3a70f9c295667"},
+ {file = "psycopg2_binary-2.9.3-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dffc08ca91c9ac09008870c9eb77b00a46b3378719584059c034b8945e26b272"},
+ {file = "psycopg2_binary-2.9.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:280b0bb5cbfe8039205c7981cceb006156a675362a00fe29b16fbc264e242834"},
+ {file = "psycopg2_binary-2.9.3-cp38-cp38-manylinux_2_24_aarch64.whl", hash = "sha256:af9813db73395fb1fc211bac696faea4ca9ef53f32dc0cfa27e4e7cf766dcf24"},
+ {file = "psycopg2_binary-2.9.3-cp38-cp38-manylinux_2_24_ppc64le.whl", hash = "sha256:63638d875be8c2784cfc952c9ac34e2b50e43f9f0a0660b65e2a87d656b3116c"},
+ {file = "psycopg2_binary-2.9.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ffb7a888a047696e7f8240d649b43fb3644f14f0ee229077e7f6b9f9081635bd"},
+ {file = "psycopg2_binary-2.9.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:0c9d5450c566c80c396b7402895c4369a410cab5a82707b11aee1e624da7d004"},
+ {file = "psycopg2_binary-2.9.3-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:d1c1b569ecafe3a69380a94e6ae09a4789bbb23666f3d3a08d06bbd2451f5ef1"},
+ {file = "psycopg2_binary-2.9.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8fc53f9af09426a61db9ba357865c77f26076d48669f2e1bb24d85a22fb52307"},
+ {file = "psycopg2_binary-2.9.3-cp38-cp38-win32.whl", hash = "sha256:6472a178e291b59e7f16ab49ec8b4f3bdada0a879c68d3817ff0963e722a82ce"},
+ {file = "psycopg2_binary-2.9.3-cp38-cp38-win_amd64.whl", hash = "sha256:35168209c9d51b145e459e05c31a9eaeffa9a6b0fd61689b48e07464ffd1a83e"},
+ {file = "psycopg2_binary-2.9.3-cp39-cp39-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:47133f3f872faf28c1e87d4357220e809dfd3fa7c64295a4a148bcd1e6e34ec9"},
+ {file = "psycopg2_binary-2.9.3-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:91920527dea30175cc02a1099f331aa8c1ba39bf8b7762b7b56cbf54bc5cce42"},
+ {file = "psycopg2_binary-2.9.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:887dd9aac71765ac0d0bac1d0d4b4f2c99d5f5c1382d8b770404f0f3d0ce8a39"},
+ {file = "psycopg2_binary-2.9.3-cp39-cp39-manylinux_2_24_aarch64.whl", hash = "sha256:1f14c8b0942714eb3c74e1e71700cbbcb415acbc311c730370e70c578a44a25c"},
+ {file = "psycopg2_binary-2.9.3-cp39-cp39-manylinux_2_24_ppc64le.whl", hash = "sha256:7af0dd86ddb2f8af5da57a976d27cd2cd15510518d582b478fbb2292428710b4"},
+ {file = "psycopg2_binary-2.9.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:93cd1967a18aa0edd4b95b1dfd554cf15af657cb606280996d393dadc88c3c35"},
+ {file = "psycopg2_binary-2.9.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bda845b664bb6c91446ca9609fc69f7db6c334ec5e4adc87571c34e4f47b7ddb"},
+ {file = "psycopg2_binary-2.9.3-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:01310cf4cf26db9aea5158c217caa92d291f0500051a6469ac52166e1a16f5b7"},
+ {file = "psycopg2_binary-2.9.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:99485cab9ba0fa9b84f1f9e1fef106f44a46ef6afdeec8885e0b88d0772b49e8"},
+ {file = "psycopg2_binary-2.9.3-cp39-cp39-win32.whl", hash = "sha256:46f0e0a6b5fa5851bbd9ab1bc805eef362d3a230fbdfbc209f4a236d0a7a990d"},
+ {file = "psycopg2_binary-2.9.3-cp39-cp39-win_amd64.whl", hash = "sha256:accfe7e982411da3178ec690baaceaad3c278652998b2c45828aaac66cd8285f"},
+]
+py = [
+ {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"},
+ {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"},
+]
+pycodestyle = [
+ {file = "pycodestyle-2.8.0-py2.py3-none-any.whl", hash = "sha256:720f8b39dde8b293825e7ff02c475f3077124006db4f440dcbc9a20b76548a20"},
+ {file = "pycodestyle-2.8.0.tar.gz", hash = "sha256:eddd5847ef438ea1c7870ca7eb78a9d47ce0cdb4851a5523949f2601d0cbbe7f"},
+]
+pyflakes = [
+ {file = "pyflakes-2.4.0-py2.py3-none-any.whl", hash = "sha256:3bb3a3f256f4b7968c9c788781e4ff07dce46bdf12339dcda61053375426ee2e"},
+ {file = "pyflakes-2.4.0.tar.gz", hash = "sha256:05a85c2872edf37a4ed30b0cce2f6093e1d0581f8c19d7393122da7e25b2b24c"},
+]
+pygments = [
+ {file = "Pygments-2.11.2-py3-none-any.whl", hash = "sha256:44238f1b60a76d78fc8ca0528ee429702aae011c265fe6a8dd8b63049ae41c65"},
+ {file = "Pygments-2.11.2.tar.gz", hash = "sha256:4e426f72023d88d03b2fa258de560726ce890ff3b630f88c21cbb8b2503b8c6a"},
+]
+pyparsing = [
+ {file = "pyparsing-3.0.7-py3-none-any.whl", hash = "sha256:a6c06a88f252e6c322f65faf8f418b16213b51bdfaece0524c1c1bc30c63c484"},
+ {file = "pyparsing-3.0.7.tar.gz", hash = "sha256:18ee9022775d270c55187733956460083db60b37d0d0fb357445f3094eed3eea"},
+]
+pytest = [
+ {file = "pytest-6.2.5-py3-none-any.whl", hash = "sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134"},
+ {file = "pytest-6.2.5.tar.gz", hash = "sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89"},
+]
+pytz = [
+ {file = "pytz-2021.3-py2.py3-none-any.whl", hash = "sha256:3672058bc3453457b622aab7a1c3bfd5ab0bdae451512f6cf25f64ed37f5b87c"},
+ {file = "pytz-2021.3.tar.gz", hash = "sha256:acad2d8b20a1af07d4e4c9d2e9285c5ed9104354062f275f3fcd88dcef4f1326"},
+]
+pyyaml = [
+ {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"},
+ {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"},
+ {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"},
+ {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"},
+ {file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"},
+ {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"},
+ {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"},
+ {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"},
+ {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"},
+ {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"},
+ {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4"},
+ {file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"},
+ {file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"},
+ {file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"},
+ {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"},
+ {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"},
+ {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9"},
+ {file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"},
+ {file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"},
+ {file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"},
+ {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"},
+ {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"},
+ {file = "PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287"},
+ {file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"},
+ {file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"},
+ {file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"},
+ {file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"},
+ {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"},
+ {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"},
+ {file = "PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0"},
+ {file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"},
+ {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"},
+ {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"},
+]
+requests = [
+ {file = "requests-2.27.1-py2.py3-none-any.whl", hash = "sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d"},
+ {file = "requests-2.27.1.tar.gz", hash = "sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61"},
+]
+six = [
+ {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
+ {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
+]
+snowballstemmer = [
+ {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"},
+ {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"},
+]
+sphinx = [
+ {file = "Sphinx-4.4.0-py3-none-any.whl", hash = "sha256:5da895959511473857b6d0200f56865ed62c31e8f82dd338063b84ec022701fe"},
+ {file = "Sphinx-4.4.0.tar.gz", hash = "sha256:6caad9786055cb1fa22b4a365c1775816b876f91966481765d7d50e9f0dd35cc"},
+]
+sphinx-autodoc-typehints = [
+ {file = "sphinx_autodoc_typehints-1.17.0-py3-none-any.whl", hash = "sha256:081daf53077b4ae1c28347d6d858e13e63aefe3b4aacef79fd717dd60687b470"},
+ {file = "sphinx_autodoc_typehints-1.17.0.tar.gz", hash = "sha256:51c7b3f5cb9ccd15d0b52088c62df3094f1abd9612930340365c26def8629a14"},
+]
+sphinx-rtd-theme = [
+ {file = "sphinx_rtd_theme-1.0.0-py2.py3-none-any.whl", hash = "sha256:4d35a56f4508cfee4c4fb604373ede6feae2a306731d533f409ef5c3496fdbd8"},
+ {file = "sphinx_rtd_theme-1.0.0.tar.gz", hash = "sha256:eec6d497e4c2195fa0e8b2016b337532b8a699a68bcb22a512870e16925c6a5c"},
+]
+sphinxcontrib-apidoc = [
+ {file = "sphinxcontrib-apidoc-0.3.0.tar.gz", hash = "sha256:729bf592cf7b7dd57c4c05794f732dc026127275d785c2a5494521fdde773fb9"},
+ {file = "sphinxcontrib_apidoc-0.3.0-py2.py3-none-any.whl", hash = "sha256:6671a46b2c6c5b0dca3d8a147849d159065e50443df79614f921b42fbd15cb09"},
+]
+sphinxcontrib-applehelp = [
+ {file = "sphinxcontrib-applehelp-1.0.2.tar.gz", hash = "sha256:a072735ec80e7675e3f432fcae8610ecf509c5f1869d17e2eecff44389cdbc58"},
+ {file = "sphinxcontrib_applehelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:806111e5e962be97c29ec4c1e7fe277bfd19e9652fb1a4392105b43e01af885a"},
+]
+sphinxcontrib-devhelp = [
+ {file = "sphinxcontrib-devhelp-1.0.2.tar.gz", hash = "sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4"},
+ {file = "sphinxcontrib_devhelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e"},
+]
+sphinxcontrib-htmlhelp = [
+ {file = "sphinxcontrib-htmlhelp-2.0.0.tar.gz", hash = "sha256:f5f8bb2d0d629f398bf47d0d69c07bc13b65f75a81ad9e2f71a63d4b7a2f6db2"},
+ {file = "sphinxcontrib_htmlhelp-2.0.0-py2.py3-none-any.whl", hash = "sha256:d412243dfb797ae3ec2b59eca0e52dac12e75a241bf0e4eb861e450d06c6ed07"},
+]
+sphinxcontrib-jsmath = [
+ {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"},
+ {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"},
+]
+sphinxcontrib-qthelp = [
+ {file = "sphinxcontrib-qthelp-1.0.3.tar.gz", hash = "sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72"},
+ {file = "sphinxcontrib_qthelp-1.0.3-py2.py3-none-any.whl", hash = "sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6"},
+]
+sphinxcontrib-serializinghtml = [
+ {file = "sphinxcontrib-serializinghtml-1.1.5.tar.gz", hash = "sha256:aa5f6de5dfdf809ef505c4895e51ef5c9eac17d0f287933eb49ec495280b6952"},
+ {file = "sphinxcontrib_serializinghtml-1.1.5-py2.py3-none-any.whl", hash = "sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd"},
+]
+toml = [
+ {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
+ {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},
+]
+tomli = [
+ {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
+ {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
+]
+typing-extensions = [
+ {file = "typing_extensions-4.1.1-py3-none-any.whl", hash = "sha256:21c85e0fe4b9a155d0799430b0ad741cdce7e359660ccbd8b530613e8df88ce2"},
+ {file = "typing_extensions-4.1.1.tar.gz", hash = "sha256:1a9462dcc3347a79b1f1c0271fbe79e844580bb598bafa1ed208b94da3cdcd42"},
+]
+urllib3 = [
+ {file = "urllib3-1.26.8-py2.py3-none-any.whl", hash = "sha256:000ca7f471a233c2251c6c7023ee85305721bfdf18621ebff4fd17a8653427ed"},
+ {file = "urllib3-1.26.8.tar.gz", hash = "sha256:0e7c33d9a63e7ddfcb86780aac87befc2fbddf46c58dbb487e0855f7ceec283c"},
+]
+virtualenv = [
+ {file = "virtualenv-20.13.2-py2.py3-none-any.whl", hash = "sha256:e7b34c9474e6476ee208c43a4d9ac1510b041c68347eabfe9a9ea0c86aa0a46b"},
+ {file = "virtualenv-20.13.2.tar.gz", hash = "sha256:01f5f80744d24a3743ce61858123488e91cb2dd1d3bdf92adaf1bba39ffdedf0"},
+]
+yarl = [
+ {file = "yarl-1.7.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f2a8508f7350512434e41065684076f640ecce176d262a7d54f0da41d99c5a95"},
+ {file = "yarl-1.7.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:da6df107b9ccfe52d3a48165e48d72db0eca3e3029b5b8cb4fe6ee3cb870ba8b"},
+ {file = "yarl-1.7.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a1d0894f238763717bdcfea74558c94e3bc34aeacd3351d769460c1a586a8b05"},
+ {file = "yarl-1.7.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dfe4b95b7e00c6635a72e2d00b478e8a28bfb122dc76349a06e20792eb53a523"},
+ {file = "yarl-1.7.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c145ab54702334c42237a6c6c4cc08703b6aa9b94e2f227ceb3d477d20c36c63"},
+ {file = "yarl-1.7.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1ca56f002eaf7998b5fcf73b2421790da9d2586331805f38acd9997743114e98"},
+ {file = "yarl-1.7.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1d3d5ad8ea96bd6d643d80c7b8d5977b4e2fb1bab6c9da7322616fd26203d125"},
+ {file = "yarl-1.7.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:167ab7f64e409e9bdd99333fe8c67b5574a1f0495dcfd905bc7454e766729b9e"},
+ {file = "yarl-1.7.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:95a1873b6c0dd1c437fb3bb4a4aaa699a48c218ac7ca1e74b0bee0ab16c7d60d"},
+ {file = "yarl-1.7.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6152224d0a1eb254f97df3997d79dadd8bb2c1a02ef283dbb34b97d4f8492d23"},
+ {file = "yarl-1.7.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:5bb7d54b8f61ba6eee541fba4b83d22b8a046b4ef4d8eb7f15a7e35db2e1e245"},
+ {file = "yarl-1.7.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:9c1f083e7e71b2dd01f7cd7434a5f88c15213194df38bc29b388ccdf1492b739"},
+ {file = "yarl-1.7.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f44477ae29025d8ea87ec308539f95963ffdc31a82f42ca9deecf2d505242e72"},
+ {file = "yarl-1.7.2-cp310-cp310-win32.whl", hash = "sha256:cff3ba513db55cc6a35076f32c4cdc27032bd075c9faef31fec749e64b45d26c"},
+ {file = "yarl-1.7.2-cp310-cp310-win_amd64.whl", hash = "sha256:c9c6d927e098c2d360695f2e9d38870b2e92e0919be07dbe339aefa32a090265"},
+ {file = "yarl-1.7.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:9b4c77d92d56a4c5027572752aa35082e40c561eec776048330d2907aead891d"},
+ {file = "yarl-1.7.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c01a89a44bb672c38f42b49cdb0ad667b116d731b3f4c896f72302ff77d71656"},
+ {file = "yarl-1.7.2-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c19324a1c5399b602f3b6e7db9478e5b1adf5cf58901996fc973fe4fccd73eed"},
+ {file = "yarl-1.7.2-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3abddf0b8e41445426d29f955b24aeecc83fa1072be1be4e0d194134a7d9baee"},
+ {file = "yarl-1.7.2-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6a1a9fe17621af43e9b9fcea8bd088ba682c8192d744b386ee3c47b56eaabb2c"},
+ {file = "yarl-1.7.2-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8b0915ee85150963a9504c10de4e4729ae700af11df0dc5550e6587ed7891e92"},
+ {file = "yarl-1.7.2-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:29e0656d5497733dcddc21797da5a2ab990c0cb9719f1f969e58a4abac66234d"},
+ {file = "yarl-1.7.2-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:bf19725fec28452474d9887a128e98dd67eee7b7d52e932e6949c532d820dc3b"},
+ {file = "yarl-1.7.2-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:d6f3d62e16c10e88d2168ba2d065aa374e3c538998ed04996cd373ff2036d64c"},
+ {file = "yarl-1.7.2-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:ac10bbac36cd89eac19f4e51c032ba6b412b3892b685076f4acd2de18ca990aa"},
+ {file = "yarl-1.7.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:aa32aaa97d8b2ed4e54dc65d241a0da1c627454950f7d7b1f95b13985afd6c5d"},
+ {file = "yarl-1.7.2-cp36-cp36m-win32.whl", hash = "sha256:87f6e082bce21464857ba58b569370e7b547d239ca22248be68ea5d6b51464a1"},
+ {file = "yarl-1.7.2-cp36-cp36m-win_amd64.whl", hash = "sha256:ac35ccde589ab6a1870a484ed136d49a26bcd06b6a1c6397b1967ca13ceb3913"},
+ {file = "yarl-1.7.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a467a431a0817a292121c13cbe637348b546e6ef47ca14a790aa2fa8cc93df63"},
+ {file = "yarl-1.7.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ab0c3274d0a846840bf6c27d2c60ba771a12e4d7586bf550eefc2df0b56b3b4"},
+ {file = "yarl-1.7.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d260d4dc495c05d6600264a197d9d6f7fc9347f21d2594926202fd08cf89a8ba"},
+ {file = "yarl-1.7.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fc4dd8b01a8112809e6b636b00f487846956402834a7fd59d46d4f4267181c41"},
+ {file = "yarl-1.7.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:c1164a2eac148d85bbdd23e07dfcc930f2e633220f3eb3c3e2a25f6148c2819e"},
+ {file = "yarl-1.7.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:67e94028817defe5e705079b10a8438b8cb56e7115fa01640e9c0bb3edf67332"},
+ {file = "yarl-1.7.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:89ccbf58e6a0ab89d487c92a490cb5660d06c3a47ca08872859672f9c511fc52"},
+ {file = "yarl-1.7.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:8cce6f9fa3df25f55521fbb5c7e4a736683148bcc0c75b21863789e5185f9185"},
+ {file = "yarl-1.7.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:211fcd65c58bf250fb994b53bc45a442ddc9f441f6fec53e65de8cba48ded986"},
+ {file = "yarl-1.7.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c10ea1e80a697cf7d80d1ed414b5cb8f1eec07d618f54637067ae3c0334133c4"},
+ {file = "yarl-1.7.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:52690eb521d690ab041c3919666bea13ab9fbff80d615ec16fa81a297131276b"},
+ {file = "yarl-1.7.2-cp37-cp37m-win32.whl", hash = "sha256:695ba021a9e04418507fa930d5f0704edbce47076bdcfeeaba1c83683e5649d1"},
+ {file = "yarl-1.7.2-cp37-cp37m-win_amd64.whl", hash = "sha256:c17965ff3706beedafd458c452bf15bac693ecd146a60a06a214614dc097a271"},
+ {file = "yarl-1.7.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:fce78593346c014d0d986b7ebc80d782b7f5e19843ca798ed62f8e3ba8728576"},
+ {file = "yarl-1.7.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c2a1ac41a6aa980db03d098a5531f13985edcb451bcd9d00670b03129922cd0d"},
+ {file = "yarl-1.7.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:39d5493c5ecd75c8093fa7700a2fb5c94fe28c839c8e40144b7ab7ccba6938c8"},
+ {file = "yarl-1.7.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1eb6480ef366d75b54c68164094a6a560c247370a68c02dddb11f20c4c6d3c9d"},
+ {file = "yarl-1.7.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5ba63585a89c9885f18331a55d25fe81dc2d82b71311ff8bd378fc8004202ff6"},
+ {file = "yarl-1.7.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e39378894ee6ae9f555ae2de332d513a5763276a9265f8e7cbaeb1b1ee74623a"},
+ {file = "yarl-1.7.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:c0910c6b6c31359d2f6184828888c983d54d09d581a4a23547a35f1d0b9484b1"},
+ {file = "yarl-1.7.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6feca8b6bfb9eef6ee057628e71e1734caf520a907b6ec0d62839e8293e945c0"},
+ {file = "yarl-1.7.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8300401dc88cad23f5b4e4c1226f44a5aa696436a4026e456fe0e5d2f7f486e6"},
+ {file = "yarl-1.7.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:788713c2896f426a4e166b11f4ec538b5736294ebf7d5f654ae445fd44270832"},
+ {file = "yarl-1.7.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:fd547ec596d90c8676e369dd8a581a21227fe9b4ad37d0dc7feb4ccf544c2d59"},
+ {file = "yarl-1.7.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:737e401cd0c493f7e3dd4db72aca11cfe069531c9761b8ea474926936b3c57c8"},
+ {file = "yarl-1.7.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:baf81561f2972fb895e7844882898bda1eef4b07b5b385bcd308d2098f1a767b"},
+ {file = "yarl-1.7.2-cp38-cp38-win32.whl", hash = "sha256:ede3b46cdb719c794427dcce9d8beb4abe8b9aa1e97526cc20de9bd6583ad1ef"},
+ {file = "yarl-1.7.2-cp38-cp38-win_amd64.whl", hash = "sha256:cc8b7a7254c0fc3187d43d6cb54b5032d2365efd1df0cd1749c0c4df5f0ad45f"},
+ {file = "yarl-1.7.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:580c1f15500e137a8c37053e4cbf6058944d4c114701fa59944607505c2fe3a0"},
+ {file = "yarl-1.7.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3ec1d9a0d7780416e657f1e405ba35ec1ba453a4f1511eb8b9fbab81cb8b3ce1"},
+ {file = "yarl-1.7.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3bf8cfe8856708ede6a73907bf0501f2dc4e104085e070a41f5d88e7faf237f3"},
+ {file = "yarl-1.7.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1be4bbb3d27a4e9aa5f3df2ab61e3701ce8fcbd3e9846dbce7c033a7e8136746"},
+ {file = "yarl-1.7.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:534b047277a9a19d858cde163aba93f3e1677d5acd92f7d10ace419d478540de"},
+ {file = "yarl-1.7.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c6ddcd80d79c96eb19c354d9dca95291589c5954099836b7c8d29278a7ec0bda"},
+ {file = "yarl-1.7.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9bfcd43c65fbb339dc7086b5315750efa42a34eefad0256ba114cd8ad3896f4b"},
+ {file = "yarl-1.7.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f64394bd7ceef1237cc604b5a89bf748c95982a84bcd3c4bbeb40f685c810794"},
+ {file = "yarl-1.7.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:044daf3012e43d4b3538562da94a88fb12a6490652dbc29fb19adfa02cf72eac"},
+ {file = "yarl-1.7.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:368bcf400247318382cc150aaa632582d0780b28ee6053cd80268c7e72796dec"},
+ {file = "yarl-1.7.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:bab827163113177aee910adb1f48ff7af31ee0289f434f7e22d10baf624a6dfe"},
+ {file = "yarl-1.7.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:0cba38120db72123db7c58322fa69e3c0efa933040ffb586c3a87c063ec7cae8"},
+ {file = "yarl-1.7.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:59218fef177296451b23214c91ea3aba7858b4ae3306dde120224cfe0f7a6ee8"},
+ {file = "yarl-1.7.2-cp39-cp39-win32.whl", hash = "sha256:1edc172dcca3f11b38a9d5c7505c83c1913c0addc99cd28e993efeaafdfaa18d"},
+ {file = "yarl-1.7.2-cp39-cp39-win_amd64.whl", hash = "sha256:797c2c412b04403d2da075fb93c123df35239cd7b4cc4e0cd9e5839b73f52c58"},
+ {file = "yarl-1.7.2.tar.gz", hash = "sha256:45399b46d60c253327a460e99856752009fcee5f5d3c80b2f7c0cae1c38d56dd"},
+]
+zipp = [
+ {file = "zipp-3.7.0-py3-none-any.whl", hash = "sha256:b47250dd24f92b7dd6a0a8fc5244da14608f3ca90a5efcd37a3b1642fac9a375"},
+ {file = "zipp-3.7.0.tar.gz", hash = "sha256:9f50f446828eb9d45b267433fd3e9da8d801f614129124863f9c51ebceafb87d"},
+]
diff --git a/packages/plugins/minos-graphql-aiohttp/poetry.toml b/packages/plugins/minos-graphql-aiohttp/poetry.toml
new file mode 100644
index 000000000..ab1033bd3
--- /dev/null
+++ b/packages/plugins/minos-graphql-aiohttp/poetry.toml
@@ -0,0 +1,2 @@
+[virtualenvs]
+in-project = true
diff --git a/packages/plugins/minos-graphql-aiohttp/pyproject.toml b/packages/plugins/minos-graphql-aiohttp/pyproject.toml
new file mode 100644
index 000000000..338ba801e
--- /dev/null
+++ b/packages/plugins/minos-graphql-aiohttp/pyproject.toml
@@ -0,0 +1,56 @@
+[tool.poetry]
+name = "minos-graphql-aiohttp"
+version = "0.6.0"
+description = "The rest plugin of the Minos Framework."
+readme = "README.md"
+repository = "https://github.com/minos-framework/minos-python"
+homepage = "http://www.minos.run/"
+authors = ["Minos Framework Devs "]
+license = "MIT"
+classifiers = [
+ "Development Status :: 4 - Beta",
+ "Intended Audience :: Developers",
+ "Natural Language :: English",
+ "Programming Language :: Python :: 3",
+ "Programming Language :: Python :: 3.9",
+]
+keywords = [
+ "clariteia",
+ "minos",
+ "microservice",
+ "saga",
+]
+packages = [
+ { include = "minos" }
+]
+include = [
+ "AUTHORS.md",
+ "HISTORY.md",
+ "LICENSE",
+]
+
+[tool.poetry.dependencies]
+python = "^3.9"
+minos-microservice-common = "^0.5.0"
+minos-microservice-networks = "^0.5.0"
+aiohttp = "^3.8.1"
+graphql-server = {version = "^3.0.0-beta.5", extras = ["aiohttp"]}
+
+[tool.poetry.dev-dependencies]
+minos-microservice-common = { path = "../../core/minos-microservice-common", develop = true }
+minos-microservice-networks = { path = "../../core/minos-microservice-networks", develop = true }
+black = "^22.1"
+isort = "^5.8.0"
+pytest = "^6.2.4"
+coverage = "^6.3"
+flake8 = "^4.0.1"
+Sphinx = "^4.0.1"
+pre-commit = "^2.12.1"
+sphinx-autodoc-typehints = "^1.12.0"
+sphinxcontrib-apidoc = "^0.3.0"
+sphinx-rtd-theme = "^1.0.0"
+m2r2 = "^0.3.2"
+
+[build-system]
+requires = ["poetry-core>=1.0.0"]
+build-backend = "poetry.core.masonry.api"
diff --git a/packages/plugins/minos-graphql-aiohttp/setup.cfg b/packages/plugins/minos-graphql-aiohttp/setup.cfg
new file mode 100644
index 000000000..dbb9ac849
--- /dev/null
+++ b/packages/plugins/minos-graphql-aiohttp/setup.cfg
@@ -0,0 +1,28 @@
+[coverage:run]
+source =
+ minos
+
+[coverage:report]
+exclude_lines =
+ pragma: no cover
+ raise NotImplementedError
+ if TYPE_CHECKING:
+ pass
+precision = 2
+
+[flake8]
+filename =
+ ./minos/**/*.py,
+ ./tests/**/*.py,
+ ./examples/**/*.py
+max-line-length = 120
+per-file-ignores =
+ ./**/__init__.py:F401,W391
+
+[isort]
+known_first_party=minos
+multi_line_output = 3
+include_trailing_comma = True
+force_grid_wrap = 1
+use_parentheses = True
+line_length = 120
diff --git a/packages/plugins/minos-graphql-aiohttp/tests/__init__.py b/packages/plugins/minos-graphql-aiohttp/tests/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/packages/plugins/minos-graphql-aiohttp/tests/services/commands.py b/packages/plugins/minos-graphql-aiohttp/tests/services/commands.py
new file mode 100644
index 000000000..7f9713073
--- /dev/null
+++ b/packages/plugins/minos-graphql-aiohttp/tests/services/commands.py
@@ -0,0 +1,36 @@
+from minos.networks import (
+ BrokerResponse,
+ Request,
+ Response,
+ enroute,
+)
+
+
+class CommandService:
+ @enroute.rest.command(url="/order", method="GET")
+ def get_order_rest(self, request: Request) -> Response:
+ return Response("get_order")
+
+ @enroute.broker.command("GetOrder")
+ def get_order_command(self, request: Request) -> Response:
+ return BrokerResponse("get_order")
+
+ @enroute.broker.command("AddOrder")
+ def add_order(self, request: Request) -> Response:
+ return BrokerResponse("add_order")
+
+ @enroute.broker.command("DeleteOrder")
+ def delete_order(self, request: Request) -> Response:
+ return BrokerResponse("delete_order")
+
+ @enroute.broker.command("UpdateOrder")
+ def update_order(self, request: Request) -> Response:
+ return BrokerResponse("update_order")
+
+ @enroute.broker.event("TicketAdded")
+ def ticket_added(self, request: Request) -> str:
+ return "command_service_ticket_added"
+
+ @enroute.periodic.event("@daily")
+ def recompute_something(self, request: Request) -> None:
+ """For testing purposes."""
diff --git a/packages/plugins/minos-graphql-aiohttp/tests/services/queries.py b/packages/plugins/minos-graphql-aiohttp/tests/services/queries.py
new file mode 100644
index 000000000..726f70b12
--- /dev/null
+++ b/packages/plugins/minos-graphql-aiohttp/tests/services/queries.py
@@ -0,0 +1,19 @@
+from minos.networks import (
+ Request,
+ Response,
+ enroute,
+)
+
+
+class QueryService:
+ @enroute.rest.query(url="/ticket", method="POST")
+ def add_ticket(self, request: Request) -> Response:
+ return Response("ticket_added")
+
+ @enroute.broker.event("TicketAdded")
+ def ticket_added(self, request: Request):
+ return "query_service_ticket_added"
+
+ @enroute.broker.event("TicketDeleted")
+ def ticket_deleted(self, request: Request):
+ return "ticket_deleted"
diff --git a/packages/plugins/minos-graphql-aiohttp/tests/test_config.yml b/packages/plugins/minos-graphql-aiohttp/tests/test_config.yml
new file mode 100644
index 000000000..79786973a
--- /dev/null
+++ b/packages/plugins/minos-graphql-aiohttp/tests/test_config.yml
@@ -0,0 +1,39 @@
+service:
+ name: Order
+ aggregate: tests.utils.Order
+services:
+ - tests.services.commands.CommandService
+ - tests.services.queries.QueryService
+rest:
+ host: localhost
+ port: 8080
+repository:
+ database: order_db
+ user: minos
+ password: min0s
+ host: localhost
+ port: 5432
+snapshot:
+ database: order_db
+ user: minos
+ password: min0s
+ host: localhost
+ port: 5432
+broker:
+ host: localhost
+ port: 9092
+ queue:
+ database: order_db
+ user: minos
+ password: min0s
+ host: localhost
+ port: 5432
+ records: 10
+ retry: 2
+saga:
+ storage:
+ path: "./order.lmdb"
+discovery:
+ client: minos.networks.InMemoryDiscoveryClient
+ host: discovery-service
+ port: 8080
diff --git a/packages/plugins/minos-graphql-aiohttp/tests/test_plugins/test_rest_aiohttp/test_handlers.py b/packages/plugins/minos-graphql-aiohttp/tests/test_plugins/test_rest_aiohttp/test_handlers.py
new file mode 100644
index 000000000..78a174cf9
--- /dev/null
+++ b/packages/plugins/minos-graphql-aiohttp/tests/test_plugins/test_rest_aiohttp/test_handlers.py
@@ -0,0 +1,131 @@
+import unittest
+from unittest.mock import (
+ AsyncMock,
+)
+from uuid import (
+ uuid4,
+)
+
+from aiohttp import (
+ web,
+)
+from aiohttp.web_exceptions import (
+ HTTPInternalServerError,
+)
+from orjson import (
+ orjson,
+)
+
+from minos.common.testing import (
+ PostgresAsyncTestCase,
+)
+from minos.networks import (
+ REQUEST_USER_CONTEXT_VAR,
+ Request,
+ Response,
+)
+from minos.plugins.rest_aiohttp import (
+ RestHandler,
+ RestResponse,
+ RestResponseException,
+)
+from tests.test_plugins.test_rest_aiohttp.utils import (
+ json_mocked_request,
+ mocked_request,
+)
+from tests.utils import (
+ BASE_PATH,
+)
+
+APPLICATION_JSON = "application/json"
+
+
+class _Cls:
+ @staticmethod
+ async def _fn(request: Request) -> Response:
+ return RestResponse(await request.content())
+
+ @staticmethod
+ async def _fn_status(request: Request) -> Response:
+ return RestResponse(status=await request.content())
+
+ @staticmethod
+ async def _fn_none(request: Request):
+ return
+
+ @staticmethod
+ async def _fn_raises_response(request: Request) -> Response:
+ raise RestResponseException("")
+
+ @staticmethod
+ async def _fn_raises_exception(request: Request) -> Response:
+ raise ValueError
+
+
+class TestRestHandler(PostgresAsyncTestCase):
+ CONFIG_FILE_PATH = BASE_PATH / "test_config.yml"
+
+ def setUp(self) -> None:
+ super().setUp()
+ self.handler = RestHandler.from_config(config=self.config)
+
+ def test_from_config(self):
+ self.assertIsInstance(self.handler, RestHandler)
+ self.assertEqual({("/order", "GET"), ("/ticket", "POST")}, set(self.handler.endpoints.keys()))
+
+ def test_from_config_raises(self):
+ with self.assertRaises(Exception):
+ RestHandler.from_config()
+
+ def test_get_app(self):
+ self.assertIsInstance(self.handler.get_app(), web.Application)
+
+ async def test_get_callback(self):
+ handler = self.handler.get_callback(_Cls._fn)
+ response = await handler(json_mocked_request({"foo": "bar"}))
+ self.assertIsInstance(response, web.Response)
+ self.assertEqual(orjson.dumps({"foo": "bar"}), response.body)
+ self.assertEqual(APPLICATION_JSON, response.content_type)
+
+ async def test_get_callback_status(self):
+ handler = self.handler.get_callback(_Cls._fn_status)
+ response = await handler(json_mocked_request(203))
+ self.assertIsInstance(response, web.Response)
+ self.assertEqual(None, response.body)
+ self.assertEqual(APPLICATION_JSON, response.content_type)
+ self.assertEqual(203, response.status)
+
+ async def test_get_callback_none(self):
+ handler = self.handler.get_callback(_Cls._fn_none)
+ response = await handler(mocked_request())
+ self.assertIsInstance(response, web.Response)
+ self.assertEqual(None, response.text)
+ self.assertEqual(APPLICATION_JSON, response.content_type)
+
+ async def test_get_callback_raises_response(self):
+ handler = self.handler.get_callback(_Cls._fn_raises_response)
+ response = await handler(json_mocked_request({"foo": "bar"}))
+ self.assertEqual(400, response.status)
+
+ async def test_get_callback_raises_exception(self):
+ handler = self.handler.get_callback(_Cls._fn_raises_exception)
+ with self.assertRaises(HTTPInternalServerError):
+ await handler(json_mocked_request({"foo": "bar"}))
+
+ async def test_get_callback_with_user(self):
+ user = uuid4()
+
+ async def _fn(request) -> None:
+ self.assertEqual(user, request.user)
+ self.assertEqual(user, REQUEST_USER_CONTEXT_VAR.get())
+
+ mock = AsyncMock(side_effect=_fn)
+
+ handler = self.handler.get_callback(mock)
+ await handler(json_mocked_request({"foo": "bar"}, user=user))
+
+ self.assertEqual(1, mock.call_count)
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/packages/plugins/minos-graphql-aiohttp/tests/test_plugins/test_rest_aiohttp/test_requests.py b/packages/plugins/minos-graphql-aiohttp/tests/test_plugins/test_rest_aiohttp/test_requests.py
new file mode 100644
index 000000000..0c824073f
--- /dev/null
+++ b/packages/plugins/minos-graphql-aiohttp/tests/test_plugins/test_rest_aiohttp/test_requests.py
@@ -0,0 +1,441 @@
+import unittest
+import warnings
+from uuid import (
+ uuid4,
+)
+
+from orjson import (
+ orjson,
+)
+
+from minos.common import (
+ MinosAvroProtocol,
+ ModelType,
+ classname,
+)
+from minos.networks import (
+ NotHasContentException,
+ NotHasParamsException,
+ Response,
+)
+from minos.plugins.rest_aiohttp import (
+ RestRequest,
+ RestResponse,
+)
+from tests.test_plugins.test_rest_aiohttp.utils import (
+ avro_mocked_request,
+ bytes_mocked_request,
+ form_mocked_request,
+ json_mocked_request,
+ mocked_request,
+ text_mocked_request,
+)
+from tests.utils import (
+ FakeModel,
+)
+
+APPLICATION_JSON = "application/json"
+APPLICATION_URLENCODED = "application/x-www-form-urlencoded"
+APPLICATION_OCTET_STREAM = "application/octet-stream"
+TEXT_PLAIN = "text/plain"
+AVRO_BINARY = "avro/binary"
+IMAGE_PNG = "image/png"
+
+
+class TestRestRequest(unittest.IsolatedAsyncioTestCase):
+ def test_raw(self):
+ raw = mocked_request()
+ request = RestRequest(raw)
+ self.assertEqual(raw, request.raw)
+
+ def test_raw_request(self):
+ raw = mocked_request()
+ request = RestRequest(raw)
+ with warnings.catch_warnings():
+ warnings.simplefilter("ignore", DeprecationWarning)
+ # noinspection PyDeprecation
+ self.assertEqual(request.raw, request.raw_request)
+
+ def test_repr(self):
+ raw = mocked_request()
+ request = RestRequest(raw)
+ self.assertEqual(f"RestRequest({raw!r})", repr(request))
+
+ def test_eq_true(self):
+ request = mocked_request()
+ self.assertEqual(RestRequest(request), RestRequest(request))
+
+ def test_eq_false(self):
+ self.assertNotEqual(RestRequest(mocked_request()), RestRequest(mocked_request()))
+
+ def test_headers(self):
+ raw = mocked_request(headers={"something": "123"})
+ request = RestRequest(raw)
+ self.assertEqual({"something": "123"}, request.headers)
+
+ def test_user(self):
+ uuid = uuid4()
+ raw = mocked_request(user=uuid)
+ request = RestRequest(raw)
+ user = request.user
+ self.assertEqual(uuid, user)
+
+ def test_user_unset(self):
+ raw = mocked_request()
+ request = RestRequest(raw)
+ self.assertEqual(None, request.user)
+
+
+class TestRestRequestContent(unittest.IsolatedAsyncioTestCase):
+ def test_has_content_false(self):
+ raw = mocked_request()
+ request = RestRequest(raw)
+ self.assertEqual(False, request.has_content)
+
+ def test_has_content_true(self):
+ raw = json_mocked_request(123)
+ request = RestRequest(raw)
+ self.assertEqual(True, request.has_content)
+
+ async def test_empty_raises(self):
+ raw = mocked_request()
+ request = RestRequest(raw)
+ with self.assertRaises(NotHasContentException):
+ await request.content()
+
+ async def test_json_int(self):
+ expected = 56
+
+ raw = json_mocked_request(56)
+ request = RestRequest(raw)
+ observed = await request.content()
+
+ self.assertEqual(expected, observed)
+
+ async def test_json_list(self):
+ expected = [{"doors": 3, "color": "blue", "owner": None}, {"doors": 5, "color": "red", "owner": None}]
+
+ raw = json_mocked_request(
+ [{"doors": 3, "color": "blue", "owner": None}, {"doors": 5, "color": "red", "owner": None}]
+ )
+ request = RestRequest(raw)
+ observed = await request.content()
+
+ self.assertEqual(expected, observed)
+
+ async def test_json_dict(self):
+ expected = {"doors": 3, "color": "blue", "owner": None}
+
+ raw = json_mocked_request({"doors": 3, "color": "blue", "owner": None})
+ request = RestRequest(raw)
+ observed = await request.content()
+
+ self.assertEqual(expected, observed)
+
+ async def test_form(self):
+ expected = {"foo": "foo1", "bar": ["bar1", "bar2"]}
+
+ raw = form_mocked_request({"foo": "foo1", "bar": ["bar1", "bar2"]})
+ request = RestRequest(raw)
+ observed = await request.content()
+
+ self.assertEqual(expected, observed)
+
+ async def test_avro_int(self):
+ expected = 56
+
+ raw = avro_mocked_request(56, "int")
+ request = RestRequest(raw)
+ observed = await request.content()
+
+ self.assertEqual(expected, observed)
+
+ async def test_avro_uuid(self):
+ expected = uuid4()
+
+ raw = avro_mocked_request(expected, {"type": "string", "logicalType": "uuid"})
+ request = RestRequest(raw)
+ observed = await request.content()
+
+ self.assertEqual(expected, observed)
+
+ async def test_avro_dto(self):
+ expected = ModelType.build("FakeModel", {"text": str})("foobar")
+
+ raw = avro_mocked_request(
+ {"text": "foobar"}, {"name": "FakeModel", "type": "record", "fields": [{"name": "text", "type": "string"}]}
+ )
+ request = RestRequest(raw)
+ observed = await request.content()
+
+ self.assertEqual(expected, observed)
+
+ async def test_avro_model(self):
+ expected = FakeModel("foobar")
+
+ raw = avro_mocked_request(expected.avro_data, expected.avro_schema)
+ request = RestRequest(raw)
+ observed = await request.content()
+
+ self.assertEqual(expected, observed)
+
+ async def test_text(self):
+ expected = "foobar"
+
+ raw = text_mocked_request("foobar")
+ request = RestRequest(raw)
+ observed = await request.content()
+
+ self.assertEqual(expected, observed)
+
+ async def test_bytes(self):
+ expected = bytes("foobar", "utf-8")
+
+ raw = bytes_mocked_request(bytes("foobar", "utf-8"))
+ request = RestRequest(raw)
+ observed = await request.content()
+
+ self.assertEqual(expected, observed)
+
+ async def test_raises(self):
+ raw = mocked_request(content_type="foo/bar", data="foobar".encode())
+ request = RestRequest(raw)
+ with self.assertRaises(ValueError):
+ await request.content()
+
+ async def test_with_type(self):
+ # noinspection PyPep8Naming
+ car = ModelType.build("Car", {"doors": int, "color": str, "owner": type(None)})
+ expected = [car(3, "blue", None), car(5, "red", None)]
+
+ raw = json_mocked_request([{"doors": "3", "color": "blue"}, {"doors": "5", "color": "red"}])
+ request = RestRequest(raw)
+ observed = await request.content(type_=list[car])
+
+ self.assertEqual(expected, observed)
+
+ async def test_with_type_classname(self):
+ expected = 3
+
+ raw = json_mocked_request("3")
+ request = RestRequest(raw)
+ observed = await request.content(type_=classname(int))
+
+ self.assertEqual(expected, observed)
+
+ async def test_with_model_type(self):
+ expected = 3
+
+ raw = json_mocked_request("3")
+ request = RestRequest(raw)
+ with warnings.catch_warnings():
+ warnings.simplefilter("ignore", DeprecationWarning)
+ observed = await request.content(model_type=classname(int))
+
+ self.assertEqual(expected, observed)
+
+
+class TestRestRequestParams(unittest.IsolatedAsyncioTestCase):
+ def test_has_url_params_false(self):
+ raw = mocked_request()
+ request = RestRequest(raw)
+ self.assertEqual(False, request.has_url_params)
+
+ def test_has_url_params_true(self):
+ raw = mocked_request(url_params=[("foo", "1"), ("bar", "2"), ("foo", "3")])
+ request = RestRequest(raw)
+ self.assertEqual(True, request.has_url_params)
+
+ async def test_url_params(self):
+ expected = {"bar": "2", "foo": ["1", "3"]}
+
+ raw = mocked_request(url_params=[("foo", "1"), ("bar", "2"), ("foo", "3")])
+ request = RestRequest(raw)
+ observed = await request.url_params()
+
+ self.assertEqual(expected, observed)
+
+ async def test_url_params_with_type(self):
+ # noinspection PyPep8Naming
+ params = ModelType.build("Params", {"foo": list[int], "bar": int})
+ expected = params(foo=[1, 3], bar=2)
+
+ raw = mocked_request(url_params=[("foo", "1"), ("bar", "2"), ("foo", "3")])
+ request = RestRequest(raw)
+ observed = await request.url_params(type_=params)
+
+ self.assertEqual(expected, observed)
+
+ async def test_url_params_raises(self):
+ raw = mocked_request()
+ request = RestRequest(raw)
+ with self.assertRaises(NotHasParamsException):
+ await request.url_params()
+
+ def test_has_query_params_false(self):
+ raw = mocked_request()
+ request = RestRequest(raw)
+ self.assertEqual(False, request.has_query_params)
+
+ def test_has_query_params_true(self):
+ raw = mocked_request(query_params=[("one", "1"), ("two", "2"), ("one", "3")])
+ request = RestRequest(raw)
+ self.assertEqual(True, request.has_query_params)
+
+ async def test_query_params(self):
+ expected = {"one": ["1", "3"], "two": "2"}
+
+ raw = mocked_request(query_params=[("one", "1"), ("two", "2"), ("one", "3")])
+ request = RestRequest(raw)
+ observed = await request.query_params()
+
+ self.assertEqual(expected, observed)
+
+ async def test_query_params_with_type(self):
+ # noinspection PyPep8Naming
+ params = ModelType.build("Params", {"one": list[int], "two": int})
+ expected = params(one=[1, 3], two=2)
+
+ raw = mocked_request(query_params=[("one", "1"), ("two", "2"), ("one", "3")])
+ request = RestRequest(raw)
+ observed = await request.query_params(type_=params)
+
+ self.assertEqual(expected, observed)
+
+ async def test_query_params_raises(self):
+ raw = mocked_request()
+ request = RestRequest(raw)
+ with self.assertRaises(NotHasParamsException):
+ await request.query_params()
+
+ def test_has_params_false(self):
+ raw = mocked_request()
+ request = RestRequest(raw)
+ self.assertEqual(False, request.has_params)
+
+ def test_has_params_true(self):
+ raw = mocked_request(url_params=[("foo", "1"), ("bar", "2"), ("foo", "3")])
+ request = RestRequest(raw)
+ self.assertEqual(True, request.has_params)
+
+ raw = mocked_request(query_params=[("one", "1"), ("two", "2"), ("one", "3")])
+ request = RestRequest(raw)
+ self.assertEqual(True, request.has_params)
+
+ raw = mocked_request(
+ url_params=[("foo", "1"), ("bar", "2"), ("foo", "3")],
+ query_params=[("one", "1"), ("two", "2"), ("one", "3")],
+ )
+ request = RestRequest(raw)
+ self.assertEqual(True, request.has_params)
+
+ async def test_params(self):
+ expected = {"bar": "2", "foo": ["1", "3"], "one": ["1", "3"], "two": "2"}
+
+ raw = mocked_request(
+ url_params=[("foo", "1"), ("bar", "2"), ("foo", "3")],
+ query_params=[("one", "1"), ("two", "2"), ("one", "3")],
+ )
+ request = RestRequest(raw)
+ observed = await request.params()
+
+ self.assertEqual(expected, observed)
+
+ async def test_params_with_type(self):
+ # noinspection PyPep8Naming
+ params = ModelType.build("Params", {"foo": list[int], "bar": int, "one": list[int], "two": int})
+ expected = params(foo=[1, 3], bar=2, one=[1, 3], two=2)
+
+ raw = mocked_request(
+ url_params=[("foo", "1"), ("bar", "2"), ("foo", "3")],
+ query_params=[("one", "1"), ("two", "2"), ("one", "3")],
+ )
+ request = RestRequest(raw)
+ observed = await request.params(type_=params)
+
+ self.assertEqual(expected, observed)
+
+ async def test_params_raises(self):
+ raw = mocked_request()
+ request = RestRequest(raw)
+ with self.assertRaises(NotHasParamsException):
+ await request.params()
+
+
+class TestRestResponse(unittest.IsolatedAsyncioTestCase):
+ async def test_from_response(self):
+ base = Response("foobar")
+
+ response = RestResponse.from_response(base)
+
+ self.assertEqual(orjson.dumps("foobar"), await response.content())
+ self.assertEqual(APPLICATION_JSON, response.content_type)
+
+ def test_from_response_already(self):
+ base = RestResponse()
+
+ response = RestResponse.from_response(base)
+
+ self.assertEqual(base, response)
+
+ def test_from_response_empty(self):
+ response = RestResponse.from_response(None)
+
+ self.assertEqual(RestResponse(), response)
+
+ async def test_empty(self):
+ response = RestResponse()
+ self.assertEqual(None, await response.content())
+ self.assertEqual(APPLICATION_JSON, response.content_type)
+
+ async def test_content_json(self):
+ data = [FakeModel("foo"), FakeModel("bar")]
+ response = RestResponse(data)
+ self.assertEqual(orjson.dumps([item.avro_data for item in data]), await response.content())
+ self.assertEqual(APPLICATION_JSON, response.content_type)
+
+ async def test_content_form(self):
+ data = {"foo": "bar", "one": "two"}
+ response = RestResponse(data, content_type="application/x-www-form-urlencoded")
+ self.assertEqual("foo=bar&one=two".encode(), await response.content())
+ self.assertEqual(APPLICATION_URLENCODED, response.content_type)
+
+ async def test_content_bytes(self):
+ data = "foobar".encode()
+ response = RestResponse(data, content_type=APPLICATION_OCTET_STREAM)
+ self.assertEqual("foobar".encode(), await response.content())
+ self.assertEqual(APPLICATION_OCTET_STREAM, response.content_type)
+
+ async def test_content_bytes_raises(self):
+ data = 56
+ response = RestResponse(data, content_type=APPLICATION_OCTET_STREAM)
+ with self.assertRaises(ValueError):
+ await response.content()
+
+ async def test_content_text(self):
+ data = "foobar"
+ response = RestResponse(data, content_type=TEXT_PLAIN)
+ self.assertEqual("foobar".encode(), await response.content())
+ self.assertEqual(TEXT_PLAIN, response.content_type)
+
+ async def test_content_text_raises(self):
+ data = 56
+ response = RestResponse(data, content_type=TEXT_PLAIN)
+ with self.assertRaises(ValueError):
+ await response.content()
+
+ async def test_content_avro(self):
+ data = "foobar"
+ response = RestResponse(data, content_type=AVRO_BINARY)
+ self.assertEqual(data, MinosAvroProtocol.decode(await response.content()))
+ self.assertEqual(AVRO_BINARY, response.content_type)
+
+ async def test_content_image(self):
+ data = bytes("image", "utf-8")
+ response = RestResponse(data, content_type=IMAGE_PNG)
+ self.assertEqual(data, await response.content())
+ self.assertEqual(IMAGE_PNG, response.content_type)
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/packages/plugins/minos-graphql-aiohttp/tests/test_plugins/test_rest_aiohttp/test_services.py b/packages/plugins/minos-graphql-aiohttp/tests/test_plugins/test_rest_aiohttp/test_services.py
new file mode 100644
index 000000000..0f7c0188a
--- /dev/null
+++ b/packages/plugins/minos-graphql-aiohttp/tests/test_plugins/test_rest_aiohttp/test_services.py
@@ -0,0 +1,48 @@
+import unittest
+
+from aiohttp.test_utils import (
+ AioHTTPTestCase,
+)
+
+from minos.common import (
+ MinosConfig,
+)
+from minos.plugins.rest_aiohttp import (
+ RestService,
+)
+from tests.utils import (
+ BASE_PATH,
+)
+
+
+class TestRestService(AioHTTPTestCase):
+ CONFIG_FILE_PATH = BASE_PATH / "test_config.yml"
+
+ async def get_application(self):
+ """
+ Override the get_app method to return your application.
+ """
+ config = MinosConfig(self.CONFIG_FILE_PATH)
+ rest_interface = RestService(config=config)
+
+ return await rest_interface.create_application()
+
+ async def test_methods(self):
+ url = "/order"
+ resp = await self.client.request("GET", url)
+ assert resp.status == 200
+ text = await resp.text()
+ assert "get_order" in text
+
+ url = "/ticket"
+ resp = await self.client.request("POST", url)
+ assert resp.status == 200
+ text = await resp.text()
+ assert "ticket_added" in text
+
+ resp = await self.client.request("GET", "/system/health")
+ assert resp.status == 200
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/packages/plugins/minos-graphql-aiohttp/tests/test_plugins/test_rest_aiohttp/utils.py b/packages/plugins/minos-graphql-aiohttp/tests/test_plugins/test_rest_aiohttp/utils.py
new file mode 100644
index 000000000..2cb3e5466
--- /dev/null
+++ b/packages/plugins/minos-graphql-aiohttp/tests/test_plugins/test_rest_aiohttp/utils.py
@@ -0,0 +1,100 @@
+import json
+from typing import (
+ Any,
+ Optional,
+ Union,
+)
+from unittest.mock import (
+ AsyncMock,
+)
+from urllib.parse import (
+ urlencode,
+)
+from uuid import (
+ UUID,
+)
+
+from aiohttp import (
+ test_utils,
+ web,
+)
+from aiohttp.streams import (
+ EmptyStreamReader,
+)
+from multidict import (
+ MultiDict,
+)
+
+from minos.common import (
+ MinosAvroProtocol,
+)
+
+
+def json_mocked_request(data: Any, **kwargs) -> web.Request:
+ """For testng purposes."""
+ return mocked_request(json.dumps(data).encode(), content_type="application/json", **kwargs)
+
+
+def form_mocked_request(data: dict[str, Any], **kwargs) -> web.Request:
+ """For testng purposes."""
+ return mocked_request(
+ urlencode(data, doseq=True).encode(), content_type="application/x-www-form-urlencoded", **kwargs
+ )
+
+
+def avro_mocked_request(data: Any, schema: Any, **kwargs) -> web.Request:
+ """For testng purposes."""
+ return mocked_request(MinosAvroProtocol.encode(data, schema), content_type="avro/binary", **kwargs)
+
+
+def text_mocked_request(data: str, **kwargs) -> web.Request:
+ """For testng purposes."""
+ return mocked_request(data.encode(), content_type="text/plain", **kwargs)
+
+
+def bytes_mocked_request(data: bytes, **kwargs) -> web.Request:
+ """For testng purposes."""
+ return mocked_request(data, content_type="application/octet-stream", **kwargs)
+
+
+def mocked_request(
+ data: Optional[bytes] = None,
+ headers: Optional[dict[str, str]] = None,
+ user: Optional[Union[str, UUID]] = None,
+ content_type: Optional[str] = None,
+ method: str = "POST",
+ path: str = "localhost",
+ url_params: Optional[list[tuple[str, str]]] = None,
+ query_params: Optional[list[tuple[str, str]]] = None,
+) -> web.Request:
+ """For testing purposes"""
+ if headers is None:
+ headers = dict()
+ else:
+ headers = headers.copy()
+
+ if user is not None:
+ headers["user"] = str(user)
+
+ if content_type is not None:
+ headers["Content-Type"] = content_type
+
+ kwargs = {
+ "method": method,
+ "path": path,
+ "headers": headers,
+ }
+ if data is None:
+ kwargs["payload"] = EmptyStreamReader()
+
+ request = test_utils.make_mocked_request(**kwargs)
+ request.read = AsyncMock(return_value=data)
+
+ if url_params is not None:
+ # noinspection PyProtectedMember
+ request._rel_url = request._rel_url.with_query(url_params)
+
+ if query_params is not None:
+ request._match_info = MultiDict(map(lambda kv: (str(kv[0]), str(kv[1])), query_params))
+
+ return request
diff --git a/packages/plugins/minos-graphql-aiohttp/tests/utils.py b/packages/plugins/minos-graphql-aiohttp/tests/utils.py
new file mode 100644
index 000000000..d7424f6ce
--- /dev/null
+++ b/packages/plugins/minos-graphql-aiohttp/tests/utils.py
@@ -0,0 +1,27 @@
+from functools import (
+ total_ordering,
+)
+from pathlib import (
+ Path,
+)
+from typing import (
+ Any,
+)
+
+from minos.common import (
+ DeclarativeModel,
+)
+
+BASE_PATH = Path(__file__).parent
+CONFIG_FILE_PATH = BASE_PATH / "test_config.yml"
+
+
+@total_ordering
+class FakeModel(DeclarativeModel):
+ """For testing purposes"""
+
+ data: Any
+
+ def __lt__(self, other: Any) -> bool:
+ # noinspection PyBroadException
+ return isinstance(other, type(self)) and self.data < other.data
From 05f92c60586bdce768651422fdf607beebe08cff Mon Sep 17 00:00:00 2001
From: Vladyslav Fenchak
Date: Thu, 3 Mar 2022 17:47:54 +0100
Subject: [PATCH 02/44] ISSUE #149
---
.../minos/networks/__init__.py | 2 ++
.../minos/networks/decorators/__init__.py | 2 ++
.../minos/networks/decorators/analyzers.py | 10 ++++++
.../minos/networks/decorators/api.py | 2 ++
.../decorators/definitions/__init__.py | 4 +++
.../decorators/definitions/graphql.py | 34 +++++++++++++++++++
.../networks/decorators/definitions/kinds.py | 1 +
.../tests/services/graphql.py | 14 ++++++++
.../test_decorators/test_analyzers.py | 16 +++++++++
.../test_networks/test_decorators/test_api.py | 5 +++
.../tests/utils.py | 12 +++++++
11 files changed, 102 insertions(+)
create mode 100644 packages/core/minos-microservice-networks/minos/networks/decorators/definitions/graphql.py
create mode 100644 packages/core/minos-microservice-networks/tests/services/graphql.py
diff --git a/packages/core/minos-microservice-networks/minos/networks/__init__.py b/packages/core/minos-microservice-networks/minos/networks/__init__.py
index 9d28eff31..a6b81ea39 100644
--- a/packages/core/minos-microservice-networks/minos/networks/__init__.py
+++ b/packages/core/minos-microservice-networks/minos/networks/__init__.py
@@ -63,6 +63,8 @@
RestCommandEnrouteDecorator,
RestEnrouteDecorator,
RestQueryEnrouteDecorator,
+ GraphqlEnrouteDecorator,
+ GraphqlQueryEnrouteDecorator,
enroute,
)
from .discovery import (
diff --git a/packages/core/minos-microservice-networks/minos/networks/decorators/__init__.py b/packages/core/minos-microservice-networks/minos/networks/decorators/__init__.py
index 05204050d..08529cf15 100644
--- a/packages/core/minos-microservice-networks/minos/networks/decorators/__init__.py
+++ b/packages/core/minos-microservice-networks/minos/networks/decorators/__init__.py
@@ -28,4 +28,6 @@
RestCommandEnrouteDecorator,
RestEnrouteDecorator,
RestQueryEnrouteDecorator,
+ GraphqlEnrouteDecorator,
+ GraphqlQueryEnrouteDecorator,
)
diff --git a/packages/core/minos-microservice-networks/minos/networks/decorators/analyzers.py b/packages/core/minos-microservice-networks/minos/networks/decorators/analyzers.py
index 96d071712..ba95a1085 100644
--- a/packages/core/minos-microservice-networks/minos/networks/decorators/analyzers.py
+++ b/packages/core/minos-microservice-networks/minos/networks/decorators/analyzers.py
@@ -28,6 +28,8 @@
RestCommandEnrouteDecorator,
RestEnrouteDecorator,
RestQueryEnrouteDecorator,
+ GraphqlEnrouteDecorator,
+ GraphqlQueryEnrouteDecorator,
)
@@ -50,6 +52,14 @@ def get_rest_command_query(self) -> dict[str, set[RestEnrouteDecorator]]:
# noinspection PyTypeChecker
return self._get_items({RestCommandEnrouteDecorator, RestQueryEnrouteDecorator})
+ def get_graphql(self) -> dict[str, set[GraphqlEnrouteDecorator]]:
+ """Returns rest's command and query values.
+
+ :return: A mapping with functions as keys and a sets of decorators as values.
+ """
+ # noinspection PyTypeChecker
+ return self._get_items({GraphqlQueryEnrouteDecorator})
+
def get_broker_command_query_event(self) -> dict[str, set[BrokerEnrouteDecorator]]:
"""Returns broker's command, query and event values.
diff --git a/packages/core/minos-microservice-networks/minos/networks/decorators/api.py b/packages/core/minos-microservice-networks/minos/networks/decorators/api.py
index b3eddd752..7964f11f8 100644
--- a/packages/core/minos-microservice-networks/minos/networks/decorators/api.py
+++ b/packages/core/minos-microservice-networks/minos/networks/decorators/api.py
@@ -9,6 +9,7 @@
PeriodicEventEnrouteDecorator,
RestCommandEnrouteDecorator,
RestQueryEnrouteDecorator,
+ GraphqlQueryEnrouteDecorator
)
@@ -39,6 +40,7 @@ class Enroute:
broker = BrokerEnroute
rest = RestEnroute
periodic = PeriodicEnroute
+ graphql = GraphqlQueryEnrouteDecorator
enroute = Enroute
diff --git a/packages/core/minos-microservice-networks/minos/networks/decorators/definitions/__init__.py b/packages/core/minos-microservice-networks/minos/networks/decorators/definitions/__init__.py
index e59e1c305..8454af195 100644
--- a/packages/core/minos-microservice-networks/minos/networks/decorators/definitions/__init__.py
+++ b/packages/core/minos-microservice-networks/minos/networks/decorators/definitions/__init__.py
@@ -22,3 +22,7 @@
RestEnrouteDecorator,
RestQueryEnrouteDecorator,
)
+from .graphql import (
+ GraphqlEnrouteDecorator,
+ GraphqlQueryEnrouteDecorator
+)
diff --git a/packages/core/minos-microservice-networks/minos/networks/decorators/definitions/graphql.py b/packages/core/minos-microservice-networks/minos/networks/decorators/definitions/graphql.py
new file mode 100644
index 000000000..95624d0ea
--- /dev/null
+++ b/packages/core/minos-microservice-networks/minos/networks/decorators/definitions/graphql.py
@@ -0,0 +1,34 @@
+from abc import (
+ ABC,
+)
+from typing import (
+ Final,
+ Iterable,
+)
+
+from .abc import (
+ EnrouteDecorator,
+)
+from .kinds import (
+ EnrouteDecoratorKind,
+)
+
+
+class GraphqlEnrouteDecorator(EnrouteDecorator, ABC):
+ """Rest Enroute class"""
+
+ def __init__(self, url: str, method: str):
+ self.url = url
+ self.method = method
+
+ def __iter__(self) -> Iterable:
+ yield from (
+ self.url,
+ self.method,
+ )
+
+
+class GraphqlQueryEnrouteDecorator(GraphqlEnrouteDecorator):
+ """Rest Command Enroute class"""
+
+ KIND: Final[EnrouteDecoratorKind] = EnrouteDecoratorKind.Graphql
diff --git a/packages/core/minos-microservice-networks/minos/networks/decorators/definitions/kinds.py b/packages/core/minos-microservice-networks/minos/networks/decorators/definitions/kinds.py
index 513947c0a..ea3bb3fab 100644
--- a/packages/core/minos-microservice-networks/minos/networks/decorators/definitions/kinds.py
+++ b/packages/core/minos-microservice-networks/minos/networks/decorators/definitions/kinds.py
@@ -10,6 +10,7 @@ class EnrouteDecoratorKind(Enum):
Command = auto()
Query = auto()
Event = auto()
+ Graphql = auto()
@property
def pre_fn_name(self) -> str:
diff --git a/packages/core/minos-microservice-networks/tests/services/graphql.py b/packages/core/minos-microservice-networks/tests/services/graphql.py
new file mode 100644
index 000000000..594089bf5
--- /dev/null
+++ b/packages/core/minos-microservice-networks/tests/services/graphql.py
@@ -0,0 +1,14 @@
+from minos.networks import (
+ Request,
+ Response,
+ enroute,
+)
+
+
+class GraphqlService:
+ @enroute.graphql(url="/graphql", method="POST")
+ def add_ticket(self, request: Request) -> Response:
+ return Response({
+ "data": {},
+ "errors": []
+ })
diff --git a/packages/core/minos-microservice-networks/tests/test_networks/test_decorators/test_analyzers.py b/packages/core/minos-microservice-networks/tests/test_networks/test_decorators/test_analyzers.py
index ab1bfae50..c04cb393c 100644
--- a/packages/core/minos-microservice-networks/tests/test_networks/test_decorators/test_analyzers.py
+++ b/packages/core/minos-microservice-networks/tests/test_networks/test_decorators/test_analyzers.py
@@ -11,6 +11,7 @@
PeriodicEventEnrouteDecorator,
RestCommandEnrouteDecorator,
RestQueryEnrouteDecorator,
+ GraphqlQueryEnrouteDecorator,
)
from tests.utils import (
FakeService,
@@ -41,6 +42,9 @@ def test_get_all(self):
},
"send_newsletter": {PeriodicEventEnrouteDecorator("@daily")},
"check_inactive_users": {PeriodicEventEnrouteDecorator("@daily")},
+ "graphql_handler": {GraphqlQueryEnrouteDecorator("graphql/", "GET")},
+ "graphql_handler2": {GraphqlQueryEnrouteDecorator("graphql-2/", "GET")},
+ "graphql_handler3": {GraphqlQueryEnrouteDecorator("graphql-3/", "POST")},
}
self.assertEqual(expected, observed)
@@ -57,6 +61,18 @@ def test_get_rest_command_query(self):
self.assertEqual(expected, observed)
+ def test_get_graphql(self):
+ analyzer = EnrouteAnalyzer(FakeService)
+
+ observed = analyzer.get_graphql()
+ expected = {
+ "graphql_handler": {GraphqlQueryEnrouteDecorator("graphql/", "GET")},
+ "graphql_handler2": {GraphqlQueryEnrouteDecorator("graphql-2/", "GET")},
+ "graphql_handler3": {GraphqlQueryEnrouteDecorator("graphql-3/", "POST")},
+ }
+
+ self.assertEqual(expected, observed)
+
def test_get_broker_command_query_event(self):
analyzer = EnrouteAnalyzer(FakeService)
diff --git a/packages/core/minos-microservice-networks/tests/test_networks/test_decorators/test_api.py b/packages/core/minos-microservice-networks/tests/test_networks/test_decorators/test_api.py
index 3ac33cd15..ffd123128 100644
--- a/packages/core/minos-microservice-networks/tests/test_networks/test_decorators/test_api.py
+++ b/packages/core/minos-microservice-networks/tests/test_networks/test_decorators/test_api.py
@@ -7,6 +7,7 @@
PeriodicEventEnrouteDecorator,
RestCommandEnrouteDecorator,
RestQueryEnrouteDecorator,
+ GraphqlQueryEnrouteDecorator,
enroute,
)
@@ -20,6 +21,10 @@ def test_rest_query(self):
decorator = enroute.rest.query(url="tickets/", method="GET")
self.assertEqual(RestQueryEnrouteDecorator("tickets/", "GET"), decorator)
+ def test_graphql(self):
+ decorator = enroute.graphql(url="graphql/", method="POST")
+ self.assertEqual(GraphqlQueryEnrouteDecorator("graphql/", "POST"), decorator)
+
def test_rest_event_raises(self):
with self.assertRaises(AttributeError):
enroute.rest.event("CreateTicket")
diff --git a/packages/core/minos-microservice-networks/tests/utils.py b/packages/core/minos-microservice-networks/tests/utils.py
index 980260a2b..da55b788e 100644
--- a/packages/core/minos-microservice-networks/tests/utils.py
+++ b/packages/core/minos-microservice-networks/tests/utils.py
@@ -169,6 +169,18 @@ def bar(self, request: Request):
"""For testing purposes."""
return Response("bar")
+ @enroute.graphql(url="graphql/", method="GET")
+ def graphql_handler(self):
+ return
+
+ @enroute.graphql(url="graphql-2/", method="GET")
+ def graphql_handler2(self):
+ return
+
+ @enroute.graphql(url="graphql-3/", method="POST")
+ def graphql_handler3(self):
+ return
+
class FakeServiceWithGetEnroute:
@staticmethod
From e9036e39a1deb1a298c9bc860548dd2f64681b8a Mon Sep 17 00:00:00 2001
From: Vladyslav Fenchak
Date: Thu, 3 Mar 2022 17:51:35 +0100
Subject: [PATCH 03/44] ISSUE #149
---
.../plugins/graphql_aiohttp/graphql_example.py | 3 +--
.../minos/plugins/graphql_aiohttp/handlers.py | 9 ++++++++-
.../minos/plugins/graphql_aiohttp/main.py | 10 ++++++++++
.../minos/plugins/graphql_aiohttp/services.py | 17 +++++++----------
4 files changed, 26 insertions(+), 13 deletions(-)
create mode 100644 packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/main.py
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/graphql_example.py b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/graphql_example.py
index 99bb89a39..c3eb4b24e 100644
--- a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/graphql_example.py
+++ b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/graphql_example.py
@@ -3,8 +3,7 @@
graphql, GraphQLSchema, GraphQLObjectType, GraphQLField, GraphQLString)
-async def resolve_hello(obj, info):
- await asyncio.sleep(3)
+def resolve_hello(obj, info):
return 'world'
schema = GraphQLSchema(
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/handlers.py b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/handlers.py
index 573a751a4..8da0723f7 100644
--- a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/handlers.py
+++ b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/handlers.py
@@ -37,6 +37,9 @@
from .responses import (
RestResponse,
)
+from graphql_server.aiohttp import GraphQLView
+
+from .graphql_example import schema
logger = logging.getLogger(__name__)
@@ -99,7 +102,8 @@ def get_app(self) -> web.Application:
@cached_property
def _app(self) -> web.Application:
app = web.Application()
- self._mount_routes(app)
+ #self._mount_routes(app)
+ self._mount_graphql(app)
return app
def _mount_routes(self, app: web.Application):
@@ -114,6 +118,9 @@ def _mount_one_route(self, method: str, url: str, action: Callable, app: web.App
handler = self.get_callback(action)
app.router.add_route(method, url, handler)
+ def _mount_graphql(self, app: web.Application):
+ GraphQLView.attach(app, schema=schema, graphiql=True)
+
@staticmethod
def get_callback(
fn: Callable[[RestRequest], Union[Optional[RestResponse], Awaitable[Optional[RestResponse]]]]
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/main.py b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/main.py
new file mode 100644
index 000000000..87695eafb
--- /dev/null
+++ b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/main.py
@@ -0,0 +1,10 @@
+from aiohttp import web
+from minos.plugins.graphql_aiohttp.services import RestService
+
+
+def main():
+ web.run_app(RestService().create_application())
+
+
+if __name__ == "__main__":
+ main()
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/services.py b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/services.py
index e9b01883e..fd9e6d1b5 100644
--- a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/services.py
+++ b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/services.py
@@ -4,7 +4,6 @@
from aiomisc.service.aiohttp import (
AIOHTTPService,
)
-
from .handlers import (
RestHandler,
)
@@ -22,9 +21,12 @@ class RestService(AIOHTTPService):
"""
def __init__(self, **kwargs):
- pass
#self.handler = RestHandler.from_config(**kwargs)
#super().__init__(**(kwargs | {"address": self.handler.host, "port": self.handler.port}))
+ super().__init__(**(kwargs | {"address": "localhost", "port": 7030}))
+
+ def _mount_graphql(self, app: web.Application):
+ GraphQLView.attach(app, schema=schema, graphiql=True)
async def create_application(self) -> web.Application:
"""Create the web application.
@@ -32,13 +34,8 @@ async def create_application(self) -> web.Application:
:return: A ``web.Application`` instance.
"""
app = web.Application()
-
- GraphQLView.attach(app, schema=schema, graphiql=True)
+ # self._mount_routes(app)
+ self._mount_graphql(app)
return app
- # return self.handler.get_app() # pragma: no cover
-
-
-async def main():
- web.run_app(RestService.create_application())
-
+ #return self.handler.get_app() # pragma: no cover
From 2aa561d6d5bc0c02a87277ccd4840b42c782e29a Mon Sep 17 00:00:00 2001
From: "Restyled.io"
Date: Thu, 3 Mar 2022 16:51:46 +0000
Subject: [PATCH 04/44] Restyled by black
---
.../minos/networks/decorators/api.py | 2 +-
.../networks/decorators/definitions/__init__.py | 5 +----
.../tests/services/graphql.py | 5 +----
.../plugins/graphql_aiohttp/graphql_example.py | 15 +++++----------
.../minos/plugins/graphql_aiohttp/handlers.py | 2 +-
.../minos/plugins/graphql_aiohttp/services.py | 7 +++----
6 files changed, 12 insertions(+), 24 deletions(-)
diff --git a/packages/core/minos-microservice-networks/minos/networks/decorators/api.py b/packages/core/minos-microservice-networks/minos/networks/decorators/api.py
index 7964f11f8..34e4721ef 100644
--- a/packages/core/minos-microservice-networks/minos/networks/decorators/api.py
+++ b/packages/core/minos-microservice-networks/minos/networks/decorators/api.py
@@ -9,7 +9,7 @@
PeriodicEventEnrouteDecorator,
RestCommandEnrouteDecorator,
RestQueryEnrouteDecorator,
- GraphqlQueryEnrouteDecorator
+ GraphqlQueryEnrouteDecorator,
)
diff --git a/packages/core/minos-microservice-networks/minos/networks/decorators/definitions/__init__.py b/packages/core/minos-microservice-networks/minos/networks/decorators/definitions/__init__.py
index 8454af195..3b570038d 100644
--- a/packages/core/minos-microservice-networks/minos/networks/decorators/definitions/__init__.py
+++ b/packages/core/minos-microservice-networks/minos/networks/decorators/definitions/__init__.py
@@ -22,7 +22,4 @@
RestEnrouteDecorator,
RestQueryEnrouteDecorator,
)
-from .graphql import (
- GraphqlEnrouteDecorator,
- GraphqlQueryEnrouteDecorator
-)
+from .graphql import GraphqlEnrouteDecorator, GraphqlQueryEnrouteDecorator
diff --git a/packages/core/minos-microservice-networks/tests/services/graphql.py b/packages/core/minos-microservice-networks/tests/services/graphql.py
index 594089bf5..b4cc7d4f4 100644
--- a/packages/core/minos-microservice-networks/tests/services/graphql.py
+++ b/packages/core/minos-microservice-networks/tests/services/graphql.py
@@ -8,7 +8,4 @@
class GraphqlService:
@enroute.graphql(url="/graphql", method="POST")
def add_ticket(self, request: Request) -> Response:
- return Response({
- "data": {},
- "errors": []
- })
+ return Response({"data": {}, "errors": []})
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/graphql_example.py b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/graphql_example.py
index c3eb4b24e..1cd976f3e 100644
--- a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/graphql_example.py
+++ b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/graphql_example.py
@@ -1,16 +1,11 @@
import asyncio
-from graphql import (
- graphql, GraphQLSchema, GraphQLObjectType, GraphQLField, GraphQLString)
+from graphql import graphql, GraphQLSchema, GraphQLObjectType, GraphQLField, GraphQLString
def resolve_hello(obj, info):
- return 'world'
+ return "world"
+
schema = GraphQLSchema(
- query=GraphQLObjectType(
- name='RootQueryType',
- fields={
- 'hello': GraphQLField(
- GraphQLString,
- resolve=resolve_hello)
- }))
+ query=GraphQLObjectType(name="RootQueryType", fields={"hello": GraphQLField(GraphQLString, resolve=resolve_hello)})
+)
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/handlers.py b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/handlers.py
index 8da0723f7..38e33179b 100644
--- a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/handlers.py
+++ b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/handlers.py
@@ -102,7 +102,7 @@ def get_app(self) -> web.Application:
@cached_property
def _app(self) -> web.Application:
app = web.Application()
- #self._mount_routes(app)
+ # self._mount_routes(app)
self._mount_graphql(app)
return app
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/services.py b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/services.py
index fd9e6d1b5..064a54894 100644
--- a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/services.py
+++ b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/services.py
@@ -21,8 +21,8 @@ class RestService(AIOHTTPService):
"""
def __init__(self, **kwargs):
- #self.handler = RestHandler.from_config(**kwargs)
- #super().__init__(**(kwargs | {"address": self.handler.host, "port": self.handler.port}))
+ # self.handler = RestHandler.from_config(**kwargs)
+ # super().__init__(**(kwargs | {"address": self.handler.host, "port": self.handler.port}))
super().__init__(**(kwargs | {"address": "localhost", "port": 7030}))
def _mount_graphql(self, app: web.Application):
@@ -37,5 +37,4 @@ async def create_application(self) -> web.Application:
# self._mount_routes(app)
self._mount_graphql(app)
return app
- #return self.handler.get_app() # pragma: no cover
-
+ # return self.handler.get_app() # pragma: no cover
From 67b40a91f0bde148f92c093952d969dcfd66113b Mon Sep 17 00:00:00 2001
From: "Restyled.io"
Date: Thu, 3 Mar 2022 16:51:47 +0000
Subject: [PATCH 05/44] Restyled by isort
---
.../minos/networks/__init__.py | 4 ++--
.../minos/networks/decorators/__init__.py | 4 ++--
.../minos/networks/decorators/analyzers.py | 4 ++--
.../minos/networks/decorators/api.py | 2 +-
.../minos/networks/decorators/definitions/__init__.py | 5 ++++-
.../test_networks/test_decorators/test_analyzers.py | 2 +-
.../tests/test_networks/test_decorators/test_api.py | 2 +-
.../minos/plugins/graphql_aiohttp/graphql_example.py | 9 ++++++++-
.../minos/plugins/graphql_aiohttp/handlers.py | 9 ++++++---
.../minos/plugins/graphql_aiohttp/main.py | 9 +++++++--
.../minos/plugins/graphql_aiohttp/services.py | 10 +++++++---
11 files changed, 41 insertions(+), 19 deletions(-)
diff --git a/packages/core/minos-microservice-networks/minos/networks/__init__.py b/packages/core/minos-microservice-networks/minos/networks/__init__.py
index a6b81ea39..0a5b567f8 100644
--- a/packages/core/minos-microservice-networks/minos/networks/__init__.py
+++ b/packages/core/minos-microservice-networks/minos/networks/__init__.py
@@ -55,6 +55,8 @@
EnrouteBuilder,
EnrouteDecorator,
EnrouteDecoratorKind,
+ GraphqlEnrouteDecorator,
+ GraphqlQueryEnrouteDecorator,
Handler,
HandlerMeta,
HandlerWrapper,
@@ -63,8 +65,6 @@
RestCommandEnrouteDecorator,
RestEnrouteDecorator,
RestQueryEnrouteDecorator,
- GraphqlEnrouteDecorator,
- GraphqlQueryEnrouteDecorator,
enroute,
)
from .discovery import (
diff --git a/packages/core/minos-microservice-networks/minos/networks/decorators/__init__.py b/packages/core/minos-microservice-networks/minos/networks/decorators/__init__.py
index 08529cf15..521edd8fc 100644
--- a/packages/core/minos-microservice-networks/minos/networks/decorators/__init__.py
+++ b/packages/core/minos-microservice-networks/minos/networks/decorators/__init__.py
@@ -23,11 +23,11 @@
CheckDecorator,
EnrouteDecorator,
EnrouteDecoratorKind,
+ GraphqlEnrouteDecorator,
+ GraphqlQueryEnrouteDecorator,
PeriodicEnrouteDecorator,
PeriodicEventEnrouteDecorator,
RestCommandEnrouteDecorator,
RestEnrouteDecorator,
RestQueryEnrouteDecorator,
- GraphqlEnrouteDecorator,
- GraphqlQueryEnrouteDecorator,
)
diff --git a/packages/core/minos-microservice-networks/minos/networks/decorators/analyzers.py b/packages/core/minos-microservice-networks/minos/networks/decorators/analyzers.py
index ba95a1085..ea8f8e4a4 100644
--- a/packages/core/minos-microservice-networks/minos/networks/decorators/analyzers.py
+++ b/packages/core/minos-microservice-networks/minos/networks/decorators/analyzers.py
@@ -24,12 +24,12 @@
BrokerEventEnrouteDecorator,
BrokerQueryEnrouteDecorator,
EnrouteDecorator,
+ GraphqlEnrouteDecorator,
+ GraphqlQueryEnrouteDecorator,
PeriodicEventEnrouteDecorator,
RestCommandEnrouteDecorator,
RestEnrouteDecorator,
RestQueryEnrouteDecorator,
- GraphqlEnrouteDecorator,
- GraphqlQueryEnrouteDecorator,
)
diff --git a/packages/core/minos-microservice-networks/minos/networks/decorators/api.py b/packages/core/minos-microservice-networks/minos/networks/decorators/api.py
index 34e4721ef..aab9ae7dd 100644
--- a/packages/core/minos-microservice-networks/minos/networks/decorators/api.py
+++ b/packages/core/minos-microservice-networks/minos/networks/decorators/api.py
@@ -6,10 +6,10 @@
BrokerCommandEnrouteDecorator,
BrokerEventEnrouteDecorator,
BrokerQueryEnrouteDecorator,
+ GraphqlQueryEnrouteDecorator,
PeriodicEventEnrouteDecorator,
RestCommandEnrouteDecorator,
RestQueryEnrouteDecorator,
- GraphqlQueryEnrouteDecorator,
)
diff --git a/packages/core/minos-microservice-networks/minos/networks/decorators/definitions/__init__.py b/packages/core/minos-microservice-networks/minos/networks/decorators/definitions/__init__.py
index 3b570038d..20f0b6a83 100644
--- a/packages/core/minos-microservice-networks/minos/networks/decorators/definitions/__init__.py
+++ b/packages/core/minos-microservice-networks/minos/networks/decorators/definitions/__init__.py
@@ -10,6 +10,10 @@
from .checkers import (
CheckDecorator,
)
+from .graphql import (
+ GraphqlEnrouteDecorator,
+ GraphqlQueryEnrouteDecorator,
+)
from .kinds import (
EnrouteDecoratorKind,
)
@@ -22,4 +26,3 @@
RestEnrouteDecorator,
RestQueryEnrouteDecorator,
)
-from .graphql import GraphqlEnrouteDecorator, GraphqlQueryEnrouteDecorator
diff --git a/packages/core/minos-microservice-networks/tests/test_networks/test_decorators/test_analyzers.py b/packages/core/minos-microservice-networks/tests/test_networks/test_decorators/test_analyzers.py
index c04cb393c..a8fe39b6e 100644
--- a/packages/core/minos-microservice-networks/tests/test_networks/test_decorators/test_analyzers.py
+++ b/packages/core/minos-microservice-networks/tests/test_networks/test_decorators/test_analyzers.py
@@ -8,10 +8,10 @@
BrokerEventEnrouteDecorator,
BrokerQueryEnrouteDecorator,
EnrouteAnalyzer,
+ GraphqlQueryEnrouteDecorator,
PeriodicEventEnrouteDecorator,
RestCommandEnrouteDecorator,
RestQueryEnrouteDecorator,
- GraphqlQueryEnrouteDecorator,
)
from tests.utils import (
FakeService,
diff --git a/packages/core/minos-microservice-networks/tests/test_networks/test_decorators/test_api.py b/packages/core/minos-microservice-networks/tests/test_networks/test_decorators/test_api.py
index ffd123128..af8e1fd9e 100644
--- a/packages/core/minos-microservice-networks/tests/test_networks/test_decorators/test_api.py
+++ b/packages/core/minos-microservice-networks/tests/test_networks/test_decorators/test_api.py
@@ -4,10 +4,10 @@
BrokerCommandEnrouteDecorator,
BrokerEventEnrouteDecorator,
BrokerQueryEnrouteDecorator,
+ GraphqlQueryEnrouteDecorator,
PeriodicEventEnrouteDecorator,
RestCommandEnrouteDecorator,
RestQueryEnrouteDecorator,
- GraphqlQueryEnrouteDecorator,
enroute,
)
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/graphql_example.py b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/graphql_example.py
index 1cd976f3e..e17e52148 100644
--- a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/graphql_example.py
+++ b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/graphql_example.py
@@ -1,5 +1,12 @@
import asyncio
-from graphql import graphql, GraphQLSchema, GraphQLObjectType, GraphQLField, GraphQLString
+
+from graphql import (
+ GraphQLField,
+ GraphQLObjectType,
+ GraphQLSchema,
+ GraphQLString,
+ graphql,
+)
def resolve_hello(obj, info):
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/handlers.py b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/handlers.py
index 38e33179b..cad7ce722 100644
--- a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/handlers.py
+++ b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/handlers.py
@@ -20,6 +20,9 @@
from aiohttp import (
web,
)
+from graphql_server.aiohttp import (
+ GraphQLView,
+)
from minos.common import (
MinosConfig,
@@ -31,15 +34,15 @@
ResponseException,
)
+from .graphql_example import (
+ schema,
+)
from .requests import (
RestRequest,
)
from .responses import (
RestResponse,
)
-from graphql_server.aiohttp import GraphQLView
-
-from .graphql_example import schema
logger = logging.getLogger(__name__)
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/main.py b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/main.py
index 87695eafb..b751c179b 100644
--- a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/main.py
+++ b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/main.py
@@ -1,5 +1,10 @@
-from aiohttp import web
-from minos.plugins.graphql_aiohttp.services import RestService
+from aiohttp import (
+ web,
+)
+
+from minos.plugins.graphql_aiohttp.services import (
+ RestService,
+)
def main():
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/services.py b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/services.py
index 064a54894..c1ff1919c 100644
--- a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/services.py
+++ b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/services.py
@@ -4,12 +4,16 @@
from aiomisc.service.aiohttp import (
AIOHTTPService,
)
+from graphql_server.aiohttp import (
+ GraphQLView,
+)
+
+from .graphql_example import (
+ schema,
+)
from .handlers import (
RestHandler,
)
-from graphql_server.aiohttp import GraphQLView
-
-from .graphql_example import schema
class RestService(AIOHTTPService):
From cbd81baaaf72d335abf7e6d00f35ce3ead2c0c34 Mon Sep 17 00:00:00 2001
From: Vladyslav Fenchak
Date: Fri, 4 Mar 2022 13:39:04 +0100
Subject: [PATCH 06/44] ISSUE #149
---
.../test_handlers.py | 4 ++--
.../test_requests.py | 4 ++--
.../test_services.py | 2 +-
.../{test_rest_aiohttp => test_graphql_aiohttp}/utils.py | 0
4 files changed, 5 insertions(+), 5 deletions(-)
rename packages/plugins/minos-graphql-aiohttp/tests/test_plugins/{test_rest_aiohttp => test_graphql_aiohttp}/test_handlers.py (97%)
rename packages/plugins/minos-graphql-aiohttp/tests/test_plugins/{test_rest_aiohttp => test_graphql_aiohttp}/test_requests.py (99%)
rename packages/plugins/minos-graphql-aiohttp/tests/test_plugins/{test_rest_aiohttp => test_graphql_aiohttp}/test_services.py (96%)
rename packages/plugins/minos-graphql-aiohttp/tests/test_plugins/{test_rest_aiohttp => test_graphql_aiohttp}/utils.py (100%)
diff --git a/packages/plugins/minos-graphql-aiohttp/tests/test_plugins/test_rest_aiohttp/test_handlers.py b/packages/plugins/minos-graphql-aiohttp/tests/test_plugins/test_graphql_aiohttp/test_handlers.py
similarity index 97%
rename from packages/plugins/minos-graphql-aiohttp/tests/test_plugins/test_rest_aiohttp/test_handlers.py
rename to packages/plugins/minos-graphql-aiohttp/tests/test_plugins/test_graphql_aiohttp/test_handlers.py
index 78a174cf9..2616a6cff 100644
--- a/packages/plugins/minos-graphql-aiohttp/tests/test_plugins/test_rest_aiohttp/test_handlers.py
+++ b/packages/plugins/minos-graphql-aiohttp/tests/test_plugins/test_graphql_aiohttp/test_handlers.py
@@ -24,12 +24,12 @@
Request,
Response,
)
-from minos.plugins.rest_aiohttp import (
+from minos.plugins.graphql_aiohttp import (
RestHandler,
RestResponse,
RestResponseException,
)
-from tests.test_plugins.test_rest_aiohttp.utils import (
+from tests.test_plugins.test_graphql_aiohttp.utils import (
json_mocked_request,
mocked_request,
)
diff --git a/packages/plugins/minos-graphql-aiohttp/tests/test_plugins/test_rest_aiohttp/test_requests.py b/packages/plugins/minos-graphql-aiohttp/tests/test_plugins/test_graphql_aiohttp/test_requests.py
similarity index 99%
rename from packages/plugins/minos-graphql-aiohttp/tests/test_plugins/test_rest_aiohttp/test_requests.py
rename to packages/plugins/minos-graphql-aiohttp/tests/test_plugins/test_graphql_aiohttp/test_requests.py
index 0c824073f..af0d8ec7f 100644
--- a/packages/plugins/minos-graphql-aiohttp/tests/test_plugins/test_rest_aiohttp/test_requests.py
+++ b/packages/plugins/minos-graphql-aiohttp/tests/test_plugins/test_graphql_aiohttp/test_requests.py
@@ -18,11 +18,11 @@
NotHasParamsException,
Response,
)
-from minos.plugins.rest_aiohttp import (
+from minos.plugins.graphql_aiohttp import (
RestRequest,
RestResponse,
)
-from tests.test_plugins.test_rest_aiohttp.utils import (
+from tests.test_plugins.test_graphql_aiohttp.utils import (
avro_mocked_request,
bytes_mocked_request,
form_mocked_request,
diff --git a/packages/plugins/minos-graphql-aiohttp/tests/test_plugins/test_rest_aiohttp/test_services.py b/packages/plugins/minos-graphql-aiohttp/tests/test_plugins/test_graphql_aiohttp/test_services.py
similarity index 96%
rename from packages/plugins/minos-graphql-aiohttp/tests/test_plugins/test_rest_aiohttp/test_services.py
rename to packages/plugins/minos-graphql-aiohttp/tests/test_plugins/test_graphql_aiohttp/test_services.py
index 0f7c0188a..640f76fc4 100644
--- a/packages/plugins/minos-graphql-aiohttp/tests/test_plugins/test_rest_aiohttp/test_services.py
+++ b/packages/plugins/minos-graphql-aiohttp/tests/test_plugins/test_graphql_aiohttp/test_services.py
@@ -7,7 +7,7 @@
from minos.common import (
MinosConfig,
)
-from minos.plugins.rest_aiohttp import (
+from minos.plugins.graphql_aiohttp import (
RestService,
)
from tests.utils import (
diff --git a/packages/plugins/minos-graphql-aiohttp/tests/test_plugins/test_rest_aiohttp/utils.py b/packages/plugins/minos-graphql-aiohttp/tests/test_plugins/test_graphql_aiohttp/utils.py
similarity index 100%
rename from packages/plugins/minos-graphql-aiohttp/tests/test_plugins/test_rest_aiohttp/utils.py
rename to packages/plugins/minos-graphql-aiohttp/tests/test_plugins/test_graphql_aiohttp/utils.py
From 5e6c21cf056c2d637f3b18c4d6ad1b08d26136ef Mon Sep 17 00:00:00 2001
From: Vladyslav Fenchak
Date: Tue, 8 Mar 2022 19:18:47 +0100
Subject: [PATCH 07/44] ISSUE #149
---
.../minos/plugins/graphql_aiohttp/__init__.py | 3 -
.../minos/plugins/graphql_aiohttp/handlers.py | 24 ++-
.../graphql_aiohttp/handlers/__init__.py | 0
.../plugins/graphql_aiohttp/handlers/abc.py | 100 +++++++++++++
.../graphql_aiohttp/handlers/graphiql.py | 10 ++
.../graphql_aiohttp/handlers/graphql.py | 11 ++
.../minos/plugins/graphql_aiohttp/schema.py | 10 ++
.../graphql_aiohttp/schemas/schema_hello.py | 16 ++
.../minos/plugins/graphql_aiohttp/services.py | 28 +++-
.../graphql_aiohttp/templates/graphiql.html | 139 ++++++++++++++++++
.../plugins/minos-graphql-aiohttp/poetry.lock | 93 ++++++++----
.../minos-graphql-aiohttp/pyproject.toml | 5 +-
12 files changed, 397 insertions(+), 42 deletions(-)
create mode 100644 packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/handlers/__init__.py
create mode 100644 packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/handlers/abc.py
create mode 100644 packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/handlers/graphiql.py
create mode 100644 packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/handlers/graphql.py
create mode 100644 packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/schema.py
create mode 100644 packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/schemas/schema_hello.py
create mode 100644 packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/templates/graphiql.html
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/__init__.py b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/__init__.py
index 1a9008f48..2aee93245 100644
--- a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/__init__.py
+++ b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/__init__.py
@@ -5,9 +5,6 @@
from .exceptions import (
RestResponseException,
)
-from .handlers import (
- RestHandler,
-)
from .requests import (
RestRequest,
)
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/handlers.py b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/handlers.py
index cad7ce722..5b1c3f01c 100644
--- a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/handlers.py
+++ b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/handlers.py
@@ -17,12 +17,11 @@
Union,
)
+import aiohttp_jinja2
+import jinja2
from aiohttp import (
web,
)
-from graphql_server.aiohttp import (
- GraphQLView,
-)
from minos.common import (
MinosConfig,
@@ -34,6 +33,8 @@
ResponseException,
)
+from minos.plugins.graphql_aiohttp.handlers.graphiql import GraphiqlHandler
+from minos.plugins.graphql_aiohttp.handlers.graphql import GraphqlHandler
from .graphql_example import (
schema,
)
@@ -105,8 +106,10 @@ def get_app(self) -> web.Application:
@cached_property
def _app(self) -> web.Application:
app = web.Application()
+ await self.initialize_jinja2(app)
+ self.register_handlers(app)
# self._mount_routes(app)
- self._mount_graphql(app)
+ # self._mount_graphql(app)
return app
def _mount_routes(self, app: web.Application):
@@ -121,8 +124,21 @@ def _mount_one_route(self, method: str, url: str, action: Callable, app: web.App
handler = self.get_callback(action)
app.router.add_route(method, url, handler)
+ """
def _mount_graphql(self, app: web.Application):
GraphQLView.attach(app, schema=schema, graphiql=True)
+ """
+
+ def register_handlers(self, app: web.Application):
+ routes = [(r'/graphql', GraphqlHandler)]
+
+ # if self.dev:
+ routes += [(r'/graphiql', GraphiqlHandler)]
+
+ app.router.add_routes([web.view(route[0], route[1]) for route in routes])
+
+ async def initialize_jinja2(self, app: web.Application):
+ aiohttp_jinja2.setup(app, loader=jinja2.FileSystemLoader('./templates'))
@staticmethod
def get_callback(
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/handlers/__init__.py b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/handlers/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/handlers/abc.py b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/handlers/abc.py
new file mode 100644
index 000000000..628fbe4a7
--- /dev/null
+++ b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/handlers/abc.py
@@ -0,0 +1,100 @@
+import asyncio
+import logging
+import urllib
+import json
+import aiohttp
+from aiohttp import web
+#from graphql.execution.executors.asyncio import AsyncioExecutor
+
+
+class BaseHandler(web.View):
+
+ @property
+ def fetch(self):
+ return self.app.fetch
+
+ @property
+ def app(self):
+ return self.request.app
+
+ async def options(self):
+ return web.Response(
+ status=204,
+ headers={
+ 'Access-Control-Allow-Origin': '*',
+ 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, PATCH',
+ 'Access-Control-Allow-Headers': 'x-requested-with,access-control-allow-origin,authorization,content-type',
+ },
+ )
+
+
+class GQLBaseHandler(BaseHandler):
+ async def get(self):
+ return await self.post()
+
+ async def post(self):
+ return await self.handle_graqhql()
+
+ async def handle_graqhql(self):
+ status = 200
+ result = await self.execute_graphql()
+ logging.debug(
+ 'GraphQL result data: %s errors: %s',
+ result.data,
+ result.errors,
+ #result.invalid,
+ )
+
+ if result and result.errors:
+ status = 500
+
+ #ex = ExecutionError(errors=result.errors)
+ #logging.debug('GraphQL Error: %s', ex)
+
+ errors = result.errors
+
+ if errors is None:
+ errors = []
+
+ return web.json_response(
+ {'data': result.data, 'errors': [str(err) for err in errors]},
+ status=status,
+ headers={'Access-Control-Allow-Origin': '*'},
+ )
+
+ async def execute_graphql(self):
+ graphql_req = await self.graphql_request
+ logging.debug('graphql request: %s', graphql_req)
+ context_value = graphql_req.get('context', {})
+ variables = graphql_req.get('variables', {})
+
+ context_value['application'] = self.app
+
+ #executor = AsyncioExecutor(loop=asyncio.get_event_loop())
+ result = self.schema.execute(
+ graphql_req['query'],
+ #executor=executor,
+ #return_promise=True,
+ context_value=context_value,
+ variable_values=variables,
+ )
+
+ ref = self.request.headers.get('referer')
+ url_path = ''
+
+ if ref:
+ url = urllib.parse.urlparse(ref)
+ url_path = url.path
+
+ if result.errors:
+ if '/graphiql' not in url_path:
+ aiohttp.log.server_logger.error(f'Graphql query error: for query "{graphql_req}"')
+
+ return result
+
+ @property
+ async def graphql_request(self):
+ if self.request.method == 'GET':
+ return json.loads(self.request.query['q'])
+
+ return await self.request.json()
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/handlers/graphiql.py b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/handlers/graphiql.py
new file mode 100644
index 000000000..2066ad102
--- /dev/null
+++ b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/handlers/graphiql.py
@@ -0,0 +1,10 @@
+import aiohttp_jinja2
+from .abc import BaseHandler
+
+
+class GraphiqlHandler(BaseHandler):
+
+ # Renderiza o GraphiQL
+ @aiohttp_jinja2.template('graphiql.html')
+ async def get(self):
+ return {'base_url': f'/graphql'}
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/handlers/graphql.py b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/handlers/graphql.py
new file mode 100644
index 000000000..9746e4f15
--- /dev/null
+++ b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/handlers/graphql.py
@@ -0,0 +1,11 @@
+import graphene
+
+from .abc import GQLBaseHandler
+from minos.plugins.graphql_aiohttp.schema import AllQuery
+
+
+class GraphqlHandler(GQLBaseHandler):
+
+ @property
+ def schema(self):
+ return graphene.Schema(query=AllQuery)
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/schema.py b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/schema.py
new file mode 100644
index 000000000..9d1ee5a0e
--- /dev/null
+++ b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/schema.py
@@ -0,0 +1,10 @@
+from schemas.schema_hello import Query
+
+
+class AllQuery(
+ Query,
+ # Schema1,
+ # Schema2,
+ # Schema3
+):
+ '''AllQuery'''
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/schemas/schema_hello.py b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/schemas/schema_hello.py
new file mode 100644
index 000000000..b29703d77
--- /dev/null
+++ b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/schemas/schema_hello.py
@@ -0,0 +1,16 @@
+from graphene import ObjectType, String, Schema
+
+
+class Query(ObjectType):
+
+ hello = String(name=String(default_value="stranger"))
+ goodbye = String()
+
+ def resolve_hello(root, info, name):
+ return f'Hello {name}!'
+
+ def resolve_goodbye(root, info):
+ return 'See ya!'
+
+
+schema = Schema(query=Query)
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/services.py b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/services.py
index c1ff1919c..b2cc92071 100644
--- a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/services.py
+++ b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/services.py
@@ -1,19 +1,17 @@
+import aiohttp_jinja2
+import jinja2
from aiohttp import (
web,
)
from aiomisc.service.aiohttp import (
AIOHTTPService,
)
-from graphql_server.aiohttp import (
- GraphQLView,
-)
from .graphql_example import (
schema,
)
-from .handlers import (
- RestHandler,
-)
+from .handlers.graphiql import GraphiqlHandler
+from .handlers.graphql import GraphqlHandler
class RestService(AIOHTTPService):
@@ -28,9 +26,21 @@ def __init__(self, **kwargs):
# self.handler = RestHandler.from_config(**kwargs)
# super().__init__(**(kwargs | {"address": self.handler.host, "port": self.handler.port}))
super().__init__(**(kwargs | {"address": "localhost", "port": 7030}))
-
+ """
def _mount_graphql(self, app: web.Application):
GraphQLView.attach(app, schema=schema, graphiql=True)
+ """
+
+ def register_handlers(self, app: web.Application):
+ routes = [(r'/graphql', GraphqlHandler)]
+
+ # if self.dev:
+ routes += [(r'/graphiql', GraphiqlHandler)]
+
+ app.router.add_routes([web.view(route[0], route[1]) for route in routes])
+
+ async def initialize_jinja2(self, app: web.Application):
+ aiohttp_jinja2.setup(app, loader=jinja2.FileSystemLoader('./templates'))
async def create_application(self) -> web.Application:
"""Create the web application.
@@ -38,7 +48,9 @@ async def create_application(self) -> web.Application:
:return: A ``web.Application`` instance.
"""
app = web.Application()
+ self.register_handlers(app)
+ await self.initialize_jinja2(app)
# self._mount_routes(app)
- self._mount_graphql(app)
+ #self._mount_graphql(app)
return app
# return self.handler.get_app() # pragma: no cover
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/templates/graphiql.html b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/templates/graphiql.html
new file mode 100644
index 000000000..0a90e30e2
--- /dev/null
+++ b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/templates/graphiql.html
@@ -0,0 +1,139 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Loading...
+
+
+
diff --git a/packages/plugins/minos-graphql-aiohttp/poetry.lock b/packages/plugins/minos-graphql-aiohttp/poetry.lock
index e4f51e8c9..231a93c28 100644
--- a/packages/plugins/minos-graphql-aiohttp/poetry.lock
+++ b/packages/plugins/minos-graphql-aiohttp/poetry.lock
@@ -18,6 +18,18 @@ yarl = ">=1.0,<2.0"
[package.extras]
speedups = ["aiodns", "brotli", "cchardet"]
+[[package]]
+name = "aiohttp-jinja2"
+version = "1.5"
+description = "jinja2 template renderer for aiohttp.web (http server for asyncio)"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+aiohttp = ">=3.6.3"
+jinja2 = ">=3.0.0"
+
[[package]]
name = "aiomisc"
version = "15.6.8"
@@ -73,6 +85,17 @@ category = "dev"
optional = false
python-versions = "*"
+[[package]]
+name = "aniso8601"
+version = "9.0.1"
+description = "A library for parsing ISO 8601 strings."
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.extras]
+dev = ["black", "coverage", "isort", "pre-commit", "pyenchant", "pylint"]
+
[[package]]
name = "async-timeout"
version = "4.0.2"
@@ -303,36 +326,41 @@ category = "main"
optional = false
python-versions = ">=3.7"
+[[package]]
+name = "graphene"
+version = "3.0"
+description = "GraphQL Framework for Python"
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+aniso8601 = ">=8,<10"
+graphql-core = ">=3.1.2,<3.2.0"
+graphql-relay = ">=3.0,<4"
+
+[package.extras]
+dev = ["black (==19.10b0)", "flake8 (>=3.7,<4)", "pytest (>=5.3,<6)", "pytest-benchmark (>=3.2,<4)", "pytest-cov (>=2.8,<3)", "pytest-mock (>=2,<3)", "pytest-asyncio (>=0.10,<2)", "snapshottest (>=0.5,<1)", "coveralls (>=1.11,<2)", "promise (>=2.3,<3)", "mock (>=4.0,<5)", "pytz (==2021.1)", "iso8601 (>=0.1,<2)"]
+test = ["pytest (>=5.3,<6)", "pytest-benchmark (>=3.2,<4)", "pytest-cov (>=2.8,<3)", "pytest-mock (>=2,<3)", "pytest-asyncio (>=0.10,<2)", "snapshottest (>=0.5,<1)", "coveralls (>=1.11,<2)", "promise (>=2.3,<3)", "mock (>=4.0,<5)", "pytz (==2021.1)", "iso8601 (>=0.1,<2)"]
+
[[package]]
name = "graphql-core"
-version = "3.2.0"
+version = "3.1.7"
description = "GraphQL implementation for Python, a port of GraphQL.js, the JavaScript reference implementation for GraphQL."
category = "main"
optional = false
python-versions = ">=3.6,<4"
[[package]]
-name = "graphql-server"
-version = "3.0.0b5"
-description = "GraphQL Server tools for powering your server"
+name = "graphql-relay"
+version = "3.1.5"
+description = "Relay library for graphql-core"
category = "main"
optional = false
-python-versions = "*"
+python-versions = ">=3.6,<4"
[package.dependencies]
-aiohttp = {version = ">=3.8,<4", optional = true, markers = "extra == \"aiohttp\""}
-graphql-core = ">=3.2,<3.3"
-typing-extensions = ">=4,<5"
-
-[package.extras]
-aiohttp = ["aiohttp (>=3.8,<4)"]
-all = ["graphql-core (>=3.2,<3.3)", "typing-extensions (>=4,<5)", "flask (>=1,<2)", "sanic (>=20.3,<21)", "webob (>=1.8.7,<2)", "aiohttp (>=3.8,<4)", "quart (>=0.6.15,<0.15)"]
-dev = ["graphql-core (>=3.2,<3.3)", "typing-extensions (>=4,<5)", "flask (>=1,<2)", "sanic (>=20.3,<21)", "webob (>=1.8.7,<2)", "aiohttp (>=3.8,<4)", "quart (>=0.6.15,<0.15)", "flake8 (>=4,<5)", "isort (>=5,<6)", "black (>=19.10b0)", "mypy (>=0.931,<1)", "check-manifest (>=0.47,<1)", "pytest (>=6.2,<6.3)", "pytest-asyncio (>=0.16,<1)", "pytest-cov (>=3,<4)", "aiohttp (>=3.8,<4)", "Jinja2 (>=2.11,<3)"]
-flask = ["flask (>=1,<2)"]
-quart = ["quart (>=0.6.15,<0.15)"]
-sanic = ["sanic (>=20.3,<21)"]
-test = ["graphql-core (>=3.2,<3.3)", "typing-extensions (>=4,<5)", "flask (>=1,<2)", "sanic (>=20.3,<21)", "webob (>=1.8.7,<2)", "aiohttp (>=3.8,<4)", "quart (>=0.6.15,<0.15)", "pytest (>=6.2,<6.3)", "pytest-asyncio (>=0.16,<1)", "pytest-cov (>=3,<4)", "aiohttp (>=3.8,<4)", "Jinja2 (>=2.11,<3)"]
-webob = ["webob (>=1.8.7,<2)"]
+graphql-core = ">=3.1,<3.2"
[[package]]
name = "identify"
@@ -403,7 +431,7 @@ plugins = ["setuptools"]
name = "jinja2"
version = "3.0.3"
description = "A very fast and expressive template engine."
-category = "dev"
+category = "main"
optional = false
python-versions = ">=3.6"
@@ -437,7 +465,7 @@ mistune = "0.8.4"
name = "markupsafe"
version = "2.1.0"
description = "Safely add untrusted strings to HTML/XML markup."
-category = "dev"
+category = "main"
optional = false
python-versions = ">=3.7"
@@ -888,7 +916,7 @@ python-versions = ">=3.7"
name = "typing-extensions"
version = "4.1.1"
description = "Backported and Experimental Type Hints for Python 3.6+"
-category = "main"
+category = "dev"
optional = false
python-versions = ">=3.6"
@@ -950,7 +978,7 @@ testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-
[metadata]
lock-version = "1.1"
python-versions = "^3.9"
-content-hash = "0dbb3f5d394a3212b60abcc7c2b56b0babee5106f012b46671236628363623bc"
+content-hash = "bdba872b9f221db81478baf76a21d4d23e88691a09a3aab49967bbceb6e42685"
[metadata.files]
aiohttp = [
@@ -1027,6 +1055,10 @@ aiohttp = [
{file = "aiohttp-3.8.1-cp39-cp39-win_amd64.whl", hash = "sha256:1c182cb873bc91b411e184dab7a2b664d4fea2743df0e4d57402f7f3fa644bac"},
{file = "aiohttp-3.8.1.tar.gz", hash = "sha256:fc5471e1a54de15ef71c1bc6ebe80d4dc681ea600e68bfd1cbce40427f0b7578"},
]
+aiohttp-jinja2 = [
+ {file = "aiohttp-jinja2-1.5.tar.gz", hash = "sha256:7c3ba5eac060b691f4e50534af2d79fca2a75712ebd2b25e6fcb1295859f910b"},
+ {file = "aiohttp_jinja2-1.5-py3-none-any.whl", hash = "sha256:b55c0ed167b0cc4b6d6a50fb2299a44beb5dc4aec9df21305b91a5484694cf74"},
+]
aiomisc = [
{file = "aiomisc-15.6.8-py3-none-any.whl", hash = "sha256:b65f7f8e50d9791bbb745253e814862880b8a35bf21e4eea545ada9daf89836c"},
{file = "aiomisc-15.6.8.tar.gz", hash = "sha256:09024d1f544de98b152dad8f7eb7d02c64457aaaaf03bd6c76a20b73328a3a74"},
@@ -1043,6 +1075,10 @@ alabaster = [
{file = "alabaster-0.7.12-py2.py3-none-any.whl", hash = "sha256:446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359"},
{file = "alabaster-0.7.12.tar.gz", hash = "sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02"},
]
+aniso8601 = [
+ {file = "aniso8601-9.0.1-py2.py3-none-any.whl", hash = "sha256:1d2b7ef82963909e93c4f24ce48d4de9e66009a21bf1c1e1c85bdd0812fe412f"},
+ {file = "aniso8601-9.0.1.tar.gz", hash = "sha256:72e3117667eedf66951bb2d93f4296a56b94b078a8a95905a052611fb3f1b973"},
+]
async-timeout = [
{file = "async-timeout-4.0.2.tar.gz", hash = "sha256:2163e1640ddb52b7a8c80d0a67a08587e5d245cc9c553a74a847056bc2976b15"},
{file = "async_timeout-4.0.2-py3-none-any.whl", hash = "sha256:8ca1e4fcf50d07413d66d1a5e416e42cfdf5851c981d679a09851a6853383b3c"},
@@ -1291,12 +1327,17 @@ frozenlist = [
{file = "frozenlist-1.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:772965f773757a6026dea111a15e6e2678fbd6216180f82a48a40b27de1ee2ab"},
{file = "frozenlist-1.3.0.tar.gz", hash = "sha256:ce6f2ba0edb7b0c1d8976565298ad2deba6f8064d2bebb6ffce2ca896eb35b0b"},
]
+graphene = [
+ {file = "graphene-3.0-py2.py3-none-any.whl", hash = "sha256:57ce5ee7c9dc194224a1df96e4f7cb48d31eae96c791091d059f8f3d4d131390"},
+ {file = "graphene-3.0.tar.gz", hash = "sha256:af48066e152200a071aac1e0b703954c717ec7268720ba190a0c91d9bcb0a122"},
+]
graphql-core = [
- {file = "graphql-core-3.2.0.tar.gz", hash = "sha256:86e2a0be008bfde19ef78388de8a725a1d942a9190ca431c24a60837973803ce"},
- {file = "graphql_core-3.2.0-py3-none-any.whl", hash = "sha256:0dda7e63676f119bb3d814621190fedad72fda07a8e9ab780bedd9f1957c6dc6"},
+ {file = "graphql-core-3.1.7.tar.gz", hash = "sha256:62ec192150ccecd9a18cfb79e3e72eb7d1fd68fb594ef19c40099b6deec8ef0c"},
+ {file = "graphql_core-3.1.7-py3-none-any.whl", hash = "sha256:9b460f60320be01c7f3b1766cf3e406933003008055079b9d983b8f3988f4400"},
]
-graphql-server = [
- {file = "graphql-server-3.0.0b5.tar.gz", hash = "sha256:85c12c421e14f339883865d75fe316f626f3f875c70f8c366a93d50c5b1c8a23"},
+graphql-relay = [
+ {file = "graphql-relay-3.1.5.tar.gz", hash = "sha256:127f40913f11cb847452ef794931261aae3b222eaaf976f7c8430298534e54d3"},
+ {file = "graphql_relay-3.1.5-py3-none-any.whl", hash = "sha256:d1455316182e1017d06f0b4d4a2ce8bafad38253a6c7c5666fc80d5d516ee5c8"},
]
identify = [
{file = "identify-2.4.11-py2.py3-none-any.whl", hash = "sha256:fd906823ed1db23c7a48f9b176a1d71cb8abede1e21ebe614bac7bdd688d9213"},
diff --git a/packages/plugins/minos-graphql-aiohttp/pyproject.toml b/packages/plugins/minos-graphql-aiohttp/pyproject.toml
index 338ba801e..9d5a2d17a 100644
--- a/packages/plugins/minos-graphql-aiohttp/pyproject.toml
+++ b/packages/plugins/minos-graphql-aiohttp/pyproject.toml
@@ -34,7 +34,10 @@ python = "^3.9"
minos-microservice-common = "^0.5.0"
minos-microservice-networks = "^0.5.0"
aiohttp = "^3.8.1"
-graphql-server = {version = "^3.0.0-beta.5", extras = ["aiohttp"]}
+aiohttp-jinja2 = "^1.5"
+async-timeout = "^4.0.2"
+graphene = "^3.0"
+Jinja2 = "^3.0.3"
[tool.poetry.dev-dependencies]
minos-microservice-common = { path = "../../core/minos-microservice-common", develop = true }
From 8f296635a645f6865e501c25600173f94539210e Mon Sep 17 00:00:00 2001
From: Vladyslav Fenchak
Date: Tue, 8 Mar 2022 19:37:18 +0100
Subject: [PATCH 08/44] ISSUE #149
---
.../plugins/graphql_aiohttp/graphql_example.py | 18 ------------------
.../minos/plugins/graphql_aiohttp/handlers.py | 7 +++----
.../graphql_aiohttp/handlers/__init__.py | 6 ++++++
.../graphql_aiohttp/handlers/graphiql.py | 2 --
.../minos/plugins/graphql_aiohttp/services.py | 8 +++-----
5 files changed, 12 insertions(+), 29 deletions(-)
delete mode 100644 packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/graphql_example.py
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/graphql_example.py b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/graphql_example.py
deleted file mode 100644
index e17e52148..000000000
--- a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/graphql_example.py
+++ /dev/null
@@ -1,18 +0,0 @@
-import asyncio
-
-from graphql import (
- GraphQLField,
- GraphQLObjectType,
- GraphQLSchema,
- GraphQLString,
- graphql,
-)
-
-
-def resolve_hello(obj, info):
- return "world"
-
-
-schema = GraphQLSchema(
- query=GraphQLObjectType(name="RootQueryType", fields={"hello": GraphQLField(GraphQLString, resolve=resolve_hello)})
-)
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/handlers.py b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/handlers.py
index 5b1c3f01c..b56c32f65 100644
--- a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/handlers.py
+++ b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/handlers.py
@@ -33,10 +33,9 @@
ResponseException,
)
-from minos.plugins.graphql_aiohttp.handlers.graphiql import GraphiqlHandler
-from minos.plugins.graphql_aiohttp.handlers.graphql import GraphqlHandler
-from .graphql_example import (
- schema,
+from .handlers import (
+ GraphiqlHandler,
+ GraphqlHandler
)
from .requests import (
RestRequest,
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/handlers/__init__.py b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/handlers/__init__.py
index e69de29bb..1e49dde44 100644
--- a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/handlers/__init__.py
+++ b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/handlers/__init__.py
@@ -0,0 +1,6 @@
+from .graphql import (
+ GraphqlHandler,
+)
+from .graphiql import (
+ GraphiqlHandler,
+)
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/handlers/graphiql.py b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/handlers/graphiql.py
index 2066ad102..4e82cd7c3 100644
--- a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/handlers/graphiql.py
+++ b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/handlers/graphiql.py
@@ -3,8 +3,6 @@
class GraphiqlHandler(BaseHandler):
-
- # Renderiza o GraphiQL
@aiohttp_jinja2.template('graphiql.html')
async def get(self):
return {'base_url': f'/graphql'}
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/services.py b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/services.py
index b2cc92071..ce26b9858 100644
--- a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/services.py
+++ b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/services.py
@@ -6,12 +6,10 @@
from aiomisc.service.aiohttp import (
AIOHTTPService,
)
-
-from .graphql_example import (
- schema,
+from .handlers import (
+ GraphiqlHandler,
+ GraphqlHandler
)
-from .handlers.graphiql import GraphiqlHandler
-from .handlers.graphql import GraphqlHandler
class RestService(AIOHTTPService):
From bedc99753faf183e447b1ac45d125ff3c1db26c9 Mon Sep 17 00:00:00 2001
From: Vladyslav Fenchak
Date: Fri, 11 Mar 2022 10:21:27 +0100
Subject: [PATCH 09/44] ISSUE #149
---
.../minos/plugins/graphql_aiohttp/__init__.py | 6 -
.../minos/plugins/graphql_aiohttp/handlers.py | 194 --------
.../plugins/graphql_aiohttp/handlers/abc.py | 29 +-
.../graphql_aiohttp/handlers/graphql.py | 11 +-
.../minos/plugins/graphql_aiohttp/requests.py | 234 ----------
.../plugins/graphql_aiohttp/responses.py | 117 -----
.../minos/plugins/graphql_aiohttp/schema.py | 10 -
.../graphql_aiohttp/schemas/schema_hello.py | 16 -
.../star_wars_example/__init__.py | 8 +
.../graphql_aiohttp/star_wars_example/data.py | 147 ++++++
.../star_wars_example/schema.py | 274 +++++++++++
.../plugins/minos-graphql-aiohttp/poetry.lock | 59 +--
.../minos-graphql-aiohttp/pyproject.toml | 3 +-
.../test_graphql_aiohttp/test_handlers.py | 131 ------
.../test_graphql_aiohttp/test_requests.py | 441 ------------------
.../test_graphql_aiohttp/test_services.py | 48 --
16 files changed, 452 insertions(+), 1276 deletions(-)
delete mode 100644 packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/handlers.py
delete mode 100644 packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/requests.py
delete mode 100644 packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/responses.py
delete mode 100644 packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/schema.py
delete mode 100644 packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/schemas/schema_hello.py
create mode 100644 packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/star_wars_example/__init__.py
create mode 100644 packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/star_wars_example/data.py
create mode 100644 packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/star_wars_example/schema.py
delete mode 100644 packages/plugins/minos-graphql-aiohttp/tests/test_plugins/test_graphql_aiohttp/test_handlers.py
delete mode 100644 packages/plugins/minos-graphql-aiohttp/tests/test_plugins/test_graphql_aiohttp/test_requests.py
delete mode 100644 packages/plugins/minos-graphql-aiohttp/tests/test_plugins/test_graphql_aiohttp/test_services.py
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/__init__.py b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/__init__.py
index 2aee93245..d1a5f0caf 100644
--- a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/__init__.py
+++ b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/__init__.py
@@ -5,12 +5,6 @@
from .exceptions import (
RestResponseException,
)
-from .requests import (
- RestRequest,
-)
-from .responses import (
- RestResponse,
-)
from .services import (
RestService,
)
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/handlers.py b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/handlers.py
deleted file mode 100644
index b56c32f65..000000000
--- a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/handlers.py
+++ /dev/null
@@ -1,194 +0,0 @@
-from __future__ import (
- annotations,
-)
-
-import logging
-from functools import (
- cached_property,
- wraps,
-)
-from inspect import (
- isawaitable,
-)
-from typing import (
- Awaitable,
- Callable,
- Optional,
- Union,
-)
-
-import aiohttp_jinja2
-import jinja2
-from aiohttp import (
- web,
-)
-
-from minos.common import (
- MinosConfig,
- MinosSetup,
-)
-from minos.networks import (
- REQUEST_USER_CONTEXT_VAR,
- EnrouteBuilder,
- ResponseException,
-)
-
-from .handlers import (
- GraphiqlHandler,
- GraphqlHandler
-)
-from .requests import (
- RestRequest,
-)
-from .responses import (
- RestResponse,
-)
-
-logger = logging.getLogger(__name__)
-
-
-class RestHandler(MinosSetup):
- """Rest Handler class."""
-
- def __init__(self, host: str, port: int, endpoints: dict[(str, str), Callable], **kwargs):
- super().__init__(**kwargs)
- self._host = host
- self._port = port
- self._endpoints = endpoints
-
- @property
- def endpoints(self) -> dict[(str, str), Callable]:
- """Endpoints getter.
-
- :return: A dictionary value.
- """
- return self._endpoints
-
- @classmethod
- def _from_config(cls, *args, config: MinosConfig, **kwargs) -> RestHandler:
- host = config.rest.host
- port = config.rest.port
- endpoints = cls._endpoints_from_config(config)
-
- return cls(host=host, port=port, endpoints=endpoints, **kwargs)
-
- @staticmethod
- def _endpoints_from_config(config: MinosConfig, **kwargs) -> dict[(str, str), Callable]:
- builder = EnrouteBuilder(*config.services, middleware=config.middleware)
- decorators = builder.get_rest_command_query(config=config, **kwargs)
- endpoints = {(decorator.url, decorator.method): fn for decorator, fn in decorators.items()}
- return endpoints
-
- @property
- def host(self) -> str:
- """Get the rest host.
-
- :return: A ``str`` object.
- """
- return self._host
-
- @property
- def port(self) -> int:
- """Get the rest port.
-
- :return: An integer value.
- """
- return self._port
-
- def get_app(self) -> web.Application:
- """Return rest application instance.
-
- :return: A `web.Application` instance.
- """
- return self._app
-
- @cached_property
- def _app(self) -> web.Application:
- app = web.Application()
- await self.initialize_jinja2(app)
- self.register_handlers(app)
- # self._mount_routes(app)
- # self._mount_graphql(app)
- return app
-
- def _mount_routes(self, app: web.Application):
- """Load routes from config file."""
- for (url, method), fn in self._endpoints.items():
- self._mount_one_route(method, url, fn, app)
-
- # Load default routes
- self._mount_system_health(app)
-
- def _mount_one_route(self, method: str, url: str, action: Callable, app: web.Application) -> None:
- handler = self.get_callback(action)
- app.router.add_route(method, url, handler)
-
- """
- def _mount_graphql(self, app: web.Application):
- GraphQLView.attach(app, schema=schema, graphiql=True)
- """
-
- def register_handlers(self, app: web.Application):
- routes = [(r'/graphql', GraphqlHandler)]
-
- # if self.dev:
- routes += [(r'/graphiql', GraphiqlHandler)]
-
- app.router.add_routes([web.view(route[0], route[1]) for route in routes])
-
- async def initialize_jinja2(self, app: web.Application):
- aiohttp_jinja2.setup(app, loader=jinja2.FileSystemLoader('./templates'))
-
- @staticmethod
- def get_callback(
- fn: Callable[[RestRequest], Union[Optional[RestResponse], Awaitable[Optional[RestResponse]]]]
- ) -> Callable[[web.Request], Awaitable[web.Response]]:
- """Get the handler function to be used by the ``aiohttp`` Controller.
-
- :param fn: The action function.
- :return: A wrapper function around the given one that is compatible with the ``aiohttp`` Controller.
- """
-
- @wraps(fn)
- async def _wrapper(request: web.Request) -> web.Response:
- logger.info(f"Dispatching '{request!s}' from '{request.remote!s}'...")
-
- request = RestRequest(request)
- token = REQUEST_USER_CONTEXT_VAR.set(request.user)
-
- try:
- response = fn(request)
- if isawaitable(response):
- response = await response
-
- if not isinstance(response, RestResponse):
- response = RestResponse.from_response(response)
-
- content = await response.content()
- content_type = response.content_type
- status = response.status
-
- return web.Response(body=content, content_type=content_type, status=status)
-
- except ResponseException as exc:
- logger.warning(f"Raised an application exception: {exc!s}")
- return web.Response(text=str(exc), status=exc.status)
- except Exception as exc:
- logger.exception(f"Raised a system exception: {exc!r}")
- raise web.HTTPInternalServerError()
- finally:
- REQUEST_USER_CONTEXT_VAR.reset(token)
-
- return _wrapper
-
- def _mount_system_health(self, app: web.Application):
- """Mount System Health Route."""
- app.router.add_get("/system/health", self._system_health_handler)
-
- @staticmethod
- async def _system_health_handler(request: web.Request) -> web.Response:
- """System Health Route Handler.
- :return: A `web.json_response` response.
- """
- logger.info(f"Dispatching '{request!s}' from '{request.remote!s}'...")
- return web.json_response({"host": request.host})
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/handlers/abc.py b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/handlers/abc.py
index 628fbe4a7..2b200ef00 100644
--- a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/handlers/abc.py
+++ b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/handlers/abc.py
@@ -1,11 +1,12 @@
-import asyncio
import logging
import urllib
import json
import aiohttp
from aiohttp import web
-#from graphql.execution.executors.asyncio import AsyncioExecutor
-
+from graphql import graphql
+from minos.plugins.graphql_aiohttp.star_wars_example import (
+ star_wars_schema,
+)
class BaseHandler(web.View):
@@ -42,42 +43,34 @@ async def handle_graqhql(self):
'GraphQL result data: %s errors: %s',
result.data,
result.errors,
- #result.invalid,
)
if result and result.errors:
status = 500
- #ex = ExecutionError(errors=result.errors)
- #logging.debug('GraphQL Error: %s', ex)
-
errors = result.errors
if errors is None:
errors = []
return web.json_response(
- {'data': result.data, 'errors': [str(err) for err in errors]},
+ {'data': result.data, 'errors': [err.message for err in errors]},
status=status,
headers={'Access-Control-Allow-Origin': '*'},
)
async def execute_graphql(self):
graphql_req = await self.graphql_request
- logging.debug('graphql request: %s', graphql_req)
context_value = graphql_req.get('context', {})
variables = graphql_req.get('variables', {})
context_value['application'] = self.app
-
- #executor = AsyncioExecutor(loop=asyncio.get_event_loop())
- result = self.schema.execute(
- graphql_req['query'],
- #executor=executor,
- #return_promise=True,
- context_value=context_value,
- variable_values=variables,
- )
+ source = graphql_req['query']
+ result = await graphql(schema=star_wars_schema,
+ source=source,
+ context_value=context_value,
+ variable_values=variables,
+ )
ref = self.request.headers.get('referer')
url_path = ''
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/handlers/graphql.py b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/handlers/graphql.py
index 9746e4f15..78a51aa7c 100644
--- a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/handlers/graphql.py
+++ b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/handlers/graphql.py
@@ -1,11 +1,14 @@
-import graphene
+from graphql.utilities import (
+ print_schema
+)
from .abc import GQLBaseHandler
-from minos.plugins.graphql_aiohttp.schema import AllQuery
-
+from minos.plugins.graphql_aiohttp.star_wars_example import (
+ star_wars_schema,
+)
class GraphqlHandler(GQLBaseHandler):
@property
def schema(self):
- return graphene.Schema(query=AllQuery)
+ return print_schema(star_wars_schema)
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/requests.py b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/requests.py
deleted file mode 100644
index 0b99c93b0..000000000
--- a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/requests.py
+++ /dev/null
@@ -1,234 +0,0 @@
-from __future__ import (
- annotations,
-)
-
-import warnings
-from collections import (
- defaultdict,
-)
-from collections.abc import (
- Callable,
- Iterable,
-)
-from itertools import (
- chain,
-)
-from typing import (
- Any,
- Optional,
- Union,
-)
-from urllib.parse import (
- parse_qsl,
-)
-from uuid import (
- UUID,
-)
-
-from aiohttp.web import Request as AioHttpRequest
-from cached_property import (
- cached_property,
-)
-
-from minos.common import (
- AvroDataDecoder,
- AvroSchemaDecoder,
- MinosAvroProtocol,
- import_module,
-)
-from minos.networks import (
- NotHasParamsException,
- Request,
-)
-
-
-class RestRequest(Request):
- """Rest Request class."""
-
- __slots__ = "raw"
-
- def __init__(self, raw: AioHttpRequest, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.raw = raw
-
- @property
- def raw_request(self) -> AioHttpRequest:
- """Get the raw request within the instance.
-
- :return: An ``aiohttp.web.Request`` instance.
- """
- warnings.warn(
- f"'{RestRequest.__name__}s.raw_request' is deprecated in favor of '{RestRequest.__name__}.raw'.",
- DeprecationWarning,
- )
- return self.raw
-
- def __eq__(self, other: RestRequest) -> bool:
- return type(self) == type(other) and self.raw == other.raw
-
- def __repr__(self) -> str:
- return f"{type(self).__name__}({self.raw!r})"
-
- @cached_property
- def user(self) -> Optional[UUID]:
- """
- Returns the UUID of the user making the Request.
- """
- if "user" not in self.headers:
- return None
- return UUID(self.headers["user"])
-
- @property
- def headers(self) -> dict[str, str]:
- """Get the headers of the request.
-
- :return: A dictionary in which keys are ``str`` instances and values are ``str`` instances.
- """
- # noinspection PyTypeChecker
- return self.raw.headers
-
- @property
- def has_content(self) -> bool:
- """Check if the request has content.
-
- :return: ``True`` if it has content or ``False`` otherwise.
- """
- return self.raw.body_exists
-
- async def _content(self, type_: Optional[Union[type, str]] = None, **kwargs) -> Any:
- if "model_type" in kwargs:
- warnings.warn("The 'model_type' argument is deprecated. Use 'type_' instead", DeprecationWarning)
- if type_ is None:
- type_ = kwargs["model_type"]
-
- data = await self._content_parser()
- return self._build(data, type_)
-
- @cached_property
- def _content_parser(self) -> Callable:
- mapper = {
- "application/json": self._raw_json,
- "application/x-www-form-urlencoded": self._raw_form,
- "avro/binary": self._raw_avro,
- "text/plain": self._raw_text,
- "application/octet-stream": self._raw_bytes,
- }
-
- if self.content_type not in mapper:
- raise ValueError(
- f"The given Content-Type ({self.content_type!r}) is not supported for automatic content parsing yet."
- )
-
- return mapper[self.content_type]
-
- async def _raw_json(self) -> Any:
- return await self.raw.json()
-
- async def _raw_form(self) -> dict[str, Any]:
- form = await self.raw.json(loads=parse_qsl)
- return self._parse_multi_dict(form)
-
- async def _raw_avro(self) -> Any:
- data = MinosAvroProtocol.decode(await self._raw_bytes())
- schema = MinosAvroProtocol.decode_schema(await self._raw_bytes())
-
- type_ = AvroSchemaDecoder(schema).build()
- return AvroDataDecoder(type_).build(data)
-
- async def _raw_text(self) -> str:
- return await self.raw.text()
-
- async def _raw_bytes(self) -> bytes:
- return await self.raw.read()
-
- @property
- def content_type(self) -> str:
- """Get the content type.
-
- :return: A ``str`` value.
- """
- return self.raw.content_type
-
- @property
- def has_params(self) -> bool:
- """Check if the request has params.
-
- :return: ``True`` if it has params or ``False`` otherwise.
- """
- sentinel = object()
- return next(chain(self._raw_url_params, self._raw_query_params), sentinel) is not sentinel
-
- async def _params(self, type_: Optional[Union[type, str]] = None, **kwargs) -> dict[str, Any]:
- data = self._parse_multi_dict(chain(self._raw_url_params, self._raw_query_params))
- return self._build(data, type_)
-
- async def url_params(self, type_: Optional[Union[type, str]] = None, **kwargs) -> Any:
- """Get the url params.
-
- :param type_: Optional ``type`` or ``str`` (classname) that defines the request content type.
- :param kwargs: Additional named arguments.
- :return: A dictionary instance.
- """
- if not self.has_url_params:
- raise NotHasParamsException(f"{self!r} has not url params.")
-
- data = self._parse_multi_dict(self._raw_url_params)
- return self._build(data, type_)
-
- @property
- def has_url_params(self) -> bool:
- """Check if the request has url params.
-
- :return: ``True`` if it has url params or ``False`` otherwise.
- """
- sentinel = object()
- return next(iter(self._raw_url_params), sentinel) is not sentinel
-
- @property
- def _raw_url_params(self):
- return self.raw.rel_url.query.items() # pragma: no cover
-
- async def query_params(self, type_: Optional[Union[type, str]] = None, **kwargs) -> Any:
- """Get the query params.
-
-
- :param type_: Optional ``type`` or ``str`` (classname) that defines the request content type.
- :param kwargs: Additional named arguments.
- :return: A dictionary instance.
- """
- if not self.has_query_params:
- raise NotHasParamsException(f"{self!r} has not query params.")
-
- data = self._parse_multi_dict(self._raw_query_params)
- return self._build(data, type_)
-
- @property
- def has_query_params(self) -> bool:
- # noinspection GrazieInspection
- """Check if the request has query params.
-
- :return: ``True`` if it has query params or ``False`` otherwise.
- """
- sentinel = object()
- return next(iter(self._raw_query_params), sentinel) is not sentinel
-
- @property
- def _raw_query_params(self):
- return self.raw.match_info.items() # pragma: no cover
-
- @staticmethod
- def _build(data: Any, type_: Union[type, str]) -> Any:
- if type_ is None:
- return data
-
- if isinstance(type_, str):
- type_ = import_module(type_)
-
- return AvroDataDecoder(type_).build(data)
-
- @staticmethod
- def _parse_multi_dict(raw: Iterable[str, Any]) -> dict[str, Any]:
- args = defaultdict(list)
- for k, v in raw:
- args[k].append(v)
- return {k: v if len(v) > 1 else v[0] for k, v in args.items()}
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/responses.py b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/responses.py
deleted file mode 100644
index ca082d103..000000000
--- a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/responses.py
+++ /dev/null
@@ -1,117 +0,0 @@
-from __future__ import (
- annotations,
-)
-
-from collections.abc import (
- Callable,
-)
-from typing import (
- Any,
- Awaitable,
- Optional,
-)
-from urllib.parse import (
- urlencode,
-)
-
-from cached_property import (
- cached_property,
-)
-from orjson import (
- orjson,
-)
-
-from minos.common import (
- AvroDataEncoder,
- AvroSchemaEncoder,
- MinosAvroProtocol,
- TypeHintBuilder,
-)
-from minos.networks import (
- Response,
-)
-
-
-class RestResponse(Response):
- """Rest Response class."""
-
- def __init__(self, *args, content_type: str = "application/json", **kwargs):
- super().__init__(*args, **kwargs)
- self.content_type = content_type
-
- @classmethod
- def from_response(cls, response: Optional[Response]) -> RestResponse:
- """Build a new ``RestRequest`` from another response.
-
- :param response: The base response.
- :return: A ``RestResponse`` instance.
- """
- if isinstance(response, RestResponse):
- return response
-
- if response is None:
- return RestResponse()
-
- return RestResponse(response._data)
-
- # noinspection PyUnusedLocal
- async def content(self, **kwargs) -> Optional[bytes]:
- """Raw response content.
-
- :param kwargs: Additional named arguments.
- :return: The raw content as a ``bytes`` instance.
- """
- if not self.has_content:
- return None
- return await self._content_parser()
-
- @cached_property
- def _content_parser(self) -> Callable[[], Awaitable[bytes]]:
- mapper = {
- "application/json": self._raw_json,
- "application/x-www-form-urlencoded": self._raw_form,
- "avro/binary": self._raw_avro,
- "text/plain": self._raw_text,
- "application/octet-stream": self._raw_bytes,
- }
-
- if self.content_type not in mapper:
- return self._raw_bytes
-
- return mapper[self.content_type]
-
- async def _raw_json(self) -> bytes:
- return orjson.dumps(self._raw_data)
-
- async def _raw_form(self) -> bytes:
- return urlencode(self._raw_data).encode()
-
- async def _raw_avro(self) -> bytes:
- type_ = TypeHintBuilder(self._data).build()
- schema = AvroSchemaEncoder(type_).build()
- data = AvroDataEncoder(self._data).build()
-
- return MinosAvroProtocol.encode(data, schema)
-
- async def _raw_text(self) -> bytes:
- if not isinstance(self._raw_data, str):
- raise ValueError(
- f"Given 'Content-Type' ({self.content_type!r}) is not supported for the given data: {self._raw_data!r}."
- )
- return self._raw_data.encode()
-
- async def _raw_bytes(self) -> bytes:
- if not isinstance(self._raw_data, bytes):
- raise ValueError(
- f"Given 'Content-Type' ({self.content_type!r}) is not supported for the given data: {self._raw_data!r}."
- )
- return self._raw_data
-
- # noinspection PyUnusedLocal
- @cached_property
- def _raw_data(self) -> Any:
- """Raw response content.
-
- :return: A list of raw items.
- """
- return AvroDataEncoder(self._data).build()
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/schema.py b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/schema.py
deleted file mode 100644
index 9d1ee5a0e..000000000
--- a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/schema.py
+++ /dev/null
@@ -1,10 +0,0 @@
-from schemas.schema_hello import Query
-
-
-class AllQuery(
- Query,
- # Schema1,
- # Schema2,
- # Schema3
-):
- '''AllQuery'''
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/schemas/schema_hello.py b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/schemas/schema_hello.py
deleted file mode 100644
index b29703d77..000000000
--- a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/schemas/schema_hello.py
+++ /dev/null
@@ -1,16 +0,0 @@
-from graphene import ObjectType, String, Schema
-
-
-class Query(ObjectType):
-
- hello = String(name=String(default_value="stranger"))
- goodbye = String()
-
- def resolve_hello(root, info, name):
- return f'Hello {name}!'
-
- def resolve_goodbye(root, info):
- return 'See ya!'
-
-
-schema = Schema(query=Query)
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/star_wars_example/__init__.py b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/star_wars_example/__init__.py
new file mode 100644
index 000000000..d06b7603f
--- /dev/null
+++ b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/star_wars_example/__init__.py
@@ -0,0 +1,8 @@
+__author__ = "Minos Framework Devs"
+__email__ = "hey@minos.run"
+__version__ = "0.6.0"
+
+from .schema import (
+ star_wars_schema,
+)
+
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/star_wars_example/data.py b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/star_wars_example/data.py
new file mode 100644
index 000000000..167d2a326
--- /dev/null
+++ b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/star_wars_example/data.py
@@ -0,0 +1,147 @@
+"""This defines a basic set of data for our Star Wars Schema.
+
+This data is hard coded for the sake of the demo, but you could imagine fetching this
+data from a backend service rather than from hardcoded JSON objects in a more complex
+demo.
+"""
+
+from typing import Awaitable, Collection, Dict, Iterator, Optional
+
+__all__ = ["get_droid", "get_friends", "get_hero", "get_human", "get_secret_backstory"]
+
+# These are classes which correspond to the schema.
+# They represent the shape of the data visited during field resolution.
+
+
+class Character:
+ id: str
+ name: str
+ friends: Collection[str]
+ appearsIn: Collection[str]
+
+
+# noinspection PyPep8Naming
+class Human(Character):
+ type = "Human"
+ homePlanet: str
+
+ # noinspection PyShadowingBuiltins
+ def __init__(self, id, name, friends, appearsIn, homePlanet):
+ self.id, self.name = id, name
+ self.friends, self.appearsIn = friends, appearsIn
+ self.homePlanet = homePlanet
+
+
+# noinspection PyPep8Naming
+class Droid(Character):
+ type = "Droid"
+ primaryFunction: str
+
+ # noinspection PyShadowingBuiltins
+ def __init__(self, id, name, friends, appearsIn, primaryFunction):
+ self.id, self.name = id, name
+ self.friends, self.appearsIn = friends, appearsIn
+ self.primaryFunction = primaryFunction
+
+
+luke = Human(
+ id="1000",
+ name="Luke Skywalker",
+ friends=["1002", "1003", "2000", "2001"],
+ appearsIn=[4, 5, 6],
+ homePlanet="Tatooine",
+)
+
+vader = Human(
+ id="1001",
+ name="Darth Vader",
+ friends=["1004"],
+ appearsIn=[4, 5, 6],
+ homePlanet="Tatooine",
+)
+
+han = Human(
+ id="1002",
+ name="Han Solo",
+ friends=["1000", "1003", "2001"],
+ appearsIn=[4, 5, 6],
+ homePlanet=None,
+)
+
+leia = Human(
+ id="1003",
+ name="Leia Organa",
+ friends=["1000", "1002", "2000", "2001"],
+ appearsIn=[4, 5, 6],
+ homePlanet="Alderaan",
+)
+
+tarkin = Human(
+ id="1004", name="Wilhuff Tarkin", friends=["1001"], appearsIn=[4], homePlanet=None
+)
+
+human_data: Dict[str, Human] = {
+ "1000": luke,
+ "1001": vader,
+ "1002": han,
+ "1003": leia,
+ "1004": tarkin,
+}
+
+threepio = Droid(
+ id="2000",
+ name="C-3PO",
+ friends=["1000", "1002", "1003", "2001"],
+ appearsIn=[4, 5, 6],
+ primaryFunction="Protocol",
+)
+
+artoo = Droid(
+ id="2001",
+ name="R2-D2",
+ friends=["1000", "1002", "1003"],
+ appearsIn=[4, 5, 6],
+ primaryFunction="Astromech",
+)
+
+droid_data: Dict[str, Droid] = {"2000": threepio, "2001": artoo}
+
+
+# noinspection PyShadowingBuiltins
+async def get_character(id: str) -> Optional[Character]:
+ """Helper function to get a character by ID."""
+ # We use an async function just to illustrate that GraphQL-core supports it.
+ return human_data.get(id) or droid_data.get(id)
+
+
+def get_friends(character: Character) -> Iterator[Awaitable[Optional[Character]]]:
+ """Allows us to query for a character's friends."""
+ # Notice that GraphQL-core accepts iterators of awaitables.
+ return map(get_character, character.friends)
+
+
+def get_hero(episode: int) -> Character:
+ """Allows us to fetch the undisputed hero of the trilogy, R2-D2."""
+ if episode == 5:
+ # Luke is the hero of Episode V.
+ return luke
+ # Artoo is the hero otherwise.
+ return artoo
+
+
+# noinspection PyShadowingBuiltins
+def get_human(id: str) -> Optional[Human]:
+ """Allows us to query for the human with the given id."""
+ return human_data.get(id)
+
+
+# noinspection PyShadowingBuiltins
+def get_droid(id: str) -> Optional[Droid]:
+ """Allows us to query for the droid with the given id."""
+ return droid_data.get(id)
+
+
+# noinspection PyUnusedLocal
+def get_secret_backstory(character: Character) -> str:
+ """Raise an error when attempting to get the secret backstory."""
+ raise RuntimeError("secretBackstory is secret.")
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/star_wars_example/schema.py b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/star_wars_example/schema.py
new file mode 100644
index 000000000..5c6adc946
--- /dev/null
+++ b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/star_wars_example/schema.py
@@ -0,0 +1,274 @@
+"""Star Wars GraphQL schema
+
+This is designed to be an end-to-end test, demonstrating the full GraphQL stack.
+
+We will create a GraphQL schema that describes the major characters in the original
+Star Wars trilogy.
+
+NOTE: This may contain spoilers for the original Star Wars trilogy.
+
+Using our shorthand to describe type systems, the type system for our Star Wars example
+is::
+
+ enum Episode { NEW_HOPE, EMPIRE, JEDI }
+
+ interface Character {
+ id: String!
+ name: String
+ friends: [Character]
+ appearsIn: [Episode]
+ }
+
+ type Human implements Character {
+ id: String!
+ name: String
+ friends: [Character]
+ appearsIn: [Episode]
+ homePlanet: String
+ }
+
+ type Droid implements Character {
+ id: String!
+ name: String
+ friends: [Character]
+ appearsIn: [Episode]
+ primaryFunction: String
+ }
+
+ type Query {
+ hero(episode: Episode): Character
+ human(id: String!): Human
+ droid(id: String!): Droid
+ }
+"""
+import asyncio
+
+from graphql.type import (
+ GraphQLArgument,
+ GraphQLEnumType,
+ GraphQLEnumValue,
+ GraphQLField,
+ GraphQLInterfaceType,
+ GraphQLList,
+ GraphQLNonNull,
+ GraphQLObjectType,
+ GraphQLSchema,
+ GraphQLString,
+)
+
+from .data import (
+ get_droid,
+ get_friends,
+ get_hero,
+ get_human,
+ get_secret_backstory,
+)
+
+__all__ = ["star_wars_schema"]
+
+# We begin by setting up our schema.
+
+# The original trilogy consists of three movies.
+#
+# This implements the following type system shorthand:
+# enum Episode { NEW_HOPE, EMPIRE, JEDI }
+
+episode_enum = GraphQLEnumType(
+ "Episode",
+ {
+ "NEW_HOPE": GraphQLEnumValue(4, description="Released in 1977."),
+ "EMPIRE": GraphQLEnumValue(5, description="Released in 1980."),
+ "JEDI": GraphQLEnumValue(6, description="Released in 1983."),
+ },
+ description="One of the films in the Star Wars Trilogy",
+)
+
+# Characters in the Star Wars trilogy are either humans or droids.
+#
+# This implements the following type system shorthand:
+# interface Character {
+# id: String!
+# name: String
+# friends: [Character]
+# appearsIn: [Episode]
+# secretBackstory: String
+
+human_type: GraphQLObjectType
+droid_type: GraphQLObjectType
+
+character_interface: GraphQLInterfaceType = GraphQLInterfaceType(
+ "Character",
+ lambda: {
+ "id": GraphQLField(
+ GraphQLNonNull(GraphQLString), description="The id of the character."
+ ),
+ "name": GraphQLField(GraphQLString, description="The name of the character."),
+ "friends": GraphQLField(
+ GraphQLList(character_interface),
+ description="The friends of the character,"
+ " or an empty list if they have none.",
+ ),
+ "appearsIn": GraphQLField(
+ GraphQLList(episode_enum), description="Which movies they appear in."
+ ),
+ "secretBackstory": GraphQLField(
+ GraphQLString, description="All secrets about their past."
+ ),
+ },
+ resolve_type=lambda character, _info, _type: {
+ "Human": human_type.name,
+ "Droid": droid_type.name,
+ }[character.type],
+ description="A character in the Star Wars Trilogy",
+)
+
+# We define our human type, which implements the character interface.
+#
+# This implements the following type system shorthand:
+# type Human : Character {
+# id: String!
+# name: String
+# friends: [Character]
+# appearsIn: [Episode]
+# secretBackstory: String
+# }
+
+human_type = GraphQLObjectType(
+ "Human",
+ lambda: {
+ "id": GraphQLField(
+ GraphQLNonNull(GraphQLString), description="The id of the human."
+ ),
+ "name": GraphQLField(GraphQLString, description="The name of the human."),
+ "friends": GraphQLField(
+ GraphQLList(character_interface),
+ description="The friends of the human,"
+ " or an empty list if they have none.",
+ resolve=lambda human, _info: get_friends(human),
+ ),
+ "appearsIn": GraphQLField(
+ GraphQLList(episode_enum), description="Which movies they appear in."
+ ),
+ "homePlanet": GraphQLField(
+ GraphQLString,
+ description="The home planet of the human, or null if unknown.",
+ ),
+ "secretBackstory": GraphQLField(
+ GraphQLString,
+ resolve=lambda human, _info: get_secret_backstory(human),
+ description="Where are they from and how they came to be who they are.",
+ ),
+ },
+ interfaces=[character_interface],
+ description="A humanoid creature in the Star Wars universe.",
+)
+
+# The other type of character in Star Wars is a droid.
+#
+# This implements the following type system shorthand:
+# type Droid : Character {
+# id: String!
+# name: String
+# friends: [Character]
+# appearsIn: [Episode]
+# secretBackstory: String
+# primaryFunction: String
+# }
+
+droid_type = GraphQLObjectType(
+ "Droid",
+ lambda: {
+ "id": GraphQLField(
+ GraphQLNonNull(GraphQLString), description="The id of the droid."
+ ),
+ "name": GraphQLField(GraphQLString, description="The name of the droid."),
+ "friends": GraphQLField(
+ GraphQLList(character_interface),
+ description="The friends of the droid,"
+ " or an empty list if they have none.",
+ resolve=lambda droid, _info: get_friends(droid),
+ ),
+ "appearsIn": GraphQLField(
+ GraphQLList(episode_enum), description="Which movies they appear in."
+ ),
+ "secretBackstory": GraphQLField(
+ GraphQLString,
+ resolve=lambda droid, _info: get_secret_backstory(droid),
+ description="Construction date and the name of the designer.",
+ ),
+ "primaryFunction": GraphQLField(
+ GraphQLString, description="The primary function of the droid."
+ ),
+ },
+ interfaces=[character_interface],
+ description="A mechanical creature in the Star Wars universe.",
+)
+
+# This is the type that will be the root of our query, and the
+# entry point into our schema. It gives us the ability to fetch
+# objects by their IDs, as well as to fetch the undisputed hero
+# of the Star Wars trilogy, R2-D2, directly.
+#
+# This implements the following type system shorthand:
+# type Query {
+# hero(episode: Episode): Character
+# human(id: String!): Human
+# droid(id: String!): Droid
+# }
+
+# noinspection PyShadowingBuiltins
+query_type = GraphQLObjectType(
+ "Query",
+ lambda: {
+ "hero": GraphQLField(
+ character_interface,
+ args={
+ "episode": GraphQLArgument(
+ episode_enum,
+ description=(
+ "If omitted, returns the hero of the whole saga."
+ " If provided, returns the hero of that particular episode."
+ ),
+ )
+ },
+ resolve=lambda _source, _info, episode=None: get_hero(episode),
+ ),
+ "human": GraphQLField(
+ human_type,
+ args={
+ "id": GraphQLArgument(
+ GraphQLNonNull(GraphQLString), description="id of the human"
+ )
+ },
+ resolve=lambda _source, _info, id: get_human(id),
+ ),
+ "droid": GraphQLField(
+ droid_type,
+ args={
+ "id": GraphQLArgument(
+ GraphQLNonNull(GraphQLString), description="id of the droid"
+ )
+ },
+ resolve=lambda _source, _info, id: get_droid(id),
+ ),
+ },
+)
+
+
+async def resolve_hello(obj, info):
+ await asyncio.sleep(3)
+ return 'world'
+
+mutation_type = GraphQLObjectType(
+ "Mutation",
+ fields={
+ 'hello': GraphQLField(
+ GraphQLString,
+ resolve=resolve_hello)
+ }
+)
+
+# Finally, we construct our schema (whose starting query type is the query
+# type we defined above) and export it.
+
+star_wars_schema = GraphQLSchema(query=query_type, mutation=mutation_type, types=[human_type, droid_type])
diff --git a/packages/plugins/minos-graphql-aiohttp/poetry.lock b/packages/plugins/minos-graphql-aiohttp/poetry.lock
index 231a93c28..d17b27ac0 100644
--- a/packages/plugins/minos-graphql-aiohttp/poetry.lock
+++ b/packages/plugins/minos-graphql-aiohttp/poetry.lock
@@ -85,17 +85,6 @@ category = "dev"
optional = false
python-versions = "*"
-[[package]]
-name = "aniso8601"
-version = "9.0.1"
-description = "A library for parsing ISO 8601 strings."
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.extras]
-dev = ["black", "coverage", "isort", "pre-commit", "pyenchant", "pylint"]
-
[[package]]
name = "async-timeout"
version = "4.0.2"
@@ -326,42 +315,14 @@ category = "main"
optional = false
python-versions = ">=3.7"
-[[package]]
-name = "graphene"
-version = "3.0"
-description = "GraphQL Framework for Python"
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.dependencies]
-aniso8601 = ">=8,<10"
-graphql-core = ">=3.1.2,<3.2.0"
-graphql-relay = ">=3.0,<4"
-
-[package.extras]
-dev = ["black (==19.10b0)", "flake8 (>=3.7,<4)", "pytest (>=5.3,<6)", "pytest-benchmark (>=3.2,<4)", "pytest-cov (>=2.8,<3)", "pytest-mock (>=2,<3)", "pytest-asyncio (>=0.10,<2)", "snapshottest (>=0.5,<1)", "coveralls (>=1.11,<2)", "promise (>=2.3,<3)", "mock (>=4.0,<5)", "pytz (==2021.1)", "iso8601 (>=0.1,<2)"]
-test = ["pytest (>=5.3,<6)", "pytest-benchmark (>=3.2,<4)", "pytest-cov (>=2.8,<3)", "pytest-mock (>=2,<3)", "pytest-asyncio (>=0.10,<2)", "snapshottest (>=0.5,<1)", "coveralls (>=1.11,<2)", "promise (>=2.3,<3)", "mock (>=4.0,<5)", "pytz (==2021.1)", "iso8601 (>=0.1,<2)"]
-
[[package]]
name = "graphql-core"
-version = "3.1.7"
+version = "3.2.0"
description = "GraphQL implementation for Python, a port of GraphQL.js, the JavaScript reference implementation for GraphQL."
category = "main"
optional = false
python-versions = ">=3.6,<4"
-[[package]]
-name = "graphql-relay"
-version = "3.1.5"
-description = "Relay library for graphql-core"
-category = "main"
-optional = false
-python-versions = ">=3.6,<4"
-
-[package.dependencies]
-graphql-core = ">=3.1,<3.2"
-
[[package]]
name = "identify"
version = "2.4.11"
@@ -978,7 +939,7 @@ testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-
[metadata]
lock-version = "1.1"
python-versions = "^3.9"
-content-hash = "bdba872b9f221db81478baf76a21d4d23e88691a09a3aab49967bbceb6e42685"
+content-hash = "e7b55f719918838ac8d73e1ee1cf1df328f9c6d62e7bb835c897f447c755a214"
[metadata.files]
aiohttp = [
@@ -1075,10 +1036,6 @@ alabaster = [
{file = "alabaster-0.7.12-py2.py3-none-any.whl", hash = "sha256:446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359"},
{file = "alabaster-0.7.12.tar.gz", hash = "sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02"},
]
-aniso8601 = [
- {file = "aniso8601-9.0.1-py2.py3-none-any.whl", hash = "sha256:1d2b7ef82963909e93c4f24ce48d4de9e66009a21bf1c1e1c85bdd0812fe412f"},
- {file = "aniso8601-9.0.1.tar.gz", hash = "sha256:72e3117667eedf66951bb2d93f4296a56b94b078a8a95905a052611fb3f1b973"},
-]
async-timeout = [
{file = "async-timeout-4.0.2.tar.gz", hash = "sha256:2163e1640ddb52b7a8c80d0a67a08587e5d245cc9c553a74a847056bc2976b15"},
{file = "async_timeout-4.0.2-py3-none-any.whl", hash = "sha256:8ca1e4fcf50d07413d66d1a5e416e42cfdf5851c981d679a09851a6853383b3c"},
@@ -1327,17 +1284,9 @@ frozenlist = [
{file = "frozenlist-1.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:772965f773757a6026dea111a15e6e2678fbd6216180f82a48a40b27de1ee2ab"},
{file = "frozenlist-1.3.0.tar.gz", hash = "sha256:ce6f2ba0edb7b0c1d8976565298ad2deba6f8064d2bebb6ffce2ca896eb35b0b"},
]
-graphene = [
- {file = "graphene-3.0-py2.py3-none-any.whl", hash = "sha256:57ce5ee7c9dc194224a1df96e4f7cb48d31eae96c791091d059f8f3d4d131390"},
- {file = "graphene-3.0.tar.gz", hash = "sha256:af48066e152200a071aac1e0b703954c717ec7268720ba190a0c91d9bcb0a122"},
-]
graphql-core = [
- {file = "graphql-core-3.1.7.tar.gz", hash = "sha256:62ec192150ccecd9a18cfb79e3e72eb7d1fd68fb594ef19c40099b6deec8ef0c"},
- {file = "graphql_core-3.1.7-py3-none-any.whl", hash = "sha256:9b460f60320be01c7f3b1766cf3e406933003008055079b9d983b8f3988f4400"},
-]
-graphql-relay = [
- {file = "graphql-relay-3.1.5.tar.gz", hash = "sha256:127f40913f11cb847452ef794931261aae3b222eaaf976f7c8430298534e54d3"},
- {file = "graphql_relay-3.1.5-py3-none-any.whl", hash = "sha256:d1455316182e1017d06f0b4d4a2ce8bafad38253a6c7c5666fc80d5d516ee5c8"},
+ {file = "graphql-core-3.2.0.tar.gz", hash = "sha256:86e2a0be008bfde19ef78388de8a725a1d942a9190ca431c24a60837973803ce"},
+ {file = "graphql_core-3.2.0-py3-none-any.whl", hash = "sha256:0dda7e63676f119bb3d814621190fedad72fda07a8e9ab780bedd9f1957c6dc6"},
]
identify = [
{file = "identify-2.4.11-py2.py3-none-any.whl", hash = "sha256:fd906823ed1db23c7a48f9b176a1d71cb8abede1e21ebe614bac7bdd688d9213"},
diff --git a/packages/plugins/minos-graphql-aiohttp/pyproject.toml b/packages/plugins/minos-graphql-aiohttp/pyproject.toml
index 9d5a2d17a..cce0f4fdf 100644
--- a/packages/plugins/minos-graphql-aiohttp/pyproject.toml
+++ b/packages/plugins/minos-graphql-aiohttp/pyproject.toml
@@ -35,9 +35,8 @@ minos-microservice-common = "^0.5.0"
minos-microservice-networks = "^0.5.0"
aiohttp = "^3.8.1"
aiohttp-jinja2 = "^1.5"
-async-timeout = "^4.0.2"
-graphene = "^3.0"
Jinja2 = "^3.0.3"
+graphql-core = "^3.2.0"
[tool.poetry.dev-dependencies]
minos-microservice-common = { path = "../../core/minos-microservice-common", develop = true }
diff --git a/packages/plugins/minos-graphql-aiohttp/tests/test_plugins/test_graphql_aiohttp/test_handlers.py b/packages/plugins/minos-graphql-aiohttp/tests/test_plugins/test_graphql_aiohttp/test_handlers.py
deleted file mode 100644
index 2616a6cff..000000000
--- a/packages/plugins/minos-graphql-aiohttp/tests/test_plugins/test_graphql_aiohttp/test_handlers.py
+++ /dev/null
@@ -1,131 +0,0 @@
-import unittest
-from unittest.mock import (
- AsyncMock,
-)
-from uuid import (
- uuid4,
-)
-
-from aiohttp import (
- web,
-)
-from aiohttp.web_exceptions import (
- HTTPInternalServerError,
-)
-from orjson import (
- orjson,
-)
-
-from minos.common.testing import (
- PostgresAsyncTestCase,
-)
-from minos.networks import (
- REQUEST_USER_CONTEXT_VAR,
- Request,
- Response,
-)
-from minos.plugins.graphql_aiohttp import (
- RestHandler,
- RestResponse,
- RestResponseException,
-)
-from tests.test_plugins.test_graphql_aiohttp.utils import (
- json_mocked_request,
- mocked_request,
-)
-from tests.utils import (
- BASE_PATH,
-)
-
-APPLICATION_JSON = "application/json"
-
-
-class _Cls:
- @staticmethod
- async def _fn(request: Request) -> Response:
- return RestResponse(await request.content())
-
- @staticmethod
- async def _fn_status(request: Request) -> Response:
- return RestResponse(status=await request.content())
-
- @staticmethod
- async def _fn_none(request: Request):
- return
-
- @staticmethod
- async def _fn_raises_response(request: Request) -> Response:
- raise RestResponseException("")
-
- @staticmethod
- async def _fn_raises_exception(request: Request) -> Response:
- raise ValueError
-
-
-class TestRestHandler(PostgresAsyncTestCase):
- CONFIG_FILE_PATH = BASE_PATH / "test_config.yml"
-
- def setUp(self) -> None:
- super().setUp()
- self.handler = RestHandler.from_config(config=self.config)
-
- def test_from_config(self):
- self.assertIsInstance(self.handler, RestHandler)
- self.assertEqual({("/order", "GET"), ("/ticket", "POST")}, set(self.handler.endpoints.keys()))
-
- def test_from_config_raises(self):
- with self.assertRaises(Exception):
- RestHandler.from_config()
-
- def test_get_app(self):
- self.assertIsInstance(self.handler.get_app(), web.Application)
-
- async def test_get_callback(self):
- handler = self.handler.get_callback(_Cls._fn)
- response = await handler(json_mocked_request({"foo": "bar"}))
- self.assertIsInstance(response, web.Response)
- self.assertEqual(orjson.dumps({"foo": "bar"}), response.body)
- self.assertEqual(APPLICATION_JSON, response.content_type)
-
- async def test_get_callback_status(self):
- handler = self.handler.get_callback(_Cls._fn_status)
- response = await handler(json_mocked_request(203))
- self.assertIsInstance(response, web.Response)
- self.assertEqual(None, response.body)
- self.assertEqual(APPLICATION_JSON, response.content_type)
- self.assertEqual(203, response.status)
-
- async def test_get_callback_none(self):
- handler = self.handler.get_callback(_Cls._fn_none)
- response = await handler(mocked_request())
- self.assertIsInstance(response, web.Response)
- self.assertEqual(None, response.text)
- self.assertEqual(APPLICATION_JSON, response.content_type)
-
- async def test_get_callback_raises_response(self):
- handler = self.handler.get_callback(_Cls._fn_raises_response)
- response = await handler(json_mocked_request({"foo": "bar"}))
- self.assertEqual(400, response.status)
-
- async def test_get_callback_raises_exception(self):
- handler = self.handler.get_callback(_Cls._fn_raises_exception)
- with self.assertRaises(HTTPInternalServerError):
- await handler(json_mocked_request({"foo": "bar"}))
-
- async def test_get_callback_with_user(self):
- user = uuid4()
-
- async def _fn(request) -> None:
- self.assertEqual(user, request.user)
- self.assertEqual(user, REQUEST_USER_CONTEXT_VAR.get())
-
- mock = AsyncMock(side_effect=_fn)
-
- handler = self.handler.get_callback(mock)
- await handler(json_mocked_request({"foo": "bar"}, user=user))
-
- self.assertEqual(1, mock.call_count)
-
-
-if __name__ == "__main__":
- unittest.main()
diff --git a/packages/plugins/minos-graphql-aiohttp/tests/test_plugins/test_graphql_aiohttp/test_requests.py b/packages/plugins/minos-graphql-aiohttp/tests/test_plugins/test_graphql_aiohttp/test_requests.py
deleted file mode 100644
index af0d8ec7f..000000000
--- a/packages/plugins/minos-graphql-aiohttp/tests/test_plugins/test_graphql_aiohttp/test_requests.py
+++ /dev/null
@@ -1,441 +0,0 @@
-import unittest
-import warnings
-from uuid import (
- uuid4,
-)
-
-from orjson import (
- orjson,
-)
-
-from minos.common import (
- MinosAvroProtocol,
- ModelType,
- classname,
-)
-from minos.networks import (
- NotHasContentException,
- NotHasParamsException,
- Response,
-)
-from minos.plugins.graphql_aiohttp import (
- RestRequest,
- RestResponse,
-)
-from tests.test_plugins.test_graphql_aiohttp.utils import (
- avro_mocked_request,
- bytes_mocked_request,
- form_mocked_request,
- json_mocked_request,
- mocked_request,
- text_mocked_request,
-)
-from tests.utils import (
- FakeModel,
-)
-
-APPLICATION_JSON = "application/json"
-APPLICATION_URLENCODED = "application/x-www-form-urlencoded"
-APPLICATION_OCTET_STREAM = "application/octet-stream"
-TEXT_PLAIN = "text/plain"
-AVRO_BINARY = "avro/binary"
-IMAGE_PNG = "image/png"
-
-
-class TestRestRequest(unittest.IsolatedAsyncioTestCase):
- def test_raw(self):
- raw = mocked_request()
- request = RestRequest(raw)
- self.assertEqual(raw, request.raw)
-
- def test_raw_request(self):
- raw = mocked_request()
- request = RestRequest(raw)
- with warnings.catch_warnings():
- warnings.simplefilter("ignore", DeprecationWarning)
- # noinspection PyDeprecation
- self.assertEqual(request.raw, request.raw_request)
-
- def test_repr(self):
- raw = mocked_request()
- request = RestRequest(raw)
- self.assertEqual(f"RestRequest({raw!r})", repr(request))
-
- def test_eq_true(self):
- request = mocked_request()
- self.assertEqual(RestRequest(request), RestRequest(request))
-
- def test_eq_false(self):
- self.assertNotEqual(RestRequest(mocked_request()), RestRequest(mocked_request()))
-
- def test_headers(self):
- raw = mocked_request(headers={"something": "123"})
- request = RestRequest(raw)
- self.assertEqual({"something": "123"}, request.headers)
-
- def test_user(self):
- uuid = uuid4()
- raw = mocked_request(user=uuid)
- request = RestRequest(raw)
- user = request.user
- self.assertEqual(uuid, user)
-
- def test_user_unset(self):
- raw = mocked_request()
- request = RestRequest(raw)
- self.assertEqual(None, request.user)
-
-
-class TestRestRequestContent(unittest.IsolatedAsyncioTestCase):
- def test_has_content_false(self):
- raw = mocked_request()
- request = RestRequest(raw)
- self.assertEqual(False, request.has_content)
-
- def test_has_content_true(self):
- raw = json_mocked_request(123)
- request = RestRequest(raw)
- self.assertEqual(True, request.has_content)
-
- async def test_empty_raises(self):
- raw = mocked_request()
- request = RestRequest(raw)
- with self.assertRaises(NotHasContentException):
- await request.content()
-
- async def test_json_int(self):
- expected = 56
-
- raw = json_mocked_request(56)
- request = RestRequest(raw)
- observed = await request.content()
-
- self.assertEqual(expected, observed)
-
- async def test_json_list(self):
- expected = [{"doors": 3, "color": "blue", "owner": None}, {"doors": 5, "color": "red", "owner": None}]
-
- raw = json_mocked_request(
- [{"doors": 3, "color": "blue", "owner": None}, {"doors": 5, "color": "red", "owner": None}]
- )
- request = RestRequest(raw)
- observed = await request.content()
-
- self.assertEqual(expected, observed)
-
- async def test_json_dict(self):
- expected = {"doors": 3, "color": "blue", "owner": None}
-
- raw = json_mocked_request({"doors": 3, "color": "blue", "owner": None})
- request = RestRequest(raw)
- observed = await request.content()
-
- self.assertEqual(expected, observed)
-
- async def test_form(self):
- expected = {"foo": "foo1", "bar": ["bar1", "bar2"]}
-
- raw = form_mocked_request({"foo": "foo1", "bar": ["bar1", "bar2"]})
- request = RestRequest(raw)
- observed = await request.content()
-
- self.assertEqual(expected, observed)
-
- async def test_avro_int(self):
- expected = 56
-
- raw = avro_mocked_request(56, "int")
- request = RestRequest(raw)
- observed = await request.content()
-
- self.assertEqual(expected, observed)
-
- async def test_avro_uuid(self):
- expected = uuid4()
-
- raw = avro_mocked_request(expected, {"type": "string", "logicalType": "uuid"})
- request = RestRequest(raw)
- observed = await request.content()
-
- self.assertEqual(expected, observed)
-
- async def test_avro_dto(self):
- expected = ModelType.build("FakeModel", {"text": str})("foobar")
-
- raw = avro_mocked_request(
- {"text": "foobar"}, {"name": "FakeModel", "type": "record", "fields": [{"name": "text", "type": "string"}]}
- )
- request = RestRequest(raw)
- observed = await request.content()
-
- self.assertEqual(expected, observed)
-
- async def test_avro_model(self):
- expected = FakeModel("foobar")
-
- raw = avro_mocked_request(expected.avro_data, expected.avro_schema)
- request = RestRequest(raw)
- observed = await request.content()
-
- self.assertEqual(expected, observed)
-
- async def test_text(self):
- expected = "foobar"
-
- raw = text_mocked_request("foobar")
- request = RestRequest(raw)
- observed = await request.content()
-
- self.assertEqual(expected, observed)
-
- async def test_bytes(self):
- expected = bytes("foobar", "utf-8")
-
- raw = bytes_mocked_request(bytes("foobar", "utf-8"))
- request = RestRequest(raw)
- observed = await request.content()
-
- self.assertEqual(expected, observed)
-
- async def test_raises(self):
- raw = mocked_request(content_type="foo/bar", data="foobar".encode())
- request = RestRequest(raw)
- with self.assertRaises(ValueError):
- await request.content()
-
- async def test_with_type(self):
- # noinspection PyPep8Naming
- car = ModelType.build("Car", {"doors": int, "color": str, "owner": type(None)})
- expected = [car(3, "blue", None), car(5, "red", None)]
-
- raw = json_mocked_request([{"doors": "3", "color": "blue"}, {"doors": "5", "color": "red"}])
- request = RestRequest(raw)
- observed = await request.content(type_=list[car])
-
- self.assertEqual(expected, observed)
-
- async def test_with_type_classname(self):
- expected = 3
-
- raw = json_mocked_request("3")
- request = RestRequest(raw)
- observed = await request.content(type_=classname(int))
-
- self.assertEqual(expected, observed)
-
- async def test_with_model_type(self):
- expected = 3
-
- raw = json_mocked_request("3")
- request = RestRequest(raw)
- with warnings.catch_warnings():
- warnings.simplefilter("ignore", DeprecationWarning)
- observed = await request.content(model_type=classname(int))
-
- self.assertEqual(expected, observed)
-
-
-class TestRestRequestParams(unittest.IsolatedAsyncioTestCase):
- def test_has_url_params_false(self):
- raw = mocked_request()
- request = RestRequest(raw)
- self.assertEqual(False, request.has_url_params)
-
- def test_has_url_params_true(self):
- raw = mocked_request(url_params=[("foo", "1"), ("bar", "2"), ("foo", "3")])
- request = RestRequest(raw)
- self.assertEqual(True, request.has_url_params)
-
- async def test_url_params(self):
- expected = {"bar": "2", "foo": ["1", "3"]}
-
- raw = mocked_request(url_params=[("foo", "1"), ("bar", "2"), ("foo", "3")])
- request = RestRequest(raw)
- observed = await request.url_params()
-
- self.assertEqual(expected, observed)
-
- async def test_url_params_with_type(self):
- # noinspection PyPep8Naming
- params = ModelType.build("Params", {"foo": list[int], "bar": int})
- expected = params(foo=[1, 3], bar=2)
-
- raw = mocked_request(url_params=[("foo", "1"), ("bar", "2"), ("foo", "3")])
- request = RestRequest(raw)
- observed = await request.url_params(type_=params)
-
- self.assertEqual(expected, observed)
-
- async def test_url_params_raises(self):
- raw = mocked_request()
- request = RestRequest(raw)
- with self.assertRaises(NotHasParamsException):
- await request.url_params()
-
- def test_has_query_params_false(self):
- raw = mocked_request()
- request = RestRequest(raw)
- self.assertEqual(False, request.has_query_params)
-
- def test_has_query_params_true(self):
- raw = mocked_request(query_params=[("one", "1"), ("two", "2"), ("one", "3")])
- request = RestRequest(raw)
- self.assertEqual(True, request.has_query_params)
-
- async def test_query_params(self):
- expected = {"one": ["1", "3"], "two": "2"}
-
- raw = mocked_request(query_params=[("one", "1"), ("two", "2"), ("one", "3")])
- request = RestRequest(raw)
- observed = await request.query_params()
-
- self.assertEqual(expected, observed)
-
- async def test_query_params_with_type(self):
- # noinspection PyPep8Naming
- params = ModelType.build("Params", {"one": list[int], "two": int})
- expected = params(one=[1, 3], two=2)
-
- raw = mocked_request(query_params=[("one", "1"), ("two", "2"), ("one", "3")])
- request = RestRequest(raw)
- observed = await request.query_params(type_=params)
-
- self.assertEqual(expected, observed)
-
- async def test_query_params_raises(self):
- raw = mocked_request()
- request = RestRequest(raw)
- with self.assertRaises(NotHasParamsException):
- await request.query_params()
-
- def test_has_params_false(self):
- raw = mocked_request()
- request = RestRequest(raw)
- self.assertEqual(False, request.has_params)
-
- def test_has_params_true(self):
- raw = mocked_request(url_params=[("foo", "1"), ("bar", "2"), ("foo", "3")])
- request = RestRequest(raw)
- self.assertEqual(True, request.has_params)
-
- raw = mocked_request(query_params=[("one", "1"), ("two", "2"), ("one", "3")])
- request = RestRequest(raw)
- self.assertEqual(True, request.has_params)
-
- raw = mocked_request(
- url_params=[("foo", "1"), ("bar", "2"), ("foo", "3")],
- query_params=[("one", "1"), ("two", "2"), ("one", "3")],
- )
- request = RestRequest(raw)
- self.assertEqual(True, request.has_params)
-
- async def test_params(self):
- expected = {"bar": "2", "foo": ["1", "3"], "one": ["1", "3"], "two": "2"}
-
- raw = mocked_request(
- url_params=[("foo", "1"), ("bar", "2"), ("foo", "3")],
- query_params=[("one", "1"), ("two", "2"), ("one", "3")],
- )
- request = RestRequest(raw)
- observed = await request.params()
-
- self.assertEqual(expected, observed)
-
- async def test_params_with_type(self):
- # noinspection PyPep8Naming
- params = ModelType.build("Params", {"foo": list[int], "bar": int, "one": list[int], "two": int})
- expected = params(foo=[1, 3], bar=2, one=[1, 3], two=2)
-
- raw = mocked_request(
- url_params=[("foo", "1"), ("bar", "2"), ("foo", "3")],
- query_params=[("one", "1"), ("two", "2"), ("one", "3")],
- )
- request = RestRequest(raw)
- observed = await request.params(type_=params)
-
- self.assertEqual(expected, observed)
-
- async def test_params_raises(self):
- raw = mocked_request()
- request = RestRequest(raw)
- with self.assertRaises(NotHasParamsException):
- await request.params()
-
-
-class TestRestResponse(unittest.IsolatedAsyncioTestCase):
- async def test_from_response(self):
- base = Response("foobar")
-
- response = RestResponse.from_response(base)
-
- self.assertEqual(orjson.dumps("foobar"), await response.content())
- self.assertEqual(APPLICATION_JSON, response.content_type)
-
- def test_from_response_already(self):
- base = RestResponse()
-
- response = RestResponse.from_response(base)
-
- self.assertEqual(base, response)
-
- def test_from_response_empty(self):
- response = RestResponse.from_response(None)
-
- self.assertEqual(RestResponse(), response)
-
- async def test_empty(self):
- response = RestResponse()
- self.assertEqual(None, await response.content())
- self.assertEqual(APPLICATION_JSON, response.content_type)
-
- async def test_content_json(self):
- data = [FakeModel("foo"), FakeModel("bar")]
- response = RestResponse(data)
- self.assertEqual(orjson.dumps([item.avro_data for item in data]), await response.content())
- self.assertEqual(APPLICATION_JSON, response.content_type)
-
- async def test_content_form(self):
- data = {"foo": "bar", "one": "two"}
- response = RestResponse(data, content_type="application/x-www-form-urlencoded")
- self.assertEqual("foo=bar&one=two".encode(), await response.content())
- self.assertEqual(APPLICATION_URLENCODED, response.content_type)
-
- async def test_content_bytes(self):
- data = "foobar".encode()
- response = RestResponse(data, content_type=APPLICATION_OCTET_STREAM)
- self.assertEqual("foobar".encode(), await response.content())
- self.assertEqual(APPLICATION_OCTET_STREAM, response.content_type)
-
- async def test_content_bytes_raises(self):
- data = 56
- response = RestResponse(data, content_type=APPLICATION_OCTET_STREAM)
- with self.assertRaises(ValueError):
- await response.content()
-
- async def test_content_text(self):
- data = "foobar"
- response = RestResponse(data, content_type=TEXT_PLAIN)
- self.assertEqual("foobar".encode(), await response.content())
- self.assertEqual(TEXT_PLAIN, response.content_type)
-
- async def test_content_text_raises(self):
- data = 56
- response = RestResponse(data, content_type=TEXT_PLAIN)
- with self.assertRaises(ValueError):
- await response.content()
-
- async def test_content_avro(self):
- data = "foobar"
- response = RestResponse(data, content_type=AVRO_BINARY)
- self.assertEqual(data, MinosAvroProtocol.decode(await response.content()))
- self.assertEqual(AVRO_BINARY, response.content_type)
-
- async def test_content_image(self):
- data = bytes("image", "utf-8")
- response = RestResponse(data, content_type=IMAGE_PNG)
- self.assertEqual(data, await response.content())
- self.assertEqual(IMAGE_PNG, response.content_type)
-
-
-if __name__ == "__main__":
- unittest.main()
diff --git a/packages/plugins/minos-graphql-aiohttp/tests/test_plugins/test_graphql_aiohttp/test_services.py b/packages/plugins/minos-graphql-aiohttp/tests/test_plugins/test_graphql_aiohttp/test_services.py
deleted file mode 100644
index 640f76fc4..000000000
--- a/packages/plugins/minos-graphql-aiohttp/tests/test_plugins/test_graphql_aiohttp/test_services.py
+++ /dev/null
@@ -1,48 +0,0 @@
-import unittest
-
-from aiohttp.test_utils import (
- AioHTTPTestCase,
-)
-
-from minos.common import (
- MinosConfig,
-)
-from minos.plugins.graphql_aiohttp import (
- RestService,
-)
-from tests.utils import (
- BASE_PATH,
-)
-
-
-class TestRestService(AioHTTPTestCase):
- CONFIG_FILE_PATH = BASE_PATH / "test_config.yml"
-
- async def get_application(self):
- """
- Override the get_app method to return your application.
- """
- config = MinosConfig(self.CONFIG_FILE_PATH)
- rest_interface = RestService(config=config)
-
- return await rest_interface.create_application()
-
- async def test_methods(self):
- url = "/order"
- resp = await self.client.request("GET", url)
- assert resp.status == 200
- text = await resp.text()
- assert "get_order" in text
-
- url = "/ticket"
- resp = await self.client.request("POST", url)
- assert resp.status == 200
- text = await resp.text()
- assert "ticket_added" in text
-
- resp = await self.client.request("GET", "/system/health")
- assert resp.status == 200
-
-
-if __name__ == "__main__":
- unittest.main()
From f35543e0fc96240af1883c8e04114cfcfd79539b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Sergio=20Garc=C3=ADa=20Prado?=
Date: Fri, 11 Mar 2022 12:23:37 +0100
Subject: [PATCH 10/44] ISSUE #149 * Rename `graphql_aiohttp` as `graphql`. *
Add `GraphQlEnrouteDecorator` and `GraphQlHttpRouter`.
---
.../minos/plugins/graphql/__init__.py | 11 +++
.../minos/plugins/graphql/decorators.py | 50 ++++++++++++
.../exceptions.py | 0
.../handlers/__init__.py | 6 +-
.../handlers/abc.py | 57 +++++++------
.../plugins/graphql/handlers/graphiql.py | 11 +++
.../handlers/graphql.py | 9 ++-
.../{graphql_aiohttp => graphql}/main.py | 0
.../minos/plugins/graphql/routers.py | 81 +++++++++++++++++++
.../{graphql_aiohttp => graphql}/services.py | 12 +--
.../star_wars_example/__init__.py | 1 -
.../star_wars_example/data.py | 12 ++-
.../star_wars_example/schema.py | 65 ++++-----------
.../templates/graphiql.html | 0
.../minos/plugins/graphql_aiohttp/__init__.py | 10 ---
.../graphql_aiohttp/handlers/graphiql.py | 8 --
16 files changed, 225 insertions(+), 108 deletions(-)
create mode 100644 packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql/__init__.py
create mode 100644 packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql/decorators.py
rename packages/plugins/minos-graphql-aiohttp/minos/plugins/{graphql_aiohttp => graphql}/exceptions.py (100%)
rename packages/plugins/minos-graphql-aiohttp/minos/plugins/{graphql_aiohttp => graphql}/handlers/__init__.py (73%)
rename packages/plugins/minos-graphql-aiohttp/minos/plugins/{graphql_aiohttp => graphql}/handlers/abc.py (55%)
create mode 100644 packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql/handlers/graphiql.py
rename packages/plugins/minos-graphql-aiohttp/minos/plugins/{graphql_aiohttp => graphql}/handlers/graphql.py (80%)
rename packages/plugins/minos-graphql-aiohttp/minos/plugins/{graphql_aiohttp => graphql}/main.py (100%)
create mode 100644 packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql/routers.py
rename packages/plugins/minos-graphql-aiohttp/minos/plugins/{graphql_aiohttp => graphql}/services.py (88%)
rename packages/plugins/minos-graphql-aiohttp/minos/plugins/{graphql_aiohttp => graphql}/star_wars_example/__init__.py (99%)
rename packages/plugins/minos-graphql-aiohttp/minos/plugins/{graphql_aiohttp => graphql}/star_wars_example/data.py (95%)
rename packages/plugins/minos-graphql-aiohttp/minos/plugins/{graphql_aiohttp => graphql}/star_wars_example/schema.py (77%)
rename packages/plugins/minos-graphql-aiohttp/minos/plugins/{graphql_aiohttp => graphql}/templates/graphiql.html (100%)
delete mode 100644 packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/__init__.py
delete mode 100644 packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/handlers/graphiql.py
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql/__init__.py b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql/__init__.py
new file mode 100644
index 000000000..af4f2bf06
--- /dev/null
+++ b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql/__init__.py
@@ -0,0 +1,11 @@
+__author__ = "Minos Framework Devs"
+__email__ = "hey@minos.run"
+__version__ = "0.6.0"
+
+from .decorators import (
+ GraphQlEnroute,
+)
+
+
+def _register_enroute():
+ GraphQlEnroute.register()
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql/decorators.py b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql/decorators.py
new file mode 100644
index 000000000..ff512ed4e
--- /dev/null
+++ b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql/decorators.py
@@ -0,0 +1,50 @@
+from abc import (
+ ABC,
+)
+from collections.abc import (
+ Iterable,
+)
+from typing import (
+ Final,
+)
+
+from minos.networks import (
+ EnrouteDecorator,
+ EnrouteDecoratorKind,
+ enroute,
+)
+
+
+class GraphQlEnrouteDecorator(EnrouteDecorator, ABC):
+ """GraphQl Enroute class"""
+
+ def __init__(self, topic: str):
+ self.topic = topic
+
+ def __iter__(self) -> Iterable:
+ yield from (self.topic,)
+
+
+class GraphQlCommandEnrouteDecorator(GraphQlEnrouteDecorator):
+ """GraphQl Command Enroute class"""
+
+ KIND: Final[EnrouteDecoratorKind] = EnrouteDecoratorKind.Command
+
+
+class GraphQlQueryEnrouteDecorator(GraphQlEnrouteDecorator):
+ """GraphQl Query Enroute class"""
+
+ KIND: Final[EnrouteDecoratorKind] = EnrouteDecoratorKind.Query
+
+
+class GraphQlEnroute:
+ """GraphQl Enroute class"""
+
+ command = GraphQlCommandEnrouteDecorator
+ query = GraphQlQueryEnrouteDecorator
+
+ @classmethod
+ def register(cls):
+ """TODO"""
+ # noinspection PyProtectedMember
+ enroute._register_sub_enroute("graphql", cls)
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/exceptions.py b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql/exceptions.py
similarity index 100%
rename from packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/exceptions.py
rename to packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql/exceptions.py
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/handlers/__init__.py b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql/handlers/__init__.py
similarity index 73%
rename from packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/handlers/__init__.py
rename to packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql/handlers/__init__.py
index 1e49dde44..6a3937e81 100644
--- a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/handlers/__init__.py
+++ b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql/handlers/__init__.py
@@ -1,6 +1,6 @@
+from .graphiql import (
+ GraphiqlHandler,
+)
from .graphql import (
GraphqlHandler,
)
-from .graphiql import (
- GraphiqlHandler,
-)
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/handlers/abc.py b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql/handlers/abc.py
similarity index 55%
rename from packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/handlers/abc.py
rename to packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql/handlers/abc.py
index 2b200ef00..18dd60455 100644
--- a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/handlers/abc.py
+++ b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql/handlers/abc.py
@@ -1,15 +1,21 @@
+import json
import logging
import urllib
-import json
+
import aiohttp
-from aiohttp import web
-from graphql import graphql
+from aiohttp import (
+ web,
+)
+from graphql import (
+ graphql,
+)
+
from minos.plugins.graphql_aiohttp.star_wars_example import (
star_wars_schema,
)
-class BaseHandler(web.View):
+class BaseHandler(web.View):
@property
def fetch(self):
return self.app.fetch
@@ -22,9 +28,9 @@ async def options(self):
return web.Response(
status=204,
headers={
- 'Access-Control-Allow-Origin': '*',
- 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, PATCH',
- 'Access-Control-Allow-Headers': 'x-requested-with,access-control-allow-origin,authorization,content-type',
+ "Access-Control-Allow-Origin": "*",
+ "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, PATCH",
+ "Access-Control-Allow-Headers": "x-requested-with,access-control-allow-origin,authorization,content-type",
},
)
@@ -40,7 +46,7 @@ async def handle_graqhql(self):
status = 200
result = await self.execute_graphql()
logging.debug(
- 'GraphQL result data: %s errors: %s',
+ "GraphQL result data: %s errors: %s",
result.data,
result.errors,
)
@@ -54,40 +60,41 @@ async def handle_graqhql(self):
errors = []
return web.json_response(
- {'data': result.data, 'errors': [err.message for err in errors]},
+ {"data": result.data, "errors": [err.message for err in errors]},
status=status,
- headers={'Access-Control-Allow-Origin': '*'},
+ headers={"Access-Control-Allow-Origin": "*"},
)
async def execute_graphql(self):
graphql_req = await self.graphql_request
- context_value = graphql_req.get('context', {})
- variables = graphql_req.get('variables', {})
-
- context_value['application'] = self.app
- source = graphql_req['query']
- result = await graphql(schema=star_wars_schema,
- source=source,
- context_value=context_value,
- variable_values=variables,
- )
+ context_value = graphql_req.get("context", {})
+ variables = graphql_req.get("variables", {})
+
+ context_value["application"] = self.app
+ source = graphql_req["query"]
+ result = await graphql(
+ schema=star_wars_schema,
+ source=source,
+ context_value=context_value,
+ variable_values=variables,
+ )
- ref = self.request.headers.get('referer')
- url_path = ''
+ ref = self.request.headers.get("referer")
+ url_path = ""
if ref:
url = urllib.parse.urlparse(ref)
url_path = url.path
if result.errors:
- if '/graphiql' not in url_path:
+ if "/graphiql" not in url_path:
aiohttp.log.server_logger.error(f'Graphql query error: for query "{graphql_req}"')
return result
@property
async def graphql_request(self):
- if self.request.method == 'GET':
- return json.loads(self.request.query['q'])
+ if self.request.method == "GET":
+ return json.loads(self.request.query["q"])
return await self.request.json()
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql/handlers/graphiql.py b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql/handlers/graphiql.py
new file mode 100644
index 000000000..3cf938e43
--- /dev/null
+++ b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql/handlers/graphiql.py
@@ -0,0 +1,11 @@
+import aiohttp_jinja2
+
+from .abc import (
+ BaseHandler,
+)
+
+
+class GraphiqlHandler(BaseHandler):
+ @aiohttp_jinja2.template("graphiql.html")
+ async def get(self):
+ return {"base_url": f"/graphql"}
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/handlers/graphql.py b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql/handlers/graphql.py
similarity index 80%
rename from packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/handlers/graphql.py
rename to packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql/handlers/graphql.py
index 78a51aa7c..2e747a72e 100644
--- a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/handlers/graphql.py
+++ b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql/handlers/graphql.py
@@ -1,14 +1,17 @@
from graphql.utilities import (
- print_schema
+ print_schema,
)
-from .abc import GQLBaseHandler
from minos.plugins.graphql_aiohttp.star_wars_example import (
star_wars_schema,
)
-class GraphqlHandler(GQLBaseHandler):
+from .abc import (
+ GQLBaseHandler,
+)
+
+class GraphqlHandler(GQLBaseHandler):
@property
def schema(self):
return print_schema(star_wars_schema)
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/main.py b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql/main.py
similarity index 100%
rename from packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/main.py
rename to packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql/main.py
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql/routers.py b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql/routers.py
new file mode 100644
index 000000000..4eee7eee7
--- /dev/null
+++ b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql/routers.py
@@ -0,0 +1,81 @@
+from __future__ import (
+ annotations,
+)
+
+from typing import (
+ Callable,
+)
+
+from minos.common import (
+ MinosConfig,
+)
+from minos.networks import (
+ EnrouteBuilder,
+ HttpEnrouteDecorator,
+ HttpRequest,
+ HttpResponse,
+ HttpRouter,
+)
+
+from .decorators import (
+ GraphQlEnrouteDecorator,
+)
+
+
+class GraphQlHttpRouter(HttpRouter):
+ """TODO"""
+
+ def __init__(self, schema, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self._schema = schema
+
+ @staticmethod
+ def _from_config(config: MinosConfig, **kwargs) -> dict[(str, str), Callable]:
+ builder = EnrouteBuilder(*config.services, middleware=config.middleware)
+ routes = builder.get_all(config=config, **kwargs)
+
+ graphql_routes = {
+ GraphQlEnrouteDecorator(
+ "CreateProduct", request=GraphQLField(GraphQLObjectType), response=GraphQLField(GraphQLObjectType)
+ ): "fn1",
+ GraphQlEnrouteDecorator(
+ "UpdateProduct", request=GraphQLField(GraphQLObjectType), response=GraphQLField(GraphQLObjectType)
+ ): "fn2",
+ }
+
+ schema = GraphQLSchema(
+ query=GraphQLObjectType(
+ name="RootQueryType",
+ fields={
+ "CreateProduct": GraphQLField(
+ GraphQLString, args=GraphQLField(GraphQLObjectType), response=GraphQLField(GraphQLObjectType)
+ ),
+ "UpdateProduct": GraphQLField(
+ GraphQLString, args=GraphQLField(GraphQLObjectType), response=GraphQLField(GraphQLObjectType)
+ ),
+ },
+ )
+ )
+
+ return cls(schema)
+
+ @property
+ def routes(self) -> dict[HttpEnrouteDecorator, Callable]:
+ """TODO"""
+ return {
+ HttpEnrouteDecorator("/graphql", "POST"): self.handle_post,
+ HttpEnrouteDecorator("/graphql", "GET"): self.handle_get,
+ }
+
+ async def handle_post(self, request: HttpRequest) -> HttpResponse:
+ """TODO"""
+ graphql_query = await request.content()
+
+ content = await self._schema(graphql_query)
+
+ return HttpResponse(content)
+
+ async def handle_get(self, request: HttpRequest) -> HttpResponse:
+ """TODO"""
+ content = self._schema.json()
+ return HttpResponse(content)
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/services.py b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql/services.py
similarity index 88%
rename from packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/services.py
rename to packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql/services.py
index ce26b9858..e8bca0f69 100644
--- a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/services.py
+++ b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql/services.py
@@ -6,9 +6,10 @@
from aiomisc.service.aiohttp import (
AIOHTTPService,
)
+
from .handlers import (
GraphiqlHandler,
- GraphqlHandler
+ GraphqlHandler,
)
@@ -24,21 +25,22 @@ def __init__(self, **kwargs):
# self.handler = RestHandler.from_config(**kwargs)
# super().__init__(**(kwargs | {"address": self.handler.host, "port": self.handler.port}))
super().__init__(**(kwargs | {"address": "localhost", "port": 7030}))
+
"""
def _mount_graphql(self, app: web.Application):
GraphQLView.attach(app, schema=schema, graphiql=True)
"""
def register_handlers(self, app: web.Application):
- routes = [(r'/graphql', GraphqlHandler)]
+ routes = [(r"/graphql", GraphqlHandler)]
# if self.dev:
- routes += [(r'/graphiql', GraphiqlHandler)]
+ routes += [(r"/graphiql", GraphiqlHandler)]
app.router.add_routes([web.view(route[0], route[1]) for route in routes])
async def initialize_jinja2(self, app: web.Application):
- aiohttp_jinja2.setup(app, loader=jinja2.FileSystemLoader('./templates'))
+ aiohttp_jinja2.setup(app, loader=jinja2.FileSystemLoader("./templates"))
async def create_application(self) -> web.Application:
"""Create the web application.
@@ -49,6 +51,6 @@ async def create_application(self) -> web.Application:
self.register_handlers(app)
await self.initialize_jinja2(app)
# self._mount_routes(app)
- #self._mount_graphql(app)
+ # self._mount_graphql(app)
return app
# return self.handler.get_app() # pragma: no cover
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/star_wars_example/__init__.py b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql/star_wars_example/__init__.py
similarity index 99%
rename from packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/star_wars_example/__init__.py
rename to packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql/star_wars_example/__init__.py
index d06b7603f..5bcaa3cb0 100644
--- a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/star_wars_example/__init__.py
+++ b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql/star_wars_example/__init__.py
@@ -5,4 +5,3 @@
from .schema import (
star_wars_schema,
)
-
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/star_wars_example/data.py b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql/star_wars_example/data.py
similarity index 95%
rename from packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/star_wars_example/data.py
rename to packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql/star_wars_example/data.py
index 167d2a326..65aa5f650 100644
--- a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/star_wars_example/data.py
+++ b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql/star_wars_example/data.py
@@ -5,7 +5,13 @@
demo.
"""
-from typing import Awaitable, Collection, Dict, Iterator, Optional
+from typing import (
+ Awaitable,
+ Collection,
+ Dict,
+ Iterator,
+ Optional,
+)
__all__ = ["get_droid", "get_friends", "get_hero", "get_human", "get_secret_backstory"]
@@ -76,9 +82,7 @@ def __init__(self, id, name, friends, appearsIn, primaryFunction):
homePlanet="Alderaan",
)
-tarkin = Human(
- id="1004", name="Wilhuff Tarkin", friends=["1001"], appearsIn=[4], homePlanet=None
-)
+tarkin = Human(id="1004", name="Wilhuff Tarkin", friends=["1001"], appearsIn=[4], homePlanet=None)
human_data: Dict[str, Human] = {
"1000": luke,
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/star_wars_example/schema.py b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql/star_wars_example/schema.py
similarity index 77%
rename from packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/star_wars_example/schema.py
rename to packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql/star_wars_example/schema.py
index 5c6adc946..b6d9068aa 100644
--- a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/star_wars_example/schema.py
+++ b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql/star_wars_example/schema.py
@@ -99,21 +99,14 @@
character_interface: GraphQLInterfaceType = GraphQLInterfaceType(
"Character",
lambda: {
- "id": GraphQLField(
- GraphQLNonNull(GraphQLString), description="The id of the character."
- ),
+ "id": GraphQLField(GraphQLNonNull(GraphQLString), description="The id of the character."),
"name": GraphQLField(GraphQLString, description="The name of the character."),
"friends": GraphQLField(
GraphQLList(character_interface),
- description="The friends of the character,"
- " or an empty list if they have none.",
- ),
- "appearsIn": GraphQLField(
- GraphQLList(episode_enum), description="Which movies they appear in."
- ),
- "secretBackstory": GraphQLField(
- GraphQLString, description="All secrets about their past."
+ description="The friends of the character," " or an empty list if they have none.",
),
+ "appearsIn": GraphQLField(GraphQLList(episode_enum), description="Which movies they appear in."),
+ "secretBackstory": GraphQLField(GraphQLString, description="All secrets about their past."),
},
resolve_type=lambda character, _info, _type: {
"Human": human_type.name,
@@ -136,19 +129,14 @@
human_type = GraphQLObjectType(
"Human",
lambda: {
- "id": GraphQLField(
- GraphQLNonNull(GraphQLString), description="The id of the human."
- ),
+ "id": GraphQLField(GraphQLNonNull(GraphQLString), description="The id of the human."),
"name": GraphQLField(GraphQLString, description="The name of the human."),
"friends": GraphQLField(
GraphQLList(character_interface),
- description="The friends of the human,"
- " or an empty list if they have none.",
+ description="The friends of the human," " or an empty list if they have none.",
resolve=lambda human, _info: get_friends(human),
),
- "appearsIn": GraphQLField(
- GraphQLList(episode_enum), description="Which movies they appear in."
- ),
+ "appearsIn": GraphQLField(GraphQLList(episode_enum), description="Which movies they appear in."),
"homePlanet": GraphQLField(
GraphQLString,
description="The home planet of the human, or null if unknown.",
@@ -178,27 +166,20 @@
droid_type = GraphQLObjectType(
"Droid",
lambda: {
- "id": GraphQLField(
- GraphQLNonNull(GraphQLString), description="The id of the droid."
- ),
+ "id": GraphQLField(GraphQLNonNull(GraphQLString), description="The id of the droid."),
"name": GraphQLField(GraphQLString, description="The name of the droid."),
"friends": GraphQLField(
GraphQLList(character_interface),
- description="The friends of the droid,"
- " or an empty list if they have none.",
+ description="The friends of the droid," " or an empty list if they have none.",
resolve=lambda droid, _info: get_friends(droid),
),
- "appearsIn": GraphQLField(
- GraphQLList(episode_enum), description="Which movies they appear in."
- ),
+ "appearsIn": GraphQLField(GraphQLList(episode_enum), description="Which movies they appear in."),
"secretBackstory": GraphQLField(
GraphQLString,
resolve=lambda droid, _info: get_secret_backstory(droid),
description="Construction date and the name of the designer.",
),
- "primaryFunction": GraphQLField(
- GraphQLString, description="The primary function of the droid."
- ),
+ "primaryFunction": GraphQLField(GraphQLString, description="The primary function of the droid."),
},
interfaces=[character_interface],
description="A mechanical creature in the Star Wars universe.",
@@ -235,20 +216,12 @@
),
"human": GraphQLField(
human_type,
- args={
- "id": GraphQLArgument(
- GraphQLNonNull(GraphQLString), description="id of the human"
- )
- },
+ args={"id": GraphQLArgument(GraphQLNonNull(GraphQLString), description="id of the human")},
resolve=lambda _source, _info, id: get_human(id),
),
"droid": GraphQLField(
droid_type,
- args={
- "id": GraphQLArgument(
- GraphQLNonNull(GraphQLString), description="id of the droid"
- )
- },
+ args={"id": GraphQLArgument(GraphQLNonNull(GraphQLString), description="id of the droid")},
resolve=lambda _source, _info, id: get_droid(id),
),
},
@@ -257,16 +230,10 @@
async def resolve_hello(obj, info):
await asyncio.sleep(3)
- return 'world'
+ return "world"
-mutation_type = GraphQLObjectType(
- "Mutation",
- fields={
- 'hello': GraphQLField(
- GraphQLString,
- resolve=resolve_hello)
- }
-)
+
+mutation_type = GraphQLObjectType("Mutation", fields={"hello": GraphQLField(GraphQLString, resolve=resolve_hello)})
# Finally, we construct our schema (whose starting query type is the query
# type we defined above) and export it.
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/templates/graphiql.html b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql/templates/graphiql.html
similarity index 100%
rename from packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/templates/graphiql.html
rename to packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql/templates/graphiql.html
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/__init__.py b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/__init__.py
deleted file mode 100644
index d1a5f0caf..000000000
--- a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/__init__.py
+++ /dev/null
@@ -1,10 +0,0 @@
-__author__ = "Minos Framework Devs"
-__email__ = "hey@minos.run"
-__version__ = "0.6.0"
-
-from .exceptions import (
- RestResponseException,
-)
-from .services import (
- RestService,
-)
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/handlers/graphiql.py b/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/handlers/graphiql.py
deleted file mode 100644
index 4e82cd7c3..000000000
--- a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql_aiohttp/handlers/graphiql.py
+++ /dev/null
@@ -1,8 +0,0 @@
-import aiohttp_jinja2
-from .abc import BaseHandler
-
-
-class GraphiqlHandler(BaseHandler):
- @aiohttp_jinja2.template('graphiql.html')
- async def get(self):
- return {'base_url': f'/graphql'}
From 26d5929576632aaa2afe6ae4d1c7683cb3e12018 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Sergio=20Garc=C3=ADa=20Prado?=
Date: Fri, 11 Mar 2022 12:24:03 +0100
Subject: [PATCH 11/44] ISSUE #149 * Move to a plugin
---
.../decorators/definitions/graphql.py | 34 -------------------
1 file changed, 34 deletions(-)
delete mode 100644 packages/core/minos-microservice-networks/minos/networks/decorators/definitions/graphql.py
diff --git a/packages/core/minos-microservice-networks/minos/networks/decorators/definitions/graphql.py b/packages/core/minos-microservice-networks/minos/networks/decorators/definitions/graphql.py
deleted file mode 100644
index 95624d0ea..000000000
--- a/packages/core/minos-microservice-networks/minos/networks/decorators/definitions/graphql.py
+++ /dev/null
@@ -1,34 +0,0 @@
-from abc import (
- ABC,
-)
-from typing import (
- Final,
- Iterable,
-)
-
-from .abc import (
- EnrouteDecorator,
-)
-from .kinds import (
- EnrouteDecoratorKind,
-)
-
-
-class GraphqlEnrouteDecorator(EnrouteDecorator, ABC):
- """Rest Enroute class"""
-
- def __init__(self, url: str, method: str):
- self.url = url
- self.method = method
-
- def __iter__(self) -> Iterable:
- yield from (
- self.url,
- self.method,
- )
-
-
-class GraphqlQueryEnrouteDecorator(GraphqlEnrouteDecorator):
- """Rest Command Enroute class"""
-
- KIND: Final[EnrouteDecoratorKind] = EnrouteDecoratorKind.Graphql
From 873b5c8d5189888acc16d723201667e33edbc841 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Sergio=20Garc=C3=ADa=20Prado?=
Date: Fri, 11 Mar 2022 12:27:35 +0100
Subject: [PATCH 12/44] ISSUE #149 * Rename `minos-graphql-aiohttp` as
`minos-router-aiohttp`.
---
.../.editorconfig | 0
.../AUTHORS.md | 0
.../HISTORY.md | 0
.../{minos-graphql-aiohttp => minos-router-graphql}/LICENSE | 0
.../{minos-graphql-aiohttp => minos-router-graphql}/Makefile | 0
.../{minos-graphql-aiohttp => minos-router-graphql}/README.md | 2 +-
.../RUNTHETESTS.md | 0
.../{minos-graphql-aiohttp => minos-router-graphql}/SETUP.md | 0
.../docs/Makefile | 0
.../docs/authors.rst | 0
.../docs/conf.py | 0
.../docs/history.rst | 0
.../docs/index.rst | 0
.../docs/make.bat | 0
.../docs/readme.rst | 0
.../docs/runthetests.rst | 0
.../docs/usage.rst | 0
.../minos/plugins/graphql/__init__.py | 0
.../minos/plugins/graphql/decorators.py | 0
.../minos/plugins/graphql/exceptions.py | 0
.../minos/plugins/graphql/handlers/__init__.py | 0
.../minos/plugins/graphql/handlers/abc.py | 0
.../minos/plugins/graphql/handlers/graphiql.py | 0
.../minos/plugins/graphql/handlers/graphql.py | 0
.../minos/plugins/graphql/main.py | 0
.../minos/plugins/graphql/routers.py | 0
.../minos/plugins/graphql/services.py | 0
.../minos/plugins/graphql/star_wars_example/__init__.py | 0
.../minos/plugins/graphql/star_wars_example/data.py | 0
.../minos/plugins/graphql/star_wars_example/schema.py | 0
.../minos/plugins/graphql/templates/graphiql.html | 0
.../poetry.lock | 0
.../poetry.toml | 0
.../pyproject.toml | 4 ++--
.../{minos-graphql-aiohttp => minos-router-graphql}/setup.cfg | 0
.../tests/__init__.py | 0
.../tests/services/commands.py | 0
.../tests/services/queries.py | 0
.../tests/test_config.yml | 0
.../tests/test_plugins/test_graphql_aiohttp/utils.py | 0
.../tests/utils.py | 0
41 files changed, 3 insertions(+), 3 deletions(-)
rename packages/plugins/{minos-graphql-aiohttp => minos-router-graphql}/.editorconfig (100%)
rename packages/plugins/{minos-graphql-aiohttp => minos-router-graphql}/AUTHORS.md (100%)
rename packages/plugins/{minos-graphql-aiohttp => minos-router-graphql}/HISTORY.md (100%)
rename packages/plugins/{minos-graphql-aiohttp => minos-router-graphql}/LICENSE (100%)
rename packages/plugins/{minos-graphql-aiohttp => minos-router-graphql}/Makefile (100%)
rename packages/plugins/{minos-graphql-aiohttp => minos-router-graphql}/README.md (98%)
rename packages/plugins/{minos-graphql-aiohttp => minos-router-graphql}/RUNTHETESTS.md (100%)
rename packages/plugins/{minos-graphql-aiohttp => minos-router-graphql}/SETUP.md (100%)
rename packages/plugins/{minos-graphql-aiohttp => minos-router-graphql}/docs/Makefile (100%)
rename packages/plugins/{minos-graphql-aiohttp => minos-router-graphql}/docs/authors.rst (100%)
rename packages/plugins/{minos-graphql-aiohttp => minos-router-graphql}/docs/conf.py (100%)
rename packages/plugins/{minos-graphql-aiohttp => minos-router-graphql}/docs/history.rst (100%)
rename packages/plugins/{minos-graphql-aiohttp => minos-router-graphql}/docs/index.rst (100%)
rename packages/plugins/{minos-graphql-aiohttp => minos-router-graphql}/docs/make.bat (100%)
rename packages/plugins/{minos-graphql-aiohttp => minos-router-graphql}/docs/readme.rst (100%)
rename packages/plugins/{minos-graphql-aiohttp => minos-router-graphql}/docs/runthetests.rst (100%)
rename packages/plugins/{minos-graphql-aiohttp => minos-router-graphql}/docs/usage.rst (100%)
rename packages/plugins/{minos-graphql-aiohttp => minos-router-graphql}/minos/plugins/graphql/__init__.py (100%)
rename packages/plugins/{minos-graphql-aiohttp => minos-router-graphql}/minos/plugins/graphql/decorators.py (100%)
rename packages/plugins/{minos-graphql-aiohttp => minos-router-graphql}/minos/plugins/graphql/exceptions.py (100%)
rename packages/plugins/{minos-graphql-aiohttp => minos-router-graphql}/minos/plugins/graphql/handlers/__init__.py (100%)
rename packages/plugins/{minos-graphql-aiohttp => minos-router-graphql}/minos/plugins/graphql/handlers/abc.py (100%)
rename packages/plugins/{minos-graphql-aiohttp => minos-router-graphql}/minos/plugins/graphql/handlers/graphiql.py (100%)
rename packages/plugins/{minos-graphql-aiohttp => minos-router-graphql}/minos/plugins/graphql/handlers/graphql.py (100%)
rename packages/plugins/{minos-graphql-aiohttp => minos-router-graphql}/minos/plugins/graphql/main.py (100%)
rename packages/plugins/{minos-graphql-aiohttp => minos-router-graphql}/minos/plugins/graphql/routers.py (100%)
rename packages/plugins/{minos-graphql-aiohttp => minos-router-graphql}/minos/plugins/graphql/services.py (100%)
rename packages/plugins/{minos-graphql-aiohttp => minos-router-graphql}/minos/plugins/graphql/star_wars_example/__init__.py (100%)
rename packages/plugins/{minos-graphql-aiohttp => minos-router-graphql}/minos/plugins/graphql/star_wars_example/data.py (100%)
rename packages/plugins/{minos-graphql-aiohttp => minos-router-graphql}/minos/plugins/graphql/star_wars_example/schema.py (100%)
rename packages/plugins/{minos-graphql-aiohttp => minos-router-graphql}/minos/plugins/graphql/templates/graphiql.html (100%)
rename packages/plugins/{minos-graphql-aiohttp => minos-router-graphql}/poetry.lock (100%)
rename packages/plugins/{minos-graphql-aiohttp => minos-router-graphql}/poetry.toml (100%)
rename packages/plugins/{minos-graphql-aiohttp => minos-router-graphql}/pyproject.toml (93%)
rename packages/plugins/{minos-graphql-aiohttp => minos-router-graphql}/setup.cfg (100%)
rename packages/plugins/{minos-graphql-aiohttp => minos-router-graphql}/tests/__init__.py (100%)
rename packages/plugins/{minos-graphql-aiohttp => minos-router-graphql}/tests/services/commands.py (100%)
rename packages/plugins/{minos-graphql-aiohttp => minos-router-graphql}/tests/services/queries.py (100%)
rename packages/plugins/{minos-graphql-aiohttp => minos-router-graphql}/tests/test_config.yml (100%)
rename packages/plugins/{minos-graphql-aiohttp => minos-router-graphql}/tests/test_plugins/test_graphql_aiohttp/utils.py (100%)
rename packages/plugins/{minos-graphql-aiohttp => minos-router-graphql}/tests/utils.py (100%)
diff --git a/packages/plugins/minos-graphql-aiohttp/.editorconfig b/packages/plugins/minos-router-graphql/.editorconfig
similarity index 100%
rename from packages/plugins/minos-graphql-aiohttp/.editorconfig
rename to packages/plugins/minos-router-graphql/.editorconfig
diff --git a/packages/plugins/minos-graphql-aiohttp/AUTHORS.md b/packages/plugins/minos-router-graphql/AUTHORS.md
similarity index 100%
rename from packages/plugins/minos-graphql-aiohttp/AUTHORS.md
rename to packages/plugins/minos-router-graphql/AUTHORS.md
diff --git a/packages/plugins/minos-graphql-aiohttp/HISTORY.md b/packages/plugins/minos-router-graphql/HISTORY.md
similarity index 100%
rename from packages/plugins/minos-graphql-aiohttp/HISTORY.md
rename to packages/plugins/minos-router-graphql/HISTORY.md
diff --git a/packages/plugins/minos-graphql-aiohttp/LICENSE b/packages/plugins/minos-router-graphql/LICENSE
similarity index 100%
rename from packages/plugins/minos-graphql-aiohttp/LICENSE
rename to packages/plugins/minos-router-graphql/LICENSE
diff --git a/packages/plugins/minos-graphql-aiohttp/Makefile b/packages/plugins/minos-router-graphql/Makefile
similarity index 100%
rename from packages/plugins/minos-graphql-aiohttp/Makefile
rename to packages/plugins/minos-router-graphql/Makefile
diff --git a/packages/plugins/minos-graphql-aiohttp/README.md b/packages/plugins/minos-router-graphql/README.md
similarity index 98%
rename from packages/plugins/minos-graphql-aiohttp/README.md
rename to packages/plugins/minos-router-graphql/README.md
index 3be59ccff..6aaf6f343 100644
--- a/packages/plugins/minos-graphql-aiohttp/README.md
+++ b/packages/plugins/minos-router-graphql/README.md
@@ -2,7 +2,7 @@
-## minos-graphql-aiohttp
+## minos-router-graphql
[](https://pypi.org/project/minos-broker-kafka/)
[](https://minos-framework.github.io/minos-python)
diff --git a/packages/plugins/minos-graphql-aiohttp/RUNTHETESTS.md b/packages/plugins/minos-router-graphql/RUNTHETESTS.md
similarity index 100%
rename from packages/plugins/minos-graphql-aiohttp/RUNTHETESTS.md
rename to packages/plugins/minos-router-graphql/RUNTHETESTS.md
diff --git a/packages/plugins/minos-graphql-aiohttp/SETUP.md b/packages/plugins/minos-router-graphql/SETUP.md
similarity index 100%
rename from packages/plugins/minos-graphql-aiohttp/SETUP.md
rename to packages/plugins/minos-router-graphql/SETUP.md
diff --git a/packages/plugins/minos-graphql-aiohttp/docs/Makefile b/packages/plugins/minos-router-graphql/docs/Makefile
similarity index 100%
rename from packages/plugins/minos-graphql-aiohttp/docs/Makefile
rename to packages/plugins/minos-router-graphql/docs/Makefile
diff --git a/packages/plugins/minos-graphql-aiohttp/docs/authors.rst b/packages/plugins/minos-router-graphql/docs/authors.rst
similarity index 100%
rename from packages/plugins/minos-graphql-aiohttp/docs/authors.rst
rename to packages/plugins/minos-router-graphql/docs/authors.rst
diff --git a/packages/plugins/minos-graphql-aiohttp/docs/conf.py b/packages/plugins/minos-router-graphql/docs/conf.py
similarity index 100%
rename from packages/plugins/minos-graphql-aiohttp/docs/conf.py
rename to packages/plugins/minos-router-graphql/docs/conf.py
diff --git a/packages/plugins/minos-graphql-aiohttp/docs/history.rst b/packages/plugins/minos-router-graphql/docs/history.rst
similarity index 100%
rename from packages/plugins/minos-graphql-aiohttp/docs/history.rst
rename to packages/plugins/minos-router-graphql/docs/history.rst
diff --git a/packages/plugins/minos-graphql-aiohttp/docs/index.rst b/packages/plugins/minos-router-graphql/docs/index.rst
similarity index 100%
rename from packages/plugins/minos-graphql-aiohttp/docs/index.rst
rename to packages/plugins/minos-router-graphql/docs/index.rst
diff --git a/packages/plugins/minos-graphql-aiohttp/docs/make.bat b/packages/plugins/minos-router-graphql/docs/make.bat
similarity index 100%
rename from packages/plugins/minos-graphql-aiohttp/docs/make.bat
rename to packages/plugins/minos-router-graphql/docs/make.bat
diff --git a/packages/plugins/minos-graphql-aiohttp/docs/readme.rst b/packages/plugins/minos-router-graphql/docs/readme.rst
similarity index 100%
rename from packages/plugins/minos-graphql-aiohttp/docs/readme.rst
rename to packages/plugins/minos-router-graphql/docs/readme.rst
diff --git a/packages/plugins/minos-graphql-aiohttp/docs/runthetests.rst b/packages/plugins/minos-router-graphql/docs/runthetests.rst
similarity index 100%
rename from packages/plugins/minos-graphql-aiohttp/docs/runthetests.rst
rename to packages/plugins/minos-router-graphql/docs/runthetests.rst
diff --git a/packages/plugins/minos-graphql-aiohttp/docs/usage.rst b/packages/plugins/minos-router-graphql/docs/usage.rst
similarity index 100%
rename from packages/plugins/minos-graphql-aiohttp/docs/usage.rst
rename to packages/plugins/minos-router-graphql/docs/usage.rst
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql/__init__.py b/packages/plugins/minos-router-graphql/minos/plugins/graphql/__init__.py
similarity index 100%
rename from packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql/__init__.py
rename to packages/plugins/minos-router-graphql/minos/plugins/graphql/__init__.py
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql/decorators.py b/packages/plugins/minos-router-graphql/minos/plugins/graphql/decorators.py
similarity index 100%
rename from packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql/decorators.py
rename to packages/plugins/minos-router-graphql/minos/plugins/graphql/decorators.py
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql/exceptions.py b/packages/plugins/minos-router-graphql/minos/plugins/graphql/exceptions.py
similarity index 100%
rename from packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql/exceptions.py
rename to packages/plugins/minos-router-graphql/minos/plugins/graphql/exceptions.py
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql/handlers/__init__.py b/packages/plugins/minos-router-graphql/minos/plugins/graphql/handlers/__init__.py
similarity index 100%
rename from packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql/handlers/__init__.py
rename to packages/plugins/minos-router-graphql/minos/plugins/graphql/handlers/__init__.py
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql/handlers/abc.py b/packages/plugins/minos-router-graphql/minos/plugins/graphql/handlers/abc.py
similarity index 100%
rename from packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql/handlers/abc.py
rename to packages/plugins/minos-router-graphql/minos/plugins/graphql/handlers/abc.py
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql/handlers/graphiql.py b/packages/plugins/minos-router-graphql/minos/plugins/graphql/handlers/graphiql.py
similarity index 100%
rename from packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql/handlers/graphiql.py
rename to packages/plugins/minos-router-graphql/minos/plugins/graphql/handlers/graphiql.py
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql/handlers/graphql.py b/packages/plugins/minos-router-graphql/minos/plugins/graphql/handlers/graphql.py
similarity index 100%
rename from packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql/handlers/graphql.py
rename to packages/plugins/minos-router-graphql/minos/plugins/graphql/handlers/graphql.py
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql/main.py b/packages/plugins/minos-router-graphql/minos/plugins/graphql/main.py
similarity index 100%
rename from packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql/main.py
rename to packages/plugins/minos-router-graphql/minos/plugins/graphql/main.py
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql/routers.py b/packages/plugins/minos-router-graphql/minos/plugins/graphql/routers.py
similarity index 100%
rename from packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql/routers.py
rename to packages/plugins/minos-router-graphql/minos/plugins/graphql/routers.py
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql/services.py b/packages/plugins/minos-router-graphql/minos/plugins/graphql/services.py
similarity index 100%
rename from packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql/services.py
rename to packages/plugins/minos-router-graphql/minos/plugins/graphql/services.py
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql/star_wars_example/__init__.py b/packages/plugins/minos-router-graphql/minos/plugins/graphql/star_wars_example/__init__.py
similarity index 100%
rename from packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql/star_wars_example/__init__.py
rename to packages/plugins/minos-router-graphql/minos/plugins/graphql/star_wars_example/__init__.py
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql/star_wars_example/data.py b/packages/plugins/minos-router-graphql/minos/plugins/graphql/star_wars_example/data.py
similarity index 100%
rename from packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql/star_wars_example/data.py
rename to packages/plugins/minos-router-graphql/minos/plugins/graphql/star_wars_example/data.py
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql/star_wars_example/schema.py b/packages/plugins/minos-router-graphql/minos/plugins/graphql/star_wars_example/schema.py
similarity index 100%
rename from packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql/star_wars_example/schema.py
rename to packages/plugins/minos-router-graphql/minos/plugins/graphql/star_wars_example/schema.py
diff --git a/packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql/templates/graphiql.html b/packages/plugins/minos-router-graphql/minos/plugins/graphql/templates/graphiql.html
similarity index 100%
rename from packages/plugins/minos-graphql-aiohttp/minos/plugins/graphql/templates/graphiql.html
rename to packages/plugins/minos-router-graphql/minos/plugins/graphql/templates/graphiql.html
diff --git a/packages/plugins/minos-graphql-aiohttp/poetry.lock b/packages/plugins/minos-router-graphql/poetry.lock
similarity index 100%
rename from packages/plugins/minos-graphql-aiohttp/poetry.lock
rename to packages/plugins/minos-router-graphql/poetry.lock
diff --git a/packages/plugins/minos-graphql-aiohttp/poetry.toml b/packages/plugins/minos-router-graphql/poetry.toml
similarity index 100%
rename from packages/plugins/minos-graphql-aiohttp/poetry.toml
rename to packages/plugins/minos-router-graphql/poetry.toml
diff --git a/packages/plugins/minos-graphql-aiohttp/pyproject.toml b/packages/plugins/minos-router-graphql/pyproject.toml
similarity index 93%
rename from packages/plugins/minos-graphql-aiohttp/pyproject.toml
rename to packages/plugins/minos-router-graphql/pyproject.toml
index cce0f4fdf..2fd5a3fa3 100644
--- a/packages/plugins/minos-graphql-aiohttp/pyproject.toml
+++ b/packages/plugins/minos-router-graphql/pyproject.toml
@@ -1,7 +1,7 @@
[tool.poetry]
-name = "minos-graphql-aiohttp"
+name = "minos-router-graphql"
version = "0.6.0"
-description = "The rest plugin of the Minos Framework."
+description = "The graphql plugin of the Minos Framework."
readme = "README.md"
repository = "https://github.com/minos-framework/minos-python"
homepage = "http://www.minos.run/"
diff --git a/packages/plugins/minos-graphql-aiohttp/setup.cfg b/packages/plugins/minos-router-graphql/setup.cfg
similarity index 100%
rename from packages/plugins/minos-graphql-aiohttp/setup.cfg
rename to packages/plugins/minos-router-graphql/setup.cfg
diff --git a/packages/plugins/minos-graphql-aiohttp/tests/__init__.py b/packages/plugins/minos-router-graphql/tests/__init__.py
similarity index 100%
rename from packages/plugins/minos-graphql-aiohttp/tests/__init__.py
rename to packages/plugins/minos-router-graphql/tests/__init__.py
diff --git a/packages/plugins/minos-graphql-aiohttp/tests/services/commands.py b/packages/plugins/minos-router-graphql/tests/services/commands.py
similarity index 100%
rename from packages/plugins/minos-graphql-aiohttp/tests/services/commands.py
rename to packages/plugins/minos-router-graphql/tests/services/commands.py
diff --git a/packages/plugins/minos-graphql-aiohttp/tests/services/queries.py b/packages/plugins/minos-router-graphql/tests/services/queries.py
similarity index 100%
rename from packages/plugins/minos-graphql-aiohttp/tests/services/queries.py
rename to packages/plugins/minos-router-graphql/tests/services/queries.py
diff --git a/packages/plugins/minos-graphql-aiohttp/tests/test_config.yml b/packages/plugins/minos-router-graphql/tests/test_config.yml
similarity index 100%
rename from packages/plugins/minos-graphql-aiohttp/tests/test_config.yml
rename to packages/plugins/minos-router-graphql/tests/test_config.yml
diff --git a/packages/plugins/minos-graphql-aiohttp/tests/test_plugins/test_graphql_aiohttp/utils.py b/packages/plugins/minos-router-graphql/tests/test_plugins/test_graphql_aiohttp/utils.py
similarity index 100%
rename from packages/plugins/minos-graphql-aiohttp/tests/test_plugins/test_graphql_aiohttp/utils.py
rename to packages/plugins/minos-router-graphql/tests/test_plugins/test_graphql_aiohttp/utils.py
diff --git a/packages/plugins/minos-graphql-aiohttp/tests/utils.py b/packages/plugins/minos-router-graphql/tests/utils.py
similarity index 100%
rename from packages/plugins/minos-graphql-aiohttp/tests/utils.py
rename to packages/plugins/minos-router-graphql/tests/utils.py
From 2216e1985c1212af94144c69eef2d7d0cecfdc4e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Sergio=20Garc=C3=ADa=20Prado?=
Date: Fri, 11 Mar 2022 12:40:36 +0100
Subject: [PATCH 13/44] ISSUE #149 * Add test_something.
---
.../plugins/minos-router-graphql/poetry.lock | 50 ++++-----
.../tests/test_graphql/__init__.py | 0
.../tests/test_graphql/test_something.py | 15 +++
.../test_graphql_aiohttp/utils.py | 100 ------------------
.../minos-router-graphql/tests/utils.py | 31 ++++++
5 files changed, 71 insertions(+), 125 deletions(-)
create mode 100644 packages/plugins/minos-router-graphql/tests/test_graphql/__init__.py
create mode 100644 packages/plugins/minos-router-graphql/tests/test_graphql/test_something.py
delete mode 100644 packages/plugins/minos-router-graphql/tests/test_plugins/test_graphql_aiohttp/utils.py
diff --git a/packages/plugins/minos-router-graphql/poetry.lock b/packages/plugins/minos-router-graphql/poetry.lock
index d17b27ac0..836ad91c6 100644
--- a/packages/plugins/minos-router-graphql/poetry.lock
+++ b/packages/plugins/minos-router-graphql/poetry.lock
@@ -32,7 +32,7 @@ jinja2 = ">=3.0.0"
[[package]]
name = "aiomisc"
-version = "15.6.8"
+version = "15.7.2"
description = "aiomisc - miscellaneous utils for asyncio"
category = "main"
optional = false
@@ -270,7 +270,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
[[package]]
name = "fastavro"
-version = "1.4.9"
+version = "1.4.10"
description = "Fast read/write of AVRO files"
category = "main"
optional = false
@@ -440,7 +440,7 @@ python-versions = "*"
[[package]]
name = "minos-microservice-common"
-version = "0.5.2"
+version = "0.5.3"
description = "The common core of the Minos Framework"
category = "main"
optional = false
@@ -463,7 +463,7 @@ url = "../../core/minos-microservice-common"
[[package]]
name = "minos-microservice-networks"
-version = "0.5.2"
+version = "0.5.3"
description = "The networks core of the Minos Framework"
category = "main"
optional = false
@@ -896,7 +896,7 @@ socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
[[package]]
name = "virtualenv"
-version = "20.13.2"
+version = "20.13.3"
description = "Virtual Python Environment builder"
category = "dev"
optional = false
@@ -1021,8 +1021,8 @@ aiohttp-jinja2 = [
{file = "aiohttp_jinja2-1.5-py3-none-any.whl", hash = "sha256:b55c0ed167b0cc4b6d6a50fb2299a44beb5dc4aec9df21305b91a5484694cf74"},
]
aiomisc = [
- {file = "aiomisc-15.6.8-py3-none-any.whl", hash = "sha256:b65f7f8e50d9791bbb745253e814862880b8a35bf21e4eea545ada9daf89836c"},
- {file = "aiomisc-15.6.8.tar.gz", hash = "sha256:09024d1f544de98b152dad8f7eb7d02c64457aaaaf03bd6c76a20b73328a3a74"},
+ {file = "aiomisc-15.7.2-py3-none-any.whl", hash = "sha256:e0b00145866b5c258a23e2c270bee2910f2562b078f6159d786a522699e8cfeb"},
+ {file = "aiomisc-15.7.2.tar.gz", hash = "sha256:fcc4517fa49056ec11ecd3ec5d341c554914c0a2d295c127b70583bab1ecab5b"},
]
aiopg = [
{file = "aiopg-1.3.3-py3-none-any.whl", hash = "sha256:2842dd8741460eeef940032dcb577bfba4d4115205dd82a73ce13b3271f5bf0a"},
@@ -1198,22 +1198,22 @@ docutils = [
{file = "docutils-0.17.1.tar.gz", hash = "sha256:686577d2e4c32380bb50cbb22f575ed742d58168cee37e99117a854bcd88f125"},
]
fastavro = [
- {file = "fastavro-1.4.9-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:2e64a77c529b638e89a879ff0211debfab5b2d114c26a2af29c81f6b013f395a"},
- {file = "fastavro-1.4.9-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1fc9c95b7c1d59c5a2d29be21075870a122152cf927d84587dafc96da6b2ac3d"},
- {file = "fastavro-1.4.9-cp310-cp310-win_amd64.whl", hash = "sha256:927fd6148a8dd9646c129c0a0e8571aea829abc3cba04a3d5a4010a866934f4c"},
- {file = "fastavro-1.4.9-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:000b70c5109a61bdbfddeb2821a506de8f5333f243c608cbced61d44657d6c2f"},
- {file = "fastavro-1.4.9-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:37a77a1b5347a06416e236c77027c750aaeda29ef8189aa456eb2a2571274b43"},
- {file = "fastavro-1.4.9-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce2c7747ce958115388872db0756d3eeb0d796084eea9b46dc3758ef32c4d952"},
- {file = "fastavro-1.4.9-cp37-cp37m-win_amd64.whl", hash = "sha256:d6ccb77604903a0308316e696bb65a8943361af5f757d10985689656c9bce6ed"},
- {file = "fastavro-1.4.9-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:9a6ada2d6e133a2319438248c2e023b6735747b249c5a79d5f08f9d431e5d226"},
- {file = "fastavro-1.4.9-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7537e4df7782b03b9761e9338cef9fc7bfcc41100ab93c36c5c60fa568e724a"},
- {file = "fastavro-1.4.9-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6a9cd6d8471beb4020b4126fd04150ed7295f74ae7234d0dc9205b55c193851e"},
- {file = "fastavro-1.4.9-cp38-cp38-win_amd64.whl", hash = "sha256:fa9d8b47e0533c84152332ad491bb63bbae76a8a7a0df1caa821e0cbebf0fb70"},
- {file = "fastavro-1.4.9-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:3759bdc77009ee1e2e76fc9f58b951c05c00a8600ef9ddbff59fee3cb0c9e235"},
- {file = "fastavro-1.4.9-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b98ef2bdb123b95945aa6d69d6a7d79f211df3274b2dd7786da7852ddec964d0"},
- {file = "fastavro-1.4.9-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32b804aa6920d80c0e94e1180d480f28f56c4b901849bd80ed180851752b5ce6"},
- {file = "fastavro-1.4.9-cp39-cp39-win_amd64.whl", hash = "sha256:f9b04acaf06b16218b47985e92d8daa98c1116d58f3cff81a5b3cf39cef9afc0"},
- {file = "fastavro-1.4.9.tar.gz", hash = "sha256:be3fec387eb2cdc9627060b5ae0690542c687dddc951b63fa11203553769ae5e"},
+ {file = "fastavro-1.4.10-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:f225c81e869b3cefef6f2b478cd452693181ed7e645be3cea4d82024354ecaa0"},
+ {file = "fastavro-1.4.10-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b7669302c9124b7cd6c1bdff80c77116b2290c984814077fb7d01d7389586054"},
+ {file = "fastavro-1.4.10-cp310-cp310-win_amd64.whl", hash = "sha256:995525bdfbdfef205ea148b5bc6a9fe5ccf921931123c39d9aad75a2b661681e"},
+ {file = "fastavro-1.4.10-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:64244c53f1e4853184c2f7383d0332e1dcb34c38c05e6613530ade0378e8acfc"},
+ {file = "fastavro-1.4.10-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c60965da6dc7a91e00ccd84d84797fad746555f44e8a816c4cc460fb231c44fe"},
+ {file = "fastavro-1.4.10-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10eb25378f37dc00c10e83c4c0442c1a6d1b871f74a6dfdfc12b6447962bbdd0"},
+ {file = "fastavro-1.4.10-cp37-cp37m-win_amd64.whl", hash = "sha256:d5719adf6045fc743de5fa738d561a81e58dc782c94f1b16cb21b5dd6253e7fd"},
+ {file = "fastavro-1.4.10-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:24c4a1a8cc92e135ecfcd9cbd1f6cfa088cbc74d78c18e02a609cb11fa33778d"},
+ {file = "fastavro-1.4.10-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0934490b0c3bcfd6bcbacbcb5144c6b5e4298cda209fbb17c856adf5405127dd"},
+ {file = "fastavro-1.4.10-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a23763d73412c077aee401a0368c64cbc23859e26711dbae78a3cf0227f65165"},
+ {file = "fastavro-1.4.10-cp38-cp38-win_amd64.whl", hash = "sha256:09f1dfdd8192ae09e0f477d1f024d8054fccdb099ad495d2a796bcee3cadebd1"},
+ {file = "fastavro-1.4.10-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:0c6695753fa3035bbd0fa5cb21bf1b5dad39483c669b32ca0bb55fb07c1ccc87"},
+ {file = "fastavro-1.4.10-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:35f960dbba04647d8d6d5616f879622d2a1e8a84eb2d2e02a883a22e0803463a"},
+ {file = "fastavro-1.4.10-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9660878ca01e3dbbee12385c5902a2b6b12ecbb5af4733d1026175a14b1ef67f"},
+ {file = "fastavro-1.4.10-cp39-cp39-win_amd64.whl", hash = "sha256:64cbd386e408b3bcb2de53b1f847163073eb0d0d0338db65f76051c6ba9a9bc1"},
+ {file = "fastavro-1.4.10.tar.gz", hash = "sha256:a24f9dd803c44bfb599476b000f9bd0088f7ac2401e6c20818f38d8af12785a0"},
]
filelock = [
{file = "filelock-3.6.0-py3-none-any.whl", hash = "sha256:f8314284bfffbdcfa0ff3d7992b023d4c628ced6feb957351d4c48d059f56bc0"},
@@ -1718,8 +1718,8 @@ urllib3 = [
{file = "urllib3-1.26.8.tar.gz", hash = "sha256:0e7c33d9a63e7ddfcb86780aac87befc2fbddf46c58dbb487e0855f7ceec283c"},
]
virtualenv = [
- {file = "virtualenv-20.13.2-py2.py3-none-any.whl", hash = "sha256:e7b34c9474e6476ee208c43a4d9ac1510b041c68347eabfe9a9ea0c86aa0a46b"},
- {file = "virtualenv-20.13.2.tar.gz", hash = "sha256:01f5f80744d24a3743ce61858123488e91cb2dd1d3bdf92adaf1bba39ffdedf0"},
+ {file = "virtualenv-20.13.3-py2.py3-none-any.whl", hash = "sha256:dd448d1ded9f14d1a4bfa6bfc0c5b96ae3be3f2d6c6c159b23ddcfd701baa021"},
+ {file = "virtualenv-20.13.3.tar.gz", hash = "sha256:e9dd1a1359d70137559034c0f5433b34caf504af2dc756367be86a5a32967134"},
]
yarl = [
{file = "yarl-1.7.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f2a8508f7350512434e41065684076f640ecce176d262a7d54f0da41d99c5a95"},
diff --git a/packages/plugins/minos-router-graphql/tests/test_graphql/__init__.py b/packages/plugins/minos-router-graphql/tests/test_graphql/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/packages/plugins/minos-router-graphql/tests/test_graphql/test_something.py b/packages/plugins/minos-router-graphql/tests/test_graphql/test_something.py
new file mode 100644
index 000000000..41c4b4605
--- /dev/null
+++ b/packages/plugins/minos-router-graphql/tests/test_graphql/test_something.py
@@ -0,0 +1,15 @@
+import unittest
+
+from minos.plugins.graphql import (
+ GraphQlEnroute,
+)
+
+
+class TestSomething(unittest.TestCase):
+
+ def test_true(self):
+ self.assertTrue(GraphQlEnroute)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/packages/plugins/minos-router-graphql/tests/test_plugins/test_graphql_aiohttp/utils.py b/packages/plugins/minos-router-graphql/tests/test_plugins/test_graphql_aiohttp/utils.py
deleted file mode 100644
index 2cb3e5466..000000000
--- a/packages/plugins/minos-router-graphql/tests/test_plugins/test_graphql_aiohttp/utils.py
+++ /dev/null
@@ -1,100 +0,0 @@
-import json
-from typing import (
- Any,
- Optional,
- Union,
-)
-from unittest.mock import (
- AsyncMock,
-)
-from urllib.parse import (
- urlencode,
-)
-from uuid import (
- UUID,
-)
-
-from aiohttp import (
- test_utils,
- web,
-)
-from aiohttp.streams import (
- EmptyStreamReader,
-)
-from multidict import (
- MultiDict,
-)
-
-from minos.common import (
- MinosAvroProtocol,
-)
-
-
-def json_mocked_request(data: Any, **kwargs) -> web.Request:
- """For testng purposes."""
- return mocked_request(json.dumps(data).encode(), content_type="application/json", **kwargs)
-
-
-def form_mocked_request(data: dict[str, Any], **kwargs) -> web.Request:
- """For testng purposes."""
- return mocked_request(
- urlencode(data, doseq=True).encode(), content_type="application/x-www-form-urlencoded", **kwargs
- )
-
-
-def avro_mocked_request(data: Any, schema: Any, **kwargs) -> web.Request:
- """For testng purposes."""
- return mocked_request(MinosAvroProtocol.encode(data, schema), content_type="avro/binary", **kwargs)
-
-
-def text_mocked_request(data: str, **kwargs) -> web.Request:
- """For testng purposes."""
- return mocked_request(data.encode(), content_type="text/plain", **kwargs)
-
-
-def bytes_mocked_request(data: bytes, **kwargs) -> web.Request:
- """For testng purposes."""
- return mocked_request(data, content_type="application/octet-stream", **kwargs)
-
-
-def mocked_request(
- data: Optional[bytes] = None,
- headers: Optional[dict[str, str]] = None,
- user: Optional[Union[str, UUID]] = None,
- content_type: Optional[str] = None,
- method: str = "POST",
- path: str = "localhost",
- url_params: Optional[list[tuple[str, str]]] = None,
- query_params: Optional[list[tuple[str, str]]] = None,
-) -> web.Request:
- """For testing purposes"""
- if headers is None:
- headers = dict()
- else:
- headers = headers.copy()
-
- if user is not None:
- headers["user"] = str(user)
-
- if content_type is not None:
- headers["Content-Type"] = content_type
-
- kwargs = {
- "method": method,
- "path": path,
- "headers": headers,
- }
- if data is None:
- kwargs["payload"] = EmptyStreamReader()
-
- request = test_utils.make_mocked_request(**kwargs)
- request.read = AsyncMock(return_value=data)
-
- if url_params is not None:
- # noinspection PyProtectedMember
- request._rel_url = request._rel_url.with_query(url_params)
-
- if query_params is not None:
- request._match_info = MultiDict(map(lambda kv: (str(kv[0]), str(kv[1])), query_params))
-
- return request
diff --git a/packages/plugins/minos-router-graphql/tests/utils.py b/packages/plugins/minos-router-graphql/tests/utils.py
index d7424f6ce..51ecad270 100644
--- a/packages/plugins/minos-router-graphql/tests/utils.py
+++ b/packages/plugins/minos-router-graphql/tests/utils.py
@@ -11,6 +11,11 @@
from minos.common import (
DeclarativeModel,
)
+from minos.networks import (
+ Request,
+ Response,
+ enroute,
+)
BASE_PATH = Path(__file__).parent
CONFIG_FILE_PATH = BASE_PATH / "test_config.yml"
@@ -25,3 +30,29 @@ class FakeModel(DeclarativeModel):
def __lt__(self, other: Any) -> bool:
# noinspection PyBroadException
return isinstance(other, type(self)) and self.data < other.data
+
+
+class FakeCommandService:
+ """For testng purposes."""
+
+ # noinspection PyUnusedLocal
+ @enroute.rest.command(url="/order", method="GET")
+ def get_order_rest(self, request: Request) -> Response:
+ """For testng purposes."""
+
+ return Response("get_order")
+
+ @enroute.broker.command("GetOrder")
+ def get_order_command(self, request: Request) -> Response:
+ """For testng purposes."""
+ return Response("get_order_command")
+
+ @enroute.graphql.command(
+ "create_product",
+ # args={"request": GraphQLArgument(GraphQlObject({"name": GraphQLString, "surname": GraphQLString}))},
+ # response=GrapqlUUID
+ )
+ def get_hero(self, request: Request) -> Response:
+ """For testng purposes."""
+ uuid = request.content()
+ return Response(uuid)
From 9e749179ce85f29d25c670be6a6c8e0803bfbd8b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Sergio=20Garc=C3=ADa=20Prado?=
Date: Fri, 11 Mar 2022 12:42:16 +0100
Subject: [PATCH 14/44] ISSUE #149 * Revert commit.
---
.../minos/networks/__init__.py | 2 --
.../minos/networks/decorators/analyzers.py | 10 ----------
.../networks/decorators/definitions/kinds.py | 1 -
.../tests/services/graphql.py | 11 -----------
.../test_decorators/test_analyzers.py | 15 ---------------
.../test_networks/test_decorators/test_api.py | 4 ----
.../minos-microservice-networks/tests/utils.py | 12 ------------
7 files changed, 55 deletions(-)
delete mode 100644 packages/core/minos-microservice-networks/tests/services/graphql.py
diff --git a/packages/core/minos-microservice-networks/minos/networks/__init__.py b/packages/core/minos-microservice-networks/minos/networks/__init__.py
index 01abec743..4ac359614 100644
--- a/packages/core/minos-microservice-networks/minos/networks/__init__.py
+++ b/packages/core/minos-microservice-networks/minos/networks/__init__.py
@@ -55,8 +55,6 @@
EnrouteBuilder,
EnrouteDecorator,
EnrouteDecoratorKind,
- GraphqlEnrouteDecorator,
- GraphqlQueryEnrouteDecorator,
Handler,
HandlerMeta,
HandlerWrapper,
diff --git a/packages/core/minos-microservice-networks/minos/networks/decorators/analyzers.py b/packages/core/minos-microservice-networks/minos/networks/decorators/analyzers.py
index bfbcb8c18..a839e8e47 100644
--- a/packages/core/minos-microservice-networks/minos/networks/decorators/analyzers.py
+++ b/packages/core/minos-microservice-networks/minos/networks/decorators/analyzers.py
@@ -24,8 +24,6 @@
BrokerEventEnrouteDecorator,
BrokerQueryEnrouteDecorator,
EnrouteDecorator,
- GraphqlEnrouteDecorator,
- GraphqlQueryEnrouteDecorator,
PeriodicEventEnrouteDecorator,
RestCommandEnrouteDecorator,
RestEnrouteDecorator,
@@ -52,14 +50,6 @@ def get_rest_command_query(self) -> dict[str, set[RestEnrouteDecorator]]:
# noinspection PyTypeChecker
return self._get_items({RestCommandEnrouteDecorator, RestQueryEnrouteDecorator})
- def get_graphql(self) -> dict[str, set[GraphqlEnrouteDecorator]]:
- """Returns rest's command and query values.
-
- :return: A mapping with functions as keys and a sets of decorators as values.
- """
- # noinspection PyTypeChecker
- return self._get_items({GraphqlQueryEnrouteDecorator})
-
def get_broker_command_query_event(self) -> dict[str, set[BrokerEnrouteDecorator]]:
"""Returns broker's command, query and event values.
diff --git a/packages/core/minos-microservice-networks/minos/networks/decorators/definitions/kinds.py b/packages/core/minos-microservice-networks/minos/networks/decorators/definitions/kinds.py
index ea3bb3fab..513947c0a 100644
--- a/packages/core/minos-microservice-networks/minos/networks/decorators/definitions/kinds.py
+++ b/packages/core/minos-microservice-networks/minos/networks/decorators/definitions/kinds.py
@@ -10,7 +10,6 @@ class EnrouteDecoratorKind(Enum):
Command = auto()
Query = auto()
Event = auto()
- Graphql = auto()
@property
def pre_fn_name(self) -> str:
diff --git a/packages/core/minos-microservice-networks/tests/services/graphql.py b/packages/core/minos-microservice-networks/tests/services/graphql.py
deleted file mode 100644
index b4cc7d4f4..000000000
--- a/packages/core/minos-microservice-networks/tests/services/graphql.py
+++ /dev/null
@@ -1,11 +0,0 @@
-from minos.networks import (
- Request,
- Response,
- enroute,
-)
-
-
-class GraphqlService:
- @enroute.graphql(url="/graphql", method="POST")
- def add_ticket(self, request: Request) -> Response:
- return Response({"data": {}, "errors": []})
diff --git a/packages/core/minos-microservice-networks/tests/test_networks/test_decorators/test_analyzers.py b/packages/core/minos-microservice-networks/tests/test_networks/test_decorators/test_analyzers.py
index a8fe39b6e..ca600dbf3 100644
--- a/packages/core/minos-microservice-networks/tests/test_networks/test_decorators/test_analyzers.py
+++ b/packages/core/minos-microservice-networks/tests/test_networks/test_decorators/test_analyzers.py
@@ -42,9 +42,6 @@ def test_get_all(self):
},
"send_newsletter": {PeriodicEventEnrouteDecorator("@daily")},
"check_inactive_users": {PeriodicEventEnrouteDecorator("@daily")},
- "graphql_handler": {GraphqlQueryEnrouteDecorator("graphql/", "GET")},
- "graphql_handler2": {GraphqlQueryEnrouteDecorator("graphql-2/", "GET")},
- "graphql_handler3": {GraphqlQueryEnrouteDecorator("graphql-3/", "POST")},
}
self.assertEqual(expected, observed)
@@ -61,18 +58,6 @@ def test_get_rest_command_query(self):
self.assertEqual(expected, observed)
- def test_get_graphql(self):
- analyzer = EnrouteAnalyzer(FakeService)
-
- observed = analyzer.get_graphql()
- expected = {
- "graphql_handler": {GraphqlQueryEnrouteDecorator("graphql/", "GET")},
- "graphql_handler2": {GraphqlQueryEnrouteDecorator("graphql-2/", "GET")},
- "graphql_handler3": {GraphqlQueryEnrouteDecorator("graphql-3/", "POST")},
- }
-
- self.assertEqual(expected, observed)
-
def test_get_broker_command_query_event(self):
analyzer = EnrouteAnalyzer(FakeService)
diff --git a/packages/core/minos-microservice-networks/tests/test_networks/test_decorators/test_api.py b/packages/core/minos-microservice-networks/tests/test_networks/test_decorators/test_api.py
index 3d96d926d..a988d7cac 100644
--- a/packages/core/minos-microservice-networks/tests/test_networks/test_decorators/test_api.py
+++ b/packages/core/minos-microservice-networks/tests/test_networks/test_decorators/test_api.py
@@ -21,10 +21,6 @@ def test_rest_query(self):
decorator = enroute.rest.query(path="tickets/", method="GET")
self.assertEqual(RestQueryEnrouteDecorator("tickets/", "GET"), decorator)
- def test_graphql(self):
- decorator = enroute.graphql(url="graphql/", method="POST")
- self.assertEqual(GraphqlQueryEnrouteDecorator("graphql/", "POST"), decorator)
-
def test_rest_event_raises(self):
with self.assertRaises(AttributeError):
enroute.rest.event("CreateTicket")
diff --git a/packages/core/minos-microservice-networks/tests/utils.py b/packages/core/minos-microservice-networks/tests/utils.py
index 1c2d5b3bb..95e20aae3 100644
--- a/packages/core/minos-microservice-networks/tests/utils.py
+++ b/packages/core/minos-microservice-networks/tests/utils.py
@@ -174,18 +174,6 @@ def bar(self, request: Request):
"""For testing purposes."""
return Response("bar")
- @enroute.graphql(url="graphql/", method="GET")
- def graphql_handler(self):
- return
-
- @enroute.graphql(url="graphql-2/", method="GET")
- def graphql_handler2(self):
- return
-
- @enroute.graphql(url="graphql-3/", method="POST")
- def graphql_handler3(self):
- return
-
class FakeServiceWithGetEnroute:
@staticmethod
From 0687c2e8d50720450dd82a8d804673449ac358a8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Sergio=20Garc=C3=ADa=20Prado?=
Date: Fri, 11 Mar 2022 12:43:47 +0100
Subject: [PATCH 15/44] ISSUE #149 * Revert commit (2).
---
.../tests/test_networks/test_decorators/test_analyzers.py | 1 -
.../tests/test_networks/test_decorators/test_api.py | 1 -
2 files changed, 2 deletions(-)
diff --git a/packages/core/minos-microservice-networks/tests/test_networks/test_decorators/test_analyzers.py b/packages/core/minos-microservice-networks/tests/test_networks/test_decorators/test_analyzers.py
index ca600dbf3..ab1bfae50 100644
--- a/packages/core/minos-microservice-networks/tests/test_networks/test_decorators/test_analyzers.py
+++ b/packages/core/minos-microservice-networks/tests/test_networks/test_decorators/test_analyzers.py
@@ -8,7 +8,6 @@
BrokerEventEnrouteDecorator,
BrokerQueryEnrouteDecorator,
EnrouteAnalyzer,
- GraphqlQueryEnrouteDecorator,
PeriodicEventEnrouteDecorator,
RestCommandEnrouteDecorator,
RestQueryEnrouteDecorator,
diff --git a/packages/core/minos-microservice-networks/tests/test_networks/test_decorators/test_api.py b/packages/core/minos-microservice-networks/tests/test_networks/test_decorators/test_api.py
index a988d7cac..075c7c5f3 100644
--- a/packages/core/minos-microservice-networks/tests/test_networks/test_decorators/test_api.py
+++ b/packages/core/minos-microservice-networks/tests/test_networks/test_decorators/test_api.py
@@ -4,7 +4,6 @@
BrokerCommandEnrouteDecorator,
BrokerEventEnrouteDecorator,
BrokerQueryEnrouteDecorator,
- GraphqlQueryEnrouteDecorator,
PeriodicEventEnrouteDecorator,
RestCommandEnrouteDecorator,
RestQueryEnrouteDecorator,
From bb32bbdbbe2fbc2fb00f366a3f67921acdb5f852 Mon Sep 17 00:00:00 2001
From: Vladyslav Fenchak
Date: Mon, 14 Mar 2022 14:25:50 +0100
Subject: [PATCH 16/44] ISSUE #149
---
.../minos/plugins/graphql/__init__.py | 6 +++
.../minos/plugins/graphql/decorators.py | 19 ++++++--
.../minos/plugins/graphql/main.py | 2 +-
.../minos/plugins/graphql/routers.py | 42 ++++++------------
.../minos/plugins/graphql/schema_builder.py | 44 +++++++++++++++++++
.../tests/services/commands.py | 33 ++++----------
.../tests/services/queries.py | 19 ++++----
.../tests/test_graphql/test_decorators.py | 39 ++++++++++++++++
.../tests/test_graphql/test_routers.py | 27 ++++++++++++
.../tests/test_graphql/test_schema_builder.py | 19 ++++++++
.../tests/test_graphql/test_something.py | 15 -------
.../minos-router-graphql/tests/utils.py | 31 ++++++++-----
12 files changed, 204 insertions(+), 92 deletions(-)
create mode 100644 packages/plugins/minos-router-graphql/minos/plugins/graphql/schema_builder.py
create mode 100644 packages/plugins/minos-router-graphql/tests/test_graphql/test_decorators.py
create mode 100644 packages/plugins/minos-router-graphql/tests/test_graphql/test_routers.py
create mode 100644 packages/plugins/minos-router-graphql/tests/test_graphql/test_schema_builder.py
delete mode 100644 packages/plugins/minos-router-graphql/tests/test_graphql/test_something.py
diff --git a/packages/plugins/minos-router-graphql/minos/plugins/graphql/__init__.py b/packages/plugins/minos-router-graphql/minos/plugins/graphql/__init__.py
index af4f2bf06..3ffd178ce 100644
--- a/packages/plugins/minos-router-graphql/minos/plugins/graphql/__init__.py
+++ b/packages/plugins/minos-router-graphql/minos/plugins/graphql/__init__.py
@@ -5,6 +5,12 @@
from .decorators import (
GraphQlEnroute,
)
+from .routers import (
+ GraphQlHttpRouter,
+)
+from .schema_builder import (
+ GraphQLSchemaBuilder,
+)
def _register_enroute():
diff --git a/packages/plugins/minos-router-graphql/minos/plugins/graphql/decorators.py b/packages/plugins/minos-router-graphql/minos/plugins/graphql/decorators.py
index ff512ed4e..8c806873d 100644
--- a/packages/plugins/minos-router-graphql/minos/plugins/graphql/decorators.py
+++ b/packages/plugins/minos-router-graphql/minos/plugins/graphql/decorators.py
@@ -13,16 +13,29 @@
EnrouteDecoratorKind,
enroute,
)
+from collections.abc import (
+ Collection,
+)
+from collections import (
+ defaultdict,
+)
class GraphQlEnrouteDecorator(EnrouteDecorator, ABC):
"""GraphQl Enroute class"""
- def __init__(self, topic: str):
- self.topic = topic
+ def __init__(self, name: str, argument, output):
+ self.name = name
+ self.argument = argument
+ self.output = output
def __iter__(self) -> Iterable:
- yield from (self.topic,)
+ yield from (self.name,)
+
+ def _validate_not_redefined(self, decorators: Collection[EnrouteDecorator]) -> None:
+ mapper = defaultdict(set)
+ for decorator in decorators:
+ mapper[tuple(decorator)].add(decorator)
class GraphQlCommandEnrouteDecorator(GraphQlEnrouteDecorator):
diff --git a/packages/plugins/minos-router-graphql/minos/plugins/graphql/main.py b/packages/plugins/minos-router-graphql/minos/plugins/graphql/main.py
index b751c179b..36c8eb2cf 100644
--- a/packages/plugins/minos-router-graphql/minos/plugins/graphql/main.py
+++ b/packages/plugins/minos-router-graphql/minos/plugins/graphql/main.py
@@ -2,7 +2,7 @@
web,
)
-from minos.plugins.graphql_aiohttp.services import (
+from minos.plugins.graphql.services import (
RestService,
)
diff --git a/packages/plugins/minos-router-graphql/minos/plugins/graphql/routers.py b/packages/plugins/minos-router-graphql/minos/plugins/graphql/routers.py
index 4eee7eee7..0110ebb1a 100644
--- a/packages/plugins/minos-router-graphql/minos/plugins/graphql/routers.py
+++ b/packages/plugins/minos-router-graphql/minos/plugins/graphql/routers.py
@@ -6,6 +6,7 @@
Callable,
)
+from graphql import GraphQLSchema
from minos.common import (
MinosConfig,
)
@@ -15,47 +16,32 @@
HttpRequest,
HttpResponse,
HttpRouter,
-)
-
-from .decorators import (
- GraphQlEnrouteDecorator,
+ EnrouteDecoratorKind,
)
class GraphQlHttpRouter(HttpRouter):
"""TODO"""
- def __init__(self, schema, *args, **kwargs):
+ def __init__(self, schema: GraphQLSchema, *args, **kwargs):
super().__init__(*args, **kwargs)
self._schema = schema
- @staticmethod
- def _from_config(config: MinosConfig, **kwargs) -> dict[(str, str), Callable]:
+ @classmethod
+ def _from_config(cls, config: MinosConfig, **kwargs) -> GraphQlHttpRouter:
builder = EnrouteBuilder(*config.services, middleware=config.middleware)
routes = builder.get_all(config=config, **kwargs)
- graphql_routes = {
- GraphQlEnrouteDecorator(
- "CreateProduct", request=GraphQLField(GraphQLObjectType), response=GraphQLField(GraphQLObjectType)
- ): "fn1",
- GraphQlEnrouteDecorator(
- "UpdateProduct", request=GraphQLField(GraphQLObjectType), response=GraphQLField(GraphQLObjectType)
- ): "fn2",
- }
+ queries = []
+ mutations = []
+ for route in routes:
+ if route.KIND == EnrouteDecoratorKind.Query:
+ queries.append(route)
- schema = GraphQLSchema(
- query=GraphQLObjectType(
- name="RootQueryType",
- fields={
- "CreateProduct": GraphQLField(
- GraphQLString, args=GraphQLField(GraphQLObjectType), response=GraphQLField(GraphQLObjectType)
- ),
- "UpdateProduct": GraphQLField(
- GraphQLString, args=GraphQLField(GraphQLObjectType), response=GraphQLField(GraphQLObjectType)
- ),
- },
- )
- )
+ if route.KIND == EnrouteDecoratorKind.Command:
+ mutations.append(route)
+ from minos.plugins.graphql import GraphQLSchemaBuilder
+ schema = GraphQLSchemaBuilder.build(queries=queries, mutations=mutations)
return cls(schema)
diff --git a/packages/plugins/minos-router-graphql/minos/plugins/graphql/schema_builder.py b/packages/plugins/minos-router-graphql/minos/plugins/graphql/schema_builder.py
new file mode 100644
index 000000000..5a5f5962b
--- /dev/null
+++ b/packages/plugins/minos-router-graphql/minos/plugins/graphql/schema_builder.py
@@ -0,0 +1,44 @@
+from __future__ import (
+ annotations,
+)
+
+from graphql import GraphQLSchema, GraphQLObjectType, GraphQLField
+
+from minos.plugins.graphql.decorators import (
+ GraphQlQueryEnrouteDecorator,
+ GraphQlCommandEnrouteDecorator,
+)
+
+
+class GraphQLSchemaBuilder:
+ def __init__(self, *args, **kwargs):
+ self.schema = GraphQLSchema(**kwargs)
+
+ @classmethod
+ def build(cls, queries: list, mutations: list) -> GraphQLSchema:
+ params = dict()
+ if len(queries) > 0:
+ params["query"] = GraphQLSchemaBuilder._build_queries(queries)
+ if len(mutations) > 0:
+ params["mutation"] = GraphQLSchemaBuilder._build_mutations(mutations)
+ return cls(**params).schema
+
+ @classmethod
+ def _build_queries(cls, queries):
+ fields = {item.name: cls._build_query(item) for item in queries}
+
+ return GraphQLObjectType("Query", fields=fields)
+
+ @staticmethod
+ def _build_query(item: GraphQlQueryEnrouteDecorator):
+ return {item.name: GraphQLField(item.argument, resolve=item)}
+
+ @classmethod
+ def _build_mutations(cls, mutations):
+ fields = {item.name: cls._build_mutation(item) for item in mutations}
+
+ return GraphQLObjectType("Mutation", fields=fields)
+
+ @staticmethod
+ def _build_mutation(item: GraphQlCommandEnrouteDecorator):
+ return {item.name: GraphQLField(item.argument, resolve=item)}
diff --git a/packages/plugins/minos-router-graphql/tests/services/commands.py b/packages/plugins/minos-router-graphql/tests/services/commands.py
index 7f9713073..71f2a2943 100644
--- a/packages/plugins/minos-router-graphql/tests/services/commands.py
+++ b/packages/plugins/minos-router-graphql/tests/services/commands.py
@@ -1,3 +1,4 @@
+from graphql import GraphQLField, GraphQLString
from minos.networks import (
BrokerResponse,
Request,
@@ -7,30 +8,14 @@
class CommandService:
- @enroute.rest.command(url="/order", method="GET")
- def get_order_rest(self, request: Request) -> Response:
- return Response("get_order")
+ @enroute.graphql.command(name="order-command", argument=GraphQLField(GraphQLString), output=GraphQLString)
+ def create_order(self, request: Request):
+ """For testng purposes."""
- @enroute.broker.command("GetOrder")
- def get_order_command(self, request: Request) -> Response:
- return BrokerResponse("get_order")
+ return "eu38hj32-889283-j2jjb5kl"
- @enroute.broker.command("AddOrder")
- def add_order(self, request: Request) -> Response:
- return BrokerResponse("add_order")
+ @enroute.graphql.command(name="ticket-command", argument=GraphQLField(GraphQLString), output=GraphQLString)
+ def create_ticket(self, request: Request):
+ """For testng purposes."""
- @enroute.broker.command("DeleteOrder")
- def delete_order(self, request: Request) -> Response:
- return BrokerResponse("delete_order")
-
- @enroute.broker.command("UpdateOrder")
- def update_order(self, request: Request) -> Response:
- return BrokerResponse("update_order")
-
- @enroute.broker.event("TicketAdded")
- def ticket_added(self, request: Request) -> str:
- return "command_service_ticket_added"
-
- @enroute.periodic.event("@daily")
- def recompute_something(self, request: Request) -> None:
- """For testing purposes."""
+ return "zdw4gg4g-gser44gkl-jh4j3h4h"
diff --git a/packages/plugins/minos-router-graphql/tests/services/queries.py b/packages/plugins/minos-router-graphql/tests/services/queries.py
index 726f70b12..54b94e2a0 100644
--- a/packages/plugins/minos-router-graphql/tests/services/queries.py
+++ b/packages/plugins/minos-router-graphql/tests/services/queries.py
@@ -1,3 +1,4 @@
+from graphql import GraphQLField, GraphQLString
from minos.networks import (
Request,
Response,
@@ -6,14 +7,14 @@
class QueryService:
- @enroute.rest.query(url="/ticket", method="POST")
- def add_ticket(self, request: Request) -> Response:
- return Response("ticket_added")
+ @enroute.graphql.query(name="order-query", argument=GraphQLField(GraphQLString), output=GraphQLString)
+ def get_order(self, request: Request):
+ """For testng purposes."""
- @enroute.broker.event("TicketAdded")
- def ticket_added(self, request: Request):
- return "query_service_ticket_added"
+ return "eu38hj32-889283-j2jjb5kl"
- @enroute.broker.event("TicketDeleted")
- def ticket_deleted(self, request: Request):
- return "ticket_deleted"
+ @enroute.graphql.query(name="ticket-query", argument=GraphQLField(GraphQLString), output=GraphQLString)
+ def get_ticket(self, request: Request):
+ """For testng purposes."""
+
+ return "zdw4gg4g-gser44gkl-jh4j3h4h"
diff --git a/packages/plugins/minos-router-graphql/tests/test_graphql/test_decorators.py b/packages/plugins/minos-router-graphql/tests/test_graphql/test_decorators.py
new file mode 100644
index 000000000..1568a6597
--- /dev/null
+++ b/packages/plugins/minos-router-graphql/tests/test_graphql/test_decorators.py
@@ -0,0 +1,39 @@
+import unittest
+
+from graphql import GraphQLField, GraphQLString
+from minos.networks import (
+ EnrouteAnalyzer,
+)
+from minos.common import (
+ classname,
+)
+
+from minos.plugins.graphql.decorators import GraphQlQueryEnrouteDecorator
+from tests.utils import (
+ FakeCommandService,
+ FakeQueryService,
+)
+from minos.plugins.graphql import (
+ GraphQlEnroute,
+)
+
+
+class TestSomething(unittest.TestCase):
+
+ def test_decorated_str(self):
+ analyzer = EnrouteAnalyzer(classname(FakeQueryService))
+ self.assertEqual(FakeQueryService, analyzer.decorated)
+
+ def test_get_all_queries(self):
+ analyzer = EnrouteAnalyzer(FakeQueryService)
+ observed = analyzer.get_all()
+
+ expected = {
+ "get_order": {GraphQlQueryEnrouteDecorator(name="order", argument=GraphQLField(GraphQLString), output=GraphQLString)},
+ }
+
+ self.assertEqual(expected, observed)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/packages/plugins/minos-router-graphql/tests/test_graphql/test_routers.py b/packages/plugins/minos-router-graphql/tests/test_graphql/test_routers.py
new file mode 100644
index 000000000..4674c92a9
--- /dev/null
+++ b/packages/plugins/minos-router-graphql/tests/test_graphql/test_routers.py
@@ -0,0 +1,27 @@
+import unittest
+
+from minos.common import MinosConfig
+
+from minos.plugins.graphql import (
+ GraphQlEnroute,
+ GraphQlHttpRouter,
+)
+from tests.utils import (
+ BASE_PATH,
+)
+from minos.common.testing import (
+ PostgresAsyncTestCase,
+)
+
+
+class TestSomething(unittest.TestCase):
+ CONFIG_FILE_PATH = BASE_PATH / "test_config.yml"
+ _config = MinosConfig(CONFIG_FILE_PATH)
+
+ def test_true(self):
+ router = GraphQlHttpRouter.from_config(config=self._config)
+ self.assertTrue(GraphQlEnroute)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/packages/plugins/minos-router-graphql/tests/test_graphql/test_schema_builder.py b/packages/plugins/minos-router-graphql/tests/test_graphql/test_schema_builder.py
new file mode 100644
index 000000000..bc9d7fc9a
--- /dev/null
+++ b/packages/plugins/minos-router-graphql/tests/test_graphql/test_schema_builder.py
@@ -0,0 +1,19 @@
+import unittest
+
+from minos.plugins.graphql import (
+ GraphQlEnroute, GraphQLSchemaBuilder,
+)
+
+
+class TestSomething(unittest.TestCase):
+
+ def test_true(self):
+ mutations = [GraphQlCommandEnrouteDecorator('order-command'), GraphQlCommandEnrouteDecorator('ticket-command')]
+ queries = [GraphQlQueryEnrouteDecorator('order-query'), GraphQlQueryEnrouteDecorator('ticket-query')]
+
+ schema = GraphQLSchemaBuilder.build(queries=queries, mutations=mutations)
+ self.assertTrue(GraphQlEnroute)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/packages/plugins/minos-router-graphql/tests/test_graphql/test_something.py b/packages/plugins/minos-router-graphql/tests/test_graphql/test_something.py
deleted file mode 100644
index 41c4b4605..000000000
--- a/packages/plugins/minos-router-graphql/tests/test_graphql/test_something.py
+++ /dev/null
@@ -1,15 +0,0 @@
-import unittest
-
-from minos.plugins.graphql import (
- GraphQlEnroute,
-)
-
-
-class TestSomething(unittest.TestCase):
-
- def test_true(self):
- self.assertTrue(GraphQlEnroute)
-
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/packages/plugins/minos-router-graphql/tests/utils.py b/packages/plugins/minos-router-graphql/tests/utils.py
index 51ecad270..d2d98e20f 100644
--- a/packages/plugins/minos-router-graphql/tests/utils.py
+++ b/packages/plugins/minos-router-graphql/tests/utils.py
@@ -8,6 +8,7 @@
Any,
)
+from graphql import GraphQLObjectType, GraphQLField, GraphQLString, GraphQLArgument
from minos.common import (
DeclarativeModel,
)
@@ -35,24 +36,30 @@ def __lt__(self, other: Any) -> bool:
class FakeCommandService:
"""For testng purposes."""
- # noinspection PyUnusedLocal
- @enroute.rest.command(url="/order", method="GET")
- def get_order_rest(self, request: Request) -> Response:
- """For testng purposes."""
-
- return Response("get_order")
-
- @enroute.broker.command("GetOrder")
+ #@enroute.broker.command("GetOrder")
def get_order_command(self, request: Request) -> Response:
"""For testng purposes."""
return Response("get_order_command")
-
+ """
@enroute.graphql.command(
- "create_product",
- # args={"request": GraphQLArgument(GraphQlObject({"name": GraphQLString, "surname": GraphQLString}))},
- # response=GrapqlUUID
+ args={"request": GraphQLArgument(GraphQlObject({"name": GraphQLString, "surname": GraphQLString}))},
+ response=GrapqlUUID
)
+ """
def get_hero(self, request: Request) -> Response:
"""For testng purposes."""
uuid = request.content()
return Response(uuid)
+
+
+
+class FakeQueryService:
+ """For testng purposes."""
+
+ # noinspection PyUnusedLocal
+ @enroute.graphql.query(name="order", argument=GraphQLField(GraphQLString), output=GraphQLString)
+ def get_order(self, request: Request):
+ """For testng purposes."""
+
+ return "eu38hj32-889283-j2jjb5kl"
+
From 018077cc52512d40bb2380d016e210515c53514d Mon Sep 17 00:00:00 2001
From: Vladyslav Fenchak
Date: Mon, 14 Mar 2022 20:40:58 +0100
Subject: [PATCH 17/44] ISSUE #149
---
.../minos/plugins/graphql/decorators.py | 15 +-
.../minos/plugins/graphql/exceptions.py | 7 -
.../plugins/graphql/handlers/__init__.py | 6 -
.../minos/plugins/graphql/handlers/abc.py | 100 --------
.../plugins/graphql/handlers/graphiql.py | 11 -
.../minos/plugins/graphql/handlers/graphql.py | 17 --
.../minos/plugins/graphql/main.py | 15 --
.../minos/plugins/graphql/routers.py | 69 +++--
.../minos/plugins/graphql/schema_builder.py | 84 ++++--
.../minos/plugins/graphql/services.py | 56 ----
.../graphql/star_wars_example/__init__.py | 7 -
.../plugins/graphql/star_wars_example/data.py | 151 -----------
.../graphql/star_wars_example/schema.py | 241 ------------------
.../plugins/graphql/templates/graphiql.html | 139 ----------
.../tests/services/commands.py | 10 +-
.../tests/services/queries.py | 14 +-
.../tests/test_graphql/test_decorators.py | 28 +-
.../tests/test_graphql/test_routers.py | 19 +-
.../tests/test_graphql/test_schema_builder.py | 89 ++++++-
.../minos-router-graphql/tests/utils.py | 14 +-
20 files changed, 255 insertions(+), 837 deletions(-)
delete mode 100644 packages/plugins/minos-router-graphql/minos/plugins/graphql/exceptions.py
delete mode 100644 packages/plugins/minos-router-graphql/minos/plugins/graphql/handlers/__init__.py
delete mode 100644 packages/plugins/minos-router-graphql/minos/plugins/graphql/handlers/abc.py
delete mode 100644 packages/plugins/minos-router-graphql/minos/plugins/graphql/handlers/graphiql.py
delete mode 100644 packages/plugins/minos-router-graphql/minos/plugins/graphql/handlers/graphql.py
delete mode 100644 packages/plugins/minos-router-graphql/minos/plugins/graphql/main.py
delete mode 100644 packages/plugins/minos-router-graphql/minos/plugins/graphql/services.py
delete mode 100644 packages/plugins/minos-router-graphql/minos/plugins/graphql/star_wars_example/__init__.py
delete mode 100644 packages/plugins/minos-router-graphql/minos/plugins/graphql/star_wars_example/data.py
delete mode 100644 packages/plugins/minos-router-graphql/minos/plugins/graphql/star_wars_example/schema.py
delete mode 100644 packages/plugins/minos-router-graphql/minos/plugins/graphql/templates/graphiql.html
diff --git a/packages/plugins/minos-router-graphql/minos/plugins/graphql/decorators.py b/packages/plugins/minos-router-graphql/minos/plugins/graphql/decorators.py
index 8c806873d..c0243708a 100644
--- a/packages/plugins/minos-router-graphql/minos/plugins/graphql/decorators.py
+++ b/packages/plugins/minos-router-graphql/minos/plugins/graphql/decorators.py
@@ -1,7 +1,11 @@
from abc import (
ABC,
)
+from collections import (
+ defaultdict,
+)
from collections.abc import (
+ Collection,
Iterable,
)
from typing import (
@@ -13,12 +17,6 @@
EnrouteDecoratorKind,
enroute,
)
-from collections.abc import (
- Collection,
-)
-from collections import (
- defaultdict,
-)
class GraphQlEnrouteDecorator(EnrouteDecorator, ABC):
@@ -32,11 +30,6 @@ def __init__(self, name: str, argument, output):
def __iter__(self) -> Iterable:
yield from (self.name,)
- def _validate_not_redefined(self, decorators: Collection[EnrouteDecorator]) -> None:
- mapper = defaultdict(set)
- for decorator in decorators:
- mapper[tuple(decorator)].add(decorator)
-
class GraphQlCommandEnrouteDecorator(GraphQlEnrouteDecorator):
"""GraphQl Command Enroute class"""
diff --git a/packages/plugins/minos-router-graphql/minos/plugins/graphql/exceptions.py b/packages/plugins/minos-router-graphql/minos/plugins/graphql/exceptions.py
deleted file mode 100644
index e32f39495..000000000
--- a/packages/plugins/minos-router-graphql/minos/plugins/graphql/exceptions.py
+++ /dev/null
@@ -1,7 +0,0 @@
-from minos.networks import (
- ResponseException,
-)
-
-
-class RestResponseException(ResponseException):
- """Rest Response Exception class."""
diff --git a/packages/plugins/minos-router-graphql/minos/plugins/graphql/handlers/__init__.py b/packages/plugins/minos-router-graphql/minos/plugins/graphql/handlers/__init__.py
deleted file mode 100644
index 6a3937e81..000000000
--- a/packages/plugins/minos-router-graphql/minos/plugins/graphql/handlers/__init__.py
+++ /dev/null
@@ -1,6 +0,0 @@
-from .graphiql import (
- GraphiqlHandler,
-)
-from .graphql import (
- GraphqlHandler,
-)
diff --git a/packages/plugins/minos-router-graphql/minos/plugins/graphql/handlers/abc.py b/packages/plugins/minos-router-graphql/minos/plugins/graphql/handlers/abc.py
deleted file mode 100644
index 18dd60455..000000000
--- a/packages/plugins/minos-router-graphql/minos/plugins/graphql/handlers/abc.py
+++ /dev/null
@@ -1,100 +0,0 @@
-import json
-import logging
-import urllib
-
-import aiohttp
-from aiohttp import (
- web,
-)
-from graphql import (
- graphql,
-)
-
-from minos.plugins.graphql_aiohttp.star_wars_example import (
- star_wars_schema,
-)
-
-
-class BaseHandler(web.View):
- @property
- def fetch(self):
- return self.app.fetch
-
- @property
- def app(self):
- return self.request.app
-
- async def options(self):
- return web.Response(
- status=204,
- headers={
- "Access-Control-Allow-Origin": "*",
- "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, PATCH",
- "Access-Control-Allow-Headers": "x-requested-with,access-control-allow-origin,authorization,content-type",
- },
- )
-
-
-class GQLBaseHandler(BaseHandler):
- async def get(self):
- return await self.post()
-
- async def post(self):
- return await self.handle_graqhql()
-
- async def handle_graqhql(self):
- status = 200
- result = await self.execute_graphql()
- logging.debug(
- "GraphQL result data: %s errors: %s",
- result.data,
- result.errors,
- )
-
- if result and result.errors:
- status = 500
-
- errors = result.errors
-
- if errors is None:
- errors = []
-
- return web.json_response(
- {"data": result.data, "errors": [err.message for err in errors]},
- status=status,
- headers={"Access-Control-Allow-Origin": "*"},
- )
-
- async def execute_graphql(self):
- graphql_req = await self.graphql_request
- context_value = graphql_req.get("context", {})
- variables = graphql_req.get("variables", {})
-
- context_value["application"] = self.app
- source = graphql_req["query"]
- result = await graphql(
- schema=star_wars_schema,
- source=source,
- context_value=context_value,
- variable_values=variables,
- )
-
- ref = self.request.headers.get("referer")
- url_path = ""
-
- if ref:
- url = urllib.parse.urlparse(ref)
- url_path = url.path
-
- if result.errors:
- if "/graphiql" not in url_path:
- aiohttp.log.server_logger.error(f'Graphql query error: for query "{graphql_req}"')
-
- return result
-
- @property
- async def graphql_request(self):
- if self.request.method == "GET":
- return json.loads(self.request.query["q"])
-
- return await self.request.json()
diff --git a/packages/plugins/minos-router-graphql/minos/plugins/graphql/handlers/graphiql.py b/packages/plugins/minos-router-graphql/minos/plugins/graphql/handlers/graphiql.py
deleted file mode 100644
index 3cf938e43..000000000
--- a/packages/plugins/minos-router-graphql/minos/plugins/graphql/handlers/graphiql.py
+++ /dev/null
@@ -1,11 +0,0 @@
-import aiohttp_jinja2
-
-from .abc import (
- BaseHandler,
-)
-
-
-class GraphiqlHandler(BaseHandler):
- @aiohttp_jinja2.template("graphiql.html")
- async def get(self):
- return {"base_url": f"/graphql"}
diff --git a/packages/plugins/minos-router-graphql/minos/plugins/graphql/handlers/graphql.py b/packages/plugins/minos-router-graphql/minos/plugins/graphql/handlers/graphql.py
deleted file mode 100644
index 2e747a72e..000000000
--- a/packages/plugins/minos-router-graphql/minos/plugins/graphql/handlers/graphql.py
+++ /dev/null
@@ -1,17 +0,0 @@
-from graphql.utilities import (
- print_schema,
-)
-
-from minos.plugins.graphql_aiohttp.star_wars_example import (
- star_wars_schema,
-)
-
-from .abc import (
- GQLBaseHandler,
-)
-
-
-class GraphqlHandler(GQLBaseHandler):
- @property
- def schema(self):
- return print_schema(star_wars_schema)
diff --git a/packages/plugins/minos-router-graphql/minos/plugins/graphql/main.py b/packages/plugins/minos-router-graphql/minos/plugins/graphql/main.py
deleted file mode 100644
index 36c8eb2cf..000000000
--- a/packages/plugins/minos-router-graphql/minos/plugins/graphql/main.py
+++ /dev/null
@@ -1,15 +0,0 @@
-from aiohttp import (
- web,
-)
-
-from minos.plugins.graphql.services import (
- RestService,
-)
-
-
-def main():
- web.run_app(RestService().create_application())
-
-
-if __name__ == "__main__":
- main()
diff --git a/packages/plugins/minos-router-graphql/minos/plugins/graphql/routers.py b/packages/plugins/minos-router-graphql/minos/plugins/graphql/routers.py
index 0110ebb1a..329dfb8e5 100644
--- a/packages/plugins/minos-router-graphql/minos/plugins/graphql/routers.py
+++ b/packages/plugins/minos-router-graphql/minos/plugins/graphql/routers.py
@@ -2,11 +2,17 @@
annotations,
)
+import json
from typing import (
Callable,
)
-from graphql import GraphQLSchema
+from graphql import (
+ GraphQLSchema,
+ graphql,
+ print_schema,
+)
+
from minos.common import (
MinosConfig,
)
@@ -16,7 +22,6 @@
HttpRequest,
HttpResponse,
HttpRouter,
- EnrouteDecoratorKind,
)
@@ -32,16 +37,11 @@ def _from_config(cls, config: MinosConfig, **kwargs) -> GraphQlHttpRouter:
builder = EnrouteBuilder(*config.services, middleware=config.middleware)
routes = builder.get_all(config=config, **kwargs)
- queries = []
- mutations = []
- for route in routes:
- if route.KIND == EnrouteDecoratorKind.Query:
- queries.append(route)
+ from minos.plugins.graphql import (
+ GraphQLSchemaBuilder,
+ )
- if route.KIND == EnrouteDecoratorKind.Command:
- mutations.append(route)
- from minos.plugins.graphql import GraphQLSchemaBuilder
- schema = GraphQLSchemaBuilder.build(queries=queries, mutations=mutations)
+ schema = GraphQLSchemaBuilder.build(routes)
return cls(schema)
@@ -50,18 +50,51 @@ def routes(self) -> dict[HttpEnrouteDecorator, Callable]:
"""TODO"""
return {
HttpEnrouteDecorator("/graphql", "POST"): self.handle_post,
- HttpEnrouteDecorator("/graphql", "GET"): self.handle_get,
+ HttpEnrouteDecorator("/graphql/schema", "GET"): self.handle_schema_get,
}
async def handle_post(self, request: HttpRequest) -> HttpResponse:
"""TODO"""
- graphql_query = await request.content()
+ status = 200
+ result = await self.execute_graphql()
+
+ if result and result.errors:
+ status = 500
+
+ errors = result.errors
+
+ if errors is None:
+ errors = []
- content = await self._schema(graphql_query)
+ return HttpResponse(
+ {"data": result.data, "errors": [err.message for err in errors]},
+ status=status,
+ headers={"Access-Control-Allow-Origin": "*"},
+ )
+
+ async def execute_graphql(self):
+ graphql_req = await self.graphql_request
+ context_value = graphql_req.get("context", {})
+ variables = graphql_req.get("variables", {})
+
+ # context_value["application"] = self.app
+ source = graphql_req["query"]
+ result = await graphql(
+ schema=self._schema,
+ source=source,
+ context_value=context_value,
+ variable_values=variables,
+ )
+
+ return result
+
+ @property
+ async def graphql_request(self):
+ if self.request.method == "GET":
+ return json.loads(self.request.query["q"])
- return HttpResponse(content)
+ return await self.request.json()
- async def handle_get(self, request: HttpRequest) -> HttpResponse:
+ async def handle_schema_get(self, request: HttpRequest) -> HttpResponse:
"""TODO"""
- content = self._schema.json()
- return HttpResponse(content)
+ return HttpResponse(print_schema(self._schema))
diff --git a/packages/plugins/minos-router-graphql/minos/plugins/graphql/schema_builder.py b/packages/plugins/minos-router-graphql/minos/plugins/graphql/schema_builder.py
index 5a5f5962b..57be9dd00 100644
--- a/packages/plugins/minos-router-graphql/minos/plugins/graphql/schema_builder.py
+++ b/packages/plugins/minos-router-graphql/minos/plugins/graphql/schema_builder.py
@@ -2,11 +2,23 @@
annotations,
)
-from graphql import GraphQLSchema, GraphQLObjectType, GraphQLField
+from functools import (
+ wraps,
+)
+from inspect import (
+ isawaitable,
+)
+
+from graphql import (
+ GraphQLArgument,
+ GraphQLField,
+ GraphQLObjectType,
+ GraphQLSchema,
+)
-from minos.plugins.graphql.decorators import (
- GraphQlQueryEnrouteDecorator,
- GraphQlCommandEnrouteDecorator,
+from minos.networks import (
+ EnrouteDecoratorKind,
+ InMemoryRequest,
)
@@ -15,30 +27,60 @@ def __init__(self, *args, **kwargs):
self.schema = GraphQLSchema(**kwargs)
@classmethod
- def build(cls, queries: list, mutations: list) -> GraphQLSchema:
- params = dict()
- if len(queries) > 0:
- params["query"] = GraphQLSchemaBuilder._build_queries(queries)
- if len(mutations) > 0:
- params["mutation"] = GraphQLSchemaBuilder._build_mutations(mutations)
- return cls(**params).schema
+ def build(cls, routes) -> GraphQLSchema:
+ schema_args = cls._build(routes)
+ return cls(**schema_args).schema
@classmethod
- def _build_queries(cls, queries):
- fields = {item.name: cls._build_query(item) for item in queries}
+ def _build(cls, routes) -> dict:
+ query = cls._build_queries(routes)
+ mutation = cls._build_mutations(routes)
- return GraphQLObjectType("Query", fields=fields)
+ return {"query": query, "mutation": mutation}
@staticmethod
- def _build_query(item: GraphQlQueryEnrouteDecorator):
- return {item.name: GraphQLField(item.argument, resolve=item)}
+ def adapt_callback(callback):
+ @wraps(callback)
+ async def _wrapper(_source, _info, request):
+ request = InMemoryRequest(request)
+
+ response = callback(request)
+ if isawaitable(response):
+ response = await response
+
+ return await response.content()
+
+ return _wrapper
@classmethod
- def _build_mutations(cls, mutations):
- fields = {item.name: cls._build_mutation(item) for item in mutations}
+ def _build_queries(cls, routes):
+ fields = dict()
+ for route, callback in routes.items():
+ callback = cls.adapt_callback(callback)
+ if route.KIND == EnrouteDecoratorKind.Query:
+ fields[route.name] = cls._build_field(route, callback)
+
+ result = None
+ if len(fields) > 0:
+ result = GraphQLObjectType("Query", fields=fields)
+
+ return result
+
+ @classmethod
+ def _build_mutations(cls, routes):
+ fields = dict()
+ for route, callback in routes.items():
+ callback = cls.adapt_callback(callback)
+
+ if route.KIND == EnrouteDecoratorKind.Command:
+ fields[route.name] = cls._build_field(route, callback)
+
+ result = None
+ if len(fields) > 0:
+ result = GraphQLObjectType("Mutation", fields=fields)
- return GraphQLObjectType("Mutation", fields=fields)
+ return result
@staticmethod
- def _build_mutation(item: GraphQlCommandEnrouteDecorator):
- return {item.name: GraphQLField(item.argument, resolve=item)}
+ def _build_field(item, callback):
+ return GraphQLField(item.output, args={"request": GraphQLArgument(item.argument)}, resolve=callback)
diff --git a/packages/plugins/minos-router-graphql/minos/plugins/graphql/services.py b/packages/plugins/minos-router-graphql/minos/plugins/graphql/services.py
deleted file mode 100644
index e8bca0f69..000000000
--- a/packages/plugins/minos-router-graphql/minos/plugins/graphql/services.py
+++ /dev/null
@@ -1,56 +0,0 @@
-import aiohttp_jinja2
-import jinja2
-from aiohttp import (
- web,
-)
-from aiomisc.service.aiohttp import (
- AIOHTTPService,
-)
-
-from .handlers import (
- GraphiqlHandler,
- GraphqlHandler,
-)
-
-
-class RestService(AIOHTTPService):
- """
- Rest Interface
-
- Expose REST Interface handler using aiomisc AIOHTTPService.
-
- """
-
- def __init__(self, **kwargs):
- # self.handler = RestHandler.from_config(**kwargs)
- # super().__init__(**(kwargs | {"address": self.handler.host, "port": self.handler.port}))
- super().__init__(**(kwargs | {"address": "localhost", "port": 7030}))
-
- """
- def _mount_graphql(self, app: web.Application):
- GraphQLView.attach(app, schema=schema, graphiql=True)
- """
-
- def register_handlers(self, app: web.Application):
- routes = [(r"/graphql", GraphqlHandler)]
-
- # if self.dev:
- routes += [(r"/graphiql", GraphiqlHandler)]
-
- app.router.add_routes([web.view(route[0], route[1]) for route in routes])
-
- async def initialize_jinja2(self, app: web.Application):
- aiohttp_jinja2.setup(app, loader=jinja2.FileSystemLoader("./templates"))
-
- async def create_application(self) -> web.Application:
- """Create the web application.
-
- :return: A ``web.Application`` instance.
- """
- app = web.Application()
- self.register_handlers(app)
- await self.initialize_jinja2(app)
- # self._mount_routes(app)
- # self._mount_graphql(app)
- return app
- # return self.handler.get_app() # pragma: no cover
diff --git a/packages/plugins/minos-router-graphql/minos/plugins/graphql/star_wars_example/__init__.py b/packages/plugins/minos-router-graphql/minos/plugins/graphql/star_wars_example/__init__.py
deleted file mode 100644
index 5bcaa3cb0..000000000
--- a/packages/plugins/minos-router-graphql/minos/plugins/graphql/star_wars_example/__init__.py
+++ /dev/null
@@ -1,7 +0,0 @@
-__author__ = "Minos Framework Devs"
-__email__ = "hey@minos.run"
-__version__ = "0.6.0"
-
-from .schema import (
- star_wars_schema,
-)
diff --git a/packages/plugins/minos-router-graphql/minos/plugins/graphql/star_wars_example/data.py b/packages/plugins/minos-router-graphql/minos/plugins/graphql/star_wars_example/data.py
deleted file mode 100644
index 65aa5f650..000000000
--- a/packages/plugins/minos-router-graphql/minos/plugins/graphql/star_wars_example/data.py
+++ /dev/null
@@ -1,151 +0,0 @@
-"""This defines a basic set of data for our Star Wars Schema.
-
-This data is hard coded for the sake of the demo, but you could imagine fetching this
-data from a backend service rather than from hardcoded JSON objects in a more complex
-demo.
-"""
-
-from typing import (
- Awaitable,
- Collection,
- Dict,
- Iterator,
- Optional,
-)
-
-__all__ = ["get_droid", "get_friends", "get_hero", "get_human", "get_secret_backstory"]
-
-# These are classes which correspond to the schema.
-# They represent the shape of the data visited during field resolution.
-
-
-class Character:
- id: str
- name: str
- friends: Collection[str]
- appearsIn: Collection[str]
-
-
-# noinspection PyPep8Naming
-class Human(Character):
- type = "Human"
- homePlanet: str
-
- # noinspection PyShadowingBuiltins
- def __init__(self, id, name, friends, appearsIn, homePlanet):
- self.id, self.name = id, name
- self.friends, self.appearsIn = friends, appearsIn
- self.homePlanet = homePlanet
-
-
-# noinspection PyPep8Naming
-class Droid(Character):
- type = "Droid"
- primaryFunction: str
-
- # noinspection PyShadowingBuiltins
- def __init__(self, id, name, friends, appearsIn, primaryFunction):
- self.id, self.name = id, name
- self.friends, self.appearsIn = friends, appearsIn
- self.primaryFunction = primaryFunction
-
-
-luke = Human(
- id="1000",
- name="Luke Skywalker",
- friends=["1002", "1003", "2000", "2001"],
- appearsIn=[4, 5, 6],
- homePlanet="Tatooine",
-)
-
-vader = Human(
- id="1001",
- name="Darth Vader",
- friends=["1004"],
- appearsIn=[4, 5, 6],
- homePlanet="Tatooine",
-)
-
-han = Human(
- id="1002",
- name="Han Solo",
- friends=["1000", "1003", "2001"],
- appearsIn=[4, 5, 6],
- homePlanet=None,
-)
-
-leia = Human(
- id="1003",
- name="Leia Organa",
- friends=["1000", "1002", "2000", "2001"],
- appearsIn=[4, 5, 6],
- homePlanet="Alderaan",
-)
-
-tarkin = Human(id="1004", name="Wilhuff Tarkin", friends=["1001"], appearsIn=[4], homePlanet=None)
-
-human_data: Dict[str, Human] = {
- "1000": luke,
- "1001": vader,
- "1002": han,
- "1003": leia,
- "1004": tarkin,
-}
-
-threepio = Droid(
- id="2000",
- name="C-3PO",
- friends=["1000", "1002", "1003", "2001"],
- appearsIn=[4, 5, 6],
- primaryFunction="Protocol",
-)
-
-artoo = Droid(
- id="2001",
- name="R2-D2",
- friends=["1000", "1002", "1003"],
- appearsIn=[4, 5, 6],
- primaryFunction="Astromech",
-)
-
-droid_data: Dict[str, Droid] = {"2000": threepio, "2001": artoo}
-
-
-# noinspection PyShadowingBuiltins
-async def get_character(id: str) -> Optional[Character]:
- """Helper function to get a character by ID."""
- # We use an async function just to illustrate that GraphQL-core supports it.
- return human_data.get(id) or droid_data.get(id)
-
-
-def get_friends(character: Character) -> Iterator[Awaitable[Optional[Character]]]:
- """Allows us to query for a character's friends."""
- # Notice that GraphQL-core accepts iterators of awaitables.
- return map(get_character, character.friends)
-
-
-def get_hero(episode: int) -> Character:
- """Allows us to fetch the undisputed hero of the trilogy, R2-D2."""
- if episode == 5:
- # Luke is the hero of Episode V.
- return luke
- # Artoo is the hero otherwise.
- return artoo
-
-
-# noinspection PyShadowingBuiltins
-def get_human(id: str) -> Optional[Human]:
- """Allows us to query for the human with the given id."""
- return human_data.get(id)
-
-
-# noinspection PyShadowingBuiltins
-def get_droid(id: str) -> Optional[Droid]:
- """Allows us to query for the droid with the given id."""
- return droid_data.get(id)
-
-
-# noinspection PyUnusedLocal
-def get_secret_backstory(character: Character) -> str:
- """Raise an error when attempting to get the secret backstory."""
- raise RuntimeError("secretBackstory is secret.")
diff --git a/packages/plugins/minos-router-graphql/minos/plugins/graphql/star_wars_example/schema.py b/packages/plugins/minos-router-graphql/minos/plugins/graphql/star_wars_example/schema.py
deleted file mode 100644
index b6d9068aa..000000000
--- a/packages/plugins/minos-router-graphql/minos/plugins/graphql/star_wars_example/schema.py
+++ /dev/null
@@ -1,241 +0,0 @@
-"""Star Wars GraphQL schema
-
-This is designed to be an end-to-end test, demonstrating the full GraphQL stack.
-
-We will create a GraphQL schema that describes the major characters in the original
-Star Wars trilogy.
-
-NOTE: This may contain spoilers for the original Star Wars trilogy.
-
-Using our shorthand to describe type systems, the type system for our Star Wars example
-is::
-
- enum Episode { NEW_HOPE, EMPIRE, JEDI }
-
- interface Character {
- id: String!
- name: String
- friends: [Character]
- appearsIn: [Episode]
- }
-
- type Human implements Character {
- id: String!
- name: String
- friends: [Character]
- appearsIn: [Episode]
- homePlanet: String
- }
-
- type Droid implements Character {
- id: String!
- name: String
- friends: [Character]
- appearsIn: [Episode]
- primaryFunction: String
- }
-
- type Query {
- hero(episode: Episode): Character
- human(id: String!): Human
- droid(id: String!): Droid
- }
-"""
-import asyncio
-
-from graphql.type import (
- GraphQLArgument,
- GraphQLEnumType,
- GraphQLEnumValue,
- GraphQLField,
- GraphQLInterfaceType,
- GraphQLList,
- GraphQLNonNull,
- GraphQLObjectType,
- GraphQLSchema,
- GraphQLString,
-)
-
-from .data import (
- get_droid,
- get_friends,
- get_hero,
- get_human,
- get_secret_backstory,
-)
-
-__all__ = ["star_wars_schema"]
-
-# We begin by setting up our schema.
-
-# The original trilogy consists of three movies.
-#
-# This implements the following type system shorthand:
-# enum Episode { NEW_HOPE, EMPIRE, JEDI }
-
-episode_enum = GraphQLEnumType(
- "Episode",
- {
- "NEW_HOPE": GraphQLEnumValue(4, description="Released in 1977."),
- "EMPIRE": GraphQLEnumValue(5, description="Released in 1980."),
- "JEDI": GraphQLEnumValue(6, description="Released in 1983."),
- },
- description="One of the films in the Star Wars Trilogy",
-)
-
-# Characters in the Star Wars trilogy are either humans or droids.
-#
-# This implements the following type system shorthand:
-# interface Character {
-# id: String!
-# name: String
-# friends: [Character]
-# appearsIn: [Episode]
-# secretBackstory: String
-
-human_type: GraphQLObjectType
-droid_type: GraphQLObjectType
-
-character_interface: GraphQLInterfaceType = GraphQLInterfaceType(
- "Character",
- lambda: {
- "id": GraphQLField(GraphQLNonNull(GraphQLString), description="The id of the character."),
- "name": GraphQLField(GraphQLString, description="The name of the character."),
- "friends": GraphQLField(
- GraphQLList(character_interface),
- description="The friends of the character," " or an empty list if they have none.",
- ),
- "appearsIn": GraphQLField(GraphQLList(episode_enum), description="Which movies they appear in."),
- "secretBackstory": GraphQLField(GraphQLString, description="All secrets about their past."),
- },
- resolve_type=lambda character, _info, _type: {
- "Human": human_type.name,
- "Droid": droid_type.name,
- }[character.type],
- description="A character in the Star Wars Trilogy",
-)
-
-# We define our human type, which implements the character interface.
-#
-# This implements the following type system shorthand:
-# type Human : Character {
-# id: String!
-# name: String
-# friends: [Character]
-# appearsIn: [Episode]
-# secretBackstory: String
-# }
-
-human_type = GraphQLObjectType(
- "Human",
- lambda: {
- "id": GraphQLField(GraphQLNonNull(GraphQLString), description="The id of the human."),
- "name": GraphQLField(GraphQLString, description="The name of the human."),
- "friends": GraphQLField(
- GraphQLList(character_interface),
- description="The friends of the human," " or an empty list if they have none.",
- resolve=lambda human, _info: get_friends(human),
- ),
- "appearsIn": GraphQLField(GraphQLList(episode_enum), description="Which movies they appear in."),
- "homePlanet": GraphQLField(
- GraphQLString,
- description="The home planet of the human, or null if unknown.",
- ),
- "secretBackstory": GraphQLField(
- GraphQLString,
- resolve=lambda human, _info: get_secret_backstory(human),
- description="Where are they from and how they came to be who they are.",
- ),
- },
- interfaces=[character_interface],
- description="A humanoid creature in the Star Wars universe.",
-)
-
-# The other type of character in Star Wars is a droid.
-#
-# This implements the following type system shorthand:
-# type Droid : Character {
-# id: String!
-# name: String
-# friends: [Character]
-# appearsIn: [Episode]
-# secretBackstory: String
-# primaryFunction: String
-# }
-
-droid_type = GraphQLObjectType(
- "Droid",
- lambda: {
- "id": GraphQLField(GraphQLNonNull(GraphQLString), description="The id of the droid."),
- "name": GraphQLField(GraphQLString, description="The name of the droid."),
- "friends": GraphQLField(
- GraphQLList(character_interface),
- description="The friends of the droid," " or an empty list if they have none.",
- resolve=lambda droid, _info: get_friends(droid),
- ),
- "appearsIn": GraphQLField(GraphQLList(episode_enum), description="Which movies they appear in."),
- "secretBackstory": GraphQLField(
- GraphQLString,
- resolve=lambda droid, _info: get_secret_backstory(droid),
- description="Construction date and the name of the designer.",
- ),
- "primaryFunction": GraphQLField(GraphQLString, description="The primary function of the droid."),
- },
- interfaces=[character_interface],
- description="A mechanical creature in the Star Wars universe.",
-)
-
-# This is the type that will be the root of our query, and the
-# entry point into our schema. It gives us the ability to fetch
-# objects by their IDs, as well as to fetch the undisputed hero
-# of the Star Wars trilogy, R2-D2, directly.
-#
-# This implements the following type system shorthand:
-# type Query {
-# hero(episode: Episode): Character
-# human(id: String!): Human
-# droid(id: String!): Droid
-# }
-
-# noinspection PyShadowingBuiltins
-query_type = GraphQLObjectType(
- "Query",
- lambda: {
- "hero": GraphQLField(
- character_interface,
- args={
- "episode": GraphQLArgument(
- episode_enum,
- description=(
- "If omitted, returns the hero of the whole saga."
- " If provided, returns the hero of that particular episode."
- ),
- )
- },
- resolve=lambda _source, _info, episode=None: get_hero(episode),
- ),
- "human": GraphQLField(
- human_type,
- args={"id": GraphQLArgument(GraphQLNonNull(GraphQLString), description="id of the human")},
- resolve=lambda _source, _info, id: get_human(id),
- ),
- "droid": GraphQLField(
- droid_type,
- args={"id": GraphQLArgument(GraphQLNonNull(GraphQLString), description="id of the droid")},
- resolve=lambda _source, _info, id: get_droid(id),
- ),
- },
-)
-
-
-async def resolve_hello(obj, info):
- await asyncio.sleep(3)
- return "world"
-
-
-mutation_type = GraphQLObjectType("Mutation", fields={"hello": GraphQLField(GraphQLString, resolve=resolve_hello)})
-
-# Finally, we construct our schema (whose starting query type is the query
-# type we defined above) and export it.
-
-star_wars_schema = GraphQLSchema(query=query_type, mutation=mutation_type, types=[human_type, droid_type])
diff --git a/packages/plugins/minos-router-graphql/minos/plugins/graphql/templates/graphiql.html b/packages/plugins/minos-router-graphql/minos/plugins/graphql/templates/graphiql.html
deleted file mode 100644
index 0a90e30e2..000000000
--- a/packages/plugins/minos-router-graphql/minos/plugins/graphql/templates/graphiql.html
+++ /dev/null
@@ -1,139 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Loading...
-
-
-
diff --git a/packages/plugins/minos-router-graphql/tests/services/commands.py b/packages/plugins/minos-router-graphql/tests/services/commands.py
index 71f2a2943..318f10354 100644
--- a/packages/plugins/minos-router-graphql/tests/services/commands.py
+++ b/packages/plugins/minos-router-graphql/tests/services/commands.py
@@ -1,4 +1,8 @@
-from graphql import GraphQLField, GraphQLString
+from graphql import (
+ GraphQLField,
+ GraphQLString,
+)
+
from minos.networks import (
BrokerResponse,
Request,
@@ -8,13 +12,13 @@
class CommandService:
- @enroute.graphql.command(name="order-command", argument=GraphQLField(GraphQLString), output=GraphQLString)
+ @enroute.graphql.command(name="order_command", argument=GraphQLString, output=GraphQLString)
def create_order(self, request: Request):
"""For testng purposes."""
return "eu38hj32-889283-j2jjb5kl"
- @enroute.graphql.command(name="ticket-command", argument=GraphQLField(GraphQLString), output=GraphQLString)
+ @enroute.graphql.command(name="ticket_command", argument=GraphQLString, output=GraphQLString)
def create_ticket(self, request: Request):
"""For testng purposes."""
diff --git a/packages/plugins/minos-router-graphql/tests/services/queries.py b/packages/plugins/minos-router-graphql/tests/services/queries.py
index 54b94e2a0..30c04c737 100644
--- a/packages/plugins/minos-router-graphql/tests/services/queries.py
+++ b/packages/plugins/minos-router-graphql/tests/services/queries.py
@@ -1,4 +1,8 @@
-from graphql import GraphQLField, GraphQLString
+from graphql import (
+ GraphQLField,
+ GraphQLString,
+)
+
from minos.networks import (
Request,
Response,
@@ -7,14 +11,14 @@
class QueryService:
- @enroute.graphql.query(name="order-query", argument=GraphQLField(GraphQLString), output=GraphQLString)
+ @enroute.graphql.query(name="order_query", argument=GraphQLString, output=GraphQLString)
def get_order(self, request: Request):
"""For testng purposes."""
- return "eu38hj32-889283-j2jjb5kl"
+ return Response("eu38hj32-889283-j2jjb5kl")
- @enroute.graphql.query(name="ticket-query", argument=GraphQLField(GraphQLString), output=GraphQLString)
+ @enroute.graphql.query(name="ticket_query", argument=GraphQLString, output=GraphQLString)
def get_ticket(self, request: Request):
"""For testng purposes."""
- return "zdw4gg4g-gser44gkl-jh4j3h4h"
+ return Response("zdw4gg4g-gser44gkl-jh4j3h4h")
diff --git a/packages/plugins/minos-router-graphql/tests/test_graphql/test_decorators.py b/packages/plugins/minos-router-graphql/tests/test_graphql/test_decorators.py
index 1568a6597..a93afd5ba 100644
--- a/packages/plugins/minos-router-graphql/tests/test_graphql/test_decorators.py
+++ b/packages/plugins/minos-router-graphql/tests/test_graphql/test_decorators.py
@@ -1,25 +1,29 @@
import unittest
-from graphql import GraphQLField, GraphQLString
-from minos.networks import (
- EnrouteAnalyzer,
+from graphql import (
+ GraphQLField,
+ GraphQLString,
)
+
from minos.common import (
classname,
)
-
-from minos.plugins.graphql.decorators import GraphQlQueryEnrouteDecorator
-from tests.utils import (
- FakeCommandService,
- FakeQueryService,
+from minos.networks import (
+ EnrouteAnalyzer,
)
from minos.plugins.graphql import (
GraphQlEnroute,
)
+from minos.plugins.graphql.decorators import (
+ GraphQlQueryEnrouteDecorator,
+)
+from tests.utils import (
+ FakeCommandService,
+ FakeQueryService,
+)
class TestSomething(unittest.TestCase):
-
def test_decorated_str(self):
analyzer = EnrouteAnalyzer(classname(FakeQueryService))
self.assertEqual(FakeQueryService, analyzer.decorated)
@@ -29,11 +33,13 @@ def test_get_all_queries(self):
observed = analyzer.get_all()
expected = {
- "get_order": {GraphQlQueryEnrouteDecorator(name="order", argument=GraphQLField(GraphQLString), output=GraphQLString)},
+ "get_order": {
+ GraphQlQueryEnrouteDecorator(name="order", argument=GraphQLField(GraphQLString), output=GraphQLString)
+ },
}
self.assertEqual(expected, observed)
-if __name__ == '__main__':
+if __name__ == "__main__":
unittest.main()
diff --git a/packages/plugins/minos-router-graphql/tests/test_graphql/test_routers.py b/packages/plugins/minos-router-graphql/tests/test_graphql/test_routers.py
index 4674c92a9..7a1be2d05 100644
--- a/packages/plugins/minos-router-graphql/tests/test_graphql/test_routers.py
+++ b/packages/plugins/minos-router-graphql/tests/test_graphql/test_routers.py
@@ -1,7 +1,13 @@
import unittest
-from minos.common import MinosConfig
+from graphql import (
+ validate_schema,
+ GraphQLSchema,
+)
+from minos.common import (
+ MinosConfig,
+)
from minos.plugins.graphql import (
GraphQlEnroute,
GraphQlHttpRouter,
@@ -9,19 +15,18 @@
from tests.utils import (
BASE_PATH,
)
-from minos.common.testing import (
- PostgresAsyncTestCase,
-)
class TestSomething(unittest.TestCase):
CONFIG_FILE_PATH = BASE_PATH / "test_config.yml"
_config = MinosConfig(CONFIG_FILE_PATH)
- def test_true(self):
+ def test_from_config(self):
router = GraphQlHttpRouter.from_config(config=self._config)
- self.assertTrue(GraphQlEnroute)
+
+ self.assertIsInstance(router._schema, GraphQLSchema)
+ self.assertIsInstance(router, GraphQlHttpRouter)
-if __name__ == '__main__':
+if __name__ == "__main__":
unittest.main()
diff --git a/packages/plugins/minos-router-graphql/tests/test_graphql/test_schema_builder.py b/packages/plugins/minos-router-graphql/tests/test_graphql/test_schema_builder.py
index bc9d7fc9a..bcbd99a12 100644
--- a/packages/plugins/minos-router-graphql/tests/test_graphql/test_schema_builder.py
+++ b/packages/plugins/minos-router-graphql/tests/test_graphql/test_schema_builder.py
@@ -1,19 +1,94 @@
import unittest
+from graphql import (
+ graphql_sync,
+ validate_schema, GraphQLString,
+)
+from minos.networks import (
+ Request,
+ Response,
+)
+from minos.common import (
+ MinosConfig,
+)
from minos.plugins.graphql import (
- GraphQlEnroute, GraphQLSchemaBuilder,
+ GraphQlEnroute,
+ GraphQlHttpRouter,
+ GraphQLSchemaBuilder,
)
+from minos.plugins.graphql.decorators import GraphQlCommandEnrouteDecorator, GraphQlQueryEnrouteDecorator
+
+from tests.utils import (
+ BASE_PATH,
+)
+
+
+async def callback_fn(request: Request):
+ return Response("ticket #4")
class TestSomething(unittest.TestCase):
+ CONFIG_FILE_PATH = BASE_PATH / "test_config.yml"
+ _config = MinosConfig(CONFIG_FILE_PATH)
+
+ def test_build(self):
+ routes = {
+ GraphQlCommandEnrouteDecorator(name="order_command", argument=GraphQLString,
+ output=GraphQLString): callback_fn,
+ GraphQlCommandEnrouteDecorator(name="ticket_command", argument=GraphQLString,
+ output=GraphQLString): callback_fn,
+ GraphQlQueryEnrouteDecorator(name="order_query", argument=GraphQLString, output=GraphQLString): callback_fn,
+ GraphQlQueryEnrouteDecorator(name="ticket_query", argument=GraphQLString, output=GraphQLString): callback_fn}
+
+ schema = GraphQLSchemaBuilder.build(routes=routes)
+
+ errors = validate_schema(schema)
+
+ self.assertEqual(errors, [])
+
+ def test_build_only_queries(self):
+ routes = {
+ GraphQlQueryEnrouteDecorator(name="order_query", argument=GraphQLString, output=GraphQLString): callback_fn,
+ GraphQlQueryEnrouteDecorator(name="ticket_query", argument=GraphQLString, output=GraphQLString): callback_fn}
+
+ schema = GraphQLSchemaBuilder.build(routes=routes)
+
+ errors = validate_schema(schema)
+
+ self.assertEqual(errors, [])
+
+ def test_build_only_commands(self):
+ routes = {
+ GraphQlCommandEnrouteDecorator(name="order_command", argument=GraphQLString,
+ output=GraphQLString): callback_fn,
+ GraphQlCommandEnrouteDecorator(name="ticket_command", argument=GraphQLString,
+ output=GraphQLString): callback_fn}
+
+ schema = GraphQLSchemaBuilder.build(routes=routes)
+
+ errors = validate_schema(schema)
+
+ self.assertEqual(errors, [])
+
+ def test_schema_valid(self):
+ router = GraphQlHttpRouter.from_config(config=self._config)
+
+ errors = validate_schema(router._schema)
+
+ self.assertEqual(errors, [])
+
+ def test_query(self):
+ routes = {
+ GraphQlQueryEnrouteDecorator(name="order_query", argument=GraphQLString, output=GraphQLString): callback_fn}
+
+ schema = GraphQLSchemaBuilder.build(routes=routes)
+
+ source = "{ order_query }"
- def test_true(self):
- mutations = [GraphQlCommandEnrouteDecorator('order-command'), GraphQlCommandEnrouteDecorator('ticket-command')]
- queries = [GraphQlQueryEnrouteDecorator('order-query'), GraphQlQueryEnrouteDecorator('ticket-query')]
+ result = graphql_sync(schema, source)
- schema = GraphQLSchemaBuilder.build(queries=queries, mutations=mutations)
- self.assertTrue(GraphQlEnroute)
+ self.assertEqual(result.errors, [])
-if __name__ == '__main__':
+if __name__ == "__main__":
unittest.main()
diff --git a/packages/plugins/minos-router-graphql/tests/utils.py b/packages/plugins/minos-router-graphql/tests/utils.py
index d2d98e20f..5a59ee2f0 100644
--- a/packages/plugins/minos-router-graphql/tests/utils.py
+++ b/packages/plugins/minos-router-graphql/tests/utils.py
@@ -8,7 +8,13 @@
Any,
)
-from graphql import GraphQLObjectType, GraphQLField, GraphQLString, GraphQLArgument
+from graphql import (
+ GraphQLArgument,
+ GraphQLField,
+ GraphQLObjectType,
+ GraphQLString,
+)
+
from minos.common import (
DeclarativeModel,
)
@@ -36,23 +42,24 @@ def __lt__(self, other: Any) -> bool:
class FakeCommandService:
"""For testng purposes."""
- #@enroute.broker.command("GetOrder")
+ # @enroute.broker.command("GetOrder")
def get_order_command(self, request: Request) -> Response:
"""For testng purposes."""
return Response("get_order_command")
+
"""
@enroute.graphql.command(
args={"request": GraphQLArgument(GraphQlObject({"name": GraphQLString, "surname": GraphQLString}))},
response=GrapqlUUID
)
"""
+
def get_hero(self, request: Request) -> Response:
"""For testng purposes."""
uuid = request.content()
return Response(uuid)
-
class FakeQueryService:
"""For testng purposes."""
@@ -62,4 +69,3 @@ def get_order(self, request: Request):
"""For testng purposes."""
return "eu38hj32-889283-j2jjb5kl"
-
From 9f52d752a640c6b5a693e5f26023cfa1a6c4c118 Mon Sep 17 00:00:00 2001
From: Vladyslav Fenchak
Date: Mon, 14 Mar 2022 20:52:17 +0100
Subject: [PATCH 18/44] ISSUE #149
---
.../minos/plugins/graphql/schema_builder.py | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/packages/plugins/minos-router-graphql/minos/plugins/graphql/schema_builder.py b/packages/plugins/minos-router-graphql/minos/plugins/graphql/schema_builder.py
index 57be9dd00..5bc97ec28 100644
--- a/packages/plugins/minos-router-graphql/minos/plugins/graphql/schema_builder.py
+++ b/packages/plugins/minos-router-graphql/minos/plugins/graphql/schema_builder.py
@@ -13,7 +13,7 @@
GraphQLArgument,
GraphQLField,
GraphQLObjectType,
- GraphQLSchema,
+ GraphQLSchema, GraphQLString,
)
from minos.networks import (
@@ -60,7 +60,11 @@ def _build_queries(cls, routes):
if route.KIND == EnrouteDecoratorKind.Query:
fields[route.name] = cls._build_field(route, callback)
- result = None
+ result = GraphQLObjectType("Query", fields={
+ 'hello': GraphQLField(
+ GraphQLString,
+ resolve=lambda obj, info: 'world')
+ })
if len(fields) > 0:
result = GraphQLObjectType("Query", fields=fields)
From 88edc781f1ac848c042c70aebb0accbe304dd7a9 Mon Sep 17 00:00:00 2001
From: "Restyled.io"
Date: Mon, 14 Mar 2022 19:52:26 +0000
Subject: [PATCH 19/44] Restyled by black
---
.../minos/plugins/graphql/schema_builder.py | 11 +++---
.../tests/test_graphql/test_schema_builder.py | 37 +++++++++++++------
2 files changed, 30 insertions(+), 18 deletions(-)
diff --git a/packages/plugins/minos-router-graphql/minos/plugins/graphql/schema_builder.py b/packages/plugins/minos-router-graphql/minos/plugins/graphql/schema_builder.py
index 5bc97ec28..29556868e 100644
--- a/packages/plugins/minos-router-graphql/minos/plugins/graphql/schema_builder.py
+++ b/packages/plugins/minos-router-graphql/minos/plugins/graphql/schema_builder.py
@@ -13,7 +13,8 @@
GraphQLArgument,
GraphQLField,
GraphQLObjectType,
- GraphQLSchema, GraphQLString,
+ GraphQLSchema,
+ GraphQLString,
)
from minos.networks import (
@@ -60,11 +61,9 @@ def _build_queries(cls, routes):
if route.KIND == EnrouteDecoratorKind.Query:
fields[route.name] = cls._build_field(route, callback)
- result = GraphQLObjectType("Query", fields={
- 'hello': GraphQLField(
- GraphQLString,
- resolve=lambda obj, info: 'world')
- })
+ result = GraphQLObjectType(
+ "Query", fields={"hello": GraphQLField(GraphQLString, resolve=lambda obj, info: "world")}
+ )
if len(fields) > 0:
result = GraphQLObjectType("Query", fields=fields)
diff --git a/packages/plugins/minos-router-graphql/tests/test_graphql/test_schema_builder.py b/packages/plugins/minos-router-graphql/tests/test_graphql/test_schema_builder.py
index bcbd99a12..73fcdfa04 100644
--- a/packages/plugins/minos-router-graphql/tests/test_graphql/test_schema_builder.py
+++ b/packages/plugins/minos-router-graphql/tests/test_graphql/test_schema_builder.py
@@ -2,7 +2,8 @@
from graphql import (
graphql_sync,
- validate_schema, GraphQLString,
+ validate_schema,
+ GraphQLString,
)
from minos.networks import (
Request,
@@ -33,12 +34,17 @@ class TestSomething(unittest.TestCase):
def test_build(self):
routes = {
- GraphQlCommandEnrouteDecorator(name="order_command", argument=GraphQLString,
- output=GraphQLString): callback_fn,
- GraphQlCommandEnrouteDecorator(name="ticket_command", argument=GraphQLString,
- output=GraphQLString): callback_fn,
+ GraphQlCommandEnrouteDecorator(
+ name="order_command", argument=GraphQLString, output=GraphQLString
+ ): callback_fn,
+ GraphQlCommandEnrouteDecorator(
+ name="ticket_command", argument=GraphQLString, output=GraphQLString
+ ): callback_fn,
GraphQlQueryEnrouteDecorator(name="order_query", argument=GraphQLString, output=GraphQLString): callback_fn,
- GraphQlQueryEnrouteDecorator(name="ticket_query", argument=GraphQLString, output=GraphQLString): callback_fn}
+ GraphQlQueryEnrouteDecorator(
+ name="ticket_query", argument=GraphQLString, output=GraphQLString
+ ): callback_fn,
+ }
schema = GraphQLSchemaBuilder.build(routes=routes)
@@ -49,7 +55,10 @@ def test_build(self):
def test_build_only_queries(self):
routes = {
GraphQlQueryEnrouteDecorator(name="order_query", argument=GraphQLString, output=GraphQLString): callback_fn,
- GraphQlQueryEnrouteDecorator(name="ticket_query", argument=GraphQLString, output=GraphQLString): callback_fn}
+ GraphQlQueryEnrouteDecorator(
+ name="ticket_query", argument=GraphQLString, output=GraphQLString
+ ): callback_fn,
+ }
schema = GraphQLSchemaBuilder.build(routes=routes)
@@ -59,10 +68,13 @@ def test_build_only_queries(self):
def test_build_only_commands(self):
routes = {
- GraphQlCommandEnrouteDecorator(name="order_command", argument=GraphQLString,
- output=GraphQLString): callback_fn,
- GraphQlCommandEnrouteDecorator(name="ticket_command", argument=GraphQLString,
- output=GraphQLString): callback_fn}
+ GraphQlCommandEnrouteDecorator(
+ name="order_command", argument=GraphQLString, output=GraphQLString
+ ): callback_fn,
+ GraphQlCommandEnrouteDecorator(
+ name="ticket_command", argument=GraphQLString, output=GraphQLString
+ ): callback_fn,
+ }
schema = GraphQLSchemaBuilder.build(routes=routes)
@@ -79,7 +91,8 @@ def test_schema_valid(self):
def test_query(self):
routes = {
- GraphQlQueryEnrouteDecorator(name="order_query", argument=GraphQLString, output=GraphQLString): callback_fn}
+ GraphQlQueryEnrouteDecorator(name="order_query", argument=GraphQLString, output=GraphQLString): callback_fn
+ }
schema = GraphQLSchemaBuilder.build(routes=routes)
From a8c51a33ad47c914ce48a607ab6e47b37707762d Mon Sep 17 00:00:00 2001
From: "Restyled.io"
Date: Mon, 14 Mar 2022 19:52:27 +0000
Subject: [PATCH 20/44] Restyled by isort
---
.../tests/test_graphql/test_routers.py | 2 +-
.../tests/test_graphql/test_schema_builder.py | 15 +++++++++------
2 files changed, 10 insertions(+), 7 deletions(-)
diff --git a/packages/plugins/minos-router-graphql/tests/test_graphql/test_routers.py b/packages/plugins/minos-router-graphql/tests/test_graphql/test_routers.py
index 7a1be2d05..4f88c7c27 100644
--- a/packages/plugins/minos-router-graphql/tests/test_graphql/test_routers.py
+++ b/packages/plugins/minos-router-graphql/tests/test_graphql/test_routers.py
@@ -1,8 +1,8 @@
import unittest
from graphql import (
- validate_schema,
GraphQLSchema,
+ validate_schema,
)
from minos.common import (
diff --git a/packages/plugins/minos-router-graphql/tests/test_graphql/test_schema_builder.py b/packages/plugins/minos-router-graphql/tests/test_graphql/test_schema_builder.py
index 73fcdfa04..59c20ea86 100644
--- a/packages/plugins/minos-router-graphql/tests/test_graphql/test_schema_builder.py
+++ b/packages/plugins/minos-router-graphql/tests/test_graphql/test_schema_builder.py
@@ -1,24 +1,27 @@
import unittest
from graphql import (
+ GraphQLString,
graphql_sync,
validate_schema,
- GraphQLString,
+)
+
+from minos.common import (
+ MinosConfig,
)
from minos.networks import (
Request,
Response,
)
-from minos.common import (
- MinosConfig,
-)
from minos.plugins.graphql import (
GraphQlEnroute,
GraphQlHttpRouter,
GraphQLSchemaBuilder,
)
-from minos.plugins.graphql.decorators import GraphQlCommandEnrouteDecorator, GraphQlQueryEnrouteDecorator
-
+from minos.plugins.graphql.decorators import (
+ GraphQlCommandEnrouteDecorator,
+ GraphQlQueryEnrouteDecorator,
+)
from tests.utils import (
BASE_PATH,
)
From 8d66c495374034ec492fcd4aa03802b3d6ef1ca8 Mon Sep 17 00:00:00 2001
From: Vladyslav Fenchak
Date: Mon, 14 Mar 2022 20:59:54 +0100
Subject: [PATCH 21/44] ISSUE #149
---
.../minos-router-graphql-publish.yml | 33 ++++++++++
.../workflows/minos-router-graphql-tests.yml | 66 +++++++++++++++++++
.../plugins/minos-router-graphql/docs/conf.py | 6 +-
3 files changed, 102 insertions(+), 3 deletions(-)
create mode 100644 .github/workflows/minos-router-graphql-publish.yml
create mode 100644 .github/workflows/minos-router-graphql-tests.yml
diff --git a/.github/workflows/minos-router-graphql-publish.yml b/.github/workflows/minos-router-graphql-publish.yml
new file mode 100644
index 000000000..0b641d384
--- /dev/null
+++ b/.github/workflows/minos-router-graphql-publish.yml
@@ -0,0 +1,33 @@
+name: "Publish: minos-router-graphql"
+
+on:
+ push:
+ branches:
+ - '*.*.x'
+ paths:
+ - 'packages/plugins/minos-router-graphql/**'
+
+jobs:
+ deploy:
+ runs-on: ubuntu-latest
+ container: python:3.9-buster
+ defaults:
+ run:
+ working-directory: packages/plugins/minos-router-graphql
+
+ steps:
+
+ - name: Check out repository code
+ uses: actions/checkout@v2
+
+ - name: Install Poetry
+ uses: snok/install-poetry@v1
+
+ - name: Install dependencies
+ run: make install
+
+ - name: Publish package
+ run: make release
+ env:
+ POETRY_HTTP_BASIC_PYPI_USERNAME: ${{ secrets.PYPI_USERNAME }}
+ POETRY_HTTP_BASIC_PYPI_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
diff --git a/.github/workflows/minos-router-graphql-tests.yml b/.github/workflows/minos-router-graphql-tests.yml
new file mode 100644
index 000000000..54c26751e
--- /dev/null
+++ b/.github/workflows/minos-router-graphql-tests.yml
@@ -0,0 +1,66 @@
+name: "Test: minos-router-graphql"
+
+on:
+ push:
+ branches:
+ - main
+ - '*.*.x'
+ pull_request:
+ paths:
+ - 'packages/plugins/minos-router-graphql/**'
+ - 'packages/core/minos-microservice-networks/**'
+ - 'packages/core/minos-microservice-common/**'
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ container: python:3.9-buster
+ defaults:
+ run:
+ working-directory: packages/plugins/minos-router-graphql
+
+ services:
+ postgres:
+ image: postgres
+ env:
+ POSTGRES_USER: minos
+ POSTGRES_PASSWORD: min0s
+ POSTGRES_DB: order_db
+ ports:
+ - 5432:5432
+ options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
+
+ env:
+ MINOS_BROKER_QUEUE_HOST: postgres
+ MINOS_BROKER_HOST: kafka
+ MINOS_REPOSITORY_HOST: postgres
+ MINOS_SNAPSHOT_HOST: postgres
+
+ steps:
+ - name: Check out repository code
+ uses: actions/checkout@v2
+
+ - name: Install Poetry
+ uses: snok/install-poetry@v1
+
+ - name: Install dependencies
+ run: make install
+
+ - name: Lint package
+ run: make lint
+
+ - name: Test package with coverage
+ run: make coverage
+
+ - name: Publish coverage
+ uses: codecov/codecov-action@v2
+ with:
+ token: ${{ secrets.CODECOV_TOKEN }}
+ files: ./packages/plugins/minos-http-aiohttp/coverage.xml
+ fail_ci_if_error: true
+
+ - name: Generate documentation
+ run: make docs
+
+ - name: Generate build
+ run: make dist
diff --git a/packages/plugins/minos-router-graphql/docs/conf.py b/packages/plugins/minos-router-graphql/docs/conf.py
index b83dc06a1..f17c29f90 100755
--- a/packages/plugins/minos-router-graphql/docs/conf.py
+++ b/packages/plugins/minos-router-graphql/docs/conf.py
@@ -25,7 +25,7 @@
import sphinx_rtd_theme
from minos.plugins import (
- rest_aiohttp,
+ graphql,
)
# -- General configuration ---------------------------------------------
@@ -67,9 +67,9 @@
# the built documents.
#
# The short X.Y version.
-version = rest_aiohttp.__version__
+version = graphql.__version__
# The full version, including alpha/beta/rc tags.
-release = rest_aiohttp.__version__
+release = graphql.__version__
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
From e9aeea090cbc2ee4b44c7a4226040911d86aa9e5 Mon Sep 17 00:00:00 2001
From: Vladyslav Fenchak
Date: Mon, 14 Mar 2022 21:04:13 +0100
Subject: [PATCH 22/44] ISSUE #149
---
.../minos/plugins/graphql/decorators.py | 4 ----
.../plugins/minos-router-graphql/tests/services/commands.py | 6 ++----
.../plugins/minos-router-graphql/tests/services/queries.py | 1 -
.../tests/test_graphql/test_decorators.py | 4 ----
.../minos-router-graphql/tests/test_graphql/test_routers.py | 2 --
.../tests/test_graphql/test_schema_builder.py | 1 -
6 files changed, 2 insertions(+), 16 deletions(-)
diff --git a/packages/plugins/minos-router-graphql/minos/plugins/graphql/decorators.py b/packages/plugins/minos-router-graphql/minos/plugins/graphql/decorators.py
index c0243708a..c48258acc 100644
--- a/packages/plugins/minos-router-graphql/minos/plugins/graphql/decorators.py
+++ b/packages/plugins/minos-router-graphql/minos/plugins/graphql/decorators.py
@@ -1,11 +1,7 @@
from abc import (
ABC,
)
-from collections import (
- defaultdict,
-)
from collections.abc import (
- Collection,
Iterable,
)
from typing import (
diff --git a/packages/plugins/minos-router-graphql/tests/services/commands.py b/packages/plugins/minos-router-graphql/tests/services/commands.py
index 318f10354..44a5aa536 100644
--- a/packages/plugins/minos-router-graphql/tests/services/commands.py
+++ b/packages/plugins/minos-router-graphql/tests/services/commands.py
@@ -1,10 +1,8 @@
from graphql import (
- GraphQLField,
GraphQLString,
)
from minos.networks import (
- BrokerResponse,
Request,
Response,
enroute,
@@ -16,10 +14,10 @@ class CommandService:
def create_order(self, request: Request):
"""For testng purposes."""
- return "eu38hj32-889283-j2jjb5kl"
+ return Response("eu38hj32-889283-j2jjb5kl")
@enroute.graphql.command(name="ticket_command", argument=GraphQLString, output=GraphQLString)
def create_ticket(self, request: Request):
"""For testng purposes."""
- return "zdw4gg4g-gser44gkl-jh4j3h4h"
+ return Response("zdw4gg4g-gser44gkl-jh4j3h4h")
diff --git a/packages/plugins/minos-router-graphql/tests/services/queries.py b/packages/plugins/minos-router-graphql/tests/services/queries.py
index 30c04c737..a57c7c3bc 100644
--- a/packages/plugins/minos-router-graphql/tests/services/queries.py
+++ b/packages/plugins/minos-router-graphql/tests/services/queries.py
@@ -1,5 +1,4 @@
from graphql import (
- GraphQLField,
GraphQLString,
)
diff --git a/packages/plugins/minos-router-graphql/tests/test_graphql/test_decorators.py b/packages/plugins/minos-router-graphql/tests/test_graphql/test_decorators.py
index a93afd5ba..4d8e70eb1 100644
--- a/packages/plugins/minos-router-graphql/tests/test_graphql/test_decorators.py
+++ b/packages/plugins/minos-router-graphql/tests/test_graphql/test_decorators.py
@@ -11,14 +11,10 @@
from minos.networks import (
EnrouteAnalyzer,
)
-from minos.plugins.graphql import (
- GraphQlEnroute,
-)
from minos.plugins.graphql.decorators import (
GraphQlQueryEnrouteDecorator,
)
from tests.utils import (
- FakeCommandService,
FakeQueryService,
)
diff --git a/packages/plugins/minos-router-graphql/tests/test_graphql/test_routers.py b/packages/plugins/minos-router-graphql/tests/test_graphql/test_routers.py
index 4f88c7c27..08333caad 100644
--- a/packages/plugins/minos-router-graphql/tests/test_graphql/test_routers.py
+++ b/packages/plugins/minos-router-graphql/tests/test_graphql/test_routers.py
@@ -2,14 +2,12 @@
from graphql import (
GraphQLSchema,
- validate_schema,
)
from minos.common import (
MinosConfig,
)
from minos.plugins.graphql import (
- GraphQlEnroute,
GraphQlHttpRouter,
)
from tests.utils import (
diff --git a/packages/plugins/minos-router-graphql/tests/test_graphql/test_schema_builder.py b/packages/plugins/minos-router-graphql/tests/test_graphql/test_schema_builder.py
index 59c20ea86..e0d9278a3 100644
--- a/packages/plugins/minos-router-graphql/tests/test_graphql/test_schema_builder.py
+++ b/packages/plugins/minos-router-graphql/tests/test_graphql/test_schema_builder.py
@@ -14,7 +14,6 @@
Response,
)
from minos.plugins.graphql import (
- GraphQlEnroute,
GraphQlHttpRouter,
GraphQLSchemaBuilder,
)
From 22915d7c01ca0292d69eea45671375a8aba36122 Mon Sep 17 00:00:00 2001
From: Vladyslav Fenchak
Date: Mon, 14 Mar 2022 21:15:11 +0100
Subject: [PATCH 23/44] ISSUE #149
---
.../plugins/minos-router-graphql/poetry.lock | 22 +++----------------
.../minos-router-graphql/pyproject.toml | 2 --
2 files changed, 3 insertions(+), 21 deletions(-)
diff --git a/packages/plugins/minos-router-graphql/poetry.lock b/packages/plugins/minos-router-graphql/poetry.lock
index 836ad91c6..1bfd569c3 100644
--- a/packages/plugins/minos-router-graphql/poetry.lock
+++ b/packages/plugins/minos-router-graphql/poetry.lock
@@ -18,18 +18,6 @@ yarl = ">=1.0,<2.0"
[package.extras]
speedups = ["aiodns", "brotli", "cchardet"]
-[[package]]
-name = "aiohttp-jinja2"
-version = "1.5"
-description = "jinja2 template renderer for aiohttp.web (http server for asyncio)"
-category = "main"
-optional = false
-python-versions = ">=3.6"
-
-[package.dependencies]
-aiohttp = ">=3.6.3"
-jinja2 = ">=3.0.0"
-
[[package]]
name = "aiomisc"
version = "15.7.2"
@@ -392,7 +380,7 @@ plugins = ["setuptools"]
name = "jinja2"
version = "3.0.3"
description = "A very fast and expressive template engine."
-category = "main"
+category = "dev"
optional = false
python-versions = ">=3.6"
@@ -426,7 +414,7 @@ mistune = "0.8.4"
name = "markupsafe"
version = "2.1.0"
description = "Safely add untrusted strings to HTML/XML markup."
-category = "main"
+category = "dev"
optional = false
python-versions = ">=3.7"
@@ -939,7 +927,7 @@ testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-
[metadata]
lock-version = "1.1"
python-versions = "^3.9"
-content-hash = "e7b55f719918838ac8d73e1ee1cf1df328f9c6d62e7bb835c897f447c755a214"
+content-hash = "3a311fef55d0ee3f97b74f9ef3d732eda18f8d20bc86df62882b3de3a81a6cba"
[metadata.files]
aiohttp = [
@@ -1016,10 +1004,6 @@ aiohttp = [
{file = "aiohttp-3.8.1-cp39-cp39-win_amd64.whl", hash = "sha256:1c182cb873bc91b411e184dab7a2b664d4fea2743df0e4d57402f7f3fa644bac"},
{file = "aiohttp-3.8.1.tar.gz", hash = "sha256:fc5471e1a54de15ef71c1bc6ebe80d4dc681ea600e68bfd1cbce40427f0b7578"},
]
-aiohttp-jinja2 = [
- {file = "aiohttp-jinja2-1.5.tar.gz", hash = "sha256:7c3ba5eac060b691f4e50534af2d79fca2a75712ebd2b25e6fcb1295859f910b"},
- {file = "aiohttp_jinja2-1.5-py3-none-any.whl", hash = "sha256:b55c0ed167b0cc4b6d6a50fb2299a44beb5dc4aec9df21305b91a5484694cf74"},
-]
aiomisc = [
{file = "aiomisc-15.7.2-py3-none-any.whl", hash = "sha256:e0b00145866b5c258a23e2c270bee2910f2562b078f6159d786a522699e8cfeb"},
{file = "aiomisc-15.7.2.tar.gz", hash = "sha256:fcc4517fa49056ec11ecd3ec5d341c554914c0a2d295c127b70583bab1ecab5b"},
diff --git a/packages/plugins/minos-router-graphql/pyproject.toml b/packages/plugins/minos-router-graphql/pyproject.toml
index 2fd5a3fa3..d0cfd45e4 100644
--- a/packages/plugins/minos-router-graphql/pyproject.toml
+++ b/packages/plugins/minos-router-graphql/pyproject.toml
@@ -34,8 +34,6 @@ python = "^3.9"
minos-microservice-common = "^0.5.0"
minos-microservice-networks = "^0.5.0"
aiohttp = "^3.8.1"
-aiohttp-jinja2 = "^1.5"
-Jinja2 = "^3.0.3"
graphql-core = "^3.2.0"
[tool.poetry.dev-dependencies]
From 3dc7e9f8248553d316a0b163a59e0465137cc3ff Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Sergio=20Garc=C3=ADa=20Prado?=
Date: Tue, 15 Mar 2022 09:00:32 +0100
Subject: [PATCH 24/44] ISSUE #149 * Add `pre-commit`.
---
.pre-commit-config.yaml | 7 +++++++
.../minos-router-graphql/.editorconfig | 21 -------------------
2 files changed, 7 insertions(+), 21 deletions(-)
delete mode 100644 packages/plugins/minos-router-graphql/.editorconfig
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index e4f94cbb1..ccb513194 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -50,3 +50,10 @@ repos:
name: Check minos-http-aiohttp
files: ^packages/plugins/minos-http-aiohttp/
language: system
+
+ - id: minos-router-graphql-check
+ pass_filenames: false
+ entry: make --directory=packages/plugins/minos-router-graphql check
+ name: Check minos-router-graphql
+ files: ^packages/plugins/minos-router-graphql/
+ language: system
diff --git a/packages/plugins/minos-router-graphql/.editorconfig b/packages/plugins/minos-router-graphql/.editorconfig
deleted file mode 100644
index d4a2c4405..000000000
--- a/packages/plugins/minos-router-graphql/.editorconfig
+++ /dev/null
@@ -1,21 +0,0 @@
-# http://editorconfig.org
-
-root = true
-
-[*]
-indent_style = space
-indent_size = 4
-trim_trailing_whitespace = true
-insert_final_newline = true
-charset = utf-8
-end_of_line = lf
-
-[*.bat]
-indent_style = tab
-end_of_line = crlf
-
-[LICENSE]
-insert_final_newline = false
-
-[Makefile]
-indent_style = tab
From fef1da3f72194558f471111f78a3f767da8a24c8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Sergio=20Garc=C3=ADa=20Prado?=
Date: Tue, 15 Mar 2022 09:37:49 +0100
Subject: [PATCH 25/44] ISSUE #149 * Be compatible with `latest` Router API
---
.../minos/plugins/graphql/handlers.py | 37 ++++++++
.../minos/plugins/graphql/routers.py | 92 +++----------------
.../minos/plugins/graphql/schema_builder.py | 7 +-
.../tests/test_graphql/test_routers.py | 16 ++--
.../tests/test_graphql/test_schema_builder.py | 25 ++---
5 files changed, 72 insertions(+), 105 deletions(-)
create mode 100644 packages/plugins/minos-router-graphql/minos/plugins/graphql/handlers.py
diff --git a/packages/plugins/minos-router-graphql/minos/plugins/graphql/handlers.py b/packages/plugins/minos-router-graphql/minos/plugins/graphql/handlers.py
new file mode 100644
index 000000000..57bb13576
--- /dev/null
+++ b/packages/plugins/minos-router-graphql/minos/plugins/graphql/handlers.py
@@ -0,0 +1,37 @@
+from graphql import (
+ graphql,
+ print_schema,
+)
+
+from minos.networks import (
+ HttpRequest,
+ HttpResponse,
+)
+
+
+class GraphQlHandler:
+ """TODO"""
+
+ def __init__(self, schema):
+ self._schema = schema
+
+ async def execute_operation(self, request: HttpRequest) -> HttpResponse:
+ """TODO"""
+
+ source = await request.content()
+
+ result = await graphql(schema=self._schema, source=source)
+ errors = result.errors
+ if errors is None:
+ errors = list()
+
+ if len(errors):
+ status = 500
+ else:
+ status = 200
+
+ return HttpResponse({"data": result.data, "errors": [err.message for err in errors]}, status=status)
+
+ async def get_schema(self, request: HttpRequest) -> HttpResponse:
+ """TODO"""
+ return HttpResponse(print_schema(self._schema))
diff --git a/packages/plugins/minos-router-graphql/minos/plugins/graphql/routers.py b/packages/plugins/minos-router-graphql/minos/plugins/graphql/routers.py
index 329dfb8e5..2edbe9b82 100644
--- a/packages/plugins/minos-router-graphql/minos/plugins/graphql/routers.py
+++ b/packages/plugins/minos-router-graphql/minos/plugins/graphql/routers.py
@@ -2,99 +2,31 @@
annotations,
)
-import json
from typing import (
Callable,
)
-from graphql import (
- GraphQLSchema,
- graphql,
- print_schema,
-)
-
-from minos.common import (
- MinosConfig,
-)
from minos.networks import (
- EnrouteBuilder,
+ EnrouteDecorator,
HttpEnrouteDecorator,
- HttpRequest,
- HttpResponse,
HttpRouter,
)
+from .handlers import (
+ GraphQlHandler,
+)
+from .schema_builder import (
+ GraphQLSchemaBuilder,
+)
+
class GraphQlHttpRouter(HttpRouter):
"""TODO"""
- def __init__(self, schema: GraphQLSchema, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self._schema = schema
-
- @classmethod
- def _from_config(cls, config: MinosConfig, **kwargs) -> GraphQlHttpRouter:
- builder = EnrouteBuilder(*config.services, middleware=config.middleware)
- routes = builder.get_all(config=config, **kwargs)
-
- from minos.plugins.graphql import (
- GraphQLSchemaBuilder,
- )
-
+ def _filter_routes(self, routes: dict[EnrouteDecorator, Callable]) -> dict[EnrouteDecorator, Callable]:
schema = GraphQLSchemaBuilder.build(routes)
-
- return cls(schema)
-
- @property
- def routes(self) -> dict[HttpEnrouteDecorator, Callable]:
- """TODO"""
+ handler = GraphQlHandler(schema)
return {
- HttpEnrouteDecorator("/graphql", "POST"): self.handle_post,
- HttpEnrouteDecorator("/graphql/schema", "GET"): self.handle_schema_get,
+ HttpEnrouteDecorator("/graphql", "POST"): handler.execute_operation,
+ HttpEnrouteDecorator("/graphql/schema", "GET"): handler.get_schema,
}
-
- async def handle_post(self, request: HttpRequest) -> HttpResponse:
- """TODO"""
- status = 200
- result = await self.execute_graphql()
-
- if result and result.errors:
- status = 500
-
- errors = result.errors
-
- if errors is None:
- errors = []
-
- return HttpResponse(
- {"data": result.data, "errors": [err.message for err in errors]},
- status=status,
- headers={"Access-Control-Allow-Origin": "*"},
- )
-
- async def execute_graphql(self):
- graphql_req = await self.graphql_request
- context_value = graphql_req.get("context", {})
- variables = graphql_req.get("variables", {})
-
- # context_value["application"] = self.app
- source = graphql_req["query"]
- result = await graphql(
- schema=self._schema,
- source=source,
- context_value=context_value,
- variable_values=variables,
- )
-
- return result
-
- @property
- async def graphql_request(self):
- if self.request.method == "GET":
- return json.loads(self.request.query["q"])
-
- return await self.request.json()
-
- async def handle_schema_get(self, request: HttpRequest) -> HttpResponse:
- """TODO"""
- return HttpResponse(print_schema(self._schema))
diff --git a/packages/plugins/minos-router-graphql/minos/plugins/graphql/schema_builder.py b/packages/plugins/minos-router-graphql/minos/plugins/graphql/schema_builder.py
index 29556868e..76ebc98ad 100644
--- a/packages/plugins/minos-router-graphql/minos/plugins/graphql/schema_builder.py
+++ b/packages/plugins/minos-router-graphql/minos/plugins/graphql/schema_builder.py
@@ -8,6 +8,9 @@
from inspect import (
isawaitable,
)
+from typing import (
+ Any,
+)
from graphql import (
GraphQLArgument,
@@ -42,8 +45,8 @@ def _build(cls, routes) -> dict:
@staticmethod
def adapt_callback(callback):
@wraps(callback)
- async def _wrapper(_source, _info, request):
- request = InMemoryRequest(request)
+ async def _wrapper(_source, _info, raw: Any = None):
+ request = InMemoryRequest(raw)
response = callback(request)
if isawaitable(response):
diff --git a/packages/plugins/minos-router-graphql/tests/test_graphql/test_routers.py b/packages/plugins/minos-router-graphql/tests/test_graphql/test_routers.py
index 08333caad..3d9487985 100644
--- a/packages/plugins/minos-router-graphql/tests/test_graphql/test_routers.py
+++ b/packages/plugins/minos-router-graphql/tests/test_graphql/test_routers.py
@@ -1,12 +1,11 @@
import unittest
-from graphql import (
- GraphQLSchema,
-)
-
from minos.common import (
MinosConfig,
)
+from minos.networks import (
+ HttpEnrouteDecorator,
+)
from minos.plugins.graphql import (
GraphQlHttpRouter,
)
@@ -15,15 +14,18 @@
)
-class TestSomething(unittest.TestCase):
+class TestGraphQlHttpRouter(unittest.TestCase):
CONFIG_FILE_PATH = BASE_PATH / "test_config.yml"
_config = MinosConfig(CONFIG_FILE_PATH)
def test_from_config(self):
- router = GraphQlHttpRouter.from_config(config=self._config)
+ router = GraphQlHttpRouter.from_config(self._config)
- self.assertIsInstance(router._schema, GraphQLSchema)
self.assertIsInstance(router, GraphQlHttpRouter)
+ self.assertEqual(
+ {HttpEnrouteDecorator("/graphql", "POST"), HttpEnrouteDecorator("/graphql/schema", "GET")},
+ router.routes.keys(),
+ )
if __name__ == "__main__":
diff --git a/packages/plugins/minos-router-graphql/tests/test_graphql/test_schema_builder.py b/packages/plugins/minos-router-graphql/tests/test_graphql/test_schema_builder.py
index e0d9278a3..940316e89 100644
--- a/packages/plugins/minos-router-graphql/tests/test_graphql/test_schema_builder.py
+++ b/packages/plugins/minos-router-graphql/tests/test_graphql/test_schema_builder.py
@@ -2,7 +2,7 @@
from graphql import (
GraphQLString,
- graphql_sync,
+ graphql,
validate_schema,
)
@@ -14,7 +14,6 @@
Response,
)
from minos.plugins.graphql import (
- GraphQlHttpRouter,
GraphQLSchemaBuilder,
)
from minos.plugins.graphql.decorators import (
@@ -30,7 +29,7 @@ async def callback_fn(request: Request):
return Response("ticket #4")
-class TestSomething(unittest.TestCase):
+class TestSchemaBuilder(unittest.IsolatedAsyncioTestCase):
CONFIG_FILE_PATH = BASE_PATH / "test_config.yml"
_config = MinosConfig(CONFIG_FILE_PATH)
@@ -52,7 +51,7 @@ def test_build(self):
errors = validate_schema(schema)
- self.assertEqual(errors, [])
+ self.assertEqual(list(), errors)
def test_build_only_queries(self):
routes = {
@@ -66,7 +65,7 @@ def test_build_only_queries(self):
errors = validate_schema(schema)
- self.assertEqual(errors, [])
+ self.assertEqual(list(), errors)
def test_build_only_commands(self):
routes = {
@@ -82,16 +81,9 @@ def test_build_only_commands(self):
errors = validate_schema(schema)
- self.assertEqual(errors, [])
+ self.assertEqual(list(), errors)
- def test_schema_valid(self):
- router = GraphQlHttpRouter.from_config(config=self._config)
-
- errors = validate_schema(router._schema)
-
- self.assertEqual(errors, [])
-
- def test_query(self):
+ async def test_query(self):
routes = {
GraphQlQueryEnrouteDecorator(name="order_query", argument=GraphQLString, output=GraphQLString): callback_fn
}
@@ -100,9 +92,10 @@ def test_query(self):
source = "{ order_query }"
- result = graphql_sync(schema, source)
+ result = await graphql(schema, source)
- self.assertEqual(result.errors, [])
+ self.assertEqual({"order_query": "ticket #4"}, result.data)
+ self.assertEqual(None, result.errors)
if __name__ == "__main__":
From d61b6b9dbede47f897850c594b6b63085c4b06be Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Sergio=20Garc=C3=ADa=20Prado?=
Date: Tue, 15 Mar 2022 09:42:19 +0100
Subject: [PATCH 26/44] ISSUE #149 * Update docs.
---
.../docs/_static/style.css | 3 ++
.../docs/_templates/layout.html | 4 +++
.../docs/{authors.rst => authors.md} | 0
.../plugins/minos-router-graphql/docs/conf.py | 8 ++---
.../docs/{history.rst => history.md} | 0
.../docs/{index.rst => index.md} | 6 ++--
.../minos-router-graphql/docs/make.bat | 36 -------------------
.../docs/{readme.rst => readme.md} | 3 +-
.../docs/{runthetests.rst => runthetests.md} | 0
.../minos-router-graphql/docs/usage.md | 1 +
.../minos-router-graphql/docs/usage.rst | 7 ----
11 files changed, 15 insertions(+), 53 deletions(-)
create mode 100644 packages/plugins/minos-router-graphql/docs/_static/style.css
create mode 100644 packages/plugins/minos-router-graphql/docs/_templates/layout.html
rename packages/plugins/minos-router-graphql/docs/{authors.rst => authors.md} (100%)
rename packages/plugins/minos-router-graphql/docs/{history.rst => history.md} (100%)
rename packages/plugins/minos-router-graphql/docs/{index.rst => index.md} (54%)
delete mode 100644 packages/plugins/minos-router-graphql/docs/make.bat
rename packages/plugins/minos-router-graphql/docs/{readme.rst => readme.md} (50%)
rename packages/plugins/minos-router-graphql/docs/{runthetests.rst => runthetests.md} (100%)
create mode 100644 packages/plugins/minos-router-graphql/docs/usage.md
delete mode 100644 packages/plugins/minos-router-graphql/docs/usage.rst
diff --git a/packages/plugins/minos-router-graphql/docs/_static/style.css b/packages/plugins/minos-router-graphql/docs/_static/style.css
new file mode 100644
index 000000000..8aa6c288f
--- /dev/null
+++ b/packages/plugins/minos-router-graphql/docs/_static/style.css
@@ -0,0 +1,3 @@
+.wy-nav-content {
+ max-width: 1200px !important;
+}
diff --git a/packages/plugins/minos-router-graphql/docs/_templates/layout.html b/packages/plugins/minos-router-graphql/docs/_templates/layout.html
new file mode 100644
index 000000000..b0a448060
--- /dev/null
+++ b/packages/plugins/minos-router-graphql/docs/_templates/layout.html
@@ -0,0 +1,4 @@
+{% extends "!layout.html" %}
+{% block extrahead %}
+
+{% endblock %}
\ No newline at end of file
diff --git a/packages/plugins/minos-router-graphql/docs/authors.rst b/packages/plugins/minos-router-graphql/docs/authors.md
similarity index 100%
rename from packages/plugins/minos-router-graphql/docs/authors.rst
rename to packages/plugins/minos-router-graphql/docs/authors.md
diff --git a/packages/plugins/minos-router-graphql/docs/conf.py b/packages/plugins/minos-router-graphql/docs/conf.py
index f17c29f90..8ac6c97e2 100755
--- a/packages/plugins/minos-router-graphql/docs/conf.py
+++ b/packages/plugins/minos-router-graphql/docs/conf.py
@@ -58,7 +58,7 @@
master_doc = "index"
# General information about the project.
-project = "Minos Rest Aiohttp"
+project = "Minos Router GraphQl"
copyright = "2021, Clariteia"
author = "Clariteia Devs"
@@ -147,7 +147,7 @@
# (source start file, target name, title, author, documentclass
# [howto, manual, or own class]).
latex_documents = [
- (master_doc, "minos.tex", "Minos Rest Aiohttp Documentation", "Clariteia Devs", "manual"),
+ (master_doc, "minos.tex", "Minos Router GraphQl Documentation", "Clariteia Devs", "manual"),
]
@@ -155,7 +155,7 @@
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
-man_pages = [(master_doc, "minos", "Minos Rest Aiohttp Documentation", [author], 1)]
+man_pages = [(master_doc, "minos", "Minos Router GraphQl Documentation", [author], 1)]
# -- Options for Texinfo output ----------------------------------------
@@ -167,7 +167,7 @@
(
master_doc,
"minos",
- "Minos Rest Aiohttp Documentation",
+ "Minos Router GraphQl Documentation",
author,
"minos",
"One line description of project.",
diff --git a/packages/plugins/minos-router-graphql/docs/history.rst b/packages/plugins/minos-router-graphql/docs/history.md
similarity index 100%
rename from packages/plugins/minos-router-graphql/docs/history.rst
rename to packages/plugins/minos-router-graphql/docs/history.md
diff --git a/packages/plugins/minos-router-graphql/docs/index.rst b/packages/plugins/minos-router-graphql/docs/index.md
similarity index 54%
rename from packages/plugins/minos-router-graphql/docs/index.rst
rename to packages/plugins/minos-router-graphql/docs/index.md
index c1b30a0fd..d62945fe1 100644
--- a/packages/plugins/minos-router-graphql/docs/index.rst
+++ b/packages/plugins/minos-router-graphql/docs/index.md
@@ -1,5 +1,4 @@
-Welcome to Minos Rest Aiohttp's documentation!
-======================================
+# Welcome to Minos Router GraphQl's documentation!
.. toctree::
:maxdepth: 2
@@ -11,8 +10,7 @@ Welcome to Minos Rest Aiohttp's documentation!
authors
history
-Indices and tables
-====================
+# Indices and tables
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
diff --git a/packages/plugins/minos-router-graphql/docs/make.bat b/packages/plugins/minos-router-graphql/docs/make.bat
deleted file mode 100644
index 916674840..000000000
--- a/packages/plugins/minos-router-graphql/docs/make.bat
+++ /dev/null
@@ -1,36 +0,0 @@
-@ECHO OFF
-
-pushd %~dp0
-
-REM Command file for Sphinx documentation
-
-if "%SPHINXBUILD%" == "" (
- set SPHINXBUILD=python -msphinx
-)
-set SOURCEDIR=.
-set BUILDDIR=_build
-set SPHINXPROJ=minos_rest_aiohttp
-
-if "%1" == "" goto help
-
-%SPHINXBUILD% >NUL 2>NUL
-if errorlevel 9009 (
- echo.
- echo.The Sphinx module was not found. Make sure you have Sphinx installed,
- echo.then set the SPHINXBUILD environment variable to point to the full
- echo.path of the 'sphinx-build' executable. Alternatively you may add the
- echo.Sphinx directory to PATH.
- echo.
- echo.If you don't have Sphinx installed, grab it from
- echo.http://sphinx-doc.org/
- exit /b 1
-)
-
-%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
-goto end
-
-:help
-%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
-
-:end
-popd
diff --git a/packages/plugins/minos-router-graphql/docs/readme.rst b/packages/plugins/minos-router-graphql/docs/readme.md
similarity index 50%
rename from packages/plugins/minos-router-graphql/docs/readme.rst
rename to packages/plugins/minos-router-graphql/docs/readme.md
index af09813a4..da72dbef1 100644
--- a/packages/plugins/minos-router-graphql/docs/readme.rst
+++ b/packages/plugins/minos-router-graphql/docs/readme.md
@@ -1,4 +1,3 @@
-Introduction
-**************
+# Introduction
.. mdinclude:: ../README.md
diff --git a/packages/plugins/minos-router-graphql/docs/runthetests.rst b/packages/plugins/minos-router-graphql/docs/runthetests.md
similarity index 100%
rename from packages/plugins/minos-router-graphql/docs/runthetests.rst
rename to packages/plugins/minos-router-graphql/docs/runthetests.md
diff --git a/packages/plugins/minos-router-graphql/docs/usage.md b/packages/plugins/minos-router-graphql/docs/usage.md
new file mode 100644
index 000000000..8f04b05ad
--- /dev/null
+++ b/packages/plugins/minos-router-graphql/docs/usage.md
@@ -0,0 +1 @@
+# Usage
diff --git a/packages/plugins/minos-router-graphql/docs/usage.rst b/packages/plugins/minos-router-graphql/docs/usage.rst
deleted file mode 100644
index 1f662b1ca..000000000
--- a/packages/plugins/minos-router-graphql/docs/usage.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-=====
-Usage
-=====
-
-To use Minos Rest Aiohttp in a project::
-
- import minos_rest_aiohttp
From a0a86d78c6e9adfa80f83233813fec0a549bc77d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Sergio=20Garc=C3=ADa=20Prado?=
Date: Tue, 15 Mar 2022 09:43:55 +0100
Subject: [PATCH 27/44] ISSUE #149 * Remove `aiohttp` from required
dependencies.
---
.../plugins/minos-router-graphql/poetry.lock | 40 +++++++++++++++++--
.../minos-router-graphql/pyproject.toml | 1 -
2 files changed, 36 insertions(+), 5 deletions(-)
diff --git a/packages/plugins/minos-router-graphql/poetry.lock b/packages/plugins/minos-router-graphql/poetry.lock
index 1bfd569c3..fa147869f 100644
--- a/packages/plugins/minos-router-graphql/poetry.lock
+++ b/packages/plugins/minos-router-graphql/poetry.lock
@@ -340,7 +340,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
[[package]]
name = "importlib-metadata"
-version = "4.11.2"
+version = "4.11.3"
description = "Read metadata from Python packages"
category = "dev"
optional = false
@@ -444,6 +444,7 @@ fastavro = "^1.4.0"
lmdb = "^1.2.1"
orjson = "^3.5.2"
PyYAML = ">=5.4.1,<7.0.0"
+uvloop = "^0.16.0"
[package.source]
type = "directory"
@@ -882,6 +883,19 @@ brotli = ["brotlipy (>=0.6.0)"]
secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"]
socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
+[[package]]
+name = "uvloop"
+version = "0.16.0"
+description = "Fast implementation of asyncio event loop on top of libuv"
+category = "main"
+optional = false
+python-versions = ">=3.7"
+
+[package.extras]
+dev = ["Cython (>=0.29.24,<0.30.0)", "pytest (>=3.6.0)", "Sphinx (>=4.1.2,<4.2.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)", "aiohttp", "flake8 (>=3.9.2,<3.10.0)", "psutil", "pycodestyle (>=2.7.0,<2.8.0)", "pyOpenSSL (>=19.0.0,<19.1.0)", "mypy (>=0.800)"]
+docs = ["Sphinx (>=4.1.2,<4.2.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)"]
+test = ["aiohttp", "flake8 (>=3.9.2,<3.10.0)", "psutil", "pycodestyle (>=2.7.0,<2.8.0)", "pyOpenSSL (>=19.0.0,<19.1.0)", "mypy (>=0.800)"]
+
[[package]]
name = "virtualenv"
version = "20.13.3"
@@ -927,7 +941,7 @@ testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-
[metadata]
lock-version = "1.1"
python-versions = "^3.9"
-content-hash = "3a311fef55d0ee3f97b74f9ef3d732eda18f8d20bc86df62882b3de3a81a6cba"
+content-hash = "da35fc32418385430fcb9d75cbbe109963f1dc2ef2ce3888697eb665f01c1ba1"
[metadata.files]
aiohttp = [
@@ -1285,8 +1299,8 @@ imagesize = [
{file = "imagesize-1.3.0.tar.gz", hash = "sha256:cd1750d452385ca327479d45b64d9c7729ecf0b3969a58148298c77092261f9d"},
]
importlib-metadata = [
- {file = "importlib_metadata-4.11.2-py3-none-any.whl", hash = "sha256:d16e8c1deb60de41b8e8ed21c1a7b947b0bc62fab7e1d470bcdf331cea2e6735"},
- {file = "importlib_metadata-4.11.2.tar.gz", hash = "sha256:b36ffa925fe3139b2f6ff11d6925ffd4fa7bc47870165e3ac260ac7b4f91e6ac"},
+ {file = "importlib_metadata-4.11.3-py3-none-any.whl", hash = "sha256:1208431ca90a8cca1a6b8af391bb53c1a2db74e5d1cef6ddced95d4b2062edc6"},
+ {file = "importlib_metadata-4.11.3.tar.gz", hash = "sha256:ea4c597ebf37142f827b8f39299579e31685c31d3a438b59f469406afd0f2539"},
]
iniconfig = [
{file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"},
@@ -1701,6 +1715,24 @@ urllib3 = [
{file = "urllib3-1.26.8-py2.py3-none-any.whl", hash = "sha256:000ca7f471a233c2251c6c7023ee85305721bfdf18621ebff4fd17a8653427ed"},
{file = "urllib3-1.26.8.tar.gz", hash = "sha256:0e7c33d9a63e7ddfcb86780aac87befc2fbddf46c58dbb487e0855f7ceec283c"},
]
+uvloop = [
+ {file = "uvloop-0.16.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6224f1401025b748ffecb7a6e2652b17768f30b1a6a3f7b44660e5b5b690b12d"},
+ {file = "uvloop-0.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:30ba9dcbd0965f5c812b7c2112a1ddf60cf904c1c160f398e7eed3a6b82dcd9c"},
+ {file = "uvloop-0.16.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:bd53f7f5db562f37cd64a3af5012df8cac2c464c97e732ed556800129505bd64"},
+ {file = "uvloop-0.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:772206116b9b57cd625c8a88f2413df2fcfd0b496eb188b82a43bed7af2c2ec9"},
+ {file = "uvloop-0.16.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b572256409f194521a9895aef274cea88731d14732343da3ecdb175228881638"},
+ {file = "uvloop-0.16.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:04ff57aa137230d8cc968f03481176041ae789308b4d5079118331ab01112450"},
+ {file = "uvloop-0.16.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a19828c4f15687675ea912cc28bbcb48e9bb907c801873bd1519b96b04fb805"},
+ {file = "uvloop-0.16.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e814ac2c6f9daf4c36eb8e85266859f42174a4ff0d71b99405ed559257750382"},
+ {file = "uvloop-0.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bd8f42ea1ea8f4e84d265769089964ddda95eb2bb38b5cbe26712b0616c3edee"},
+ {file = "uvloop-0.16.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:647e481940379eebd314c00440314c81ea547aa636056f554d491e40503c8464"},
+ {file = "uvloop-0.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e0d26fa5875d43ddbb0d9d79a447d2ace4180d9e3239788208527c4784f7cab"},
+ {file = "uvloop-0.16.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:6ccd57ae8db17d677e9e06192e9c9ec4bd2066b77790f9aa7dede2cc4008ee8f"},
+ {file = "uvloop-0.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:089b4834fd299d82d83a25e3335372f12117a7d38525217c2258e9b9f4578897"},
+ {file = "uvloop-0.16.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98d117332cc9e5ea8dfdc2b28b0a23f60370d02e1395f88f40d1effd2cb86c4f"},
+ {file = "uvloop-0.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e5f2e2ff51aefe6c19ee98af12b4ae61f5be456cd24396953244a30880ad861"},
+ {file = "uvloop-0.16.0.tar.gz", hash = "sha256:f74bc20c7b67d1c27c72601c78cf95be99d5c2cdd4514502b4f3eb0933ff1228"},
+]
virtualenv = [
{file = "virtualenv-20.13.3-py2.py3-none-any.whl", hash = "sha256:dd448d1ded9f14d1a4bfa6bfc0c5b96ae3be3f2d6c6c159b23ddcfd701baa021"},
{file = "virtualenv-20.13.3.tar.gz", hash = "sha256:e9dd1a1359d70137559034c0f5433b34caf504af2dc756367be86a5a32967134"},
diff --git a/packages/plugins/minos-router-graphql/pyproject.toml b/packages/plugins/minos-router-graphql/pyproject.toml
index d0cfd45e4..97dbd0367 100644
--- a/packages/plugins/minos-router-graphql/pyproject.toml
+++ b/packages/plugins/minos-router-graphql/pyproject.toml
@@ -33,7 +33,6 @@ include = [
python = "^3.9"
minos-microservice-common = "^0.5.0"
minos-microservice-networks = "^0.5.0"
-aiohttp = "^3.8.1"
graphql-core = "^3.2.0"
[tool.poetry.dev-dependencies]
From 5fd824c0cc13f2a16065545f72e68a6b8212d5d1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Sergio=20Garc=C3=ADa=20Prado?=
Date: Tue, 15 Mar 2022 09:56:57 +0100
Subject: [PATCH 28/44] ISSUE #149 * Fix documentation bug.
---
README.md | 1 +
packages/plugins/minos-router-graphql/docs/index.md | 2 +-
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 0cf6c90fc..f94c5f13d 100644
--- a/README.md
+++ b/README.md
@@ -1157,6 +1157,7 @@ The plugin packages provide connectors to external technologies like brokers, di
* [minos-broker-kafka](https://minos-framework.github.io/minos-python/packages/plugins/minos-broker-kafka): The `kafka` plugin package.
* [minos-discovery-minos](https://minos-framework.github.io/minos-python/packages/plugins/minos-discovery-minos): The `minos-discovery` plugin package.
* [minos-http-aiohttp](https://minos-framework.github.io/minos-python/packages/plugins/minos-http-aiohttp): The `aiohttp` plugin package.
+* * [minos-router-graphql](https://minos-framework.github.io/minos-python/packages/plugins/minos-router-graphql): The `grapqhl` plugin package.
## Source Code
diff --git a/packages/plugins/minos-router-graphql/docs/index.md b/packages/plugins/minos-router-graphql/docs/index.md
index d62945fe1..038a73eb8 100644
--- a/packages/plugins/minos-router-graphql/docs/index.md
+++ b/packages/plugins/minos-router-graphql/docs/index.md
@@ -6,7 +6,7 @@
readme
runthetests
usage
- api/minos
+ api/minos.plugins
authors
history
From 55a0b6c03e19a4ae6cf9bd545158d33dac59cc57 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Sergio=20Garc=C3=ADa=20Prado?=
Date: Tue, 15 Mar 2022 09:58:25 +0100
Subject: [PATCH 29/44] ISSUE #149 * Fix bug related with coverage publishing.
---
.github/workflows/minos-router-graphql-tests.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/minos-router-graphql-tests.yml b/.github/workflows/minos-router-graphql-tests.yml
index 54c26751e..e830f4370 100644
--- a/.github/workflows/minos-router-graphql-tests.yml
+++ b/.github/workflows/minos-router-graphql-tests.yml
@@ -56,7 +56,7 @@ jobs:
uses: codecov/codecov-action@v2
with:
token: ${{ secrets.CODECOV_TOKEN }}
- files: ./packages/plugins/minos-http-aiohttp/coverage.xml
+ files: ./packages/plugins/minos-router-graphql/coverage.xml
fail_ci_if_error: true
- name: Generate documentation
From 4d822c159b9e094cf8e5964c14bba7e06b4827bc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Sergio=20Garc=C3=ADa=20Prado?=
Date: Tue, 15 Mar 2022 10:03:13 +0100
Subject: [PATCH 30/44] ISSUE #149 * Add `builders` module.
---
.../minos-router-graphql/minos/plugins/graphql/__init__.py | 6 +++---
.../minos/plugins/graphql/builders/__init__.py | 3 +++
.../graphql/{schema_builder.py => builders/schema.py} | 5 +++++
.../minos-router-graphql/minos/plugins/graphql/routers.py | 6 +++---
.../tests/test_graphql/test_builders/__init__.py | 0
.../test_schema.py} | 0
6 files changed, 14 insertions(+), 6 deletions(-)
create mode 100644 packages/plugins/minos-router-graphql/minos/plugins/graphql/builders/__init__.py
rename packages/plugins/minos-router-graphql/minos/plugins/graphql/{schema_builder.py => builders/schema.py} (97%)
create mode 100644 packages/plugins/minos-router-graphql/tests/test_graphql/test_builders/__init__.py
rename packages/plugins/minos-router-graphql/tests/test_graphql/{test_schema_builder.py => test_builders/test_schema.py} (100%)
diff --git a/packages/plugins/minos-router-graphql/minos/plugins/graphql/__init__.py b/packages/plugins/minos-router-graphql/minos/plugins/graphql/__init__.py
index 3ffd178ce..37578be32 100644
--- a/packages/plugins/minos-router-graphql/minos/plugins/graphql/__init__.py
+++ b/packages/plugins/minos-router-graphql/minos/plugins/graphql/__init__.py
@@ -2,15 +2,15 @@
__email__ = "hey@minos.run"
__version__ = "0.6.0"
+from .builders import (
+ GraphQLSchemaBuilder,
+)
from .decorators import (
GraphQlEnroute,
)
from .routers import (
GraphQlHttpRouter,
)
-from .schema_builder import (
- GraphQLSchemaBuilder,
-)
def _register_enroute():
diff --git a/packages/plugins/minos-router-graphql/minos/plugins/graphql/builders/__init__.py b/packages/plugins/minos-router-graphql/minos/plugins/graphql/builders/__init__.py
new file mode 100644
index 000000000..1bc5fda51
--- /dev/null
+++ b/packages/plugins/minos-router-graphql/minos/plugins/graphql/builders/__init__.py
@@ -0,0 +1,3 @@
+from .schema import (
+ GraphQLSchemaBuilder,
+)
diff --git a/packages/plugins/minos-router-graphql/minos/plugins/graphql/schema_builder.py b/packages/plugins/minos-router-graphql/minos/plugins/graphql/builders/schema.py
similarity index 97%
rename from packages/plugins/minos-router-graphql/minos/plugins/graphql/schema_builder.py
rename to packages/plugins/minos-router-graphql/minos/plugins/graphql/builders/schema.py
index 76ebc98ad..3680983db 100644
--- a/packages/plugins/minos-router-graphql/minos/plugins/graphql/schema_builder.py
+++ b/packages/plugins/minos-router-graphql/minos/plugins/graphql/builders/schema.py
@@ -27,11 +27,14 @@
class GraphQLSchemaBuilder:
+ """TODO"""
+
def __init__(self, *args, **kwargs):
self.schema = GraphQLSchema(**kwargs)
@classmethod
def build(cls, routes) -> GraphQLSchema:
+ """TODO"""
schema_args = cls._build(routes)
return cls(**schema_args).schema
@@ -44,6 +47,8 @@ def _build(cls, routes) -> dict:
@staticmethod
def adapt_callback(callback):
+ """TODO"""
+
@wraps(callback)
async def _wrapper(_source, _info, raw: Any = None):
request = InMemoryRequest(raw)
diff --git a/packages/plugins/minos-router-graphql/minos/plugins/graphql/routers.py b/packages/plugins/minos-router-graphql/minos/plugins/graphql/routers.py
index 2edbe9b82..3c216f829 100644
--- a/packages/plugins/minos-router-graphql/minos/plugins/graphql/routers.py
+++ b/packages/plugins/minos-router-graphql/minos/plugins/graphql/routers.py
@@ -12,12 +12,12 @@
HttpRouter,
)
+from .builders import (
+ GraphQLSchemaBuilder,
+)
from .handlers import (
GraphQlHandler,
)
-from .schema_builder import (
- GraphQLSchemaBuilder,
-)
class GraphQlHttpRouter(HttpRouter):
diff --git a/packages/plugins/minos-router-graphql/tests/test_graphql/test_builders/__init__.py b/packages/plugins/minos-router-graphql/tests/test_graphql/test_builders/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/packages/plugins/minos-router-graphql/tests/test_graphql/test_schema_builder.py b/packages/plugins/minos-router-graphql/tests/test_graphql/test_builders/test_schema.py
similarity index 100%
rename from packages/plugins/minos-router-graphql/tests/test_graphql/test_schema_builder.py
rename to packages/plugins/minos-router-graphql/tests/test_graphql/test_builders/test_schema.py
From 2b7fdb05a88f060b7007677a07fc49a7ca8c638a Mon Sep 17 00:00:00 2001
From: Vladyslav Fenchak
Date: Tue, 15 Mar 2022 13:31:27 +0100
Subject: [PATCH 31/44] ISSUE #149
---
.../minos/plugins/graphql/builders/schema.py | 5 +-
.../minos/plugins/graphql/decorators.py | 3 +-
.../minos/plugins/graphql/handlers.py | 3 +-
.../test_graphql/test_builders/test_schema.py | 36 ++++
.../tests/test_graphql/test_handlers.py | 161 ++++++++++++++++++
5 files changed, 205 insertions(+), 3 deletions(-)
create mode 100644 packages/plugins/minos-router-graphql/tests/test_graphql/test_handlers.py
diff --git a/packages/plugins/minos-router-graphql/minos/plugins/graphql/builders/schema.py b/packages/plugins/minos-router-graphql/minos/plugins/graphql/builders/schema.py
index 3680983db..c1a2685ed 100644
--- a/packages/plugins/minos-router-graphql/minos/plugins/graphql/builders/schema.py
+++ b/packages/plugins/minos-router-graphql/minos/plugins/graphql/builders/schema.py
@@ -94,4 +94,7 @@ def _build_mutations(cls, routes):
@staticmethod
def _build_field(item, callback):
- return GraphQLField(item.output, args={"request": GraphQLArgument(item.argument)}, resolve=callback)
+ args = None
+ if item.argument is not None:
+ args = {"request": GraphQLArgument(item.argument)}
+ return GraphQLField(item.output, args=args, resolve=callback)
diff --git a/packages/plugins/minos-router-graphql/minos/plugins/graphql/decorators.py b/packages/plugins/minos-router-graphql/minos/plugins/graphql/decorators.py
index c48258acc..1c83e0e8e 100644
--- a/packages/plugins/minos-router-graphql/minos/plugins/graphql/decorators.py
+++ b/packages/plugins/minos-router-graphql/minos/plugins/graphql/decorators.py
@@ -6,6 +6,7 @@
)
from typing import (
Final,
+ Optional,
)
from minos.networks import (
@@ -18,7 +19,7 @@
class GraphQlEnrouteDecorator(EnrouteDecorator, ABC):
"""GraphQl Enroute class"""
- def __init__(self, name: str, argument, output):
+ def __init__(self, name: str, output, argument: Optional = None):
self.name = name
self.argument = argument
self.output = output
diff --git a/packages/plugins/minos-router-graphql/minos/plugins/graphql/handlers.py b/packages/plugins/minos-router-graphql/minos/plugins/graphql/handlers.py
index 57bb13576..49248e06a 100644
--- a/packages/plugins/minos-router-graphql/minos/plugins/graphql/handlers.py
+++ b/packages/plugins/minos-router-graphql/minos/plugins/graphql/handlers.py
@@ -1,4 +1,5 @@
from graphql import (
+ GraphQLSchema,
graphql,
print_schema,
)
@@ -12,7 +13,7 @@
class GraphQlHandler:
"""TODO"""
- def __init__(self, schema):
+ def __init__(self, schema: GraphQLSchema):
self._schema = schema
async def execute_operation(self, request: HttpRequest) -> HttpResponse:
diff --git a/packages/plugins/minos-router-graphql/tests/test_graphql/test_builders/test_schema.py b/packages/plugins/minos-router-graphql/tests/test_graphql/test_builders/test_schema.py
index 940316e89..f7ddf303b 100644
--- a/packages/plugins/minos-router-graphql/tests/test_graphql/test_builders/test_schema.py
+++ b/packages/plugins/minos-router-graphql/tests/test_graphql/test_builders/test_schema.py
@@ -67,6 +67,18 @@ def test_build_only_queries(self):
self.assertEqual(list(), errors)
+ def test_build_queries_without_arguments(self):
+ routes = {
+ GraphQlQueryEnrouteDecorator(name="order_query", output=GraphQLString): callback_fn,
+ GraphQlQueryEnrouteDecorator(name="ticket_query", output=GraphQLString): callback_fn,
+ }
+
+ schema = GraphQLSchemaBuilder.build(routes=routes)
+
+ errors = validate_schema(schema)
+
+ self.assertEqual(list(), errors)
+
def test_build_only_commands(self):
routes = {
GraphQlCommandEnrouteDecorator(
@@ -83,6 +95,18 @@ def test_build_only_commands(self):
self.assertEqual(list(), errors)
+ def test_build_commands_without_arguments(self):
+ routes = {
+ GraphQlCommandEnrouteDecorator(name="order_command", output=GraphQLString): callback_fn,
+ GraphQlCommandEnrouteDecorator(name="ticket_command", output=GraphQLString): callback_fn,
+ }
+
+ schema = GraphQLSchemaBuilder.build(routes=routes)
+
+ errors = validate_schema(schema)
+
+ self.assertEqual(list(), errors)
+
async def test_query(self):
routes = {
GraphQlQueryEnrouteDecorator(name="order_query", argument=GraphQLString, output=GraphQLString): callback_fn
@@ -97,6 +121,18 @@ async def test_query(self):
self.assertEqual({"order_query": "ticket #4"}, result.data)
self.assertEqual(None, result.errors)
+ async def test_query_without_arguments(self):
+ routes = {GraphQlQueryEnrouteDecorator(name="order_query", output=GraphQLString): callback_fn}
+
+ schema = GraphQLSchemaBuilder.build(routes=routes)
+
+ source = "{ order_query }"
+
+ result = await graphql(schema, source)
+
+ self.assertEqual({"order_query": "ticket #4"}, result.data)
+ self.assertEqual(None, result.errors)
+
if __name__ == "__main__":
unittest.main()
diff --git a/packages/plugins/minos-router-graphql/tests/test_graphql/test_handlers.py b/packages/plugins/minos-router-graphql/tests/test_graphql/test_handlers.py
new file mode 100644
index 000000000..f42754386
--- /dev/null
+++ b/packages/plugins/minos-router-graphql/tests/test_graphql/test_handlers.py
@@ -0,0 +1,161 @@
+import json
+import unittest
+from typing import (
+ NamedTuple,
+ Optional,
+)
+
+from graphql import (
+ GraphQLArgument,
+ GraphQLBoolean,
+ GraphQLField,
+ GraphQLID,
+ GraphQLInt,
+ GraphQLNonNull,
+ GraphQLObjectType,
+ GraphQLString,
+)
+
+from minos.common import (
+ MinosConfig,
+)
+from minos.networks import (
+ InMemoryRequest,
+ Request,
+ Response,
+)
+from minos.plugins.graphql import (
+ GraphQLSchemaBuilder,
+)
+from minos.plugins.graphql.decorators import (
+ GraphQlQueryEnrouteDecorator,
+)
+from minos.plugins.graphql.handlers import (
+ GraphQlHandler,
+)
+from tests.test_graphql.test_builders.test_schema import (
+ callback_fn,
+)
+from tests.utils import (
+ BASE_PATH,
+)
+
+
+class User(NamedTuple):
+ """A simple user object class."""
+
+ firstName: str
+ lastName: str
+ tweets: Optional[int]
+ id: Optional[str] = None
+ verified: bool = False
+
+
+async def resolve_ticket(request: Request):
+ return Response(3)
+
+
+user_type = GraphQLObjectType(
+ "UserType",
+ {
+ "id": GraphQLField(GraphQLNonNull(GraphQLID)),
+ "firstName": GraphQLField(GraphQLNonNull(GraphQLString)),
+ "lastName": GraphQLField(GraphQLNonNull(GraphQLString)),
+ "tweets": GraphQLField(GraphQLInt),
+ "verified": GraphQLField(GraphQLNonNull(GraphQLBoolean)),
+ },
+)
+
+
+class TestGraphQlHttpRouter(unittest.IsolatedAsyncioTestCase):
+ CONFIG_FILE_PATH = BASE_PATH / "test_config.yml"
+ _config = MinosConfig(CONFIG_FILE_PATH)
+
+ async def test_execute_operation(self):
+ routes = {
+ GraphQlQueryEnrouteDecorator(name="order_query", output=GraphQLString): callback_fn,
+ GraphQlQueryEnrouteDecorator(name="ticket_query", output=GraphQLString): callback_fn,
+ }
+
+ schema = GraphQLSchemaBuilder.build(routes=routes)
+
+ handler = GraphQlHandler(schema)
+
+ request = InMemoryRequest(content="{ order_query }")
+
+ result = await handler.execute_operation(request)
+
+ self.assertDictEqual(await result.content(), {"data": {"order_query": "ticket #4"}, "errors": []})
+
+ async def test_execute_wrong_operation(self):
+ routes = {
+ GraphQlQueryEnrouteDecorator(name="order_query", output=GraphQLString): callback_fn,
+ GraphQlQueryEnrouteDecorator(name="ticket_query", output=GraphQLString): callback_fn,
+ }
+
+ schema = GraphQLSchemaBuilder.build(routes=routes)
+
+ handler = GraphQlHandler(schema)
+
+ request = InMemoryRequest(content="{ fictitious_query }")
+
+ result = await handler.execute_operation(request)
+
+ content = await result.content()
+
+ self.assertNotEquals(content["errors"], [])
+
+ async def test_schema(self):
+ routes = {
+ GraphQlQueryEnrouteDecorator(name="order_query", output=GraphQLString): callback_fn,
+ GraphQlQueryEnrouteDecorator(name="ticket_query", output=GraphQLString): callback_fn,
+ }
+
+ schema = GraphQLSchemaBuilder.build(routes=routes)
+
+ handler = GraphQlHandler(schema)
+
+ request = InMemoryRequest(content="Some content")
+
+ result = await handler.get_schema(request)
+
+ self.assertIsInstance(await result.content(), str)
+ self.assertMultiLineEqual(
+ await result.content(), "type Query {\n order_query: String\n ticket_query: String\n}"
+ )
+
+ async def operation_with_variables(self):
+ routes = {
+ GraphQlQueryEnrouteDecorator(
+ name="order_query", argument=GraphQLArgument(GraphQLID), output=user_type
+ ): callback_fn
+ }
+
+ schema = GraphQLSchemaBuilder.build(routes=routes)
+
+ handler = GraphQlHandler(schema)
+
+ query = """
+ query ($userId: ID!) {
+ User(id: $userId) {
+ id, firstName, lastName, tweets, verified
+ }
+ }
+ """
+
+ variables = {"userId": 3}
+
+ content = {"query": query, "variables": variables}
+
+ request = InMemoryRequest(content=json.dumps(content))
+
+ result = await handler.get_schema(request)
+
+ self.assertIsInstance(await result.content(), str)
+ self.assertMultiLineEqual(
+ await result.content(), "type Query {\n order_query: String\n ticket_query: String\n}"
+ )
+
+
+if __name__ == "__main__":
+ unittest.main()
From f395a2ba14ee03889cc820dba87109b394a993fb Mon Sep 17 00:00:00 2001
From: Vladyslav Fenchak
Date: Tue, 15 Mar 2022 17:09:23 +0100
Subject: [PATCH 32/44] ISSUE #149
---
.../minos/plugins/graphql/builders/schema.py | 4 +-
.../minos/plugins/graphql/handlers.py | 20 ++-
.../test_graphql/test_builders/test_schema.py | 11 ++
.../tests/test_graphql/test_handlers.py | 137 ++++++++++++------
.../minos-router-graphql/tests/utils.py | 58 ++++++++
5 files changed, 180 insertions(+), 50 deletions(-)
diff --git a/packages/plugins/minos-router-graphql/minos/plugins/graphql/builders/schema.py b/packages/plugins/minos-router-graphql/minos/plugins/graphql/builders/schema.py
index c1a2685ed..da207fbcf 100644
--- a/packages/plugins/minos-router-graphql/minos/plugins/graphql/builders/schema.py
+++ b/packages/plugins/minos-router-graphql/minos/plugins/graphql/builders/schema.py
@@ -50,8 +50,8 @@ def adapt_callback(callback):
"""TODO"""
@wraps(callback)
- async def _wrapper(_source, _info, raw: Any = None):
- request = InMemoryRequest(raw)
+ async def _wrapper(_source, _info, request: Any = None):
+ request = InMemoryRequest(request)
response = callback(request)
if isawaitable(response):
diff --git a/packages/plugins/minos-router-graphql/minos/plugins/graphql/handlers.py b/packages/plugins/minos-router-graphql/minos/plugins/graphql/handlers.py
index 49248e06a..632b29189 100644
--- a/packages/plugins/minos-router-graphql/minos/plugins/graphql/handlers.py
+++ b/packages/plugins/minos-router-graphql/minos/plugins/graphql/handlers.py
@@ -19,15 +19,29 @@ def __init__(self, schema: GraphQLSchema):
async def execute_operation(self, request: HttpRequest) -> HttpResponse:
"""TODO"""
- source = await request.content()
+ content = await request.content()
+
+ source = dict()
+ variables = dict()
+
+ if type(content) == str:
+ source = content
+
+ if type(content) == dict:
+ if "query" in content:
+ source = content["query"]
+
+ if "variables" in content:
+ variables = content["variables"]
+
+ result = await graphql(schema=self._schema, source=source, variable_values=variables)
- result = await graphql(schema=self._schema, source=source)
errors = result.errors
if errors is None:
errors = list()
if len(errors):
- status = 500
+ status = 400
else:
status = 200
diff --git a/packages/plugins/minos-router-graphql/tests/test_graphql/test_builders/test_schema.py b/packages/plugins/minos-router-graphql/tests/test_graphql/test_builders/test_schema.py
index f7ddf303b..6267438bc 100644
--- a/packages/plugins/minos-router-graphql/tests/test_graphql/test_builders/test_schema.py
+++ b/packages/plugins/minos-router-graphql/tests/test_graphql/test_builders/test_schema.py
@@ -1,6 +1,7 @@
import unittest
from graphql import (
+ GraphQLID,
GraphQLString,
graphql,
validate_schema,
@@ -22,6 +23,7 @@
)
from tests.utils import (
BASE_PATH,
+ user_type,
)
@@ -133,6 +135,15 @@ async def test_query_without_arguments(self):
self.assertEqual({"order_query": "ticket #4"}, result.data)
self.assertEqual(None, result.errors)
+ async def test_schema(self):
+ routes = {GraphQlQueryEnrouteDecorator(name="order_query", argument=GraphQLID, output=user_type): callback_fn}
+
+ schema = GraphQLSchemaBuilder.build(routes=routes)
+
+ errors = validate_schema(schema)
+
+ self.assertEqual(list(), errors)
+
if __name__ == "__main__":
unittest.main()
diff --git a/packages/plugins/minos-router-graphql/tests/test_graphql/test_handlers.py b/packages/plugins/minos-router-graphql/tests/test_graphql/test_handlers.py
index f42754386..2ae5e6be0 100644
--- a/packages/plugins/minos-router-graphql/tests/test_graphql/test_handlers.py
+++ b/packages/plugins/minos-router-graphql/tests/test_graphql/test_handlers.py
@@ -1,18 +1,8 @@
-import json
import unittest
-from typing import (
- NamedTuple,
- Optional,
-)
from graphql import (
- GraphQLArgument,
- GraphQLBoolean,
- GraphQLField,
- GraphQLID,
GraphQLInt,
GraphQLNonNull,
- GraphQLObjectType,
GraphQLString,
)
@@ -28,6 +18,7 @@
GraphQLSchemaBuilder,
)
from minos.plugins.graphql.decorators import (
+ GraphQlCommandEnrouteDecorator,
GraphQlQueryEnrouteDecorator,
)
from minos.plugins.graphql.handlers import (
@@ -38,35 +29,17 @@
)
from tests.utils import (
BASE_PATH,
+ resolve_create_user,
+ resolve_user,
+ user_input_type,
+ user_type,
)
-class User(NamedTuple):
- """A simple user object class."""
-
- firstName: str
- lastName: str
- tweets: Optional[int]
- id: Optional[str] = None
- verified: bool = False
-
-
async def resolve_ticket(request: Request):
return Response(3)
-user_type = GraphQLObjectType(
- "UserType",
- {
- "id": GraphQLField(GraphQLNonNull(GraphQLID)),
- "firstName": GraphQLField(GraphQLNonNull(GraphQLString)),
- "lastName": GraphQLField(GraphQLNonNull(GraphQLString)),
- "tweets": GraphQLField(GraphQLInt),
- "verified": GraphQLField(GraphQLNonNull(GraphQLBoolean)),
- },
-)
-
-
class TestGraphQlHttpRouter(unittest.IsolatedAsyncioTestCase):
CONFIG_FILE_PATH = BASE_PATH / "test_config.yml"
_config = MinosConfig(CONFIG_FILE_PATH)
@@ -103,7 +76,7 @@ async def test_execute_wrong_operation(self):
content = await result.content()
- self.assertNotEquals(content["errors"], [])
+ self.assertNotEqual(content["errors"], [])
async def test_schema(self):
routes = {
@@ -124,11 +97,9 @@ async def test_schema(self):
await result.content(), "type Query {\n order_query: String\n ticket_query: String\n}"
)
- async def operation_with_variables(self):
+ async def test_query_with_variables(self):
routes = {
- GraphQlQueryEnrouteDecorator(
- name="order_query", argument=GraphQLArgument(GraphQLID), output=user_type
- ): callback_fn
+ GraphQlQueryEnrouteDecorator(name="order_query", argument=GraphQLInt, output=GraphQLInt): resolve_ticket
}
schema = GraphQLSchemaBuilder.build(routes=routes)
@@ -136,9 +107,39 @@ async def operation_with_variables(self):
handler = GraphQlHandler(schema)
query = """
- query ($userId: ID!) {
- User(id: $userId) {
- id, firstName, lastName, tweets, verified
+ query ($userId: Int!) {
+ order_query(request: $userId)
+ }
+ """
+
+ variables = {"userId": 3}
+
+ content = {"query": query, "variables": variables}
+
+ request = InMemoryRequest(content=content)
+
+ result = await handler.execute_operation(request)
+
+ content = await result.content()
+
+ self.assertDictEqual({"order_query": 3}, content["data"])
+ self.assertEqual([], content["errors"])
+
+ async def test_query_with_variables_return_user(self):
+ routes = {GraphQlQueryEnrouteDecorator(name="order_query", argument=GraphQLInt, output=user_type): resolve_user}
+
+ schema = GraphQLSchemaBuilder.build(routes=routes)
+
+ handler = GraphQlHandler(schema)
+
+ query = """
+ query ($userId: Int!) {
+ order_query(request: $userId) {
+ id
+ firstName
+ lastName
+ tweets
+ verified
}
}
"""
@@ -147,14 +148,60 @@ async def operation_with_variables(self):
content = {"query": query, "variables": variables}
- request = InMemoryRequest(content=json.dumps(content))
+ request = InMemoryRequest(content=content)
- result = await handler.get_schema(request)
+ result = await handler.execute_operation(request)
- self.assertIsInstance(await result.content(), str)
- self.assertMultiLineEqual(
- await result.content(), "type Query {\n order_query: String\n ticket_query: String\n}"
+ content = await result.content()
+
+ self.assertDictEqual(
+ {"order_query": {"id": "3", "firstName": "Jack", "lastName": "Johnson", "tweets": 563, "verified": True}},
+ content["data"],
+ )
+ self.assertEqual([], content["errors"])
+
+ async def test_mutation(self):
+ routes = {
+ GraphQlCommandEnrouteDecorator(
+ name="createUser", argument=GraphQLNonNull(user_input_type), output=user_type
+ ): resolve_create_user
+ }
+
+ schema = GraphQLSchemaBuilder.build(routes=routes)
+
+ handler = GraphQlHandler(schema)
+
+ query = """
+ mutation ($userData: UserInputType!) {
+ createUser(request: $userData) {
+ id, firstName, lastName, tweets, verified
+ }
+ }
+ """
+
+ variables = {"userData": dict(firstName="John", lastName="Doe", tweets=42, verified=True)}
+
+ content = {"query": query, "variables": variables}
+
+ request = InMemoryRequest(content=content)
+
+ result = await handler.execute_operation(request)
+
+ content = await result.content()
+
+ self.assertDictEqual(
+ {
+ "createUser": {
+ "id": "4kjjj43-l23k4l3-325kgaa2",
+ "firstName": "John",
+ "lastName": "Doe",
+ "tweets": 42,
+ "verified": True,
+ }
+ },
+ content["data"],
)
+ self.assertEqual([], content["errors"])
if __name__ == "__main__":
diff --git a/packages/plugins/minos-router-graphql/tests/utils.py b/packages/plugins/minos-router-graphql/tests/utils.py
index 5a59ee2f0..1963f10da 100644
--- a/packages/plugins/minos-router-graphql/tests/utils.py
+++ b/packages/plugins/minos-router-graphql/tests/utils.py
@@ -6,11 +6,19 @@
)
from typing import (
Any,
+ NamedTuple,
+ Optional,
)
from graphql import (
GraphQLArgument,
+ GraphQLBoolean,
GraphQLField,
+ GraphQLID,
+ GraphQLInputField,
+ GraphQLInputObjectType,
+ GraphQLInt,
+ GraphQLNonNull,
GraphQLObjectType,
GraphQLString,
)
@@ -69,3 +77,53 @@ def get_order(self, request: Request):
"""For testng purposes."""
return "eu38hj32-889283-j2jjb5kl"
+
+
+user_type = GraphQLObjectType(
+ "UserType",
+ {
+ "id": GraphQLField(GraphQLNonNull(GraphQLID)),
+ "firstName": GraphQLField(GraphQLNonNull(GraphQLString)),
+ "lastName": GraphQLField(GraphQLNonNull(GraphQLString)),
+ "tweets": GraphQLField(GraphQLInt),
+ "verified": GraphQLField(GraphQLNonNull(GraphQLBoolean)),
+ },
+)
+
+user_input_type = GraphQLInputObjectType(
+ "UserInputType",
+ {
+ "firstName": GraphQLInputField(GraphQLNonNull(GraphQLString)),
+ "lastName": GraphQLInputField(GraphQLNonNull(GraphQLString)),
+ "tweets": GraphQLInputField(GraphQLInt),
+ "verified": GraphQLInputField(GraphQLBoolean),
+ },
+)
+
+
+class User(NamedTuple):
+ """A simple user object class."""
+
+ firstName: str
+ lastName: str
+ tweets: Optional[int]
+ id: Optional[str] = None
+ verified: bool = False
+
+
+async def resolve_user(request: Request):
+ id = await request.content()
+ return Response(User(firstName="Jack", lastName="Johnson", tweets=563, id=str(id), verified=True))
+
+
+async def resolve_create_user(request: Request):
+ params = await request.content()
+ return Response(
+ User(
+ firstName=params["firstName"],
+ lastName=params["lastName"],
+ tweets=params["tweets"],
+ id="4kjjj43-l23k4l3-325kgaa2",
+ verified=params["verified"],
+ )
+ )
From e670910957e0252e7e521236f6f40c858a3c89f9 Mon Sep 17 00:00:00 2001
From: Vladyslav Fenchak
Date: Tue, 15 Mar 2022 17:53:34 +0100
Subject: [PATCH 33/44] ISSUE #149
---
.../minos/plugins/graphql/handlers.py | 18 ++++++++-----
.../tests/test_graphql/test_handlers.py | 26 +++++++++++++++++++
2 files changed, 37 insertions(+), 7 deletions(-)
diff --git a/packages/plugins/minos-router-graphql/minos/plugins/graphql/handlers.py b/packages/plugins/minos-router-graphql/minos/plugins/graphql/handlers.py
index 632b29189..8179787ad 100644
--- a/packages/plugins/minos-router-graphql/minos/plugins/graphql/handlers.py
+++ b/packages/plugins/minos-router-graphql/minos/plugins/graphql/handlers.py
@@ -7,27 +7,28 @@
from minos.networks import (
HttpRequest,
HttpResponse,
+ ResponseException,
)
class GraphQlHandler:
- """TODO"""
+ """GraphQl Handler"""
def __init__(self, schema: GraphQLSchema):
self._schema = schema
async def execute_operation(self, request: HttpRequest) -> HttpResponse:
- """TODO"""
+ """Execute incoming request extracting variables and passing to graphql"""
content = await request.content()
source = dict()
variables = dict()
- if type(content) == str:
+ if isinstance(content, str):
source = content
- if type(content) == dict:
+ if isinstance(content, dict):
if "query" in content:
source = content["query"]
@@ -40,13 +41,16 @@ async def execute_operation(self, request: HttpRequest) -> HttpResponse:
if errors is None:
errors = list()
+ status = 200
+
if len(errors):
status = 400
- else:
- status = 200
+ for error in errors:
+ if isinstance(error.original_error, ResponseException):
+ status = error.original_error.status
return HttpResponse({"data": result.data, "errors": [err.message for err in errors]}, status=status)
async def get_schema(self, request: HttpRequest) -> HttpResponse:
- """TODO"""
+ """Get schema"""
return HttpResponse(print_schema(self._schema))
diff --git a/packages/plugins/minos-router-graphql/tests/test_graphql/test_handlers.py b/packages/plugins/minos-router-graphql/tests/test_graphql/test_handlers.py
index 2ae5e6be0..3ef2a44f7 100644
--- a/packages/plugins/minos-router-graphql/tests/test_graphql/test_handlers.py
+++ b/packages/plugins/minos-router-graphql/tests/test_graphql/test_handlers.py
@@ -13,6 +13,7 @@
InMemoryRequest,
Request,
Response,
+ ResponseException,
)
from minos.plugins.graphql import (
GraphQLSchemaBuilder,
@@ -40,6 +41,10 @@ async def resolve_ticket(request: Request):
return Response(3)
+async def resolve_ticket_raises(request: Request):
+ raise ResponseException("Some error.", status=403)
+
+
class TestGraphQlHttpRouter(unittest.IsolatedAsyncioTestCase):
CONFIG_FILE_PATH = BASE_PATH / "test_config.yml"
_config = MinosConfig(CONFIG_FILE_PATH)
@@ -58,8 +63,24 @@ async def test_execute_operation(self):
result = await handler.execute_operation(request)
+ self.assertEqual(200, result.status)
self.assertDictEqual(await result.content(), {"data": {"order_query": "ticket #4"}, "errors": []})
+ async def test_execute_operation_raises(self):
+ routes = {
+ GraphQlQueryEnrouteDecorator(name="ticket_query", output=GraphQLString): resolve_ticket_raises,
+ }
+
+ schema = GraphQLSchemaBuilder.build(routes=routes)
+
+ handler = GraphQlHandler(schema)
+
+ request = InMemoryRequest(content="{ ticket_query }")
+
+ result = await handler.execute_operation(request)
+
+ self.assertEqual(403, result.status)
+
async def test_execute_wrong_operation(self):
routes = {
GraphQlQueryEnrouteDecorator(name="order_query", output=GraphQLString): callback_fn,
@@ -76,6 +97,7 @@ async def test_execute_wrong_operation(self):
content = await result.content()
+ self.assertEqual(400, result.status)
self.assertNotEqual(content["errors"], [])
async def test_schema(self):
@@ -92,6 +114,7 @@ async def test_schema(self):
result = await handler.get_schema(request)
+ self.assertEqual(200, result.status)
self.assertIsInstance(await result.content(), str)
self.assertMultiLineEqual(
await result.content(), "type Query {\n order_query: String\n ticket_query: String\n}"
@@ -122,6 +145,7 @@ async def test_query_with_variables(self):
content = await result.content()
+ self.assertEqual(200, result.status)
self.assertDictEqual({"order_query": 3}, content["data"])
self.assertEqual([], content["errors"])
@@ -154,6 +178,7 @@ async def test_query_with_variables_return_user(self):
content = await result.content()
+ self.assertEqual(200, result.status)
self.assertDictEqual(
{"order_query": {"id": "3", "firstName": "Jack", "lastName": "Johnson", "tweets": 563, "verified": True}},
content["data"],
@@ -189,6 +214,7 @@ async def test_mutation(self):
content = await result.content()
+ self.assertEqual(200, result.status)
self.assertDictEqual(
{
"createUser": {
From abe57eb5f72d2407a3db662be21771e3aea50712 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Sergio=20Garc=C3=ADa=20Prado?=
Date: Wed, 16 Mar 2022 08:49:45 +0100
Subject: [PATCH 34/44] ISSUE #149 * Minor change.
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index f94c5f13d..89f6913a4 100644
--- a/README.md
+++ b/README.md
@@ -1157,7 +1157,7 @@ The plugin packages provide connectors to external technologies like brokers, di
* [minos-broker-kafka](https://minos-framework.github.io/minos-python/packages/plugins/minos-broker-kafka): The `kafka` plugin package.
* [minos-discovery-minos](https://minos-framework.github.io/minos-python/packages/plugins/minos-discovery-minos): The `minos-discovery` plugin package.
* [minos-http-aiohttp](https://minos-framework.github.io/minos-python/packages/plugins/minos-http-aiohttp): The `aiohttp` plugin package.
-* * [minos-router-graphql](https://minos-framework.github.io/minos-python/packages/plugins/minos-router-graphql): The `grapqhl` plugin package.
+* [minos-router-graphql](https://minos-framework.github.io/minos-python/packages/plugins/minos-router-graphql): The `grapqhl` plugin package.
## Source Code
From d33a557d44ea130f103d99074d02614f7790445a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Sergio=20Garc=C3=ADa=20Prado?=
Date: Wed, 16 Mar 2022 09:02:10 +0100
Subject: [PATCH 35/44] Apply suggestions from code review
---
.../minos/plugins/graphql/decorators.py | 8 ++++----
.../minos-router-graphql/minos/plugins/graphql/routers.py | 2 +-
.../tests/test_graphql/test_builders/test_schema.py | 2 +-
.../tests/test_graphql/test_decorators.py | 2 +-
.../tests/test_graphql/test_handlers.py | 2 +-
5 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/packages/plugins/minos-router-graphql/minos/plugins/graphql/decorators.py b/packages/plugins/minos-router-graphql/minos/plugins/graphql/decorators.py
index 1c83e0e8e..6f7016398 100644
--- a/packages/plugins/minos-router-graphql/minos/plugins/graphql/decorators.py
+++ b/packages/plugins/minos-router-graphql/minos/plugins/graphql/decorators.py
@@ -17,7 +17,7 @@
class GraphQlEnrouteDecorator(EnrouteDecorator, ABC):
- """GraphQl Enroute class"""
+ """GraphQl Enroute Decorator class"""
def __init__(self, name: str, output, argument: Optional = None):
self.name = name
@@ -29,13 +29,13 @@ def __iter__(self) -> Iterable:
class GraphQlCommandEnrouteDecorator(GraphQlEnrouteDecorator):
- """GraphQl Command Enroute class"""
+ """GraphQl Command Enroute Decorator class"""
KIND: Final[EnrouteDecoratorKind] = EnrouteDecoratorKind.Command
class GraphQlQueryEnrouteDecorator(GraphQlEnrouteDecorator):
- """GraphQl Query Enroute class"""
+ """GraphQl Query Enroute Decorator class"""
KIND: Final[EnrouteDecoratorKind] = EnrouteDecoratorKind.Query
@@ -48,6 +48,6 @@ class GraphQlEnroute:
@classmethod
def register(cls):
- """TODO"""
+ """Register the graphql sub-enroute."""
# noinspection PyProtectedMember
enroute._register_sub_enroute("graphql", cls)
diff --git a/packages/plugins/minos-router-graphql/minos/plugins/graphql/routers.py b/packages/plugins/minos-router-graphql/minos/plugins/graphql/routers.py
index 3c216f829..9b01201dc 100644
--- a/packages/plugins/minos-router-graphql/minos/plugins/graphql/routers.py
+++ b/packages/plugins/minos-router-graphql/minos/plugins/graphql/routers.py
@@ -21,7 +21,7 @@
class GraphQlHttpRouter(HttpRouter):
- """TODO"""
+ """GraphQl Http Router class."""
def _filter_routes(self, routes: dict[EnrouteDecorator, Callable]) -> dict[EnrouteDecorator, Callable]:
schema = GraphQLSchemaBuilder.build(routes)
diff --git a/packages/plugins/minos-router-graphql/tests/test_graphql/test_builders/test_schema.py b/packages/plugins/minos-router-graphql/tests/test_graphql/test_builders/test_schema.py
index 6267438bc..6a997aae1 100644
--- a/packages/plugins/minos-router-graphql/tests/test_graphql/test_builders/test_schema.py
+++ b/packages/plugins/minos-router-graphql/tests/test_graphql/test_builders/test_schema.py
@@ -31,7 +31,7 @@ async def callback_fn(request: Request):
return Response("ticket #4")
-class TestSchemaBuilder(unittest.IsolatedAsyncioTestCase):
+class TestGraphQLSchemaBuilder(unittest.IsolatedAsyncioTestCase):
CONFIG_FILE_PATH = BASE_PATH / "test_config.yml"
_config = MinosConfig(CONFIG_FILE_PATH)
diff --git a/packages/plugins/minos-router-graphql/tests/test_graphql/test_decorators.py b/packages/plugins/minos-router-graphql/tests/test_graphql/test_decorators.py
index 4d8e70eb1..2774487ba 100644
--- a/packages/plugins/minos-router-graphql/tests/test_graphql/test_decorators.py
+++ b/packages/plugins/minos-router-graphql/tests/test_graphql/test_decorators.py
@@ -19,7 +19,7 @@
)
-class TestSomething(unittest.TestCase):
+class TestGraphQlEnrouteDecorator(unittest.TestCase):
def test_decorated_str(self):
analyzer = EnrouteAnalyzer(classname(FakeQueryService))
self.assertEqual(FakeQueryService, analyzer.decorated)
diff --git a/packages/plugins/minos-router-graphql/tests/test_graphql/test_handlers.py b/packages/plugins/minos-router-graphql/tests/test_graphql/test_handlers.py
index 3ef2a44f7..83c0504aa 100644
--- a/packages/plugins/minos-router-graphql/tests/test_graphql/test_handlers.py
+++ b/packages/plugins/minos-router-graphql/tests/test_graphql/test_handlers.py
@@ -45,7 +45,7 @@ async def resolve_ticket_raises(request: Request):
raise ResponseException("Some error.", status=403)
-class TestGraphQlHttpRouter(unittest.IsolatedAsyncioTestCase):
+class TestGraphQlHandler(unittest.IsolatedAsyncioTestCase):
CONFIG_FILE_PATH = BASE_PATH / "test_config.yml"
_config = MinosConfig(CONFIG_FILE_PATH)
From 6d6b1ce3c2604829e712584c1f6e8a655384af9e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Sergio=20Garc=C3=ADa=20Prado?=
Date: Wed, 16 Mar 2022 10:30:44 +0100
Subject: [PATCH 36/44] ISSUE #149 * Remove warnings. * Set version to `0.0.0`
---
.../minos-http-aiohttp/minos/plugins/aiohttp/__init__.py | 2 +-
.../minos-router-graphql/minos/plugins/graphql/__init__.py | 2 +-
packages/plugins/minos-router-graphql/pyproject.toml | 2 +-
.../tests/test_graphql/test_builders/test_schema.py | 4 ++--
.../minos-router-graphql/tests/test_graphql/test_handlers.py | 4 ++--
.../minos-router-graphql/tests/test_graphql/test_routers.py | 4 ++--
6 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/packages/plugins/minos-http-aiohttp/minos/plugins/aiohttp/__init__.py b/packages/plugins/minos-http-aiohttp/minos/plugins/aiohttp/__init__.py
index 9a07f1da2..a44aedd20 100644
--- a/packages/plugins/minos-http-aiohttp/minos/plugins/aiohttp/__init__.py
+++ b/packages/plugins/minos-http-aiohttp/minos/plugins/aiohttp/__init__.py
@@ -1,6 +1,6 @@
__author__ = "Minos Framework Devs"
__email__ = "hey@minos.run"
-__version__ = "0.6.0"
+__version__ = "0.0.0"
from .connectors import (
AioHttpConnector,
diff --git a/packages/plugins/minos-router-graphql/minos/plugins/graphql/__init__.py b/packages/plugins/minos-router-graphql/minos/plugins/graphql/__init__.py
index 37578be32..1e038f258 100644
--- a/packages/plugins/minos-router-graphql/minos/plugins/graphql/__init__.py
+++ b/packages/plugins/minos-router-graphql/minos/plugins/graphql/__init__.py
@@ -1,6 +1,6 @@
__author__ = "Minos Framework Devs"
__email__ = "hey@minos.run"
-__version__ = "0.6.0"
+__version__ = "0.0.0"
from .builders import (
GraphQLSchemaBuilder,
diff --git a/packages/plugins/minos-router-graphql/pyproject.toml b/packages/plugins/minos-router-graphql/pyproject.toml
index 97dbd0367..35156832d 100644
--- a/packages/plugins/minos-router-graphql/pyproject.toml
+++ b/packages/plugins/minos-router-graphql/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "minos-router-graphql"
-version = "0.6.0"
+version = "0.0.0"
description = "The graphql plugin of the Minos Framework."
readme = "README.md"
repository = "https://github.com/minos-framework/minos-python"
diff --git a/packages/plugins/minos-router-graphql/tests/test_graphql/test_builders/test_schema.py b/packages/plugins/minos-router-graphql/tests/test_graphql/test_builders/test_schema.py
index 6a997aae1..095803c62 100644
--- a/packages/plugins/minos-router-graphql/tests/test_graphql/test_builders/test_schema.py
+++ b/packages/plugins/minos-router-graphql/tests/test_graphql/test_builders/test_schema.py
@@ -8,7 +8,7 @@
)
from minos.common import (
- MinosConfig,
+ Config,
)
from minos.networks import (
Request,
@@ -33,7 +33,7 @@ async def callback_fn(request: Request):
class TestGraphQLSchemaBuilder(unittest.IsolatedAsyncioTestCase):
CONFIG_FILE_PATH = BASE_PATH / "test_config.yml"
- _config = MinosConfig(CONFIG_FILE_PATH)
+ _config = Config(CONFIG_FILE_PATH)
def test_build(self):
routes = {
diff --git a/packages/plugins/minos-router-graphql/tests/test_graphql/test_handlers.py b/packages/plugins/minos-router-graphql/tests/test_graphql/test_handlers.py
index 83c0504aa..ab8f1acf5 100644
--- a/packages/plugins/minos-router-graphql/tests/test_graphql/test_handlers.py
+++ b/packages/plugins/minos-router-graphql/tests/test_graphql/test_handlers.py
@@ -7,7 +7,7 @@
)
from minos.common import (
- MinosConfig,
+ Config,
)
from minos.networks import (
InMemoryRequest,
@@ -47,7 +47,7 @@ async def resolve_ticket_raises(request: Request):
class TestGraphQlHandler(unittest.IsolatedAsyncioTestCase):
CONFIG_FILE_PATH = BASE_PATH / "test_config.yml"
- _config = MinosConfig(CONFIG_FILE_PATH)
+ _config = Config(CONFIG_FILE_PATH)
async def test_execute_operation(self):
routes = {
diff --git a/packages/plugins/minos-router-graphql/tests/test_graphql/test_routers.py b/packages/plugins/minos-router-graphql/tests/test_graphql/test_routers.py
index 3d9487985..9681278e9 100644
--- a/packages/plugins/minos-router-graphql/tests/test_graphql/test_routers.py
+++ b/packages/plugins/minos-router-graphql/tests/test_graphql/test_routers.py
@@ -1,7 +1,7 @@
import unittest
from minos.common import (
- MinosConfig,
+ Config,
)
from minos.networks import (
HttpEnrouteDecorator,
@@ -16,7 +16,7 @@
class TestGraphQlHttpRouter(unittest.TestCase):
CONFIG_FILE_PATH = BASE_PATH / "test_config.yml"
- _config = MinosConfig(CONFIG_FILE_PATH)
+ _config = Config(CONFIG_FILE_PATH)
def test_from_config(self):
router = GraphQlHttpRouter.from_config(self._config)
From d37cb99c0db5e19a47b5bde1d25f2964fbad7d18 Mon Sep 17 00:00:00 2001
From: Vladyslav Fenchak
Date: Wed, 16 Mar 2022 11:18:40 +0100
Subject: [PATCH 37/44] ISSUE #149
---
.../minos-router-graphql/minos/plugins/graphql/__init__.py | 5 +++++
.../minos/plugins/graphql/builders/schema.py | 2 +-
.../minos-router-graphql/minos/plugins/graphql/handlers.py | 2 +-
.../tests/test_graphql/test_builders/test_schema.py | 2 --
.../tests/test_graphql/test_decorators.py | 2 +-
.../tests/test_graphql/test_handlers.py | 6 ++----
6 files changed, 10 insertions(+), 9 deletions(-)
diff --git a/packages/plugins/minos-router-graphql/minos/plugins/graphql/__init__.py b/packages/plugins/minos-router-graphql/minos/plugins/graphql/__init__.py
index 1e038f258..bbf8c0123 100644
--- a/packages/plugins/minos-router-graphql/minos/plugins/graphql/__init__.py
+++ b/packages/plugins/minos-router-graphql/minos/plugins/graphql/__init__.py
@@ -6,7 +6,12 @@
GraphQLSchemaBuilder,
)
from .decorators import (
+ GraphQlCommandEnrouteDecorator,
GraphQlEnroute,
+ GraphQlQueryEnrouteDecorator,
+)
+from .handlers import (
+ GraphQlHandler,
)
from .routers import (
GraphQlHttpRouter,
diff --git a/packages/plugins/minos-router-graphql/minos/plugins/graphql/builders/schema.py b/packages/plugins/minos-router-graphql/minos/plugins/graphql/builders/schema.py
index da207fbcf..82a5bc12f 100644
--- a/packages/plugins/minos-router-graphql/minos/plugins/graphql/builders/schema.py
+++ b/packages/plugins/minos-router-graphql/minos/plugins/graphql/builders/schema.py
@@ -70,7 +70,7 @@ def _build_queries(cls, routes):
fields[route.name] = cls._build_field(route, callback)
result = GraphQLObjectType(
- "Query", fields={"hello": GraphQLField(GraphQLString, resolve=lambda obj, info: "world")}
+ "Query", fields={"dummy": GraphQLString}
)
if len(fields) > 0:
result = GraphQLObjectType("Query", fields=fields)
diff --git a/packages/plugins/minos-router-graphql/minos/plugins/graphql/handlers.py b/packages/plugins/minos-router-graphql/minos/plugins/graphql/handlers.py
index 8179787ad..abfb3da43 100644
--- a/packages/plugins/minos-router-graphql/minos/plugins/graphql/handlers.py
+++ b/packages/plugins/minos-router-graphql/minos/plugins/graphql/handlers.py
@@ -44,7 +44,7 @@ async def execute_operation(self, request: HttpRequest) -> HttpResponse:
status = 200
if len(errors):
- status = 400
+ status = 500
for error in errors:
if isinstance(error.original_error, ResponseException):
status = error.original_error.status
diff --git a/packages/plugins/minos-router-graphql/tests/test_graphql/test_builders/test_schema.py b/packages/plugins/minos-router-graphql/tests/test_graphql/test_builders/test_schema.py
index 095803c62..abf9c7bb1 100644
--- a/packages/plugins/minos-router-graphql/tests/test_graphql/test_builders/test_schema.py
+++ b/packages/plugins/minos-router-graphql/tests/test_graphql/test_builders/test_schema.py
@@ -16,8 +16,6 @@
)
from minos.plugins.graphql import (
GraphQLSchemaBuilder,
-)
-from minos.plugins.graphql.decorators import (
GraphQlCommandEnrouteDecorator,
GraphQlQueryEnrouteDecorator,
)
diff --git a/packages/plugins/minos-router-graphql/tests/test_graphql/test_decorators.py b/packages/plugins/minos-router-graphql/tests/test_graphql/test_decorators.py
index 2774487ba..060820bc6 100644
--- a/packages/plugins/minos-router-graphql/tests/test_graphql/test_decorators.py
+++ b/packages/plugins/minos-router-graphql/tests/test_graphql/test_decorators.py
@@ -11,7 +11,7 @@
from minos.networks import (
EnrouteAnalyzer,
)
-from minos.plugins.graphql.decorators import (
+from minos.plugins.graphql import (
GraphQlQueryEnrouteDecorator,
)
from tests.utils import (
diff --git a/packages/plugins/minos-router-graphql/tests/test_graphql/test_handlers.py b/packages/plugins/minos-router-graphql/tests/test_graphql/test_handlers.py
index ab8f1acf5..9512b0062 100644
--- a/packages/plugins/minos-router-graphql/tests/test_graphql/test_handlers.py
+++ b/packages/plugins/minos-router-graphql/tests/test_graphql/test_handlers.py
@@ -16,15 +16,13 @@
ResponseException,
)
from minos.plugins.graphql import (
+ GraphQlHandler,
GraphQLSchemaBuilder,
)
from minos.plugins.graphql.decorators import (
GraphQlCommandEnrouteDecorator,
GraphQlQueryEnrouteDecorator,
)
-from minos.plugins.graphql.handlers import (
- GraphQlHandler,
-)
from tests.test_graphql.test_builders.test_schema import (
callback_fn,
)
@@ -97,7 +95,7 @@ async def test_execute_wrong_operation(self):
content = await result.content()
- self.assertEqual(400, result.status)
+ self.assertEqual(500, result.status)
self.assertNotEqual(content["errors"], [])
async def test_schema(self):
From 96013c33043b56e79dab7ec6bc32518b8dc9981b Mon Sep 17 00:00:00 2001
From: "Restyled.io"
Date: Wed, 16 Mar 2022 10:18:49 +0000
Subject: [PATCH 38/44] Restyled by black
---
.../minos/plugins/graphql/builders/schema.py | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/packages/plugins/minos-router-graphql/minos/plugins/graphql/builders/schema.py b/packages/plugins/minos-router-graphql/minos/plugins/graphql/builders/schema.py
index 82a5bc12f..be9b7e4a4 100644
--- a/packages/plugins/minos-router-graphql/minos/plugins/graphql/builders/schema.py
+++ b/packages/plugins/minos-router-graphql/minos/plugins/graphql/builders/schema.py
@@ -69,9 +69,7 @@ def _build_queries(cls, routes):
if route.KIND == EnrouteDecoratorKind.Query:
fields[route.name] = cls._build_field(route, callback)
- result = GraphQLObjectType(
- "Query", fields={"dummy": GraphQLString}
- )
+ result = GraphQLObjectType("Query", fields={"dummy": GraphQLString})
if len(fields) > 0:
result = GraphQLObjectType("Query", fields=fields)
From 96ce996eb45c154c474b003830cae1b83dfbdb6c Mon Sep 17 00:00:00 2001
From: "Restyled.io"
Date: Wed, 16 Mar 2022 10:18:50 +0000
Subject: [PATCH 39/44] Restyled by isort
---
.../tests/test_graphql/test_builders/test_schema.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/plugins/minos-router-graphql/tests/test_graphql/test_builders/test_schema.py b/packages/plugins/minos-router-graphql/tests/test_graphql/test_builders/test_schema.py
index abf9c7bb1..f33fa82e3 100644
--- a/packages/plugins/minos-router-graphql/tests/test_graphql/test_builders/test_schema.py
+++ b/packages/plugins/minos-router-graphql/tests/test_graphql/test_builders/test_schema.py
@@ -15,9 +15,9 @@
Response,
)
from minos.plugins.graphql import (
- GraphQLSchemaBuilder,
GraphQlCommandEnrouteDecorator,
GraphQlQueryEnrouteDecorator,
+ GraphQLSchemaBuilder,
)
from tests.utils import (
BASE_PATH,
From 605de860cf6ae59ff4c6e1b1936287b1ce8a8d59 Mon Sep 17 00:00:00 2001
From: Vladyslav Fenchak
Date: Wed, 16 Mar 2022 14:43:54 +0100
Subject: [PATCH 40/44] ISSUE #149
---
.../plugins/minos-router-graphql/README.md | 311 +++++++++++++++++-
.../minos-router-graphql/docs/index.md | 3 +-
.../tests/test_graphql/test_handlers.py | 27 ++
3 files changed, 337 insertions(+), 4 deletions(-)
diff --git a/packages/plugins/minos-router-graphql/README.md b/packages/plugins/minos-router-graphql/README.md
index 6aaf6f343..cf7148835 100644
--- a/packages/plugins/minos-router-graphql/README.md
+++ b/packages/plugins/minos-router-graphql/README.md
@@ -12,9 +12,314 @@
## Summary
-Minos is a framework which helps you create [reactive](https://www.reactivemanifesto.org/) microservices in Python.
-Internally, it leverages Event Sourcing, CQRS and a message driven architecture to fulfil the commitments of an
-asynchronous environment.
+This is graphQL plugin for Minos framework. This plugin integrates the official [graphql-core](https://github.com/graphql-python/graphql-core) library. It is oriented to facilitate the development and better organize the graphql code.
+
+## How to
+### Installation
+Modify `config.yaml` file and add:
+```yaml
+...
+routers:
+ - minos.plugins.graphql.GraphQlRouter
+...
+```
+
+### Define your bussines operation
+We will use simple query for this demostration:
+```python
+from graphql import (
+ GraphQLString,
+)
+
+from minos.networks import (
+ Request,
+ Response,
+ enroute,
+)
+
+
+class QueryService:
+ @enroute.graphql.query(name="SimpleQuery", output=GraphQLString)
+ def simple_query(self, request: Request):
+ ...
+
+ return Response("ABCD")
+```
+
+### Execute query
+Send `post` request to `http://your_ip_address:port/graphql` endpoint:
+```javascript
+{ SimpleQuery }
+```
+
+You will receive:
+```json
+{
+ "data": {
+ "SimpleQuery": "ABCD"
+ },
+ "errors": []
+}
+```
+That's all you need to make it work !
+
+For more information about graphql and how to define fields or structures, please see the official [graphql-core](https://github.com/graphql-python/graphql-core). library.
+
+## Decorators
+There are 2 types of decorators, one for `queries` and one for `mutations` (commands).
+
+```python
+@enroute.graphql.query(name="TestQuery", argument=GraphQLString, output=GraphQLString)
+def test_query(self, request: Request):
+ ...
+ return Responnse(...)
+
+@enroute.graphql.command(name="TestCommand", argument=GraphQLString, output=GraphQLString)
+def test_command(self, request: Request):
+ ...
+ return Responnse(...)
+```
+
+Both decorators have the following arguments:
+ - `name`: The name of the query or command
+ - `argument` [Optional]: The arguments it receives, if any.
+ - `output`: The expected output.
+
+### Resolvers
+As you have seen above, the decorator does not specify the function that will resolve it.
+
+This is because it automatically takes the decorated function.
+
+In the following example:
+
+```python
+@enroute.graphql.query(name="TestQuery", argument=GraphQLString, output=GraphQLString)
+def test_query(self, request: Request):
+ ...
+ return Responnse(...)
+```
+
+The function in charge of resolving the query is the decorated function `test_query`.
+
+### Queries (Query Service)
+Queries are used for a single purpose as their name indicates and that is to obtain information, that is, for queries.
+
+Base structure example:
+```python
+class QueryService:
+ @enroute.graphql.query(name="TestQuery", argument=GraphQLString, output=GraphQLString)
+ def test_query(self, request: Request):
+ ...
+ return Responnse(...)
+```
+
+More complex example:
+```python
+from graphql import (
+ GraphQLBoolean,
+ GraphQLField,
+ GraphQLID,
+ GraphQLInt,
+ GraphQLNonNull,
+ GraphQLObjectType,
+ GraphQLString,
+)
+from typing import (
+ NamedTuple,
+ Optional,
+)
+from minos.networks import (
+ Request,
+ Response,
+ enroute,
+)
+
+
+user_type = GraphQLObjectType(
+ "UserType",
+ {
+ "id": GraphQLField(GraphQLNonNull(GraphQLID)),
+ "firstName": GraphQLField(GraphQLNonNull(GraphQLString)),
+ "lastName": GraphQLField(GraphQLNonNull(GraphQLString)),
+ "tweets": GraphQLField(GraphQLInt),
+ "verified": GraphQLField(GraphQLNonNull(GraphQLBoolean)),
+ },
+)
+
+
+class User(NamedTuple):
+ """A simple user object class."""
+
+ firstName: str
+ lastName: str
+ tweets: Optional[int]
+ id: Optional[str] = None
+ verified: bool = False
+
+
+class QueryService:
+ @enroute.graphql.query(name="GetUser", argument=GraphQLInt, output=user_type)
+ def test_query(self, request: Request):
+ id = await request.content()
+ return Response(User(firstName="Jack", lastName="Johnson", tweets=563, id=str(id), verified=True))
+```
+
+If you POST `/graphql` endpoint passing the query and variables:
+
+```json
+{
+ "query": "query ($userId: Int!) {
+ order_query(request: $userId) {
+ id
+ firstName
+ lastName
+ tweets
+ verified
+ }
+ }",
+ "variables": {
+ "userId": 3
+ }
+}
+```
+
+Yoy will receive:
+
+```json
+{
+ "data": {
+ "order_query": {
+ "id": "3",
+ "firstName": "Jack",
+ "lastName": "Johnson",
+ "tweets": 563,
+ "verified": true
+ }
+ },
+ "errors": []
+}
+```
+
+### Mutations (Command Service)
+Mutations are used to create, update or delete information.
+
+Base structure example:
+```python
+class CommandService:
+ @enroute.graphql.command(name="TestQuery", argument=GraphQLString, output=GraphQLString)
+ def test_command(self, request: Request):
+ ...
+ return Responnse(...)
+```
+
+More complex example:
+```python
+from graphql import (
+ GraphQLBoolean,
+ GraphQLField,
+ GraphQLID,
+ GraphQLInputField,
+ GraphQLInputObjectType,
+ GraphQLInt,
+ GraphQLNonNull,
+ GraphQLObjectType,
+ GraphQLString,
+)
+from typing import (
+ NamedTuple,
+ Optional,
+)
+from minos.networks import (
+ Request,
+ Response,
+ enroute,
+)
+
+
+user_type = GraphQLObjectType(
+ "UserType",
+ {
+ "id": GraphQLField(GraphQLNonNull(GraphQLID)),
+ "firstName": GraphQLField(GraphQLNonNull(GraphQLString)),
+ "lastName": GraphQLField(GraphQLNonNull(GraphQLString)),
+ "tweets": GraphQLField(GraphQLInt),
+ "verified": GraphQLField(GraphQLNonNull(GraphQLBoolean)),
+ },
+)
+
+
+user_input_type = GraphQLInputObjectType(
+ "UserInputType",
+ {
+ "firstName": GraphQLInputField(GraphQLNonNull(GraphQLString)),
+ "lastName": GraphQLInputField(GraphQLNonNull(GraphQLString)),
+ "tweets": GraphQLInputField(GraphQLInt),
+ "verified": GraphQLInputField(GraphQLBoolean),
+ },
+)
+
+
+class User(NamedTuple):
+ """A simple user object class."""
+
+ firstName: str
+ lastName: str
+ tweets: Optional[int]
+ id: Optional[str] = None
+ verified: bool = False
+
+
+class CommandService:
+ @enroute.graphql.command(name="GetUser", argument=GraphQLNonNull(user_input_type), output=user_type)
+ def test_command(self, request: Request):
+ params = await request.content()
+ return Response(
+ User(
+ firstName=params["firstName"],
+ lastName=params["lastName"],
+ tweets=params["tweets"],
+ id="4kjjj43-l23k4l3-325kgaa2",
+ verified=params["verified"],
+ )
+ )
+```
+
+If you POST `/graphql` endpoint passing the query and variables:
+
+```json
+{
+ "query": "mutation ($userData: UserInputType!) {
+ createUser(request: $userData) {
+ id, firstName, lastName, tweets, verified
+ }
+ }",
+ "variables": {
+ "userData": {
+ "firstName": "John",
+ "lastName":"Doe",
+ "tweets": 42,
+ "verified":true
+ }
+ }
+}
+```
+
+Yoy will receive:
+
+```json
+{
+ "data": {
+ "createUser": {
+ "id": "4kjjj43-l23k4l3-325kgaa2",
+ "firstName": "John",
+ "lastName": "Doe",
+ "tweets": 42,
+ "verified": true
+ }
+ },
+ "errors": []
+}
+```
## Documentation
diff --git a/packages/plugins/minos-router-graphql/docs/index.md b/packages/plugins/minos-router-graphql/docs/index.md
index 038a73eb8..afb71f631 100644
--- a/packages/plugins/minos-router-graphql/docs/index.md
+++ b/packages/plugins/minos-router-graphql/docs/index.md
@@ -1,4 +1,5 @@
-# Welcome to Minos Router GraphQl's documentation!
+# GraphQL plugin form Minos framework!
+This plugin integrates the official graphql-core library.
.. toctree::
:maxdepth: 2
diff --git a/packages/plugins/minos-router-graphql/tests/test_graphql/test_handlers.py b/packages/plugins/minos-router-graphql/tests/test_graphql/test_handlers.py
index 9512b0062..fd5d7f68f 100644
--- a/packages/plugins/minos-router-graphql/tests/test_graphql/test_handlers.py
+++ b/packages/plugins/minos-router-graphql/tests/test_graphql/test_handlers.py
@@ -39,6 +39,10 @@ async def resolve_ticket(request: Request):
return Response(3)
+async def resolve_simple_query(request: Request):
+ return Response("ABCD")
+
+
async def resolve_ticket_raises(request: Request):
raise ResponseException("Some error.", status=403)
@@ -147,6 +151,29 @@ async def test_query_with_variables(self):
self.assertDictEqual({"order_query": 3}, content["data"])
self.assertEqual([], content["errors"])
+ async def test_simple_query(self):
+ routes = {GraphQlQueryEnrouteDecorator(name="SimpleQuery", output=GraphQLString): resolve_simple_query}
+
+ schema = GraphQLSchemaBuilder.build(routes=routes)
+
+ handler = GraphQlHandler(schema)
+
+ query = """
+ { SimpleQuery }
+ """
+
+ content = {"query": query}
+
+ request = InMemoryRequest(content=content)
+
+ result = await handler.execute_operation(request)
+
+ content = await result.content()
+
+ self.assertEqual(200, result.status)
+ self.assertDictEqual({"SimpleQuery": "ABCD"}, content["data"])
+ self.assertEqual([], content["errors"])
+
async def test_query_with_variables_return_user(self):
routes = {GraphQlQueryEnrouteDecorator(name="order_query", argument=GraphQLInt, output=user_type): resolve_user}
From ffa7fb8941543a3dee6f4daa45609141f97b9389 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Sergio=20Garc=C3=ADa=20Prado?=
Date: Thu, 17 Mar 2022 17:35:43 +0100
Subject: [PATCH 41/44] ISSUE #149 * Add description message. * Add type hints.
---
.../minos/plugins/graphql/builders/schema.py | 43 +++++++++++++------
1 file changed, 29 insertions(+), 14 deletions(-)
diff --git a/packages/plugins/minos-router-graphql/minos/plugins/graphql/builders/schema.py b/packages/plugins/minos-router-graphql/minos/plugins/graphql/builders/schema.py
index be9b7e4a4..48e9c7ca7 100644
--- a/packages/plugins/minos-router-graphql/minos/plugins/graphql/builders/schema.py
+++ b/packages/plugins/minos-router-graphql/minos/plugins/graphql/builders/schema.py
@@ -2,6 +2,9 @@
annotations,
)
+from collections.abc import (
+ Callable,
+)
from functools import (
wraps,
)
@@ -10,6 +13,9 @@
)
from typing import (
Any,
+ Awaitable,
+ Optional,
+ Union,
)
from graphql import (
@@ -23,6 +29,12 @@
from minos.networks import (
EnrouteDecoratorKind,
InMemoryRequest,
+ Request,
+ Response,
+)
+
+from ..decorators import (
+ GraphQlEnrouteDecorator,
)
@@ -33,20 +45,22 @@ def __init__(self, *args, **kwargs):
self.schema = GraphQLSchema(**kwargs)
@classmethod
- def build(cls, routes) -> GraphQLSchema:
+ def build(cls, routes: dict[GraphQlEnrouteDecorator, Callable]) -> GraphQLSchema:
"""TODO"""
schema_args = cls._build(routes)
return cls(**schema_args).schema
@classmethod
- def _build(cls, routes) -> dict:
+ def _build(cls, routes: dict[GraphQlEnrouteDecorator, Callable]) -> dict[str, Optional[GraphQLObjectType]]:
query = cls._build_queries(routes)
mutation = cls._build_mutations(routes)
return {"query": query, "mutation": mutation}
@staticmethod
- def adapt_callback(callback):
+ def adapt_callback(
+ callback: Callable[[Request], Union[Optional[Response], Awaitable[Optional[Response]]]]
+ ) -> Callable[[Any, Any, Any], Awaitable[Any]]:
"""TODO"""
@wraps(callback)
@@ -62,21 +76,23 @@ async def _wrapper(_source, _info, request: Any = None):
return _wrapper
@classmethod
- def _build_queries(cls, routes):
+ def _build_queries(cls, routes: dict[GraphQlEnrouteDecorator, Callable]) -> GraphQLObjectType:
fields = dict()
for route, callback in routes.items():
callback = cls.adapt_callback(callback)
if route.KIND == EnrouteDecoratorKind.Query:
fields[route.name] = cls._build_field(route, callback)
- result = GraphQLObjectType("Query", fields={"dummy": GraphQLString})
- if len(fields) > 0:
- result = GraphQLObjectType("Query", fields=fields)
+ if not len(fields):
+ fields["dummy"] = GraphQLField(
+ type_=GraphQLString,
+ description="Dummy query added to surpass the 'Type Query must define at least one field' constraint."
+ )
- return result
+ return GraphQLObjectType("Query", fields=fields)
@classmethod
- def _build_mutations(cls, routes):
+ def _build_mutations(cls, routes: dict[GraphQlEnrouteDecorator, Callable]) -> Optional[GraphQLObjectType]:
fields = dict()
for route, callback in routes.items():
callback = cls.adapt_callback(callback)
@@ -84,14 +100,13 @@ def _build_mutations(cls, routes):
if route.KIND == EnrouteDecoratorKind.Command:
fields[route.name] = cls._build_field(route, callback)
- result = None
- if len(fields) > 0:
- result = GraphQLObjectType("Mutation", fields=fields)
+ if not len(fields):
+ return None
- return result
+ return GraphQLObjectType("Mutation", fields=fields)
@staticmethod
- def _build_field(item, callback):
+ def _build_field(item: GraphQlEnrouteDecorator, callback: Callable) -> GraphQLField:
args = None
if item.argument is not None:
args = {"request": GraphQLArgument(item.argument)}
From dcf5a4e162f6a92baafde499d30bfea74c0559dc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Sergio=20Garc=C3=ADa=20Prado?=
Date: Thu, 17 Mar 2022 17:42:50 +0100
Subject: [PATCH 42/44] ISSUE #149 * Minor change.
---
.../minos/plugins/graphql/builders/schema.py | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/packages/plugins/minos-router-graphql/minos/plugins/graphql/builders/schema.py b/packages/plugins/minos-router-graphql/minos/plugins/graphql/builders/schema.py
index 48e9c7ca7..6345aeb1d 100644
--- a/packages/plugins/minos-router-graphql/minos/plugins/graphql/builders/schema.py
+++ b/packages/plugins/minos-router-graphql/minos/plugins/graphql/builders/schema.py
@@ -107,7 +107,11 @@ def _build_mutations(cls, routes: dict[GraphQlEnrouteDecorator, Callable]) -> Op
@staticmethod
def _build_field(item: GraphQlEnrouteDecorator, callback: Callable) -> GraphQLField:
+ argument = item.argument
+ output = item.output
+
args = None
- if item.argument is not None:
- args = {"request": GraphQLArgument(item.argument)}
- return GraphQLField(item.output, args=args, resolve=callback)
+ if argument is not None:
+ args = {"request": GraphQLArgument(argument)}
+
+ return GraphQLField(output, args=args, resolve=callback)
From ff072562626193eb180d92053a485d14954d14ec Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Sergio=20Garc=C3=ADa=20Prado?=
Date: Fri, 18 Mar 2022 10:03:00 +0100
Subject: [PATCH 43/44] ISSUE #150 * Reformat code.
---
.../minos/plugins/graphql/builders/schema.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/plugins/minos-router-graphql/minos/plugins/graphql/builders/schema.py b/packages/plugins/minos-router-graphql/minos/plugins/graphql/builders/schema.py
index 6345aeb1d..6e9a4b77b 100644
--- a/packages/plugins/minos-router-graphql/minos/plugins/graphql/builders/schema.py
+++ b/packages/plugins/minos-router-graphql/minos/plugins/graphql/builders/schema.py
@@ -86,7 +86,7 @@ def _build_queries(cls, routes: dict[GraphQlEnrouteDecorator, Callable]) -> Grap
if not len(fields):
fields["dummy"] = GraphQLField(
type_=GraphQLString,
- description="Dummy query added to surpass the 'Type Query must define at least one field' constraint."
+ description="Dummy query added to surpass the 'Type Query must define at least one field' constraint.",
)
return GraphQLObjectType("Query", fields=fields)
From 12604df19943e7232343c8d7467407ea3b8e568a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Sergio=20Garc=C3=ADa=20Prado?=
Date: Fri, 18 Mar 2022 12:44:55 +0100
Subject: [PATCH 44/44] ISSUE #149 * Minor improvements.
---
.../minos/plugins/graphql/builders/schema.py | 14 ++++--
.../minos/plugins/graphql/handlers.py | 44 +++++++++++++------
.../minos/plugins/graphql/routers.py | 8 ++++
3 files changed, 50 insertions(+), 16 deletions(-)
diff --git a/packages/plugins/minos-router-graphql/minos/plugins/graphql/builders/schema.py b/packages/plugins/minos-router-graphql/minos/plugins/graphql/builders/schema.py
index 6e9a4b77b..4655d217b 100644
--- a/packages/plugins/minos-router-graphql/minos/plugins/graphql/builders/schema.py
+++ b/packages/plugins/minos-router-graphql/minos/plugins/graphql/builders/schema.py
@@ -39,14 +39,18 @@
class GraphQLSchemaBuilder:
- """TODO"""
+ """GraphQL Schema Builder class."""
def __init__(self, *args, **kwargs):
self.schema = GraphQLSchema(**kwargs)
@classmethod
def build(cls, routes: dict[GraphQlEnrouteDecorator, Callable]) -> GraphQLSchema:
- """TODO"""
+ """Build a new schema from routes.
+
+ :param routes: The routes to build the schema.
+ :return: A ``GraphQLSchema`` instance.
+ """
schema_args = cls._build(routes)
return cls(**schema_args).schema
@@ -61,7 +65,11 @@ def _build(cls, routes: dict[GraphQlEnrouteDecorator, Callable]) -> dict[str, Op
def adapt_callback(
callback: Callable[[Request], Union[Optional[Response], Awaitable[Optional[Response]]]]
) -> Callable[[Any, Any, Any], Awaitable[Any]]:
- """TODO"""
+ """Adapt a callback from framework's Request-Response to GraphQl structure.
+
+ :param callback: The callback to be adapted.
+ :return: The adapted callback.
+ """
@wraps(callback)
async def _wrapper(_source, _info, request: Any = None):
diff --git a/packages/plugins/minos-router-graphql/minos/plugins/graphql/handlers.py b/packages/plugins/minos-router-graphql/minos/plugins/graphql/handlers.py
index abfb3da43..6d8a0cd54 100644
--- a/packages/plugins/minos-router-graphql/minos/plugins/graphql/handlers.py
+++ b/packages/plugins/minos-router-graphql/minos/plugins/graphql/handlers.py
@@ -1,12 +1,17 @@
+from typing import (
+ Any,
+)
+
from graphql import (
+ ExecutionResult,
GraphQLSchema,
graphql,
print_schema,
)
from minos.networks import (
- HttpRequest,
- HttpResponse,
+ Request,
+ Response,
ResponseException,
)
@@ -17,26 +22,33 @@ class GraphQlHandler:
def __init__(self, schema: GraphQLSchema):
self._schema = schema
- async def execute_operation(self, request: HttpRequest) -> HttpResponse:
- """Execute incoming request extracting variables and passing to graphql"""
+ async def execute_operation(self, request: Request) -> Response:
+ """Execute incoming request extracting variables and passing to graphql.
- content = await request.content()
+ :param request: The request containing the graphql operation.
+ :return: A response containing the graphql result.
+ """
+ result = await graphql(schema=self._schema, **(await self._build_graphql_arguments(request)))
+ return self._build_response_from_graphql(result)
+ @staticmethod
+ async def _build_graphql_arguments(request: Request) -> dict[str, Any]:
+ content = await request.content()
source = dict()
variables = dict()
if isinstance(content, str):
source = content
-
- if isinstance(content, dict):
+ elif isinstance(content, dict):
if "query" in content:
source = content["query"]
-
if "variables" in content:
variables = content["variables"]
- result = await graphql(schema=self._schema, source=source, variable_values=variables)
+ return {"source": source, "variable_values": variables}
+ @staticmethod
+ def _build_response_from_graphql(result: ExecutionResult) -> Response:
errors = result.errors
if errors is None:
errors = list()
@@ -49,8 +61,14 @@ async def execute_operation(self, request: HttpRequest) -> HttpResponse:
if isinstance(error.original_error, ResponseException):
status = error.original_error.status
- return HttpResponse({"data": result.data, "errors": [err.message for err in errors]}, status=status)
+ content = {"data": result.data, "errors": [err.message for err in errors]}
+
+ return Response(content, status=status)
+
+ async def get_schema(self, request: Request) -> Response:
+ """Get schema
- async def get_schema(self, request: HttpRequest) -> HttpResponse:
- """Get schema"""
- return HttpResponse(print_schema(self._schema))
+ :param request: An empty request.
+ :return: A Response containing the schema as a string.
+ """
+ return Response(print_schema(self._schema))
diff --git a/packages/plugins/minos-router-graphql/minos/plugins/graphql/routers.py b/packages/plugins/minos-router-graphql/minos/plugins/graphql/routers.py
index 9b01201dc..322b1105a 100644
--- a/packages/plugins/minos-router-graphql/minos/plugins/graphql/routers.py
+++ b/packages/plugins/minos-router-graphql/minos/plugins/graphql/routers.py
@@ -15,6 +15,9 @@
from .builders import (
GraphQLSchemaBuilder,
)
+from .decorators import (
+ GraphQlEnrouteDecorator,
+)
from .handlers import (
GraphQlHandler,
)
@@ -24,6 +27,11 @@ class GraphQlHttpRouter(HttpRouter):
"""GraphQl Http Router class."""
def _filter_routes(self, routes: dict[EnrouteDecorator, Callable]) -> dict[EnrouteDecorator, Callable]:
+ routes = {
+ decorator: callback
+ for decorator, callback in routes.items()
+ if isinstance(decorator, GraphQlEnrouteDecorator)
+ }
schema = GraphQLSchemaBuilder.build(routes)
handler = GraphQlHandler(schema)
return {