From 67d5add8847eeaca76eaf3e4fe8d27a9b403fe36 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan
Date: Tue, 21 Sep 2021 11:07:16 +0530
Subject: [PATCH 001/150] add more models
---
reddit-clone/.flake8 | 7 +
reddit-clone/README.md | 45 +
reddit-clone/alembic/.gitkeep | 0
reddit-clone/poetry.lock | 1092 ++++++++++++++++++++
reddit-clone/pyproject.toml | 25 +
reddit-clone/reddit/__init__.py | 25 +
reddit-clone/reddit/comments/__init__.py | 0
reddit-clone/reddit/comments/model.py | 7 +
reddit-clone/reddit/core/__init__.py | 0
reddit-clone/reddit/core/config.py | 11 +
reddit-clone/reddit/core/models.py | 42 +
reddit-clone/reddit/schema.py | 6 +
reddit-clone/reddit/subreddits/__init__.py | 0
reddit-clone/reddit/subreddits/models.py | 59 ++
reddit-clone/reddit/threads/__init__.py | 0
reddit-clone/reddit/threads/models.py | 74 ++
reddit-clone/reddit/users/__init__.py | 0
reddit-clone/reddit/users/models.py | 57 +
reddit-clone/setup.cfg | 2 +
19 files changed, 1452 insertions(+)
create mode 100644 reddit-clone/.flake8
create mode 100644 reddit-clone/README.md
create mode 100644 reddit-clone/alembic/.gitkeep
create mode 100644 reddit-clone/poetry.lock
create mode 100644 reddit-clone/pyproject.toml
create mode 100644 reddit-clone/reddit/__init__.py
create mode 100644 reddit-clone/reddit/comments/__init__.py
create mode 100644 reddit-clone/reddit/comments/model.py
create mode 100644 reddit-clone/reddit/core/__init__.py
create mode 100644 reddit-clone/reddit/core/config.py
create mode 100644 reddit-clone/reddit/core/models.py
create mode 100644 reddit-clone/reddit/schema.py
create mode 100644 reddit-clone/reddit/subreddits/__init__.py
create mode 100644 reddit-clone/reddit/subreddits/models.py
create mode 100644 reddit-clone/reddit/threads/__init__.py
create mode 100644 reddit-clone/reddit/threads/models.py
create mode 100644 reddit-clone/reddit/users/__init__.py
create mode 100644 reddit-clone/reddit/users/models.py
create mode 100644 reddit-clone/setup.cfg
diff --git a/reddit-clone/.flake8 b/reddit-clone/.flake8
new file mode 100644
index 00000000..812e13d9
--- /dev/null
+++ b/reddit-clone/.flake8
@@ -0,0 +1,7 @@
+[flake8]
+max-line-length = 89
+exclude=.venv,.git
+ignore = W503
+extend-ignore =
+ # See https://github.com/PyCQA/pycodestyle/issues/373
+ E203,
diff --git a/reddit-clone/README.md b/reddit-clone/README.md
new file mode 100644
index 00000000..78a07283
--- /dev/null
+++ b/reddit-clone/README.md
@@ -0,0 +1,45 @@
+# Reddit GraphQL API
+
+This example shows you how to create a Reddit API clone using GraphQL.
+
+## Tech Stack used:
+
+- Strawberry GraphQL
+- FastAPI w/ Starlette
+- SQLAlchemy (asyncio)
+
+## Features at a glance
+
+- Error handling with unions
+- Authorization with the permissions API
+- Batch loading with dataloaders
+- Pagination with SQLAlchemy
+
+## How to use
+
+1. Install dependencies
+
+Use [poetry](https://python-poetry.org/) to install dependencies:
+
+```bash
+poetry install
+```
+
+2. Run migrations
+
+Run [alembic](https://alembic.sqlalchemy.org/en/latest/) to create the database
+and populate it with movie data:
+
+```bash
+poetry run alembic upgrade head
+```
+
+3. Run the server
+
+Run [uvicorn](https://www.uvicorn.org/) to run the server:
+
+```bash
+poetry run uvicorn reddit:app --reload
+```
+
+The GraphQL API should now be available at http://localhost:8000/graphql
diff --git a/reddit-clone/alembic/.gitkeep b/reddit-clone/alembic/.gitkeep
new file mode 100644
index 00000000..e69de29b
diff --git a/reddit-clone/poetry.lock b/reddit-clone/poetry.lock
new file mode 100644
index 00000000..eef41ea6
--- /dev/null
+++ b/reddit-clone/poetry.lock
@@ -0,0 +1,1092 @@
+[[package]]
+name = "alembic"
+version = "1.7.3"
+description = "A database migration tool for SQLAlchemy."
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+importlib-metadata = {version = "*", markers = "python_version < \"3.8\""}
+importlib-resources = {version = "*", markers = "python_version < \"3.9\""}
+Mako = "*"
+SQLAlchemy = ">=1.3.0"
+
+[package.extras]
+tz = ["python-dateutil"]
+
+[[package]]
+name = "asgiref"
+version = "3.4.1"
+description = "ASGI specs, helper code, and adapters"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+typing-extensions = {version = "*", markers = "python_version < \"3.8\""}
+
+[package.extras]
+tests = ["pytest", "pytest-asyncio", "mypy (>=0.800)"]
+
+[[package]]
+name = "attrs"
+version = "21.2.0"
+description = "Classes Without Boilerplate"
+category = "dev"
+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"]
+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"]
+tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins"]
+
+[[package]]
+name = "black"
+version = "21.9b0"
+description = "The uncompromising code formatter."
+category = "dev"
+optional = false
+python-versions = ">=3.6.2"
+
+[package.dependencies]
+click = ">=7.1.2"
+mypy-extensions = ">=0.4.3"
+pathspec = ">=0.9.0,<1"
+platformdirs = ">=2"
+regex = ">=2020.1.8"
+tomli = ">=0.2.6,<2.0.0"
+typed-ast = {version = ">=1.4.2", markers = "python_version < \"3.8\""}
+typing-extensions = [
+ {version = ">=3.10.0.0", markers = "python_version < \"3.10\""},
+ {version = "!=3.10.0.1", markers = "python_version >= \"3.10\""},
+]
+
+[package.extras]
+colorama = ["colorama (>=0.4.3)"]
+d = ["aiohttp (>=3.6.0)", "aiohttp-cors (>=0.4.0)"]
+jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"]
+python2 = ["typed-ast (>=1.4.2)"]
+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 = "click"
+version = "8.0.1"
+description = "Composable command line interface toolkit"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+colorama = {version = "*", markers = "platform_system == \"Windows\""}
+importlib-metadata = {version = "*", markers = "python_version < \"3.8\""}
+
+[[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 = "fastapi"
+version = "0.68.1"
+description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+pydantic = ">=1.6.2,<1.7 || >1.7,<1.7.1 || >1.7.1,<1.7.2 || >1.7.2,<1.7.3 || >1.7.3,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0"
+starlette = "0.14.2"
+
+[package.extras]
+all = ["requests (>=2.24.0,<3.0.0)", "aiofiles (>=0.5.0,<0.6.0)", "jinja2 (>=2.11.2,<3.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "itsdangerous (>=1.1.0,<2.0.0)", "pyyaml (>=5.3.1,<6.0.0)", "graphene (>=2.1.8,<3.0.0)", "ujson (>=4.0.1,<5.0.0)", "orjson (>=3.2.1,<4.0.0)", "email_validator (>=1.1.1,<2.0.0)", "uvicorn[standard] (>=0.12.0,<0.14.0)", "async_exit_stack (>=1.0.1,<2.0.0)", "async_generator (>=1.10,<2.0.0)"]
+dev = ["python-jose[cryptography] (>=3.3.0,<4.0.0)", "passlib[bcrypt] (>=1.7.2,<2.0.0)", "autoflake (>=1.3.1,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)", "uvicorn[standard] (>=0.12.0,<0.14.0)", "graphene (>=2.1.8,<3.0.0)"]
+doc = ["mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=7.1.9,<8.0.0)", "mdx-include (>=1.4.1,<2.0.0)", "mkdocs-markdownextradata-plugin (>=0.1.7,<0.2.0)", "typer-cli (>=0.0.12,<0.0.13)", "pyyaml (>=5.3.1,<6.0.0)"]
+test = ["pytest (>=6.2.4,<7.0.0)", "pytest-cov (>=2.12.0,<3.0.0)", "pytest-asyncio (>=0.14.0,<0.15.0)", "mypy (==0.812)", "flake8 (>=3.8.3,<4.0.0)", "black (==20.8b1)", "isort (>=5.0.6,<6.0.0)", "requests (>=2.24.0,<3.0.0)", "httpx (>=0.14.0,<0.15.0)", "email_validator (>=1.1.1,<2.0.0)", "sqlalchemy (>=1.3.18,<1.4.0)", "peewee (>=3.13.3,<4.0.0)", "databases[sqlite] (>=0.3.2,<0.4.0)", "orjson (>=3.2.1,<4.0.0)", "ujson (>=4.0.1,<5.0.0)", "async_exit_stack (>=1.0.1,<2.0.0)", "async_generator (>=1.10,<2.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "aiofiles (>=0.5.0,<0.6.0)", "flask (>=1.1.2,<2.0.0)"]
+
+[[package]]
+name = "flake8"
+version = "3.9.2"
+description = "the modular source code checker: pep8 pyflakes and co"
+category = "dev"
+optional = false
+python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
+
+[package.dependencies]
+importlib-metadata = {version = "*", markers = "python_version < \"3.8\""}
+mccabe = ">=0.6.0,<0.7.0"
+pycodestyle = ">=2.7.0,<2.8.0"
+pyflakes = ">=2.3.0,<2.4.0"
+
+[[package]]
+name = "flake8-black"
+version = "0.2.3"
+description = "flake8 plugin to call black as a code style validator"
+category = "dev"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+black = "*"
+flake8 = ">=3.0.0"
+toml = "*"
+
+[[package]]
+name = "flake8-bugbear"
+version = "21.9.1"
+description = "A plugin for flake8 finding likely bugs and design problems in your program. Contains warnings that don't belong in pyflakes and pycodestyle."
+category = "dev"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+attrs = ">=19.2.0"
+flake8 = ">=3.0.0"
+
+[package.extras]
+dev = ["coverage", "black", "hypothesis", "hypothesmith"]
+
+[[package]]
+name = "graphql-core"
+version = "3.1.6"
+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 = "greenlet"
+version = "1.1.1"
+description = "Lightweight in-process concurrent programming"
+category = "main"
+optional = false
+python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*"
+
+[package.extras]
+docs = ["sphinx"]
+
+[[package]]
+name = "h11"
+version = "0.12.0"
+description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[[package]]
+name = "httptools"
+version = "0.2.0"
+description = "A collection of framework independent HTTP protocol utils."
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.extras]
+test = ["Cython (==0.29.22)"]
+
+[[package]]
+name = "importlib-metadata"
+version = "4.8.1"
+description = "Read metadata from Python packages"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""}
+zipp = ">=0.5"
+
+[package.extras]
+docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"]
+perf = ["ipython"]
+testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-perf (>=0.9.2)", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"]
+
+[[package]]
+name = "importlib-resources"
+version = "5.2.2"
+description = "Read resources from Python packages"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""}
+
+[package.extras]
+docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"]
+testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-black (>=0.3.7)", "pytest-mypy"]
+
+[[package]]
+name = "mako"
+version = "1.1.5"
+description = "A super-fast templating language that borrows the best ideas from the existing templating languages."
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+
+[package.dependencies]
+MarkupSafe = ">=0.9.2"
+
+[package.extras]
+babel = ["babel"]
+lingua = ["lingua"]
+
+[[package]]
+name = "markupsafe"
+version = "2.0.1"
+description = "Safely add untrusted strings to HTML/XML markup."
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[[package]]
+name = "mccabe"
+version = "0.6.1"
+description = "McCabe checker, plugin for flake8"
+category = "dev"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "mypy"
+version = "0.910"
+description = "Optional static typing for Python"
+category = "main"
+optional = false
+python-versions = ">=3.5"
+
+[package.dependencies]
+mypy-extensions = ">=0.4.3,<0.5.0"
+toml = "*"
+typed-ast = {version = ">=1.4.0,<1.5.0", markers = "python_version < \"3.8\""}
+typing-extensions = ">=3.7.4"
+
+[package.extras]
+dmypy = ["psutil (>=4.0)"]
+python2 = ["typed-ast (>=1.4.0,<1.5.0)"]
+
+[[package]]
+name = "mypy-extensions"
+version = "0.4.3"
+description = "Experimental type system extensions for programs checked with the mypy typechecker."
+category = "main"
+optional = false
+python-versions = "*"
+
+[[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 = "platformdirs"
+version = "2.3.0"
+description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
+category = "dev"
+optional = false
+python-versions = ">=3.6"
+
+[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 = "pycodestyle"
+version = "2.7.0"
+description = "Python style guide checker"
+category = "dev"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+
+[[package]]
+name = "pydantic"
+version = "1.8.2"
+description = "Data validation and settings management using python 3.6 type hinting"
+category = "main"
+optional = false
+python-versions = ">=3.6.1"
+
+[package.dependencies]
+typing-extensions = ">=3.7.4.3"
+
+[package.extras]
+dotenv = ["python-dotenv (>=0.10.4)"]
+email = ["email-validator (>=1.0.3)"]
+
+[[package]]
+name = "pyflakes"
+version = "2.3.1"
+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.10.0"
+description = "Pygments is a syntax highlighting package written in Python."
+category = "main"
+optional = false
+python-versions = ">=3.5"
+
+[[package]]
+name = "python-dateutil"
+version = "2.8.2"
+description = "Extensions to the standard Python datetime module"
+category = "main"
+optional = false
+python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
+
+[package.dependencies]
+six = ">=1.5"
+
+[[package]]
+name = "python-dotenv"
+version = "0.19.0"
+description = "Read key-value pairs from a .env file and set them as environment variables"
+category = "main"
+optional = false
+python-versions = ">=3.5"
+
+[package.extras]
+cli = ["click (>=5.0)"]
+
+[[package]]
+name = "python-multipart"
+version = "0.0.5"
+description = "A streaming multipart parser for Python"
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+six = ">=1.4.0"
+
+[[package]]
+name = "pyyaml"
+version = "5.4.1"
+description = "YAML parser and emitter for Python"
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
+
+[[package]]
+name = "regex"
+version = "2021.8.28"
+description = "Alternative regular expression module, to replace re."
+category = "dev"
+optional = false
+python-versions = "*"
+
+[[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 = "sqlalchemy"
+version = "1.4.23"
+description = "Database Abstraction Library"
+category = "main"
+optional = false
+python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7"
+
+[package.dependencies]
+greenlet = {version = "!=0.4.17", markers = "python_version >= \"3\" and platform_machine in \"x86_64 X86_64 aarch64 AARCH64 ppc64le PPC64LE amd64 AMD64 win32 WIN32\""}
+importlib-metadata = {version = "*", markers = "python_version < \"3.8\""}
+mypy = {version = ">=0.910", optional = true, markers = "python_version >= \"3\" and extra == \"mypy\""}
+sqlalchemy2-stubs = {version = "*", optional = true, markers = "extra == \"mypy\""}
+
+[package.extras]
+aiomysql = ["greenlet (!=0.4.17)", "aiomysql"]
+aiosqlite = ["greenlet (!=0.4.17)", "aiosqlite"]
+asyncio = ["greenlet (!=0.4.17)"]
+mariadb_connector = ["mariadb (>=1.0.1)"]
+mssql = ["pyodbc"]
+mssql_pymssql = ["pymssql"]
+mssql_pyodbc = ["pyodbc"]
+mypy = ["sqlalchemy2-stubs", "mypy (>=0.910)"]
+mysql = ["mysqlclient (>=1.4.0,<2)", "mysqlclient (>=1.4.0)"]
+mysql_connector = ["mysqlconnector"]
+oracle = ["cx_oracle (>=7,<8)", "cx_oracle (>=7)"]
+postgresql = ["psycopg2 (>=2.7)"]
+postgresql_asyncpg = ["greenlet (!=0.4.17)", "asyncpg"]
+postgresql_pg8000 = ["pg8000 (>=1.16.6)"]
+postgresql_psycopg2binary = ["psycopg2-binary"]
+postgresql_psycopg2cffi = ["psycopg2cffi"]
+pymysql = ["pymysql (<1)", "pymysql"]
+sqlcipher = ["sqlcipher3-binary"]
+
+[[package]]
+name = "sqlalchemy2-stubs"
+version = "0.0.2a15"
+description = "Typing Stubs for SQLAlchemy 1.4"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+typing-extensions = ">=3.7.4"
+
+[[package]]
+name = "starlette"
+version = "0.14.2"
+description = "The little ASGI library that shines."
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.extras]
+full = ["aiofiles", "graphene", "itsdangerous", "jinja2", "python-multipart", "pyyaml", "requests"]
+
+[[package]]
+name = "strawberry-graphql"
+version = "0.77.12"
+description = "A library for creating GraphQL APIs"
+category = "main"
+optional = false
+python-versions = ">=3.7,<4.0"
+
+[package.dependencies]
+cached-property = ">=1.5.2,<2.0.0"
+click = ">=7.0,<9.0"
+graphql-core = ">=3.1.0,<3.2.0"
+pygments = ">=2.3,<3.0"
+python-dateutil = ">=2.7.0,<3.0.0"
+python-multipart = ">=0.0.5,<0.0.6"
+starlette = {version = ">=0.13.6,<0.17.0", optional = true, markers = "extra == \"asgi\" or extra == \"debug-server\""}
+typing_extensions = ">=3.7.4,<4.0.0"
+
+[package.extras]
+asgi = ["starlette (>=0.13.6,<0.17.0)"]
+debug-server = ["starlette (>=0.13.6,<0.17.0)", "uvicorn (>=0.11.6,<0.16.0)"]
+django = ["django (>=2,<4)", "asgiref (>=3.2,<4.0)"]
+flask = ["flask (>=1.1,<2.0)"]
+opentelemetry = ["opentelemetry-api (<2)", "opentelemetry-sdk (<2)"]
+pydantic = ["pydantic (<2)"]
+sanic = ["sanic (>=20.12.2,<22.0.0)"]
+aiohttp = ["aiohttp (>=3.7.4.post0,<4.0.0)"]
+
+[[package]]
+name = "toml"
+version = "0.10.2"
+description = "Python Library for Tom's Obvious, Minimal Language"
+category = "main"
+optional = false
+python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
+
+[[package]]
+name = "tomli"
+version = "1.2.1"
+description = "A lil' TOML parser"
+category = "dev"
+optional = false
+python-versions = ">=3.6"
+
+[[package]]
+name = "typed-ast"
+version = "1.4.3"
+description = "a fork of Python 2 and 3 ast modules with type comment support"
+category = "main"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "typing-extensions"
+version = "3.10.0.2"
+description = "Backported and Experimental Type Hints for Python 3.5+"
+category = "main"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "uvicorn"
+version = "0.15.0"
+description = "The lightning-fast ASGI server."
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+asgiref = ">=3.4.0"
+click = ">=7.0"
+colorama = {version = ">=0.4", optional = true, markers = "sys_platform == \"win32\" and extra == \"standard\""}
+h11 = ">=0.8"
+httptools = {version = ">=0.2.0,<0.3.0", optional = true, markers = "extra == \"standard\""}
+python-dotenv = {version = ">=0.13", optional = true, markers = "extra == \"standard\""}
+PyYAML = {version = ">=5.1", optional = true, markers = "extra == \"standard\""}
+typing-extensions = {version = "*", markers = "python_version < \"3.8\""}
+uvloop = {version = ">=0.14.0,<0.15.0 || >0.15.0,<0.15.1 || >0.15.1", optional = true, markers = "sys_platform != \"win32\" and sys_platform != \"cygwin\" and platform_python_implementation != \"PyPy\" and extra == \"standard\""}
+watchgod = {version = ">=0.6", optional = true, markers = "extra == \"standard\""}
+websockets = {version = ">=9.1", optional = true, markers = "extra == \"standard\""}
+
+[package.extras]
+standard = ["websockets (>=9.1)", "httptools (>=0.2.0,<0.3.0)", "watchgod (>=0.6)", "python-dotenv (>=0.13)", "PyYAML (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "colorama (>=0.4)"]
+
+[[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 = "watchgod"
+version = "0.7"
+description = "Simple, modern file watching and code reload in python."
+category = "main"
+optional = false
+python-versions = ">=3.5"
+
+[[package]]
+name = "websockets"
+version = "10.0"
+description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)"
+category = "main"
+optional = false
+python-versions = ">=3.7"
+
+[[package]]
+name = "zipp"
+version = "3.5.0"
+description = "Backport of pathlib-compatible object wrapper for zip files"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.extras]
+docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"]
+testing = ["pytest (>=4.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.7"
+content-hash = "98fa69364a20e1543505c385c05fd9be26fdc5abbb60fc0e52136a65a1036d88"
+
+[metadata.files]
+alembic = [
+ {file = "alembic-1.7.3-py3-none-any.whl", hash = "sha256:d0c580041f9f6487d5444df672a83da9be57398f39d6c1802bbedec6fefbeef6"},
+ {file = "alembic-1.7.3.tar.gz", hash = "sha256:bc5bdf03d1b9814ee4d72adc0b19df2123f6c50a60c1ea761733f3640feedb8d"},
+]
+asgiref = [
+ {file = "asgiref-3.4.1-py3-none-any.whl", hash = "sha256:ffc141aa908e6f175673e7b1b3b7af4fdb0ecb738fc5c8b88f69f055c2415214"},
+ {file = "asgiref-3.4.1.tar.gz", hash = "sha256:4ef1ab46b484e3c706329cedeff284a5d40824200638503f5768edb6de7d58e9"},
+]
+attrs = [
+ {file = "attrs-21.2.0-py2.py3-none-any.whl", hash = "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1"},
+ {file = "attrs-21.2.0.tar.gz", hash = "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"},
+]
+black = [
+ {file = "black-21.9b0-py3-none-any.whl", hash = "sha256:380f1b5da05e5a1429225676655dddb96f5ae8c75bdf91e53d798871b902a115"},
+ {file = "black-21.9b0.tar.gz", hash = "sha256:7de4cfc7eb6b710de325712d40125689101d21d25283eed7e9998722cf10eb91"},
+]
+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"},
+]
+click = [
+ {file = "click-8.0.1-py3-none-any.whl", hash = "sha256:fba402a4a47334742d782209a7c79bc448911afe1149d07bdabdf480b3e2f4b6"},
+ {file = "click-8.0.1.tar.gz", hash = "sha256:8c04c11192119b1ef78ea049e0a6f0463e4c48ef00a30160c704337586f3ad7a"},
+]
+colorama = [
+ {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"},
+ {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"},
+]
+fastapi = [
+ {file = "fastapi-0.68.1-py3-none-any.whl", hash = "sha256:94d2820906c36b9b8303796fb7271337ec89c74223229e3cfcf056b5a7d59e23"},
+ {file = "fastapi-0.68.1.tar.gz", hash = "sha256:644bb815bae326575c4b2842469fb83053a4b974b82fa792ff9283d17fbbd99d"},
+]
+flake8 = [
+ {file = "flake8-3.9.2-py2.py3-none-any.whl", hash = "sha256:bf8fd333346d844f616e8d47905ef3a3384edae6b4e9beb0c5101e25e3110907"},
+ {file = "flake8-3.9.2.tar.gz", hash = "sha256:07528381786f2a6237b061f6e96610a4167b226cb926e2aa2b6b1d78057c576b"},
+]
+flake8-black = [
+ {file = "flake8-black-0.2.3.tar.gz", hash = "sha256:c199844bc1b559d91195ebe8620216f21ed67f2cc1ff6884294c91a0d2492684"},
+ {file = "flake8_black-0.2.3-py3-none-any.whl", hash = "sha256:cc080ba5b3773b69ba102b6617a00cc4ecbad8914109690cfda4d565ea435d96"},
+]
+flake8-bugbear = [
+ {file = "flake8-bugbear-21.9.1.tar.gz", hash = "sha256:2f60c8ce0dc53d51da119faab2d67dea978227f0f92ed3c44eb7d65fb2e06a96"},
+ {file = "flake8_bugbear-21.9.1-py36.py37.py38-none-any.whl", hash = "sha256:45bfdccfb9f2d8aa140e33cac8f46f1e38215c13d5aa8650e7e188d84e2f94c6"},
+]
+graphql-core = [
+ {file = "graphql-core-3.1.6.tar.gz", hash = "sha256:e65975b6a13878f9113a1fa5320760585b522d139944e005936b1b8358d0651a"},
+ {file = "graphql_core-3.1.6-py3-none-any.whl", hash = "sha256:c78d09596d347e1cffd266c5384abfedf43ed1eae08729773bebb3d527fe5a14"},
+]
+greenlet = [
+ {file = "greenlet-1.1.1-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:476ba9435afaead4382fbab8f1882f75e3fb2285c35c9285abb3dd30237f9142"},
+ {file = "greenlet-1.1.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:44556302c0ab376e37939fd0058e1f0db2e769580d340fb03b01678d1ff25f68"},
+ {file = "greenlet-1.1.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:40abb7fec4f6294225d2b5464bb6d9552050ded14a7516588d6f010e7e366dcc"},
+ {file = "greenlet-1.1.1-cp27-cp27m-win32.whl", hash = "sha256:a11b6199a0b9dc868990456a2667167d0ba096c5224f6258e452bfbe5a9742c5"},
+ {file = "greenlet-1.1.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e22a82d2b416d9227a500c6860cf13e74060cf10e7daf6695cbf4e6a94e0eee4"},
+ {file = "greenlet-1.1.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:bad269e442f1b7ffa3fa8820b3c3aa66f02a9f9455b5ba2db5a6f9eea96f56de"},
+ {file = "greenlet-1.1.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:8ddb38fb6ad96c2ef7468ff73ba5c6876b63b664eebb2c919c224261ae5e8378"},
+ {file = "greenlet-1.1.1-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:84782c80a433d87530ae3f4b9ed58d4a57317d9918dfcc6a59115fa2d8731f2c"},
+ {file = "greenlet-1.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac991947ca6533ada4ce7095f0e28fe25d5b2f3266ad5b983ed4201e61596acf"},
+ {file = "greenlet-1.1.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5317701c7ce167205c0569c10abc4bd01c7f4cf93f642c39f2ce975fa9b78a3c"},
+ {file = "greenlet-1.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4870b018ca685ff573edd56b93f00a122f279640732bb52ce3a62b73ee5c4a92"},
+ {file = "greenlet-1.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:990e0f5e64bcbc6bdbd03774ecb72496224d13b664aa03afd1f9b171a3269272"},
+ {file = "greenlet-1.1.1-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:a414f8e14aa7bacfe1578f17c11d977e637d25383b6210587c29210af995ef04"},
+ {file = "greenlet-1.1.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:e02780da03f84a671bb4205c5968c120f18df081236d7b5462b380fd4f0b497b"},
+ {file = "greenlet-1.1.1-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:dfcb5a4056e161307d103bc013478892cfd919f1262c2bb8703220adcb986362"},
+ {file = "greenlet-1.1.1-cp35-cp35m-win32.whl", hash = "sha256:655ab836324a473d4cd8cf231a2d6f283ed71ed77037679da554e38e606a7117"},
+ {file = "greenlet-1.1.1-cp35-cp35m-win_amd64.whl", hash = "sha256:6ce9d0784c3c79f3e5c5c9c9517bbb6c7e8aa12372a5ea95197b8a99402aa0e6"},
+ {file = "greenlet-1.1.1-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:3fc6a447735749d651d8919da49aab03c434a300e9f0af1c886d560405840fd1"},
+ {file = "greenlet-1.1.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:8039f5fe8030c43cd1732d9a234fdcbf4916fcc32e21745ca62e75023e4d4649"},
+ {file = "greenlet-1.1.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:fddfb31aa2ac550b938d952bca8a87f1db0f8dc930ffa14ce05b5c08d27e7fd1"},
+ {file = "greenlet-1.1.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b97a807437b81f90f85022a9dcfd527deea38368a3979ccb49d93c9198b2c722"},
+ {file = "greenlet-1.1.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cf31e894dabb077a35bbe6963285d4515a387ff657bd25b0530c7168e48f167f"},
+ {file = "greenlet-1.1.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4eae94de9924bbb4d24960185363e614b1b62ff797c23dc3c8a7c75bbb8d187e"},
+ {file = "greenlet-1.1.1-cp36-cp36m-win32.whl", hash = "sha256:c1862f9f1031b1dee3ff00f1027fcd098ffc82120f43041fe67804b464bbd8a7"},
+ {file = "greenlet-1.1.1-cp36-cp36m-win_amd64.whl", hash = "sha256:9b02e6039eafd75e029d8c58b7b1f3e450ca563ef1fe21c7e3e40b9936c8d03e"},
+ {file = "greenlet-1.1.1-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:84488516639c3c5e5c0e52f311fff94ebc45b56788c2a3bfe9cf8e75670f4de3"},
+ {file = "greenlet-1.1.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:3f8fc59bc5d64fa41f58b0029794f474223693fd00016b29f4e176b3ee2cfd9f"},
+ {file = "greenlet-1.1.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:3e594015a2349ec6dcceda9aca29da8dc89e85b56825b7d1f138a3f6bb79dd4c"},
+ {file = "greenlet-1.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e41f72f225192d5d4df81dad2974a8943b0f2d664a2a5cfccdf5a01506f5523c"},
+ {file = "greenlet-1.1.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:75ff270fd05125dce3303e9216ccddc541a9e072d4fc764a9276d44dee87242b"},
+ {file = "greenlet-1.1.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5cde7ee190196cbdc078511f4df0be367af85636b84d8be32230f4871b960687"},
+ {file = "greenlet-1.1.1-cp37-cp37m-win32.whl", hash = "sha256:f253dad38605486a4590f9368ecbace95865fea0f2b66615d121ac91fd1a1563"},
+ {file = "greenlet-1.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a91ee268f059583176c2c8b012a9fce7e49ca6b333a12bbc2dd01fc1a9783885"},
+ {file = "greenlet-1.1.1-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:34e6675167a238bede724ee60fe0550709e95adaff6a36bcc97006c365290384"},
+ {file = "greenlet-1.1.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:bf3725d79b1ceb19e83fb1aed44095518c0fcff88fba06a76c0891cfd1f36837"},
+ {file = "greenlet-1.1.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:5c3b735ccf8fc8048664ee415f8af5a3a018cc92010a0d7195395059b4b39b7d"},
+ {file = "greenlet-1.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2002a59453858c7f3404690ae80f10c924a39f45f6095f18a985a1234c37334"},
+ {file = "greenlet-1.1.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:04e1849c88aa56584d4a0a6e36af5ec7cc37993fdc1fda72b56aa1394a92ded3"},
+ {file = "greenlet-1.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8d4ed48eed7414ccb2aaaecbc733ed2a84c299714eae3f0f48db085342d5629"},
+ {file = "greenlet-1.1.1-cp38-cp38-win32.whl", hash = "sha256:2f89d74b4f423e756a018832cd7a0a571e0a31b9ca59323b77ce5f15a437629b"},
+ {file = "greenlet-1.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:d15cb6f8706678dc47fb4e4f8b339937b04eda48a0af1cca95f180db552e7663"},
+ {file = "greenlet-1.1.1-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:b050dbb96216db273b56f0e5960959c2b4cb679fe1e58a0c3906fa0a60c00662"},
+ {file = "greenlet-1.1.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:6e0696525500bc8aa12eae654095d2260db4dc95d5c35af2b486eae1bf914ccd"},
+ {file = "greenlet-1.1.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:07e6d88242e09b399682b39f8dfa1e7e6eca66b305de1ff74ed9eb1a7d8e539c"},
+ {file = "greenlet-1.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:98b491976ed656be9445b79bc57ed21decf08a01aaaf5fdabf07c98c108111f6"},
+ {file = "greenlet-1.1.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05e72db813c28906cdc59bd0da7c325d9b82aa0b0543014059c34c8c4ad20e16"},
+ {file = "greenlet-1.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:090126004c8ab9cd0787e2acf63d79e80ab41a18f57d6448225bbfcba475034f"},
+ {file = "greenlet-1.1.1-cp39-cp39-win32.whl", hash = "sha256:1796f2c283faab2b71c67e9b9aefb3f201fdfbee5cb55001f5ffce9125f63a45"},
+ {file = "greenlet-1.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:4adaf53ace289ced90797d92d767d37e7cdc29f13bd3830c3f0a561277a4ae83"},
+ {file = "greenlet-1.1.1.tar.gz", hash = "sha256:c0f22774cd8294078bdf7392ac73cf00bfa1e5e0ed644bd064fdabc5f2a2f481"},
+]
+h11 = [
+ {file = "h11-0.12.0-py3-none-any.whl", hash = "sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6"},
+ {file = "h11-0.12.0.tar.gz", hash = "sha256:47222cb6067e4a307d535814917cd98fd0a57b6788ce715755fa2b6c28b56042"},
+]
+httptools = [
+ {file = "httptools-0.2.0-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:79dbc21f3612a78b28384e989b21872e2e3cf3968532601544696e4ed0007ce5"},
+ {file = "httptools-0.2.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:78d03dd39b09c99ec917d50189e6743adbfd18c15d5944392d2eabda688bf149"},
+ {file = "httptools-0.2.0-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:a23166e5ae2775709cf4f7ad4c2048755ebfb272767d244e1a96d55ac775cca7"},
+ {file = "httptools-0.2.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:3ab1f390d8867f74b3b5ee2a7ecc9b8d7f53750bd45714bf1cb72a953d7dfa77"},
+ {file = "httptools-0.2.0-cp36-cp36m-win_amd64.whl", hash = "sha256:a7594f9a010cdf1e16a58b3bf26c9da39bbf663e3b8d46d39176999d71816658"},
+ {file = "httptools-0.2.0-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:01b392a166adcc8bc2f526a939a8aabf89fe079243e1543fd0e7dc1b58d737cb"},
+ {file = "httptools-0.2.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:80ffa04fe8c8dfacf6e4cef8277347d35b0442c581f5814f3b0cf41b65c43c6e"},
+ {file = "httptools-0.2.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d5682eeb10cca0606c4a8286a3391d4c3c5a36f0c448e71b8bd05be4e1694bfb"},
+ {file = "httptools-0.2.0-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:a289c27ccae399a70eacf32df9a44059ca2ba4ac444604b00a19a6c1f0809943"},
+ {file = "httptools-0.2.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:813871f961edea6cb2fe312f2d9b27d12a51ba92545380126f80d0de1917ea15"},
+ {file = "httptools-0.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:cc9be041e428c10f8b6ab358c6b393648f9457094e1dcc11b4906026d43cd380"},
+ {file = "httptools-0.2.0-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:b08d00d889a118f68f37f3c43e359aab24ee29eb2e3fe96d64c6a2ba8b9d6557"},
+ {file = "httptools-0.2.0-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:fd3b8905e21431ad306eeaf56644a68fdd621bf8f3097eff54d0f6bdf7262065"},
+ {file = "httptools-0.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:200fc1cdf733a9ff554c0bb97a4047785cfaad9875307d6087001db3eb2b417f"},
+ {file = "httptools-0.2.0.tar.gz", hash = "sha256:94505026be56652d7a530ab03d89474dc6021019d6b8682281977163b3471ea0"},
+]
+importlib-metadata = [
+ {file = "importlib_metadata-4.8.1-py3-none-any.whl", hash = "sha256:b618b6d2d5ffa2f16add5697cf57a46c76a56229b0ed1c438322e4e95645bd15"},
+ {file = "importlib_metadata-4.8.1.tar.gz", hash = "sha256:f284b3e11256ad1e5d03ab86bb2ccd6f5339688ff17a4d797a0fe7df326f23b1"},
+]
+importlib-resources = [
+ {file = "importlib_resources-5.2.2-py3-none-any.whl", hash = "sha256:2480d8e07d1890056cb53c96e3de44fead9c62f2ba949b0f2e4c4345f4afa977"},
+ {file = "importlib_resources-5.2.2.tar.gz", hash = "sha256:a65882a4d0fe5fbf702273456ba2ce74fe44892c25e42e057aca526b702a6d4b"},
+]
+mako = [
+ {file = "Mako-1.1.5-py2.py3-none-any.whl", hash = "sha256:6804ee66a7f6a6416910463b00d76a7b25194cd27f1918500c5bd7be2a088a23"},
+ {file = "Mako-1.1.5.tar.gz", hash = "sha256:169fa52af22a91900d852e937400e79f535496191c63712e3b9fda5a9bed6fc3"},
+]
+markupsafe = [
+ {file = "MarkupSafe-2.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d8446c54dc28c01e5a2dbac5a25f071f6653e6e40f3a8818e8b45d790fe6ef53"},
+ {file = "MarkupSafe-2.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:36bc903cbb393720fad60fc28c10de6acf10dc6cc883f3e24ee4012371399a38"},
+ {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d7d807855b419fc2ed3e631034685db6079889a1f01d5d9dac950f764da3dad"},
+ {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:add36cb2dbb8b736611303cd3bfcee00afd96471b09cda130da3581cbdc56a6d"},
+ {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:168cd0a3642de83558a5153c8bd34f175a9a6e7f6dc6384b9655d2697312a646"},
+ {file = "MarkupSafe-2.0.1-cp310-cp310-win32.whl", hash = "sha256:99df47edb6bda1249d3e80fdabb1dab8c08ef3975f69aed437cb69d0a5de1e28"},
+ {file = "MarkupSafe-2.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:e0f138900af21926a02425cf736db95be9f4af72ba1bb21453432a07f6082134"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f9081981fe268bd86831e5c75f7de206ef275defcb82bc70740ae6dc507aee51"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:0955295dd5eec6cb6cc2fe1698f4c6d84af2e92de33fbcac4111913cd100a6ff"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:0446679737af14f45767963a1a9ef7620189912317d095f2d9ffa183a4d25d2b"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:f826e31d18b516f653fe296d967d700fddad5901ae07c622bb3705955e1faa94"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:fa130dd50c57d53368c9d59395cb5526eda596d3ffe36666cd81a44d56e48872"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:905fec760bd2fa1388bb5b489ee8ee5f7291d692638ea5f67982d968366bef9f"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf5d821ffabf0ef3533c39c518f3357b171a1651c1ff6827325e4489b0e46c3c"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0d4b31cc67ab36e3392bbf3862cfbadac3db12bdd8b02a2731f509ed5b829724"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:baa1a4e8f868845af802979fcdbf0bb11f94f1cb7ced4c4b8a351bb60d108145"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-win32.whl", hash = "sha256:6c4ca60fa24e85fe25b912b01e62cb969d69a23a5d5867682dd3e80b5b02581d"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b2f4bf27480f5e5e8ce285a8c8fd176c0b03e93dcc6646477d4630e83440c6a9"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0717a7390a68be14b8c793ba258e075c6f4ca819f15edfc2a3a027c823718567"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:6557b31b5e2c9ddf0de32a691f2312a32f77cd7681d8af66c2692efdbef84c18"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:49e3ceeabbfb9d66c3aef5af3a60cc43b85c33df25ce03d0031a608b0a8b2e3f"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:d7f9850398e85aba693bb640262d3611788b1f29a79f0c93c565694658f4071f"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:6a7fae0dd14cf60ad5ff42baa2e95727c3d81ded453457771d02b7d2b3f9c0c2"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:b7f2d075102dc8c794cbde1947378051c4e5180d52d276987b8d28a3bd58c17d"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9936f0b261d4df76ad22f8fee3ae83b60d7c3e871292cd42f40b81b70afae85"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2a7d351cbd8cfeb19ca00de495e224dea7e7d919659c2841bbb7f420ad03e2d6"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:60bf42e36abfaf9aff1f50f52644b336d4f0a3fd6d8a60ca0d054ac9f713a864"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-win32.whl", hash = "sha256:a30e67a65b53ea0a5e62fe23682cfe22712e01f453b95233b25502f7c61cb415"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:611d1ad9a4288cf3e3c16014564df047fe08410e628f89805e475368bd304914"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5bb28c636d87e840583ee3adeb78172efc47c8b26127267f54a9c0ec251d41a9"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:be98f628055368795d818ebf93da628541e10b75b41c559fdf36d104c5787066"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1d609f577dc6e1aa17d746f8bd3c31aa4d258f4070d61b2aa5c4166c1539de35"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7d91275b0245b1da4d4cfa07e0faedd5b0812efc15b702576d103293e252af1b"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:01a9b8ea66f1658938f65b93a85ebe8bc016e6769611be228d797c9d998dd298"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:47ab1e7b91c098ab893b828deafa1203de86d0bc6ab587b160f78fe6c4011f75"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:97383d78eb34da7e1fa37dd273c20ad4320929af65d156e35a5e2d89566d9dfb"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fcf051089389abe060c9cd7caa212c707e58153afa2c649f00346ce6d260f1b"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5855f8438a7d1d458206a2466bf82b0f104a3724bf96a1c781ab731e4201731a"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3dd007d54ee88b46be476e293f48c85048603f5f516008bee124ddd891398ed6"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-win32.whl", hash = "sha256:023cb26ec21ece8dc3907c0e8320058b2e0cb3c55cf9564da612bc325bed5e64"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:984d76483eb32f1bcb536dc27e4ad56bba4baa70be32fa87152832cdd9db0833"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2ef54abee730b502252bcdf31b10dacb0a416229b72c18b19e24a4509f273d26"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3c112550557578c26af18a1ccc9e090bfe03832ae994343cfdacd287db6a6ae7"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:53edb4da6925ad13c07b6d26c2a852bd81e364f95301c66e930ab2aef5b5ddd8"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:f5653a225f31e113b152e56f154ccbe59eeb1c7487b39b9d9f9cdb58e6c79dc5"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:4efca8f86c54b22348a5467704e3fec767b2db12fc39c6d963168ab1d3fc9135"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:ab3ef638ace319fa26553db0624c4699e31a28bb2a835c5faca8f8acf6a5a902"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:f8ba0e8349a38d3001fae7eadded3f6606f0da5d748ee53cc1dab1d6527b9509"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c47adbc92fc1bb2b3274c4b3a43ae0e4573d9fbff4f54cd484555edbf030baf1"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:37205cac2a79194e3750b0af2a5720d95f786a55ce7df90c3af697bfa100eaac"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:1f2ade76b9903f39aa442b4aadd2177decb66525062db244b35d71d0ee8599b6"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-win32.whl", hash = "sha256:10f82115e21dc0dfec9ab5c0223652f7197feb168c940f3ef61563fc2d6beb74"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8"},
+ {file = "MarkupSafe-2.0.1.tar.gz", hash = "sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a"},
+]
+mccabe = [
+ {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"},
+ {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"},
+]
+mypy = [
+ {file = "mypy-0.910-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:a155d80ea6cee511a3694b108c4494a39f42de11ee4e61e72bc424c490e46457"},
+ {file = "mypy-0.910-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:b94e4b785e304a04ea0828759172a15add27088520dc7e49ceade7834275bedb"},
+ {file = "mypy-0.910-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:088cd9c7904b4ad80bec811053272986611b84221835e079be5bcad029e79dd9"},
+ {file = "mypy-0.910-cp35-cp35m-win_amd64.whl", hash = "sha256:adaeee09bfde366d2c13fe6093a7df5df83c9a2ba98638c7d76b010694db760e"},
+ {file = "mypy-0.910-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:ecd2c3fe726758037234c93df7e98deb257fd15c24c9180dacf1ef829da5f921"},
+ {file = "mypy-0.910-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:d9dd839eb0dc1bbe866a288ba3c1afc33a202015d2ad83b31e875b5905a079b6"},
+ {file = "mypy-0.910-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:3e382b29f8e0ccf19a2df2b29a167591245df90c0b5a2542249873b5c1d78212"},
+ {file = "mypy-0.910-cp36-cp36m-win_amd64.whl", hash = "sha256:53fd2eb27a8ee2892614370896956af2ff61254c275aaee4c230ae771cadd885"},
+ {file = "mypy-0.910-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b6fb13123aeef4a3abbcfd7e71773ff3ff1526a7d3dc538f3929a49b42be03f0"},
+ {file = "mypy-0.910-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e4dab234478e3bd3ce83bac4193b2ecd9cf94e720ddd95ce69840273bf44f6de"},
+ {file = "mypy-0.910-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:7df1ead20c81371ccd6091fa3e2878559b5c4d4caadaf1a484cf88d93ca06703"},
+ {file = "mypy-0.910-cp37-cp37m-win_amd64.whl", hash = "sha256:0aadfb2d3935988ec3815952e44058a3100499f5be5b28c34ac9d79f002a4a9a"},
+ {file = "mypy-0.910-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ec4e0cd079db280b6bdabdc807047ff3e199f334050db5cbb91ba3e959a67504"},
+ {file = "mypy-0.910-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:119bed3832d961f3a880787bf621634ba042cb8dc850a7429f643508eeac97b9"},
+ {file = "mypy-0.910-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:866c41f28cee548475f146aa4d39a51cf3b6a84246969f3759cb3e9c742fc072"},
+ {file = "mypy-0.910-cp38-cp38-win_amd64.whl", hash = "sha256:ceb6e0a6e27fb364fb3853389607cf7eb3a126ad335790fa1e14ed02fba50811"},
+ {file = "mypy-0.910-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1a85e280d4d217150ce8cb1a6dddffd14e753a4e0c3cf90baabb32cefa41b59e"},
+ {file = "mypy-0.910-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:42c266ced41b65ed40a282c575705325fa7991af370036d3f134518336636f5b"},
+ {file = "mypy-0.910-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:3c4b8ca36877fc75339253721f69603a9c7fdb5d4d5a95a1a1b899d8b86a4de2"},
+ {file = "mypy-0.910-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:c0df2d30ed496a08de5daed2a9ea807d07c21ae0ab23acf541ab88c24b26ab97"},
+ {file = "mypy-0.910-cp39-cp39-win_amd64.whl", hash = "sha256:c6c2602dffb74867498f86e6129fd52a2770c48b7cd3ece77ada4fa38f94eba8"},
+ {file = "mypy-0.910-py3-none-any.whl", hash = "sha256:ef565033fa5a958e62796867b1df10c40263ea9ded87164d67572834e57a174d"},
+ {file = "mypy-0.910.tar.gz", hash = "sha256:704098302473cb31a218f1775a873b376b30b4c18229421e9e9dc8916fd16150"},
+]
+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"},
+]
+pathspec = [
+ {file = "pathspec-0.9.0-py2.py3-none-any.whl", hash = "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a"},
+ {file = "pathspec-0.9.0.tar.gz", hash = "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"},
+]
+platformdirs = [
+ {file = "platformdirs-2.3.0-py3-none-any.whl", hash = "sha256:8003ac87717ae2c7ee1ea5a84a1a61e87f3fbd16eb5aadba194ea30a9019f648"},
+ {file = "platformdirs-2.3.0.tar.gz", hash = "sha256:15b056538719b1c94bdaccb29e5f81879c7f7f0f4a153f46086d155dffcd4f0f"},
+]
+pycodestyle = [
+ {file = "pycodestyle-2.7.0-py2.py3-none-any.whl", hash = "sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068"},
+ {file = "pycodestyle-2.7.0.tar.gz", hash = "sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef"},
+]
+pydantic = [
+ {file = "pydantic-1.8.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:05ddfd37c1720c392f4e0d43c484217b7521558302e7069ce8d318438d297739"},
+ {file = "pydantic-1.8.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:a7c6002203fe2c5a1b5cbb141bb85060cbff88c2d78eccbc72d97eb7022c43e4"},
+ {file = "pydantic-1.8.2-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:589eb6cd6361e8ac341db97602eb7f354551482368a37f4fd086c0733548308e"},
+ {file = "pydantic-1.8.2-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:10e5622224245941efc193ad1d159887872776df7a8fd592ed746aa25d071840"},
+ {file = "pydantic-1.8.2-cp36-cp36m-win_amd64.whl", hash = "sha256:99a9fc39470010c45c161a1dc584997f1feb13f689ecf645f59bb4ba623e586b"},
+ {file = "pydantic-1.8.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a83db7205f60c6a86f2c44a61791d993dff4b73135df1973ecd9eed5ea0bda20"},
+ {file = "pydantic-1.8.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:41b542c0b3c42dc17da70554bc6f38cbc30d7066d2c2815a94499b5684582ecb"},
+ {file = "pydantic-1.8.2-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:ea5cb40a3b23b3265f6325727ddfc45141b08ed665458be8c6285e7b85bd73a1"},
+ {file = "pydantic-1.8.2-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:18b5ea242dd3e62dbf89b2b0ec9ba6c7b5abaf6af85b95a97b00279f65845a23"},
+ {file = "pydantic-1.8.2-cp37-cp37m-win_amd64.whl", hash = "sha256:234a6c19f1c14e25e362cb05c68afb7f183eb931dd3cd4605eafff055ebbf287"},
+ {file = "pydantic-1.8.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:021ea0e4133e8c824775a0cfe098677acf6fa5a3cbf9206a376eed3fc09302cd"},
+ {file = "pydantic-1.8.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:e710876437bc07bd414ff453ac8ec63d219e7690128d925c6e82889d674bb505"},
+ {file = "pydantic-1.8.2-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:ac8eed4ca3bd3aadc58a13c2aa93cd8a884bcf21cb019f8cfecaae3b6ce3746e"},
+ {file = "pydantic-1.8.2-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:4a03cbbe743e9c7247ceae6f0d8898f7a64bb65800a45cbdc52d65e370570820"},
+ {file = "pydantic-1.8.2-cp38-cp38-win_amd64.whl", hash = "sha256:8621559dcf5afacf0069ed194278f35c255dc1a1385c28b32dd6c110fd6531b3"},
+ {file = "pydantic-1.8.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8b223557f9510cf0bfd8b01316bf6dd281cf41826607eada99662f5e4963f316"},
+ {file = "pydantic-1.8.2-cp39-cp39-manylinux1_i686.whl", hash = "sha256:244ad78eeb388a43b0c927e74d3af78008e944074b7d0f4f696ddd5b2af43c62"},
+ {file = "pydantic-1.8.2-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:05ef5246a7ffd2ce12a619cbb29f3307b7c4509307b1b49f456657b43529dc6f"},
+ {file = "pydantic-1.8.2-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:54cd5121383f4a461ff7644c7ca20c0419d58052db70d8791eacbbe31528916b"},
+ {file = "pydantic-1.8.2-cp39-cp39-win_amd64.whl", hash = "sha256:4be75bebf676a5f0f87937c6ddb061fa39cbea067240d98e298508c1bda6f3f3"},
+ {file = "pydantic-1.8.2-py3-none-any.whl", hash = "sha256:fec866a0b59f372b7e776f2d7308511784dace622e0992a0b59ea3ccee0ae833"},
+ {file = "pydantic-1.8.2.tar.gz", hash = "sha256:26464e57ccaafe72b7ad156fdaa4e9b9ef051f69e175dbbb463283000c05ab7b"},
+]
+pyflakes = [
+ {file = "pyflakes-2.3.1-py2.py3-none-any.whl", hash = "sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3"},
+ {file = "pyflakes-2.3.1.tar.gz", hash = "sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db"},
+]
+pygments = [
+ {file = "Pygments-2.10.0-py3-none-any.whl", hash = "sha256:b8e67fe6af78f492b3c4b3e2970c0624cbf08beb1e493b2c99b9fa1b67a20380"},
+ {file = "Pygments-2.10.0.tar.gz", hash = "sha256:f398865f7eb6874156579fdf36bc840a03cab64d1cde9e93d68f46a425ec52c6"},
+]
+python-dateutil = [
+ {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"},
+ {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"},
+]
+python-dotenv = [
+ {file = "python-dotenv-0.19.0.tar.gz", hash = "sha256:f521bc2ac9a8e03c736f62911605c5d83970021e3fa95b37d769e2bbbe9b6172"},
+ {file = "python_dotenv-0.19.0-py2.py3-none-any.whl", hash = "sha256:aae25dc1ebe97c420f50b81fb0e5c949659af713f31fdb63c749ca68748f34b1"},
+]
+python-multipart = [
+ {file = "python-multipart-0.0.5.tar.gz", hash = "sha256:f7bb5f611fc600d15fa47b3974c8aa16e93724513b49b5f95c81e6624c83fa43"},
+]
+pyyaml = [
+ {file = "PyYAML-5.4.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922"},
+ {file = "PyYAML-5.4.1-cp27-cp27m-win32.whl", hash = "sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393"},
+ {file = "PyYAML-5.4.1-cp27-cp27m-win_amd64.whl", hash = "sha256:4465124ef1b18d9ace298060f4eccc64b0850899ac4ac53294547536533800c8"},
+ {file = "PyYAML-5.4.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185"},
+ {file = "PyYAML-5.4.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253"},
+ {file = "PyYAML-5.4.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc"},
+ {file = "PyYAML-5.4.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:72a01f726a9c7851ca9bfad6fd09ca4e090a023c00945ea05ba1638c09dc3347"},
+ {file = "PyYAML-5.4.1-cp36-cp36m-manylinux2014_s390x.whl", hash = "sha256:895f61ef02e8fed38159bb70f7e100e00f471eae2bc838cd0f4ebb21e28f8541"},
+ {file = "PyYAML-5.4.1-cp36-cp36m-win32.whl", hash = "sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5"},
+ {file = "PyYAML-5.4.1-cp36-cp36m-win_amd64.whl", hash = "sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df"},
+ {file = "PyYAML-5.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018"},
+ {file = "PyYAML-5.4.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63"},
+ {file = "PyYAML-5.4.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:cb333c16912324fd5f769fff6bc5de372e9e7a202247b48870bc251ed40239aa"},
+ {file = "PyYAML-5.4.1-cp37-cp37m-manylinux2014_s390x.whl", hash = "sha256:fe69978f3f768926cfa37b867e3843918e012cf83f680806599ddce33c2c68b0"},
+ {file = "PyYAML-5.4.1-cp37-cp37m-win32.whl", hash = "sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b"},
+ {file = "PyYAML-5.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf"},
+ {file = "PyYAML-5.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46"},
+ {file = "PyYAML-5.4.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb"},
+ {file = "PyYAML-5.4.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:fd7f6999a8070df521b6384004ef42833b9bd62cfee11a09bda1079b4b704247"},
+ {file = "PyYAML-5.4.1-cp38-cp38-manylinux2014_s390x.whl", hash = "sha256:bfb51918d4ff3d77c1c856a9699f8492c612cde32fd3bcd344af9be34999bfdc"},
+ {file = "PyYAML-5.4.1-cp38-cp38-win32.whl", hash = "sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc"},
+ {file = "PyYAML-5.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696"},
+ {file = "PyYAML-5.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77"},
+ {file = "PyYAML-5.4.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183"},
+ {file = "PyYAML-5.4.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:d483ad4e639292c90170eb6f7783ad19490e7a8defb3e46f97dfe4bacae89122"},
+ {file = "PyYAML-5.4.1-cp39-cp39-manylinux2014_s390x.whl", hash = "sha256:fdc842473cd33f45ff6bce46aea678a54e3d21f1b61a7750ce3c498eedfe25d6"},
+ {file = "PyYAML-5.4.1-cp39-cp39-win32.whl", hash = "sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10"},
+ {file = "PyYAML-5.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db"},
+ {file = "PyYAML-5.4.1.tar.gz", hash = "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e"},
+]
+regex = [
+ {file = "regex-2021.8.28-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9d05ad5367c90814099000442b2125535e9d77581855b9bee8780f1b41f2b1a2"},
+ {file = "regex-2021.8.28-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3bf1bc02bc421047bfec3343729c4bbbea42605bcfd6d6bfe2c07ade8b12d2a"},
+ {file = "regex-2021.8.28-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f6a808044faae658f546dd5f525e921de9fa409de7a5570865467f03a626fc0"},
+ {file = "regex-2021.8.28-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a617593aeacc7a691cc4af4a4410031654f2909053bd8c8e7db837f179a630eb"},
+ {file = "regex-2021.8.28-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:79aef6b5cd41feff359acaf98e040844613ff5298d0d19c455b3d9ae0bc8c35a"},
+ {file = "regex-2021.8.28-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:0fc1f8f06977c2d4f5e3d3f0d4a08089be783973fc6b6e278bde01f0544ff308"},
+ {file = "regex-2021.8.28-cp310-cp310-win32.whl", hash = "sha256:6eebf512aa90751d5ef6a7c2ac9d60113f32e86e5687326a50d7686e309f66ed"},
+ {file = "regex-2021.8.28-cp310-cp310-win_amd64.whl", hash = "sha256:ac88856a8cbccfc14f1b2d0b829af354cc1743cb375e7f04251ae73b2af6adf8"},
+ {file = "regex-2021.8.28-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:c206587c83e795d417ed3adc8453a791f6d36b67c81416676cad053b4104152c"},
+ {file = "regex-2021.8.28-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8690ed94481f219a7a967c118abaf71ccc440f69acd583cab721b90eeedb77c"},
+ {file = "regex-2021.8.28-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:328a1fad67445550b982caa2a2a850da5989fd6595e858f02d04636e7f8b0b13"},
+ {file = "regex-2021.8.28-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:c7cb4c512d2d3b0870e00fbbac2f291d4b4bf2634d59a31176a87afe2777c6f0"},
+ {file = "regex-2021.8.28-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:66256b6391c057305e5ae9209941ef63c33a476b73772ca967d4a2df70520ec1"},
+ {file = "regex-2021.8.28-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8e44769068d33e0ea6ccdf4b84d80c5afffe5207aa4d1881a629cf0ef3ec398f"},
+ {file = "regex-2021.8.28-cp36-cp36m-win32.whl", hash = "sha256:08d74bfaa4c7731b8dac0a992c63673a2782758f7cfad34cf9c1b9184f911354"},
+ {file = "regex-2021.8.28-cp36-cp36m-win_amd64.whl", hash = "sha256:abb48494d88e8a82601af905143e0de838c776c1241d92021e9256d5515b3645"},
+ {file = "regex-2021.8.28-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b4c220a1fe0d2c622493b0a1fd48f8f991998fb447d3cd368033a4b86cf1127a"},
+ {file = "regex-2021.8.28-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4a332404baa6665b54e5d283b4262f41f2103c255897084ec8f5487ce7b9e8e"},
+ {file = "regex-2021.8.28-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c61dcc1cf9fd165127a2853e2c31eb4fb961a4f26b394ac9fe5669c7a6592892"},
+ {file = "regex-2021.8.28-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:ee329d0387b5b41a5dddbb6243a21cb7896587a651bebb957e2d2bb8b63c0791"},
+ {file = "regex-2021.8.28-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f60667673ff9c249709160529ab39667d1ae9fd38634e006bec95611f632e759"},
+ {file = "regex-2021.8.28-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b844fb09bd9936ed158ff9df0ab601e2045b316b17aa8b931857365ea8586906"},
+ {file = "regex-2021.8.28-cp37-cp37m-win32.whl", hash = "sha256:4cde065ab33bcaab774d84096fae266d9301d1a2f5519d7bd58fc55274afbf7a"},
+ {file = "regex-2021.8.28-cp37-cp37m-win_amd64.whl", hash = "sha256:1413b5022ed6ac0d504ba425ef02549a57d0f4276de58e3ab7e82437892704fc"},
+ {file = "regex-2021.8.28-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ed4b50355b066796dacdd1cf538f2ce57275d001838f9b132fab80b75e8c84dd"},
+ {file = "regex-2021.8.28-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28fc475f560d8f67cc8767b94db4c9440210f6958495aeae70fac8faec631797"},
+ {file = "regex-2021.8.28-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bdc178caebd0f338d57ae445ef8e9b737ddf8fbc3ea187603f65aec5b041248f"},
+ {file = "regex-2021.8.28-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:999ad08220467b6ad4bd3dd34e65329dd5d0df9b31e47106105e407954965256"},
+ {file = "regex-2021.8.28-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:808ee5834e06f57978da3e003ad9d6292de69d2bf6263662a1a8ae30788e080b"},
+ {file = "regex-2021.8.28-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d5111d4c843d80202e62b4fdbb4920db1dcee4f9366d6b03294f45ed7b18b42e"},
+ {file = "regex-2021.8.28-cp38-cp38-win32.whl", hash = "sha256:473858730ef6d6ff7f7d5f19452184cd0caa062a20047f6d6f3e135a4648865d"},
+ {file = "regex-2021.8.28-cp38-cp38-win_amd64.whl", hash = "sha256:31a99a4796bf5aefc8351e98507b09e1b09115574f7c9dbb9cf2111f7220d2e2"},
+ {file = "regex-2021.8.28-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:04f6b9749e335bb0d2f68c707f23bb1773c3fb6ecd10edf0f04df12a8920d468"},
+ {file = "regex-2021.8.28-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b006628fe43aa69259ec04ca258d88ed19b64791693df59c422b607b6ece8bb"},
+ {file = "regex-2021.8.28-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:121f4b3185feaade3f85f70294aef3f777199e9b5c0c0245c774ae884b110a2d"},
+ {file = "regex-2021.8.28-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a577a21de2ef8059b58f79ff76a4da81c45a75fe0bfb09bc8b7bb4293fa18983"},
+ {file = "regex-2021.8.28-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1743345e30917e8c574f273f51679c294effba6ad372db1967852f12c76759d8"},
+ {file = "regex-2021.8.28-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e1e8406b895aba6caa63d9fd1b6b1700d7e4825f78ccb1e5260551d168db38ed"},
+ {file = "regex-2021.8.28-cp39-cp39-win32.whl", hash = "sha256:ed283ab3a01d8b53de3a05bfdf4473ae24e43caee7dcb5584e86f3f3e5ab4374"},
+ {file = "regex-2021.8.28-cp39-cp39-win_amd64.whl", hash = "sha256:610b690b406653c84b7cb6091facb3033500ee81089867ee7d59e675f9ca2b73"},
+ {file = "regex-2021.8.28.tar.gz", hash = "sha256:f585cbbeecb35f35609edccb95efd95a3e35824cd7752b586503f7e6087303f1"},
+]
+six = [
+ {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
+ {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
+]
+sqlalchemy = [
+ {file = "SQLAlchemy-1.4.23-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:25e9b2e5ca088879ce3740d9ccd4d58cb9061d49566a0b5e12166f403d6f4da0"},
+ {file = "SQLAlchemy-1.4.23-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:d9667260125688c71ccf9af321c37e9fb71c2693575af8210f763bfbbee847c7"},
+ {file = "SQLAlchemy-1.4.23-cp27-cp27m-win32.whl", hash = "sha256:cec1a4c6ddf5f82191301a25504f0e675eccd86635f0d5e4c69e0661691931c5"},
+ {file = "SQLAlchemy-1.4.23-cp27-cp27m-win_amd64.whl", hash = "sha256:ae07895b55c7d58a7dd47438f437ac219c0f09d24c2e7d69fdebc1ea75350f00"},
+ {file = "SQLAlchemy-1.4.23-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:967307ea52985985224a79342527c36ec2d1daa257a39748dd90e001a4be4d90"},
+ {file = "SQLAlchemy-1.4.23-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:be185b3daf651c6c0639987a916bf41e97b60e68f860f27c9cb6574385f5cbb4"},
+ {file = "SQLAlchemy-1.4.23-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a0d3b3d51c83a66f5b72c57e1aad061406e4c390bd42cf1fda94effe82fac81"},
+ {file = "SQLAlchemy-1.4.23-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a8395c4db3e1450eef2b68069abf500cc48af4b442a0d98b5d3c9535fe40cde8"},
+ {file = "SQLAlchemy-1.4.23-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9b128a78581faea7a5ee626ad4471353eee051e4e94616dfeff4742b6e5ba262"},
+ {file = "SQLAlchemy-1.4.23-cp36-cp36m-win32.whl", hash = "sha256:43fc207be06e50158e4dae4cc4f27ce80afbdbfa7c490b3b22feb64f6d9775a0"},
+ {file = "SQLAlchemy-1.4.23-cp36-cp36m-win_amd64.whl", hash = "sha256:e9d4f4552aa5e0d1417fc64a2ce1cdf56a30bab346ba6b0dd5e838eb56db4d29"},
+ {file = "SQLAlchemy-1.4.23-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:512f52a8872e8d63d898e4e158eda17e2ee40b8d2496b3b409422e71016db0bd"},
+ {file = "SQLAlchemy-1.4.23-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:355024cf061ed04271900414eb4a22671520241d2216ddb691bdd8a992172389"},
+ {file = "SQLAlchemy-1.4.23-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:82c03325111eab88d64e0ff48b6fe15c75d23787429fa1d84c0995872e702787"},
+ {file = "SQLAlchemy-1.4.23-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0aa312f9906ecebe133d7f44168c3cae4c76f27a25192fa7682f3fad505543c9"},
+ {file = "SQLAlchemy-1.4.23-cp37-cp37m-win32.whl", hash = "sha256:059c5f41e8630f51741a234e6ba2a034228c11b3b54a15478e61d8b55fa8bd9d"},
+ {file = "SQLAlchemy-1.4.23-cp37-cp37m-win_amd64.whl", hash = "sha256:cd68c5f9d13ffc8f4d6802cceee786678c5b1c668c97bc07b9f4a60883f36cd1"},
+ {file = "SQLAlchemy-1.4.23-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:6a8dbf3d46e889d864a57ee880c4ad3a928db5aa95e3d359cbe0da2f122e50c4"},
+ {file = "SQLAlchemy-1.4.23-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c15191f2430a30082f540ec6f331214746fc974cfdf136d7a1471d1c61d68ff"},
+ {file = "SQLAlchemy-1.4.23-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cd0e85dd2067159848c7672acd517f0c38b7b98867a347411ea01b432003f8d9"},
+ {file = "SQLAlchemy-1.4.23-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:370f4688ce47f0dc1e677a020a4d46252a31a2818fd67f5c256417faefc938af"},
+ {file = "SQLAlchemy-1.4.23-cp38-cp38-win32.whl", hash = "sha256:bd41f8063a9cd11b76d6d7d6af8139ab3c087f5dbbe5a50c02cb8ece7da34d67"},
+ {file = "SQLAlchemy-1.4.23-cp38-cp38-win_amd64.whl", hash = "sha256:2bca9a6e30ee425cc321d988a152a5fe1be519648e7541ac45c36cd4f569421f"},
+ {file = "SQLAlchemy-1.4.23-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:4803a481d4c14ce6ad53dc35458c57821863e9a079695c27603d38355e61fb7f"},
+ {file = "SQLAlchemy-1.4.23-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:07b9099a95dd2b2620498544300eda590741ac54915c6b20809b6de7e3c58090"},
+ {file = "SQLAlchemy-1.4.23-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:37f2bd1b8e32c5999280f846701712347fc0ee7370e016ede2283c71712e127a"},
+ {file = "SQLAlchemy-1.4.23-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:448612570aa1437a5d1b94ada161805778fe80aba5b9a08a403e8ae4e071ded6"},
+ {file = "SQLAlchemy-1.4.23-cp39-cp39-win32.whl", hash = "sha256:e0ce4a2e48fe0a9ea3a5160411a4c5135da5255ed9ac9c15f15f2bcf58c34194"},
+ {file = "SQLAlchemy-1.4.23-cp39-cp39-win_amd64.whl", hash = "sha256:0aa746d1173587743960ff17b89b540e313aacfe6c1e9c81aa48393182c36d4f"},
+ {file = "SQLAlchemy-1.4.23.tar.gz", hash = "sha256:76ff246881f528089bf19385131b966197bb494653990396d2ce138e2a447583"},
+]
+sqlalchemy2-stubs = [
+ {file = "sqlalchemy2-stubs-0.0.2a15.tar.gz", hash = "sha256:9336e0724c985623e055e2db28e8d78a62b40a2628b862c6cd50d7942fd5a238"},
+ {file = "sqlalchemy2_stubs-0.0.2a15-py3-none-any.whl", hash = "sha256:3da19252c87c1ae6bf0e49c2873e72a6e5684a1ede2490c8b75b78834239258b"},
+]
+starlette = [
+ {file = "starlette-0.14.2-py3-none-any.whl", hash = "sha256:3c8e48e52736b3161e34c9f0e8153b4f32ec5d8995a3ee1d59410d92f75162ed"},
+ {file = "starlette-0.14.2.tar.gz", hash = "sha256:7d49f4a27f8742262ef1470608c59ddbc66baf37c148e938c7038e6bc7a998aa"},
+]
+strawberry-graphql = [
+ {file = "strawberry-graphql-0.77.12.tar.gz", hash = "sha256:1adc770643fa8423345a8c4c7a9956172f49a2e01559981be9e4ead6e80611fe"},
+ {file = "strawberry_graphql-0.77.12-py3-none-any.whl", hash = "sha256:2080d93c1a80139b10ae5248ba2e8878c0182362ad9853424eecdd5aebdf3331"},
+]
+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-1.2.1-py3-none-any.whl", hash = "sha256:8dd0e9524d6f386271a36b41dbf6c57d8e32fd96fd22b6584679dc569d20899f"},
+ {file = "tomli-1.2.1.tar.gz", hash = "sha256:a5b75cb6f3968abb47af1b40c1819dc519ea82bcc065776a866e8d74c5ca9442"},
+]
+typed-ast = [
+ {file = "typed_ast-1.4.3-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:2068531575a125b87a41802130fa7e29f26c09a2833fea68d9a40cf33902eba6"},
+ {file = "typed_ast-1.4.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:c907f561b1e83e93fad565bac5ba9c22d96a54e7ea0267c708bffe863cbe4075"},
+ {file = "typed_ast-1.4.3-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:1b3ead4a96c9101bef08f9f7d1217c096f31667617b58de957f690c92378b528"},
+ {file = "typed_ast-1.4.3-cp35-cp35m-win32.whl", hash = "sha256:dde816ca9dac1d9c01dd504ea5967821606f02e510438120091b84e852367428"},
+ {file = "typed_ast-1.4.3-cp35-cp35m-win_amd64.whl", hash = "sha256:777a26c84bea6cd934422ac2e3b78863a37017618b6e5c08f92ef69853e765d3"},
+ {file = "typed_ast-1.4.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f8afcf15cc511ada719a88e013cec87c11aff7b91f019295eb4530f96fe5ef2f"},
+ {file = "typed_ast-1.4.3-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:52b1eb8c83f178ab787f3a4283f68258525f8d70f778a2f6dd54d3b5e5fb4341"},
+ {file = "typed_ast-1.4.3-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:01ae5f73431d21eead5015997ab41afa53aa1fbe252f9da060be5dad2c730ace"},
+ {file = "typed_ast-1.4.3-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:c190f0899e9f9f8b6b7863debfb739abcb21a5c054f911ca3596d12b8a4c4c7f"},
+ {file = "typed_ast-1.4.3-cp36-cp36m-win32.whl", hash = "sha256:398e44cd480f4d2b7ee8d98385ca104e35c81525dd98c519acff1b79bdaac363"},
+ {file = "typed_ast-1.4.3-cp36-cp36m-win_amd64.whl", hash = "sha256:bff6ad71c81b3bba8fa35f0f1921fb24ff4476235a6e94a26ada2e54370e6da7"},
+ {file = "typed_ast-1.4.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0fb71b8c643187d7492c1f8352f2c15b4c4af3f6338f21681d3681b3dc31a266"},
+ {file = "typed_ast-1.4.3-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:760ad187b1041a154f0e4d0f6aae3e40fdb51d6de16e5c99aedadd9246450e9e"},
+ {file = "typed_ast-1.4.3-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5feca99c17af94057417d744607b82dd0a664fd5e4ca98061480fd8b14b18d04"},
+ {file = "typed_ast-1.4.3-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:95431a26309a21874005845c21118c83991c63ea800dd44843e42a916aec5899"},
+ {file = "typed_ast-1.4.3-cp37-cp37m-win32.whl", hash = "sha256:aee0c1256be6c07bd3e1263ff920c325b59849dc95392a05f258bb9b259cf39c"},
+ {file = "typed_ast-1.4.3-cp37-cp37m-win_amd64.whl", hash = "sha256:9ad2c92ec681e02baf81fdfa056fe0d818645efa9af1f1cd5fd6f1bd2bdfd805"},
+ {file = "typed_ast-1.4.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b36b4f3920103a25e1d5d024d155c504080959582b928e91cb608a65c3a49e1a"},
+ {file = "typed_ast-1.4.3-cp38-cp38-manylinux1_i686.whl", hash = "sha256:067a74454df670dcaa4e59349a2e5c81e567d8d65458d480a5b3dfecec08c5ff"},
+ {file = "typed_ast-1.4.3-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7538e495704e2ccda9b234b82423a4038f324f3a10c43bc088a1636180f11a41"},
+ {file = "typed_ast-1.4.3-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:af3d4a73793725138d6b334d9d247ce7e5f084d96284ed23f22ee626a7b88e39"},
+ {file = "typed_ast-1.4.3-cp38-cp38-win32.whl", hash = "sha256:f2362f3cb0f3172c42938946dbc5b7843c2a28aec307c49100c8b38764eb6927"},
+ {file = "typed_ast-1.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:dd4a21253f42b8d2b48410cb31fe501d32f8b9fbeb1f55063ad102fe9c425e40"},
+ {file = "typed_ast-1.4.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f328adcfebed9f11301eaedfa48e15bdece9b519fb27e6a8c01aa52a17ec31b3"},
+ {file = "typed_ast-1.4.3-cp39-cp39-manylinux1_i686.whl", hash = "sha256:2c726c276d09fc5c414693a2de063f521052d9ea7c240ce553316f70656c84d4"},
+ {file = "typed_ast-1.4.3-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:cae53c389825d3b46fb37538441f75d6aecc4174f615d048321b716df2757fb0"},
+ {file = "typed_ast-1.4.3-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:b9574c6f03f685070d859e75c7f9eeca02d6933273b5e69572e5ff9d5e3931c3"},
+ {file = "typed_ast-1.4.3-cp39-cp39-win32.whl", hash = "sha256:209596a4ec71d990d71d5e0d312ac935d86930e6eecff6ccc7007fe54d703808"},
+ {file = "typed_ast-1.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:9c6d1a54552b5330bc657b7ef0eae25d00ba7ffe85d9ea8ae6540d2197a3788c"},
+ {file = "typed_ast-1.4.3.tar.gz", hash = "sha256:fb1bbeac803adea29cedd70781399c99138358c26d05fcbd23c13016b7f5ec65"},
+]
+typing-extensions = [
+ {file = "typing_extensions-3.10.0.2-py2-none-any.whl", hash = "sha256:d8226d10bc02a29bcc81df19a26e56a9647f8b0a6d4a83924139f4a8b01f17b7"},
+ {file = "typing_extensions-3.10.0.2-py3-none-any.whl", hash = "sha256:f1d25edafde516b146ecd0613dabcc61409817af4766fbbcfb8d1ad4ec441a34"},
+ {file = "typing_extensions-3.10.0.2.tar.gz", hash = "sha256:49f75d16ff11f1cd258e1b988ccff82a3ca5570217d7ad8c5f48205dd99a677e"},
+]
+uvicorn = [
+ {file = "uvicorn-0.15.0-py3-none-any.whl", hash = "sha256:17f898c64c71a2640514d4089da2689e5db1ce5d4086c2d53699bf99513421c1"},
+ {file = "uvicorn-0.15.0.tar.gz", hash = "sha256:d9a3c0dd1ca86728d3e235182683b4cf94cd53a867c288eaeca80ee781b2caff"},
+]
+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"},
+]
+watchgod = [
+ {file = "watchgod-0.7-py3-none-any.whl", hash = "sha256:d6c1ea21df37847ac0537ca0d6c2f4cdf513562e95f77bb93abbcf05573407b7"},
+ {file = "watchgod-0.7.tar.gz", hash = "sha256:48140d62b0ebe9dd9cf8381337f06351e1f2e70b2203fa9c6eff4e572ca84f29"},
+]
+websockets = [
+ {file = "websockets-10.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cd8c6f2ec24aedace251017bc7a414525171d4e6578f914acab9349362def4da"},
+ {file = "websockets-10.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:1f6b814cff6aadc4288297cb3a248614829c6e4ff5556593c44a115e9dd49939"},
+ {file = "websockets-10.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:01db0ecd1a0ca6702d02a5ed40413e18b7d22f94afb3bbe0d323bac86c42c1c8"},
+ {file = "websockets-10.0-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:82b17524b1ce6ae7f7dd93e4d18e9b9474071e28b65dbf1dfe9b5767778db379"},
+ {file = "websockets-10.0-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:8bbf8660c3f833ddc8b1afab90213f2e672a9ddac6eecb3cde968e6b2807c1c7"},
+ {file = "websockets-10.0-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:b8176deb6be540a46695960a765a77c28ac8b2e3ef2ec95d50a4f5df901edb1c"},
+ {file = "websockets-10.0-cp37-cp37m-win32.whl", hash = "sha256:706e200fc7f03bed99ad0574cd1ea8b0951477dd18cc978ccb190683c69dba76"},
+ {file = "websockets-10.0-cp37-cp37m-win_amd64.whl", hash = "sha256:5b2600e01c7ca6f840c42c747ffbe0254f319594ed108db847eb3d75f4aacb80"},
+ {file = "websockets-10.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:085bb8a6e780d30eaa1ba48ac7f3a6707f925edea787cfb761ce5a39e77ac09b"},
+ {file = "websockets-10.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:9a4d889162bd48588e80950e07fa5e039eee9deb76a58092e8c3ece96d7ef537"},
+ {file = "websockets-10.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:b4ade7569b6fd17912452f9c3757d96f8e4044016b6d22b3b8391e641ca50456"},
+ {file = "websockets-10.0-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:2a43072e434c041a99f2e1eb9b692df0232a38c37c61d00e9f24db79474329e4"},
+ {file = "websockets-10.0-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:7f79f02c7f9a8320aff7d3321cd1c7e3a7dbc15d922ac996cca827301ee75238"},
+ {file = "websockets-10.0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:1ac35426fe3e7d3d0fac3d63c8965c76ed67a8fd713937be072bf0ce22808539"},
+ {file = "websockets-10.0-cp38-cp38-win32.whl", hash = "sha256:ff59c6bdb87b31f7e2d596f09353d5a38c8c8ff571b0e2238e8ee2d55ad68465"},
+ {file = "websockets-10.0-cp38-cp38-win_amd64.whl", hash = "sha256:d67646ddd17a86117ae21c27005d83c1895c0cef5d7be548b7549646372f868a"},
+ {file = "websockets-10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:82bd921885231f4a30d9bc550552495b3fc36b1235add6d374e7c65c3babd805"},
+ {file = "websockets-10.0-cp39-cp39-manylinux1_i686.whl", hash = "sha256:7d2e12e4f901f1bc062dfdf91831712c4106ed18a9a4cdb65e2e5f502124ca37"},
+ {file = "websockets-10.0-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:71358c7816e2762f3e4af3adf0040f268e219f5a38cb3487a9d0fc2e554fef6a"},
+ {file = "websockets-10.0-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:fe83b3ec9ef34063d86dfe1029160a85f24a5a94271036e5714a57acfdd089a1"},
+ {file = "websockets-10.0-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:eb282127e9c136f860c6068a4fba5756eb25e755baffb5940b6f1eae071928b2"},
+ {file = "websockets-10.0-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:62160772314920397f9d219147f958b33fa27a12c662d4455c9ccbba9a07e474"},
+ {file = "websockets-10.0-cp39-cp39-win32.whl", hash = "sha256:e42a1f1e03437b017af341e9bbfdc09252cd48ef32a8c3c3ead769eab3b17368"},
+ {file = "websockets-10.0-cp39-cp39-win_amd64.whl", hash = "sha256:c5880442f5fc268f1ef6d37b2c152c114deccca73f48e3a8c48004d2f16f4567"},
+ {file = "websockets-10.0.tar.gz", hash = "sha256:c4fc9a1d242317892590abe5b61a9127f1a61740477bfb121743f290b8054002"},
+]
+zipp = [
+ {file = "zipp-3.5.0-py3-none-any.whl", hash = "sha256:957cfda87797e389580cb8b9e3870841ca991e2125350677b2ca83a0e99390a3"},
+ {file = "zipp-3.5.0.tar.gz", hash = "sha256:f5812b1e007e48cff63449a5e9f4e7ebea716b4111f9c4f9a645f91d579bf0c4"},
+]
diff --git a/reddit-clone/pyproject.toml b/reddit-clone/pyproject.toml
new file mode 100644
index 00000000..0715528a
--- /dev/null
+++ b/reddit-clone/pyproject.toml
@@ -0,0 +1,25 @@
+[tool.poetry]
+name = "reddit-graphql"
+version = "0.1.0"
+description = "A reddit API clone built with GraphQL."
+authors = ["Aryan Iyappan "]
+
+[tool.poetry.dependencies]
+python = "^3.7"
+uvicorn = {extras = ["standard"], version = "^0.15.0"}
+SQLAlchemy = {extras = ["mypy"], version = "^1.4.23"}
+alembic = "^1.7.1"
+strawberry-graphql = {extras = ["asgi"], version = "^0.77.0"}
+fastapi = "^0.68.1"
+
+
+[tool.poetry.dev-dependencies]
+black = "^21.8b0"
+flake8 = "^3.9.2"
+flake8-black = "^0.2.3"
+flake8-bugbear = "^21.4.3"
+mypy = "^0.910"
+
+[build-system]
+requires = ["poetry-core>=1.0.0"]
+build-backend = "poetry.core.masonry.api"
diff --git a/reddit-clone/reddit/__init__.py b/reddit-clone/reddit/__init__.py
new file mode 100644
index 00000000..61797eca
--- /dev/null
+++ b/reddit-clone/reddit/__init__.py
@@ -0,0 +1,25 @@
+from fastapi import FastAPI
+from strawberry.asgi import GraphQL
+
+from reddit.core.config import DEBUG
+from reddit.schema import schema
+
+__all__ = ("app",)
+
+
+def create_application() -> FastAPI:
+ """
+ Creates an application instance.
+
+ :return: The created application.
+ """
+ application = FastAPI(title="Reddit GraphQL", debug=DEBUG)
+
+ application.add_route(
+ path="/graphql", route=GraphQL(schema=schema, graphiql=True, debug=DEBUG)
+ )
+
+ return application
+
+
+app = create_application()
diff --git a/reddit-clone/reddit/comments/__init__.py b/reddit-clone/reddit/comments/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/reddit-clone/reddit/comments/model.py b/reddit-clone/reddit/comments/model.py
new file mode 100644
index 00000000..fe4e3f57
--- /dev/null
+++ b/reddit-clone/reddit/comments/model.py
@@ -0,0 +1,7 @@
+from reddit.core.models import BaseModel
+
+
+class Comment(BaseModel):
+ """
+ Represents a Comment in a thread.
+ """
diff --git a/reddit-clone/reddit/core/__init__.py b/reddit-clone/reddit/core/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/reddit-clone/reddit/core/config.py b/reddit-clone/reddit/core/config.py
new file mode 100644
index 00000000..701bbbf2
--- /dev/null
+++ b/reddit-clone/reddit/core/config.py
@@ -0,0 +1,11 @@
+from starlette.config import Config
+from starlette.datastructures import Secret
+
+
+config = Config(env_file=".env")
+
+# whether the application is in development mode.
+DEBUG: bool = config("DEBUG", cast=bool, default=False)
+
+# secret key to use for sessions.
+SECRET_KEY: Secret = config("SECRET_KEY", cast=Secret)
diff --git a/reddit-clone/reddit/core/models.py b/reddit-clone/reddit/core/models.py
new file mode 100644
index 00000000..b755d3d5
--- /dev/null
+++ b/reddit-clone/reddit/core/models.py
@@ -0,0 +1,42 @@
+from typing import Optional
+from datetime import datetime
+
+from sqlalchemy import Column
+from sqlalchemy.ext.declarative import declarative_base
+
+
+Base = declarative_base()
+
+
+class BaseModel(Base):
+ """
+ The base class which every model
+ must subclass/ inherit from.
+ """
+
+ __abstract__ = True
+
+ id: Optional[int] = Column(
+ default=None,
+ primary_key=True,
+ description="""
+ Identifier for the object.
+ """,
+ )
+
+ created_at: datetime = Column(
+ nullable=False,
+ default=datetime.now(),
+ description="""
+ When the object was created.
+ """,
+ )
+
+ updated_at: datetime = Column(
+ nullable=False,
+ default=datetime.now(),
+ onupdate=datetime.now(),
+ description="""
+ When the object was updated.
+ """,
+ )
diff --git a/reddit-clone/reddit/schema.py b/reddit-clone/reddit/schema.py
new file mode 100644
index 00000000..b0f87467
--- /dev/null
+++ b/reddit-clone/reddit/schema.py
@@ -0,0 +1,6 @@
+from strawberry import Schema
+
+__all__ = ("schema",)
+
+
+schema = Schema(query=None, mutation=None)
diff --git a/reddit-clone/reddit/subreddits/__init__.py b/reddit-clone/reddit/subreddits/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/reddit-clone/reddit/subreddits/models.py b/reddit-clone/reddit/subreddits/models.py
new file mode 100644
index 00000000..18e5d27a
--- /dev/null
+++ b/reddit-clone/reddit/subreddits/models.py
@@ -0,0 +1,59 @@
+from typing import Optional
+
+from sqlalchemy import Column, String, Integer, SmallInteger, ForeignKey
+from sqlalchemy.orm import relationship
+
+from reddit.core.models import Base
+
+
+class SubReddit(Base):
+ """
+ Represents a SubReddit.
+ """
+
+ __tablename__ = "subreddits"
+
+ name: str = Column(
+ String(75),
+ unique=True,
+ nullable=False,
+ comment="""
+ The name for the subreddit.
+ """,
+ )
+
+ description: Optional[str] = Column(
+ String(255),
+ default=None,
+ comment="""
+ The description for the subreddit.
+ """,
+ )
+
+ admin_id: int = Column(
+ Integer,
+ ForeignKey("users.id"),
+ comment="""
+ The ID of the subreddit's admin.
+ """,
+ )
+
+ status: int = Column(
+ SmallInteger,
+ comment="""
+ The status of the subreddit.
+ """,
+ )
+
+ icon: Optional[str] = Column(
+ String(255),
+ default=None,
+ comment="""
+ The icon for the subreddit.
+ """,
+ )
+
+ threads = relationship("Thread", backref="subreddit", lazy="dynamic")
+
+ def __repr__(self) -> str:
+ return "" % self.name
diff --git a/reddit-clone/reddit/threads/__init__.py b/reddit-clone/reddit/threads/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/reddit-clone/reddit/threads/models.py b/reddit-clone/reddit/threads/models.py
new file mode 100644
index 00000000..4da12872
--- /dev/null
+++ b/reddit-clone/reddit/threads/models.py
@@ -0,0 +1,74 @@
+from typing import Optional
+
+from sqlalchemy import Column, Integer, String, ForeignKey
+from sqlalchemy.orm import relationship
+
+from reddit.core.models import BaseModel
+
+
+class Thread(BaseModel):
+ """
+ Represents a Thread in a SubReddit.
+ """
+
+ __tablename__ = "threads"
+
+ title: str = Column(
+ String(150),
+ comment="""
+ The title for the thread.
+ """,
+ )
+
+ text: Optional[str] = Column(
+ String(1024),
+ default=None,
+ comment="""
+ The text for the thread.
+ """,
+ )
+
+ link: Optional[str] = Column(
+ String(255),
+ default=None,
+ comment="""
+ The link for the thread.
+ """,
+ )
+
+ thumbnail: Optional[str] = Column(
+ String(255),
+ default=None,
+ comment="""
+ The thumbnail URL for the thread.
+ """,
+ )
+
+ user_id: int = Column(
+ Integer,
+ ForeignKey("users.id"),
+ comment="""
+ The owner ID the thread.
+ """,
+ )
+
+ subreddit_id: int = Column(
+ Integer,
+ ForeignKey("subreddits.id"),
+ comment="""
+ The SubReddit ID of the thread.
+ """,
+ )
+
+ votes: int = Column(
+ Integer,
+ default=1,
+ comment="""
+ The votes for the thread.
+ """,
+ )
+
+ comments = relationship("Comment", backref="thread", lazy="dynamic")
+
+ def __repr__(self) -> str:
+ return "" % self.title
diff --git a/reddit-clone/reddit/users/__init__.py b/reddit-clone/reddit/users/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/reddit-clone/reddit/users/models.py b/reddit-clone/reddit/users/models.py
new file mode 100644
index 00000000..4e6fb2a9
--- /dev/null
+++ b/reddit-clone/reddit/users/models.py
@@ -0,0 +1,57 @@
+from typing import Optional
+
+from sqlalchemy import Column, String
+from sqlalchemy.orm import relationship
+
+from reddit.core.models import Base
+
+
+class User(Base):
+ """
+ Represents an individual user account.
+ """
+
+ __tablename__ = "users"
+
+ username: str = Column(
+ String(32),
+ nullable=False,
+ unique=True,
+ comment="""
+ The username for the user.
+ """,
+ )
+
+ email: str = Column(
+ String(255),
+ nullable=False,
+ unique=True,
+ comment="""
+ The email for the user.
+ """,
+ )
+
+ password: str = Column(
+ String(255),
+ nullable=False,
+ comment="""
+ The password for the user.
+ """,
+ )
+
+ avatar: Optional[str] = Column(
+ String(255),
+ default=None,
+ comment="""
+ The avatar for the user.
+ """,
+ )
+
+ threads = relationship("Thread", backref="user", lazy="dynamic")
+
+ subreddits = relationship("Subreddit", backref="user", lazy="dynamic")
+
+ comments = relationship("Comment", backref="user", lazy="dynamic")
+
+ def __repr__(self) -> str:
+ return "" % self.username
diff --git a/reddit-clone/setup.cfg b/reddit-clone/setup.cfg
new file mode 100644
index 00000000..a4fec64b
--- /dev/null
+++ b/reddit-clone/setup.cfg
@@ -0,0 +1,2 @@
+[mypy]
+plugins = sqlalchemy.ext.mypy.plugin,strawberry.ext.mypy_plugin
From d429da0a28f9bfcf310cb702295b7ff34b8012d7 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan
Date: Tue, 21 Sep 2021 11:07:59 +0530
Subject: [PATCH 002/150] rename model.py to models.py
---
reddit-clone/reddit/comments/{model.py => models.py} | 0
1 file changed, 0 insertions(+), 0 deletions(-)
rename reddit-clone/reddit/comments/{model.py => models.py} (100%)
diff --git a/reddit-clone/reddit/comments/model.py b/reddit-clone/reddit/comments/models.py
similarity index 100%
rename from reddit-clone/reddit/comments/model.py
rename to reddit-clone/reddit/comments/models.py
From 5e48aff6f5402fcc7e7fea6a2f272c1c036a3b30 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan
Date: Tue, 21 Sep 2021 11:11:18 +0530
Subject: [PATCH 003/150] update tech stack
---
reddit-clone/README.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/reddit-clone/README.md b/reddit-clone/README.md
index 78a07283..e4f12e40 100644
--- a/reddit-clone/README.md
+++ b/reddit-clone/README.md
@@ -7,6 +7,7 @@ This example shows you how to create a Reddit API clone using GraphQL.
- Strawberry GraphQL
- FastAPI w/ Starlette
- SQLAlchemy (asyncio)
+- Alembic for migrations
## Features at a glance
From fc413ec2925d5c88daef19473329395adf30484f Mon Sep 17 00:00:00 2001
From: Aryan Iyappan
Date: Tue, 21 Sep 2021 11:16:36 +0530
Subject: [PATCH 004/150] fix base model inheritance
---
reddit-clone/reddit/core/models.py | 2 ++
reddit-clone/reddit/subreddits/models.py | 6 +++---
reddit-clone/reddit/users/models.py | 4 ++--
3 files changed, 7 insertions(+), 5 deletions(-)
diff --git a/reddit-clone/reddit/core/models.py b/reddit-clone/reddit/core/models.py
index b755d3d5..9db24303 100644
--- a/reddit-clone/reddit/core/models.py
+++ b/reddit-clone/reddit/core/models.py
@@ -5,6 +5,8 @@
from sqlalchemy.ext.declarative import declarative_base
+__all__ = ("BaseModel",)
+
Base = declarative_base()
diff --git a/reddit-clone/reddit/subreddits/models.py b/reddit-clone/reddit/subreddits/models.py
index 18e5d27a..3c45b317 100644
--- a/reddit-clone/reddit/subreddits/models.py
+++ b/reddit-clone/reddit/subreddits/models.py
@@ -3,10 +3,10 @@
from sqlalchemy import Column, String, Integer, SmallInteger, ForeignKey
from sqlalchemy.orm import relationship
-from reddit.core.models import Base
+from reddit.core.models import BaseModel
-class SubReddit(Base):
+class SubReddit(BaseModel):
"""
Represents a SubReddit.
"""
@@ -49,7 +49,7 @@ class SubReddit(Base):
String(255),
default=None,
comment="""
- The icon for the subreddit.
+ The icon URL for the subreddit.
""",
)
diff --git a/reddit-clone/reddit/users/models.py b/reddit-clone/reddit/users/models.py
index 4e6fb2a9..c0a653bb 100644
--- a/reddit-clone/reddit/users/models.py
+++ b/reddit-clone/reddit/users/models.py
@@ -3,10 +3,10 @@
from sqlalchemy import Column, String
from sqlalchemy.orm import relationship
-from reddit.core.models import Base
+from reddit.core.models import BaseModel
-class User(Base):
+class User(BaseModel):
"""
Represents an individual user account.
"""
From 9b31170a136dbce9bb872b9fbdf8840d3b4221f8 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan
Date: Tue, 21 Sep 2021 11:18:22 +0530
Subject: [PATCH 005/150] update field comments
---
reddit-clone/reddit/subreddits/models.py | 2 +-
reddit-clone/reddit/threads/models.py | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/reddit-clone/reddit/subreddits/models.py b/reddit-clone/reddit/subreddits/models.py
index 3c45b317..f922a640 100644
--- a/reddit-clone/reddit/subreddits/models.py
+++ b/reddit-clone/reddit/subreddits/models.py
@@ -34,7 +34,7 @@ class SubReddit(BaseModel):
Integer,
ForeignKey("users.id"),
comment="""
- The ID of the subreddit's admin.
+ The admin ID of the subreddit.
""",
)
diff --git a/reddit-clone/reddit/threads/models.py b/reddit-clone/reddit/threads/models.py
index 4da12872..d14e06a1 100644
--- a/reddit-clone/reddit/threads/models.py
+++ b/reddit-clone/reddit/threads/models.py
@@ -48,7 +48,7 @@ class Thread(BaseModel):
Integer,
ForeignKey("users.id"),
comment="""
- The owner ID the thread.
+ The owner ID of the thread.
""",
)
From 8c6bd2148a7e569c37fad4d74817478c3907e656 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan
Date: Tue, 21 Sep 2021 11:21:00 +0530
Subject: [PATCH 006/150] update __init__.py
---
reddit-clone/reddit/__init__.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/reddit-clone/reddit/__init__.py b/reddit-clone/reddit/__init__.py
index 61797eca..a7a2b453 100644
--- a/reddit-clone/reddit/__init__.py
+++ b/reddit-clone/reddit/__init__.py
@@ -15,9 +15,9 @@ def create_application() -> FastAPI:
"""
application = FastAPI(title="Reddit GraphQL", debug=DEBUG)
- application.add_route(
- path="/graphql", route=GraphQL(schema=schema, graphiql=True, debug=DEBUG)
- )
+ graphql_app = GraphQL(schema=schema, graphiql=True, debug=DEBUG)
+
+ application.add_route(path="/graphql", route=graphql_app)
return application
From b4788f7bbcdc50ce31ccd431cd332b2cec800152 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan
Date: Tue, 21 Sep 2021 11:26:27 +0530
Subject: [PATCH 007/150] add example goal
---
reddit-clone/README.md | 3 +++
1 file changed, 3 insertions(+)
diff --git a/reddit-clone/README.md b/reddit-clone/README.md
index e4f12e40..379a3549 100644
--- a/reddit-clone/README.md
+++ b/reddit-clone/README.md
@@ -1,6 +1,9 @@
# Reddit GraphQL API
This example shows you how to create a Reddit API clone using GraphQL.
+The goal of this example is not to re-create the entire reddit API, but
+to produce a simpler version that is easier to understand, and implements
+most of the features that Strawberry gives us.
## Tech Stack used:
From 9ce38ab51ebbf909fbdf08d81bead702222bd64b Mon Sep 17 00:00:00 2001
From: Aryan Iyappan
Date: Tue, 21 Sep 2021 11:47:15 +0530
Subject: [PATCH 008/150] add alembic skeleton
---
reddit-clone/alembic.ini | 100 +++++++++++++++++++++++++
reddit-clone/alembic/.gitkeep | 0
reddit-clone/alembic/README | 1 +
reddit-clone/alembic/env.py | 75 +++++++++++++++++++
reddit-clone/alembic/script.py.mako | 24 ++++++
reddit-clone/reddit/comments/models.py | 34 +++++++++
6 files changed, 234 insertions(+)
create mode 100644 reddit-clone/alembic.ini
delete mode 100644 reddit-clone/alembic/.gitkeep
create mode 100644 reddit-clone/alembic/README
create mode 100644 reddit-clone/alembic/env.py
create mode 100644 reddit-clone/alembic/script.py.mako
diff --git a/reddit-clone/alembic.ini b/reddit-clone/alembic.ini
new file mode 100644
index 00000000..49641573
--- /dev/null
+++ b/reddit-clone/alembic.ini
@@ -0,0 +1,100 @@
+# A generic, single database configuration.
+
+[alembic]
+# path to migration scripts
+script_location = alembic
+
+# template used to generate migration files
+# file_template = %%(rev)s_%%(slug)s
+
+# sys.path path, will be prepended to sys.path if present.
+# defaults to the current working directory.
+prepend_sys_path = .
+
+# timezone to use when rendering the date within the migration file
+# as well as the filename.
+# If specified, requires the python-dateutil library that can be
+# installed by adding `alembic[tz]` to the pip requirements
+# string value is passed to dateutil.tz.gettz()
+# leave blank for localtime
+# timezone =
+
+# max length of characters to apply to the
+# "slug" field
+# truncate_slug_length = 40
+
+# set to 'true' to run the environment during
+# the 'revision' command, regardless of autogenerate
+# revision_environment = false
+
+# set to 'true' to allow .pyc and .pyo files without
+# a source .py file to be detected as revisions in the
+# versions/ directory
+# sourceless = false
+
+# version location specification; This defaults
+# to alembic/versions. When using multiple version
+# directories, initial revisions must be specified with --version-path.
+# The path separator used here should be the separator specified by "version_path_separator"
+# version_locations = %(here)s/bar:%(here)s/bat:alembic/versions
+
+# version path separator; As mentioned above, this is the character used to split
+# version_locations. Valid values are:
+#
+# version_path_separator = :
+# version_path_separator = ;
+# version_path_separator = space
+version_path_separator = os # default: use os.pathsep
+
+# the output encoding used when revision files
+# are written from script.py.mako
+# output_encoding = utf-8
+
+sqlalchemy.url = driver://user:pass@localhost/dbname
+
+
+[post_write_hooks]
+# post_write_hooks defines scripts or Python functions that are run
+# on newly generated revision scripts. See the documentation for further
+# detail and examples
+
+# format using "black" - use the console_scripts runner, against the "black" entrypoint
+# hooks = black
+# black.type = console_scripts
+# black.entrypoint = black
+# black.options = -l 79 REVISION_SCRIPT_FILENAME
+
+# Logging configuration
+[loggers]
+keys = root,sqlalchemy,alembic
+
+[handlers]
+keys = console
+
+[formatters]
+keys = generic
+
+[logger_root]
+level = WARN
+handlers = console
+qualname =
+
+[logger_sqlalchemy]
+level = WARN
+handlers =
+qualname = sqlalchemy.engine
+
+[logger_alembic]
+level = INFO
+handlers =
+qualname = alembic
+
+[handler_console]
+class = StreamHandler
+args = (sys.stderr,)
+level = NOTSET
+formatter = generic
+
+[formatter_generic]
+format = %(levelname)-5.5s [%(name)s] %(message)s
+datefmt = %H:%M:%S
diff --git a/reddit-clone/alembic/.gitkeep b/reddit-clone/alembic/.gitkeep
deleted file mode 100644
index e69de29b..00000000
diff --git a/reddit-clone/alembic/README b/reddit-clone/alembic/README
new file mode 100644
index 00000000..2500aa1b
--- /dev/null
+++ b/reddit-clone/alembic/README
@@ -0,0 +1 @@
+Generic single-database configuration.
diff --git a/reddit-clone/alembic/env.py b/reddit-clone/alembic/env.py
new file mode 100644
index 00000000..788c43d1
--- /dev/null
+++ b/reddit-clone/alembic/env.py
@@ -0,0 +1,75 @@
+from logging.config import fileConfig
+
+from sqlalchemy import engine_from_config
+from sqlalchemy import pool
+
+from alembic import context
+
+# this is the Alembic Config object, which provides
+# access to the values within the .ini file in use.
+config = context.config
+
+# Interpret the config file for Python logging.
+# This line sets up loggers basically.
+fileConfig(config.config_file_name)
+
+# add your model's MetaData object here
+# for 'autogenerate' support
+# from myapp import mymodel
+# target_metadata = mymodel.Base.metadata
+target_metadata = None
+
+# other values from the config, defined by the needs of env.py,
+# can be acquired:
+# my_important_option = config.get_main_option("my_important_option")
+# ... etc.
+
+
+def run_migrations_offline():
+ """Run migrations in 'offline' mode.
+
+ This configures the context with just a URL
+ and not an Engine, though an Engine is acceptable
+ here as well. By skipping the Engine creation
+ we don't even need a DBAPI to be available.
+
+ Calls to context.execute() here emit the given string to the
+ script output.
+
+ """
+ url = config.get_main_option("sqlalchemy.url")
+ context.configure(
+ url=url,
+ target_metadata=target_metadata,
+ literal_binds=True,
+ dialect_opts={"paramstyle": "named"},
+ )
+
+ with context.begin_transaction():
+ context.run_migrations()
+
+
+def run_migrations_online():
+ """Run migrations in 'online' mode.
+
+ In this scenario we need to create an Engine
+ and associate a connection with the context.
+
+ """
+ connectable = engine_from_config(
+ config.get_section(config.config_ini_section),
+ prefix="sqlalchemy.",
+ poolclass=pool.NullPool,
+ )
+
+ with connectable.connect() as connection:
+ context.configure(connection=connection, target_metadata=target_metadata)
+
+ with context.begin_transaction():
+ context.run_migrations()
+
+
+if context.is_offline_mode():
+ run_migrations_offline()
+else:
+ run_migrations_online()
diff --git a/reddit-clone/alembic/script.py.mako b/reddit-clone/alembic/script.py.mako
new file mode 100644
index 00000000..2c015630
--- /dev/null
+++ b/reddit-clone/alembic/script.py.mako
@@ -0,0 +1,24 @@
+"""${message}
+
+Revision ID: ${up_revision}
+Revises: ${down_revision | comma,n}
+Create Date: ${create_date}
+
+"""
+from alembic import op
+import sqlalchemy as sa
+${imports if imports else ""}
+
+# revision identifiers, used by Alembic.
+revision = ${repr(up_revision)}
+down_revision = ${repr(down_revision)}
+branch_labels = ${repr(branch_labels)}
+depends_on = ${repr(depends_on)}
+
+
+def upgrade():
+ ${upgrades if upgrades else "pass"}
+
+
+def downgrade():
+ ${downgrades if downgrades else "pass"}
diff --git a/reddit-clone/reddit/comments/models.py b/reddit-clone/reddit/comments/models.py
index fe4e3f57..70647b01 100644
--- a/reddit-clone/reddit/comments/models.py
+++ b/reddit-clone/reddit/comments/models.py
@@ -1,3 +1,8 @@
+from typing import Optional
+
+from sqlalchemy import Column, Integer, Text, ForeignKey
+from sqlalchemy.orm import relationship
+
from reddit.core.models import BaseModel
@@ -5,3 +10,32 @@ class Comment(BaseModel):
"""
Represents a Comment in a thread.
"""
+
+ content: str = Column(
+ Text,
+ nullable=False,
+ comment="""
+ The content of the comment.
+ """,
+ )
+
+ votes: int = Column(
+ Integer,
+ default=1,
+ comment="""
+ The votes for the thread.
+ """,
+ )
+
+ user_id: Optional[int] = Column(
+ Integer,
+ ForeignKey("users.id"),
+ comment="""
+ The owner ID of the comment.
+ """,
+ )
+
+ replies = relationship("Comment", backref="parent", lazy="dynamic")
+
+ def __repr__(self) -> str:
+ return "" % self.id
From 294396a180f10a8f2d815663aa9cc2292144fa48 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan
Date: Sat, 25 Sep 2021 11:15:02 +0530
Subject: [PATCH 009/150] refactor example structure
---
reddit-clone/reddit/__init__.py | 2 --
reddit-clone/reddit/core/config.py | 3 ++
.../reddit/{comments => db}/__init__.py | 0
reddit-clone/reddit/db/base.py | 5 ++++
reddit-clone/reddit/db/session.py | 28 +++++++++++++++++++
.../reddit/{subreddits => models}/__init__.py | 0
.../reddit/{core/models.py => models/base.py} | 12 +++-----
.../{comments/models.py => models/comment.py} | 2 +-
.../{threads/models.py => models/post.py} | 26 ++++++++---------
.../models.py => models/subreddit.py} | 8 +++---
.../{users/models.py => models/user.py} | 4 +--
reddit-clone/reddit/schema.py | 2 --
reddit-clone/reddit/threads/__init__.py | 0
reddit-clone/reddit/users/__init__.py | 0
14 files changed, 60 insertions(+), 32 deletions(-)
rename reddit-clone/reddit/{comments => db}/__init__.py (100%)
create mode 100644 reddit-clone/reddit/db/base.py
create mode 100644 reddit-clone/reddit/db/session.py
rename reddit-clone/reddit/{subreddits => models}/__init__.py (100%)
rename reddit-clone/reddit/{core/models.py => models/base.py} (79%)
rename reddit-clone/reddit/{comments/models.py => models/comment.py} (95%)
rename reddit-clone/reddit/{threads/models.py => models/post.py} (65%)
rename reddit-clone/reddit/{subreddits/models.py => models/subreddit.py} (85%)
rename reddit-clone/reddit/{users/models.py => models/user.py} (90%)
delete mode 100644 reddit-clone/reddit/threads/__init__.py
delete mode 100644 reddit-clone/reddit/users/__init__.py
diff --git a/reddit-clone/reddit/__init__.py b/reddit-clone/reddit/__init__.py
index a7a2b453..59fce6d2 100644
--- a/reddit-clone/reddit/__init__.py
+++ b/reddit-clone/reddit/__init__.py
@@ -4,8 +4,6 @@
from reddit.core.config import DEBUG
from reddit.schema import schema
-__all__ = ("app",)
-
def create_application() -> FastAPI:
"""
diff --git a/reddit-clone/reddit/core/config.py b/reddit-clone/reddit/core/config.py
index 701bbbf2..1a8a6b83 100644
--- a/reddit-clone/reddit/core/config.py
+++ b/reddit-clone/reddit/core/config.py
@@ -9,3 +9,6 @@
# secret key to use for sessions.
SECRET_KEY: Secret = config("SECRET_KEY", cast=Secret)
+
+# sqlalchemy database url.
+DATABASE_URI: str = config("DATABASE_URI", cast=str)
diff --git a/reddit-clone/reddit/comments/__init__.py b/reddit-clone/reddit/db/__init__.py
similarity index 100%
rename from reddit-clone/reddit/comments/__init__.py
rename to reddit-clone/reddit/db/__init__.py
diff --git a/reddit-clone/reddit/db/base.py b/reddit-clone/reddit/db/base.py
new file mode 100644
index 00000000..8132fcfe
--- /dev/null
+++ b/reddit-clone/reddit/db/base.py
@@ -0,0 +1,5 @@
+from sqlalchemy.ext.declarative import declarative_base
+
+__all__ = ("Base",)
+
+Base = declarative_base()
diff --git a/reddit-clone/reddit/db/session.py b/reddit-clone/reddit/db/session.py
new file mode 100644
index 00000000..3cdada70
--- /dev/null
+++ b/reddit-clone/reddit/db/session.py
@@ -0,0 +1,28 @@
+from contextlib import asynccontextmanager
+
+from sqlalchemy.orm import sessionmaker
+from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
+
+from reddit.core.config import DATABASE_URI
+
+async_engine = create_async_engine(DATABASE_URI, future=True)
+
+session_factory = sessionmaker(bind=async_engine, class_=AsyncSession)
+
+
+@asynccontextmanager
+async def get_session() -> AsyncSession:
+ """
+ Gets a session instance.
+
+ :return: the obtained session.
+ """
+ session = session_factory()
+ try:
+ yield session
+ await session.commit()
+ except Exception as err:
+ await session.rollback()
+ raise err
+ finally:
+ await session.close()
diff --git a/reddit-clone/reddit/subreddits/__init__.py b/reddit-clone/reddit/models/__init__.py
similarity index 100%
rename from reddit-clone/reddit/subreddits/__init__.py
rename to reddit-clone/reddit/models/__init__.py
diff --git a/reddit-clone/reddit/core/models.py b/reddit-clone/reddit/models/base.py
similarity index 79%
rename from reddit-clone/reddit/core/models.py
rename to reddit-clone/reddit/models/base.py
index 9db24303..9d0373d7 100644
--- a/reddit-clone/reddit/core/models.py
+++ b/reddit-clone/reddit/models/base.py
@@ -2,12 +2,8 @@
from datetime import datetime
from sqlalchemy import Column
-from sqlalchemy.ext.declarative import declarative_base
-
-__all__ = ("BaseModel",)
-
-Base = declarative_base()
+from reddit.db.base import Base
class BaseModel(Base):
@@ -21,7 +17,7 @@ class BaseModel(Base):
id: Optional[int] = Column(
default=None,
primary_key=True,
- description="""
+ comment="""
Identifier for the object.
""",
)
@@ -29,7 +25,7 @@ class BaseModel(Base):
created_at: datetime = Column(
nullable=False,
default=datetime.now(),
- description="""
+ comment="""
When the object was created.
""",
)
@@ -38,7 +34,7 @@ class BaseModel(Base):
nullable=False,
default=datetime.now(),
onupdate=datetime.now(),
- description="""
+ comment="""
When the object was updated.
""",
)
diff --git a/reddit-clone/reddit/comments/models.py b/reddit-clone/reddit/models/comment.py
similarity index 95%
rename from reddit-clone/reddit/comments/models.py
rename to reddit-clone/reddit/models/comment.py
index 70647b01..51fd2cc7 100644
--- a/reddit-clone/reddit/comments/models.py
+++ b/reddit-clone/reddit/models/comment.py
@@ -3,7 +3,7 @@
from sqlalchemy import Column, Integer, Text, ForeignKey
from sqlalchemy.orm import relationship
-from reddit.core.models import BaseModel
+from .base import BaseModel
class Comment(BaseModel):
diff --git a/reddit-clone/reddit/threads/models.py b/reddit-clone/reddit/models/post.py
similarity index 65%
rename from reddit-clone/reddit/threads/models.py
rename to reddit-clone/reddit/models/post.py
index d14e06a1..b72dc5be 100644
--- a/reddit-clone/reddit/threads/models.py
+++ b/reddit-clone/reddit/models/post.py
@@ -3,20 +3,20 @@
from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.orm import relationship
-from reddit.core.models import BaseModel
+from .base import BaseModel
-class Thread(BaseModel):
+class Post(BaseModel):
"""
- Represents a Thread in a SubReddit.
+ Represents a post in a Subreddit.
"""
- __tablename__ = "threads"
+ __tablename__ = "posts"
title: str = Column(
String(150),
comment="""
- The title for the thread.
+ The title for the post.
""",
)
@@ -24,7 +24,7 @@ class Thread(BaseModel):
String(1024),
default=None,
comment="""
- The text for the thread.
+ The text for the post.
""",
)
@@ -32,7 +32,7 @@ class Thread(BaseModel):
String(255),
default=None,
comment="""
- The link for the thread.
+ The link for the post.
""",
)
@@ -40,7 +40,7 @@ class Thread(BaseModel):
String(255),
default=None,
comment="""
- The thumbnail URL for the thread.
+ The thumbnail URL for the post.
""",
)
@@ -48,7 +48,7 @@ class Thread(BaseModel):
Integer,
ForeignKey("users.id"),
comment="""
- The owner ID of the thread.
+ The owner ID of the post.
""",
)
@@ -56,7 +56,7 @@ class Thread(BaseModel):
Integer,
ForeignKey("subreddits.id"),
comment="""
- The SubReddit ID of the thread.
+ The SubReddit ID of the post.
""",
)
@@ -64,11 +64,11 @@ class Thread(BaseModel):
Integer,
default=1,
comment="""
- The votes for the thread.
+ The votes for the post.
""",
)
- comments = relationship("Comment", backref="thread", lazy="dynamic")
+ comments = relationship("Comment", backref="post", lazy="dynamic")
def __repr__(self) -> str:
- return "" % self.title
+ return "" % self.title
diff --git a/reddit-clone/reddit/subreddits/models.py b/reddit-clone/reddit/models/subreddit.py
similarity index 85%
rename from reddit-clone/reddit/subreddits/models.py
rename to reddit-clone/reddit/models/subreddit.py
index f922a640..a54c9013 100644
--- a/reddit-clone/reddit/subreddits/models.py
+++ b/reddit-clone/reddit/models/subreddit.py
@@ -3,12 +3,12 @@
from sqlalchemy import Column, String, Integer, SmallInteger, ForeignKey
from sqlalchemy.orm import relationship
-from reddit.core.models import BaseModel
+from .base import BaseModel
-class SubReddit(BaseModel):
+class Subreddit(BaseModel):
"""
- Represents a SubReddit.
+ Represents a Subreddit.
"""
__tablename__ = "subreddits"
@@ -53,7 +53,7 @@ class SubReddit(BaseModel):
""",
)
- threads = relationship("Thread", backref="subreddit", lazy="dynamic")
+ posts = relationship("Post", backref="subreddit", lazy="dynamic")
def __repr__(self) -> str:
return "" % self.name
diff --git a/reddit-clone/reddit/users/models.py b/reddit-clone/reddit/models/user.py
similarity index 90%
rename from reddit-clone/reddit/users/models.py
rename to reddit-clone/reddit/models/user.py
index c0a653bb..6f8308e3 100644
--- a/reddit-clone/reddit/users/models.py
+++ b/reddit-clone/reddit/models/user.py
@@ -3,7 +3,7 @@
from sqlalchemy import Column, String
from sqlalchemy.orm import relationship
-from reddit.core.models import BaseModel
+from .base import BaseModel
class User(BaseModel):
@@ -47,7 +47,7 @@ class User(BaseModel):
""",
)
- threads = relationship("Thread", backref="user", lazy="dynamic")
+ posrs = relationship("Post", backref="user", lazy="dynamic")
subreddits = relationship("Subreddit", backref="user", lazy="dynamic")
diff --git a/reddit-clone/reddit/schema.py b/reddit-clone/reddit/schema.py
index b0f87467..41baebbf 100644
--- a/reddit-clone/reddit/schema.py
+++ b/reddit-clone/reddit/schema.py
@@ -1,6 +1,4 @@
from strawberry import Schema
-__all__ = ("schema",)
-
schema = Schema(query=None, mutation=None)
diff --git a/reddit-clone/reddit/threads/__init__.py b/reddit-clone/reddit/threads/__init__.py
deleted file mode 100644
index e69de29b..00000000
diff --git a/reddit-clone/reddit/users/__init__.py b/reddit-clone/reddit/users/__init__.py
deleted file mode 100644
index e69de29b..00000000
From ac8b8e2cc4f88fc1054221e514bf66167922f46b Mon Sep 17 00:00:00 2001
From: Aryan Iyappan
Date: Sat, 25 Sep 2021 11:17:26 +0530
Subject: [PATCH 010/150] update model comments
---
reddit-clone/reddit/models/user.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/reddit-clone/reddit/models/user.py b/reddit-clone/reddit/models/user.py
index 6f8308e3..e05ca18c 100644
--- a/reddit-clone/reddit/models/user.py
+++ b/reddit-clone/reddit/models/user.py
@@ -43,7 +43,7 @@ class User(BaseModel):
String(255),
default=None,
comment="""
- The avatar for the user.
+ The avatar URL for the user.
""",
)
From 458f223041099ad1970b36e3ef20f753d188dfc9 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan
Date: Sat, 25 Sep 2021 11:24:29 +0530
Subject: [PATCH 011/150] update typing
---
reddit-clone/reddit/db/session.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/reddit-clone/reddit/db/session.py b/reddit-clone/reddit/db/session.py
index 3cdada70..357101a2 100644
--- a/reddit-clone/reddit/db/session.py
+++ b/reddit-clone/reddit/db/session.py
@@ -1,4 +1,5 @@
from contextlib import asynccontextmanager
+from typing import Generator
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
@@ -11,7 +12,7 @@
@asynccontextmanager
-async def get_session() -> AsyncSession:
+async def get_session() -> Generator[AsyncSession]:
"""
Gets a session instance.
From 900d2597f739de8ecf4615535c8417b83996fce8 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+codebyaryan@users.noreply.github.com>
Date: Sat, 25 Sep 2021 17:01:14 +0530
Subject: [PATCH 012/150] Update env.py
---
reddit-clone/alembic/env.py | 8 +++-----
1 file changed, 3 insertions(+), 5 deletions(-)
diff --git a/reddit-clone/alembic/env.py b/reddit-clone/alembic/env.py
index 788c43d1..842f8fd9 100644
--- a/reddit-clone/alembic/env.py
+++ b/reddit-clone/alembic/env.py
@@ -13,11 +13,9 @@
# This line sets up loggers basically.
fileConfig(config.config_file_name)
-# add your model's MetaData object here
-# for 'autogenerate' support
-# from myapp import mymodel
-# target_metadata = mymodel.Base.metadata
-target_metadata = None
+from reddit.db.base import Base # noqa
+
+target_metadata = Base.metadata
# other values from the config, defined by the needs of env.py,
# can be acquired:
From fa12496df79fe1d9cffefb49797826db8bd8eced Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
<66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Sat, 25 Sep 2021 11:31:24 +0000
Subject: [PATCH 013/150] [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
---
reddit-clone/alembic/env.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/reddit-clone/alembic/env.py b/reddit-clone/alembic/env.py
index 842f8fd9..c78f0514 100644
--- a/reddit-clone/alembic/env.py
+++ b/reddit-clone/alembic/env.py
@@ -13,7 +13,7 @@
# This line sets up loggers basically.
fileConfig(config.config_file_name)
-from reddit.db.base import Base # noqa
+from reddit.db.base import Base # noqa
target_metadata = Base.metadata
From 972acd433923c95d968a213de1dc839b7f3ec098 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+codebyaryan@users.noreply.github.com>
Date: Sat, 25 Sep 2021 17:35:12 +0530
Subject: [PATCH 014/150] Update alembic.ini
---
reddit-clone/alembic.ini | 26 --------------------------
1 file changed, 26 deletions(-)
diff --git a/reddit-clone/alembic.ini b/reddit-clone/alembic.ini
index 49641573..eb6b4537 100644
--- a/reddit-clone/alembic.ini
+++ b/reddit-clone/alembic.ini
@@ -38,32 +38,6 @@ prepend_sys_path = .
# The path separator used here should be the separator specified by "version_path_separator"
# version_locations = %(here)s/bar:%(here)s/bat:alembic/versions
-# version path separator; As mentioned above, this is the character used to split
-# version_locations. Valid values are:
-#
-# version_path_separator = :
-# version_path_separator = ;
-# version_path_separator = space
-version_path_separator = os # default: use os.pathsep
-
-# the output encoding used when revision files
-# are written from script.py.mako
-# output_encoding = utf-8
-
-sqlalchemy.url = driver://user:pass@localhost/dbname
-
-
-[post_write_hooks]
-# post_write_hooks defines scripts or Python functions that are run
-# on newly generated revision scripts. See the documentation for further
-# detail and examples
-
-# format using "black" - use the console_scripts runner, against the "black" entrypoint
-# hooks = black
-# black.type = console_scripts
-# black.entrypoint = black
-# black.options = -l 79 REVISION_SCRIPT_FILENAME
-
# Logging configuration
[loggers]
keys = root,sqlalchemy,alembic
From 45502a5fb07d23fb19831ce2db1e32d5a0f383ef Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+codebyaryan@users.noreply.github.com>
Date: Sat, 25 Sep 2021 17:37:18 +0530
Subject: [PATCH 015/150] Update post.py
---
reddit-clone/reddit/models/post.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/reddit-clone/reddit/models/post.py b/reddit-clone/reddit/models/post.py
index b72dc5be..b330a45f 100644
--- a/reddit-clone/reddit/models/post.py
+++ b/reddit-clone/reddit/models/post.py
@@ -31,6 +31,7 @@ class Post(BaseModel):
link: Optional[str] = Column(
String(255),
default=None,
+ unique=True,
comment="""
The link for the post.
""",
From b75378bd89019ce9132441260a81c0aa0b4dfe50 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+codebyaryan@users.noreply.github.com>
Date: Sat, 25 Sep 2021 17:39:26 +0530
Subject: [PATCH 016/150] Update user.py
---
reddit-clone/reddit/models/user.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/reddit-clone/reddit/models/user.py b/reddit-clone/reddit/models/user.py
index e05ca18c..5efdd992 100644
--- a/reddit-clone/reddit/models/user.py
+++ b/reddit-clone/reddit/models/user.py
@@ -47,7 +47,7 @@ class User(BaseModel):
""",
)
- posrs = relationship("Post", backref="user", lazy="dynamic")
+ posts = relationship("Post", backref="user", lazy="dynamic")
subreddits = relationship("Subreddit", backref="user", lazy="dynamic")
From d100613bf1ec4ed8cc1ae20f75aacb1ef7cd9612 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+codebyaryan@users.noreply.github.com>
Date: Sat, 25 Sep 2021 18:47:21 +0530
Subject: [PATCH 017/150] Update user.py
---
reddit-clone/reddit/models/user.py | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/reddit-clone/reddit/models/user.py b/reddit-clone/reddit/models/user.py
index 5efdd992..cf9ce918 100644
--- a/reddit-clone/reddit/models/user.py
+++ b/reddit-clone/reddit/models/user.py
@@ -49,9 +49,16 @@ class User(BaseModel):
posts = relationship("Post", backref="user", lazy="dynamic")
- subreddits = relationship("Subreddit", backref="user", lazy="dynamic")
+ subreddits = relationship("Subreddit", backref="user", secondary="subreddit_users", lazy="dynamic")
comments = relationship("Comment", backref="user", lazy="dynamic")
def __repr__(self) -> str:
return "" % self.username
+
+
+class SubredditUser(BaseModel):
+ """
+ Represents a Subreddit-user relationship.
+ """
+ __tablename__ = "subreddit_users"
From 19d6dad3228f63bcdd08f03a0074134907b7b58e Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
<66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Sat, 25 Sep 2021 13:17:30 +0000
Subject: [PATCH 018/150] [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
---
reddit-clone/reddit/models/user.py | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/reddit-clone/reddit/models/user.py b/reddit-clone/reddit/models/user.py
index cf9ce918..3035d499 100644
--- a/reddit-clone/reddit/models/user.py
+++ b/reddit-clone/reddit/models/user.py
@@ -49,7 +49,9 @@ class User(BaseModel):
posts = relationship("Post", backref="user", lazy="dynamic")
- subreddits = relationship("Subreddit", backref="user", secondary="subreddit_users", lazy="dynamic")
+ subreddits = relationship(
+ "Subreddit", backref="user", secondary="subreddit_users", lazy="dynamic"
+ )
comments = relationship("Comment", backref="user", lazy="dynamic")
@@ -61,4 +63,5 @@ class SubredditUser(BaseModel):
"""
Represents a Subreddit-user relationship.
"""
+
__tablename__ = "subreddit_users"
From 4e8a60e7bc246859c92848dcb169d791dd7d08f1 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+codebyaryan@users.noreply.github.com>
Date: Sat, 25 Sep 2021 18:50:46 +0530
Subject: [PATCH 019/150] Update user.py
---
reddit-clone/reddit/models/user.py | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/reddit-clone/reddit/models/user.py b/reddit-clone/reddit/models/user.py
index 3035d499..cf813744 100644
--- a/reddit-clone/reddit/models/user.py
+++ b/reddit-clone/reddit/models/user.py
@@ -53,6 +53,8 @@ class User(BaseModel):
"Subreddit", backref="user", secondary="subreddit_users", lazy="dynamic"
)
+
+
comments = relationship("Comment", backref="user", lazy="dynamic")
def __repr__(self) -> str:
@@ -65,3 +67,21 @@ class SubredditUser(BaseModel):
"""
__tablename__ = "subreddit_users"
+
+ user_id: int = Column(
+ Integer,
+ ForeignKey("users.id"),
+ nullable=False,
+ comment="""
+ The relationship's user ID.
+ """,
+ )
+
+ subreddit_id: int = Column(
+ Integer,
+ ForeignKey("subreddits.id"),
+ nullable=False,
+ comment="""
+ The relationship's subreddit ID.
+ """,
+ )
From bc85d467c0c185179e597c7a39e7d1f1a5509899 Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
<66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Sat, 25 Sep 2021 13:20:56 +0000
Subject: [PATCH 020/150] [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
---
reddit-clone/reddit/models/user.py | 2 --
1 file changed, 2 deletions(-)
diff --git a/reddit-clone/reddit/models/user.py b/reddit-clone/reddit/models/user.py
index cf813744..056b73ee 100644
--- a/reddit-clone/reddit/models/user.py
+++ b/reddit-clone/reddit/models/user.py
@@ -53,8 +53,6 @@ class User(BaseModel):
"Subreddit", backref="user", secondary="subreddit_users", lazy="dynamic"
)
-
-
comments = relationship("Comment", backref="user", lazy="dynamic")
def __repr__(self) -> str:
From 8b0bf797864e2a52ee96988b48f352aea87e9d32 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+codebyaryan@users.noreply.github.com>
Date: Sat, 25 Sep 2021 18:53:04 +0530
Subject: [PATCH 021/150] Update user.py
---
reddit-clone/reddit/models/user.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/reddit-clone/reddit/models/user.py b/reddit-clone/reddit/models/user.py
index 056b73ee..9701021f 100644
--- a/reddit-clone/reddit/models/user.py
+++ b/reddit-clone/reddit/models/user.py
@@ -69,7 +69,7 @@ class SubredditUser(BaseModel):
user_id: int = Column(
Integer,
ForeignKey("users.id"),
- nullable=False,
+ primary_key=True,
comment="""
The relationship's user ID.
""",
@@ -78,7 +78,7 @@ class SubredditUser(BaseModel):
subreddit_id: int = Column(
Integer,
ForeignKey("subreddits.id"),
- nullable=False,
+ primary_key=True,
comment="""
The relationship's subreddit ID.
""",
From 3204fbbc9f5f61956dd0c3756d214790a3166b6b Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+codebyaryan@users.noreply.github.com>
Date: Sun, 26 Sep 2021 13:41:39 +0530
Subject: [PATCH 022/150] Create database.py
---
reddit-clone/reddit/database.py | 29 +++++++++++++++++++++++++++++
1 file changed, 29 insertions(+)
create mode 100644 reddit-clone/reddit/database.py
diff --git a/reddit-clone/reddit/database.py b/reddit-clone/reddit/database.py
new file mode 100644
index 00000000..357101a2
--- /dev/null
+++ b/reddit-clone/reddit/database.py
@@ -0,0 +1,29 @@
+from contextlib import asynccontextmanager
+from typing import Generator
+
+from sqlalchemy.orm import sessionmaker
+from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
+
+from reddit.core.config import DATABASE_URI
+
+async_engine = create_async_engine(DATABASE_URI, future=True)
+
+session_factory = sessionmaker(bind=async_engine, class_=AsyncSession)
+
+
+@asynccontextmanager
+async def get_session() -> Generator[AsyncSession]:
+ """
+ Gets a session instance.
+
+ :return: the obtained session.
+ """
+ session = session_factory()
+ try:
+ yield session
+ await session.commit()
+ except Exception as err:
+ await session.rollback()
+ raise err
+ finally:
+ await session.close()
From 61ae172c7bfb903449d26979656b358366f544fa Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+codebyaryan@users.noreply.github.com>
Date: Sun, 26 Sep 2021 13:42:54 +0530
Subject: [PATCH 023/150] Update database.py
---
reddit-clone/reddit/database.py | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/reddit-clone/reddit/database.py b/reddit-clone/reddit/database.py
index 357101a2..e862ffa2 100644
--- a/reddit-clone/reddit/database.py
+++ b/reddit-clone/reddit/database.py
@@ -3,6 +3,8 @@
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
+from sqlalchemy.ext.declarative import declarative_base
+
from reddit.core.config import DATABASE_URI
@@ -27,3 +29,5 @@ async def get_session() -> Generator[AsyncSession]:
raise err
finally:
await session.close()
+
+Base = declarative_base()
From 64b1a9a0f8df4457e0ee300f9ab48ce3e0755a38 Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
<66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Sun, 26 Sep 2021 08:13:04 +0000
Subject: [PATCH 024/150] [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
---
reddit-clone/reddit/database.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/reddit-clone/reddit/database.py b/reddit-clone/reddit/database.py
index e862ffa2..9b529274 100644
--- a/reddit-clone/reddit/database.py
+++ b/reddit-clone/reddit/database.py
@@ -30,4 +30,5 @@ async def get_session() -> Generator[AsyncSession]:
finally:
await session.close()
+
Base = declarative_base()
From cc4fb355ef3d038ce35fd7b8dd8050ae8536b8c2 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+codebyaryan@users.noreply.github.com>
Date: Sun, 26 Sep 2021 13:43:18 +0530
Subject: [PATCH 025/150] Delete reddit-clone/reddit/db directory
---
reddit-clone/reddit/db/__init__.py | 0
reddit-clone/reddit/db/base.py | 5 -----
reddit-clone/reddit/db/session.py | 29 -----------------------------
3 files changed, 34 deletions(-)
delete mode 100644 reddit-clone/reddit/db/__init__.py
delete mode 100644 reddit-clone/reddit/db/base.py
delete mode 100644 reddit-clone/reddit/db/session.py
diff --git a/reddit-clone/reddit/db/__init__.py b/reddit-clone/reddit/db/__init__.py
deleted file mode 100644
index e69de29b..00000000
diff --git a/reddit-clone/reddit/db/base.py b/reddit-clone/reddit/db/base.py
deleted file mode 100644
index 8132fcfe..00000000
--- a/reddit-clone/reddit/db/base.py
+++ /dev/null
@@ -1,5 +0,0 @@
-from sqlalchemy.ext.declarative import declarative_base
-
-__all__ = ("Base",)
-
-Base = declarative_base()
diff --git a/reddit-clone/reddit/db/session.py b/reddit-clone/reddit/db/session.py
deleted file mode 100644
index 357101a2..00000000
--- a/reddit-clone/reddit/db/session.py
+++ /dev/null
@@ -1,29 +0,0 @@
-from contextlib import asynccontextmanager
-from typing import Generator
-
-from sqlalchemy.orm import sessionmaker
-from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
-
-from reddit.core.config import DATABASE_URI
-
-async_engine = create_async_engine(DATABASE_URI, future=True)
-
-session_factory = sessionmaker(bind=async_engine, class_=AsyncSession)
-
-
-@asynccontextmanager
-async def get_session() -> Generator[AsyncSession]:
- """
- Gets a session instance.
-
- :return: the obtained session.
- """
- session = session_factory()
- try:
- yield session
- await session.commit()
- except Exception as err:
- await session.rollback()
- raise err
- finally:
- await session.close()
From 5dc145424d75dda874d3981a21299f0a9a15d6e8 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+codebyaryan@users.noreply.github.com>
Date: Sun, 26 Sep 2021 13:43:41 +0530
Subject: [PATCH 026/150] Update env.py
---
reddit-clone/alembic/env.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/reddit-clone/alembic/env.py b/reddit-clone/alembic/env.py
index c78f0514..bea3fde4 100644
--- a/reddit-clone/alembic/env.py
+++ b/reddit-clone/alembic/env.py
@@ -13,7 +13,7 @@
# This line sets up loggers basically.
fileConfig(config.config_file_name)
-from reddit.db.base import Base # noqa
+from reddit.database import Base # noqa
target_metadata = Base.metadata
From 4317bbe22ea77586b7889e6df1365abe86abc5b7 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+codebyaryan@users.noreply.github.com>
Date: Sun, 26 Sep 2021 13:45:00 +0530
Subject: [PATCH 027/150] Create settings.py
---
reddit-clone/reddit/settings.py | 14 ++++++++++++++
1 file changed, 14 insertions(+)
create mode 100644 reddit-clone/reddit/settings.py
diff --git a/reddit-clone/reddit/settings.py b/reddit-clone/reddit/settings.py
new file mode 100644
index 00000000..1a8a6b83
--- /dev/null
+++ b/reddit-clone/reddit/settings.py
@@ -0,0 +1,14 @@
+from starlette.config import Config
+from starlette.datastructures import Secret
+
+
+config = Config(env_file=".env")
+
+# whether the application is in development mode.
+DEBUG: bool = config("DEBUG", cast=bool, default=False)
+
+# secret key to use for sessions.
+SECRET_KEY: Secret = config("SECRET_KEY", cast=Secret)
+
+# sqlalchemy database url.
+DATABASE_URI: str = config("DATABASE_URI", cast=str)
From 8280d5a0fcfad4d11d0e5197ead061f42e826fdb Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+codebyaryan@users.noreply.github.com>
Date: Sun, 26 Sep 2021 13:58:19 +0530
Subject: [PATCH 028/150] Update database.py
---
reddit-clone/reddit/database.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/reddit-clone/reddit/database.py b/reddit-clone/reddit/database.py
index 9b529274..82bb1e54 100644
--- a/reddit-clone/reddit/database.py
+++ b/reddit-clone/reddit/database.py
@@ -6,7 +6,7 @@
from sqlalchemy.ext.declarative import declarative_base
-from reddit.core.config import DATABASE_URI
+from .settings import DATABASE_URI
async_engine = create_async_engine(DATABASE_URI, future=True)
From 74dd9133137a687db54118e100f366e22582dd1f Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+codebyaryan@users.noreply.github.com>
Date: Sun, 26 Sep 2021 13:58:30 +0530
Subject: [PATCH 029/150] Delete reddit-clone/reddit/core directory
---
reddit-clone/reddit/core/__init__.py | 0
reddit-clone/reddit/core/config.py | 14 --------------
2 files changed, 14 deletions(-)
delete mode 100644 reddit-clone/reddit/core/__init__.py
delete mode 100644 reddit-clone/reddit/core/config.py
diff --git a/reddit-clone/reddit/core/__init__.py b/reddit-clone/reddit/core/__init__.py
deleted file mode 100644
index e69de29b..00000000
diff --git a/reddit-clone/reddit/core/config.py b/reddit-clone/reddit/core/config.py
deleted file mode 100644
index 1a8a6b83..00000000
--- a/reddit-clone/reddit/core/config.py
+++ /dev/null
@@ -1,14 +0,0 @@
-from starlette.config import Config
-from starlette.datastructures import Secret
-
-
-config = Config(env_file=".env")
-
-# whether the application is in development mode.
-DEBUG: bool = config("DEBUG", cast=bool, default=False)
-
-# secret key to use for sessions.
-SECRET_KEY: Secret = config("SECRET_KEY", cast=Secret)
-
-# sqlalchemy database url.
-DATABASE_URI: str = config("DATABASE_URI", cast=str)
From 8ac9df72df2cab6f89c89afede9d0a1d7a6ba8c7 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Thu, 30 Sep 2021 08:42:01 +0530
Subject: [PATCH 030/150] refactor project structure
---
.idea/.gitignore | 3 +
.idea/inspectionProfiles/Project_Default.xml | 46 +++
.../inspectionProfiles/profiles_settings.xml | 6 +
.idea/misc.xml | 4 +
.idea/modules.xml | 8 +
.idea/strawberry-examples.iml | 12 +
.idea/vcs.xml | 6 +
.../models/__init__.py => .projectroot} | 0
reddit-clone/poetry.lock | 299 ++++++++----------
reddit-clone/reddit/comments/__init__.py | 0
reddit-clone/reddit/comments/models.py | 23 ++
reddit-clone/reddit/database.py | 5 +-
reddit-clone/reddit/models/base.py | 40 ---
reddit-clone/reddit/models/comment.py | 41 ---
reddit-clone/reddit/models/post.py | 75 -----
reddit-clone/reddit/models/subreddit.py | 59 ----
reddit-clone/reddit/models/user.py | 85 -----
reddit-clone/reddit/posts/__init__.py | 0
reddit-clone/reddit/posts/models.py | 35 ++
reddit-clone/reddit/subreddits/__init__.py | 0
reddit-clone/reddit/subreddits/models.py | 31 ++
reddit-clone/reddit/users/__init__.py | 0
reddit-clone/reddit/users/models.py | 50 +++
23 files changed, 366 insertions(+), 462 deletions(-)
create mode 100644 .idea/.gitignore
create mode 100644 .idea/inspectionProfiles/Project_Default.xml
create mode 100644 .idea/inspectionProfiles/profiles_settings.xml
create mode 100644 .idea/misc.xml
create mode 100644 .idea/modules.xml
create mode 100644 .idea/strawberry-examples.iml
create mode 100644 .idea/vcs.xml
rename reddit-clone/{reddit/models/__init__.py => .projectroot} (100%)
create mode 100644 reddit-clone/reddit/comments/__init__.py
create mode 100644 reddit-clone/reddit/comments/models.py
delete mode 100644 reddit-clone/reddit/models/base.py
delete mode 100644 reddit-clone/reddit/models/comment.py
delete mode 100644 reddit-clone/reddit/models/post.py
delete mode 100644 reddit-clone/reddit/models/subreddit.py
delete mode 100644 reddit-clone/reddit/models/user.py
create mode 100644 reddit-clone/reddit/posts/__init__.py
create mode 100644 reddit-clone/reddit/posts/models.py
create mode 100644 reddit-clone/reddit/subreddits/__init__.py
create mode 100644 reddit-clone/reddit/subreddits/models.py
create mode 100644 reddit-clone/reddit/users/__init__.py
create mode 100644 reddit-clone/reddit/users/models.py
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 00000000..26d33521
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 00000000..3c6e0d59
--- /dev/null
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml
new file mode 100644
index 00000000..cc5462da
--- /dev/null
+++ b/.idea/inspectionProfiles/profiles_settings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 00000000..2a9526cd
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 00000000..1e10262b
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/.idea/strawberry-examples.iml b/.idea/strawberry-examples.iml
new file mode 100644
index 00000000..292aeb08
--- /dev/null
+++ b/.idea/strawberry-examples.iml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 00000000..5ace414d
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/reddit-clone/reddit/models/__init__.py b/reddit-clone/.projectroot
similarity index 100%
rename from reddit-clone/reddit/models/__init__.py
rename to reddit-clone/.projectroot
diff --git a/reddit-clone/poetry.lock b/reddit-clone/poetry.lock
index eef41ea6..79b5fd46 100644
--- a/reddit-clone/poetry.lock
+++ b/reddit-clone/poetry.lock
@@ -146,7 +146,7 @@ toml = "*"
[[package]]
name = "flake8-bugbear"
-version = "21.9.1"
+version = "21.9.2"
description = "A plugin for flake8 finding likely bugs and design problems in your program. Contains warnings that don't belong in pyflakes and pycodestyle."
category = "dev"
optional = false
@@ -169,7 +169,7 @@ python-versions = ">=3.6,<4"
[[package]]
name = "greenlet"
-version = "1.1.1"
+version = "1.1.2"
description = "Lightweight in-process concurrent programming"
category = "main"
optional = false
@@ -296,7 +296,7 @@ python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
[[package]]
name = "platformdirs"
-version = "2.3.0"
+version = "2.4.0"
description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
category = "dev"
optional = false
@@ -388,7 +388,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
[[package]]
name = "regex"
-version = "2021.8.28"
+version = "2021.9.24"
description = "Alternative regular expression module, to replace re."
category = "dev"
optional = false
@@ -404,29 +404,30 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
[[package]]
name = "sqlalchemy"
-version = "1.4.23"
+version = "1.4.25"
description = "Database Abstraction Library"
category = "main"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7"
[package.dependencies]
-greenlet = {version = "!=0.4.17", markers = "python_version >= \"3\" and platform_machine in \"x86_64 X86_64 aarch64 AARCH64 ppc64le PPC64LE amd64 AMD64 win32 WIN32\""}
+greenlet = {version = "!=0.4.17", markers = "python_version >= \"3\" and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\")"}
importlib-metadata = {version = "*", markers = "python_version < \"3.8\""}
mypy = {version = ">=0.910", optional = true, markers = "python_version >= \"3\" and extra == \"mypy\""}
sqlalchemy2-stubs = {version = "*", optional = true, markers = "extra == \"mypy\""}
[package.extras]
aiomysql = ["greenlet (!=0.4.17)", "aiomysql"]
-aiosqlite = ["greenlet (!=0.4.17)", "aiosqlite"]
+aiosqlite = ["typing_extensions (!=3.10.0.1)", "greenlet (!=0.4.17)", "aiosqlite"]
asyncio = ["greenlet (!=0.4.17)"]
+asyncmy = ["greenlet (!=0.4.17)", "asyncmy (>=0.2.0)"]
mariadb_connector = ["mariadb (>=1.0.1)"]
mssql = ["pyodbc"]
mssql_pymssql = ["pymssql"]
mssql_pyodbc = ["pyodbc"]
mypy = ["sqlalchemy2-stubs", "mypy (>=0.910)"]
mysql = ["mysqlclient (>=1.4.0,<2)", "mysqlclient (>=1.4.0)"]
-mysql_connector = ["mysqlconnector"]
+mysql_connector = ["mysql-connector-python"]
oracle = ["cx_oracle (>=7,<8)", "cx_oracle (>=7)"]
postgresql = ["psycopg2 (>=2.7)"]
postgresql_asyncpg = ["greenlet (!=0.4.17)", "asyncpg"]
@@ -438,7 +439,7 @@ sqlcipher = ["sqlcipher3-binary"]
[[package]]
name = "sqlalchemy2-stubs"
-version = "0.0.2a15"
+version = "0.0.2a17"
description = "Typing Stubs for SQLAlchemy 1.4"
category = "main"
optional = false
@@ -573,7 +574,7 @@ python-versions = ">=3.7"
[[package]]
name = "zipp"
-version = "3.5.0"
+version = "3.6.0"
description = "Backport of pathlib-compatible object wrapper for zip files"
category = "main"
optional = false
@@ -630,64 +631,64 @@ flake8-black = [
{file = "flake8_black-0.2.3-py3-none-any.whl", hash = "sha256:cc080ba5b3773b69ba102b6617a00cc4ecbad8914109690cfda4d565ea435d96"},
]
flake8-bugbear = [
- {file = "flake8-bugbear-21.9.1.tar.gz", hash = "sha256:2f60c8ce0dc53d51da119faab2d67dea978227f0f92ed3c44eb7d65fb2e06a96"},
- {file = "flake8_bugbear-21.9.1-py36.py37.py38-none-any.whl", hash = "sha256:45bfdccfb9f2d8aa140e33cac8f46f1e38215c13d5aa8650e7e188d84e2f94c6"},
+ {file = "flake8-bugbear-21.9.2.tar.gz", hash = "sha256:db9a09893a6c649a197f5350755100bb1dd84f110e60cf532fdfa07e41808ab2"},
+ {file = "flake8_bugbear-21.9.2-py36.py37.py38-none-any.whl", hash = "sha256:4f7eaa6f05b7d7ea4cbbde93f7bcdc5438e79320fa1ec420d860c181af38b769"},
]
graphql-core = [
{file = "graphql-core-3.1.6.tar.gz", hash = "sha256:e65975b6a13878f9113a1fa5320760585b522d139944e005936b1b8358d0651a"},
{file = "graphql_core-3.1.6-py3-none-any.whl", hash = "sha256:c78d09596d347e1cffd266c5384abfedf43ed1eae08729773bebb3d527fe5a14"},
]
greenlet = [
- {file = "greenlet-1.1.1-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:476ba9435afaead4382fbab8f1882f75e3fb2285c35c9285abb3dd30237f9142"},
- {file = "greenlet-1.1.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:44556302c0ab376e37939fd0058e1f0db2e769580d340fb03b01678d1ff25f68"},
- {file = "greenlet-1.1.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:40abb7fec4f6294225d2b5464bb6d9552050ded14a7516588d6f010e7e366dcc"},
- {file = "greenlet-1.1.1-cp27-cp27m-win32.whl", hash = "sha256:a11b6199a0b9dc868990456a2667167d0ba096c5224f6258e452bfbe5a9742c5"},
- {file = "greenlet-1.1.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e22a82d2b416d9227a500c6860cf13e74060cf10e7daf6695cbf4e6a94e0eee4"},
- {file = "greenlet-1.1.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:bad269e442f1b7ffa3fa8820b3c3aa66f02a9f9455b5ba2db5a6f9eea96f56de"},
- {file = "greenlet-1.1.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:8ddb38fb6ad96c2ef7468ff73ba5c6876b63b664eebb2c919c224261ae5e8378"},
- {file = "greenlet-1.1.1-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:84782c80a433d87530ae3f4b9ed58d4a57317d9918dfcc6a59115fa2d8731f2c"},
- {file = "greenlet-1.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac991947ca6533ada4ce7095f0e28fe25d5b2f3266ad5b983ed4201e61596acf"},
- {file = "greenlet-1.1.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5317701c7ce167205c0569c10abc4bd01c7f4cf93f642c39f2ce975fa9b78a3c"},
- {file = "greenlet-1.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4870b018ca685ff573edd56b93f00a122f279640732bb52ce3a62b73ee5c4a92"},
- {file = "greenlet-1.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:990e0f5e64bcbc6bdbd03774ecb72496224d13b664aa03afd1f9b171a3269272"},
- {file = "greenlet-1.1.1-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:a414f8e14aa7bacfe1578f17c11d977e637d25383b6210587c29210af995ef04"},
- {file = "greenlet-1.1.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:e02780da03f84a671bb4205c5968c120f18df081236d7b5462b380fd4f0b497b"},
- {file = "greenlet-1.1.1-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:dfcb5a4056e161307d103bc013478892cfd919f1262c2bb8703220adcb986362"},
- {file = "greenlet-1.1.1-cp35-cp35m-win32.whl", hash = "sha256:655ab836324a473d4cd8cf231a2d6f283ed71ed77037679da554e38e606a7117"},
- {file = "greenlet-1.1.1-cp35-cp35m-win_amd64.whl", hash = "sha256:6ce9d0784c3c79f3e5c5c9c9517bbb6c7e8aa12372a5ea95197b8a99402aa0e6"},
- {file = "greenlet-1.1.1-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:3fc6a447735749d651d8919da49aab03c434a300e9f0af1c886d560405840fd1"},
- {file = "greenlet-1.1.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:8039f5fe8030c43cd1732d9a234fdcbf4916fcc32e21745ca62e75023e4d4649"},
- {file = "greenlet-1.1.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:fddfb31aa2ac550b938d952bca8a87f1db0f8dc930ffa14ce05b5c08d27e7fd1"},
- {file = "greenlet-1.1.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b97a807437b81f90f85022a9dcfd527deea38368a3979ccb49d93c9198b2c722"},
- {file = "greenlet-1.1.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cf31e894dabb077a35bbe6963285d4515a387ff657bd25b0530c7168e48f167f"},
- {file = "greenlet-1.1.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4eae94de9924bbb4d24960185363e614b1b62ff797c23dc3c8a7c75bbb8d187e"},
- {file = "greenlet-1.1.1-cp36-cp36m-win32.whl", hash = "sha256:c1862f9f1031b1dee3ff00f1027fcd098ffc82120f43041fe67804b464bbd8a7"},
- {file = "greenlet-1.1.1-cp36-cp36m-win_amd64.whl", hash = "sha256:9b02e6039eafd75e029d8c58b7b1f3e450ca563ef1fe21c7e3e40b9936c8d03e"},
- {file = "greenlet-1.1.1-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:84488516639c3c5e5c0e52f311fff94ebc45b56788c2a3bfe9cf8e75670f4de3"},
- {file = "greenlet-1.1.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:3f8fc59bc5d64fa41f58b0029794f474223693fd00016b29f4e176b3ee2cfd9f"},
- {file = "greenlet-1.1.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:3e594015a2349ec6dcceda9aca29da8dc89e85b56825b7d1f138a3f6bb79dd4c"},
- {file = "greenlet-1.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e41f72f225192d5d4df81dad2974a8943b0f2d664a2a5cfccdf5a01506f5523c"},
- {file = "greenlet-1.1.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:75ff270fd05125dce3303e9216ccddc541a9e072d4fc764a9276d44dee87242b"},
- {file = "greenlet-1.1.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5cde7ee190196cbdc078511f4df0be367af85636b84d8be32230f4871b960687"},
- {file = "greenlet-1.1.1-cp37-cp37m-win32.whl", hash = "sha256:f253dad38605486a4590f9368ecbace95865fea0f2b66615d121ac91fd1a1563"},
- {file = "greenlet-1.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a91ee268f059583176c2c8b012a9fce7e49ca6b333a12bbc2dd01fc1a9783885"},
- {file = "greenlet-1.1.1-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:34e6675167a238bede724ee60fe0550709e95adaff6a36bcc97006c365290384"},
- {file = "greenlet-1.1.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:bf3725d79b1ceb19e83fb1aed44095518c0fcff88fba06a76c0891cfd1f36837"},
- {file = "greenlet-1.1.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:5c3b735ccf8fc8048664ee415f8af5a3a018cc92010a0d7195395059b4b39b7d"},
- {file = "greenlet-1.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2002a59453858c7f3404690ae80f10c924a39f45f6095f18a985a1234c37334"},
- {file = "greenlet-1.1.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:04e1849c88aa56584d4a0a6e36af5ec7cc37993fdc1fda72b56aa1394a92ded3"},
- {file = "greenlet-1.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8d4ed48eed7414ccb2aaaecbc733ed2a84c299714eae3f0f48db085342d5629"},
- {file = "greenlet-1.1.1-cp38-cp38-win32.whl", hash = "sha256:2f89d74b4f423e756a018832cd7a0a571e0a31b9ca59323b77ce5f15a437629b"},
- {file = "greenlet-1.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:d15cb6f8706678dc47fb4e4f8b339937b04eda48a0af1cca95f180db552e7663"},
- {file = "greenlet-1.1.1-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:b050dbb96216db273b56f0e5960959c2b4cb679fe1e58a0c3906fa0a60c00662"},
- {file = "greenlet-1.1.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:6e0696525500bc8aa12eae654095d2260db4dc95d5c35af2b486eae1bf914ccd"},
- {file = "greenlet-1.1.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:07e6d88242e09b399682b39f8dfa1e7e6eca66b305de1ff74ed9eb1a7d8e539c"},
- {file = "greenlet-1.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:98b491976ed656be9445b79bc57ed21decf08a01aaaf5fdabf07c98c108111f6"},
- {file = "greenlet-1.1.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05e72db813c28906cdc59bd0da7c325d9b82aa0b0543014059c34c8c4ad20e16"},
- {file = "greenlet-1.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:090126004c8ab9cd0787e2acf63d79e80ab41a18f57d6448225bbfcba475034f"},
- {file = "greenlet-1.1.1-cp39-cp39-win32.whl", hash = "sha256:1796f2c283faab2b71c67e9b9aefb3f201fdfbee5cb55001f5ffce9125f63a45"},
- {file = "greenlet-1.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:4adaf53ace289ced90797d92d767d37e7cdc29f13bd3830c3f0a561277a4ae83"},
- {file = "greenlet-1.1.1.tar.gz", hash = "sha256:c0f22774cd8294078bdf7392ac73cf00bfa1e5e0ed644bd064fdabc5f2a2f481"},
+ {file = "greenlet-1.1.2-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:58df5c2a0e293bf665a51f8a100d3e9956febfbf1d9aaf8c0677cf70218910c6"},
+ {file = "greenlet-1.1.2-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:aec52725173bd3a7b56fe91bc56eccb26fbdff1386ef123abb63c84c5b43b63a"},
+ {file = "greenlet-1.1.2-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:833e1551925ed51e6b44c800e71e77dacd7e49181fdc9ac9a0bf3714d515785d"},
+ {file = "greenlet-1.1.2-cp27-cp27m-win32.whl", hash = "sha256:aa5b467f15e78b82257319aebc78dd2915e4c1436c3c0d1ad6f53e47ba6e2713"},
+ {file = "greenlet-1.1.2-cp27-cp27m-win_amd64.whl", hash = "sha256:40b951f601af999a8bf2ce8c71e8aaa4e8c6f78ff8afae7b808aae2dc50d4c40"},
+ {file = "greenlet-1.1.2-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:95e69877983ea39b7303570fa6760f81a3eec23d0e3ab2021b7144b94d06202d"},
+ {file = "greenlet-1.1.2-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:356b3576ad078c89a6107caa9c50cc14e98e3a6c4874a37c3e0273e4baf33de8"},
+ {file = "greenlet-1.1.2-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:8639cadfda96737427330a094476d4c7a56ac03de7265622fcf4cfe57c8ae18d"},
+ {file = "greenlet-1.1.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:97e5306482182170ade15c4b0d8386ded995a07d7cc2ca8f27958d34d6736497"},
+ {file = "greenlet-1.1.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e6a36bb9474218c7a5b27ae476035497a6990e21d04c279884eb10d9b290f1b1"},
+ {file = "greenlet-1.1.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:abb7a75ed8b968f3061327c433a0fbd17b729947b400747c334a9c29a9af6c58"},
+ {file = "greenlet-1.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:14d4f3cd4e8b524ae9b8aa567858beed70c392fdec26dbdb0a8a418392e71708"},
+ {file = "greenlet-1.1.2-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:17ff94e7a83aa8671a25bf5b59326ec26da379ace2ebc4411d690d80a7fbcf23"},
+ {file = "greenlet-1.1.2-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:9f3cba480d3deb69f6ee2c1825060177a22c7826431458c697df88e6aeb3caee"},
+ {file = "greenlet-1.1.2-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:fa877ca7f6b48054f847b61d6fa7bed5cebb663ebc55e018fda12db09dcc664c"},
+ {file = "greenlet-1.1.2-cp35-cp35m-win32.whl", hash = "sha256:7cbd7574ce8e138bda9df4efc6bf2ab8572c9aff640d8ecfece1b006b68da963"},
+ {file = "greenlet-1.1.2-cp35-cp35m-win_amd64.whl", hash = "sha256:903bbd302a2378f984aef528f76d4c9b1748f318fe1294961c072bdc7f2ffa3e"},
+ {file = "greenlet-1.1.2-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:049fe7579230e44daef03a259faa24511d10ebfa44f69411d99e6a184fe68073"},
+ {file = "greenlet-1.1.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:dd0b1e9e891f69e7675ba5c92e28b90eaa045f6ab134ffe70b52e948aa175b3c"},
+ {file = "greenlet-1.1.2-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:7418b6bfc7fe3331541b84bb2141c9baf1ec7132a7ecd9f375912eca810e714e"},
+ {file = "greenlet-1.1.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f9d29ca8a77117315101425ec7ec2a47a22ccf59f5593378fc4077ac5b754fce"},
+ {file = "greenlet-1.1.2-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:21915eb821a6b3d9d8eefdaf57d6c345b970ad722f856cd71739493ce003ad08"},
+ {file = "greenlet-1.1.2-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eff9d20417ff9dcb0d25e2defc2574d10b491bf2e693b4e491914738b7908168"},
+ {file = "greenlet-1.1.2-cp36-cp36m-win32.whl", hash = "sha256:32ca72bbc673adbcfecb935bb3fb1b74e663d10a4b241aaa2f5a75fe1d1f90aa"},
+ {file = "greenlet-1.1.2-cp36-cp36m-win_amd64.whl", hash = "sha256:f0214eb2a23b85528310dad848ad2ac58e735612929c8072f6093f3585fd342d"},
+ {file = "greenlet-1.1.2-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:b92e29e58bef6d9cfd340c72b04d74c4b4e9f70c9fa7c78b674d1fec18896dc4"},
+ {file = "greenlet-1.1.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:fdcec0b8399108577ec290f55551d926d9a1fa6cad45882093a7a07ac5ec147b"},
+ {file = "greenlet-1.1.2-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:93f81b134a165cc17123626ab8da2e30c0455441d4ab5576eed73a64c025b25c"},
+ {file = "greenlet-1.1.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e12bdc622676ce47ae9abbf455c189e442afdde8818d9da983085df6312e7a1"},
+ {file = "greenlet-1.1.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8c790abda465726cfb8bb08bd4ca9a5d0a7bd77c7ac1ca1b839ad823b948ea28"},
+ {file = "greenlet-1.1.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f276df9830dba7a333544bd41070e8175762a7ac20350786b322b714b0e654f5"},
+ {file = "greenlet-1.1.2-cp37-cp37m-win32.whl", hash = "sha256:64e6175c2e53195278d7388c454e0b30997573f3f4bd63697f88d855f7a6a1fc"},
+ {file = "greenlet-1.1.2-cp37-cp37m-win_amd64.whl", hash = "sha256:b11548073a2213d950c3f671aa88e6f83cda6e2fb97a8b6317b1b5b33d850e06"},
+ {file = "greenlet-1.1.2-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:9633b3034d3d901f0a46b7939f8c4d64427dfba6bbc5a36b1a67364cf148a1b0"},
+ {file = "greenlet-1.1.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:eb6ea6da4c787111adf40f697b4e58732ee0942b5d3bd8f435277643329ba627"},
+ {file = "greenlet-1.1.2-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:f3acda1924472472ddd60c29e5b9db0cec629fbe3c5c5accb74d6d6d14773478"},
+ {file = "greenlet-1.1.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e859fcb4cbe93504ea18008d1df98dee4f7766db66c435e4882ab35cf70cac43"},
+ {file = "greenlet-1.1.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:00e44c8afdbe5467e4f7b5851be223be68adb4272f44696ee71fe46b7036a711"},
+ {file = "greenlet-1.1.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec8c433b3ab0419100bd45b47c9c8551248a5aee30ca5e9d399a0b57ac04651b"},
+ {file = "greenlet-1.1.2-cp38-cp38-win32.whl", hash = "sha256:288c6a76705dc54fba69fbcb59904ae4ad768b4c768839b8ca5fdadec6dd8cfd"},
+ {file = "greenlet-1.1.2-cp38-cp38-win_amd64.whl", hash = "sha256:8d2f1fb53a421b410751887eb4ff21386d119ef9cde3797bf5e7ed49fb51a3b3"},
+ {file = "greenlet-1.1.2-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:166eac03e48784a6a6e0e5f041cfebb1ab400b394db188c48b3a84737f505b67"},
+ {file = "greenlet-1.1.2-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:572e1787d1460da79590bf44304abbc0a2da944ea64ec549188fa84d89bba7ab"},
+ {file = "greenlet-1.1.2-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:be5f425ff1f5f4b3c1e33ad64ab994eed12fc284a6ea71c5243fd564502ecbe5"},
+ {file = "greenlet-1.1.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1692f7d6bc45e3200844be0dba153612103db241691088626a33ff1f24a0d88"},
+ {file = "greenlet-1.1.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7227b47e73dedaa513cdebb98469705ef0d66eb5a1250144468e9c3097d6b59b"},
+ {file = "greenlet-1.1.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ff61ff178250f9bb3cd89752df0f1dd0e27316a8bd1465351652b1b4a4cdfd3"},
+ {file = "greenlet-1.1.2-cp39-cp39-win32.whl", hash = "sha256:f70a9e237bb792c7cc7e44c531fd48f5897961701cdaa06cf22fc14965c496cf"},
+ {file = "greenlet-1.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:013d61294b6cd8fe3242932c1c5e36e5d1db2c8afb58606c5a67efce62c1f5fd"},
+ {file = "greenlet-1.1.2.tar.gz", hash = "sha256:e30f5ea4ae2346e62cedde8794a56858a67b878dd79f7df76a0767e356b1744a"},
]
h11 = [
{file = "h11-0.12.0-py3-none-any.whl", hash = "sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6"},
@@ -723,22 +724,12 @@ mako = [
{file = "Mako-1.1.5.tar.gz", hash = "sha256:169fa52af22a91900d852e937400e79f535496191c63712e3b9fda5a9bed6fc3"},
]
markupsafe = [
- {file = "MarkupSafe-2.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d8446c54dc28c01e5a2dbac5a25f071f6653e6e40f3a8818e8b45d790fe6ef53"},
- {file = "MarkupSafe-2.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:36bc903cbb393720fad60fc28c10de6acf10dc6cc883f3e24ee4012371399a38"},
- {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d7d807855b419fc2ed3e631034685db6079889a1f01d5d9dac950f764da3dad"},
- {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:add36cb2dbb8b736611303cd3bfcee00afd96471b09cda130da3581cbdc56a6d"},
- {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:168cd0a3642de83558a5153c8bd34f175a9a6e7f6dc6384b9655d2697312a646"},
- {file = "MarkupSafe-2.0.1-cp310-cp310-win32.whl", hash = "sha256:99df47edb6bda1249d3e80fdabb1dab8c08ef3975f69aed437cb69d0a5de1e28"},
- {file = "MarkupSafe-2.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:e0f138900af21926a02425cf736db95be9f4af72ba1bb21453432a07f6082134"},
{file = "MarkupSafe-2.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f9081981fe268bd86831e5c75f7de206ef275defcb82bc70740ae6dc507aee51"},
{file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:0955295dd5eec6cb6cc2fe1698f4c6d84af2e92de33fbcac4111913cd100a6ff"},
{file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:0446679737af14f45767963a1a9ef7620189912317d095f2d9ffa183a4d25d2b"},
{file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:f826e31d18b516f653fe296d967d700fddad5901ae07c622bb3705955e1faa94"},
{file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:fa130dd50c57d53368c9d59395cb5526eda596d3ffe36666cd81a44d56e48872"},
{file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:905fec760bd2fa1388bb5b489ee8ee5f7291d692638ea5f67982d968366bef9f"},
- {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf5d821ffabf0ef3533c39c518f3357b171a1651c1ff6827325e4489b0e46c3c"},
- {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0d4b31cc67ab36e3392bbf3862cfbadac3db12bdd8b02a2731f509ed5b829724"},
- {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:baa1a4e8f868845af802979fcdbf0bb11f94f1cb7ced4c4b8a351bb60d108145"},
{file = "MarkupSafe-2.0.1-cp36-cp36m-win32.whl", hash = "sha256:6c4ca60fa24e85fe25b912b01e62cb969d69a23a5d5867682dd3e80b5b02581d"},
{file = "MarkupSafe-2.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b2f4bf27480f5e5e8ce285a8c8fd176c0b03e93dcc6646477d4630e83440c6a9"},
{file = "MarkupSafe-2.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0717a7390a68be14b8c793ba258e075c6f4ca819f15edfc2a3a027c823718567"},
@@ -747,21 +738,14 @@ markupsafe = [
{file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:d7f9850398e85aba693bb640262d3611788b1f29a79f0c93c565694658f4071f"},
{file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:6a7fae0dd14cf60ad5ff42baa2e95727c3d81ded453457771d02b7d2b3f9c0c2"},
{file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:b7f2d075102dc8c794cbde1947378051c4e5180d52d276987b8d28a3bd58c17d"},
- {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9936f0b261d4df76ad22f8fee3ae83b60d7c3e871292cd42f40b81b70afae85"},
- {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2a7d351cbd8cfeb19ca00de495e224dea7e7d919659c2841bbb7f420ad03e2d6"},
- {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:60bf42e36abfaf9aff1f50f52644b336d4f0a3fd6d8a60ca0d054ac9f713a864"},
{file = "MarkupSafe-2.0.1-cp37-cp37m-win32.whl", hash = "sha256:a30e67a65b53ea0a5e62fe23682cfe22712e01f453b95233b25502f7c61cb415"},
{file = "MarkupSafe-2.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:611d1ad9a4288cf3e3c16014564df047fe08410e628f89805e475368bd304914"},
- {file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5bb28c636d87e840583ee3adeb78172efc47c8b26127267f54a9c0ec251d41a9"},
{file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:be98f628055368795d818ebf93da628541e10b75b41c559fdf36d104c5787066"},
{file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1d609f577dc6e1aa17d746f8bd3c31aa4d258f4070d61b2aa5c4166c1539de35"},
{file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7d91275b0245b1da4d4cfa07e0faedd5b0812efc15b702576d103293e252af1b"},
{file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:01a9b8ea66f1658938f65b93a85ebe8bc016e6769611be228d797c9d998dd298"},
{file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:47ab1e7b91c098ab893b828deafa1203de86d0bc6ab587b160f78fe6c4011f75"},
{file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:97383d78eb34da7e1fa37dd273c20ad4320929af65d156e35a5e2d89566d9dfb"},
- {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fcf051089389abe060c9cd7caa212c707e58153afa2c649f00346ce6d260f1b"},
- {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5855f8438a7d1d458206a2466bf82b0f104a3724bf96a1c781ab731e4201731a"},
- {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3dd007d54ee88b46be476e293f48c85048603f5f516008bee124ddd891398ed6"},
{file = "MarkupSafe-2.0.1-cp38-cp38-win32.whl", hash = "sha256:023cb26ec21ece8dc3907c0e8320058b2e0cb3c55cf9564da612bc325bed5e64"},
{file = "MarkupSafe-2.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:984d76483eb32f1bcb536dc27e4ad56bba4baa70be32fa87152832cdd9db0833"},
{file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2ef54abee730b502252bcdf31b10dacb0a416229b72c18b19e24a4509f273d26"},
@@ -771,9 +755,6 @@ markupsafe = [
{file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:4efca8f86c54b22348a5467704e3fec767b2db12fc39c6d963168ab1d3fc9135"},
{file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:ab3ef638ace319fa26553db0624c4699e31a28bb2a835c5faca8f8acf6a5a902"},
{file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:f8ba0e8349a38d3001fae7eadded3f6606f0da5d748ee53cc1dab1d6527b9509"},
- {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c47adbc92fc1bb2b3274c4b3a43ae0e4573d9fbff4f54cd484555edbf030baf1"},
- {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:37205cac2a79194e3750b0af2a5720d95f786a55ce7df90c3af697bfa100eaac"},
- {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:1f2ade76b9903f39aa442b4aadd2177decb66525062db244b35d71d0ee8599b6"},
{file = "MarkupSafe-2.0.1-cp39-cp39-win32.whl", hash = "sha256:10f82115e21dc0dfec9ab5c0223652f7197feb168c940f3ef61563fc2d6beb74"},
{file = "MarkupSafe-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8"},
{file = "MarkupSafe-2.0.1.tar.gz", hash = "sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a"},
@@ -816,8 +797,8 @@ pathspec = [
{file = "pathspec-0.9.0.tar.gz", hash = "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"},
]
platformdirs = [
- {file = "platformdirs-2.3.0-py3-none-any.whl", hash = "sha256:8003ac87717ae2c7ee1ea5a84a1a61e87f3fbd16eb5aadba194ea30a9019f648"},
- {file = "platformdirs-2.3.0.tar.gz", hash = "sha256:15b056538719b1c94bdaccb29e5f81879c7f7f0f4a153f46086d155dffcd4f0f"},
+ {file = "platformdirs-2.4.0-py3-none-any.whl", hash = "sha256:8868bbe3c3c80d42f20156f22e7131d2fb321f5bc86a2a345375c6481a67021d"},
+ {file = "platformdirs-2.4.0.tar.gz", hash = "sha256:367a5e80b3d04d2428ffa76d33f124cf11e8fff2acdaa9b43d545f5c7d661ef2"},
]
pycodestyle = [
{file = "pycodestyle-2.7.0-py2.py3-none-any.whl", hash = "sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068"},
@@ -898,87 +879,87 @@ pyyaml = [
{file = "PyYAML-5.4.1.tar.gz", hash = "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e"},
]
regex = [
- {file = "regex-2021.8.28-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9d05ad5367c90814099000442b2125535e9d77581855b9bee8780f1b41f2b1a2"},
- {file = "regex-2021.8.28-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3bf1bc02bc421047bfec3343729c4bbbea42605bcfd6d6bfe2c07ade8b12d2a"},
- {file = "regex-2021.8.28-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f6a808044faae658f546dd5f525e921de9fa409de7a5570865467f03a626fc0"},
- {file = "regex-2021.8.28-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a617593aeacc7a691cc4af4a4410031654f2909053bd8c8e7db837f179a630eb"},
- {file = "regex-2021.8.28-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:79aef6b5cd41feff359acaf98e040844613ff5298d0d19c455b3d9ae0bc8c35a"},
- {file = "regex-2021.8.28-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:0fc1f8f06977c2d4f5e3d3f0d4a08089be783973fc6b6e278bde01f0544ff308"},
- {file = "regex-2021.8.28-cp310-cp310-win32.whl", hash = "sha256:6eebf512aa90751d5ef6a7c2ac9d60113f32e86e5687326a50d7686e309f66ed"},
- {file = "regex-2021.8.28-cp310-cp310-win_amd64.whl", hash = "sha256:ac88856a8cbccfc14f1b2d0b829af354cc1743cb375e7f04251ae73b2af6adf8"},
- {file = "regex-2021.8.28-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:c206587c83e795d417ed3adc8453a791f6d36b67c81416676cad053b4104152c"},
- {file = "regex-2021.8.28-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8690ed94481f219a7a967c118abaf71ccc440f69acd583cab721b90eeedb77c"},
- {file = "regex-2021.8.28-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:328a1fad67445550b982caa2a2a850da5989fd6595e858f02d04636e7f8b0b13"},
- {file = "regex-2021.8.28-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:c7cb4c512d2d3b0870e00fbbac2f291d4b4bf2634d59a31176a87afe2777c6f0"},
- {file = "regex-2021.8.28-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:66256b6391c057305e5ae9209941ef63c33a476b73772ca967d4a2df70520ec1"},
- {file = "regex-2021.8.28-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8e44769068d33e0ea6ccdf4b84d80c5afffe5207aa4d1881a629cf0ef3ec398f"},
- {file = "regex-2021.8.28-cp36-cp36m-win32.whl", hash = "sha256:08d74bfaa4c7731b8dac0a992c63673a2782758f7cfad34cf9c1b9184f911354"},
- {file = "regex-2021.8.28-cp36-cp36m-win_amd64.whl", hash = "sha256:abb48494d88e8a82601af905143e0de838c776c1241d92021e9256d5515b3645"},
- {file = "regex-2021.8.28-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b4c220a1fe0d2c622493b0a1fd48f8f991998fb447d3cd368033a4b86cf1127a"},
- {file = "regex-2021.8.28-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4a332404baa6665b54e5d283b4262f41f2103c255897084ec8f5487ce7b9e8e"},
- {file = "regex-2021.8.28-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c61dcc1cf9fd165127a2853e2c31eb4fb961a4f26b394ac9fe5669c7a6592892"},
- {file = "regex-2021.8.28-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:ee329d0387b5b41a5dddbb6243a21cb7896587a651bebb957e2d2bb8b63c0791"},
- {file = "regex-2021.8.28-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f60667673ff9c249709160529ab39667d1ae9fd38634e006bec95611f632e759"},
- {file = "regex-2021.8.28-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b844fb09bd9936ed158ff9df0ab601e2045b316b17aa8b931857365ea8586906"},
- {file = "regex-2021.8.28-cp37-cp37m-win32.whl", hash = "sha256:4cde065ab33bcaab774d84096fae266d9301d1a2f5519d7bd58fc55274afbf7a"},
- {file = "regex-2021.8.28-cp37-cp37m-win_amd64.whl", hash = "sha256:1413b5022ed6ac0d504ba425ef02549a57d0f4276de58e3ab7e82437892704fc"},
- {file = "regex-2021.8.28-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ed4b50355b066796dacdd1cf538f2ce57275d001838f9b132fab80b75e8c84dd"},
- {file = "regex-2021.8.28-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28fc475f560d8f67cc8767b94db4c9440210f6958495aeae70fac8faec631797"},
- {file = "regex-2021.8.28-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bdc178caebd0f338d57ae445ef8e9b737ddf8fbc3ea187603f65aec5b041248f"},
- {file = "regex-2021.8.28-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:999ad08220467b6ad4bd3dd34e65329dd5d0df9b31e47106105e407954965256"},
- {file = "regex-2021.8.28-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:808ee5834e06f57978da3e003ad9d6292de69d2bf6263662a1a8ae30788e080b"},
- {file = "regex-2021.8.28-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d5111d4c843d80202e62b4fdbb4920db1dcee4f9366d6b03294f45ed7b18b42e"},
- {file = "regex-2021.8.28-cp38-cp38-win32.whl", hash = "sha256:473858730ef6d6ff7f7d5f19452184cd0caa062a20047f6d6f3e135a4648865d"},
- {file = "regex-2021.8.28-cp38-cp38-win_amd64.whl", hash = "sha256:31a99a4796bf5aefc8351e98507b09e1b09115574f7c9dbb9cf2111f7220d2e2"},
- {file = "regex-2021.8.28-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:04f6b9749e335bb0d2f68c707f23bb1773c3fb6ecd10edf0f04df12a8920d468"},
- {file = "regex-2021.8.28-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b006628fe43aa69259ec04ca258d88ed19b64791693df59c422b607b6ece8bb"},
- {file = "regex-2021.8.28-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:121f4b3185feaade3f85f70294aef3f777199e9b5c0c0245c774ae884b110a2d"},
- {file = "regex-2021.8.28-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a577a21de2ef8059b58f79ff76a4da81c45a75fe0bfb09bc8b7bb4293fa18983"},
- {file = "regex-2021.8.28-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1743345e30917e8c574f273f51679c294effba6ad372db1967852f12c76759d8"},
- {file = "regex-2021.8.28-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e1e8406b895aba6caa63d9fd1b6b1700d7e4825f78ccb1e5260551d168db38ed"},
- {file = "regex-2021.8.28-cp39-cp39-win32.whl", hash = "sha256:ed283ab3a01d8b53de3a05bfdf4473ae24e43caee7dcb5584e86f3f3e5ab4374"},
- {file = "regex-2021.8.28-cp39-cp39-win_amd64.whl", hash = "sha256:610b690b406653c84b7cb6091facb3033500ee81089867ee7d59e675f9ca2b73"},
- {file = "regex-2021.8.28.tar.gz", hash = "sha256:f585cbbeecb35f35609edccb95efd95a3e35824cd7752b586503f7e6087303f1"},
+ {file = "regex-2021.9.24-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0628ed7d6334e8f896f882a5c1240de8c4d9b0dd7c7fb8e9f4692f5684b7d656"},
+ {file = "regex-2021.9.24-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3baf3eaa41044d4ced2463fd5d23bf7bd4b03d68739c6c99a59ce1f95599a673"},
+ {file = "regex-2021.9.24-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c000635fd78400a558bd7a3c2981bb2a430005ebaa909d31e6e300719739a949"},
+ {file = "regex-2021.9.24-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:295bc8a13554a25ad31e44c4bedabd3c3e28bba027e4feeb9bb157647a2344a7"},
+ {file = "regex-2021.9.24-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b0e3f59d3c772f2c3baaef2db425e6fc4149d35a052d874bb95ccfca10a1b9f4"},
+ {file = "regex-2021.9.24-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:aea4006b73b555fc5bdb650a8b92cf486d678afa168cf9b38402bb60bf0f9c18"},
+ {file = "regex-2021.9.24-cp310-cp310-win32.whl", hash = "sha256:09eb62654030f39f3ba46bc6726bea464069c29d00a9709e28c9ee9623a8da4a"},
+ {file = "regex-2021.9.24-cp310-cp310-win_amd64.whl", hash = "sha256:8d80087320632457aefc73f686f66139801959bf5b066b4419b92be85be3543c"},
+ {file = "regex-2021.9.24-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:7e3536f305f42ad6d31fc86636c54c7dafce8d634e56fef790fbacb59d499dd5"},
+ {file = "regex-2021.9.24-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c31f35a984caffb75f00a86852951a337540b44e4a22171354fb760cefa09346"},
+ {file = "regex-2021.9.24-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c7cb25adba814d5f419733fe565f3289d6fa629ab9e0b78f6dff5fa94ab0456"},
+ {file = "regex-2021.9.24-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:85c61bee5957e2d7be390392feac7e1d7abd3a49cbaed0c8cee1541b784c8561"},
+ {file = "regex-2021.9.24-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c94722bf403b8da744b7d0bb87e1f2529383003ceec92e754f768ef9323f69ad"},
+ {file = "regex-2021.9.24-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6adc1bd68f81968c9d249aab8c09cdc2cbe384bf2d2cb7f190f56875000cdc72"},
+ {file = "regex-2021.9.24-cp36-cp36m-win32.whl", hash = "sha256:2054dea683f1bda3a804fcfdb0c1c74821acb968093d0be16233873190d459e3"},
+ {file = "regex-2021.9.24-cp36-cp36m-win_amd64.whl", hash = "sha256:7783d89bd5413d183a38761fbc68279b984b9afcfbb39fa89d91f63763fbfb90"},
+ {file = "regex-2021.9.24-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b15dc34273aefe522df25096d5d087abc626e388a28a28ac75a4404bb7668736"},
+ {file = "regex-2021.9.24-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:10a7a9cbe30bd90b7d9a1b4749ef20e13a3528e4215a2852be35784b6bd070f0"},
+ {file = "regex-2021.9.24-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fb9f5844db480e2ef9fce3a72e71122dd010ab7b2920f777966ba25f7eb63819"},
+ {file = "regex-2021.9.24-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:17310b181902e0bb42b29c700e2c2346b8d81f26e900b1328f642e225c88bce1"},
+ {file = "regex-2021.9.24-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0bba1f6df4eafe79db2ecf38835c2626dbd47911e0516f6962c806f83e7a99ae"},
+ {file = "regex-2021.9.24-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:821e10b73e0898544807a0692a276e539e5bafe0a055506a6882814b6a02c3ec"},
+ {file = "regex-2021.9.24-cp37-cp37m-win32.whl", hash = "sha256:9c371dd326289d85906c27ec2bc1dcdedd9d0be12b543d16e37bad35754bde48"},
+ {file = "regex-2021.9.24-cp37-cp37m-win_amd64.whl", hash = "sha256:1e8d1898d4fb817120a5f684363b30108d7b0b46c7261264b100d14ec90a70e7"},
+ {file = "regex-2021.9.24-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8a5c2250c0a74428fd5507ae8853706fdde0f23bfb62ee1ec9418eeacf216078"},
+ {file = "regex-2021.9.24-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8aec4b4da165c4a64ea80443c16e49e3b15df0f56c124ac5f2f8708a65a0eddc"},
+ {file = "regex-2021.9.24-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:650c4f1fc4273f4e783e1d8e8b51a3e2311c2488ba0fcae6425b1e2c248a189d"},
+ {file = "regex-2021.9.24-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2cdb3789736f91d0b3333ac54d12a7e4f9efbc98f53cb905d3496259a893a8b3"},
+ {file = "regex-2021.9.24-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4e61100200fa6ab7c99b61476f9f9653962ae71b931391d0264acfb4d9527d9c"},
+ {file = "regex-2021.9.24-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8c268e78d175798cd71d29114b0a1f1391c7d011995267d3b62319ec1a4ecaa1"},
+ {file = "regex-2021.9.24-cp38-cp38-win32.whl", hash = "sha256:658e3477676009083422042c4bac2bdad77b696e932a3de001c42cc046f8eda2"},
+ {file = "regex-2021.9.24-cp38-cp38-win_amd64.whl", hash = "sha256:a731552729ee8ae9c546fb1c651c97bf5f759018fdd40d0e9b4d129e1e3a44c8"},
+ {file = "regex-2021.9.24-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:86f9931eb92e521809d4b64ec8514f18faa8e11e97d6c2d1afa1bcf6c20a8eab"},
+ {file = "regex-2021.9.24-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dcbbc9cfa147d55a577d285fd479b43103188855074552708df7acc31a476dd9"},
+ {file = "regex-2021.9.24-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:29385c4dbb3f8b3a55ce13de6a97a3d21bd00de66acd7cdfc0b49cb2f08c906c"},
+ {file = "regex-2021.9.24-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:c50a6379763c733562b1fee877372234d271e5c78cd13ade5f25978aa06744db"},
+ {file = "regex-2021.9.24-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f74b6d8f59f3cfb8237e25c532b11f794b96f5c89a6f4a25857d85f84fbef11"},
+ {file = "regex-2021.9.24-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6c4d83d21d23dd854ffbc8154cf293f4e43ba630aa9bd2539c899343d7f59da3"},
+ {file = "regex-2021.9.24-cp39-cp39-win32.whl", hash = "sha256:95e89a8558c8c48626dcffdf9c8abac26b7c251d352688e7ab9baf351e1c7da6"},
+ {file = "regex-2021.9.24-cp39-cp39-win_amd64.whl", hash = "sha256:835962f432bce92dc9bf22903d46c50003c8d11b1dc64084c8fae63bca98564a"},
+ {file = "regex-2021.9.24.tar.gz", hash = "sha256:6266fde576e12357b25096351aac2b4b880b0066263e7bc7a9a1b4307991bb0e"},
]
six = [
{file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
]
sqlalchemy = [
- {file = "SQLAlchemy-1.4.23-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:25e9b2e5ca088879ce3740d9ccd4d58cb9061d49566a0b5e12166f403d6f4da0"},
- {file = "SQLAlchemy-1.4.23-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:d9667260125688c71ccf9af321c37e9fb71c2693575af8210f763bfbbee847c7"},
- {file = "SQLAlchemy-1.4.23-cp27-cp27m-win32.whl", hash = "sha256:cec1a4c6ddf5f82191301a25504f0e675eccd86635f0d5e4c69e0661691931c5"},
- {file = "SQLAlchemy-1.4.23-cp27-cp27m-win_amd64.whl", hash = "sha256:ae07895b55c7d58a7dd47438f437ac219c0f09d24c2e7d69fdebc1ea75350f00"},
- {file = "SQLAlchemy-1.4.23-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:967307ea52985985224a79342527c36ec2d1daa257a39748dd90e001a4be4d90"},
- {file = "SQLAlchemy-1.4.23-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:be185b3daf651c6c0639987a916bf41e97b60e68f860f27c9cb6574385f5cbb4"},
- {file = "SQLAlchemy-1.4.23-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a0d3b3d51c83a66f5b72c57e1aad061406e4c390bd42cf1fda94effe82fac81"},
- {file = "SQLAlchemy-1.4.23-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a8395c4db3e1450eef2b68069abf500cc48af4b442a0d98b5d3c9535fe40cde8"},
- {file = "SQLAlchemy-1.4.23-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9b128a78581faea7a5ee626ad4471353eee051e4e94616dfeff4742b6e5ba262"},
- {file = "SQLAlchemy-1.4.23-cp36-cp36m-win32.whl", hash = "sha256:43fc207be06e50158e4dae4cc4f27ce80afbdbfa7c490b3b22feb64f6d9775a0"},
- {file = "SQLAlchemy-1.4.23-cp36-cp36m-win_amd64.whl", hash = "sha256:e9d4f4552aa5e0d1417fc64a2ce1cdf56a30bab346ba6b0dd5e838eb56db4d29"},
- {file = "SQLAlchemy-1.4.23-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:512f52a8872e8d63d898e4e158eda17e2ee40b8d2496b3b409422e71016db0bd"},
- {file = "SQLAlchemy-1.4.23-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:355024cf061ed04271900414eb4a22671520241d2216ddb691bdd8a992172389"},
- {file = "SQLAlchemy-1.4.23-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:82c03325111eab88d64e0ff48b6fe15c75d23787429fa1d84c0995872e702787"},
- {file = "SQLAlchemy-1.4.23-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0aa312f9906ecebe133d7f44168c3cae4c76f27a25192fa7682f3fad505543c9"},
- {file = "SQLAlchemy-1.4.23-cp37-cp37m-win32.whl", hash = "sha256:059c5f41e8630f51741a234e6ba2a034228c11b3b54a15478e61d8b55fa8bd9d"},
- {file = "SQLAlchemy-1.4.23-cp37-cp37m-win_amd64.whl", hash = "sha256:cd68c5f9d13ffc8f4d6802cceee786678c5b1c668c97bc07b9f4a60883f36cd1"},
- {file = "SQLAlchemy-1.4.23-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:6a8dbf3d46e889d864a57ee880c4ad3a928db5aa95e3d359cbe0da2f122e50c4"},
- {file = "SQLAlchemy-1.4.23-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c15191f2430a30082f540ec6f331214746fc974cfdf136d7a1471d1c61d68ff"},
- {file = "SQLAlchemy-1.4.23-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cd0e85dd2067159848c7672acd517f0c38b7b98867a347411ea01b432003f8d9"},
- {file = "SQLAlchemy-1.4.23-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:370f4688ce47f0dc1e677a020a4d46252a31a2818fd67f5c256417faefc938af"},
- {file = "SQLAlchemy-1.4.23-cp38-cp38-win32.whl", hash = "sha256:bd41f8063a9cd11b76d6d7d6af8139ab3c087f5dbbe5a50c02cb8ece7da34d67"},
- {file = "SQLAlchemy-1.4.23-cp38-cp38-win_amd64.whl", hash = "sha256:2bca9a6e30ee425cc321d988a152a5fe1be519648e7541ac45c36cd4f569421f"},
- {file = "SQLAlchemy-1.4.23-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:4803a481d4c14ce6ad53dc35458c57821863e9a079695c27603d38355e61fb7f"},
- {file = "SQLAlchemy-1.4.23-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:07b9099a95dd2b2620498544300eda590741ac54915c6b20809b6de7e3c58090"},
- {file = "SQLAlchemy-1.4.23-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:37f2bd1b8e32c5999280f846701712347fc0ee7370e016ede2283c71712e127a"},
- {file = "SQLAlchemy-1.4.23-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:448612570aa1437a5d1b94ada161805778fe80aba5b9a08a403e8ae4e071ded6"},
- {file = "SQLAlchemy-1.4.23-cp39-cp39-win32.whl", hash = "sha256:e0ce4a2e48fe0a9ea3a5160411a4c5135da5255ed9ac9c15f15f2bcf58c34194"},
- {file = "SQLAlchemy-1.4.23-cp39-cp39-win_amd64.whl", hash = "sha256:0aa746d1173587743960ff17b89b540e313aacfe6c1e9c81aa48393182c36d4f"},
- {file = "SQLAlchemy-1.4.23.tar.gz", hash = "sha256:76ff246881f528089bf19385131b966197bb494653990396d2ce138e2a447583"},
+ {file = "SQLAlchemy-1.4.25-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:a36ea43919e51b0de0c0bc52bcfdad7683f6ea9fb81b340cdabb9df0e045e0f7"},
+ {file = "SQLAlchemy-1.4.25-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:75cd5d48389a7635393ff5a9214b90695c06b3d74912109c3b00ce7392b69c6c"},
+ {file = "SQLAlchemy-1.4.25-cp27-cp27m-win32.whl", hash = "sha256:16ef07e102d2d4f974ba9b0d4ac46345a411ad20ad988b3654d59ff08e553b1c"},
+ {file = "SQLAlchemy-1.4.25-cp27-cp27m-win_amd64.whl", hash = "sha256:a79abdb404d9256afb8aeaa0d3a4bc7d3b6d8b66103d8b0f2f91febd3909976e"},
+ {file = "SQLAlchemy-1.4.25-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:7ad59e2e16578b6c1a2873e4888134112365605b08a6067dd91e899e026efa1c"},
+ {file = "SQLAlchemy-1.4.25-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:a505ecc0642f52e7c65afb02cc6181377d833b7df0994ecde15943b18d0fa89c"},
+ {file = "SQLAlchemy-1.4.25-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a28fe28c359835f3be20c89efd517b35e8f97dbb2ca09c6cf0d9ac07f62d7ef6"},
+ {file = "SQLAlchemy-1.4.25-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:41a916d815a3a23cb7fff8d11ad0c9b93369ac074e91e428075e088fe57d5358"},
+ {file = "SQLAlchemy-1.4.25-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:842c49dd584aedd75c2ee05f6c950730c3ffcddd21c5824ed0f820808387e1e3"},
+ {file = "SQLAlchemy-1.4.25-cp36-cp36m-win32.whl", hash = "sha256:6b602e3351f59f3999e9fb8b87e5b95cb2faab6a6ecdb482382ac6fdfbee5266"},
+ {file = "SQLAlchemy-1.4.25-cp36-cp36m-win_amd64.whl", hash = "sha256:6400b22e4e41cc27623a9a75630b7719579cd9a3a2027bcf16ad5aaa9a7806c0"},
+ {file = "SQLAlchemy-1.4.25-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:dd4ed12a775f2cde4519f4267d3601990a97d8ecde5c944ab06bfd6e8e8ea177"},
+ {file = "SQLAlchemy-1.4.25-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b7778a205f956755e05721eebf9f11a6ac18b2409bff5db53ce5fe7ede79831"},
+ {file = "SQLAlchemy-1.4.25-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:08d9396a2a38e672133266b31ed39b2b1f2b5ec712b5bff5e08033970563316a"},
+ {file = "SQLAlchemy-1.4.25-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e93978993a2ad0af43f132be3ea8805f56b2f2cd223403ec28d3e7d5c6d39ed1"},
+ {file = "SQLAlchemy-1.4.25-cp37-cp37m-win32.whl", hash = "sha256:0566a6e90951590c0307c75f9176597c88ef4be2724958ca1d28e8ae05ec8822"},
+ {file = "SQLAlchemy-1.4.25-cp37-cp37m-win_amd64.whl", hash = "sha256:0b08a53e40b34205acfeb5328b832f44437956d673a6c09fce55c66ab0e54916"},
+ {file = "SQLAlchemy-1.4.25-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:33a1e86abad782e90976de36150d910748b58e02cd7d35680d441f9a76806c18"},
+ {file = "SQLAlchemy-1.4.25-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ed67aae8cde4d32aacbdba4f7f38183d14443b714498eada5e5a7a37769c0b7"},
+ {file = "SQLAlchemy-1.4.25-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:1ebd69365717becaa1b618220a3df97f7c08aa68e759491de516d1c3667bba54"},
+ {file = "SQLAlchemy-1.4.25-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26b0cd2d5c7ea96d3230cb20acac3d89de3b593339c1447b4d64bfcf4eac1110"},
+ {file = "SQLAlchemy-1.4.25-cp38-cp38-win32.whl", hash = "sha256:c211e8ec81522ce87b0b39f0cf0712c998d4305a030459a0e115a2b3dc71598f"},
+ {file = "SQLAlchemy-1.4.25-cp38-cp38-win_amd64.whl", hash = "sha256:9a1df8c93a0dd9cef0839917f0c6c49f46c75810cf8852be49884da4a7de3c59"},
+ {file = "SQLAlchemy-1.4.25-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:1b38db2417b9f7005d6ceba7ce2a526bf10e3f6f635c0f163e6ed6a42b5b62b2"},
+ {file = "SQLAlchemy-1.4.25-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e37621b37c73b034997b5116678862f38ee70e5a054821c7b19d0e55df270dec"},
+ {file = "SQLAlchemy-1.4.25-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:91cd87d1de0111eaca11ccc3d31af441c753fa2bc22df72e5009cfb0a1af5b03"},
+ {file = "SQLAlchemy-1.4.25-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90fe429285b171bcc252e21515703bdc2a4721008d1f13aa5b7150336f8a8493"},
+ {file = "SQLAlchemy-1.4.25-cp39-cp39-win32.whl", hash = "sha256:6003771ea597346ab1e97f2f58405c6cacbf6a308af3d28a9201a643c0ac7bb3"},
+ {file = "SQLAlchemy-1.4.25-cp39-cp39-win_amd64.whl", hash = "sha256:9ebe49c3960aa2219292ea2e5df6acdc425fc828f2f3d50b4cfae1692bcb5f02"},
+ {file = "SQLAlchemy-1.4.25.tar.gz", hash = "sha256:1adf3d25e2e33afbcd48cfad8076f9378793be43e7fec3e4334306cac6bec138"},
]
sqlalchemy2-stubs = [
- {file = "sqlalchemy2-stubs-0.0.2a15.tar.gz", hash = "sha256:9336e0724c985623e055e2db28e8d78a62b40a2628b862c6cd50d7942fd5a238"},
- {file = "sqlalchemy2_stubs-0.0.2a15-py3-none-any.whl", hash = "sha256:3da19252c87c1ae6bf0e49c2873e72a6e5684a1ede2490c8b75b78834239258b"},
+ {file = "sqlalchemy2-stubs-0.0.2a17.tar.gz", hash = "sha256:d7e1f63f82711c83d49eb6adccbf6bbf3cc94783436c0d34cf7ae49820023046"},
+ {file = "sqlalchemy2_stubs-0.0.2a17-py3-none-any.whl", hash = "sha256:6f112f9381a29575676c3012ce10ed6bceabfad11f1d5569d7072ccf66cfa060"},
]
starlette = [
{file = "starlette-0.14.2-py3-none-any.whl", hash = "sha256:3c8e48e52736b3161e34c9f0e8153b4f32ec5d8995a3ee1d59410d92f75162ed"},
@@ -1087,6 +1068,6 @@ websockets = [
{file = "websockets-10.0.tar.gz", hash = "sha256:c4fc9a1d242317892590abe5b61a9127f1a61740477bfb121743f290b8054002"},
]
zipp = [
- {file = "zipp-3.5.0-py3-none-any.whl", hash = "sha256:957cfda87797e389580cb8b9e3870841ca991e2125350677b2ca83a0e99390a3"},
- {file = "zipp-3.5.0.tar.gz", hash = "sha256:f5812b1e007e48cff63449a5e9f4e7ebea716b4111f9c4f9a645f91d579bf0c4"},
+ {file = "zipp-3.6.0-py3-none-any.whl", hash = "sha256:9fe5ea21568a0a70e50f273397638d39b03353731e6cbbb3fd8502a33fec40bc"},
+ {file = "zipp-3.6.0.tar.gz", hash = "sha256:71c644c5369f4a6e07636f0aa966270449561fcea2e3d6747b8d23efaa9d7832"},
]
diff --git a/reddit-clone/reddit/comments/__init__.py b/reddit-clone/reddit/comments/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/reddit-clone/reddit/comments/models.py b/reddit-clone/reddit/comments/models.py
new file mode 100644
index 00000000..f44c489a
--- /dev/null
+++ b/reddit-clone/reddit/comments/models.py
@@ -0,0 +1,23 @@
+from typing import Optional
+
+from sqlalchemy import Column, Integer, Text, ForeignKey
+from sqlalchemy.orm import relationship
+
+from ..database import Base
+
+
+class Comment(Base):
+ """
+ Represents a Comment in a thread.
+ """
+
+ content: str = Column(Text, nullable=False)
+
+ votes: int = Column(Integer, default=1)
+
+ user_id: Optional[int] = Column(Integer, ForeignKey("users.id"))
+
+ replies = relationship("Comment", back_populates="parent", lazy="dynamic")
+
+ def __repr__(self) -> str:
+ return f""
diff --git a/reddit-clone/reddit/database.py b/reddit-clone/reddit/database.py
index 82bb1e54..17f08f37 100644
--- a/reddit-clone/reddit/database.py
+++ b/reddit-clone/reddit/database.py
@@ -5,12 +5,11 @@
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
from sqlalchemy.ext.declarative import declarative_base
-
from .settings import DATABASE_URI
-async_engine = create_async_engine(DATABASE_URI, future=True)
+engine = create_async_engine(DATABASE_URI, future=True)
-session_factory = sessionmaker(bind=async_engine, class_=AsyncSession)
+session_factory = sessionmaker(bind=engine, class_=AsyncSession)
@asynccontextmanager
diff --git a/reddit-clone/reddit/models/base.py b/reddit-clone/reddit/models/base.py
deleted file mode 100644
index 9d0373d7..00000000
--- a/reddit-clone/reddit/models/base.py
+++ /dev/null
@@ -1,40 +0,0 @@
-from typing import Optional
-from datetime import datetime
-
-from sqlalchemy import Column
-
-from reddit.db.base import Base
-
-
-class BaseModel(Base):
- """
- The base class which every model
- must subclass/ inherit from.
- """
-
- __abstract__ = True
-
- id: Optional[int] = Column(
- default=None,
- primary_key=True,
- comment="""
- Identifier for the object.
- """,
- )
-
- created_at: datetime = Column(
- nullable=False,
- default=datetime.now(),
- comment="""
- When the object was created.
- """,
- )
-
- updated_at: datetime = Column(
- nullable=False,
- default=datetime.now(),
- onupdate=datetime.now(),
- comment="""
- When the object was updated.
- """,
- )
diff --git a/reddit-clone/reddit/models/comment.py b/reddit-clone/reddit/models/comment.py
deleted file mode 100644
index 51fd2cc7..00000000
--- a/reddit-clone/reddit/models/comment.py
+++ /dev/null
@@ -1,41 +0,0 @@
-from typing import Optional
-
-from sqlalchemy import Column, Integer, Text, ForeignKey
-from sqlalchemy.orm import relationship
-
-from .base import BaseModel
-
-
-class Comment(BaseModel):
- """
- Represents a Comment in a thread.
- """
-
- content: str = Column(
- Text,
- nullable=False,
- comment="""
- The content of the comment.
- """,
- )
-
- votes: int = Column(
- Integer,
- default=1,
- comment="""
- The votes for the thread.
- """,
- )
-
- user_id: Optional[int] = Column(
- Integer,
- ForeignKey("users.id"),
- comment="""
- The owner ID of the comment.
- """,
- )
-
- replies = relationship("Comment", backref="parent", lazy="dynamic")
-
- def __repr__(self) -> str:
- return "" % self.id
diff --git a/reddit-clone/reddit/models/post.py b/reddit-clone/reddit/models/post.py
deleted file mode 100644
index b330a45f..00000000
--- a/reddit-clone/reddit/models/post.py
+++ /dev/null
@@ -1,75 +0,0 @@
-from typing import Optional
-
-from sqlalchemy import Column, Integer, String, ForeignKey
-from sqlalchemy.orm import relationship
-
-from .base import BaseModel
-
-
-class Post(BaseModel):
- """
- Represents a post in a Subreddit.
- """
-
- __tablename__ = "posts"
-
- title: str = Column(
- String(150),
- comment="""
- The title for the post.
- """,
- )
-
- text: Optional[str] = Column(
- String(1024),
- default=None,
- comment="""
- The text for the post.
- """,
- )
-
- link: Optional[str] = Column(
- String(255),
- default=None,
- unique=True,
- comment="""
- The link for the post.
- """,
- )
-
- thumbnail: Optional[str] = Column(
- String(255),
- default=None,
- comment="""
- The thumbnail URL for the post.
- """,
- )
-
- user_id: int = Column(
- Integer,
- ForeignKey("users.id"),
- comment="""
- The owner ID of the post.
- """,
- )
-
- subreddit_id: int = Column(
- Integer,
- ForeignKey("subreddits.id"),
- comment="""
- The SubReddit ID of the post.
- """,
- )
-
- votes: int = Column(
- Integer,
- default=1,
- comment="""
- The votes for the post.
- """,
- )
-
- comments = relationship("Comment", backref="post", lazy="dynamic")
-
- def __repr__(self) -> str:
- return "" % self.title
diff --git a/reddit-clone/reddit/models/subreddit.py b/reddit-clone/reddit/models/subreddit.py
deleted file mode 100644
index a54c9013..00000000
--- a/reddit-clone/reddit/models/subreddit.py
+++ /dev/null
@@ -1,59 +0,0 @@
-from typing import Optional
-
-from sqlalchemy import Column, String, Integer, SmallInteger, ForeignKey
-from sqlalchemy.orm import relationship
-
-from .base import BaseModel
-
-
-class Subreddit(BaseModel):
- """
- Represents a Subreddit.
- """
-
- __tablename__ = "subreddits"
-
- name: str = Column(
- String(75),
- unique=True,
- nullable=False,
- comment="""
- The name for the subreddit.
- """,
- )
-
- description: Optional[str] = Column(
- String(255),
- default=None,
- comment="""
- The description for the subreddit.
- """,
- )
-
- admin_id: int = Column(
- Integer,
- ForeignKey("users.id"),
- comment="""
- The admin ID of the subreddit.
- """,
- )
-
- status: int = Column(
- SmallInteger,
- comment="""
- The status of the subreddit.
- """,
- )
-
- icon: Optional[str] = Column(
- String(255),
- default=None,
- comment="""
- The icon URL for the subreddit.
- """,
- )
-
- posts = relationship("Post", backref="subreddit", lazy="dynamic")
-
- def __repr__(self) -> str:
- return "" % self.name
diff --git a/reddit-clone/reddit/models/user.py b/reddit-clone/reddit/models/user.py
deleted file mode 100644
index 9701021f..00000000
--- a/reddit-clone/reddit/models/user.py
+++ /dev/null
@@ -1,85 +0,0 @@
-from typing import Optional
-
-from sqlalchemy import Column, String
-from sqlalchemy.orm import relationship
-
-from .base import BaseModel
-
-
-class User(BaseModel):
- """
- Represents an individual user account.
- """
-
- __tablename__ = "users"
-
- username: str = Column(
- String(32),
- nullable=False,
- unique=True,
- comment="""
- The username for the user.
- """,
- )
-
- email: str = Column(
- String(255),
- nullable=False,
- unique=True,
- comment="""
- The email for the user.
- """,
- )
-
- password: str = Column(
- String(255),
- nullable=False,
- comment="""
- The password for the user.
- """,
- )
-
- avatar: Optional[str] = Column(
- String(255),
- default=None,
- comment="""
- The avatar URL for the user.
- """,
- )
-
- posts = relationship("Post", backref="user", lazy="dynamic")
-
- subreddits = relationship(
- "Subreddit", backref="user", secondary="subreddit_users", lazy="dynamic"
- )
-
- comments = relationship("Comment", backref="user", lazy="dynamic")
-
- def __repr__(self) -> str:
- return "" % self.username
-
-
-class SubredditUser(BaseModel):
- """
- Represents a Subreddit-user relationship.
- """
-
- __tablename__ = "subreddit_users"
-
- user_id: int = Column(
- Integer,
- ForeignKey("users.id"),
- primary_key=True,
- comment="""
- The relationship's user ID.
- """,
- )
-
- subreddit_id: int = Column(
- Integer,
- ForeignKey("subreddits.id"),
- primary_key=True,
- comment="""
- The relationship's subreddit ID.
- """,
- )
diff --git a/reddit-clone/reddit/posts/__init__.py b/reddit-clone/reddit/posts/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/reddit-clone/reddit/posts/models.py b/reddit-clone/reddit/posts/models.py
new file mode 100644
index 00000000..70e36f57
--- /dev/null
+++ b/reddit-clone/reddit/posts/models.py
@@ -0,0 +1,35 @@
+from typing import Optional
+
+from sqlalchemy import Column, Integer, String, ForeignKey
+from sqlalchemy.orm import relationship
+
+from ..database import Base
+
+
+class Post(Base):
+ """
+ Represents a post in a Subreddit.
+ """
+
+ __tablename__ = "posts"
+
+ id: Optional[int] = Column(default=None, primary_key=True)
+
+ title: str = Column(String(150))
+
+ text: Optional[str] = Column(String(1024), default=None)
+
+ link: Optional[str] = Column(String(255), default=None, unique=True)
+
+ thumbnail: Optional[str] = Column(String(255), default=None)
+
+ user_id: int = Column(Integer, ForeignKey("users.id"))
+
+ subreddit_id: int = Column(Integer, ForeignKey("subreddits.id"))
+
+ votes: int = Column(Integer, default=1)
+
+ comments = relationship("Comment", back_populates="post", lazy="dynamic")
+
+ def __repr__(self) -> str:
+ return f""
diff --git a/reddit-clone/reddit/subreddits/__init__.py b/reddit-clone/reddit/subreddits/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/reddit-clone/reddit/subreddits/models.py b/reddit-clone/reddit/subreddits/models.py
new file mode 100644
index 00000000..c1e5e4f1
--- /dev/null
+++ b/reddit-clone/reddit/subreddits/models.py
@@ -0,0 +1,31 @@
+from typing import Optional
+
+from sqlalchemy import Column, String, Integer, SmallInteger, ForeignKey
+from sqlalchemy.orm import relationship
+
+from ..database import Base
+
+
+class Subreddit(Base):
+ """
+ Represents a Subreddit.
+ """
+
+ __tablename__ = "subreddits"
+
+ id: Optional[int] = Column(default=None, primary_key=True)
+
+ name: str = Column(String(75), unique=True, nullable=False)
+
+ description: Optional[str] = Column(String(255), default=None)
+
+ admin_id: int = Column(Integer, ForeignKey("users.id"))
+
+ status: int = Column(SmallInteger)
+
+ icon: Optional[str] = Column(String(255), default=None)
+
+ posts = relationship("Post", back_populates="subreddit", lazy="dynamic")
+
+ def __repr__(self) -> str:
+ return f""
diff --git a/reddit-clone/reddit/users/__init__.py b/reddit-clone/reddit/users/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/reddit-clone/reddit/users/models.py b/reddit-clone/reddit/users/models.py
new file mode 100644
index 00000000..f34cf72e
--- /dev/null
+++ b/reddit-clone/reddit/users/models.py
@@ -0,0 +1,50 @@
+from typing import Optional
+
+from sqlalchemy import Column, String, Integer, ForeignKey
+from sqlalchemy.orm import relationship
+
+from ..database import Base
+
+
+class User(Base):
+ """
+ Represents an individual user account.
+ """
+
+ __tablename__ = "users"
+
+ id: Optional[int] = Column(default=None, primary_key=True)
+
+ username: str = Column(String(32), nullable=False, unique=True)
+
+ email: str = Column(String(255), nullable=False, unique=True)
+
+ password: str = Column(String(255), nullable=False)
+
+ avatar: Optional[str] = Column(String(255), default=None)
+
+ posts = relationship("Post", back_populates="user", lazy="dynamic")
+
+ subreddits = relationship(
+ "Subreddit", back_populates="user", secondary="subreddit_users", lazy="dynamic"
+ )
+
+ comments = relationship("Comment", back_populates="user", lazy="dynamic")
+
+ def __repr__(self) -> str:
+ return f""
+
+
+class SubredditUser(Base):
+ """
+ Represents a Subreddit-user relationship.
+ """
+
+ __tablename__ = "subreddit_users"
+
+ user_id: int = Column(Integer, ForeignKey("users.id"), primary_key=True)
+
+ subreddit_id: int = Column(Integer, ForeignKey("subreddits.id"), primary_key=True)
+
+ def __repr__(self) -> str:
+ return f""
From cef2a1c527830d31e8753a3d094cd6c0853ae319 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Thu, 30 Sep 2021 08:46:14 +0530
Subject: [PATCH 031/150] type hint relationships
---
reddit-clone/reddit/comments/models.py | 8 ++++++--
reddit-clone/reddit/posts/models.py | 7 +++++--
reddit-clone/reddit/subreddits/models.py | 5 +++--
reddit-clone/reddit/users/models.py | 13 +++++++++----
4 files changed, 23 insertions(+), 10 deletions(-)
diff --git a/reddit-clone/reddit/comments/models.py b/reddit-clone/reddit/comments/models.py
index f44c489a..50fa1a48 100644
--- a/reddit-clone/reddit/comments/models.py
+++ b/reddit-clone/reddit/comments/models.py
@@ -1,4 +1,6 @@
-from typing import Optional
+from __future__ import annotations
+
+from typing import Optional, List
from sqlalchemy import Column, Integer, Text, ForeignKey
from sqlalchemy.orm import relationship
@@ -17,7 +19,9 @@ class Comment(Base):
user_id: Optional[int] = Column(Integer, ForeignKey("users.id"))
- replies = relationship("Comment", back_populates="parent", lazy="dynamic")
+ replies: List[Comment] = relationship(
+ "Comment", back_populates="parent", lazy="dynamic"
+ )
def __repr__(self) -> str:
return f""
diff --git a/reddit-clone/reddit/posts/models.py b/reddit-clone/reddit/posts/models.py
index 70e36f57..10cdd6dd 100644
--- a/reddit-clone/reddit/posts/models.py
+++ b/reddit-clone/reddit/posts/models.py
@@ -1,9 +1,10 @@
-from typing import Optional
+from typing import Optional, List
from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.orm import relationship
from ..database import Base
+from ..comments.models import Comment
class Post(Base):
@@ -29,7 +30,9 @@ class Post(Base):
votes: int = Column(Integer, default=1)
- comments = relationship("Comment", back_populates="post", lazy="dynamic")
+ comments: List[Comment] = relationship(
+ "Comment", back_populates="post", lazy="dynamic"
+ )
def __repr__(self) -> str:
return f""
diff --git a/reddit-clone/reddit/subreddits/models.py b/reddit-clone/reddit/subreddits/models.py
index c1e5e4f1..d23f65c6 100644
--- a/reddit-clone/reddit/subreddits/models.py
+++ b/reddit-clone/reddit/subreddits/models.py
@@ -1,9 +1,10 @@
-from typing import Optional
+from typing import Optional, List
from sqlalchemy import Column, String, Integer, SmallInteger, ForeignKey
from sqlalchemy.orm import relationship
from ..database import Base
+from ..posts.models import Post
class Subreddit(Base):
@@ -25,7 +26,7 @@ class Subreddit(Base):
icon: Optional[str] = Column(String(255), default=None)
- posts = relationship("Post", back_populates="subreddit", lazy="dynamic")
+ posts: List[Post] = relationship("Post", back_populates="subreddit", lazy="dynamic")
def __repr__(self) -> str:
return f""
diff --git a/reddit-clone/reddit/users/models.py b/reddit-clone/reddit/users/models.py
index f34cf72e..22ceb7a5 100644
--- a/reddit-clone/reddit/users/models.py
+++ b/reddit-clone/reddit/users/models.py
@@ -1,9 +1,12 @@
-from typing import Optional
+from typing import Optional, List
from sqlalchemy import Column, String, Integer, ForeignKey
from sqlalchemy.orm import relationship
from ..database import Base
+from ..comments.models import Comment
+from ..subreddits.models import Subreddit
+from ..posts.models import Post
class User(Base):
@@ -23,13 +26,15 @@ class User(Base):
avatar: Optional[str] = Column(String(255), default=None)
- posts = relationship("Post", back_populates="user", lazy="dynamic")
+ posts: List[Post] = relationship("Post", back_populates="user", lazy="dynamic")
- subreddits = relationship(
+ subreddits: List[Subreddit] = relationship(
"Subreddit", back_populates="user", secondary="subreddit_users", lazy="dynamic"
)
- comments = relationship("Comment", back_populates="user", lazy="dynamic")
+ comments: List[Comment] = relationship(
+ "Comment", back_populates="user", lazy="dynamic"
+ )
def __repr__(self) -> str:
return f""
From 5dc7f903b28d4659bf0425ba7a04bdd1278d6376 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Thu, 30 Sep 2021 08:55:54 +0530
Subject: [PATCH 032/150] add dummy schema (and update instructions)
---
reddit-clone/README.md | 8 ++++++--
reddit-clone/reddit/__init__.py | 4 ++--
reddit-clone/reddit/schema.py | 11 +++++++++--
reddit-clone/reddit/settings.py | 4 ----
4 files changed, 17 insertions(+), 10 deletions(-)
diff --git a/reddit-clone/README.md b/reddit-clone/README.md
index 379a3549..05ce54e0 100644
--- a/reddit-clone/README.md
+++ b/reddit-clone/README.md
@@ -29,7 +29,11 @@ Use [poetry](https://python-poetry.org/) to install dependencies:
poetry install
```
-2. Run migrations
+2. setup database
+ This example uses a PostgreSQL database. Make sure you have it installed
+ on your machine, and configure the `DATABASE_URI` environment variable.
+
+3. Run migrations
Run [alembic](https://alembic.sqlalchemy.org/en/latest/) to create the database
and populate it with movie data:
@@ -38,7 +42,7 @@ and populate it with movie data:
poetry run alembic upgrade head
```
-3. Run the server
+4. Run the server
Run [uvicorn](https://www.uvicorn.org/) to run the server:
diff --git a/reddit-clone/reddit/__init__.py b/reddit-clone/reddit/__init__.py
index 59fce6d2..e6684e7a 100644
--- a/reddit-clone/reddit/__init__.py
+++ b/reddit-clone/reddit/__init__.py
@@ -1,8 +1,8 @@
from fastapi import FastAPI
from strawberry.asgi import GraphQL
-from reddit.core.config import DEBUG
-from reddit.schema import schema
+from .settings import DEBUG
+from .schema import schema
def create_application() -> FastAPI:
diff --git a/reddit-clone/reddit/schema.py b/reddit-clone/reddit/schema.py
index 41baebbf..77439e13 100644
--- a/reddit-clone/reddit/schema.py
+++ b/reddit-clone/reddit/schema.py
@@ -1,4 +1,11 @@
-from strawberry import Schema
+import strawberry
-schema = Schema(query=None, mutation=None)
+@strawberry.type
+class Query:
+ @strawberry.field()
+ def hello_world(self) -> str:
+ return "Hello world!"
+
+
+schema = strawberry.Schema(query=Query, mutation=None)
diff --git a/reddit-clone/reddit/settings.py b/reddit-clone/reddit/settings.py
index 1a8a6b83..86fbcac9 100644
--- a/reddit-clone/reddit/settings.py
+++ b/reddit-clone/reddit/settings.py
@@ -1,5 +1,4 @@
from starlette.config import Config
-from starlette.datastructures import Secret
config = Config(env_file=".env")
@@ -7,8 +6,5 @@
# whether the application is in development mode.
DEBUG: bool = config("DEBUG", cast=bool, default=False)
-# secret key to use for sessions.
-SECRET_KEY: Secret = config("SECRET_KEY", cast=Secret)
-
# sqlalchemy database url.
DATABASE_URI: str = config("DATABASE_URI", cast=str)
From 797f7c38feafb0ffaa87a90607d61fe5dc275675 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Thu, 30 Sep 2021 10:22:03 +0530
Subject: [PATCH 033/150] make migrations
---
.idea/misc.xml | 3 +
reddit-clone/README.md | 6 +
reddit-clone/alembic.ini | 32 +++++-
reddit-clone/alembic/README | 1 -
reddit-clone/migrations/README | 1 +
reddit-clone/{alembic => migrations}/env.py | 43 +++++---
.../{alembic => migrations}/script.py.mako | 0
.../versions/6128e6679253_initial.py | 104 ++++++++++++++++++
reddit-clone/poetry.lock | 33 +++++-
reddit-clone/pyproject.toml | 1 +
reddit-clone/reddit/comments/models.py | 4 +
reddit-clone/reddit/database.py | 4 +-
reddit-clone/reddit/posts/models.py | 2 +-
reddit-clone/reddit/subreddits/models.py | 2 +-
reddit-clone/reddit/users/models.py | 2 +-
15 files changed, 215 insertions(+), 23 deletions(-)
delete mode 100644 reddit-clone/alembic/README
create mode 100644 reddit-clone/migrations/README
rename reddit-clone/{alembic => migrations}/env.py (61%)
rename reddit-clone/{alembic => migrations}/script.py.mako (100%)
create mode 100644 reddit-clone/migrations/versions/6128e6679253_initial.py
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 2a9526cd..5f549113 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -1,4 +1,7 @@
+
+
+
diff --git a/reddit-clone/README.md b/reddit-clone/README.md
index 05ce54e0..4f4f15ee 100644
--- a/reddit-clone/README.md
+++ b/reddit-clone/README.md
@@ -33,6 +33,12 @@ poetry install
This example uses a PostgreSQL database. Make sure you have it installed
on your machine, and configure the `DATABASE_URI` environment variable.
+ An example database URL:
+
+ ```text
+ postgresql+asyncpg://localhost/db_name?user=user&password=password
+ ```
+
3. Run migrations
Run [alembic](https://alembic.sqlalchemy.org/en/latest/) to create the database
diff --git a/reddit-clone/alembic.ini b/reddit-clone/alembic.ini
index eb6b4537..2d4431e1 100644
--- a/reddit-clone/alembic.ini
+++ b/reddit-clone/alembic.ini
@@ -2,7 +2,7 @@
[alembic]
# path to migration scripts
-script_location = alembic
+script_location = migrations
# template used to generate migration files
# file_template = %%(rev)s_%%(slug)s
@@ -33,10 +33,36 @@ prepend_sys_path = .
# sourceless = false
# version location specification; This defaults
-# to alembic/versions. When using multiple version
+# to migrations/versions. When using multiple version
# directories, initial revisions must be specified with --version-path.
# The path separator used here should be the separator specified by "version_path_separator"
-# version_locations = %(here)s/bar:%(here)s/bat:alembic/versions
+# version_locations = %(here)s/bar:%(here)s/bat:migrations/versions
+
+# version path separator; As mentioned above, this is the character used to split
+# version_locations. Valid values are:
+#
+# version_path_separator = :
+# version_path_separator = ;
+# version_path_separator = space
+version_path_separator = os # default: use os.pathsep
+
+# the output encoding used when revision files
+# are written from script.py.mako
+# output_encoding = utf-8
+
+sqlalchemy.url = driver://user:pass@localhost/dbname
+
+
+[post_write_hooks]
+# post_write_hooks defines scripts or Python functions that are run
+# on newly generated revision scripts. See the documentation for further
+# detail and examples
+
+# format using "black" - use the console_scripts runner, against the "black" entrypoint
+# hooks = black
+# black.type = console_scripts
+# black.entrypoint = black
+# black.options = -l 79 REVISION_SCRIPT_FILENAME
# Logging configuration
[loggers]
diff --git a/reddit-clone/alembic/README b/reddit-clone/alembic/README
deleted file mode 100644
index 2500aa1b..00000000
--- a/reddit-clone/alembic/README
+++ /dev/null
@@ -1 +0,0 @@
-Generic single-database configuration.
diff --git a/reddit-clone/migrations/README b/reddit-clone/migrations/README
new file mode 100644
index 00000000..a23d4fb5
--- /dev/null
+++ b/reddit-clone/migrations/README
@@ -0,0 +1 @@
+Generic single-database configuration with an async dbapi.
diff --git a/reddit-clone/alembic/env.py b/reddit-clone/migrations/env.py
similarity index 61%
rename from reddit-clone/alembic/env.py
rename to reddit-clone/migrations/env.py
index bea3fde4..b76cae02 100644
--- a/reddit-clone/alembic/env.py
+++ b/reddit-clone/migrations/env.py
@@ -1,20 +1,30 @@
+import asyncio
from logging.config import fileConfig
from sqlalchemy import engine_from_config
from sqlalchemy import pool
+from sqlalchemy.ext.asyncio import AsyncEngine
from alembic import context
+from reddit.database import Base
+from reddit.settings import DATABASE_URI
+
+
# this is the Alembic Config object, which provides
# access to the values within the .ini file in use.
config = context.config
+config.set_main_option("sqlalchemy.url", DATABASE_URI)
+
# Interpret the config file for Python logging.
# This line sets up loggers basically.
fileConfig(config.config_file_name)
-from reddit.database import Base # noqa
-
+# add your model's MetaData object here
+# for 'autogenerate' support
+# from myapp import mymodel
+# target_metadata = mymodel.Base.metadata
target_metadata = Base.metadata
# other values from the config, defined by the needs of env.py,
@@ -47,27 +57,34 @@ def run_migrations_offline():
context.run_migrations()
-def run_migrations_online():
+def do_run_migrations(connection):
+ context.configure(connection=connection, target_metadata=target_metadata)
+
+ with context.begin_transaction():
+ context.run_migrations()
+
+
+async def run_migrations_online():
"""Run migrations in 'online' mode.
In this scenario we need to create an Engine
and associate a connection with the context.
"""
- connectable = engine_from_config(
- config.get_section(config.config_ini_section),
- prefix="sqlalchemy.",
- poolclass=pool.NullPool,
+ connectable = AsyncEngine(
+ engine_from_config(
+ config.get_section(config.config_ini_section),
+ prefix="sqlalchemy.",
+ poolclass=pool.NullPool,
+ future=True,
+ )
)
- with connectable.connect() as connection:
- context.configure(connection=connection, target_metadata=target_metadata)
-
- with context.begin_transaction():
- context.run_migrations()
+ async with connectable.connect() as connection:
+ await connection.run_sync(do_run_migrations)
if context.is_offline_mode():
run_migrations_offline()
else:
- run_migrations_online()
+ asyncio.run(run_migrations_online())
diff --git a/reddit-clone/alembic/script.py.mako b/reddit-clone/migrations/script.py.mako
similarity index 100%
rename from reddit-clone/alembic/script.py.mako
rename to reddit-clone/migrations/script.py.mako
diff --git a/reddit-clone/migrations/versions/6128e6679253_initial.py b/reddit-clone/migrations/versions/6128e6679253_initial.py
new file mode 100644
index 00000000..a31c917f
--- /dev/null
+++ b/reddit-clone/migrations/versions/6128e6679253_initial.py
@@ -0,0 +1,104 @@
+"""initial
+
+Revision ID: 6128e6679253
+Revises:
+Create Date: 2021-09-30 10:21:28.876680
+
+"""
+from alembic import op
+import sqlalchemy as sa
+
+
+# revision identifiers, used by Alembic.
+revision = "6128e6679253"
+down_revision = None
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.create_table(
+ "users",
+ sa.Column("id", sa.Integer(), nullable=False),
+ sa.Column("username", sa.String(length=32), nullable=False),
+ sa.Column("email", sa.String(length=255), nullable=False),
+ sa.Column("password", sa.String(length=255), nullable=False),
+ sa.Column("avatar", sa.String(length=255), nullable=True),
+ sa.PrimaryKeyConstraint("id"),
+ sa.UniqueConstraint("email"),
+ sa.UniqueConstraint("username"),
+ )
+ op.create_table(
+ "comments",
+ sa.Column("id", sa.Integer(), nullable=False),
+ sa.Column("content", sa.Text(), nullable=False),
+ sa.Column("votes", sa.Integer(), nullable=True),
+ sa.Column("user_id", sa.Integer(), nullable=True),
+ sa.ForeignKeyConstraint(
+ ["user_id"],
+ ["users.id"],
+ ),
+ sa.PrimaryKeyConstraint("id"),
+ )
+ op.create_table(
+ "subreddits",
+ sa.Column("id", sa.Integer(), nullable=False),
+ sa.Column("name", sa.String(length=75), nullable=False),
+ sa.Column("description", sa.String(length=255), nullable=True),
+ sa.Column("admin_id", sa.Integer(), nullable=True),
+ sa.Column("status", sa.SmallInteger(), nullable=True),
+ sa.Column("icon", sa.String(length=255), nullable=True),
+ sa.ForeignKeyConstraint(
+ ["admin_id"],
+ ["users.id"],
+ ),
+ sa.PrimaryKeyConstraint("id"),
+ sa.UniqueConstraint("name"),
+ )
+ op.create_table(
+ "posts",
+ sa.Column("id", sa.Integer(), nullable=False),
+ sa.Column("title", sa.String(length=150), nullable=True),
+ sa.Column("text", sa.String(length=1024), nullable=True),
+ sa.Column("link", sa.String(length=255), nullable=True),
+ sa.Column("thumbnail", sa.String(length=255), nullable=True),
+ sa.Column("user_id", sa.Integer(), nullable=True),
+ sa.Column("subreddit_id", sa.Integer(), nullable=True),
+ sa.Column("votes", sa.Integer(), nullable=True),
+ sa.ForeignKeyConstraint(
+ ["subreddit_id"],
+ ["subreddits.id"],
+ ),
+ sa.ForeignKeyConstraint(
+ ["user_id"],
+ ["users.id"],
+ ),
+ sa.PrimaryKeyConstraint("id"),
+ sa.UniqueConstraint("link"),
+ )
+ op.create_table(
+ "subreddit_users",
+ sa.Column("user_id", sa.Integer(), nullable=False),
+ sa.Column("subreddit_id", sa.Integer(), nullable=False),
+ sa.ForeignKeyConstraint(
+ ["subreddit_id"],
+ ["subreddits.id"],
+ ),
+ sa.ForeignKeyConstraint(
+ ["user_id"],
+ ["users.id"],
+ ),
+ sa.PrimaryKeyConstraint("user_id", "subreddit_id"),
+ )
+ # ### end Alembic commands ###
+
+
+def downgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.drop_table("subreddit_users")
+ op.drop_table("posts")
+ op.drop_table("subreddits")
+ op.drop_table("comments")
+ op.drop_table("users")
+ # ### end Alembic commands ###
diff --git a/reddit-clone/poetry.lock b/reddit-clone/poetry.lock
index 79b5fd46..37b7640c 100644
--- a/reddit-clone/poetry.lock
+++ b/reddit-clone/poetry.lock
@@ -29,6 +29,22 @@ typing-extensions = {version = "*", markers = "python_version < \"3.8\""}
[package.extras]
tests = ["pytest", "pytest-asyncio", "mypy (>=0.800)"]
+[[package]]
+name = "asyncpg"
+version = "0.24.0"
+description = "An asyncio PostgreSQL driver"
+category = "main"
+optional = false
+python-versions = ">=3.6.0"
+
+[package.dependencies]
+typing-extensions = {version = ">=3.7.4.3", markers = "python_version < \"3.8\""}
+
+[package.extras]
+dev = ["Cython (>=0.29.24,<0.30.0)", "pytest (>=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)", "pycodestyle (>=2.7.0,<2.8.0)", "flake8 (>=3.9.2,<3.10.0)", "uvloop (>=0.15.3)"]
+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 = ["pycodestyle (>=2.7.0,<2.8.0)", "flake8 (>=3.9.2,<3.10.0)", "uvloop (>=0.15.3)"]
+
[[package]]
name = "attrs"
version = "21.2.0"
@@ -587,7 +603,7 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytes
[metadata]
lock-version = "1.1"
python-versions = "^3.7"
-content-hash = "98fa69364a20e1543505c385c05fd9be26fdc5abbb60fc0e52136a65a1036d88"
+content-hash = "0be25f7a7b2a6d2767a4d40638945fea8d0148af2efd0ab4c26d50b2eaced91b"
[metadata.files]
alembic = [
@@ -598,6 +614,21 @@ asgiref = [
{file = "asgiref-3.4.1-py3-none-any.whl", hash = "sha256:ffc141aa908e6f175673e7b1b3b7af4fdb0ecb738fc5c8b88f69f055c2415214"},
{file = "asgiref-3.4.1.tar.gz", hash = "sha256:4ef1ab46b484e3c706329cedeff284a5d40824200638503f5768edb6de7d58e9"},
]
+asyncpg = [
+ {file = "asyncpg-0.24.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c4fc0205fe4ddd5aeb3dfdc0f7bafd43411181e1f5650189608e5971cceacff1"},
+ {file = "asyncpg-0.24.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a7095890c96ba36f9f668eb552bb020dddb44f8e73e932f8573efc613ee83843"},
+ {file = "asyncpg-0.24.0-cp310-cp310-win_amd64.whl", hash = "sha256:8ff5073d4b654e34bd5eaadc01dc4d68b8a9609084d835acd364cd934190a08d"},
+ {file = "asyncpg-0.24.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e36c6806883786b19551bb70a4882561f31135dc8105a59662e0376cf5b2cbc5"},
+ {file = "asyncpg-0.24.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ddffcb85227bf39cd1bedd4603e0082b243cf3b14ced64dce506a15b05232b83"},
+ {file = "asyncpg-0.24.0-cp37-cp37m-win_amd64.whl", hash = "sha256:41704c561d354bef01353835a7846e5606faabbeb846214dfcf666cf53319f18"},
+ {file = "asyncpg-0.24.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:29ef6ae0a617fc13cc2ac5dc8e9b367bb83cba220614b437af9b67766f4b6b20"},
+ {file = "asyncpg-0.24.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:eed43abc6ccf1dc02e0d0efc06ce46a411362f3358847c6b0ec9a43426f91ece"},
+ {file = "asyncpg-0.24.0-cp38-cp38-win_amd64.whl", hash = "sha256:129d501f3d30616afd51eb8d3142ef51ba05374256bd5834cec3ef4956a9b317"},
+ {file = "asyncpg-0.24.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a458fc69051fbb67d995fdda46d75a012b5d6200f91e17d23d4751482640ed4c"},
+ {file = "asyncpg-0.24.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:556b0e92e2b75dc028b3c4bc9bd5162ddf0053b856437cf1f04c97f9c6837d03"},
+ {file = "asyncpg-0.24.0-cp39-cp39-win_amd64.whl", hash = "sha256:a738f4807c853623d3f93f0fea11f61be6b0e5ca16ea8aeb42c2c7ee742aa853"},
+ {file = "asyncpg-0.24.0.tar.gz", hash = "sha256:dd2fa063c3344823487d9ddccb40802f02622ddf8bf8a6cc53885ee7a2c1c0c6"},
+]
attrs = [
{file = "attrs-21.2.0-py2.py3-none-any.whl", hash = "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1"},
{file = "attrs-21.2.0.tar.gz", hash = "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"},
diff --git a/reddit-clone/pyproject.toml b/reddit-clone/pyproject.toml
index 0715528a..7d362310 100644
--- a/reddit-clone/pyproject.toml
+++ b/reddit-clone/pyproject.toml
@@ -9,6 +9,7 @@ python = "^3.7"
uvicorn = {extras = ["standard"], version = "^0.15.0"}
SQLAlchemy = {extras = ["mypy"], version = "^1.4.23"}
alembic = "^1.7.1"
+asyncpg = "^0.24"
strawberry-graphql = {extras = ["asgi"], version = "^0.77.0"}
fastapi = "^0.68.1"
diff --git a/reddit-clone/reddit/comments/models.py b/reddit-clone/reddit/comments/models.py
index 50fa1a48..62f3bce7 100644
--- a/reddit-clone/reddit/comments/models.py
+++ b/reddit-clone/reddit/comments/models.py
@@ -13,6 +13,10 @@ class Comment(Base):
Represents a Comment in a thread.
"""
+ __tablename__ = "comments"
+
+ id: int = Column(Integer, primary_key=True, nullable=False)
+
content: str = Column(Text, nullable=False)
votes: int = Column(Integer, default=1)
diff --git a/reddit-clone/reddit/database.py b/reddit-clone/reddit/database.py
index 17f08f37..aa2cc340 100644
--- a/reddit-clone/reddit/database.py
+++ b/reddit-clone/reddit/database.py
@@ -1,5 +1,5 @@
from contextlib import asynccontextmanager
-from typing import Generator
+from typing import AsyncIterator
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
@@ -13,7 +13,7 @@
@asynccontextmanager
-async def get_session() -> Generator[AsyncSession]:
+async def get_session() -> AsyncIterator[AsyncSession]:
"""
Gets a session instance.
diff --git a/reddit-clone/reddit/posts/models.py b/reddit-clone/reddit/posts/models.py
index 10cdd6dd..0d314006 100644
--- a/reddit-clone/reddit/posts/models.py
+++ b/reddit-clone/reddit/posts/models.py
@@ -14,7 +14,7 @@ class Post(Base):
__tablename__ = "posts"
- id: Optional[int] = Column(default=None, primary_key=True)
+ id: int = Column(Integer, primary_key=True, nullable=False)
title: str = Column(String(150))
diff --git a/reddit-clone/reddit/subreddits/models.py b/reddit-clone/reddit/subreddits/models.py
index d23f65c6..cba9449a 100644
--- a/reddit-clone/reddit/subreddits/models.py
+++ b/reddit-clone/reddit/subreddits/models.py
@@ -14,7 +14,7 @@ class Subreddit(Base):
__tablename__ = "subreddits"
- id: Optional[int] = Column(default=None, primary_key=True)
+ id: int = Column(Integer, primary_key=True, nullable=False)
name: str = Column(String(75), unique=True, nullable=False)
diff --git a/reddit-clone/reddit/users/models.py b/reddit-clone/reddit/users/models.py
index 22ceb7a5..5eec2459 100644
--- a/reddit-clone/reddit/users/models.py
+++ b/reddit-clone/reddit/users/models.py
@@ -16,7 +16,7 @@ class User(Base):
__tablename__ = "users"
- id: Optional[int] = Column(default=None, primary_key=True)
+ id: int = Column(Integer, primary_key=True, nullable=False)
username: str = Column(String(32), nullable=False, unique=True)
From 784ec420259b21aaafb168f217feb69fb7ce21e7 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Thu, 30 Sep 2021 10:24:42 +0530
Subject: [PATCH 034/150] remove .idea
---
.gitignore | 3 ++
.idea/.gitignore | 3 --
.idea/inspectionProfiles/Project_Default.xml | 46 -------------------
.../inspectionProfiles/profiles_settings.xml | 6 ---
.idea/misc.xml | 7 ---
.idea/modules.xml | 8 ----
.idea/strawberry-examples.iml | 12 -----
.idea/vcs.xml | 6 ---
8 files changed, 3 insertions(+), 88 deletions(-)
delete mode 100644 .idea/.gitignore
delete mode 100644 .idea/inspectionProfiles/Project_Default.xml
delete mode 100644 .idea/inspectionProfiles/profiles_settings.xml
delete mode 100644 .idea/misc.xml
delete mode 100644 .idea/modules.xml
delete mode 100644 .idea/strawberry-examples.iml
delete mode 100644 .idea/vcs.xml
diff --git a/.gitignore b/.gitignore
index b6e47617..50af712e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -127,3 +127,6 @@ dmypy.json
# Pyre type checker
.pyre/
+
+# PyCharm
+.idea
diff --git a/.idea/.gitignore b/.idea/.gitignore
deleted file mode 100644
index 26d33521..00000000
--- a/.idea/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
-# Default ignored files
-/shelf/
-/workspace.xml
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
deleted file mode 100644
index 3c6e0d59..00000000
--- a/.idea/inspectionProfiles/Project_Default.xml
+++ /dev/null
@@ -1,46 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml
deleted file mode 100644
index cc5462da..00000000
--- a/.idea/inspectionProfiles/profiles_settings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
diff --git a/.idea/misc.xml b/.idea/misc.xml
deleted file mode 100644
index 5f549113..00000000
--- a/.idea/misc.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
-
-
diff --git a/.idea/modules.xml b/.idea/modules.xml
deleted file mode 100644
index 1e10262b..00000000
--- a/.idea/modules.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
-
-
-
-
diff --git a/.idea/strawberry-examples.iml b/.idea/strawberry-examples.iml
deleted file mode 100644
index 292aeb08..00000000
--- a/.idea/strawberry-examples.iml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
deleted file mode 100644
index 5ace414d..00000000
--- a/.idea/vcs.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
From 923e5ee6a23a6a15a2ec540882f68aebfda03698 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Thu, 30 Sep 2021 10:28:11 +0530
Subject: [PATCH 035/150] remove broken migrations
---
.../versions/6128e6679253_initial.py | 104 ------------------
1 file changed, 104 deletions(-)
delete mode 100644 reddit-clone/migrations/versions/6128e6679253_initial.py
diff --git a/reddit-clone/migrations/versions/6128e6679253_initial.py b/reddit-clone/migrations/versions/6128e6679253_initial.py
deleted file mode 100644
index a31c917f..00000000
--- a/reddit-clone/migrations/versions/6128e6679253_initial.py
+++ /dev/null
@@ -1,104 +0,0 @@
-"""initial
-
-Revision ID: 6128e6679253
-Revises:
-Create Date: 2021-09-30 10:21:28.876680
-
-"""
-from alembic import op
-import sqlalchemy as sa
-
-
-# revision identifiers, used by Alembic.
-revision = "6128e6679253"
-down_revision = None
-branch_labels = None
-depends_on = None
-
-
-def upgrade():
- # ### commands auto generated by Alembic - please adjust! ###
- op.create_table(
- "users",
- sa.Column("id", sa.Integer(), nullable=False),
- sa.Column("username", sa.String(length=32), nullable=False),
- sa.Column("email", sa.String(length=255), nullable=False),
- sa.Column("password", sa.String(length=255), nullable=False),
- sa.Column("avatar", sa.String(length=255), nullable=True),
- sa.PrimaryKeyConstraint("id"),
- sa.UniqueConstraint("email"),
- sa.UniqueConstraint("username"),
- )
- op.create_table(
- "comments",
- sa.Column("id", sa.Integer(), nullable=False),
- sa.Column("content", sa.Text(), nullable=False),
- sa.Column("votes", sa.Integer(), nullable=True),
- sa.Column("user_id", sa.Integer(), nullable=True),
- sa.ForeignKeyConstraint(
- ["user_id"],
- ["users.id"],
- ),
- sa.PrimaryKeyConstraint("id"),
- )
- op.create_table(
- "subreddits",
- sa.Column("id", sa.Integer(), nullable=False),
- sa.Column("name", sa.String(length=75), nullable=False),
- sa.Column("description", sa.String(length=255), nullable=True),
- sa.Column("admin_id", sa.Integer(), nullable=True),
- sa.Column("status", sa.SmallInteger(), nullable=True),
- sa.Column("icon", sa.String(length=255), nullable=True),
- sa.ForeignKeyConstraint(
- ["admin_id"],
- ["users.id"],
- ),
- sa.PrimaryKeyConstraint("id"),
- sa.UniqueConstraint("name"),
- )
- op.create_table(
- "posts",
- sa.Column("id", sa.Integer(), nullable=False),
- sa.Column("title", sa.String(length=150), nullable=True),
- sa.Column("text", sa.String(length=1024), nullable=True),
- sa.Column("link", sa.String(length=255), nullable=True),
- sa.Column("thumbnail", sa.String(length=255), nullable=True),
- sa.Column("user_id", sa.Integer(), nullable=True),
- sa.Column("subreddit_id", sa.Integer(), nullable=True),
- sa.Column("votes", sa.Integer(), nullable=True),
- sa.ForeignKeyConstraint(
- ["subreddit_id"],
- ["subreddits.id"],
- ),
- sa.ForeignKeyConstraint(
- ["user_id"],
- ["users.id"],
- ),
- sa.PrimaryKeyConstraint("id"),
- sa.UniqueConstraint("link"),
- )
- op.create_table(
- "subreddit_users",
- sa.Column("user_id", sa.Integer(), nullable=False),
- sa.Column("subreddit_id", sa.Integer(), nullable=False),
- sa.ForeignKeyConstraint(
- ["subreddit_id"],
- ["subreddits.id"],
- ),
- sa.ForeignKeyConstraint(
- ["user_id"],
- ["users.id"],
- ),
- sa.PrimaryKeyConstraint("user_id", "subreddit_id"),
- )
- # ### end Alembic commands ###
-
-
-def downgrade():
- # ### commands auto generated by Alembic - please adjust! ###
- op.drop_table("subreddit_users")
- op.drop_table("posts")
- op.drop_table("subreddits")
- op.drop_table("comments")
- op.drop_table("users")
- # ### end Alembic commands ###
From c475d9423c7c7a4d8431ee2f166b5587dfdf175b Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Thu, 30 Sep 2021 11:02:21 +0530
Subject: [PATCH 036/150] populate metadata
---
reddit-clone/migrations/env.py | 6 +
.../versions/6fd9b8fa9cd7_initial.py | 104 ++++++++++++++++++
2 files changed, 110 insertions(+)
create mode 100644 reddit-clone/migrations/versions/6fd9b8fa9cd7_initial.py
diff --git a/reddit-clone/migrations/env.py b/reddit-clone/migrations/env.py
index b76cae02..8b43a8a4 100644
--- a/reddit-clone/migrations/env.py
+++ b/reddit-clone/migrations/env.py
@@ -21,6 +21,12 @@
# This line sets up loggers basically.
fileConfig(config.config_file_name)
+# populate Base.metadata
+from reddit.users.models import User # noqa
+from reddit.subreddits.models import Subreddit # noqa
+from reddit.posts.models import Post # noqa
+from reddit.comments.models import Comment # noqa
+
# add your model's MetaData object here
# for 'autogenerate' support
# from myapp import mymodel
diff --git a/reddit-clone/migrations/versions/6fd9b8fa9cd7_initial.py b/reddit-clone/migrations/versions/6fd9b8fa9cd7_initial.py
new file mode 100644
index 00000000..6a2baa12
--- /dev/null
+++ b/reddit-clone/migrations/versions/6fd9b8fa9cd7_initial.py
@@ -0,0 +1,104 @@
+"""initial
+
+Revision ID: 6fd9b8fa9cd7
+Revises:
+Create Date: 2021-09-30 11:01:49.083637
+
+"""
+from alembic import op
+import sqlalchemy as sa
+
+
+# revision identifiers, used by Alembic.
+revision = "6fd9b8fa9cd7"
+down_revision = None
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.create_table(
+ "users",
+ sa.Column("id", sa.Integer(), nullable=False),
+ sa.Column("username", sa.String(length=32), nullable=False),
+ sa.Column("email", sa.String(length=255), nullable=False),
+ sa.Column("password", sa.String(length=255), nullable=False),
+ sa.Column("avatar", sa.String(length=255), nullable=True),
+ sa.PrimaryKeyConstraint("id"),
+ sa.UniqueConstraint("email"),
+ sa.UniqueConstraint("username"),
+ )
+ op.create_table(
+ "comments",
+ sa.Column("id", sa.Integer(), nullable=False),
+ sa.Column("content", sa.Text(), nullable=False),
+ sa.Column("votes", sa.Integer(), nullable=True),
+ sa.Column("user_id", sa.Integer(), nullable=True),
+ sa.ForeignKeyConstraint(
+ ["user_id"],
+ ["users.id"],
+ ),
+ sa.PrimaryKeyConstraint("id"),
+ )
+ op.create_table(
+ "subreddits",
+ sa.Column("id", sa.Integer(), nullable=False),
+ sa.Column("name", sa.String(length=75), nullable=False),
+ sa.Column("description", sa.String(length=255), nullable=True),
+ sa.Column("admin_id", sa.Integer(), nullable=True),
+ sa.Column("status", sa.SmallInteger(), nullable=True),
+ sa.Column("icon", sa.String(length=255), nullable=True),
+ sa.ForeignKeyConstraint(
+ ["admin_id"],
+ ["users.id"],
+ ),
+ sa.PrimaryKeyConstraint("id"),
+ sa.UniqueConstraint("name"),
+ )
+ op.create_table(
+ "posts",
+ sa.Column("id", sa.Integer(), nullable=False),
+ sa.Column("title", sa.String(length=150), nullable=True),
+ sa.Column("text", sa.String(length=1024), nullable=True),
+ sa.Column("link", sa.String(length=255), nullable=True),
+ sa.Column("thumbnail", sa.String(length=255), nullable=True),
+ sa.Column("user_id", sa.Integer(), nullable=True),
+ sa.Column("subreddit_id", sa.Integer(), nullable=True),
+ sa.Column("votes", sa.Integer(), nullable=True),
+ sa.ForeignKeyConstraint(
+ ["subreddit_id"],
+ ["subreddits.id"],
+ ),
+ sa.ForeignKeyConstraint(
+ ["user_id"],
+ ["users.id"],
+ ),
+ sa.PrimaryKeyConstraint("id"),
+ sa.UniqueConstraint("link"),
+ )
+ op.create_table(
+ "subreddit_users",
+ sa.Column("user_id", sa.Integer(), nullable=False),
+ sa.Column("subreddit_id", sa.Integer(), nullable=False),
+ sa.ForeignKeyConstraint(
+ ["subreddit_id"],
+ ["subreddits.id"],
+ ),
+ sa.ForeignKeyConstraint(
+ ["user_id"],
+ ["users.id"],
+ ),
+ sa.PrimaryKeyConstraint("user_id", "subreddit_id"),
+ )
+ # ### end Alembic commands ###
+
+
+def downgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.drop_table("subreddit_users")
+ op.drop_table("posts")
+ op.drop_table("subreddits")
+ op.drop_table("comments")
+ op.drop_table("users")
+ # ### end Alembic commands ###
From a0934b845541ebf339e6f3e9b6e0eaed0033723a Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Tue, 5 Oct 2021 14:51:45 +0530
Subject: [PATCH 037/150] add schema types
---
reddit-clone/reddit/base/__init__.py | 0
reddit-clone/reddit/base/types.py | 10 ++++
reddit-clone/reddit/comments/types.py | 45 +++++++++++++++
reddit-clone/reddit/posts/types.py | 74 +++++++++++++++++++++++++
reddit-clone/reddit/subreddits/types.py | 60 ++++++++++++++++++++
reddit-clone/reddit/users/types.py | 55 ++++++++++++++++++
6 files changed, 244 insertions(+)
create mode 100644 reddit-clone/reddit/base/__init__.py
create mode 100644 reddit-clone/reddit/base/types.py
create mode 100644 reddit-clone/reddit/comments/types.py
create mode 100644 reddit-clone/reddit/posts/types.py
create mode 100644 reddit-clone/reddit/subreddits/types.py
create mode 100644 reddit-clone/reddit/users/types.py
diff --git a/reddit-clone/reddit/base/__init__.py b/reddit-clone/reddit/base/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/reddit-clone/reddit/base/types.py b/reddit-clone/reddit/base/types.py
new file mode 100644
index 00000000..9057bbf1
--- /dev/null
+++ b/reddit-clone/reddit/base/types.py
@@ -0,0 +1,10 @@
+from strawberry import type, field, ID
+
+
+@type(name="Node", description="An object with an ID.")
+class NodeType:
+ id: ID = field(
+ description="""
+ ID of the object.
+ """
+ )
diff --git a/reddit-clone/reddit/comments/types.py b/reddit-clone/reddit/comments/types.py
new file mode 100644
index 00000000..ed3839a7
--- /dev/null
+++ b/reddit-clone/reddit/comments/types.py
@@ -0,0 +1,45 @@
+from __future__ import annotations
+
+from typing import List, Optional
+
+from strawberry import type, field
+
+from reddit.base.types import NodeType
+from reddit.comments.models import Comment
+
+
+@type(name="Comment")
+class CommentType(NodeType):
+ content: str = field(
+ description="""
+ The content of the comment.
+ """
+ )
+
+ votes: int = field(
+ description="""
+ The votes the comment has.
+ """
+ )
+
+ user_id: Optional[int] = field(
+ description="""
+ The owner ID of the comment.
+ """
+ )
+
+ replies: List[CommentType] = field(
+ description="""
+ The replies for the comment.
+ """
+ )
+
+ @classmethod
+ def from_instance(cls, instance: Comment) -> CommentType:
+ return CommentType(
+ id=instance.id,
+ content=instance.content,
+ votes=instance.votes,
+ user_id=instance.user_id,
+ replies=instance.replies,
+ )
diff --git a/reddit-clone/reddit/posts/types.py b/reddit-clone/reddit/posts/types.py
new file mode 100644
index 00000000..728cee89
--- /dev/null
+++ b/reddit-clone/reddit/posts/types.py
@@ -0,0 +1,74 @@
+from __future__ import annotations
+
+from typing import List, Optional
+
+from strawberry import type, field
+
+from reddit.base.types import NodeType
+from reddit.posts.models import Post
+from reddit.comments.types import CommentType
+
+
+@type(name="Post")
+class PostType(NodeType):
+ title: str = field(
+ description="""
+ The title of the post.
+ """
+ )
+
+ text: Optional[str] = field(
+ description="""
+ The text for the post.
+ """
+ )
+
+ link: Optional[str] = field(
+ description="""
+ The link of the post.
+ """
+ )
+
+ thumbnail: Optional[str] = field(
+ description="""
+ The thumbnail URL of the post.
+ """
+ )
+
+ user_id: int = field(
+ description="""
+ The owner ID of the post.
+ """
+ )
+
+ subreddit_id: int = field(
+ description="""
+ The subreddit ID of the post.
+ """
+ )
+
+ votes: int = field(
+ description="""
+ The votes the post has.
+ """
+ )
+
+ comments: List[CommentType] = field(
+ description="""
+ The comments for the post.
+ """
+ )
+
+ @classmethod
+ def from_instance(cls, instance: Post) -> PostType:
+ return PostType(
+ id=instance.id,
+ title=instance.title,
+ text=instance.text,
+ link=instance.link,
+ thumbnail=instance.thumbnail,
+ user_id=instance.user_id,
+ subreddit_id=instance.subreddit_id,
+ votes=instance.votes,
+ comments=instance.comments,
+ )
diff --git a/reddit-clone/reddit/subreddits/types.py b/reddit-clone/reddit/subreddits/types.py
new file mode 100644
index 00000000..ca361891
--- /dev/null
+++ b/reddit-clone/reddit/subreddits/types.py
@@ -0,0 +1,60 @@
+from __future__ import annotations
+
+from typing import List
+
+from strawberry import type, field
+
+from reddit.base.types import NodeType
+from reddit.subreddits.models import Subreddit
+from reddit.posts.types import PostType
+
+
+@type(name="Subreddit")
+class SubredditType(NodeType):
+ name: str = field(
+ description="""
+ The name of the subreddit.
+ """
+ )
+
+ description: str = field(
+ description="""
+ The description of the subreddit.
+ """
+ )
+
+ admin_id: int = field(
+ description="""
+ The owner ID of the subreddit.
+ """
+ )
+
+ # TODO: make status an enum
+ status: int = field(
+ description="""
+ The status of the subreddit.
+ """
+ )
+
+ icon: str = field(
+ description="""
+ The icon URL of the subreddit.
+ """
+ )
+
+ posts: List[PostType] = field(
+ description="""
+ The posts for the subreddit.
+ """
+ )
+
+ @classmethod
+ def from_instance(cls, instance: Subreddit) -> SubredditType:
+ return SubredditType(
+ id=instance.id,
+ description=instance.description,
+ admin_id=instance.admin_id,
+ status=instance.status,
+ icon=instance.icon,
+ posts=instance.posts,
+ )
diff --git a/reddit-clone/reddit/users/types.py b/reddit-clone/reddit/users/types.py
new file mode 100644
index 00000000..4aa483b5
--- /dev/null
+++ b/reddit-clone/reddit/users/types.py
@@ -0,0 +1,55 @@
+from __future__ import annotations
+
+from typing import List, Optional
+
+from strawberry import type, field
+
+from reddit.users.models import User
+from reddit.base.types import NodeType
+from reddit.posts.types import PostType
+from reddit.subreddits.types import SubredditType
+from reddit.comments.types import CommentType
+
+
+@type(name="User")
+class UserType(NodeType):
+ username: str = field(
+ description="""
+ The username of the user.
+ """
+ )
+
+ avatar: Optional[str] = field(
+ description="""
+ The avatar URL of the user.
+ """
+ )
+
+ posts: List[PostType] = field(
+ description="""
+ The posts for the user.
+ """
+ )
+
+ subreddits: List[SubredditType] = field(
+ description="""
+ The subreddits the user is in.
+ """
+ )
+
+ comments: List[CommentType] = field(
+ description="""
+ The comments for the user.
+ """
+ )
+
+ @classmethod
+ def from_instance(cls, instance: User) -> UserType:
+ return UserType(
+ id=instance.id,
+ username=instance.username,
+ avatar=instance.avatar,
+ posts=instance.posts,
+ subreddits=instance.subreddits,
+ comments=instance.comments,
+ )
From 717a30ad79a034db9138057e88441c145ec33f64 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Tue, 5 Oct 2021 16:10:00 +0530
Subject: [PATCH 038/150] add user loaders
---
reddit-clone/reddit/users/loaders.py | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
create mode 100644 reddit-clone/reddit/users/loaders.py
diff --git a/reddit-clone/reddit/users/loaders.py b/reddit-clone/reddit/users/loaders.py
new file mode 100644
index 00000000..36617a1c
--- /dev/null
+++ b/reddit-clone/reddit/users/loaders.py
@@ -0,0 +1,18 @@
+from typing import List, Optional, Dict
+
+from sqlalchemy import select
+from sqlalchemy.engine import Result
+
+from reddit.database import get_session
+from reddit.users.models import User
+
+
+async def load_users(user_ids: List[int]) -> List[Optional[User]]:
+ """
+ Batch-loads users by their IDs.
+ """
+ query = select(User).filter(User.id.in_(user_ids))
+ async with get_session() as session:
+ result: Result = await session.execute(query)
+ user_map: Dict[int, User] = {user.id: user for user in result.scalars()}
+ return [user_map.get(user_id) for user_id in user_ids]
From 82c1c99153905f8e8c32cd5adf0d7c0d5243955b Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Tue, 5 Oct 2021 16:15:40 +0530
Subject: [PATCH 039/150] update context
---
reddit-clone/poetry.lock | 86 ++++++++++++++++-----------------
reddit-clone/pyproject.toml | 1 +
reddit-clone/reddit/__init__.py | 22 +++++++--
3 files changed, 63 insertions(+), 46 deletions(-)
diff --git a/reddit-clone/poetry.lock b/reddit-clone/poetry.lock
index 37b7640c..a772147d 100644
--- a/reddit-clone/poetry.lock
+++ b/reddit-clone/poetry.lock
@@ -404,7 +404,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
[[package]]
name = "regex"
-version = "2021.9.24"
+version = "2021.9.30"
description = "Alternative regular expression module, to replace re."
category = "dev"
optional = false
@@ -603,7 +603,7 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytes
[metadata]
lock-version = "1.1"
python-versions = "^3.7"
-content-hash = "0be25f7a7b2a6d2767a4d40638945fea8d0148af2efd0ab4c26d50b2eaced91b"
+content-hash = "f3370347eb4ab8f408c13bdc052245300e88eca06607c5302a492c703bf2d4f8"
[metadata.files]
alembic = [
@@ -910,47 +910,47 @@ pyyaml = [
{file = "PyYAML-5.4.1.tar.gz", hash = "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e"},
]
regex = [
- {file = "regex-2021.9.24-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0628ed7d6334e8f896f882a5c1240de8c4d9b0dd7c7fb8e9f4692f5684b7d656"},
- {file = "regex-2021.9.24-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3baf3eaa41044d4ced2463fd5d23bf7bd4b03d68739c6c99a59ce1f95599a673"},
- {file = "regex-2021.9.24-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c000635fd78400a558bd7a3c2981bb2a430005ebaa909d31e6e300719739a949"},
- {file = "regex-2021.9.24-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:295bc8a13554a25ad31e44c4bedabd3c3e28bba027e4feeb9bb157647a2344a7"},
- {file = "regex-2021.9.24-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b0e3f59d3c772f2c3baaef2db425e6fc4149d35a052d874bb95ccfca10a1b9f4"},
- {file = "regex-2021.9.24-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:aea4006b73b555fc5bdb650a8b92cf486d678afa168cf9b38402bb60bf0f9c18"},
- {file = "regex-2021.9.24-cp310-cp310-win32.whl", hash = "sha256:09eb62654030f39f3ba46bc6726bea464069c29d00a9709e28c9ee9623a8da4a"},
- {file = "regex-2021.9.24-cp310-cp310-win_amd64.whl", hash = "sha256:8d80087320632457aefc73f686f66139801959bf5b066b4419b92be85be3543c"},
- {file = "regex-2021.9.24-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:7e3536f305f42ad6d31fc86636c54c7dafce8d634e56fef790fbacb59d499dd5"},
- {file = "regex-2021.9.24-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c31f35a984caffb75f00a86852951a337540b44e4a22171354fb760cefa09346"},
- {file = "regex-2021.9.24-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c7cb25adba814d5f419733fe565f3289d6fa629ab9e0b78f6dff5fa94ab0456"},
- {file = "regex-2021.9.24-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:85c61bee5957e2d7be390392feac7e1d7abd3a49cbaed0c8cee1541b784c8561"},
- {file = "regex-2021.9.24-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c94722bf403b8da744b7d0bb87e1f2529383003ceec92e754f768ef9323f69ad"},
- {file = "regex-2021.9.24-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6adc1bd68f81968c9d249aab8c09cdc2cbe384bf2d2cb7f190f56875000cdc72"},
- {file = "regex-2021.9.24-cp36-cp36m-win32.whl", hash = "sha256:2054dea683f1bda3a804fcfdb0c1c74821acb968093d0be16233873190d459e3"},
- {file = "regex-2021.9.24-cp36-cp36m-win_amd64.whl", hash = "sha256:7783d89bd5413d183a38761fbc68279b984b9afcfbb39fa89d91f63763fbfb90"},
- {file = "regex-2021.9.24-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b15dc34273aefe522df25096d5d087abc626e388a28a28ac75a4404bb7668736"},
- {file = "regex-2021.9.24-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:10a7a9cbe30bd90b7d9a1b4749ef20e13a3528e4215a2852be35784b6bd070f0"},
- {file = "regex-2021.9.24-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fb9f5844db480e2ef9fce3a72e71122dd010ab7b2920f777966ba25f7eb63819"},
- {file = "regex-2021.9.24-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:17310b181902e0bb42b29c700e2c2346b8d81f26e900b1328f642e225c88bce1"},
- {file = "regex-2021.9.24-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0bba1f6df4eafe79db2ecf38835c2626dbd47911e0516f6962c806f83e7a99ae"},
- {file = "regex-2021.9.24-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:821e10b73e0898544807a0692a276e539e5bafe0a055506a6882814b6a02c3ec"},
- {file = "regex-2021.9.24-cp37-cp37m-win32.whl", hash = "sha256:9c371dd326289d85906c27ec2bc1dcdedd9d0be12b543d16e37bad35754bde48"},
- {file = "regex-2021.9.24-cp37-cp37m-win_amd64.whl", hash = "sha256:1e8d1898d4fb817120a5f684363b30108d7b0b46c7261264b100d14ec90a70e7"},
- {file = "regex-2021.9.24-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8a5c2250c0a74428fd5507ae8853706fdde0f23bfb62ee1ec9418eeacf216078"},
- {file = "regex-2021.9.24-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8aec4b4da165c4a64ea80443c16e49e3b15df0f56c124ac5f2f8708a65a0eddc"},
- {file = "regex-2021.9.24-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:650c4f1fc4273f4e783e1d8e8b51a3e2311c2488ba0fcae6425b1e2c248a189d"},
- {file = "regex-2021.9.24-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2cdb3789736f91d0b3333ac54d12a7e4f9efbc98f53cb905d3496259a893a8b3"},
- {file = "regex-2021.9.24-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4e61100200fa6ab7c99b61476f9f9653962ae71b931391d0264acfb4d9527d9c"},
- {file = "regex-2021.9.24-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8c268e78d175798cd71d29114b0a1f1391c7d011995267d3b62319ec1a4ecaa1"},
- {file = "regex-2021.9.24-cp38-cp38-win32.whl", hash = "sha256:658e3477676009083422042c4bac2bdad77b696e932a3de001c42cc046f8eda2"},
- {file = "regex-2021.9.24-cp38-cp38-win_amd64.whl", hash = "sha256:a731552729ee8ae9c546fb1c651c97bf5f759018fdd40d0e9b4d129e1e3a44c8"},
- {file = "regex-2021.9.24-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:86f9931eb92e521809d4b64ec8514f18faa8e11e97d6c2d1afa1bcf6c20a8eab"},
- {file = "regex-2021.9.24-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dcbbc9cfa147d55a577d285fd479b43103188855074552708df7acc31a476dd9"},
- {file = "regex-2021.9.24-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:29385c4dbb3f8b3a55ce13de6a97a3d21bd00de66acd7cdfc0b49cb2f08c906c"},
- {file = "regex-2021.9.24-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:c50a6379763c733562b1fee877372234d271e5c78cd13ade5f25978aa06744db"},
- {file = "regex-2021.9.24-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f74b6d8f59f3cfb8237e25c532b11f794b96f5c89a6f4a25857d85f84fbef11"},
- {file = "regex-2021.9.24-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6c4d83d21d23dd854ffbc8154cf293f4e43ba630aa9bd2539c899343d7f59da3"},
- {file = "regex-2021.9.24-cp39-cp39-win32.whl", hash = "sha256:95e89a8558c8c48626dcffdf9c8abac26b7c251d352688e7ab9baf351e1c7da6"},
- {file = "regex-2021.9.24-cp39-cp39-win_amd64.whl", hash = "sha256:835962f432bce92dc9bf22903d46c50003c8d11b1dc64084c8fae63bca98564a"},
- {file = "regex-2021.9.24.tar.gz", hash = "sha256:6266fde576e12357b25096351aac2b4b880b0066263e7bc7a9a1b4307991bb0e"},
+ {file = "regex-2021.9.30-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:66696c8336a1b5d1182464f3af3427cc760118f26d0b09a2ddc16a976a4d2637"},
+ {file = "regex-2021.9.30-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d87459ad3ab40cd8493774f8a454b2e490d8e729e7e402a0625867a983e4e02"},
+ {file = "regex-2021.9.30-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78cf6a1e023caf5e9a982f5377414e1aeac55198831b852835732cfd0a0ca5ff"},
+ {file = "regex-2021.9.30-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:255791523f80ea8e48e79af7120b4697ef3b74f6886995dcdb08c41f8e516be0"},
+ {file = "regex-2021.9.30-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e502f8d4e5ef714bcc2c94d499684890c94239526d61fdf1096547db91ca6aa6"},
+ {file = "regex-2021.9.30-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4907fb0f9b9309a5bded72343e675a252c2589a41871874feace9a05a540241e"},
+ {file = "regex-2021.9.30-cp310-cp310-win32.whl", hash = "sha256:3be40f720af170a6b20ddd2ad7904c58b13d2b56f6734ee5d09bbdeed2fa4816"},
+ {file = "regex-2021.9.30-cp310-cp310-win_amd64.whl", hash = "sha256:c2b180ed30856dfa70cfe927b0fd38e6b68198a03039abdbeb1f2029758d87e7"},
+ {file = "regex-2021.9.30-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:e6f2d2f93001801296fe3ca86515eb04915472b5380d4d8752f09f25f0b9b0ed"},
+ {file = "regex-2021.9.30-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4fa7ba9ab2eba7284e0d7d94f61df7af86015b0398e123331362270d71fab0b9"},
+ {file = "regex-2021.9.30-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28040e89a04b60d579c69095c509a4f6a1a5379cd865258e3a186b7105de72c6"},
+ {file = "regex-2021.9.30-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f588209d3e4797882cd238195c175290dbc501973b10a581086b5c6bcd095ffb"},
+ {file = "regex-2021.9.30-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:42952d325439ef223e4e9db7ee6d9087b5c68c5c15b1f9de68e990837682fc7b"},
+ {file = "regex-2021.9.30-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cae4099031d80703954c39680323dabd87a69b21262303160776aa0e55970ca0"},
+ {file = "regex-2021.9.30-cp36-cp36m-win32.whl", hash = "sha256:0de8ad66b08c3e673b61981b9e3626f8784d5564f8c3928e2ad408c0eb5ac38c"},
+ {file = "regex-2021.9.30-cp36-cp36m-win_amd64.whl", hash = "sha256:b345ecde37c86dd7084c62954468a4a655fd2d24fd9b237949dd07a4d0dd6f4c"},
+ {file = "regex-2021.9.30-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a6f08187136f11e430638c2c66e1db091105d7c2e9902489f0dbc69b44c222b4"},
+ {file = "regex-2021.9.30-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b55442650f541d195a535ccec33078c78a9521973fb960923da7515e9ed78fa6"},
+ {file = "regex-2021.9.30-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87e9c489aa98f50f367fb26cc9c8908d668e9228d327644d7aa568d47e456f47"},
+ {file = "regex-2021.9.30-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:e2cb7d4909ed16ed35729d38af585673f1f0833e73dfdf0c18e5be0061107b99"},
+ {file = "regex-2021.9.30-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d0861e7f6325e821d5c40514c551fd538b292f8cc3960086e73491b9c5d8291d"},
+ {file = "regex-2021.9.30-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:81fdc90f999b2147fc62e303440c424c47e5573a9b615ed5d43a5b832efcca9e"},
+ {file = "regex-2021.9.30-cp37-cp37m-win32.whl", hash = "sha256:8c1ad61fa024195136a6b7b89538030bd00df15f90ac177ca278df9b2386c96f"},
+ {file = "regex-2021.9.30-cp37-cp37m-win_amd64.whl", hash = "sha256:e3770781353a4886b68ef10cec31c1f61e8e3a0be5f213c2bb15a86efd999bc4"},
+ {file = "regex-2021.9.30-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9c065d95a514a06b92a5026766d72ac91bfabf581adb5b29bc5c91d4b3ee9b83"},
+ {file = "regex-2021.9.30-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9925985be05d54b3d25fd6c1ea8e50ff1f7c2744c75bdc4d3b45c790afa2bcb3"},
+ {file = "regex-2021.9.30-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:470f2c882f2672d8eeda8ab27992aec277c067d280b52541357e1acd7e606dae"},
+ {file = "regex-2021.9.30-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:ad0517df22a97f1da20d8f1c8cb71a5d1997fa383326b81f9cf22c9dadfbdf34"},
+ {file = "regex-2021.9.30-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c9e30838df7bfd20db6466fd309d9b580d32855f8e2c2e6d74cf9da27dcd9b63"},
+ {file = "regex-2021.9.30-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5b34d2335d6aedec7dcadd3f8283b9682fadad8b9b008da8788d2fce76125ebe"},
+ {file = "regex-2021.9.30-cp38-cp38-win32.whl", hash = "sha256:e07049cece3462c626d650e8bf42ddbca3abf4aa08155002c28cb6d9a5a281e2"},
+ {file = "regex-2021.9.30-cp38-cp38-win_amd64.whl", hash = "sha256:37868075eda024470bd0feab872c692ac4ee29db1e14baec103257bf6cc64346"},
+ {file = "regex-2021.9.30-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d331f238a7accfbbe1c4cd1ba610d4c087b206353539331e32a8f05345c74aec"},
+ {file = "regex-2021.9.30-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6348a7ab2a502cbdd0b7fd0496d614007489adb7361956b38044d1d588e66e04"},
+ {file = "regex-2021.9.30-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce7b1cca6c23f19bee8dc40228d9c314d86d1e51996b86f924aca302fc8f8bf9"},
+ {file = "regex-2021.9.30-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1f1125bc5172ab3a049bc6f4b9c0aae95a2a2001a77e6d6e4239fa3653e202b5"},
+ {file = "regex-2021.9.30-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:638e98d069b14113e8afba6a54d1ca123f712c0d105e67c1f9211b2a825ef926"},
+ {file = "regex-2021.9.30-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9a0b0db6b49da7fa37ca8eddf9f40a8dbc599bad43e64f452284f37b6c34d91c"},
+ {file = "regex-2021.9.30-cp39-cp39-win32.whl", hash = "sha256:9910869c472e5a6728680ca357b5846546cbbd2ab3ad5bef986ef0bc438d0aa6"},
+ {file = "regex-2021.9.30-cp39-cp39-win_amd64.whl", hash = "sha256:3b71213ec3bad9a5a02e049f2ec86b3d7c3e350129ae0f4e2f99c12b5da919ed"},
+ {file = "regex-2021.9.30.tar.gz", hash = "sha256:81e125d9ba54c34579e4539a967e976a3c56150796674aec318b1b2f49251be7"},
]
six = [
{file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
diff --git a/reddit-clone/pyproject.toml b/reddit-clone/pyproject.toml
index 7d362310..cb8694d8 100644
--- a/reddit-clone/pyproject.toml
+++ b/reddit-clone/pyproject.toml
@@ -12,6 +12,7 @@ alembic = "^1.7.1"
asyncpg = "^0.24"
strawberry-graphql = {extras = ["asgi"], version = "^0.77.0"}
fastapi = "^0.68.1"
+starlette = "^0.14"
[tool.poetry.dev-dependencies]
diff --git a/reddit-clone/reddit/__init__.py b/reddit-clone/reddit/__init__.py
index e6684e7a..5b1cccc8 100644
--- a/reddit-clone/reddit/__init__.py
+++ b/reddit-clone/reddit/__init__.py
@@ -1,8 +1,24 @@
+from typing import Union, Optional, Any
+
from fastapi import FastAPI
+from starlette.requests import Request
+from starlette.responses import Response
+from starlette.websockets import WebSocket
+from strawberry.dataloader import DataLoader
from strawberry.asgi import GraphQL
-from .settings import DEBUG
-from .schema import schema
+from reddit.settings import DEBUG
+from reddit.schema import schema
+from reddit.users.loaders import load_users
+
+
+class MyGraphQL(GraphQL):
+ async def get_context(
+ self, request: Union[Request, WebSocket], response: Optional[Response] = None
+ ) -> Optional[Any]:
+ context: dict = await super().get_context(request, response=response)
+ context.update(user_loader=DataLoader(load_fn=load_users))
+ return context
def create_application() -> FastAPI:
@@ -13,7 +29,7 @@ def create_application() -> FastAPI:
"""
application = FastAPI(title="Reddit GraphQL", debug=DEBUG)
- graphql_app = GraphQL(schema=schema, graphiql=True, debug=DEBUG)
+ graphql_app = MyGraphQL(schema=schema, graphiql=True, debug=DEBUG)
application.add_route(path="/graphql", route=graphql_app)
From c7e5e5d0c7eca6c5d5bf478c47b5649e81325068 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Wed, 6 Oct 2021 14:42:12 +0530
Subject: [PATCH 040/150] update model: user
---
reddit-clone/poetry.lock | 18 +++++++++++++++++-
reddit-clone/pyproject.toml | 1 +
reddit-clone/reddit/base/queries.py | 0
reddit-clone/reddit/base/types.py | 23 +++++++++++++++++++++++
reddit-clone/reddit/schema.py | 8 ++++----
reddit-clone/reddit/users/models.py | 4 ++--
6 files changed, 47 insertions(+), 7 deletions(-)
create mode 100644 reddit-clone/reddit/base/queries.py
diff --git a/reddit-clone/poetry.lock b/reddit-clone/poetry.lock
index a772147d..0728f1a4 100644
--- a/reddit-clone/poetry.lock
+++ b/reddit-clone/poetry.lock
@@ -183,6 +183,18 @@ category = "main"
optional = false
python-versions = ">=3.6,<4"
+[[package]]
+name = "graphql-relay"
+version = "3.1.0"
+description = "Relay library for graphql-core"
+category = "main"
+optional = false
+python-versions = ">=3.6,<4"
+
+[package.dependencies]
+graphql-core = ">=3.1"
+typing-extensions = {version = ">=3.7,<4", markers = "python_version < \"3.8\""}
+
[[package]]
name = "greenlet"
version = "1.1.2"
@@ -603,7 +615,7 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytes
[metadata]
lock-version = "1.1"
python-versions = "^3.7"
-content-hash = "f3370347eb4ab8f408c13bdc052245300e88eca06607c5302a492c703bf2d4f8"
+content-hash = "c7f68ad613b93e290f94e02da38f6c0c46e7b049d42fcf911543dc7762ac2c97"
[metadata.files]
alembic = [
@@ -669,6 +681,10 @@ graphql-core = [
{file = "graphql-core-3.1.6.tar.gz", hash = "sha256:e65975b6a13878f9113a1fa5320760585b522d139944e005936b1b8358d0651a"},
{file = "graphql_core-3.1.6-py3-none-any.whl", hash = "sha256:c78d09596d347e1cffd266c5384abfedf43ed1eae08729773bebb3d527fe5a14"},
]
+graphql-relay = [
+ {file = "graphql-relay-3.1.0.tar.gz", hash = "sha256:70d5a7ee5995ea7c2a9a37e51227663b1a464f1f40e98fdde950be5415dfe0b4"},
+ {file = "graphql_relay-3.1.0-py3-none-any.whl", hash = "sha256:2cda0ac0199dd56c28ca4f6e0381cdcf5787809c06d1507df3c2a738f9ad846f"},
+]
greenlet = [
{file = "greenlet-1.1.2-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:58df5c2a0e293bf665a51f8a100d3e9956febfbf1d9aaf8c0677cf70218910c6"},
{file = "greenlet-1.1.2-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:aec52725173bd3a7b56fe91bc56eccb26fbdff1386ef123abb63c84c5b43b63a"},
diff --git a/reddit-clone/pyproject.toml b/reddit-clone/pyproject.toml
index cb8694d8..0bca5049 100644
--- a/reddit-clone/pyproject.toml
+++ b/reddit-clone/pyproject.toml
@@ -13,6 +13,7 @@ asyncpg = "^0.24"
strawberry-graphql = {extras = ["asgi"], version = "^0.77.0"}
fastapi = "^0.68.1"
starlette = "^0.14"
+graphql-relay = "^3.1.0"
[tool.poetry.dev-dependencies]
diff --git a/reddit-clone/reddit/base/queries.py b/reddit-clone/reddit/base/queries.py
new file mode 100644
index 00000000..e69de29b
diff --git a/reddit-clone/reddit/base/types.py b/reddit-clone/reddit/base/types.py
index 9057bbf1..c2e15e92 100644
--- a/reddit-clone/reddit/base/types.py
+++ b/reddit-clone/reddit/base/types.py
@@ -1,4 +1,6 @@
from strawberry import type, field, ID
+from strawberry.types import Info
+from graphql_relay import from_global_id, to_global_id
@type(name="Node", description="An object with an ID.")
@@ -8,3 +10,24 @@ class NodeType:
ID of the object.
"""
)
+
+ @classmethod
+ def get_node_from_global_id(cls, info: Info, global_id: str, only_type=None):
+ try:
+ _type, _id = cls.from_global_id(global_id)
+ except Exception as e:
+ raise Exception(
+ f'Unable to parse global ID "{global_id}". '
+ 'Make sure it is a base64 encoded string in the format: "TypeName:id". '
+ f"Exception message: {str(e)}"
+ )
+
+ info.schema.get_type(_type)
+
+ @classmethod
+ def from_global_id(cls, global_id: str):
+ return from_global_id(global_id)
+
+ @classmethod
+ def to_global_id(cls, schema_type: str, id: str):
+ return to_global_id(schema_type, id)
diff --git a/reddit-clone/reddit/schema.py b/reddit-clone/reddit/schema.py
index 77439e13..539ad262 100644
--- a/reddit-clone/reddit/schema.py
+++ b/reddit-clone/reddit/schema.py
@@ -1,11 +1,11 @@
-import strawberry
+from strawberry import type, field, Schema
-@strawberry.type
+@type
class Query:
- @strawberry.field()
+ @field
def hello_world(self) -> str:
return "Hello world!"
-schema = strawberry.Schema(query=Query, mutation=None)
+schema = Schema(query=Query, mutation=None)
diff --git a/reddit-clone/reddit/users/models.py b/reddit-clone/reddit/users/models.py
index 5eec2459..a1b8c0bf 100644
--- a/reddit-clone/reddit/users/models.py
+++ b/reddit-clone/reddit/users/models.py
@@ -1,4 +1,4 @@
-from typing import Optional, List
+from typing import List
from sqlalchemy import Column, String, Integer, ForeignKey
from sqlalchemy.orm import relationship
@@ -24,7 +24,7 @@ class User(Base):
password: str = Column(String(255), nullable=False)
- avatar: Optional[str] = Column(String(255), default=None)
+ avatar: str = Column(String(255), nullable=False, default="default.jpg")
posts: List[Post] = relationship("Post", back_populates="user", lazy="dynamic")
From fdf42aeeb6de32efa9425b023cad1377b6597446 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Wed, 6 Oct 2021 14:49:05 +0530
Subject: [PATCH 041/150] update type: node
---
reddit-clone/reddit/base/types.py | 17 ++++++++++++++++-
1 file changed, 16 insertions(+), 1 deletion(-)
diff --git a/reddit-clone/reddit/base/types.py b/reddit-clone/reddit/base/types.py
index c2e15e92..5acaa53b 100644
--- a/reddit-clone/reddit/base/types.py
+++ b/reddit-clone/reddit/base/types.py
@@ -22,7 +22,22 @@ def get_node_from_global_id(cls, info: Info, global_id: str, only_type=None):
f"Exception message: {str(e)}"
)
- info.schema.get_type(_type)
+ schema_type = info.schema.get_type(_type)
+ if schema_type is None:
+ raise Exception(f'Relay Node "{_type}" not found in schema')
+
+ if only_type:
+ assert schema_type == only_type
+
+ # We make sure the ObjectType implements the "Node" interface
+ if cls not in schema_type.interfaces:
+ raise Exception(
+ f'ObjectType "{_type}" does not implement the "{cls}" interface.'
+ )
+
+ get_node = getattr(schema_type, "get_node", None)
+ if get_node:
+ return get_node(info, _id)
@classmethod
def from_global_id(cls, global_id: str):
From c1520b691188f22421a3a3aa193323ba87e2ac10 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Wed, 6 Oct 2021 15:04:10 +0530
Subject: [PATCH 042/150] implement classmethod: get_node on node types
---
reddit-clone/reddit/comments/types.py | 13 +++++++++++++
reddit-clone/reddit/posts/types.py | 13 +++++++++++++
reddit-clone/reddit/subreddits/types.py | 15 ++++++++++++++-
reddit-clone/reddit/users/types.py | 15 ++++++++++++++-
4 files changed, 54 insertions(+), 2 deletions(-)
diff --git a/reddit-clone/reddit/comments/types.py b/reddit-clone/reddit/comments/types.py
index ed3839a7..341b7b0e 100644
--- a/reddit-clone/reddit/comments/types.py
+++ b/reddit-clone/reddit/comments/types.py
@@ -3,9 +3,12 @@
from typing import List, Optional
from strawberry import type, field
+from strawberry.types import Info
+from sqlalchemy import select
from reddit.base.types import NodeType
from reddit.comments.models import Comment
+from reddit.database import get_session
@type(name="Comment")
@@ -34,6 +37,16 @@ class CommentType(NodeType):
"""
)
+ @classmethod
+ async def get_node(cls, info: Info, comment_id: str) -> Optional[Comment]:
+ """
+ Gets a comment with the given ID.
+ """
+ query = select(Comment).filter_by(id=comment_id).first()
+ async with get_session() as session:
+ user = await session.execute(query)
+ return user
+
@classmethod
def from_instance(cls, instance: Comment) -> CommentType:
return CommentType(
diff --git a/reddit-clone/reddit/posts/types.py b/reddit-clone/reddit/posts/types.py
index 728cee89..35b983ca 100644
--- a/reddit-clone/reddit/posts/types.py
+++ b/reddit-clone/reddit/posts/types.py
@@ -3,10 +3,13 @@
from typing import List, Optional
from strawberry import type, field
+from strawberry.types import Info
+from sqlalchemy import select
from reddit.base.types import NodeType
from reddit.posts.models import Post
from reddit.comments.types import CommentType
+from reddit.database import get_session
@type(name="Post")
@@ -59,6 +62,16 @@ class PostType(NodeType):
"""
)
+ @classmethod
+ async def get_node(cls, info: Info, post_id: str) -> Optional[Post]:
+ """
+ Gets a post with the given ID.
+ """
+ query = select(Post).filter_by(id=post_id).first()
+ async with get_session() as session:
+ user = await session.execute(query)
+ return user
+
@classmethod
def from_instance(cls, instance: Post) -> PostType:
return PostType(
diff --git a/reddit-clone/reddit/subreddits/types.py b/reddit-clone/reddit/subreddits/types.py
index ca361891..ce36c26b 100644
--- a/reddit-clone/reddit/subreddits/types.py
+++ b/reddit-clone/reddit/subreddits/types.py
@@ -1,12 +1,15 @@
from __future__ import annotations
-from typing import List
+from typing import List, Optional
from strawberry import type, field
+from strawberry.types import Info
+from sqlalchemy import select
from reddit.base.types import NodeType
from reddit.subreddits.models import Subreddit
from reddit.posts.types import PostType
+from reddit.database import get_session
@type(name="Subreddit")
@@ -48,6 +51,16 @@ class SubredditType(NodeType):
"""
)
+ @classmethod
+ async def get_node(cls, info: Info, subreddit_id: str) -> Optional[Subreddit]:
+ """
+ Gets a subreddit with the given ID.
+ """
+ query = select(Subreddit).filter_by(id=subreddit_id).first()
+ async with get_session() as session:
+ user = await session.execute(query)
+ return user
+
@classmethod
def from_instance(cls, instance: Subreddit) -> SubredditType:
return SubredditType(
diff --git a/reddit-clone/reddit/users/types.py b/reddit-clone/reddit/users/types.py
index 4aa483b5..1975410e 100644
--- a/reddit-clone/reddit/users/types.py
+++ b/reddit-clone/reddit/users/types.py
@@ -3,12 +3,15 @@
from typing import List, Optional
from strawberry import type, field
+from strawberry.types import Info
+from sqlalchemy import select
from reddit.users.models import User
from reddit.base.types import NodeType
from reddit.posts.types import PostType
from reddit.subreddits.types import SubredditType
from reddit.comments.types import CommentType
+from reddit.database import get_session
@type(name="User")
@@ -19,7 +22,7 @@ class UserType(NodeType):
"""
)
- avatar: Optional[str] = field(
+ avatar: str = field(
description="""
The avatar URL of the user.
"""
@@ -43,6 +46,16 @@ class UserType(NodeType):
"""
)
+ @classmethod
+ async def get_node(cls, info: Info, user_id: str) -> Optional[User]:
+ """
+ Gets an user with the given ID.
+ """
+ query = select(User).filter_by(id=user_id).first()
+ async with get_session() as session:
+ user = await session.execute(query)
+ return user
+
@classmethod
def from_instance(cls, instance: User) -> UserType:
return UserType(
From 2487b21eda8cbd1f0dfbd762e138f2d4783ff6d0 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Wed, 6 Oct 2021 15:31:59 +0530
Subject: [PATCH 043/150] update nodes
---
reddit-clone/reddit/comments/types.py | 7 ++++---
reddit-clone/reddit/posts/types.py | 7 ++++---
reddit-clone/reddit/subreddits/types.py | 7 ++++---
reddit-clone/reddit/users/types.py | 5 +++--
4 files changed, 15 insertions(+), 11 deletions(-)
diff --git a/reddit-clone/reddit/comments/types.py b/reddit-clone/reddit/comments/types.py
index 341b7b0e..ba805fd1 100644
--- a/reddit-clone/reddit/comments/types.py
+++ b/reddit-clone/reddit/comments/types.py
@@ -38,14 +38,15 @@ class CommentType(NodeType):
)
@classmethod
- async def get_node(cls, info: Info, comment_id: str) -> Optional[Comment]:
+ async def get_node(cls, info: Info, comment_id: str) -> Optional[CommentType]:
"""
Gets a comment with the given ID.
"""
query = select(Comment).filter_by(id=comment_id).first()
async with get_session() as session:
- user = await session.execute(query)
- return user
+ comment = await session.execute(query)
+ if comment is not None:
+ return cls.from_instance(comment)
@classmethod
def from_instance(cls, instance: Comment) -> CommentType:
diff --git a/reddit-clone/reddit/posts/types.py b/reddit-clone/reddit/posts/types.py
index 35b983ca..e7bb72d6 100644
--- a/reddit-clone/reddit/posts/types.py
+++ b/reddit-clone/reddit/posts/types.py
@@ -63,14 +63,15 @@ class PostType(NodeType):
)
@classmethod
- async def get_node(cls, info: Info, post_id: str) -> Optional[Post]:
+ async def get_node(cls, info: Info, post_id: str) -> Optional[PostType]:
"""
Gets a post with the given ID.
"""
query = select(Post).filter_by(id=post_id).first()
async with get_session() as session:
- user = await session.execute(query)
- return user
+ post = await session.execute(query)
+ if post is not None:
+ return cls.from_instance(post)
@classmethod
def from_instance(cls, instance: Post) -> PostType:
diff --git a/reddit-clone/reddit/subreddits/types.py b/reddit-clone/reddit/subreddits/types.py
index ce36c26b..defdf8b7 100644
--- a/reddit-clone/reddit/subreddits/types.py
+++ b/reddit-clone/reddit/subreddits/types.py
@@ -52,14 +52,15 @@ class SubredditType(NodeType):
)
@classmethod
- async def get_node(cls, info: Info, subreddit_id: str) -> Optional[Subreddit]:
+ async def get_node(cls, info: Info, subreddit_id: str) -> Optional[SubredditType]:
"""
Gets a subreddit with the given ID.
"""
query = select(Subreddit).filter_by(id=subreddit_id).first()
async with get_session() as session:
- user = await session.execute(query)
- return user
+ subreddit = await session.execute(query)
+ if subreddit is not None:
+ return cls.from_instance(subreddit)
@classmethod
def from_instance(cls, instance: Subreddit) -> SubredditType:
diff --git a/reddit-clone/reddit/users/types.py b/reddit-clone/reddit/users/types.py
index 1975410e..2a76fef3 100644
--- a/reddit-clone/reddit/users/types.py
+++ b/reddit-clone/reddit/users/types.py
@@ -47,14 +47,15 @@ class UserType(NodeType):
)
@classmethod
- async def get_node(cls, info: Info, user_id: str) -> Optional[User]:
+ async def get_node(cls, info: Info, user_id: str) -> Optional[UserType]:
"""
Gets an user with the given ID.
"""
query = select(User).filter_by(id=user_id).first()
async with get_session() as session:
user = await session.execute(query)
- return user
+ if user is not None:
+ return cls.from_instance(user)
@classmethod
def from_instance(cls, instance: User) -> UserType:
From f4ec66395ebb443b75769663351792f7fea3b405 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Wed, 6 Oct 2021 15:33:18 +0530
Subject: [PATCH 044/150] rename node resolver
---
reddit-clone/reddit/base/queries.py | 0
reddit-clone/reddit/base/types.py | 6 +++---
reddit-clone/reddit/comments/types.py | 2 +-
reddit-clone/reddit/posts/types.py | 2 +-
reddit-clone/reddit/subreddits/types.py | 4 +++-
reddit-clone/reddit/users/types.py | 2 +-
6 files changed, 9 insertions(+), 7 deletions(-)
delete mode 100644 reddit-clone/reddit/base/queries.py
diff --git a/reddit-clone/reddit/base/queries.py b/reddit-clone/reddit/base/queries.py
deleted file mode 100644
index e69de29b..00000000
diff --git a/reddit-clone/reddit/base/types.py b/reddit-clone/reddit/base/types.py
index 5acaa53b..941ac2de 100644
--- a/reddit-clone/reddit/base/types.py
+++ b/reddit-clone/reddit/base/types.py
@@ -35,9 +35,9 @@ def get_node_from_global_id(cls, info: Info, global_id: str, only_type=None):
f'ObjectType "{_type}" does not implement the "{cls}" interface.'
)
- get_node = getattr(schema_type, "get_node", None)
- if get_node:
- return get_node(info, _id)
+ resolver = getattr(schema_type, "resolve_node", None)
+ if resolver is not None:
+ return resolver(info, _id)
@classmethod
def from_global_id(cls, global_id: str):
diff --git a/reddit-clone/reddit/comments/types.py b/reddit-clone/reddit/comments/types.py
index ba805fd1..13a173dc 100644
--- a/reddit-clone/reddit/comments/types.py
+++ b/reddit-clone/reddit/comments/types.py
@@ -38,7 +38,7 @@ class CommentType(NodeType):
)
@classmethod
- async def get_node(cls, info: Info, comment_id: str) -> Optional[CommentType]:
+ async def resolve_node(cls, info: Info, comment_id: str) -> Optional[CommentType]:
"""
Gets a comment with the given ID.
"""
diff --git a/reddit-clone/reddit/posts/types.py b/reddit-clone/reddit/posts/types.py
index e7bb72d6..450c3552 100644
--- a/reddit-clone/reddit/posts/types.py
+++ b/reddit-clone/reddit/posts/types.py
@@ -63,7 +63,7 @@ class PostType(NodeType):
)
@classmethod
- async def get_node(cls, info: Info, post_id: str) -> Optional[PostType]:
+ async def resolve_node(cls, info: Info, post_id: str) -> Optional[PostType]:
"""
Gets a post with the given ID.
"""
diff --git a/reddit-clone/reddit/subreddits/types.py b/reddit-clone/reddit/subreddits/types.py
index defdf8b7..6fea60af 100644
--- a/reddit-clone/reddit/subreddits/types.py
+++ b/reddit-clone/reddit/subreddits/types.py
@@ -52,7 +52,9 @@ class SubredditType(NodeType):
)
@classmethod
- async def get_node(cls, info: Info, subreddit_id: str) -> Optional[SubredditType]:
+ async def resolve_node(
+ cls, info: Info, subreddit_id: str
+ ) -> Optional[SubredditType]:
"""
Gets a subreddit with the given ID.
"""
diff --git a/reddit-clone/reddit/users/types.py b/reddit-clone/reddit/users/types.py
index 2a76fef3..bf41b74b 100644
--- a/reddit-clone/reddit/users/types.py
+++ b/reddit-clone/reddit/users/types.py
@@ -47,7 +47,7 @@ class UserType(NodeType):
)
@classmethod
- async def get_node(cls, info: Info, user_id: str) -> Optional[UserType]:
+ async def resolve_node(cls, info: Info, user_id: str) -> Optional[UserType]:
"""
Gets an user with the given ID.
"""
From 7be5c59ba98819880a61d1a6fe1d01c48d246b03 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Wed, 6 Oct 2021 15:52:37 +0530
Subject: [PATCH 045/150] add field: node
---
reddit-clone/reddit/base/queries.py | 13 +++++++++++++
reddit-clone/reddit/base/types.py | 15 ++++++++++-----
reddit-clone/reddit/schema.py | 12 ++++++------
3 files changed, 29 insertions(+), 11 deletions(-)
create mode 100644 reddit-clone/reddit/base/queries.py
diff --git a/reddit-clone/reddit/base/queries.py b/reddit-clone/reddit/base/queries.py
new file mode 100644
index 00000000..89de49c0
--- /dev/null
+++ b/reddit-clone/reddit/base/queries.py
@@ -0,0 +1,13 @@
+from typing import Optional
+
+from strawberry import field, type, ID
+from strawberry.types import Info
+
+from reddit.base.types import NodeType
+
+
+@type
+class BaseQueries:
+ @field(name="node", description="Fetches an object given its ID.")
+ def resolve_node(self, info: Info, id: ID) -> Optional[NodeType]:
+ return NodeType.get_node_from_global_id(info=info, global_id=id)
diff --git a/reddit-clone/reddit/base/types.py b/reddit-clone/reddit/base/types.py
index 941ac2de..10b28034 100644
--- a/reddit-clone/reddit/base/types.py
+++ b/reddit-clone/reddit/base/types.py
@@ -1,9 +1,13 @@
-from strawberry import type, field, ID
+from __future__ import annotations
+
+from typing import Optional, Type
+
+from strawberry import interface, field, ID
from strawberry.types import Info
from graphql_relay import from_global_id, to_global_id
-@type(name="Node", description="An object with an ID.")
+@interface(name="Node", description="An object with an ID.")
class NodeType:
id: ID = field(
description="""
@@ -12,13 +16,14 @@ class NodeType:
)
@classmethod
- def get_node_from_global_id(cls, info: Info, global_id: str, only_type=None):
+ def get_node_from_global_id(
+ cls, info: Info, global_id: str, only_type=None
+ ) -> Optional[Type[NodeType]]:
try:
_type, _id = cls.from_global_id(global_id)
except Exception as e:
raise Exception(
f'Unable to parse global ID "{global_id}". '
- 'Make sure it is a base64 encoded string in the format: "TypeName:id". '
f"Exception message: {str(e)}"
)
@@ -44,5 +49,5 @@ def from_global_id(cls, global_id: str):
return from_global_id(global_id)
@classmethod
- def to_global_id(cls, schema_type: str, id: str):
+ def to_global_id(cls, schema_type: str, id: str) -> str:
return to_global_id(schema_type, id)
diff --git a/reddit-clone/reddit/schema.py b/reddit-clone/reddit/schema.py
index 539ad262..eec8017a 100644
--- a/reddit-clone/reddit/schema.py
+++ b/reddit-clone/reddit/schema.py
@@ -1,11 +1,11 @@
-from strawberry import type, field, Schema
+from strawberry import type, Schema
+
+from reddit.base.queries import BaseQueries
@type
-class Query:
- @field
- def hello_world(self) -> str:
- return "Hello world!"
+class Query(BaseQueries):
+ pass
-schema = Schema(query=Query, mutation=None)
+schema = Schema(query=Query)
From 49c7c143f4afc5b2df1ba12d0f906aeafabc6eda Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Wed, 6 Oct 2021 15:56:25 +0530
Subject: [PATCH 046/150] import strawberry as a whole module
---
reddit-clone/reddit/base/queries.py | 8 ++++----
reddit-clone/reddit/base/types.py | 6 +++---
reddit-clone/reddit/comments/types.py | 12 ++++++------
reddit-clone/reddit/posts/types.py | 20 ++++++++++----------
reddit-clone/reddit/schema.py | 6 +++---
reddit-clone/reddit/subreddits/types.py | 16 ++++++++--------
reddit-clone/reddit/users/types.py | 14 +++++++-------
7 files changed, 41 insertions(+), 41 deletions(-)
diff --git a/reddit-clone/reddit/base/queries.py b/reddit-clone/reddit/base/queries.py
index 89de49c0..fa0c9e75 100644
--- a/reddit-clone/reddit/base/queries.py
+++ b/reddit-clone/reddit/base/queries.py
@@ -1,13 +1,13 @@
from typing import Optional
-from strawberry import field, type, ID
+import strawberry
from strawberry.types import Info
from reddit.base.types import NodeType
-@type
+@strawberry.type
class BaseQueries:
- @field(name="node", description="Fetches an object given its ID.")
- def resolve_node(self, info: Info, id: ID) -> Optional[NodeType]:
+ @strawberry.field(name="node", description="Fetches an object given its ID.")
+ def resolve_node(self, info: Info, id: strawberry.ID) -> Optional[NodeType]:
return NodeType.get_node_from_global_id(info=info, global_id=id)
diff --git a/reddit-clone/reddit/base/types.py b/reddit-clone/reddit/base/types.py
index 10b28034..4eef0812 100644
--- a/reddit-clone/reddit/base/types.py
+++ b/reddit-clone/reddit/base/types.py
@@ -2,14 +2,14 @@
from typing import Optional, Type
-from strawberry import interface, field, ID
+import strawberry
from strawberry.types import Info
from graphql_relay import from_global_id, to_global_id
-@interface(name="Node", description="An object with an ID.")
+@strawberry.interface(name="Node", description="An object with an ID.")
class NodeType:
- id: ID = field(
+ id: strawberry.ID = strawberry.field(
description="""
ID of the object.
"""
diff --git a/reddit-clone/reddit/comments/types.py b/reddit-clone/reddit/comments/types.py
index 13a173dc..0f725773 100644
--- a/reddit-clone/reddit/comments/types.py
+++ b/reddit-clone/reddit/comments/types.py
@@ -2,7 +2,7 @@
from typing import List, Optional
-from strawberry import type, field
+import strawberry
from strawberry.types import Info
from sqlalchemy import select
@@ -11,27 +11,27 @@
from reddit.database import get_session
-@type(name="Comment")
+@strawberry.type(name="Comment")
class CommentType(NodeType):
- content: str = field(
+ content: str = strawberry.field(
description="""
The content of the comment.
"""
)
- votes: int = field(
+ votes: int = strawberry.field(
description="""
The votes the comment has.
"""
)
- user_id: Optional[int] = field(
+ user_id: Optional[int] = strawberry.field(
description="""
The owner ID of the comment.
"""
)
- replies: List[CommentType] = field(
+ replies: List[CommentType] = strawberry.field(
description="""
The replies for the comment.
"""
diff --git a/reddit-clone/reddit/posts/types.py b/reddit-clone/reddit/posts/types.py
index 450c3552..b3901df1 100644
--- a/reddit-clone/reddit/posts/types.py
+++ b/reddit-clone/reddit/posts/types.py
@@ -2,7 +2,7 @@
from typing import List, Optional
-from strawberry import type, field
+import strawberry
from strawberry.types import Info
from sqlalchemy import select
@@ -12,51 +12,51 @@
from reddit.database import get_session
-@type(name="Post")
+@strawberry.type(name="Post")
class PostType(NodeType):
- title: str = field(
+ title: str = strawberry.field(
description="""
The title of the post.
"""
)
- text: Optional[str] = field(
+ text: Optional[str] = strawberry.field(
description="""
The text for the post.
"""
)
- link: Optional[str] = field(
+ link: Optional[str] = strawberry.field(
description="""
The link of the post.
"""
)
- thumbnail: Optional[str] = field(
+ thumbnail: Optional[str] = strawberry.field(
description="""
The thumbnail URL of the post.
"""
)
- user_id: int = field(
+ user_id: int = strawberry.field(
description="""
The owner ID of the post.
"""
)
- subreddit_id: int = field(
+ subreddit_id: int = strawberry.field(
description="""
The subreddit ID of the post.
"""
)
- votes: int = field(
+ votes: int = strawberry.field(
description="""
The votes the post has.
"""
)
- comments: List[CommentType] = field(
+ comments: List[CommentType] = strawberry.field(
description="""
The comments for the post.
"""
diff --git a/reddit-clone/reddit/schema.py b/reddit-clone/reddit/schema.py
index eec8017a..fdd15fae 100644
--- a/reddit-clone/reddit/schema.py
+++ b/reddit-clone/reddit/schema.py
@@ -1,11 +1,11 @@
-from strawberry import type, Schema
+import strawberry
from reddit.base.queries import BaseQueries
-@type
+@strawberry.type
class Query(BaseQueries):
pass
-schema = Schema(query=Query)
+schema = strawberry.Schema(query=Query)
diff --git a/reddit-clone/reddit/subreddits/types.py b/reddit-clone/reddit/subreddits/types.py
index 6fea60af..f6c57fb3 100644
--- a/reddit-clone/reddit/subreddits/types.py
+++ b/reddit-clone/reddit/subreddits/types.py
@@ -2,7 +2,7 @@
from typing import List, Optional
-from strawberry import type, field
+import strawberry
from strawberry.types import Info
from sqlalchemy import select
@@ -12,40 +12,40 @@
from reddit.database import get_session
-@type(name="Subreddit")
+@strawberry.type(name="Subreddit")
class SubredditType(NodeType):
- name: str = field(
+ name: str = strawberry.field(
description="""
The name of the subreddit.
"""
)
- description: str = field(
+ description: str = strawberry.field(
description="""
The description of the subreddit.
"""
)
- admin_id: int = field(
+ admin_id: int = strawberry.field(
description="""
The owner ID of the subreddit.
"""
)
# TODO: make status an enum
- status: int = field(
+ status: int = strawberry.field(
description="""
The status of the subreddit.
"""
)
- icon: str = field(
+ icon: str = strawberry.field(
description="""
The icon URL of the subreddit.
"""
)
- posts: List[PostType] = field(
+ posts: List[PostType] = strawberry.field(
description="""
The posts for the subreddit.
"""
diff --git a/reddit-clone/reddit/users/types.py b/reddit-clone/reddit/users/types.py
index bf41b74b..c5973d67 100644
--- a/reddit-clone/reddit/users/types.py
+++ b/reddit-clone/reddit/users/types.py
@@ -2,7 +2,7 @@
from typing import List, Optional
-from strawberry import type, field
+import strawberry
from strawberry.types import Info
from sqlalchemy import select
@@ -14,33 +14,33 @@
from reddit.database import get_session
-@type(name="User")
+@strawberry.type(name="User")
class UserType(NodeType):
- username: str = field(
+ username: str = strawberry.field(
description="""
The username of the user.
"""
)
- avatar: str = field(
+ avatar: str = strawberry.field(
description="""
The avatar URL of the user.
"""
)
- posts: List[PostType] = field(
+ posts: List[PostType] = strawberry.field(
description="""
The posts for the user.
"""
)
- subreddits: List[SubredditType] = field(
+ subreddits: List[SubredditType] = strawberry.field(
description="""
The subreddits the user is in.
"""
)
- comments: List[CommentType] = field(
+ comments: List[CommentType] = strawberry.field(
description="""
The comments for the user.
"""
From f9c4df030cb8cef143eb398654b715443cd04a1e Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Wed, 6 Oct 2021 16:01:10 +0530
Subject: [PATCH 047/150] update interface checking
---
reddit-clone/reddit/base/types.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/reddit-clone/reddit/base/types.py b/reddit-clone/reddit/base/types.py
index 4eef0812..46902997 100644
--- a/reddit-clone/reddit/base/types.py
+++ b/reddit-clone/reddit/base/types.py
@@ -35,7 +35,7 @@ def get_node_from_global_id(
assert schema_type == only_type
# We make sure the ObjectType implements the "Node" interface
- if cls not in schema_type.interfaces:
+ if cls not in schema_type._type_definition.interfaces:
raise Exception(
f'ObjectType "{_type}" does not implement the "{cls}" interface.'
)
From 5379890b7275dabc602e4889f4ac03a6b191d709 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Sat, 9 Oct 2021 16:13:59 +0530
Subject: [PATCH 048/150] update typehints
---
reddit-clone/reddit/database.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/reddit-clone/reddit/database.py b/reddit-clone/reddit/database.py
index aa2cc340..03c74a2e 100644
--- a/reddit-clone/reddit/database.py
+++ b/reddit-clone/reddit/database.py
@@ -1,5 +1,5 @@
from contextlib import asynccontextmanager
-from typing import AsyncIterator
+from typing import AsyncGenerator
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
@@ -13,7 +13,7 @@
@asynccontextmanager
-async def get_session() -> AsyncIterator[AsyncSession]:
+async def get_session() -> AsyncGenerator[AsyncSession, None]:
"""
Gets a session instance.
From 63e706444f773bf2c55f7cc6bc6e51f18cc692b0 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Sat, 9 Oct 2021 16:54:02 +0530
Subject: [PATCH 049/150] add user mutations and queries
---
reddit-clone/reddit/base/queries.py | 2 +-
reddit-clone/reddit/schema.py | 13 +++++--
reddit-clone/reddit/users/mutations.py | 9 +++++
.../reddit/users/mutations/__init__.py | 25 +++++++++++++
.../reddit/users/mutations/authenticate.py | 30 ++++++++++++++++
.../reddit/users/mutations/avatar_remove.py | 24 +++++++++++++
.../reddit/users/mutations/email_change.py | 30 ++++++++++++++++
.../users/mutations/email_change_request.py | 35 +++++++++++++++++++
.../reddit/users/mutations/password_reset.py | 31 ++++++++++++++++
.../users/mutations/password_reset_request.py | 32 +++++++++++++++++
.../reddit/users/mutations/user_create.py | 31 ++++++++++++++++
.../reddit/users/mutations/user_deactivate.py | 31 ++++++++++++++++
.../reddit/users/mutations/user_update.py | 31 ++++++++++++++++
reddit-clone/reddit/users/queries.py | 24 +++++++++++++
14 files changed, 344 insertions(+), 4 deletions(-)
create mode 100644 reddit-clone/reddit/users/mutations.py
create mode 100644 reddit-clone/reddit/users/mutations/__init__.py
create mode 100644 reddit-clone/reddit/users/mutations/authenticate.py
create mode 100644 reddit-clone/reddit/users/mutations/avatar_remove.py
create mode 100644 reddit-clone/reddit/users/mutations/email_change.py
create mode 100644 reddit-clone/reddit/users/mutations/email_change_request.py
create mode 100644 reddit-clone/reddit/users/mutations/password_reset.py
create mode 100644 reddit-clone/reddit/users/mutations/password_reset_request.py
create mode 100644 reddit-clone/reddit/users/mutations/user_create.py
create mode 100644 reddit-clone/reddit/users/mutations/user_deactivate.py
create mode 100644 reddit-clone/reddit/users/mutations/user_update.py
create mode 100644 reddit-clone/reddit/users/queries.py
diff --git a/reddit-clone/reddit/base/queries.py b/reddit-clone/reddit/base/queries.py
index fa0c9e75..c209cd9e 100644
--- a/reddit-clone/reddit/base/queries.py
+++ b/reddit-clone/reddit/base/queries.py
@@ -7,7 +7,7 @@
@strawberry.type
-class BaseQueries:
+class BaseQuery:
@strawberry.field(name="node", description="Fetches an object given its ID.")
def resolve_node(self, info: Info, id: strawberry.ID) -> Optional[NodeType]:
return NodeType.get_node_from_global_id(info=info, global_id=id)
diff --git a/reddit-clone/reddit/schema.py b/reddit-clone/reddit/schema.py
index fdd15fae..48d4e8ae 100644
--- a/reddit-clone/reddit/schema.py
+++ b/reddit-clone/reddit/schema.py
@@ -1,11 +1,18 @@
import strawberry
-from reddit.base.queries import BaseQueries
+from reddit.base.queries import BaseQuery
+from reddit.users.queries import UserQuery
+from reddit.users.mutations import UserMutation
@strawberry.type
-class Query(BaseQueries):
+class Query(BaseQuery, UserQuery):
pass
-schema = strawberry.Schema(query=Query)
+@strawberry.type
+class Mutation(UserMutation):
+ pass
+
+
+schema = strawberry.Schema(query=Query, mutation=Mutation)
diff --git a/reddit-clone/reddit/users/mutations.py b/reddit-clone/reddit/users/mutations.py
new file mode 100644
index 00000000..c5e7232c
--- /dev/null
+++ b/reddit-clone/reddit/users/mutations.py
@@ -0,0 +1,9 @@
+import strawberry
+from strawberry.types import Info
+
+
+@strawberry.type
+class UserMutations:
+ @strawberry.mutation(description="Logs the current user out.")
+ async def unauthenticate(self, info: Info, *args):
+ pass
diff --git a/reddit-clone/reddit/users/mutations/__init__.py b/reddit-clone/reddit/users/mutations/__init__.py
new file mode 100644
index 00000000..8af771f4
--- /dev/null
+++ b/reddit-clone/reddit/users/mutations/__init__.py
@@ -0,0 +1,25 @@
+from strawberry.tools import create_type
+
+from .authenticate import authenticate
+from .avatar_remove import avatar_remove
+from .email_change_request import email_change_request
+from .email_change import email_change
+from .password_reset_request import password_reset_request
+from .password_reset import password_reset
+from .user_create import user_create
+from .user_deactivate import user_deactivate
+from .user_update import user_update
+
+UserMutation = create_type(
+ fields=(
+ authenticate,
+ avatar_remove,
+ email_change_request,
+ email_change,
+ password_reset_request,
+ password_reset,
+ user_create,
+ user_deactivate,
+ user_update,
+ )
+)
diff --git a/reddit-clone/reddit/users/mutations/authenticate.py b/reddit-clone/reddit/users/mutations/authenticate.py
new file mode 100644
index 00000000..5d1f03fe
--- /dev/null
+++ b/reddit-clone/reddit/users/mutations/authenticate.py
@@ -0,0 +1,30 @@
+import strawberry
+from strawberry.types import Info
+
+from reddit.users.types import UserType
+
+
+@strawberry.type
+class AuthenticateInput:
+ username: str
+ password: str
+
+
+@strawberry.type
+class AuthenticateSuccess:
+ user: UserType
+
+
+@strawberry.type
+class AuthenticateError:
+ error: str
+
+
+AuthenticateResult = strawberry.union(
+ name="AuthenticateResult", types=(AuthenticateSuccess, AuthenticateError)
+)
+
+
+@strawberry.mutation(description="Logs the current user in.")
+async def authenticate(info: Info, input: AuthenticateInput) -> AuthenticateResult:
+ pass
diff --git a/reddit-clone/reddit/users/mutations/avatar_remove.py b/reddit-clone/reddit/users/mutations/avatar_remove.py
new file mode 100644
index 00000000..cfcf9a50
--- /dev/null
+++ b/reddit-clone/reddit/users/mutations/avatar_remove.py
@@ -0,0 +1,24 @@
+import strawberry
+from strawberry.types import Info
+
+from reddit.users.types import UserType
+
+
+@strawberry.type
+class AvatarRemoveSuccess:
+ user: UserType
+
+
+@strawberry.type
+class AvatarRemoveError:
+ error: str
+
+
+AvatarRemoveResult = strawberry.union(
+ name="AvatarRemoveResult", types=(AvatarRemoveSuccess, AvatarRemoveError)
+)
+
+
+@strawberry.mutation(description="Removes the current user's avatar.")
+async def avatar_remove(info: Info) -> AvatarRemoveResult:
+ pass
diff --git a/reddit-clone/reddit/users/mutations/email_change.py b/reddit-clone/reddit/users/mutations/email_change.py
new file mode 100644
index 00000000..f8b8bd50
--- /dev/null
+++ b/reddit-clone/reddit/users/mutations/email_change.py
@@ -0,0 +1,30 @@
+import strawberry
+from strawberry.types import Info
+
+from reddit.users.types import UserType
+
+
+@strawberry.type
+class EmailChangeInput:
+ email: str
+ change_code: str
+
+
+@strawberry.type
+class EmailChangeSuccess:
+ user: UserType
+
+
+@strawberry.type
+class EmailChangeError:
+ error: str
+
+
+EmailChangeResult = strawberry.union(
+ name="EmailChangeResult", types=(EmailChangeSuccess, EmailChangeError)
+)
+
+
+@strawberry.mutation(description="Changes the email for associated user.")
+async def email_change(info: Info, input: EmailChangeInput) -> EmailChangeResult:
+ pass
diff --git a/reddit-clone/reddit/users/mutations/email_change_request.py b/reddit-clone/reddit/users/mutations/email_change_request.py
new file mode 100644
index 00000000..9d0dc9d9
--- /dev/null
+++ b/reddit-clone/reddit/users/mutations/email_change_request.py
@@ -0,0 +1,35 @@
+import strawberry
+from strawberry.types import Info
+
+from reddit.users.types import UserType
+
+
+@strawberry.type
+class EmailChangeRequestInput:
+ email: str
+ password: str
+
+
+@strawberry.type
+class EmailChangeRequestSuccess:
+ user: UserType
+
+
+@strawberry.type
+class EmailChangeRequestError:
+ error: str
+
+
+EmailChangeRequestResult = strawberry.union(
+ name="EmailChangeRequestResult",
+ types=(EmailChangeRequestSuccess, EmailChangeRequestError),
+)
+
+
+@strawberry.mutation(
+ description="Sends an email change code to the given email address."
+)
+async def email_change_request(
+ info: Info, input: EmailChangeRequestInput
+) -> EmailChangeRequestResult:
+ pass
diff --git a/reddit-clone/reddit/users/mutations/password_reset.py b/reddit-clone/reddit/users/mutations/password_reset.py
new file mode 100644
index 00000000..6637e4ca
--- /dev/null
+++ b/reddit-clone/reddit/users/mutations/password_reset.py
@@ -0,0 +1,31 @@
+import strawberry
+from strawberry.types import Info
+
+from reddit.users.types import UserType
+
+
+@strawberry.type
+class PasswordResetInput:
+ password: str
+ reset_code: str
+ email: str
+
+
+@strawberry.type
+class PasswordResetSuccess:
+ user: UserType
+
+
+@strawberry.type
+class PasswordResetError:
+ error: str
+
+
+PasswordResetResult = strawberry.union(
+ name="PasswordResetResult", types=(PasswordResetSuccess, PasswordResetError)
+)
+
+
+@strawberry.mutation(description="Resets the password for the user account.")
+async def password_reset(info: Info, input: PasswordResetInput) -> PasswordResetResult:
+ pass
diff --git a/reddit-clone/reddit/users/mutations/password_reset_request.py b/reddit-clone/reddit/users/mutations/password_reset_request.py
new file mode 100644
index 00000000..1b239abf
--- /dev/null
+++ b/reddit-clone/reddit/users/mutations/password_reset_request.py
@@ -0,0 +1,32 @@
+import strawberry
+from strawberry.types import Info
+
+from reddit.users.types import UserType
+
+
+@strawberry.type
+class PasswordResetRequestInput:
+ email: str
+
+
+@strawberry.type
+class PasswordResetRequestSuccess:
+ user: UserType
+
+
+@strawberry.type
+class PasswordResetRequestError:
+ error: str
+
+
+PasswordResetRequestResult = strawberry.union(
+ name="PasswordResetRequestResult",
+ types=(PasswordResetRequestSuccess, PasswordResetRequestError),
+)
+
+
+@strawberry.mutation(description="Sends a password reset code to the given email.")
+async def password_reset_request(
+ info: Info, input: PasswordResetRequestInput
+) -> PasswordResetRequestResult:
+ pass
diff --git a/reddit-clone/reddit/users/mutations/user_create.py b/reddit-clone/reddit/users/mutations/user_create.py
new file mode 100644
index 00000000..0c105bf7
--- /dev/null
+++ b/reddit-clone/reddit/users/mutations/user_create.py
@@ -0,0 +1,31 @@
+import strawberry
+from strawberry.types import Info
+
+from reddit.users.types import UserType
+
+
+@strawberry.type
+class UserCreateInput:
+ email: str
+ username: str
+ password: str
+
+
+@strawberry.type
+class UserCreateSuccess:
+ user: UserType
+
+
+@strawberry.type
+class UserCreateError:
+ error: str
+
+
+UserCreateResult = strawberry.union(
+ name="UserCreateResult", types=(UserCreateSuccess, UserCreateError)
+)
+
+
+@strawberry.mutation(description="Creates a new user.")
+async def user_create(info: Info, input: UserCreateInput) -> UserCreateResult:
+ pass
diff --git a/reddit-clone/reddit/users/mutations/user_deactivate.py b/reddit-clone/reddit/users/mutations/user_deactivate.py
new file mode 100644
index 00000000..3ca674db
--- /dev/null
+++ b/reddit-clone/reddit/users/mutations/user_deactivate.py
@@ -0,0 +1,31 @@
+import strawberry
+from strawberry.types import Info
+
+from reddit.users.types import UserType
+
+
+@strawberry.type
+class UserDeactivateInput:
+ password: str
+
+
+@strawberry.type
+class UserDeactivateSuccess:
+ user: UserType
+
+
+@strawberry.type
+class UserDeactivateError:
+ error: str
+
+
+UserDeactivateResult = strawberry.union(
+ name="UserDeactivateResult", types=(UserDeactivateSuccess, UserDeactivateError)
+)
+
+
+@strawberry.mutation(description="Deactivates the current user.")
+async def user_deactivate(
+ info: Info, input: UserDeactivateInput
+) -> UserDeactivateResult:
+ pass
diff --git a/reddit-clone/reddit/users/mutations/user_update.py b/reddit-clone/reddit/users/mutations/user_update.py
new file mode 100644
index 00000000..8c7cac4d
--- /dev/null
+++ b/reddit-clone/reddit/users/mutations/user_update.py
@@ -0,0 +1,31 @@
+import strawberry
+from strawberry.types import Info
+
+from reddit.users.types import UserType
+
+
+@strawberry.type
+class UserUpdateInput:
+ email: str
+ username: str
+ password: str
+
+
+@strawberry.type
+class UserUpdateSuccess:
+ user: UserType
+
+
+@strawberry.type
+class UserUpdateError:
+ error: str
+
+
+UserUpdateResult = strawberry.union(
+ name="UserUpdateResult", types=(UserUpdateSuccess, UserUpdateError)
+)
+
+
+@strawberry.mutation(description="Updates the current user.")
+async def user_update(info: Info, input: UserUpdateInput) -> UserUpdateResult:
+ pass
diff --git a/reddit-clone/reddit/users/queries.py b/reddit-clone/reddit/users/queries.py
new file mode 100644
index 00000000..9ef32119
--- /dev/null
+++ b/reddit-clone/reddit/users/queries.py
@@ -0,0 +1,24 @@
+from typing import Optional
+
+import strawberry
+from strawberry.types import Info
+from sqlalchemy import select
+
+from reddit.database import get_session
+from reddit.users.types import UserType
+from reddit.users.models import User
+
+
+@strawberry.type
+class UserQuery:
+ @strawberry.field(description="Gets an user by username.")
+ async def user(self, info: Info, username: str) -> Optional[UserType]:
+ query = select(User).filter_by(username=username).first()
+ async with get_session() as session:
+ user = await session.execute(query)
+ if user is not None:
+ return UserType.from_instance(user)
+
+ @strawberry.field(description="Gets the current user.")
+ async def current_user(self, info: Info) -> UserType:
+ pass
From ee6bbcf9d50cad6c5637bcc2e09d85d5534735e9 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Sat, 9 Oct 2021 17:07:29 +0530
Subject: [PATCH 050/150] replace strawberry.union with typing.Union
---
reddit-clone/reddit/users/mutations/authenticate.py | 6 +++---
reddit-clone/reddit/users/mutations/avatar_remove.py | 6 +++---
reddit-clone/reddit/users/mutations/email_change.py | 6 +++---
.../reddit/users/mutations/email_change_request.py | 7 +++----
reddit-clone/reddit/users/mutations/password_reset.py | 6 +++---
.../reddit/users/mutations/password_reset_request.py | 9 +++++----
reddit-clone/reddit/users/mutations/user_create.py | 6 +++---
reddit-clone/reddit/users/mutations/user_deactivate.py | 6 +++---
reddit-clone/reddit/users/mutations/user_update.py | 6 +++---
9 files changed, 29 insertions(+), 29 deletions(-)
diff --git a/reddit-clone/reddit/users/mutations/authenticate.py b/reddit-clone/reddit/users/mutations/authenticate.py
index 5d1f03fe..4ba38704 100644
--- a/reddit-clone/reddit/users/mutations/authenticate.py
+++ b/reddit-clone/reddit/users/mutations/authenticate.py
@@ -1,3 +1,5 @@
+from typing import Union
+
import strawberry
from strawberry.types import Info
@@ -20,9 +22,7 @@ class AuthenticateError:
error: str
-AuthenticateResult = strawberry.union(
- name="AuthenticateResult", types=(AuthenticateSuccess, AuthenticateError)
-)
+AuthenticateResult = Union[AuthenticateSuccess, AuthenticateError]
@strawberry.mutation(description="Logs the current user in.")
diff --git a/reddit-clone/reddit/users/mutations/avatar_remove.py b/reddit-clone/reddit/users/mutations/avatar_remove.py
index cfcf9a50..e99272ae 100644
--- a/reddit-clone/reddit/users/mutations/avatar_remove.py
+++ b/reddit-clone/reddit/users/mutations/avatar_remove.py
@@ -1,3 +1,5 @@
+from typing import Union
+
import strawberry
from strawberry.types import Info
@@ -14,9 +16,7 @@ class AvatarRemoveError:
error: str
-AvatarRemoveResult = strawberry.union(
- name="AvatarRemoveResult", types=(AvatarRemoveSuccess, AvatarRemoveError)
-)
+AvatarRemoveResult = Union[AvatarRemoveSuccess, AvatarRemoveError]
@strawberry.mutation(description="Removes the current user's avatar.")
diff --git a/reddit-clone/reddit/users/mutations/email_change.py b/reddit-clone/reddit/users/mutations/email_change.py
index f8b8bd50..d1c955c3 100644
--- a/reddit-clone/reddit/users/mutations/email_change.py
+++ b/reddit-clone/reddit/users/mutations/email_change.py
@@ -1,3 +1,5 @@
+from typing import Union
+
import strawberry
from strawberry.types import Info
@@ -20,9 +22,7 @@ class EmailChangeError:
error: str
-EmailChangeResult = strawberry.union(
- name="EmailChangeResult", types=(EmailChangeSuccess, EmailChangeError)
-)
+EmailChangeResult = Union[EmailChangeSuccess, EmailChangeError]
@strawberry.mutation(description="Changes the email for associated user.")
diff --git a/reddit-clone/reddit/users/mutations/email_change_request.py b/reddit-clone/reddit/users/mutations/email_change_request.py
index 9d0dc9d9..d310a8be 100644
--- a/reddit-clone/reddit/users/mutations/email_change_request.py
+++ b/reddit-clone/reddit/users/mutations/email_change_request.py
@@ -1,3 +1,5 @@
+from typing import Union
+
import strawberry
from strawberry.types import Info
@@ -20,10 +22,7 @@ class EmailChangeRequestError:
error: str
-EmailChangeRequestResult = strawberry.union(
- name="EmailChangeRequestResult",
- types=(EmailChangeRequestSuccess, EmailChangeRequestError),
-)
+EmailChangeRequestResult = Union[EmailChangeRequestSuccess, EmailChangeRequestError]
@strawberry.mutation(
diff --git a/reddit-clone/reddit/users/mutations/password_reset.py b/reddit-clone/reddit/users/mutations/password_reset.py
index 6637e4ca..012a62d8 100644
--- a/reddit-clone/reddit/users/mutations/password_reset.py
+++ b/reddit-clone/reddit/users/mutations/password_reset.py
@@ -1,3 +1,5 @@
+from typing import Union
+
import strawberry
from strawberry.types import Info
@@ -21,9 +23,7 @@ class PasswordResetError:
error: str
-PasswordResetResult = strawberry.union(
- name="PasswordResetResult", types=(PasswordResetSuccess, PasswordResetError)
-)
+PasswordResetResult = Union[PasswordResetSuccess, PasswordResetError]
@strawberry.mutation(description="Resets the password for the user account.")
diff --git a/reddit-clone/reddit/users/mutations/password_reset_request.py b/reddit-clone/reddit/users/mutations/password_reset_request.py
index 1b239abf..dd70821c 100644
--- a/reddit-clone/reddit/users/mutations/password_reset_request.py
+++ b/reddit-clone/reddit/users/mutations/password_reset_request.py
@@ -1,3 +1,5 @@
+from typing import Union
+
import strawberry
from strawberry.types import Info
@@ -19,10 +21,9 @@ class PasswordResetRequestError:
error: str
-PasswordResetRequestResult = strawberry.union(
- name="PasswordResetRequestResult",
- types=(PasswordResetRequestSuccess, PasswordResetRequestError),
-)
+PasswordResetRequestResult = Union[
+ PasswordResetRequestSuccess, PasswordResetRequestError
+]
@strawberry.mutation(description="Sends a password reset code to the given email.")
diff --git a/reddit-clone/reddit/users/mutations/user_create.py b/reddit-clone/reddit/users/mutations/user_create.py
index 0c105bf7..2c7c2f5a 100644
--- a/reddit-clone/reddit/users/mutations/user_create.py
+++ b/reddit-clone/reddit/users/mutations/user_create.py
@@ -1,3 +1,5 @@
+from typing import Union
+
import strawberry
from strawberry.types import Info
@@ -21,9 +23,7 @@ class UserCreateError:
error: str
-UserCreateResult = strawberry.union(
- name="UserCreateResult", types=(UserCreateSuccess, UserCreateError)
-)
+UserCreateResult = Union[UserCreateSuccess, UserCreateError]
@strawberry.mutation(description="Creates a new user.")
diff --git a/reddit-clone/reddit/users/mutations/user_deactivate.py b/reddit-clone/reddit/users/mutations/user_deactivate.py
index 3ca674db..d675193b 100644
--- a/reddit-clone/reddit/users/mutations/user_deactivate.py
+++ b/reddit-clone/reddit/users/mutations/user_deactivate.py
@@ -1,3 +1,5 @@
+from typing import Union
+
import strawberry
from strawberry.types import Info
@@ -19,9 +21,7 @@ class UserDeactivateError:
error: str
-UserDeactivateResult = strawberry.union(
- name="UserDeactivateResult", types=(UserDeactivateSuccess, UserDeactivateError)
-)
+UserDeactivateResult = Union[UserDeactivateSuccess, UserDeactivateError]
@strawberry.mutation(description="Deactivates the current user.")
diff --git a/reddit-clone/reddit/users/mutations/user_update.py b/reddit-clone/reddit/users/mutations/user_update.py
index 8c7cac4d..81995ff7 100644
--- a/reddit-clone/reddit/users/mutations/user_update.py
+++ b/reddit-clone/reddit/users/mutations/user_update.py
@@ -1,3 +1,5 @@
+from typing import Union
+
import strawberry
from strawberry.types import Info
@@ -21,9 +23,7 @@ class UserUpdateError:
error: str
-UserUpdateResult = strawberry.union(
- name="UserUpdateResult", types=(UserUpdateSuccess, UserUpdateError)
-)
+UserUpdateResult = Union[UserUpdateSuccess, UserUpdateError]
@strawberry.mutation(description="Updates the current user.")
From d0ba9e02ad3e8f60d57269e842dbf099f3b97f4c Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Sat, 9 Oct 2021 17:14:28 +0530
Subject: [PATCH 051/150] delete mutations.py
---
reddit-clone/reddit/users/mutations.py | 9 ---------
1 file changed, 9 deletions(-)
delete mode 100644 reddit-clone/reddit/users/mutations.py
diff --git a/reddit-clone/reddit/users/mutations.py b/reddit-clone/reddit/users/mutations.py
deleted file mode 100644
index c5e7232c..00000000
--- a/reddit-clone/reddit/users/mutations.py
+++ /dev/null
@@ -1,9 +0,0 @@
-import strawberry
-from strawberry.types import Info
-
-
-@strawberry.type
-class UserMutations:
- @strawberry.mutation(description="Logs the current user out.")
- async def unauthenticate(self, info: Info, *args):
- pass
From c09a2ad03d11e2b2ae3508c0efc4e602e42f92e5 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Sat, 9 Oct 2021 17:16:29 +0530
Subject: [PATCH 052/150] mark input types correctly
---
reddit-clone/reddit/users/mutations/authenticate.py | 2 +-
reddit-clone/reddit/users/mutations/email_change.py | 2 +-
reddit-clone/reddit/users/mutations/email_change_request.py | 2 +-
reddit-clone/reddit/users/mutations/password_reset.py | 2 +-
.../reddit/users/mutations/password_reset_request.py | 2 +-
reddit-clone/reddit/users/mutations/user_create.py | 2 +-
reddit-clone/reddit/users/mutations/user_deactivate.py | 2 +-
reddit-clone/reddit/users/mutations/user_update.py | 6 +++---
8 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/reddit-clone/reddit/users/mutations/authenticate.py b/reddit-clone/reddit/users/mutations/authenticate.py
index 4ba38704..dd6c7462 100644
--- a/reddit-clone/reddit/users/mutations/authenticate.py
+++ b/reddit-clone/reddit/users/mutations/authenticate.py
@@ -6,7 +6,7 @@
from reddit.users.types import UserType
-@strawberry.type
+@strawberry.input
class AuthenticateInput:
username: str
password: str
diff --git a/reddit-clone/reddit/users/mutations/email_change.py b/reddit-clone/reddit/users/mutations/email_change.py
index d1c955c3..19595e75 100644
--- a/reddit-clone/reddit/users/mutations/email_change.py
+++ b/reddit-clone/reddit/users/mutations/email_change.py
@@ -6,7 +6,7 @@
from reddit.users.types import UserType
-@strawberry.type
+@strawberry.input
class EmailChangeInput:
email: str
change_code: str
diff --git a/reddit-clone/reddit/users/mutations/email_change_request.py b/reddit-clone/reddit/users/mutations/email_change_request.py
index d310a8be..545e61a4 100644
--- a/reddit-clone/reddit/users/mutations/email_change_request.py
+++ b/reddit-clone/reddit/users/mutations/email_change_request.py
@@ -6,7 +6,7 @@
from reddit.users.types import UserType
-@strawberry.type
+@strawberry.input
class EmailChangeRequestInput:
email: str
password: str
diff --git a/reddit-clone/reddit/users/mutations/password_reset.py b/reddit-clone/reddit/users/mutations/password_reset.py
index 012a62d8..46ec8bce 100644
--- a/reddit-clone/reddit/users/mutations/password_reset.py
+++ b/reddit-clone/reddit/users/mutations/password_reset.py
@@ -6,7 +6,7 @@
from reddit.users.types import UserType
-@strawberry.type
+@strawberry.input
class PasswordResetInput:
password: str
reset_code: str
diff --git a/reddit-clone/reddit/users/mutations/password_reset_request.py b/reddit-clone/reddit/users/mutations/password_reset_request.py
index dd70821c..06fd88c5 100644
--- a/reddit-clone/reddit/users/mutations/password_reset_request.py
+++ b/reddit-clone/reddit/users/mutations/password_reset_request.py
@@ -6,7 +6,7 @@
from reddit.users.types import UserType
-@strawberry.type
+@strawberry.input
class PasswordResetRequestInput:
email: str
diff --git a/reddit-clone/reddit/users/mutations/user_create.py b/reddit-clone/reddit/users/mutations/user_create.py
index 2c7c2f5a..6f016c5b 100644
--- a/reddit-clone/reddit/users/mutations/user_create.py
+++ b/reddit-clone/reddit/users/mutations/user_create.py
@@ -6,7 +6,7 @@
from reddit.users.types import UserType
-@strawberry.type
+@strawberry.input
class UserCreateInput:
email: str
username: str
diff --git a/reddit-clone/reddit/users/mutations/user_deactivate.py b/reddit-clone/reddit/users/mutations/user_deactivate.py
index d675193b..8cbb0810 100644
--- a/reddit-clone/reddit/users/mutations/user_deactivate.py
+++ b/reddit-clone/reddit/users/mutations/user_deactivate.py
@@ -6,7 +6,7 @@
from reddit.users.types import UserType
-@strawberry.type
+@strawberry.input
class UserDeactivateInput:
password: str
diff --git a/reddit-clone/reddit/users/mutations/user_update.py b/reddit-clone/reddit/users/mutations/user_update.py
index 81995ff7..d3b3536a 100644
--- a/reddit-clone/reddit/users/mutations/user_update.py
+++ b/reddit-clone/reddit/users/mutations/user_update.py
@@ -1,16 +1,16 @@
from typing import Union
import strawberry
+from strawberry.file_uploads import Upload
from strawberry.types import Info
from reddit.users.types import UserType
-@strawberry.type
+@strawberry.input
class UserUpdateInput:
- email: str
username: str
- password: str
+ avatar: Upload
@strawberry.type
From 005353f1d3a16edeb2e34313684c43562680383f Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Sat, 9 Oct 2021 17:36:02 +0530
Subject: [PATCH 053/150] remove decorator-based field definitions
---
.../reddit/users/mutations/__init__.py | 3 +-
.../reddit/users/mutations/authenticate.py | 16 ++++++-
.../reddit/users/mutations/avatar_remove.py | 13 +++++-
.../reddit/users/mutations/email_change.py | 15 ++++++-
.../users/mutations/email_change_request.py | 17 +++++--
.../reddit/users/mutations/password_reset.py | 15 ++++++-
.../users/mutations/password_reset_request.py | 13 +++++-
.../reddit/users/mutations/user_create.py | 13 +++++-
.../reddit/users/mutations/user_deactivate.py | 13 +++++-
.../reddit/users/mutations/user_update.py | 13 +++++-
reddit-clone/reddit/users/queries.py | 45 +++++++++++--------
11 files changed, 137 insertions(+), 39 deletions(-)
diff --git a/reddit-clone/reddit/users/mutations/__init__.py b/reddit-clone/reddit/users/mutations/__init__.py
index 8af771f4..91432cc5 100644
--- a/reddit-clone/reddit/users/mutations/__init__.py
+++ b/reddit-clone/reddit/users/mutations/__init__.py
@@ -11,6 +11,7 @@
from .user_update import user_update
UserMutation = create_type(
+ name="UserMutation",
fields=(
authenticate,
avatar_remove,
@@ -21,5 +22,5 @@
user_create,
user_deactivate,
user_update,
- )
+ ),
)
diff --git a/reddit-clone/reddit/users/mutations/authenticate.py b/reddit-clone/reddit/users/mutations/authenticate.py
index dd6c7462..8983d5b8 100644
--- a/reddit-clone/reddit/users/mutations/authenticate.py
+++ b/reddit-clone/reddit/users/mutations/authenticate.py
@@ -6,6 +6,9 @@
from reddit.users.types import UserType
+__all__ = ("authenticate",)
+
+
@strawberry.input
class AuthenticateInput:
username: str
@@ -25,6 +28,15 @@ class AuthenticateError:
AuthenticateResult = Union[AuthenticateSuccess, AuthenticateError]
-@strawberry.mutation(description="Logs the current user in.")
-async def authenticate(info: Info, input: AuthenticateInput) -> AuthenticateResult:
+async def resolve_authenticate(
+ info: Info, input: AuthenticateInput
+) -> AuthenticateResult:
pass
+
+
+authenticate = strawberry.mutation(
+ resolver=resolve_authenticate,
+ description="""
+ Logs the current user in.
+ """,
+)
diff --git a/reddit-clone/reddit/users/mutations/avatar_remove.py b/reddit-clone/reddit/users/mutations/avatar_remove.py
index e99272ae..9505484f 100644
--- a/reddit-clone/reddit/users/mutations/avatar_remove.py
+++ b/reddit-clone/reddit/users/mutations/avatar_remove.py
@@ -5,6 +5,8 @@
from reddit.users.types import UserType
+__all__ = ("avatar_remove",)
+
@strawberry.type
class AvatarRemoveSuccess:
@@ -19,6 +21,13 @@ class AvatarRemoveError:
AvatarRemoveResult = Union[AvatarRemoveSuccess, AvatarRemoveError]
-@strawberry.mutation(description="Removes the current user's avatar.")
-async def avatar_remove(info: Info) -> AvatarRemoveResult:
+async def resolve_avatar_remove(info: Info) -> AvatarRemoveResult:
pass
+
+
+avatar_remove = strawberry.mutation(
+ resolver=resolve_avatar_remove,
+ description="""
+ Removes the current user's avatar.
+ """,
+)
diff --git a/reddit-clone/reddit/users/mutations/email_change.py b/reddit-clone/reddit/users/mutations/email_change.py
index 19595e75..0e98bc45 100644
--- a/reddit-clone/reddit/users/mutations/email_change.py
+++ b/reddit-clone/reddit/users/mutations/email_change.py
@@ -5,6 +5,8 @@
from reddit.users.types import UserType
+__all__ = ("email_change",)
+
@strawberry.input
class EmailChangeInput:
@@ -25,6 +27,15 @@ class EmailChangeError:
EmailChangeResult = Union[EmailChangeSuccess, EmailChangeError]
-@strawberry.mutation(description="Changes the email for associated user.")
-async def email_change(info: Info, input: EmailChangeInput) -> EmailChangeResult:
+async def resolve_email_change(
+ info: Info, input: EmailChangeInput
+) -> EmailChangeResult:
pass
+
+
+email_change = strawberry.mutation(
+ resolver=resolve_email_change,
+ description="""
+ Changes the email for associated user.
+ """,
+)
diff --git a/reddit-clone/reddit/users/mutations/email_change_request.py b/reddit-clone/reddit/users/mutations/email_change_request.py
index 545e61a4..b16f3a60 100644
--- a/reddit-clone/reddit/users/mutations/email_change_request.py
+++ b/reddit-clone/reddit/users/mutations/email_change_request.py
@@ -6,6 +6,9 @@
from reddit.users.types import UserType
+__all__ = ("email_change_request",)
+
+
@strawberry.input
class EmailChangeRequestInput:
email: str
@@ -25,10 +28,16 @@ class EmailChangeRequestError:
EmailChangeRequestResult = Union[EmailChangeRequestSuccess, EmailChangeRequestError]
-@strawberry.mutation(
- description="Sends an email change code to the given email address."
-)
-async def email_change_request(
+async def resolve_email_change_request(
info: Info, input: EmailChangeRequestInput
) -> EmailChangeRequestResult:
pass
+
+
+email_change_request = strawberry.mutation(
+ resolver=resolve_email_change_request,
+ description="""
+ Sends an email change code to
+ the given email address.
+ """,
+)
diff --git a/reddit-clone/reddit/users/mutations/password_reset.py b/reddit-clone/reddit/users/mutations/password_reset.py
index 46ec8bce..985ad875 100644
--- a/reddit-clone/reddit/users/mutations/password_reset.py
+++ b/reddit-clone/reddit/users/mutations/password_reset.py
@@ -5,6 +5,8 @@
from reddit.users.types import UserType
+__all__ = ("password_reset",)
+
@strawberry.input
class PasswordResetInput:
@@ -26,6 +28,15 @@ class PasswordResetError:
PasswordResetResult = Union[PasswordResetSuccess, PasswordResetError]
-@strawberry.mutation(description="Resets the password for the user account.")
-async def password_reset(info: Info, input: PasswordResetInput) -> PasswordResetResult:
+async def resolve_password_reset(
+ info: Info, input: PasswordResetInput
+) -> PasswordResetResult:
pass
+
+
+password_reset = strawberry.mutation(
+ resolver=resolve_password_reset,
+ description="""
+ Resets the password for the user account.
+ """,
+)
diff --git a/reddit-clone/reddit/users/mutations/password_reset_request.py b/reddit-clone/reddit/users/mutations/password_reset_request.py
index 06fd88c5..a433df17 100644
--- a/reddit-clone/reddit/users/mutations/password_reset_request.py
+++ b/reddit-clone/reddit/users/mutations/password_reset_request.py
@@ -5,6 +5,8 @@
from reddit.users.types import UserType
+__all__ = ("password_reset_request",)
+
@strawberry.input
class PasswordResetRequestInput:
@@ -26,8 +28,15 @@ class PasswordResetRequestError:
]
-@strawberry.mutation(description="Sends a password reset code to the given email.")
-async def password_reset_request(
+async def resolve_password_reset_request(
info: Info, input: PasswordResetRequestInput
) -> PasswordResetRequestResult:
pass
+
+
+password_reset_request = strawberry.mutation(
+ resolver=resolve_password_reset_request,
+ description="""
+ Sends a password reset code to the given email.
+ """,
+)
diff --git a/reddit-clone/reddit/users/mutations/user_create.py b/reddit-clone/reddit/users/mutations/user_create.py
index 6f016c5b..30ca3003 100644
--- a/reddit-clone/reddit/users/mutations/user_create.py
+++ b/reddit-clone/reddit/users/mutations/user_create.py
@@ -5,6 +5,8 @@
from reddit.users.types import UserType
+__all__ = ("user_create",)
+
@strawberry.input
class UserCreateInput:
@@ -26,6 +28,13 @@ class UserCreateError:
UserCreateResult = Union[UserCreateSuccess, UserCreateError]
-@strawberry.mutation(description="Creates a new user.")
-async def user_create(info: Info, input: UserCreateInput) -> UserCreateResult:
+async def resolve_user_create(info: Info, input: UserCreateInput) -> UserCreateResult:
pass
+
+
+user_create = strawberry.mutation(
+ resolver=resolve_user_create,
+ description="""
+ Creates a new user.
+ """,
+)
diff --git a/reddit-clone/reddit/users/mutations/user_deactivate.py b/reddit-clone/reddit/users/mutations/user_deactivate.py
index 8cbb0810..8d339687 100644
--- a/reddit-clone/reddit/users/mutations/user_deactivate.py
+++ b/reddit-clone/reddit/users/mutations/user_deactivate.py
@@ -5,6 +5,8 @@
from reddit.users.types import UserType
+__all__ = ("user_deactivate",)
+
@strawberry.input
class UserDeactivateInput:
@@ -24,8 +26,15 @@ class UserDeactivateError:
UserDeactivateResult = Union[UserDeactivateSuccess, UserDeactivateError]
-@strawberry.mutation(description="Deactivates the current user.")
-async def user_deactivate(
+async def resolve_user_deactivate(
info: Info, input: UserDeactivateInput
) -> UserDeactivateResult:
pass
+
+
+user_deactivate = strawberry.mutation(
+ resolver=resolve_user_deactivate,
+ description="""
+ Deactivates the current user.
+ """,
+)
diff --git a/reddit-clone/reddit/users/mutations/user_update.py b/reddit-clone/reddit/users/mutations/user_update.py
index d3b3536a..3740cebb 100644
--- a/reddit-clone/reddit/users/mutations/user_update.py
+++ b/reddit-clone/reddit/users/mutations/user_update.py
@@ -6,6 +6,8 @@
from reddit.users.types import UserType
+__all__ = ("user_update",)
+
@strawberry.input
class UserUpdateInput:
@@ -26,6 +28,13 @@ class UserUpdateError:
UserUpdateResult = Union[UserUpdateSuccess, UserUpdateError]
-@strawberry.mutation(description="Updates the current user.")
-async def user_update(info: Info, input: UserUpdateInput) -> UserUpdateResult:
+async def resolve_user_update(info: Info, input: UserUpdateInput) -> UserUpdateResult:
pass
+
+
+user_update = strawberry.mutation(
+ resolver=resolve_user_update,
+ description="""
+ Updates the current user.
+ """,
+)
diff --git a/reddit-clone/reddit/users/queries.py b/reddit-clone/reddit/users/queries.py
index 9ef32119..a6d1f332 100644
--- a/reddit-clone/reddit/users/queries.py
+++ b/reddit-clone/reddit/users/queries.py
@@ -1,24 +1,33 @@
from typing import Optional
import strawberry
+from strawberry.tools import create_type
from strawberry.types import Info
-from sqlalchemy import select
-from reddit.database import get_session
from reddit.users.types import UserType
-from reddit.users.models import User
-
-
-@strawberry.type
-class UserQuery:
- @strawberry.field(description="Gets an user by username.")
- async def user(self, info: Info, username: str) -> Optional[UserType]:
- query = select(User).filter_by(username=username).first()
- async with get_session() as session:
- user = await session.execute(query)
- if user is not None:
- return UserType.from_instance(user)
-
- @strawberry.field(description="Gets the current user.")
- async def current_user(self, info: Info) -> UserType:
- pass
+
+
+async def resolve_user(info: Info, username: str) -> Optional[UserType]:
+ pass
+
+
+user = strawberry.field(
+ resolver=resolve_user,
+ description="""
+ Get an user by username.
+ """,
+)
+
+
+async def resolve_current_user(info: Info) -> UserType:
+ pass
+
+
+current_user = strawberry.field(
+ resolver=resolve_current_user,
+ description="""
+ Gets the current user.
+ """,
+)
+
+UserQuery = create_type(name="UserQuery", fields=(user, current_user))
From 234e1b98e443c147b4cf261cdfb97733302c0a1d Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Sat, 9 Oct 2021 17:39:49 +0530
Subject: [PATCH 054/150] update field descriptions
---
reddit-clone/reddit/users/mutations/email_change.py | 3 ++-
reddit-clone/reddit/users/mutations/password_reset.py | 3 ++-
reddit-clone/reddit/users/mutations/password_reset_request.py | 3 ++-
reddit-clone/reddit/users/queries.py | 2 +-
4 files changed, 7 insertions(+), 4 deletions(-)
diff --git a/reddit-clone/reddit/users/mutations/email_change.py b/reddit-clone/reddit/users/mutations/email_change.py
index 0e98bc45..b08f6036 100644
--- a/reddit-clone/reddit/users/mutations/email_change.py
+++ b/reddit-clone/reddit/users/mutations/email_change.py
@@ -36,6 +36,7 @@ async def resolve_email_change(
email_change = strawberry.mutation(
resolver=resolve_email_change,
description="""
- Changes the email for associated user.
+ Changes the email for the user account
+ associated with the given email.
""",
)
diff --git a/reddit-clone/reddit/users/mutations/password_reset.py b/reddit-clone/reddit/users/mutations/password_reset.py
index 985ad875..16e25ebf 100644
--- a/reddit-clone/reddit/users/mutations/password_reset.py
+++ b/reddit-clone/reddit/users/mutations/password_reset.py
@@ -37,6 +37,7 @@ async def resolve_password_reset(
password_reset = strawberry.mutation(
resolver=resolve_password_reset,
description="""
- Resets the password for the user account.
+ Resets the password for the user account
+ associated with the given email.
""",
)
diff --git a/reddit-clone/reddit/users/mutations/password_reset_request.py b/reddit-clone/reddit/users/mutations/password_reset_request.py
index a433df17..6e2104ef 100644
--- a/reddit-clone/reddit/users/mutations/password_reset_request.py
+++ b/reddit-clone/reddit/users/mutations/password_reset_request.py
@@ -37,6 +37,7 @@ async def resolve_password_reset_request(
password_reset_request = strawberry.mutation(
resolver=resolve_password_reset_request,
description="""
- Sends a password reset code to the given email.
+ Sends a password reset code to the
+ provided email, if it actually exists.
""",
)
diff --git a/reddit-clone/reddit/users/queries.py b/reddit-clone/reddit/users/queries.py
index a6d1f332..50fe6e13 100644
--- a/reddit-clone/reddit/users/queries.py
+++ b/reddit-clone/reddit/users/queries.py
@@ -14,7 +14,7 @@ async def resolve_user(info: Info, username: str) -> Optional[UserType]:
user = strawberry.field(
resolver=resolve_user,
description="""
- Get an user by username.
+ Gets an user by username.
""",
)
From 0e75b362f724ebc57d0114dbc8c2ddb23917eca4 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Sat, 9 Oct 2021 17:46:36 +0530
Subject: [PATCH 055/150] update BaseQuery
---
reddit-clone/reddit/base/queries.py | 19 ++++++++++++++-----
1 file changed, 14 insertions(+), 5 deletions(-)
diff --git a/reddit-clone/reddit/base/queries.py b/reddit-clone/reddit/base/queries.py
index c209cd9e..05bdc83c 100644
--- a/reddit-clone/reddit/base/queries.py
+++ b/reddit-clone/reddit/base/queries.py
@@ -1,13 +1,22 @@
from typing import Optional
import strawberry
+from strawberry.tools import create_type
from strawberry.types import Info
from reddit.base.types import NodeType
-@strawberry.type
-class BaseQuery:
- @strawberry.field(name="node", description="Fetches an object given its ID.")
- def resolve_node(self, info: Info, id: strawberry.ID) -> Optional[NodeType]:
- return NodeType.get_node_from_global_id(info=info, global_id=id)
+def resolve_node(info: Info, id: strawberry.ID) -> Optional[NodeType]:
+ return NodeType.get_node_from_global_id(info=info, global_id=id)
+
+
+node = strawberry.field(
+ resolver=resolve_node,
+ description="""
+ Fetches an object given its ID.
+ """,
+)
+
+
+BaseQuery = create_type(name="BaseQuery", fields=(node,))
From e6637fa58dd2ef19b2d3a1b73bb69b93274078c4 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Sat, 9 Oct 2021 17:58:42 +0530
Subject: [PATCH 056/150] add a few email templates
---
.../templates/emails/activate_email.html | 11 +
.../reddit/templates/emails/base_email.html | 233 ++++++++++++++++++
.../templates/emails/password_reset.html | 14 ++
3 files changed, 258 insertions(+)
create mode 100644 reddit-clone/reddit/templates/emails/activate_email.html
create mode 100644 reddit-clone/reddit/templates/emails/base_email.html
create mode 100644 reddit-clone/reddit/templates/emails/password_reset.html
diff --git a/reddit-clone/reddit/templates/emails/activate_email.html b/reddit-clone/reddit/templates/emails/activate_email.html
new file mode 100644
index 00000000..727af27c
--- /dev/null
+++ b/reddit-clone/reddit/templates/emails/activate_email.html
@@ -0,0 +1,11 @@
+{% extends "emails/base_email.html" %} {% block title %} Confirm Email {%
+endblock %} {% block content %}
+
+ Someone tried to sign up for a {{ config.SITE_NAME }} account
+ with {{ email }}. If this was you,
+ enter this confirmation code in the app:
+
+{{ code }}
+{% endblock %} {% block content_more %}
+If this wasn't you, this email can be safely ignored.
+{% endblock %}
diff --git a/reddit-clone/reddit/templates/emails/base_email.html b/reddit-clone/reddit/templates/emails/base_email.html
new file mode 100644
index 00000000..31530f06
--- /dev/null
+++ b/reddit-clone/reddit/templates/emails/base_email.html
@@ -0,0 +1,233 @@
+
+
+ {% block title %}{% endblock %}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ |
+ {% block content %}{% endblock %}
+ |
+
+
+ |
+
+
+
+
+
+ |
+ {% block content_more %}{% endblock %}
+ |
+
+
+ |
+
+
+ |
+
+
+ |
+
+
+
+ |
+
+ |
+
+
+
+
diff --git a/reddit-clone/reddit/templates/emails/password_reset.html b/reddit-clone/reddit/templates/emails/password_reset.html
new file mode 100644
index 00000000..5a759fe8
--- /dev/null
+++ b/reddit-clone/reddit/templates/emails/password_reset.html
@@ -0,0 +1,14 @@
+{% extends "emails/base_email.html" %} {% block title %} Password Reset {%
+endblock %} {% block content %}
+Hey {{ user.username }}!
+
+ We heard that you lost your password. Sorry about that!
+ But don’t worry! Enter the following code to reset your password.
+
+
{{ code }}
+
+{% endblock %} {% block content_more %}
+
+ If this wasn't you, this email can be safely ignored.
+
+{% endblock %}
From 628ae0ee83efedc11119b7c2cde665042c608176 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Sat, 9 Oct 2021 19:08:48 +0530
Subject: [PATCH 057/150] add skeleton for subreddit mutations and queries
---
reddit-clone/reddit/base/queries.py | 2 +-
reddit-clone/reddit/base/types.py | 2 +-
reddit-clone/reddit/schema.py | 6 ++-
.../reddit/subreddits/mutations/__init__.py | 3 ++
reddit-clone/reddit/subreddits/queries.py | 21 ++++++++++
reddit-clone/reddit/subreddits/types.py | 7 ++++
.../reddit/users/mutations/__init__.py | 2 +
.../reddit/users/mutations/user_block.py | 39 +++++++++++++++++++
reddit-clone/reddit/users/types.py | 6 +++
9 files changed, 84 insertions(+), 4 deletions(-)
create mode 100644 reddit-clone/reddit/subreddits/mutations/__init__.py
create mode 100644 reddit-clone/reddit/subreddits/queries.py
create mode 100644 reddit-clone/reddit/users/mutations/user_block.py
diff --git a/reddit-clone/reddit/base/queries.py b/reddit-clone/reddit/base/queries.py
index 05bdc83c..b70f4744 100644
--- a/reddit-clone/reddit/base/queries.py
+++ b/reddit-clone/reddit/base/queries.py
@@ -8,7 +8,7 @@
def resolve_node(info: Info, id: strawberry.ID) -> Optional[NodeType]:
- return NodeType.get_node_from_global_id(info=info, global_id=id)
+ return NodeType.resolve(info=info, global_id=id)
node = strawberry.field(
diff --git a/reddit-clone/reddit/base/types.py b/reddit-clone/reddit/base/types.py
index 46902997..7d3096e7 100644
--- a/reddit-clone/reddit/base/types.py
+++ b/reddit-clone/reddit/base/types.py
@@ -16,7 +16,7 @@ class NodeType:
)
@classmethod
- def get_node_from_global_id(
+ def resolve(
cls, info: Info, global_id: str, only_type=None
) -> Optional[Type[NodeType]]:
try:
diff --git a/reddit-clone/reddit/schema.py b/reddit-clone/reddit/schema.py
index 48d4e8ae..ef92bc16 100644
--- a/reddit-clone/reddit/schema.py
+++ b/reddit-clone/reddit/schema.py
@@ -1,17 +1,19 @@
import strawberry
from reddit.base.queries import BaseQuery
+from reddit.subreddits.queries import SubredditQuery
from reddit.users.queries import UserQuery
+from reddit.subreddits.mutations import SubredditMutation
from reddit.users.mutations import UserMutation
@strawberry.type
-class Query(BaseQuery, UserQuery):
+class Query(BaseQuery, UserQuery, SubredditQuery):
pass
@strawberry.type
-class Mutation(UserMutation):
+class Mutation(UserMutation, SubredditMutation):
pass
diff --git a/reddit-clone/reddit/subreddits/mutations/__init__.py b/reddit-clone/reddit/subreddits/mutations/__init__.py
new file mode 100644
index 00000000..2ab7caaf
--- /dev/null
+++ b/reddit-clone/reddit/subreddits/mutations/__init__.py
@@ -0,0 +1,3 @@
+from strawberry.tools import create_type
+
+SubredditMutation = create_type(name="SubredditMutation", fields=())
diff --git a/reddit-clone/reddit/subreddits/queries.py b/reddit-clone/reddit/subreddits/queries.py
new file mode 100644
index 00000000..77998552
--- /dev/null
+++ b/reddit-clone/reddit/subreddits/queries.py
@@ -0,0 +1,21 @@
+from typing import List
+
+import strawberry
+from strawberry.tools import create_type
+from strawberry.types import Info
+
+from reddit.subreddits.types import SubredditType
+
+
+async def resolve_subreddits(info: Info) -> List[SubredditType]:
+ pass
+
+
+subreddits = strawberry.field(
+ resolver=resolve_subreddits,
+ description="""
+ Gets the available subreddits.
+ """,
+)
+
+SubredditQuery = create_type(name="SubredditQuery", fields=(subreddits,))
diff --git a/reddit-clone/reddit/subreddits/types.py b/reddit-clone/reddit/subreddits/types.py
index f6c57fb3..6d3ca010 100644
--- a/reddit-clone/reddit/subreddits/types.py
+++ b/reddit-clone/reddit/subreddits/types.py
@@ -32,6 +32,13 @@ class SubredditType(NodeType):
"""
)
+ submit_text: str = strawberry.field(
+ description="""
+ The text set by the subreddit moderators, intended
+ to be displayed on the submission form.
+ """
+ )
+
# TODO: make status an enum
status: int = strawberry.field(
description="""
diff --git a/reddit-clone/reddit/users/mutations/__init__.py b/reddit-clone/reddit/users/mutations/__init__.py
index 91432cc5..83a8adab 100644
--- a/reddit-clone/reddit/users/mutations/__init__.py
+++ b/reddit-clone/reddit/users/mutations/__init__.py
@@ -6,6 +6,7 @@
from .email_change import email_change
from .password_reset_request import password_reset_request
from .password_reset import password_reset
+from .user_block import user_block
from .user_create import user_create
from .user_deactivate import user_deactivate
from .user_update import user_update
@@ -19,6 +20,7 @@
email_change,
password_reset_request,
password_reset,
+ user_block,
user_create,
user_deactivate,
user_update,
diff --git a/reddit-clone/reddit/users/mutations/user_block.py b/reddit-clone/reddit/users/mutations/user_block.py
new file mode 100644
index 00000000..b3b7b6bc
--- /dev/null
+++ b/reddit-clone/reddit/users/mutations/user_block.py
@@ -0,0 +1,39 @@
+from typing import Union
+
+import strawberry
+from strawberry.types import Info
+
+from reddit.users.types import UserType
+
+
+__all__ = ("user_block",)
+
+
+@strawberry.input
+class UserBlockInput:
+ user_id: strawberry.ID
+
+
+@strawberry.type
+class UserBlockSuccess:
+ user: UserType
+
+
+@strawberry.type
+class UserBlockError:
+ error: str
+
+
+UserBlockResult = Union[UserBlockSuccess, UserBlockError]
+
+
+async def resolve_user_block(info: Info, input: UserBlockInput) -> UserBlockResult:
+ pass
+
+
+user_block = strawberry.mutation(
+ resolver=resolve_user_block,
+ description="""
+ Blocks an user account.
+ """,
+)
diff --git a/reddit-clone/reddit/users/types.py b/reddit-clone/reddit/users/types.py
index c5973d67..266c093b 100644
--- a/reddit-clone/reddit/users/types.py
+++ b/reddit-clone/reddit/users/types.py
@@ -28,6 +28,12 @@ class UserType(NodeType):
"""
)
+ blocked: List[UserType] = strawberry.field(
+ description="""
+ The accounts blocked by the user.
+ """
+ )
+
posts: List[PostType] = strawberry.field(
description="""
The posts for the user.
From af11ed29c87d6afbc50f1ade6d3f6641202484d8 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Sat, 9 Oct 2021 19:27:11 +0530
Subject: [PATCH 058/150] remove from_instance before we finalize data models
---
reddit-clone/reddit/comments/types.py | 8 +---
reddit-clone/reddit/posts/types.py | 12 +-----
.../reddit/subreddits/mutations/__init__.py | 4 +-
.../subreddits/mutations/subreddit_create.py | 41 +++++++++++++++++++
reddit-clone/reddit/subreddits/types.py | 30 ++++----------
reddit-clone/reddit/users/types.py | 9 +---
6 files changed, 55 insertions(+), 49 deletions(-)
create mode 100644 reddit-clone/reddit/subreddits/mutations/subreddit_create.py
diff --git a/reddit-clone/reddit/comments/types.py b/reddit-clone/reddit/comments/types.py
index 0f725773..e5cc7697 100644
--- a/reddit-clone/reddit/comments/types.py
+++ b/reddit-clone/reddit/comments/types.py
@@ -50,10 +50,4 @@ async def resolve_node(cls, info: Info, comment_id: str) -> Optional[CommentType
@classmethod
def from_instance(cls, instance: Comment) -> CommentType:
- return CommentType(
- id=instance.id,
- content=instance.content,
- votes=instance.votes,
- user_id=instance.user_id,
- replies=instance.replies,
- )
+ pass
diff --git a/reddit-clone/reddit/posts/types.py b/reddit-clone/reddit/posts/types.py
index b3901df1..c9a1cb30 100644
--- a/reddit-clone/reddit/posts/types.py
+++ b/reddit-clone/reddit/posts/types.py
@@ -75,14 +75,4 @@ async def resolve_node(cls, info: Info, post_id: str) -> Optional[PostType]:
@classmethod
def from_instance(cls, instance: Post) -> PostType:
- return PostType(
- id=instance.id,
- title=instance.title,
- text=instance.text,
- link=instance.link,
- thumbnail=instance.thumbnail,
- user_id=instance.user_id,
- subreddit_id=instance.subreddit_id,
- votes=instance.votes,
- comments=instance.comments,
- )
+ pass
diff --git a/reddit-clone/reddit/subreddits/mutations/__init__.py b/reddit-clone/reddit/subreddits/mutations/__init__.py
index 2ab7caaf..1b96ae52 100644
--- a/reddit-clone/reddit/subreddits/mutations/__init__.py
+++ b/reddit-clone/reddit/subreddits/mutations/__init__.py
@@ -1,3 +1,5 @@
from strawberry.tools import create_type
-SubredditMutation = create_type(name="SubredditMutation", fields=())
+from .subreddit_create import subreddit_create
+
+SubredditMutation = create_type(name="SubredditMutation", fields=(subreddit_create,))
diff --git a/reddit-clone/reddit/subreddits/mutations/subreddit_create.py b/reddit-clone/reddit/subreddits/mutations/subreddit_create.py
new file mode 100644
index 00000000..201bd07c
--- /dev/null
+++ b/reddit-clone/reddit/subreddits/mutations/subreddit_create.py
@@ -0,0 +1,41 @@
+from typing import Union
+
+import strawberry
+from strawberry.types import Info
+
+from reddit.subreddits.types import SubredditType
+
+
+__all__ = ("subreddit_create",)
+
+
+@strawberry.input
+class SubredditCreateInput:
+ name: str
+
+
+@strawberry.type
+class SubredditCreateSuccess:
+ subreddit: SubredditType
+
+
+@strawberry.type
+class SubredditCreateError:
+ error: str
+
+
+SubredditCreateResult = Union[SubredditCreateSuccess, SubredditCreateError]
+
+
+async def resolve_subreddit_create(
+ info: Info, input: SubredditCreateInput
+) -> SubredditCreateResult:
+ pass
+
+
+subreddit_create = strawberry.mutation(
+ resolver=resolve_subreddit_create,
+ description="""
+ Creates a new Subreddit.
+ """,
+)
diff --git a/reddit-clone/reddit/subreddits/types.py b/reddit-clone/reddit/subreddits/types.py
index 6d3ca010..7bf23b32 100644
--- a/reddit-clone/reddit/subreddits/types.py
+++ b/reddit-clone/reddit/subreddits/types.py
@@ -16,45 +16,38 @@
class SubredditType(NodeType):
name: str = strawberry.field(
description="""
- The name of the subreddit.
+ The name of the Subreddit.
"""
)
description: str = strawberry.field(
description="""
- The description of the subreddit.
+ The description of the Subreddit.
"""
)
admin_id: int = strawberry.field(
description="""
- The owner ID of the subreddit.
+ The owner ID of the Subreddit.
"""
)
submit_text: str = strawberry.field(
description="""
- The text set by the subreddit moderators, intended
+ The text set by the Subreddit moderators, intended
to be displayed on the submission form.
"""
)
- # TODO: make status an enum
- status: int = strawberry.field(
- description="""
- The status of the subreddit.
- """
- )
-
icon: str = strawberry.field(
description="""
- The icon URL of the subreddit.
+ The icon URL of the Subreddit.
"""
)
posts: List[PostType] = strawberry.field(
description="""
- The posts for the subreddit.
+ The posts for the Subreddit.
"""
)
@@ -63,7 +56,7 @@ async def resolve_node(
cls, info: Info, subreddit_id: str
) -> Optional[SubredditType]:
"""
- Gets a subreddit with the given ID.
+ Gets a Subreddit with the given ID.
"""
query = select(Subreddit).filter_by(id=subreddit_id).first()
async with get_session() as session:
@@ -73,11 +66,4 @@ async def resolve_node(
@classmethod
def from_instance(cls, instance: Subreddit) -> SubredditType:
- return SubredditType(
- id=instance.id,
- description=instance.description,
- admin_id=instance.admin_id,
- status=instance.status,
- icon=instance.icon,
- posts=instance.posts,
- )
+ pass
diff --git a/reddit-clone/reddit/users/types.py b/reddit-clone/reddit/users/types.py
index 266c093b..09df0b6d 100644
--- a/reddit-clone/reddit/users/types.py
+++ b/reddit-clone/reddit/users/types.py
@@ -65,11 +65,4 @@ async def resolve_node(cls, info: Info, user_id: str) -> Optional[UserType]:
@classmethod
def from_instance(cls, instance: User) -> UserType:
- return UserType(
- id=instance.id,
- username=instance.username,
- avatar=instance.avatar,
- posts=instance.posts,
- subreddits=instance.subreddits,
- comments=instance.comments,
- )
+ pass
From 743b15666fa613e32c67280a121fc704cfb3d6ee Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Sun, 10 Oct 2021 09:24:52 +0530
Subject: [PATCH 059/150] add more mutations
---
.../reddit/comments/mutations/__init__.py | 11 +++++
.../comments/mutations/comment_create.py | 44 ++++++++++++++++++
.../comments/mutations/comment_delete.py | 44 ++++++++++++++++++
.../comments/mutations/comment_update.py | 45 +++++++++++++++++++
.../reddit/comments/mutations/comment_vote.py | 44 ++++++++++++++++++
.../reddit/posts/mutations/__init__.py | 10 +++++
.../reddit/posts/mutations/post_create.py | 42 +++++++++++++++++
.../reddit/posts/mutations/post_delete.py | 41 +++++++++++++++++
.../reddit/posts/mutations/post_update.py | 43 ++++++++++++++++++
.../reddit/posts/mutations/post_vote.py | 41 +++++++++++++++++
reddit-clone/reddit/schema.py | 4 +-
.../reddit/subreddits/mutations/__init__.py | 15 ++++++-
.../subreddits/mutations/subreddit_create.py | 4 +-
.../subreddits/mutations/subreddit_delete.py | 42 +++++++++++++++++
.../subreddits/mutations/subreddit_join.py | 42 +++++++++++++++++
.../subreddits/mutations/subreddit_leave.py | 42 +++++++++++++++++
.../subreddits/mutations/subreddit_update.py | 44 ++++++++++++++++++
reddit-clone/reddit/subreddits/queries.py | 1 +
.../reddit/users/mutations/authenticate.py | 1 +
.../reddit/users/mutations/avatar_remove.py | 1 +
.../reddit/users/mutations/email_change.py | 1 +
.../users/mutations/email_change_request.py | 1 +
.../reddit/users/mutations/password_reset.py | 1 +
.../users/mutations/password_reset_request.py | 1 +
.../reddit/users/mutations/user_block.py | 1 +
.../reddit/users/mutations/user_create.py | 1 +
.../reddit/users/mutations/user_deactivate.py | 1 +
.../reddit/users/mutations/user_update.py | 1 +
reddit-clone/reddit/users/queries.py | 2 +
29 files changed, 568 insertions(+), 3 deletions(-)
create mode 100644 reddit-clone/reddit/comments/mutations/__init__.py
create mode 100644 reddit-clone/reddit/comments/mutations/comment_create.py
create mode 100644 reddit-clone/reddit/comments/mutations/comment_delete.py
create mode 100644 reddit-clone/reddit/comments/mutations/comment_update.py
create mode 100644 reddit-clone/reddit/comments/mutations/comment_vote.py
create mode 100644 reddit-clone/reddit/posts/mutations/__init__.py
create mode 100644 reddit-clone/reddit/posts/mutations/post_create.py
create mode 100644 reddit-clone/reddit/posts/mutations/post_delete.py
create mode 100644 reddit-clone/reddit/posts/mutations/post_update.py
create mode 100644 reddit-clone/reddit/posts/mutations/post_vote.py
create mode 100644 reddit-clone/reddit/subreddits/mutations/subreddit_delete.py
create mode 100644 reddit-clone/reddit/subreddits/mutations/subreddit_join.py
create mode 100644 reddit-clone/reddit/subreddits/mutations/subreddit_leave.py
create mode 100644 reddit-clone/reddit/subreddits/mutations/subreddit_update.py
diff --git a/reddit-clone/reddit/comments/mutations/__init__.py b/reddit-clone/reddit/comments/mutations/__init__.py
new file mode 100644
index 00000000..a37863d9
--- /dev/null
+++ b/reddit-clone/reddit/comments/mutations/__init__.py
@@ -0,0 +1,11 @@
+from strawberry.tools import create_type
+
+from .comment_create import comment_create
+from .comment_delete import comment_delete
+from .comment_update import comment_update
+from .comment_vote import comment_vote
+
+CommentMutation = create_type(
+ name="CommentMutation",
+ fields=(comment_create, comment_delete, comment_update, comment_vote),
+)
diff --git a/reddit-clone/reddit/comments/mutations/comment_create.py b/reddit-clone/reddit/comments/mutations/comment_create.py
new file mode 100644
index 00000000..cbea3ebf
--- /dev/null
+++ b/reddit-clone/reddit/comments/mutations/comment_create.py
@@ -0,0 +1,44 @@
+from typing import Union
+
+import strawberry
+from strawberry.types import Info
+
+from reddit.comments.types import CommentType
+
+
+__all__ = ("comment_create",)
+
+
+@strawberry.input
+class CommentCreateInput:
+ content: str
+ subreddit_id: strawberry.ID
+ post_id: strawberry.ID
+
+
+@strawberry.type
+class CommentCreateSuccess:
+ comment: CommentType
+
+
+@strawberry.type
+class CommentCreateError:
+ error: str
+
+
+CommentCreateResult = Union[CommentCreateSuccess, CommentCreateError]
+
+
+async def resolve_comment_create(
+ info: Info, input: CommentCreateInput
+) -> CommentCreateResult:
+ pass
+
+
+comment_create = strawberry.mutation(
+ name="comment_create",
+ resolver=resolve_comment_create,
+ description="""
+ Creates a new comment on a post.
+ """,
+)
diff --git a/reddit-clone/reddit/comments/mutations/comment_delete.py b/reddit-clone/reddit/comments/mutations/comment_delete.py
new file mode 100644
index 00000000..c9973867
--- /dev/null
+++ b/reddit-clone/reddit/comments/mutations/comment_delete.py
@@ -0,0 +1,44 @@
+from typing import Union
+
+import strawberry
+from strawberry.types import Info
+
+from reddit.comments.types import CommentType
+
+
+__all__ = ("comment_delete",)
+
+
+@strawberry.input
+class CommentDeleteInput:
+ comment_id: strawberry.ID
+ subreddit_id: strawberry.ID
+ post_id: strawberry.ID
+
+
+@strawberry.type
+class CommentDeleteSuccess:
+ comment: CommentType
+
+
+@strawberry.type
+class CommentDeleteError:
+ error: str
+
+
+CommentDeleteResult = Union[CommentDeleteSuccess, CommentDeleteError]
+
+
+async def resolve_comment_delete(
+ info: Info, input: CommentDeleteInput
+) -> CommentDeleteResult:
+ pass
+
+
+comment_delete = strawberry.mutation(
+ name="comment_delete",
+ resolver=resolve_comment_delete,
+ description="""
+ Deletes a comment on a post.
+ """,
+)
diff --git a/reddit-clone/reddit/comments/mutations/comment_update.py b/reddit-clone/reddit/comments/mutations/comment_update.py
new file mode 100644
index 00000000..a8455283
--- /dev/null
+++ b/reddit-clone/reddit/comments/mutations/comment_update.py
@@ -0,0 +1,45 @@
+from typing import Union, Optional
+
+import strawberry
+from strawberry.types import Info
+
+from reddit.comments.types import CommentType
+
+
+__all__ = ("comment_update",)
+
+
+@strawberry.input
+class CommentUpdateInput:
+ comment: Optional[str]
+ comment_id: strawberry.ID
+ subreddit_id: strawberry.ID
+ post_id: strawberry.ID
+
+
+@strawberry.type
+class CommentUpdateSuccess:
+ comment: CommentType
+
+
+@strawberry.type
+class CommentUpdateError:
+ error: str
+
+
+CommentUpdateResult = Union[CommentUpdateSuccess, CommentUpdateError]
+
+
+async def resolve_comment_update(
+ info: Info, input: CommentUpdateInput
+) -> CommentUpdateResult:
+ pass
+
+
+comment_update = strawberry.mutation(
+ name="comment_update",
+ resolver=resolve_comment_update,
+ description="""
+ Updates a comment on a post.
+ """,
+)
diff --git a/reddit-clone/reddit/comments/mutations/comment_vote.py b/reddit-clone/reddit/comments/mutations/comment_vote.py
new file mode 100644
index 00000000..3c216e59
--- /dev/null
+++ b/reddit-clone/reddit/comments/mutations/comment_vote.py
@@ -0,0 +1,44 @@
+from typing import Union
+
+import strawberry
+from strawberry.types import Info
+
+from reddit.comments.types import CommentType
+
+
+__all__ = ("comment_vote",)
+
+
+@strawberry.input
+class CommentVoteInput:
+ comment_id: strawberry.ID
+ subreddit_id: strawberry.ID
+ post_id: strawberry.ID
+
+
+@strawberry.type
+class CommentVoteSuccess:
+ comment: CommentType
+
+
+@strawberry.type
+class CommentVoteError:
+ error: str
+
+
+CommentVoteResult = Union[CommentVoteSuccess, CommentVoteError]
+
+
+async def resolve_comment_vote(
+ info: Info, input: CommentVoteInput
+) -> CommentVoteResult:
+ pass
+
+
+comment_vote = strawberry.mutation(
+ name="comment_vote",
+ resolver=resolve_comment_vote,
+ description="""
+ Creates a vote on a comment.
+ """,
+)
diff --git a/reddit-clone/reddit/posts/mutations/__init__.py b/reddit-clone/reddit/posts/mutations/__init__.py
new file mode 100644
index 00000000..06457192
--- /dev/null
+++ b/reddit-clone/reddit/posts/mutations/__init__.py
@@ -0,0 +1,10 @@
+from strawberry.tools import create_type
+
+from .post_create import post_create
+from .post_delete import post_delete
+from .post_update import post_update
+from .post_vote import post_vote
+
+PostMutation = create_type(
+ name="PostMutation", fields=(post_create, post_delete, post_update, post_vote)
+)
diff --git a/reddit-clone/reddit/posts/mutations/post_create.py b/reddit-clone/reddit/posts/mutations/post_create.py
new file mode 100644
index 00000000..4fac0a36
--- /dev/null
+++ b/reddit-clone/reddit/posts/mutations/post_create.py
@@ -0,0 +1,42 @@
+from typing import Union, Optional
+
+import strawberry
+from strawberry.types import Info
+
+from reddit.posts.types import PostType
+
+
+__all__ = ("post_create",)
+
+
+@strawberry.input
+class PostCreateInput:
+ title: str
+ subreddit_id: strawberry.ID
+ text: Optional[str]
+
+
+@strawberry.type
+class PostCreateSuccess:
+ post: PostType
+
+
+@strawberry.type
+class PostCreateError:
+ error: str
+
+
+PostCreateResult = Union[PostCreateSuccess, PostCreateError]
+
+
+async def resolve_post_create(info: Info, input: PostCreateInput) -> PostCreateResult:
+ pass
+
+
+post_create = strawberry.mutation(
+ name="post_create",
+ resolver=resolve_post_create,
+ description="""
+ Creates a new post in a Subreddit.
+ """,
+)
diff --git a/reddit-clone/reddit/posts/mutations/post_delete.py b/reddit-clone/reddit/posts/mutations/post_delete.py
new file mode 100644
index 00000000..074410b8
--- /dev/null
+++ b/reddit-clone/reddit/posts/mutations/post_delete.py
@@ -0,0 +1,41 @@
+from typing import Union
+
+import strawberry
+from strawberry.types import Info
+
+from reddit.posts.types import PostType
+
+
+__all__ = ("post_delete",)
+
+
+@strawberry.input
+class PostDeleteInput:
+ post_id: strawberry.ID
+ subreddit_id: strawberry.ID
+
+
+@strawberry.type
+class PostDeleteSuccess:
+ post: PostType
+
+
+@strawberry.type
+class PostDeleteError:
+ error: str
+
+
+PostDeleteResult = Union[PostDeleteSuccess, PostDeleteError]
+
+
+async def resolve_post_delete(info: Info, input: PostDeleteInput) -> PostDeleteResult:
+ pass
+
+
+post_delete = strawberry.mutation(
+ name="post_delete",
+ resolver=resolve_post_delete,
+ description="""
+ Deletes a post in a Subreddit.
+ """,
+)
diff --git a/reddit-clone/reddit/posts/mutations/post_update.py b/reddit-clone/reddit/posts/mutations/post_update.py
new file mode 100644
index 00000000..4fa38a42
--- /dev/null
+++ b/reddit-clone/reddit/posts/mutations/post_update.py
@@ -0,0 +1,43 @@
+from typing import Union, Optional
+
+import strawberry
+from strawberry.types import Info
+
+from reddit.posts.types import PostType
+
+
+__all__ = ("post_update",)
+
+
+@strawberry.input
+class PostUpdateInput:
+ title: Optional[str]
+ text: Optional[str]
+ post_id: strawberry.ID
+ subreddit_id: strawberry.ID
+
+
+@strawberry.type
+class PostUpdateSuccess:
+ post: PostType
+
+
+@strawberry.type
+class PostUpdateError:
+ error: str
+
+
+PostUpdateResult = Union[PostUpdateSuccess, PostUpdateError]
+
+
+async def resolve_post_update(info: Info, input: PostUpdateInput) -> PostUpdateResult:
+ pass
+
+
+post_update = strawberry.mutation(
+ name="post_update",
+ resolver=resolve_post_update,
+ description="""
+ Updates a post in a Subreddit.
+ """,
+)
diff --git a/reddit-clone/reddit/posts/mutations/post_vote.py b/reddit-clone/reddit/posts/mutations/post_vote.py
new file mode 100644
index 00000000..2a86c47d
--- /dev/null
+++ b/reddit-clone/reddit/posts/mutations/post_vote.py
@@ -0,0 +1,41 @@
+from typing import Union
+
+import strawberry
+from strawberry.types import Info
+
+from reddit.posts.types import PostType
+
+
+__all__ = ("post_vote",)
+
+
+@strawberry.input
+class PostVoteInput:
+ post_id: strawberry.ID
+ subreddit_id: strawberry.ID
+
+
+@strawberry.type
+class PostVoteSuccess:
+ post: PostType
+
+
+@strawberry.type
+class PostVoteError:
+ error: str
+
+
+PostVoteResult = Union[PostVoteSuccess, PostVoteError]
+
+
+async def resolve_post_vote(info: Info, input: PostVoteInput) -> PostVoteResult:
+ pass
+
+
+post_vote = strawberry.mutation(
+ name="post_vote",
+ resolver=resolve_post_vote,
+ description="""
+ Creates a vote on a post.
+ """,
+)
diff --git a/reddit-clone/reddit/schema.py b/reddit-clone/reddit/schema.py
index ef92bc16..af03218e 100644
--- a/reddit-clone/reddit/schema.py
+++ b/reddit-clone/reddit/schema.py
@@ -3,6 +3,8 @@
from reddit.base.queries import BaseQuery
from reddit.subreddits.queries import SubredditQuery
from reddit.users.queries import UserQuery
+from reddit.comments.mutations import CommentMutation
+from reddit.posts.mutations import PostMutation
from reddit.subreddits.mutations import SubredditMutation
from reddit.users.mutations import UserMutation
@@ -13,7 +15,7 @@ class Query(BaseQuery, UserQuery, SubredditQuery):
@strawberry.type
-class Mutation(UserMutation, SubredditMutation):
+class Mutation(CommentMutation, PostMutation, UserMutation, SubredditMutation):
pass
diff --git a/reddit-clone/reddit/subreddits/mutations/__init__.py b/reddit-clone/reddit/subreddits/mutations/__init__.py
index 1b96ae52..83a4ad10 100644
--- a/reddit-clone/reddit/subreddits/mutations/__init__.py
+++ b/reddit-clone/reddit/subreddits/mutations/__init__.py
@@ -1,5 +1,18 @@
from strawberry.tools import create_type
from .subreddit_create import subreddit_create
+from .subreddit_delete import subreddit_delete
+from .subreddit_join import subreddit_join
+from .subreddit_leave import subreddit_leave
+from .subreddit_update import subreddit_update
-SubredditMutation = create_type(name="SubredditMutation", fields=(subreddit_create,))
+SubredditMutation = create_type(
+ name="SubredditMutation",
+ fields=(
+ subreddit_create,
+ subreddit_delete,
+ subreddit_join,
+ subreddit_leave,
+ subreddit_update,
+ ),
+)
diff --git a/reddit-clone/reddit/subreddits/mutations/subreddit_create.py b/reddit-clone/reddit/subreddits/mutations/subreddit_create.py
index 201bd07c..0e65be74 100644
--- a/reddit-clone/reddit/subreddits/mutations/subreddit_create.py
+++ b/reddit-clone/reddit/subreddits/mutations/subreddit_create.py
@@ -1,4 +1,4 @@
-from typing import Union
+from typing import Optional, Union
import strawberry
from strawberry.types import Info
@@ -12,6 +12,7 @@
@strawberry.input
class SubredditCreateInput:
name: str
+ description: Optional[str]
@strawberry.type
@@ -34,6 +35,7 @@ async def resolve_subreddit_create(
subreddit_create = strawberry.mutation(
+ name="subreddit_create",
resolver=resolve_subreddit_create,
description="""
Creates a new Subreddit.
diff --git a/reddit-clone/reddit/subreddits/mutations/subreddit_delete.py b/reddit-clone/reddit/subreddits/mutations/subreddit_delete.py
new file mode 100644
index 00000000..86dd7ec4
--- /dev/null
+++ b/reddit-clone/reddit/subreddits/mutations/subreddit_delete.py
@@ -0,0 +1,42 @@
+from typing import Union
+
+import strawberry
+from strawberry.types import Info
+
+from reddit.subreddits.types import SubredditType
+
+
+__all__ = ("subreddit_delete",)
+
+
+@strawberry.input
+class SubredditDeleteInput:
+ subreddit_id: strawberry.ID
+
+
+@strawberry.type
+class SubredditDeleteSuccess:
+ subreddit: SubredditType
+
+
+@strawberry.type
+class SubredditDeleteError:
+ error: str
+
+
+SubredditDeleteResult = Union[SubredditDeleteSuccess, SubredditDeleteError]
+
+
+async def resolve_subreddit_delete(
+ info: Info, input: SubredditDeleteInput
+) -> SubredditDeleteResult:
+ pass
+
+
+subreddit_delete = strawberry.mutation(
+ name="subreddit_delete",
+ resolver=resolve_subreddit_delete,
+ description="""
+ Deletes a Subreddit.
+ """,
+)
diff --git a/reddit-clone/reddit/subreddits/mutations/subreddit_join.py b/reddit-clone/reddit/subreddits/mutations/subreddit_join.py
new file mode 100644
index 00000000..3a35ab8b
--- /dev/null
+++ b/reddit-clone/reddit/subreddits/mutations/subreddit_join.py
@@ -0,0 +1,42 @@
+from typing import Union
+
+import strawberry
+from strawberry.types import Info
+
+from reddit.subreddits.types import SubredditType
+
+
+__all__ = ("subreddit_join",)
+
+
+@strawberry.input
+class SubredditJoinInput:
+ subreddit_id: strawberry.ID
+
+
+@strawberry.type
+class SubredditJoinSuccess:
+ subreddit: SubredditType
+
+
+@strawberry.type
+class SubredditJoinError:
+ error: str
+
+
+SubredditJoinResult = Union[SubredditJoinSuccess, SubredditJoinError]
+
+
+async def resolve_subreddit_join(
+ info: Info, input: SubredditJoinInput
+) -> SubredditJoinResult:
+ pass
+
+
+subreddit_join = strawberry.mutation(
+ name="subreddit_join",
+ resolver=resolve_subreddit_join,
+ description="""
+ Creates a new Subreddit-User relationship.
+ """,
+)
diff --git a/reddit-clone/reddit/subreddits/mutations/subreddit_leave.py b/reddit-clone/reddit/subreddits/mutations/subreddit_leave.py
new file mode 100644
index 00000000..f49786eb
--- /dev/null
+++ b/reddit-clone/reddit/subreddits/mutations/subreddit_leave.py
@@ -0,0 +1,42 @@
+from typing import Union
+
+import strawberry
+from strawberry.types import Info
+
+from reddit.subreddits.types import SubredditType
+
+
+__all__ = ("subreddit_leave",)
+
+
+@strawberry.input
+class SubredditLeaveInput:
+ subreddit_id: strawberry.ID
+
+
+@strawberry.type
+class SubredditLeaveSuccess:
+ subreddit: SubredditType
+
+
+@strawberry.type
+class SubredditLeaveError:
+ error: str
+
+
+SubredditLeaveResult = Union[SubredditLeaveSuccess, SubredditLeaveError]
+
+
+async def resolve_subreddit_leave(
+ info: Info, input: SubredditLeaveInput
+) -> SubredditLeaveResult:
+ pass
+
+
+subreddit_leave = strawberry.mutation(
+ name="subreddit_leave",
+ resolver=resolve_subreddit_leave,
+ description="""
+ Deletes a Subreddit-User relationship.
+ """,
+)
diff --git a/reddit-clone/reddit/subreddits/mutations/subreddit_update.py b/reddit-clone/reddit/subreddits/mutations/subreddit_update.py
new file mode 100644
index 00000000..40f5ca18
--- /dev/null
+++ b/reddit-clone/reddit/subreddits/mutations/subreddit_update.py
@@ -0,0 +1,44 @@
+from typing import Optional, Union
+
+import strawberry
+from strawberry.types import Info
+
+from reddit.subreddits.types import SubredditType
+
+
+__all__ = ("subreddit_update",)
+
+
+@strawberry.input
+class SubredditUpdateInput:
+ name: Optional[str]
+ description: Optional[str]
+ subreddit_id: strawberry.ID
+
+
+@strawberry.type
+class SubredditUpdateSuccess:
+ subreddit: SubredditType
+
+
+@strawberry.type
+class SubredditUpdateError:
+ error: str
+
+
+SubredditUpdateResult = Union[SubredditUpdateSuccess, SubredditUpdateError]
+
+
+async def resolve_subreddit_update(
+ info: Info, input: SubredditUpdateInput
+) -> SubredditUpdateResult:
+ pass
+
+
+subreddit_update = strawberry.mutation(
+ name="subreddit_update",
+ resolver=resolve_subreddit_update,
+ description="""
+ Updates a Subreddit.
+ """,
+)
diff --git a/reddit-clone/reddit/subreddits/queries.py b/reddit-clone/reddit/subreddits/queries.py
index 77998552..f4135180 100644
--- a/reddit-clone/reddit/subreddits/queries.py
+++ b/reddit-clone/reddit/subreddits/queries.py
@@ -12,6 +12,7 @@ async def resolve_subreddits(info: Info) -> List[SubredditType]:
subreddits = strawberry.field(
+ name="subreddits",
resolver=resolve_subreddits,
description="""
Gets the available subreddits.
diff --git a/reddit-clone/reddit/users/mutations/authenticate.py b/reddit-clone/reddit/users/mutations/authenticate.py
index 8983d5b8..8f8e5bf3 100644
--- a/reddit-clone/reddit/users/mutations/authenticate.py
+++ b/reddit-clone/reddit/users/mutations/authenticate.py
@@ -35,6 +35,7 @@ async def resolve_authenticate(
authenticate = strawberry.mutation(
+ name="authenticate",
resolver=resolve_authenticate,
description="""
Logs the current user in.
diff --git a/reddit-clone/reddit/users/mutations/avatar_remove.py b/reddit-clone/reddit/users/mutations/avatar_remove.py
index 9505484f..1bd1fbb8 100644
--- a/reddit-clone/reddit/users/mutations/avatar_remove.py
+++ b/reddit-clone/reddit/users/mutations/avatar_remove.py
@@ -26,6 +26,7 @@ async def resolve_avatar_remove(info: Info) -> AvatarRemoveResult:
avatar_remove = strawberry.mutation(
+ name="avatar_remove",
resolver=resolve_avatar_remove,
description="""
Removes the current user's avatar.
diff --git a/reddit-clone/reddit/users/mutations/email_change.py b/reddit-clone/reddit/users/mutations/email_change.py
index b08f6036..45303a62 100644
--- a/reddit-clone/reddit/users/mutations/email_change.py
+++ b/reddit-clone/reddit/users/mutations/email_change.py
@@ -34,6 +34,7 @@ async def resolve_email_change(
email_change = strawberry.mutation(
+ name="email_change",
resolver=resolve_email_change,
description="""
Changes the email for the user account
diff --git a/reddit-clone/reddit/users/mutations/email_change_request.py b/reddit-clone/reddit/users/mutations/email_change_request.py
index b16f3a60..17843cec 100644
--- a/reddit-clone/reddit/users/mutations/email_change_request.py
+++ b/reddit-clone/reddit/users/mutations/email_change_request.py
@@ -35,6 +35,7 @@ async def resolve_email_change_request(
email_change_request = strawberry.mutation(
+ name="email_change_request",
resolver=resolve_email_change_request,
description="""
Sends an email change code to
diff --git a/reddit-clone/reddit/users/mutations/password_reset.py b/reddit-clone/reddit/users/mutations/password_reset.py
index 16e25ebf..0afcffc8 100644
--- a/reddit-clone/reddit/users/mutations/password_reset.py
+++ b/reddit-clone/reddit/users/mutations/password_reset.py
@@ -35,6 +35,7 @@ async def resolve_password_reset(
password_reset = strawberry.mutation(
+ name="password_reset",
resolver=resolve_password_reset,
description="""
Resets the password for the user account
diff --git a/reddit-clone/reddit/users/mutations/password_reset_request.py b/reddit-clone/reddit/users/mutations/password_reset_request.py
index 6e2104ef..24b94415 100644
--- a/reddit-clone/reddit/users/mutations/password_reset_request.py
+++ b/reddit-clone/reddit/users/mutations/password_reset_request.py
@@ -35,6 +35,7 @@ async def resolve_password_reset_request(
password_reset_request = strawberry.mutation(
+ name="password_reset_request",
resolver=resolve_password_reset_request,
description="""
Sends a password reset code to the
diff --git a/reddit-clone/reddit/users/mutations/user_block.py b/reddit-clone/reddit/users/mutations/user_block.py
index b3b7b6bc..0634b822 100644
--- a/reddit-clone/reddit/users/mutations/user_block.py
+++ b/reddit-clone/reddit/users/mutations/user_block.py
@@ -32,6 +32,7 @@ async def resolve_user_block(info: Info, input: UserBlockInput) -> UserBlockResu
user_block = strawberry.mutation(
+ name="user_block",
resolver=resolve_user_block,
description="""
Blocks an user account.
diff --git a/reddit-clone/reddit/users/mutations/user_create.py b/reddit-clone/reddit/users/mutations/user_create.py
index 30ca3003..49ca1ab3 100644
--- a/reddit-clone/reddit/users/mutations/user_create.py
+++ b/reddit-clone/reddit/users/mutations/user_create.py
@@ -33,6 +33,7 @@ async def resolve_user_create(info: Info, input: UserCreateInput) -> UserCreateR
user_create = strawberry.mutation(
+ name="user_create",
resolver=resolve_user_create,
description="""
Creates a new user.
diff --git a/reddit-clone/reddit/users/mutations/user_deactivate.py b/reddit-clone/reddit/users/mutations/user_deactivate.py
index 8d339687..20a42786 100644
--- a/reddit-clone/reddit/users/mutations/user_deactivate.py
+++ b/reddit-clone/reddit/users/mutations/user_deactivate.py
@@ -33,6 +33,7 @@ async def resolve_user_deactivate(
user_deactivate = strawberry.mutation(
+ name="user_deactivate",
resolver=resolve_user_deactivate,
description="""
Deactivates the current user.
diff --git a/reddit-clone/reddit/users/mutations/user_update.py b/reddit-clone/reddit/users/mutations/user_update.py
index 3740cebb..314674c9 100644
--- a/reddit-clone/reddit/users/mutations/user_update.py
+++ b/reddit-clone/reddit/users/mutations/user_update.py
@@ -33,6 +33,7 @@ async def resolve_user_update(info: Info, input: UserUpdateInput) -> UserUpdateR
user_update = strawberry.mutation(
+ name="user_update",
resolver=resolve_user_update,
description="""
Updates the current user.
diff --git a/reddit-clone/reddit/users/queries.py b/reddit-clone/reddit/users/queries.py
index 50fe6e13..cc54f45a 100644
--- a/reddit-clone/reddit/users/queries.py
+++ b/reddit-clone/reddit/users/queries.py
@@ -12,6 +12,7 @@ async def resolve_user(info: Info, username: str) -> Optional[UserType]:
user = strawberry.field(
+ name="user",
resolver=resolve_user,
description="""
Gets an user by username.
@@ -24,6 +25,7 @@ async def resolve_current_user(info: Info) -> UserType:
current_user = strawberry.field(
+ name="current_user",
resolver=resolve_current_user,
description="""
Gets the current user.
From 53e09fc36a00e92ba36fed6e9746beba466dbefb Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Sun, 10 Oct 2021 09:37:54 +0530
Subject: [PATCH 060/150] remove explicit field names
---
reddit-clone/reddit/comments/mutations/comment_create.py | 1 -
reddit-clone/reddit/comments/mutations/comment_delete.py | 1 -
reddit-clone/reddit/comments/mutations/comment_update.py | 1 -
reddit-clone/reddit/comments/mutations/comment_vote.py | 1 -
reddit-clone/reddit/posts/mutations/post_create.py | 1 -
reddit-clone/reddit/posts/mutations/post_delete.py | 1 -
reddit-clone/reddit/posts/mutations/post_update.py | 1 -
reddit-clone/reddit/posts/mutations/post_vote.py | 1 -
reddit-clone/reddit/subreddits/mutations/subreddit_create.py | 1 -
reddit-clone/reddit/subreddits/mutations/subreddit_delete.py | 1 -
reddit-clone/reddit/subreddits/mutations/subreddit_join.py | 1 -
reddit-clone/reddit/subreddits/mutations/subreddit_leave.py | 1 -
reddit-clone/reddit/subreddits/mutations/subreddit_update.py | 1 -
reddit-clone/reddit/subreddits/queries.py | 1 -
reddit-clone/reddit/users/mutations/authenticate.py | 1 -
reddit-clone/reddit/users/mutations/avatar_remove.py | 1 -
reddit-clone/reddit/users/mutations/email_change.py | 1 -
reddit-clone/reddit/users/mutations/email_change_request.py | 1 -
reddit-clone/reddit/users/mutations/password_reset.py | 1 -
reddit-clone/reddit/users/mutations/password_reset_request.py | 1 -
reddit-clone/reddit/users/mutations/user_block.py | 1 -
reddit-clone/reddit/users/mutations/user_create.py | 1 -
reddit-clone/reddit/users/mutations/user_deactivate.py | 1 -
reddit-clone/reddit/users/mutations/user_update.py | 1 -
reddit-clone/reddit/users/queries.py | 2 --
25 files changed, 26 deletions(-)
diff --git a/reddit-clone/reddit/comments/mutations/comment_create.py b/reddit-clone/reddit/comments/mutations/comment_create.py
index cbea3ebf..81143ae4 100644
--- a/reddit-clone/reddit/comments/mutations/comment_create.py
+++ b/reddit-clone/reddit/comments/mutations/comment_create.py
@@ -36,7 +36,6 @@ async def resolve_comment_create(
comment_create = strawberry.mutation(
- name="comment_create",
resolver=resolve_comment_create,
description="""
Creates a new comment on a post.
diff --git a/reddit-clone/reddit/comments/mutations/comment_delete.py b/reddit-clone/reddit/comments/mutations/comment_delete.py
index c9973867..4b903360 100644
--- a/reddit-clone/reddit/comments/mutations/comment_delete.py
+++ b/reddit-clone/reddit/comments/mutations/comment_delete.py
@@ -36,7 +36,6 @@ async def resolve_comment_delete(
comment_delete = strawberry.mutation(
- name="comment_delete",
resolver=resolve_comment_delete,
description="""
Deletes a comment on a post.
diff --git a/reddit-clone/reddit/comments/mutations/comment_update.py b/reddit-clone/reddit/comments/mutations/comment_update.py
index a8455283..2d5e738a 100644
--- a/reddit-clone/reddit/comments/mutations/comment_update.py
+++ b/reddit-clone/reddit/comments/mutations/comment_update.py
@@ -37,7 +37,6 @@ async def resolve_comment_update(
comment_update = strawberry.mutation(
- name="comment_update",
resolver=resolve_comment_update,
description="""
Updates a comment on a post.
diff --git a/reddit-clone/reddit/comments/mutations/comment_vote.py b/reddit-clone/reddit/comments/mutations/comment_vote.py
index 3c216e59..5912656b 100644
--- a/reddit-clone/reddit/comments/mutations/comment_vote.py
+++ b/reddit-clone/reddit/comments/mutations/comment_vote.py
@@ -36,7 +36,6 @@ async def resolve_comment_vote(
comment_vote = strawberry.mutation(
- name="comment_vote",
resolver=resolve_comment_vote,
description="""
Creates a vote on a comment.
diff --git a/reddit-clone/reddit/posts/mutations/post_create.py b/reddit-clone/reddit/posts/mutations/post_create.py
index 4fac0a36..2bec4f6b 100644
--- a/reddit-clone/reddit/posts/mutations/post_create.py
+++ b/reddit-clone/reddit/posts/mutations/post_create.py
@@ -34,7 +34,6 @@ async def resolve_post_create(info: Info, input: PostCreateInput) -> PostCreateR
post_create = strawberry.mutation(
- name="post_create",
resolver=resolve_post_create,
description="""
Creates a new post in a Subreddit.
diff --git a/reddit-clone/reddit/posts/mutations/post_delete.py b/reddit-clone/reddit/posts/mutations/post_delete.py
index 074410b8..369967c2 100644
--- a/reddit-clone/reddit/posts/mutations/post_delete.py
+++ b/reddit-clone/reddit/posts/mutations/post_delete.py
@@ -33,7 +33,6 @@ async def resolve_post_delete(info: Info, input: PostDeleteInput) -> PostDeleteR
post_delete = strawberry.mutation(
- name="post_delete",
resolver=resolve_post_delete,
description="""
Deletes a post in a Subreddit.
diff --git a/reddit-clone/reddit/posts/mutations/post_update.py b/reddit-clone/reddit/posts/mutations/post_update.py
index 4fa38a42..3e318437 100644
--- a/reddit-clone/reddit/posts/mutations/post_update.py
+++ b/reddit-clone/reddit/posts/mutations/post_update.py
@@ -35,7 +35,6 @@ async def resolve_post_update(info: Info, input: PostUpdateInput) -> PostUpdateR
post_update = strawberry.mutation(
- name="post_update",
resolver=resolve_post_update,
description="""
Updates a post in a Subreddit.
diff --git a/reddit-clone/reddit/posts/mutations/post_vote.py b/reddit-clone/reddit/posts/mutations/post_vote.py
index 2a86c47d..4d82fcea 100644
--- a/reddit-clone/reddit/posts/mutations/post_vote.py
+++ b/reddit-clone/reddit/posts/mutations/post_vote.py
@@ -33,7 +33,6 @@ async def resolve_post_vote(info: Info, input: PostVoteInput) -> PostVoteResult:
post_vote = strawberry.mutation(
- name="post_vote",
resolver=resolve_post_vote,
description="""
Creates a vote on a post.
diff --git a/reddit-clone/reddit/subreddits/mutations/subreddit_create.py b/reddit-clone/reddit/subreddits/mutations/subreddit_create.py
index 0e65be74..0625b8a9 100644
--- a/reddit-clone/reddit/subreddits/mutations/subreddit_create.py
+++ b/reddit-clone/reddit/subreddits/mutations/subreddit_create.py
@@ -35,7 +35,6 @@ async def resolve_subreddit_create(
subreddit_create = strawberry.mutation(
- name="subreddit_create",
resolver=resolve_subreddit_create,
description="""
Creates a new Subreddit.
diff --git a/reddit-clone/reddit/subreddits/mutations/subreddit_delete.py b/reddit-clone/reddit/subreddits/mutations/subreddit_delete.py
index 86dd7ec4..4ce08dc9 100644
--- a/reddit-clone/reddit/subreddits/mutations/subreddit_delete.py
+++ b/reddit-clone/reddit/subreddits/mutations/subreddit_delete.py
@@ -34,7 +34,6 @@ async def resolve_subreddit_delete(
subreddit_delete = strawberry.mutation(
- name="subreddit_delete",
resolver=resolve_subreddit_delete,
description="""
Deletes a Subreddit.
diff --git a/reddit-clone/reddit/subreddits/mutations/subreddit_join.py b/reddit-clone/reddit/subreddits/mutations/subreddit_join.py
index 3a35ab8b..a81c9b57 100644
--- a/reddit-clone/reddit/subreddits/mutations/subreddit_join.py
+++ b/reddit-clone/reddit/subreddits/mutations/subreddit_join.py
@@ -34,7 +34,6 @@ async def resolve_subreddit_join(
subreddit_join = strawberry.mutation(
- name="subreddit_join",
resolver=resolve_subreddit_join,
description="""
Creates a new Subreddit-User relationship.
diff --git a/reddit-clone/reddit/subreddits/mutations/subreddit_leave.py b/reddit-clone/reddit/subreddits/mutations/subreddit_leave.py
index f49786eb..582be748 100644
--- a/reddit-clone/reddit/subreddits/mutations/subreddit_leave.py
+++ b/reddit-clone/reddit/subreddits/mutations/subreddit_leave.py
@@ -34,7 +34,6 @@ async def resolve_subreddit_leave(
subreddit_leave = strawberry.mutation(
- name="subreddit_leave",
resolver=resolve_subreddit_leave,
description="""
Deletes a Subreddit-User relationship.
diff --git a/reddit-clone/reddit/subreddits/mutations/subreddit_update.py b/reddit-clone/reddit/subreddits/mutations/subreddit_update.py
index 40f5ca18..0c97b382 100644
--- a/reddit-clone/reddit/subreddits/mutations/subreddit_update.py
+++ b/reddit-clone/reddit/subreddits/mutations/subreddit_update.py
@@ -36,7 +36,6 @@ async def resolve_subreddit_update(
subreddit_update = strawberry.mutation(
- name="subreddit_update",
resolver=resolve_subreddit_update,
description="""
Updates a Subreddit.
diff --git a/reddit-clone/reddit/subreddits/queries.py b/reddit-clone/reddit/subreddits/queries.py
index f4135180..77998552 100644
--- a/reddit-clone/reddit/subreddits/queries.py
+++ b/reddit-clone/reddit/subreddits/queries.py
@@ -12,7 +12,6 @@ async def resolve_subreddits(info: Info) -> List[SubredditType]:
subreddits = strawberry.field(
- name="subreddits",
resolver=resolve_subreddits,
description="""
Gets the available subreddits.
diff --git a/reddit-clone/reddit/users/mutations/authenticate.py b/reddit-clone/reddit/users/mutations/authenticate.py
index 8f8e5bf3..8983d5b8 100644
--- a/reddit-clone/reddit/users/mutations/authenticate.py
+++ b/reddit-clone/reddit/users/mutations/authenticate.py
@@ -35,7 +35,6 @@ async def resolve_authenticate(
authenticate = strawberry.mutation(
- name="authenticate",
resolver=resolve_authenticate,
description="""
Logs the current user in.
diff --git a/reddit-clone/reddit/users/mutations/avatar_remove.py b/reddit-clone/reddit/users/mutations/avatar_remove.py
index 1bd1fbb8..9505484f 100644
--- a/reddit-clone/reddit/users/mutations/avatar_remove.py
+++ b/reddit-clone/reddit/users/mutations/avatar_remove.py
@@ -26,7 +26,6 @@ async def resolve_avatar_remove(info: Info) -> AvatarRemoveResult:
avatar_remove = strawberry.mutation(
- name="avatar_remove",
resolver=resolve_avatar_remove,
description="""
Removes the current user's avatar.
diff --git a/reddit-clone/reddit/users/mutations/email_change.py b/reddit-clone/reddit/users/mutations/email_change.py
index 45303a62..b08f6036 100644
--- a/reddit-clone/reddit/users/mutations/email_change.py
+++ b/reddit-clone/reddit/users/mutations/email_change.py
@@ -34,7 +34,6 @@ async def resolve_email_change(
email_change = strawberry.mutation(
- name="email_change",
resolver=resolve_email_change,
description="""
Changes the email for the user account
diff --git a/reddit-clone/reddit/users/mutations/email_change_request.py b/reddit-clone/reddit/users/mutations/email_change_request.py
index 17843cec..b16f3a60 100644
--- a/reddit-clone/reddit/users/mutations/email_change_request.py
+++ b/reddit-clone/reddit/users/mutations/email_change_request.py
@@ -35,7 +35,6 @@ async def resolve_email_change_request(
email_change_request = strawberry.mutation(
- name="email_change_request",
resolver=resolve_email_change_request,
description="""
Sends an email change code to
diff --git a/reddit-clone/reddit/users/mutations/password_reset.py b/reddit-clone/reddit/users/mutations/password_reset.py
index 0afcffc8..16e25ebf 100644
--- a/reddit-clone/reddit/users/mutations/password_reset.py
+++ b/reddit-clone/reddit/users/mutations/password_reset.py
@@ -35,7 +35,6 @@ async def resolve_password_reset(
password_reset = strawberry.mutation(
- name="password_reset",
resolver=resolve_password_reset,
description="""
Resets the password for the user account
diff --git a/reddit-clone/reddit/users/mutations/password_reset_request.py b/reddit-clone/reddit/users/mutations/password_reset_request.py
index 24b94415..6e2104ef 100644
--- a/reddit-clone/reddit/users/mutations/password_reset_request.py
+++ b/reddit-clone/reddit/users/mutations/password_reset_request.py
@@ -35,7 +35,6 @@ async def resolve_password_reset_request(
password_reset_request = strawberry.mutation(
- name="password_reset_request",
resolver=resolve_password_reset_request,
description="""
Sends a password reset code to the
diff --git a/reddit-clone/reddit/users/mutations/user_block.py b/reddit-clone/reddit/users/mutations/user_block.py
index 0634b822..b3b7b6bc 100644
--- a/reddit-clone/reddit/users/mutations/user_block.py
+++ b/reddit-clone/reddit/users/mutations/user_block.py
@@ -32,7 +32,6 @@ async def resolve_user_block(info: Info, input: UserBlockInput) -> UserBlockResu
user_block = strawberry.mutation(
- name="user_block",
resolver=resolve_user_block,
description="""
Blocks an user account.
diff --git a/reddit-clone/reddit/users/mutations/user_create.py b/reddit-clone/reddit/users/mutations/user_create.py
index 49ca1ab3..30ca3003 100644
--- a/reddit-clone/reddit/users/mutations/user_create.py
+++ b/reddit-clone/reddit/users/mutations/user_create.py
@@ -33,7 +33,6 @@ async def resolve_user_create(info: Info, input: UserCreateInput) -> UserCreateR
user_create = strawberry.mutation(
- name="user_create",
resolver=resolve_user_create,
description="""
Creates a new user.
diff --git a/reddit-clone/reddit/users/mutations/user_deactivate.py b/reddit-clone/reddit/users/mutations/user_deactivate.py
index 20a42786..8d339687 100644
--- a/reddit-clone/reddit/users/mutations/user_deactivate.py
+++ b/reddit-clone/reddit/users/mutations/user_deactivate.py
@@ -33,7 +33,6 @@ async def resolve_user_deactivate(
user_deactivate = strawberry.mutation(
- name="user_deactivate",
resolver=resolve_user_deactivate,
description="""
Deactivates the current user.
diff --git a/reddit-clone/reddit/users/mutations/user_update.py b/reddit-clone/reddit/users/mutations/user_update.py
index 314674c9..3740cebb 100644
--- a/reddit-clone/reddit/users/mutations/user_update.py
+++ b/reddit-clone/reddit/users/mutations/user_update.py
@@ -33,7 +33,6 @@ async def resolve_user_update(info: Info, input: UserUpdateInput) -> UserUpdateR
user_update = strawberry.mutation(
- name="user_update",
resolver=resolve_user_update,
description="""
Updates the current user.
diff --git a/reddit-clone/reddit/users/queries.py b/reddit-clone/reddit/users/queries.py
index cc54f45a..50fe6e13 100644
--- a/reddit-clone/reddit/users/queries.py
+++ b/reddit-clone/reddit/users/queries.py
@@ -12,7 +12,6 @@ async def resolve_user(info: Info, username: str) -> Optional[UserType]:
user = strawberry.field(
- name="user",
resolver=resolve_user,
description="""
Gets an user by username.
@@ -25,7 +24,6 @@ async def resolve_current_user(info: Info) -> UserType:
current_user = strawberry.field(
- name="current_user",
resolver=resolve_current_user,
description="""
Gets the current user.
From 076777fbae21043cac54aba75ac2ea3d3013152a Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Sun, 10 Oct 2021 10:08:12 +0530
Subject: [PATCH 061/150] setup email client
---
reddit-clone/.env.example | 3 +++
reddit-clone/poetry.lock | 18 +++++++++++++-
reddit-clone/pyproject.toml | 1 +
reddit-clone/reddit/emails.py | 42 +++++++++++++++++++++++++++++++++
reddit-clone/reddit/settings.py | 7 ++++++
5 files changed, 70 insertions(+), 1 deletion(-)
create mode 100644 reddit-clone/.env.example
create mode 100644 reddit-clone/reddit/emails.py
diff --git a/reddit-clone/.env.example b/reddit-clone/.env.example
new file mode 100644
index 00000000..44462925
--- /dev/null
+++ b/reddit-clone/.env.example
@@ -0,0 +1,3 @@
+DEBUG=
+DATABASE_URI=
+SECRET_KEY=
diff --git a/reddit-clone/poetry.lock b/reddit-clone/poetry.lock
index 0728f1a4..38bd324c 100644
--- a/reddit-clone/poetry.lock
+++ b/reddit-clone/poetry.lock
@@ -1,3 +1,15 @@
+[[package]]
+name = "aiosmtplib"
+version = "1.1.6"
+description = "asyncio SMTP client"
+category = "main"
+optional = false
+python-versions = ">=3.5.2,<4.0.0"
+
+[package.extras]
+uvloop = ["uvloop (>=0.13,<0.15)"]
+docs = ["sphinx (>=2,<4)", "sphinx_autodoc_typehints (>=1.7.0,<2.0.0)"]
+
[[package]]
name = "alembic"
version = "1.7.3"
@@ -615,9 +627,13 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytes
[metadata]
lock-version = "1.1"
python-versions = "^3.7"
-content-hash = "c7f68ad613b93e290f94e02da38f6c0c46e7b049d42fcf911543dc7762ac2c97"
+content-hash = "4bd91cde71e77298fc4bece81a46d4fbe0dc6e526c22ac3daa5f3b392eda8780"
[metadata.files]
+aiosmtplib = [
+ {file = "aiosmtplib-1.1.6-py3-none-any.whl", hash = "sha256:84174765778b2c5e0e207fbce0a769202fcf0c3de81faa87cc03551a6333bfa9"},
+ {file = "aiosmtplib-1.1.6.tar.gz", hash = "sha256:d138fe6ffecbc9e6320269690b9ac0b75e540ef96e8f5c77d4a306760014dce2"},
+]
alembic = [
{file = "alembic-1.7.3-py3-none-any.whl", hash = "sha256:d0c580041f9f6487d5444df672a83da9be57398f39d6c1802bbedec6fefbeef6"},
{file = "alembic-1.7.3.tar.gz", hash = "sha256:bc5bdf03d1b9814ee4d72adc0b19df2123f6c50a60c1ea761733f3640feedb8d"},
diff --git a/reddit-clone/pyproject.toml b/reddit-clone/pyproject.toml
index 0bca5049..b3ca7d3f 100644
--- a/reddit-clone/pyproject.toml
+++ b/reddit-clone/pyproject.toml
@@ -14,6 +14,7 @@ strawberry-graphql = {extras = ["asgi"], version = "^0.77.0"}
fastapi = "^0.68.1"
starlette = "^0.14"
graphql-relay = "^3.1.0"
+aiosmtplib = "^1.1.6"
[tool.poetry.dev-dependencies]
diff --git a/reddit-clone/reddit/emails.py b/reddit-clone/reddit/emails.py
new file mode 100644
index 00000000..9c68ef87
--- /dev/null
+++ b/reddit-clone/reddit/emails.py
@@ -0,0 +1,42 @@
+from email.mime.multipart import MIMEMultipart
+from email.mime.text import MIMEText
+from typing import Optional
+
+from aiosmtplib import send
+
+from reddit.settings import (
+ MAIL_SENDER,
+ MAIL_USERNAME,
+ MAIL_PASSWORD,
+ MAIL_HOST,
+ MAIL_PORT,
+)
+
+
+async def send_message(
+ recipient: str, subject: str, content: str, html_content: Optional[str] = None
+) -> None:
+ """
+ Sends a multipart email to the provided
+ recipient asynchronously.
+ """
+ message = MIMEMultipart()
+ message["From"] = None
+ message["To"] = recipient
+ message["Subject"] = subject
+
+ plain_text = MIMEText(content)
+ message.attach(plain_text)
+
+ if html_content is not None:
+ html_text = MIMEText(html_content, "html")
+ message.attach(html_text)
+
+ await send(
+ message=message,
+ sender=MAIL_SENDER,
+ username=MAIL_USERNAME,
+ password=MAIL_PASSWORD,
+ hostname=MAIL_HOST,
+ port=MAIL_PORT,
+ )
diff --git a/reddit-clone/reddit/settings.py b/reddit-clone/reddit/settings.py
index 86fbcac9..0586fd26 100644
--- a/reddit-clone/reddit/settings.py
+++ b/reddit-clone/reddit/settings.py
@@ -8,3 +8,10 @@
# sqlalchemy database url.
DATABASE_URI: str = config("DATABASE_URI", cast=str)
+
+# mail configuration.
+MAIL_HOST: str = config("MAIL_HOST", cast=str)
+MAIL_PORT: int = config("MAIL_PORT", cast=int)
+MAIL_USERNAME: str = config("MAIL_USERNAME", cast=str)
+MAIL_PASSWORD: str = config("MAIL_PASSWORD", cast=str)
+MAIL_SENDER: str = config("MAIL_SENDER", cast=str)
From 8033676af47ca4141ebf4d71b113f15f035b60f3 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Sun, 10 Oct 2021 10:10:10 +0530
Subject: [PATCH 062/150] update example env file
---
reddit-clone/.env.example | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/reddit-clone/.env.example b/reddit-clone/.env.example
index 44462925..ddc0b3bf 100644
--- a/reddit-clone/.env.example
+++ b/reddit-clone/.env.example
@@ -1,3 +1,8 @@
DEBUG=
DATABASE_URI=
SECRET_KEY=
+MAIL_HOST=
+MAIL_PORT=
+MAIL_USERNAME=
+MAIL_PASSWORD=
+MAIL_SENDER=
From 3d15d8ad77165ae51b5f67f54b2ce744c7087541 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Sun, 10 Oct 2021 10:12:17 +0530
Subject: [PATCH 063/150] rename email helper
---
reddit-clone/reddit/__init__.py | 2 ++
reddit-clone/reddit/database.py | 2 ++
reddit-clone/reddit/emails.py | 4 +++-
reddit-clone/reddit/schema.py | 2 ++
4 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/reddit-clone/reddit/__init__.py b/reddit-clone/reddit/__init__.py
index 5b1cccc8..976dd360 100644
--- a/reddit-clone/reddit/__init__.py
+++ b/reddit-clone/reddit/__init__.py
@@ -11,6 +11,8 @@
from reddit.schema import schema
from reddit.users.loaders import load_users
+__all__ = ("app",)
+
class MyGraphQL(GraphQL):
async def get_context(
diff --git a/reddit-clone/reddit/database.py b/reddit-clone/reddit/database.py
index 03c74a2e..0e0d6db2 100644
--- a/reddit-clone/reddit/database.py
+++ b/reddit-clone/reddit/database.py
@@ -11,6 +11,8 @@
session_factory = sessionmaker(bind=engine, class_=AsyncSession)
+__all__ = ("get_session", "Base")
+
@asynccontextmanager
async def get_session() -> AsyncGenerator[AsyncSession, None]:
diff --git a/reddit-clone/reddit/emails.py b/reddit-clone/reddit/emails.py
index 9c68ef87..81768e20 100644
--- a/reddit-clone/reddit/emails.py
+++ b/reddit-clone/reddit/emails.py
@@ -12,8 +12,10 @@
MAIL_PORT,
)
+__all__ = ("send_mail",)
-async def send_message(
+
+async def send_mail(
recipient: str, subject: str, content: str, html_content: Optional[str] = None
) -> None:
"""
diff --git a/reddit-clone/reddit/schema.py b/reddit-clone/reddit/schema.py
index af03218e..1298ec23 100644
--- a/reddit-clone/reddit/schema.py
+++ b/reddit-clone/reddit/schema.py
@@ -8,6 +8,8 @@
from reddit.subreddits.mutations import SubredditMutation
from reddit.users.mutations import UserMutation
+__all__ = ("schema",)
+
@strawberry.type
class Query(BaseQuery, UserQuery, SubredditQuery):
From afafbbd8c8b7635e7a07d5983917d16e2669a8f8 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Sun, 10 Oct 2021 10:22:00 +0530
Subject: [PATCH 064/150] update README.md
---
reddit-clone/README.md | 17 +++++++++--------
1 file changed, 9 insertions(+), 8 deletions(-)
diff --git a/reddit-clone/README.md b/reddit-clone/README.md
index 4f4f15ee..7b3b198a 100644
--- a/reddit-clone/README.md
+++ b/reddit-clone/README.md
@@ -7,17 +7,18 @@ most of the features that Strawberry gives us.
## Tech Stack used:
-- Strawberry GraphQL
-- FastAPI w/ Starlette
-- SQLAlchemy (asyncio)
-- Alembic for migrations
+- [Strawberry GraphQL](https://github.com/strawberry-graphql/strawberry)
+- [FastAPI](https://github.com/tiangolo/fastapi) w/ [Starlette](https://github.com/encode/starlette)
+- [SQLAlchemy](https://github.com/sqlalchemy/sqlalchemy) (asyncio)
+- [Alembic](https://github.com/sqlalchemy/alembic) for migrations
## Features at a glance
-- Error handling with unions
-- Authorization with the permissions API
-- Batch loading with dataloaders
-- Pagination with SQLAlchemy
+- [ ] Error handling with unions
+- [ ] Authorization with the permissions API
+- [ ] Batch loading with dataloaders
+- [ ] Pagination with SQLAlchemy
+- [x] modular codebase
## How to use
From fc47ef70eb7039f0713b3221af8f8508e45dc72c Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Sun, 10 Oct 2021 10:35:10 +0530
Subject: [PATCH 065/150] update setup instructions
---
reddit-clone/.env.example | 7 +++----
reddit-clone/README.md | 19 ++++++++-----------
reddit-clone/reddit/__init__.py | 6 +++---
reddit-clone/reddit/database.py | 4 ++--
reddit-clone/reddit/emails.py | 18 ++++++------------
reddit-clone/reddit/settings.py | 12 +++++++-----
6 files changed, 29 insertions(+), 37 deletions(-)
diff --git a/reddit-clone/.env.example b/reddit-clone/.env.example
index ddc0b3bf..c120265a 100644
--- a/reddit-clone/.env.example
+++ b/reddit-clone/.env.example
@@ -1,8 +1,7 @@
+DATABASE_URI=postgresql+asyncpg://localhost/db_name?user=postgres&password=postgres
+MAIL_HOST=127.0.0.1
+MAIL_PORT=25
DEBUG=
-DATABASE_URI=
-SECRET_KEY=
-MAIL_HOST=
-MAIL_PORT=
MAIL_USERNAME=
MAIL_PASSWORD=
MAIL_SENDER=
diff --git a/reddit-clone/README.md b/reddit-clone/README.md
index 7b3b198a..32029f4f 100644
--- a/reddit-clone/README.md
+++ b/reddit-clone/README.md
@@ -30,17 +30,14 @@ Use [poetry](https://python-poetry.org/) to install dependencies:
poetry install
```
-2. setup database
- This example uses a PostgreSQL database. Make sure you have it installed
- on your machine, and configure the `DATABASE_URI` environment variable.
+2. Setup database
+ This example needs a PostgreSQL database. Make sure you have one on your machine.
- An example database URL:
+3. Configure envionment variables
+ Next up, you'd need to setup your environment variables. There is an example [`.env`](.env.example) file
+ which you can reference! (Blank variables are optional)
- ```text
- postgresql+asyncpg://localhost/db_name?user=user&password=password
- ```
-
-3. Run migrations
+4. Run migrations
Run [alembic](https://alembic.sqlalchemy.org/en/latest/) to create the database
and populate it with movie data:
@@ -49,7 +46,7 @@ and populate it with movie data:
poetry run alembic upgrade head
```
-4. Run the server
+5. Run the server
Run [uvicorn](https://www.uvicorn.org/) to run the server:
@@ -57,4 +54,4 @@ Run [uvicorn](https://www.uvicorn.org/) to run the server:
poetry run uvicorn reddit:app --reload
```
-The GraphQL API should now be available at http://localhost:8000/graphql
+You can now explore the GraphQL API here: http://localhost:8000/graphql
diff --git a/reddit-clone/reddit/__init__.py b/reddit-clone/reddit/__init__.py
index 976dd360..1fd94fb2 100644
--- a/reddit-clone/reddit/__init__.py
+++ b/reddit-clone/reddit/__init__.py
@@ -7,7 +7,7 @@
from strawberry.dataloader import DataLoader
from strawberry.asgi import GraphQL
-from reddit.settings import DEBUG
+from reddit import settings
from reddit.schema import schema
from reddit.users.loaders import load_users
@@ -29,9 +29,9 @@ def create_application() -> FastAPI:
:return: The created application.
"""
- application = FastAPI(title="Reddit GraphQL", debug=DEBUG)
+ application = FastAPI(title="Reddit GraphQL", debug=settings.DEBUG)
- graphql_app = MyGraphQL(schema=schema, graphiql=True, debug=DEBUG)
+ graphql_app = MyGraphQL(schema=schema, graphiql=True, debug=settings.DEBUG)
application.add_route(path="/graphql", route=graphql_app)
diff --git a/reddit-clone/reddit/database.py b/reddit-clone/reddit/database.py
index 0e0d6db2..2f7b7c23 100644
--- a/reddit-clone/reddit/database.py
+++ b/reddit-clone/reddit/database.py
@@ -5,9 +5,9 @@
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
from sqlalchemy.ext.declarative import declarative_base
-from .settings import DATABASE_URI
+from reddit import settings
-engine = create_async_engine(DATABASE_URI, future=True)
+engine = create_async_engine(settings.DATABASE_URI, future=True)
session_factory = sessionmaker(bind=engine, class_=AsyncSession)
diff --git a/reddit-clone/reddit/emails.py b/reddit-clone/reddit/emails.py
index 81768e20..7a222fab 100644
--- a/reddit-clone/reddit/emails.py
+++ b/reddit-clone/reddit/emails.py
@@ -4,13 +4,7 @@
from aiosmtplib import send
-from reddit.settings import (
- MAIL_SENDER,
- MAIL_USERNAME,
- MAIL_PASSWORD,
- MAIL_HOST,
- MAIL_PORT,
-)
+from reddit import settings
__all__ = ("send_mail",)
@@ -36,9 +30,9 @@ async def send_mail(
await send(
message=message,
- sender=MAIL_SENDER,
- username=MAIL_USERNAME,
- password=MAIL_PASSWORD,
- hostname=MAIL_HOST,
- port=MAIL_PORT,
+ sender=settings.MAIL_SENDER,
+ username=settings.MAIL_USERNAME,
+ password=settings.MAIL_PASSWORD,
+ hostname=settings.MAIL_HOST,
+ port=settings.MAIL_PORT,
)
diff --git a/reddit-clone/reddit/settings.py b/reddit-clone/reddit/settings.py
index 0586fd26..12ac8eee 100644
--- a/reddit-clone/reddit/settings.py
+++ b/reddit-clone/reddit/settings.py
@@ -1,3 +1,5 @@
+from typing import Optional
+
from starlette.config import Config
@@ -6,12 +8,12 @@
# whether the application is in development mode.
DEBUG: bool = config("DEBUG", cast=bool, default=False)
-# sqlalchemy database url.
+# SQLAlchemy database URL.
DATABASE_URI: str = config("DATABASE_URI", cast=str)
-# mail configuration.
+# mail client configuration.
MAIL_HOST: str = config("MAIL_HOST", cast=str)
MAIL_PORT: int = config("MAIL_PORT", cast=int)
-MAIL_USERNAME: str = config("MAIL_USERNAME", cast=str)
-MAIL_PASSWORD: str = config("MAIL_PASSWORD", cast=str)
-MAIL_SENDER: str = config("MAIL_SENDER", cast=str)
+MAIL_USERNAME: Optional[str] = config("MAIL_USERNAME", cast=str, default=None)
+MAIL_PASSWORD: Optional[str] = config("MAIL_PASSWORD", cast=str, default=None)
+MAIL_SENDER: Optional[str] = config("MAIL_SENDER", cast=str, default=None)
From ce67dd3940729b7d7b58ad4ca21e507c7e9ad810 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Sun, 10 Oct 2021 10:44:21 +0530
Subject: [PATCH 066/150] fix model: comment
---
reddit-clone/reddit/comments/models.py | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/reddit-clone/reddit/comments/models.py b/reddit-clone/reddit/comments/models.py
index 62f3bce7..80ec648e 100644
--- a/reddit-clone/reddit/comments/models.py
+++ b/reddit-clone/reddit/comments/models.py
@@ -10,7 +10,7 @@
class Comment(Base):
"""
- Represents a Comment in a thread.
+ Represents a Comment on a post.
"""
__tablename__ = "comments"
@@ -23,6 +23,8 @@ class Comment(Base):
user_id: Optional[int] = Column(Integer, ForeignKey("users.id"))
+ post_id: int = Column(Integer, ForeignKey("posts.id"))
+
replies: List[Comment] = relationship(
"Comment", back_populates="parent", lazy="dynamic"
)
From c05389915357f8cc8967a8dc24ef3e4fe229ee57 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Sun, 10 Oct 2021 10:45:52 +0530
Subject: [PATCH 067/150] update README.md
---
reddit-clone/README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/reddit-clone/README.md b/reddit-clone/README.md
index 32029f4f..391d7c20 100644
--- a/reddit-clone/README.md
+++ b/reddit-clone/README.md
@@ -14,7 +14,7 @@ most of the features that Strawberry gives us.
## Features at a glance
-- [ ] Error handling with unions
+- [ ] Error modelling within the schema
- [ ] Authorization with the permissions API
- [ ] Batch loading with dataloaders
- [ ] Pagination with SQLAlchemy
From da35bd9f14061b8ff136ee3783d4931f0cae4196 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Sun, 10 Oct 2021 15:09:34 +0530
Subject: [PATCH 068/150] use schematics for data validation
---
reddit-clone/poetry.lock | 14 +++++++++++++-
reddit-clone/pyproject.toml | 1 +
reddit-clone/reddit/users/serializers.py | 0
3 files changed, 14 insertions(+), 1 deletion(-)
create mode 100644 reddit-clone/reddit/users/serializers.py
diff --git a/reddit-clone/poetry.lock b/reddit-clone/poetry.lock
index 38bd324c..130dacf8 100644
--- a/reddit-clone/poetry.lock
+++ b/reddit-clone/poetry.lock
@@ -434,6 +434,14 @@ category = "dev"
optional = false
python-versions = "*"
+[[package]]
+name = "schematics"
+version = "2.1.1"
+description = "Python Data Structures for Humans"
+category = "main"
+optional = false
+python-versions = "*"
+
[[package]]
name = "six"
version = "1.16.0"
@@ -627,7 +635,7 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytes
[metadata]
lock-version = "1.1"
python-versions = "^3.7"
-content-hash = "4bd91cde71e77298fc4bece81a46d4fbe0dc6e526c22ac3daa5f3b392eda8780"
+content-hash = "87e854daf2789f690051ca6542bccb2e94c7c4f3cb2ba41db8f2ea1aa2413913"
[metadata.files]
aiosmtplib = [
@@ -984,6 +992,10 @@ regex = [
{file = "regex-2021.9.30-cp39-cp39-win_amd64.whl", hash = "sha256:3b71213ec3bad9a5a02e049f2ec86b3d7c3e350129ae0f4e2f99c12b5da919ed"},
{file = "regex-2021.9.30.tar.gz", hash = "sha256:81e125d9ba54c34579e4539a967e976a3c56150796674aec318b1b2f49251be7"},
]
+schematics = [
+ {file = "schematics-2.1.1-py2.py3-none-any.whl", hash = "sha256:be2d451bfb86789975e5ec0864aec569b63cea9010f0d24cbbd992a4e564c647"},
+ {file = "schematics-2.1.1.tar.gz", hash = "sha256:34c87f51a25063bb498ae1cc201891b134cfcb329baf9e9f4f3ae869b767560f"},
+]
six = [
{file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
diff --git a/reddit-clone/pyproject.toml b/reddit-clone/pyproject.toml
index b3ca7d3f..bb99aa3a 100644
--- a/reddit-clone/pyproject.toml
+++ b/reddit-clone/pyproject.toml
@@ -15,6 +15,7 @@ fastapi = "^0.68.1"
starlette = "^0.14"
graphql-relay = "^3.1.0"
aiosmtplib = "^1.1.6"
+schematics = "^2.1.1"
[tool.poetry.dev-dependencies]
diff --git a/reddit-clone/reddit/users/serializers.py b/reddit-clone/reddit/users/serializers.py
new file mode 100644
index 00000000..e69de29b
From 560c8e6e58a214b2c428ece764e3af2a23fb236a Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Sun, 10 Oct 2021 17:03:54 +0530
Subject: [PATCH 069/150] add user service
---
reddit-clone/poetry.lock | 101 +++++++++++++++++-
reddit-clone/pyproject.toml | 1 +
reddit-clone/reddit/base/services.py | 12 +++
reddit-clone/reddit/users/models.py | 2 +
.../reddit/users/mutations/authenticate.py | 11 +-
.../reddit/users/mutations/user_create.py | 9 +-
reddit-clone/reddit/users/serializers.py | 0
reddit-clone/reddit/users/services.py | 55 ++++++++++
8 files changed, 186 insertions(+), 5 deletions(-)
create mode 100644 reddit-clone/reddit/base/services.py
delete mode 100644 reddit-clone/reddit/users/serializers.py
create mode 100644 reddit-clone/reddit/users/services.py
diff --git a/reddit-clone/poetry.lock b/reddit-clone/poetry.lock
index 130dacf8..7c62cc79 100644
--- a/reddit-clone/poetry.lock
+++ b/reddit-clone/poetry.lock
@@ -27,6 +27,22 @@ SQLAlchemy = ">=1.3.0"
[package.extras]
tz = ["python-dateutil"]
+[[package]]
+name = "argon2-cffi"
+version = "21.1.0"
+description = "The secure Argon2 password hashing algorithm."
+category = "main"
+optional = false
+python-versions = ">=3.5"
+
+[package.dependencies]
+cffi = ">=1.0.0"
+
+[package.extras]
+dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pytest", "sphinx", "furo", "wheel", "pre-commit"]
+docs = ["sphinx", "furo"]
+tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pytest"]
+
[[package]]
name = "asgiref"
version = "3.4.1"
@@ -107,6 +123,17 @@ category = "main"
optional = false
python-versions = "*"
+[[package]]
+name = "cffi"
+version = "1.14.6"
+description = "Foreign Function Interface for Python calling C code."
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+pycparser = "*"
+
[[package]]
name = "click"
version = "8.0.1"
@@ -354,6 +381,14 @@ category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+[[package]]
+name = "pycparser"
+version = "2.20"
+description = "C parser in Python"
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+
[[package]]
name = "pydantic"
version = "1.8.2"
@@ -635,7 +670,7 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytes
[metadata]
lock-version = "1.1"
python-versions = "^3.7"
-content-hash = "87e854daf2789f690051ca6542bccb2e94c7c4f3cb2ba41db8f2ea1aa2413913"
+content-hash = "4fc35f54f9b160fc75e33293576947b81057524eb6786aa11dd8cdff8d2befb1"
[metadata.files]
aiosmtplib = [
@@ -646,6 +681,19 @@ alembic = [
{file = "alembic-1.7.3-py3-none-any.whl", hash = "sha256:d0c580041f9f6487d5444df672a83da9be57398f39d6c1802bbedec6fefbeef6"},
{file = "alembic-1.7.3.tar.gz", hash = "sha256:bc5bdf03d1b9814ee4d72adc0b19df2123f6c50a60c1ea761733f3640feedb8d"},
]
+argon2-cffi = [
+ {file = "argon2-cffi-21.1.0.tar.gz", hash = "sha256:f710b61103d1a1f692ca3ecbd1373e28aa5e545ac625ba067ff2feca1b2bb870"},
+ {file = "argon2_cffi-21.1.0-cp35-abi3-macosx_10_14_x86_64.whl", hash = "sha256:217b4f0f853ccbbb5045242946ad2e162e396064575860141b71a85eb47e475a"},
+ {file = "argon2_cffi-21.1.0-cp35-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:fa7e7d1fc22514a32b1761fdfa1882b6baa5c36bb3ef557bdd69e6fc9ba14a41"},
+ {file = "argon2_cffi-21.1.0-cp35-abi3-win32.whl", hash = "sha256:e4d8f0ae1524b7b0372a3e574a2561cbdddb3fdb6c28b70a72868189bda19659"},
+ {file = "argon2_cffi-21.1.0-cp35-abi3-win_amd64.whl", hash = "sha256:65213a9174320a1aee03fe826596e0620783966b49eb636955958b3074e87ff9"},
+ {file = "argon2_cffi-21.1.0-pp36-pypy36_pp73-macosx_10_7_x86_64.whl", hash = "sha256:245f64a203012b144b7b8c8ea6d468cb02b37caa5afee5ba4a10c80599334f6a"},
+ {file = "argon2_cffi-21.1.0-pp36-pypy36_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4ad152c418f7eb640eac41ac815534e6aa61d1624530b8e7779114ecfbf327f8"},
+ {file = "argon2_cffi-21.1.0-pp36-pypy36_pp73-win32.whl", hash = "sha256:bc513db2283c385ea4da31a2cd039c33380701f376f4edd12fe56db118a3b21a"},
+ {file = "argon2_cffi-21.1.0-pp37-pypy37_pp73-macosx_10_7_x86_64.whl", hash = "sha256:c7a7c8cc98ac418002090e4add5bebfff1b915ea1cb459c578cd8206fef10378"},
+ {file = "argon2_cffi-21.1.0-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:165cadae5ac1e26644f5ade3bd9c18d89963be51d9ea8817bd671006d7909057"},
+ {file = "argon2_cffi-21.1.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:566ffb581bbd9db5562327aee71b2eda24a1c15b23a356740abe3c011bbe0dcb"},
+]
asgiref = [
{file = "asgiref-3.4.1-py3-none-any.whl", hash = "sha256:ffc141aa908e6f175673e7b1b3b7af4fdb0ecb738fc5c8b88f69f055c2415214"},
{file = "asgiref-3.4.1.tar.gz", hash = "sha256:4ef1ab46b484e3c706329cedeff284a5d40824200638503f5768edb6de7d58e9"},
@@ -677,6 +725,53 @@ 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"},
]
+cffi = [
+ {file = "cffi-1.14.6-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:22b9c3c320171c108e903d61a3723b51e37aaa8c81255b5e7ce102775bd01e2c"},
+ {file = "cffi-1.14.6-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:f0c5d1acbfca6ebdd6b1e3eded8d261affb6ddcf2186205518f1428b8569bb99"},
+ {file = "cffi-1.14.6-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:99f27fefe34c37ba9875f224a8f36e31d744d8083e00f520f133cab79ad5e819"},
+ {file = "cffi-1.14.6-cp27-cp27m-win32.whl", hash = "sha256:55af55e32ae468e9946f741a5d51f9896da6b9bf0bbdd326843fec05c730eb20"},
+ {file = "cffi-1.14.6-cp27-cp27m-win_amd64.whl", hash = "sha256:7bcac9a2b4fdbed2c16fa5681356d7121ecabf041f18d97ed5b8e0dd38a80224"},
+ {file = "cffi-1.14.6-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:ed38b924ce794e505647f7c331b22a693bee1538fdf46b0222c4717b42f744e7"},
+ {file = "cffi-1.14.6-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:e22dcb48709fc51a7b58a927391b23ab37eb3737a98ac4338e2448bef8559b33"},
+ {file = "cffi-1.14.6-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:aedb15f0a5a5949ecb129a82b72b19df97bbbca024081ed2ef88bd5c0a610534"},
+ {file = "cffi-1.14.6-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:48916e459c54c4a70e52745639f1db524542140433599e13911b2f329834276a"},
+ {file = "cffi-1.14.6-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:f627688813d0a4140153ff532537fbe4afea5a3dffce1f9deb7f91f848a832b5"},
+ {file = "cffi-1.14.6-cp35-cp35m-win32.whl", hash = "sha256:f0010c6f9d1a4011e429109fda55a225921e3206e7f62a0c22a35344bfd13cca"},
+ {file = "cffi-1.14.6-cp35-cp35m-win_amd64.whl", hash = "sha256:57e555a9feb4a8460415f1aac331a2dc833b1115284f7ded7278b54afc5bd218"},
+ {file = "cffi-1.14.6-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:e8c6a99be100371dbb046880e7a282152aa5d6127ae01783e37662ef73850d8f"},
+ {file = "cffi-1.14.6-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:19ca0dbdeda3b2615421d54bef8985f72af6e0c47082a8d26122adac81a95872"},
+ {file = "cffi-1.14.6-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:d950695ae4381ecd856bcaf2b1e866720e4ab9a1498cba61c602e56630ca7195"},
+ {file = "cffi-1.14.6-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9dc245e3ac69c92ee4c167fbdd7428ec1956d4e754223124991ef29eb57a09d"},
+ {file = "cffi-1.14.6-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a8661b2ce9694ca01c529bfa204dbb144b275a31685a075ce123f12331be790b"},
+ {file = "cffi-1.14.6-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b315d709717a99f4b27b59b021e6207c64620790ca3e0bde636a6c7f14618abb"},
+ {file = "cffi-1.14.6-cp36-cp36m-win32.whl", hash = "sha256:80b06212075346b5546b0417b9f2bf467fea3bfe7352f781ffc05a8ab24ba14a"},
+ {file = "cffi-1.14.6-cp36-cp36m-win_amd64.whl", hash = "sha256:a9da7010cec5a12193d1af9872a00888f396aba3dc79186604a09ea3ee7c029e"},
+ {file = "cffi-1.14.6-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:4373612d59c404baeb7cbd788a18b2b2a8331abcc84c3ba40051fcd18b17a4d5"},
+ {file = "cffi-1.14.6-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:f10afb1004f102c7868ebfe91c28f4a712227fe4cb24974350ace1f90e1febbf"},
+ {file = "cffi-1.14.6-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:fd4305f86f53dfd8cd3522269ed7fc34856a8ee3709a5e28b2836b2db9d4cd69"},
+ {file = "cffi-1.14.6-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6d6169cb3c6c2ad50db5b868db6491a790300ade1ed5d1da29289d73bbe40b56"},
+ {file = "cffi-1.14.6-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5d4b68e216fc65e9fe4f524c177b54964af043dde734807586cf5435af84045c"},
+ {file = "cffi-1.14.6-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33791e8a2dc2953f28b8d8d300dde42dd929ac28f974c4b4c6272cb2955cb762"},
+ {file = "cffi-1.14.6-cp37-cp37m-win32.whl", hash = "sha256:0c0591bee64e438883b0c92a7bed78f6290d40bf02e54c5bf0978eaf36061771"},
+ {file = "cffi-1.14.6-cp37-cp37m-win_amd64.whl", hash = "sha256:8eb687582ed7cd8c4bdbff3df6c0da443eb89c3c72e6e5dcdd9c81729712791a"},
+ {file = "cffi-1.14.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ba6f2b3f452e150945d58f4badd92310449876c4c954836cfb1803bdd7b422f0"},
+ {file = "cffi-1.14.6-cp38-cp38-manylinux1_i686.whl", hash = "sha256:64fda793737bc4037521d4899be780534b9aea552eb673b9833b01f945904c2e"},
+ {file = "cffi-1.14.6-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:9f3e33c28cd39d1b655ed1ba7247133b6f7fc16fa16887b120c0c670e35ce346"},
+ {file = "cffi-1.14.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26bb2549b72708c833f5abe62b756176022a7b9a7f689b571e74c8478ead51dc"},
+ {file = "cffi-1.14.6-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eb687a11f0a7a1839719edd80f41e459cc5366857ecbed383ff376c4e3cc6afd"},
+ {file = "cffi-1.14.6-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d2ad4d668a5c0645d281dcd17aff2be3212bc109b33814bbb15c4939f44181cc"},
+ {file = "cffi-1.14.6-cp38-cp38-win32.whl", hash = "sha256:487d63e1454627c8e47dd230025780e91869cfba4c753a74fda196a1f6ad6548"},
+ {file = "cffi-1.14.6-cp38-cp38-win_amd64.whl", hash = "sha256:c33d18eb6e6bc36f09d793c0dc58b0211fccc6ae5149b808da4a62660678b156"},
+ {file = "cffi-1.14.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:06c54a68935738d206570b20da5ef2b6b6d92b38ef3ec45c5422c0ebaf338d4d"},
+ {file = "cffi-1.14.6-cp39-cp39-manylinux1_i686.whl", hash = "sha256:f174135f5609428cc6e1b9090f9268f5c8935fddb1b25ccb8255a2d50de6789e"},
+ {file = "cffi-1.14.6-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:f3ebe6e73c319340830a9b2825d32eb6d8475c1dac020b4f0aa774ee3b898d1c"},
+ {file = "cffi-1.14.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c8d896becff2fa653dc4438b54a5a25a971d1f4110b32bd3068db3722c80202"},
+ {file = "cffi-1.14.6-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4922cd707b25e623b902c86188aca466d3620892db76c0bdd7b99a3d5e61d35f"},
+ {file = "cffi-1.14.6-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c9e005e9bd57bc987764c32a1bee4364c44fdc11a3cc20a40b93b444984f2b87"},
+ {file = "cffi-1.14.6-cp39-cp39-win32.whl", hash = "sha256:eb9e2a346c5238a30a746893f23a9535e700f8192a68c07c0258e7ece6ff3728"},
+ {file = "cffi-1.14.6-cp39-cp39-win_amd64.whl", hash = "sha256:818014c754cd3dba7229c0f5884396264d51ffb87ec86e927ef0be140bfdb0d2"},
+ {file = "cffi-1.14.6.tar.gz", hash = "sha256:c9a875ce9d7fe32887784274dd533c57909b7b1dcadcc128a2ac21331a9765dd"},
+]
click = [
{file = "click-8.0.1-py3-none-any.whl", hash = "sha256:fba402a4a47334742d782209a7c79bc448911afe1149d07bdabdf480b3e2f4b6"},
{file = "click-8.0.1.tar.gz", hash = "sha256:8c04c11192119b1ef78ea049e0a6f0463e4c48ef00a30160c704337586f3ad7a"},
@@ -875,6 +970,10 @@ pycodestyle = [
{file = "pycodestyle-2.7.0-py2.py3-none-any.whl", hash = "sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068"},
{file = "pycodestyle-2.7.0.tar.gz", hash = "sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef"},
]
+pycparser = [
+ {file = "pycparser-2.20-py2.py3-none-any.whl", hash = "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"},
+ {file = "pycparser-2.20.tar.gz", hash = "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0"},
+]
pydantic = [
{file = "pydantic-1.8.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:05ddfd37c1720c392f4e0d43c484217b7521558302e7069ce8d318438d297739"},
{file = "pydantic-1.8.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:a7c6002203fe2c5a1b5cbb141bb85060cbff88c2d78eccbc72d97eb7022c43e4"},
diff --git a/reddit-clone/pyproject.toml b/reddit-clone/pyproject.toml
index bb99aa3a..55ff1b14 100644
--- a/reddit-clone/pyproject.toml
+++ b/reddit-clone/pyproject.toml
@@ -16,6 +16,7 @@ starlette = "^0.14"
graphql-relay = "^3.1.0"
aiosmtplib = "^1.1.6"
schematics = "^2.1.1"
+argon2-cffi = "^21.1.0"
[tool.poetry.dev-dependencies]
diff --git a/reddit-clone/reddit/base/services.py b/reddit-clone/reddit/base/services.py
new file mode 100644
index 00000000..6001c2db
--- /dev/null
+++ b/reddit-clone/reddit/base/services.py
@@ -0,0 +1,12 @@
+from sqlalchemy.ext.asyncio.session import AsyncSession
+
+
+class BaseService:
+ """
+ The base class that every service must subclass.
+ Services are classes which implement business logic
+ and improve code reusability.
+ """
+
+ def __init__(self, session: AsyncSession) -> None:
+ self._session = session
diff --git a/reddit-clone/reddit/users/models.py b/reddit-clone/reddit/users/models.py
index a1b8c0bf..df4b8601 100644
--- a/reddit-clone/reddit/users/models.py
+++ b/reddit-clone/reddit/users/models.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
from typing import List
from sqlalchemy import Column, String, Integer, ForeignKey
diff --git a/reddit-clone/reddit/users/mutations/authenticate.py b/reddit-clone/reddit/users/mutations/authenticate.py
index 8983d5b8..346f22ef 100644
--- a/reddit-clone/reddit/users/mutations/authenticate.py
+++ b/reddit-clone/reddit/users/mutations/authenticate.py
@@ -1,9 +1,11 @@
-from typing import Union
+from typing import Union, cast
import strawberry
from strawberry.types import Info
+from reddit.database import get_session
from reddit.users.types import UserType
+from reddit.users.services import UserService
__all__ = ("authenticate",)
@@ -31,7 +33,12 @@ class AuthenticateError:
async def resolve_authenticate(
info: Info, input: AuthenticateInput
) -> AuthenticateResult:
- pass
+ async with get_session() as session:
+ service = UserService(session=session)
+ user = await service.authenticate(**input)
+ if user is None:
+ return AuthenticateError(error="Invalid credentials provided.")
+ return AuthenticateSuccess(user=cast(UserType, user))
authenticate = strawberry.mutation(
diff --git a/reddit-clone/reddit/users/mutations/user_create.py b/reddit-clone/reddit/users/mutations/user_create.py
index 30ca3003..f6ea7c86 100644
--- a/reddit-clone/reddit/users/mutations/user_create.py
+++ b/reddit-clone/reddit/users/mutations/user_create.py
@@ -1,9 +1,11 @@
-from typing import Union
+from typing import Union, cast
import strawberry
from strawberry.types import Info
+from reddit.database import get_session
from reddit.users.types import UserType
+from reddit.users.services import UserService
__all__ = ("user_create",)
@@ -29,7 +31,10 @@ class UserCreateError:
async def resolve_user_create(info: Info, input: UserCreateInput) -> UserCreateResult:
- pass
+ async with get_session() as session:
+ service = UserService(session=session)
+ user = await service.create_user(**input)
+ return UserCreateSuccess(user=cast(UserType, user))
user_create = strawberry.mutation(
diff --git a/reddit-clone/reddit/users/serializers.py b/reddit-clone/reddit/users/serializers.py
deleted file mode 100644
index e69de29b..00000000
diff --git a/reddit-clone/reddit/users/services.py b/reddit-clone/reddit/users/services.py
new file mode 100644
index 00000000..38695c85
--- /dev/null
+++ b/reddit-clone/reddit/users/services.py
@@ -0,0 +1,55 @@
+from typing import Optional
+
+from argon2 import PasswordHasher
+from argon2.exceptions import VerifyMismatchError
+from sqlalchemy import select
+
+from reddit.base.services import BaseService
+from reddit.users.models import User
+
+
+class UserService(BaseService):
+ _hasher = PasswordHasher()
+
+ async def create_user(self, username: str, password: str, email: str) -> User:
+ """
+ Creates an user instance.
+ """
+ # TODO: validate input before creating
+ user = User(
+ email=email,
+ username=username,
+ password=self._hasher.hash(password=password),
+ )
+ self._session.add(instance=user)
+ await self._session.commit()
+ await self._session.refresh(instance=user)
+ return user
+
+ async def get_by_username(self, username: str) -> Optional[User]:
+ """
+ Gets an user by their username.
+ """
+ stmt = select(User).filter_by(username=username).first()
+ return await self._session.execute(stmt)
+
+ async def authenticate(self, username: str, password: str) -> Optional[User]:
+ """
+ Checks whether the given credentials match
+ and returns the associated user instance.
+ """
+ user = self.get_by_username(username=username)
+ if user is None:
+ return user
+ try:
+ self._hasher.verify(hash=user.password, password=password)
+ except VerifyMismatchError:
+ return None
+
+ if self._hasher.check_needs_rehash(hash=user.password):
+ # recalculate the user's password hash.
+ user.password = self._hasher.hash(password=password)
+ self._session.add(instance=user)
+ await self._session.commit()
+ await self._session.refresh(instance=user)
+ return user
From 81f281b01b067f6a3241a737a2ca10f446181ca2 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Sun, 10 Oct 2021 18:33:40 +0530
Subject: [PATCH 070/150] add user services
---
reddit-clone/reddit/base/services.py | 12 --
.../reddit/users/mutations/authenticate.py | 11 +-
.../reddit/users/mutations/email_change.py | 1 +
.../reddit/users/mutations/user_create.py | 9 +-
reddit-clone/reddit/users/services.py | 122 +++++++++++-------
5 files changed, 78 insertions(+), 77 deletions(-)
delete mode 100644 reddit-clone/reddit/base/services.py
diff --git a/reddit-clone/reddit/base/services.py b/reddit-clone/reddit/base/services.py
deleted file mode 100644
index 6001c2db..00000000
--- a/reddit-clone/reddit/base/services.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from sqlalchemy.ext.asyncio.session import AsyncSession
-
-
-class BaseService:
- """
- The base class that every service must subclass.
- Services are classes which implement business logic
- and improve code reusability.
- """
-
- def __init__(self, session: AsyncSession) -> None:
- self._session = session
diff --git a/reddit-clone/reddit/users/mutations/authenticate.py b/reddit-clone/reddit/users/mutations/authenticate.py
index 346f22ef..8983d5b8 100644
--- a/reddit-clone/reddit/users/mutations/authenticate.py
+++ b/reddit-clone/reddit/users/mutations/authenticate.py
@@ -1,11 +1,9 @@
-from typing import Union, cast
+from typing import Union
import strawberry
from strawberry.types import Info
-from reddit.database import get_session
from reddit.users.types import UserType
-from reddit.users.services import UserService
__all__ = ("authenticate",)
@@ -33,12 +31,7 @@ class AuthenticateError:
async def resolve_authenticate(
info: Info, input: AuthenticateInput
) -> AuthenticateResult:
- async with get_session() as session:
- service = UserService(session=session)
- user = await service.authenticate(**input)
- if user is None:
- return AuthenticateError(error="Invalid credentials provided.")
- return AuthenticateSuccess(user=cast(UserType, user))
+ pass
authenticate = strawberry.mutation(
diff --git a/reddit-clone/reddit/users/mutations/email_change.py b/reddit-clone/reddit/users/mutations/email_change.py
index b08f6036..5abd905d 100644
--- a/reddit-clone/reddit/users/mutations/email_change.py
+++ b/reddit-clone/reddit/users/mutations/email_change.py
@@ -12,6 +12,7 @@
class EmailChangeInput:
email: str
change_code: str
+ password: str
@strawberry.type
diff --git a/reddit-clone/reddit/users/mutations/user_create.py b/reddit-clone/reddit/users/mutations/user_create.py
index f6ea7c86..30ca3003 100644
--- a/reddit-clone/reddit/users/mutations/user_create.py
+++ b/reddit-clone/reddit/users/mutations/user_create.py
@@ -1,11 +1,9 @@
-from typing import Union, cast
+from typing import Union
import strawberry
from strawberry.types import Info
-from reddit.database import get_session
from reddit.users.types import UserType
-from reddit.users.services import UserService
__all__ = ("user_create",)
@@ -31,10 +29,7 @@ class UserCreateError:
async def resolve_user_create(info: Info, input: UserCreateInput) -> UserCreateResult:
- async with get_session() as session:
- service = UserService(session=session)
- user = await service.create_user(**input)
- return UserCreateSuccess(user=cast(UserType, user))
+ pass
user_create = strawberry.mutation(
diff --git a/reddit-clone/reddit/users/services.py b/reddit-clone/reddit/users/services.py
index 38695c85..0b1ce40f 100644
--- a/reddit-clone/reddit/users/services.py
+++ b/reddit-clone/reddit/users/services.py
@@ -1,55 +1,79 @@
from typing import Optional
-from argon2 import PasswordHasher
-from argon2.exceptions import VerifyMismatchError
-from sqlalchemy import select
+from sqlalchemy.ext.asyncio import AsyncSession
-from reddit.base.services import BaseService
from reddit.users.models import User
-class UserService(BaseService):
- _hasher = PasswordHasher()
-
- async def create_user(self, username: str, password: str, email: str) -> User:
- """
- Creates an user instance.
- """
- # TODO: validate input before creating
- user = User(
- email=email,
- username=username,
- password=self._hasher.hash(password=password),
- )
- self._session.add(instance=user)
- await self._session.commit()
- await self._session.refresh(instance=user)
- return user
-
- async def get_by_username(self, username: str) -> Optional[User]:
- """
- Gets an user by their username.
- """
- stmt = select(User).filter_by(username=username).first()
- return await self._session.execute(stmt)
-
- async def authenticate(self, username: str, password: str) -> Optional[User]:
- """
- Checks whether the given credentials match
- and returns the associated user instance.
- """
- user = self.get_by_username(username=username)
- if user is None:
- return user
- try:
- self._hasher.verify(hash=user.password, password=password)
- except VerifyMismatchError:
- return None
-
- if self._hasher.check_needs_rehash(hash=user.password):
- # recalculate the user's password hash.
- user.password = self._hasher.hash(password=password)
- self._session.add(instance=user)
- await self._session.commit()
- await self._session.refresh(instance=user)
- return user
+async def authenticate(
+ session: AsyncSession, username: str, password: str
+) -> Optional[User]:
+ """
+ Checks if the provided credentials are valid.
+ """
+
+
+async def create_user(
+ session: AsyncSession, email: str, username: str, password: str
+) -> User:
+ """
+ Creates a new user instance.
+ """
+ user = User(email=email, username=username, password=str)
+ session.add(instance=user)
+ await session.commit()
+ await session.refresh(instance=user)
+ return user
+
+
+async def update_user(session: AsyncSession, user: User):
+ """
+ Updates the given user instance.
+ """
+
+
+async def remove_avatar(session: AsyncSession, user: User):
+ """
+ Removes the avatar for the given user instance.
+ """
+
+
+async def request_change_email(session: AsyncSession, email: str, password: str):
+ """
+ Sends an email change code to the user's new email.
+ """
+
+
+async def change_email(
+ session: AsyncSession, email: str, change_code: str, password: str
+):
+ """
+ Changes the email for the given user instance.
+ """
+
+
+async def request_reset_password(session: AsyncSession, email: str):
+ """
+ Sends a password reset code to the given email, if it
+ actually exists.
+ """
+
+
+async def reset_password(
+ session: AsyncSession, password: str, reset_code: str, email: str
+):
+ """
+ Resets the password for the given user instance.
+ """
+
+
+async def block_user(session: AsyncSession, user_id: int, user: User):
+ """
+ Blocks the user account for the given user instance.
+ """
+
+
+async def deactivate_user(session: AsyncSession, password: str, user: User):
+ """
+ Deactivates the given user instance.
+ """
From eb7bd5f8b9db3eab3b964ec931f66ba2068a1988 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Sun, 10 Oct 2021 18:34:39 +0530
Subject: [PATCH 071/150] update user services
---
reddit-clone/reddit/users/services.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/reddit-clone/reddit/users/services.py b/reddit-clone/reddit/users/services.py
index 0b1ce40f..639f8613 100644
--- a/reddit-clone/reddit/users/services.py
+++ b/reddit-clone/reddit/users/services.py
@@ -19,7 +19,7 @@ async def create_user(
"""
Creates a new user instance.
"""
- user = User(email=email, username=username, password=str)
+ user = User(email=email, username=username, password=password)
session.add(instance=user)
await session.commit()
await session.refresh(instance=user)
From 9c221ba29097ea76e18402a8cbb4b79efad8cb4e Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Sun, 10 Oct 2021 18:59:42 +0530
Subject: [PATCH 072/150] update user services
---
reddit-clone/poetry.lock | 119 ++++----------------------
reddit-clone/pyproject.toml | 2 +-
reddit-clone/reddit/users/models.py | 28 +++++-
reddit-clone/reddit/users/services.py | 18 +++-
4 files changed, 61 insertions(+), 106 deletions(-)
diff --git a/reddit-clone/poetry.lock b/reddit-clone/poetry.lock
index 7c62cc79..9f44abcf 100644
--- a/reddit-clone/poetry.lock
+++ b/reddit-clone/poetry.lock
@@ -27,22 +27,6 @@ SQLAlchemy = ">=1.3.0"
[package.extras]
tz = ["python-dateutil"]
-[[package]]
-name = "argon2-cffi"
-version = "21.1.0"
-description = "The secure Argon2 password hashing algorithm."
-category = "main"
-optional = false
-python-versions = ">=3.5"
-
-[package.dependencies]
-cffi = ">=1.0.0"
-
-[package.extras]
-dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pytest", "sphinx", "furo", "wheel", "pre-commit"]
-docs = ["sphinx", "furo"]
-tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pytest"]
-
[[package]]
name = "asgiref"
version = "3.4.1"
@@ -123,17 +107,6 @@ category = "main"
optional = false
python-versions = "*"
-[[package]]
-name = "cffi"
-version = "1.14.6"
-description = "Foreign Function Interface for Python calling C code."
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.dependencies]
-pycparser = "*"
-
[[package]]
name = "click"
version = "8.0.1"
@@ -353,6 +326,20 @@ category = "main"
optional = false
python-versions = "*"
+[[package]]
+name = "passlib"
+version = "1.7.4"
+description = "comprehensive password hashing framework supporting over 30 schemes"
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.extras]
+argon2 = ["argon2-cffi (>=18.2.0)"]
+bcrypt = ["bcrypt (>=3.1.0)"]
+build_docs = ["sphinx (>=1.6)", "sphinxcontrib-fulltoc (>=1.2.0)", "cloud-sptheme (>=1.10.1)"]
+totp = ["cryptography"]
+
[[package]]
name = "pathspec"
version = "0.9.0"
@@ -381,14 +368,6 @@ category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
-[[package]]
-name = "pycparser"
-version = "2.20"
-description = "C parser in Python"
-category = "main"
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
-
[[package]]
name = "pydantic"
version = "1.8.2"
@@ -670,7 +649,7 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytes
[metadata]
lock-version = "1.1"
python-versions = "^3.7"
-content-hash = "4fc35f54f9b160fc75e33293576947b81057524eb6786aa11dd8cdff8d2befb1"
+content-hash = "03a61f70a8c276ecdeb0f5dcd3c519f5b4dbe2187ce15e4a68c607f5e594c7e0"
[metadata.files]
aiosmtplib = [
@@ -681,19 +660,6 @@ alembic = [
{file = "alembic-1.7.3-py3-none-any.whl", hash = "sha256:d0c580041f9f6487d5444df672a83da9be57398f39d6c1802bbedec6fefbeef6"},
{file = "alembic-1.7.3.tar.gz", hash = "sha256:bc5bdf03d1b9814ee4d72adc0b19df2123f6c50a60c1ea761733f3640feedb8d"},
]
-argon2-cffi = [
- {file = "argon2-cffi-21.1.0.tar.gz", hash = "sha256:f710b61103d1a1f692ca3ecbd1373e28aa5e545ac625ba067ff2feca1b2bb870"},
- {file = "argon2_cffi-21.1.0-cp35-abi3-macosx_10_14_x86_64.whl", hash = "sha256:217b4f0f853ccbbb5045242946ad2e162e396064575860141b71a85eb47e475a"},
- {file = "argon2_cffi-21.1.0-cp35-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:fa7e7d1fc22514a32b1761fdfa1882b6baa5c36bb3ef557bdd69e6fc9ba14a41"},
- {file = "argon2_cffi-21.1.0-cp35-abi3-win32.whl", hash = "sha256:e4d8f0ae1524b7b0372a3e574a2561cbdddb3fdb6c28b70a72868189bda19659"},
- {file = "argon2_cffi-21.1.0-cp35-abi3-win_amd64.whl", hash = "sha256:65213a9174320a1aee03fe826596e0620783966b49eb636955958b3074e87ff9"},
- {file = "argon2_cffi-21.1.0-pp36-pypy36_pp73-macosx_10_7_x86_64.whl", hash = "sha256:245f64a203012b144b7b8c8ea6d468cb02b37caa5afee5ba4a10c80599334f6a"},
- {file = "argon2_cffi-21.1.0-pp36-pypy36_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4ad152c418f7eb640eac41ac815534e6aa61d1624530b8e7779114ecfbf327f8"},
- {file = "argon2_cffi-21.1.0-pp36-pypy36_pp73-win32.whl", hash = "sha256:bc513db2283c385ea4da31a2cd039c33380701f376f4edd12fe56db118a3b21a"},
- {file = "argon2_cffi-21.1.0-pp37-pypy37_pp73-macosx_10_7_x86_64.whl", hash = "sha256:c7a7c8cc98ac418002090e4add5bebfff1b915ea1cb459c578cd8206fef10378"},
- {file = "argon2_cffi-21.1.0-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:165cadae5ac1e26644f5ade3bd9c18d89963be51d9ea8817bd671006d7909057"},
- {file = "argon2_cffi-21.1.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:566ffb581bbd9db5562327aee71b2eda24a1c15b23a356740abe3c011bbe0dcb"},
-]
asgiref = [
{file = "asgiref-3.4.1-py3-none-any.whl", hash = "sha256:ffc141aa908e6f175673e7b1b3b7af4fdb0ecb738fc5c8b88f69f055c2415214"},
{file = "asgiref-3.4.1.tar.gz", hash = "sha256:4ef1ab46b484e3c706329cedeff284a5d40824200638503f5768edb6de7d58e9"},
@@ -725,53 +691,6 @@ 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"},
]
-cffi = [
- {file = "cffi-1.14.6-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:22b9c3c320171c108e903d61a3723b51e37aaa8c81255b5e7ce102775bd01e2c"},
- {file = "cffi-1.14.6-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:f0c5d1acbfca6ebdd6b1e3eded8d261affb6ddcf2186205518f1428b8569bb99"},
- {file = "cffi-1.14.6-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:99f27fefe34c37ba9875f224a8f36e31d744d8083e00f520f133cab79ad5e819"},
- {file = "cffi-1.14.6-cp27-cp27m-win32.whl", hash = "sha256:55af55e32ae468e9946f741a5d51f9896da6b9bf0bbdd326843fec05c730eb20"},
- {file = "cffi-1.14.6-cp27-cp27m-win_amd64.whl", hash = "sha256:7bcac9a2b4fdbed2c16fa5681356d7121ecabf041f18d97ed5b8e0dd38a80224"},
- {file = "cffi-1.14.6-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:ed38b924ce794e505647f7c331b22a693bee1538fdf46b0222c4717b42f744e7"},
- {file = "cffi-1.14.6-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:e22dcb48709fc51a7b58a927391b23ab37eb3737a98ac4338e2448bef8559b33"},
- {file = "cffi-1.14.6-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:aedb15f0a5a5949ecb129a82b72b19df97bbbca024081ed2ef88bd5c0a610534"},
- {file = "cffi-1.14.6-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:48916e459c54c4a70e52745639f1db524542140433599e13911b2f329834276a"},
- {file = "cffi-1.14.6-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:f627688813d0a4140153ff532537fbe4afea5a3dffce1f9deb7f91f848a832b5"},
- {file = "cffi-1.14.6-cp35-cp35m-win32.whl", hash = "sha256:f0010c6f9d1a4011e429109fda55a225921e3206e7f62a0c22a35344bfd13cca"},
- {file = "cffi-1.14.6-cp35-cp35m-win_amd64.whl", hash = "sha256:57e555a9feb4a8460415f1aac331a2dc833b1115284f7ded7278b54afc5bd218"},
- {file = "cffi-1.14.6-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:e8c6a99be100371dbb046880e7a282152aa5d6127ae01783e37662ef73850d8f"},
- {file = "cffi-1.14.6-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:19ca0dbdeda3b2615421d54bef8985f72af6e0c47082a8d26122adac81a95872"},
- {file = "cffi-1.14.6-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:d950695ae4381ecd856bcaf2b1e866720e4ab9a1498cba61c602e56630ca7195"},
- {file = "cffi-1.14.6-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9dc245e3ac69c92ee4c167fbdd7428ec1956d4e754223124991ef29eb57a09d"},
- {file = "cffi-1.14.6-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a8661b2ce9694ca01c529bfa204dbb144b275a31685a075ce123f12331be790b"},
- {file = "cffi-1.14.6-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b315d709717a99f4b27b59b021e6207c64620790ca3e0bde636a6c7f14618abb"},
- {file = "cffi-1.14.6-cp36-cp36m-win32.whl", hash = "sha256:80b06212075346b5546b0417b9f2bf467fea3bfe7352f781ffc05a8ab24ba14a"},
- {file = "cffi-1.14.6-cp36-cp36m-win_amd64.whl", hash = "sha256:a9da7010cec5a12193d1af9872a00888f396aba3dc79186604a09ea3ee7c029e"},
- {file = "cffi-1.14.6-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:4373612d59c404baeb7cbd788a18b2b2a8331abcc84c3ba40051fcd18b17a4d5"},
- {file = "cffi-1.14.6-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:f10afb1004f102c7868ebfe91c28f4a712227fe4cb24974350ace1f90e1febbf"},
- {file = "cffi-1.14.6-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:fd4305f86f53dfd8cd3522269ed7fc34856a8ee3709a5e28b2836b2db9d4cd69"},
- {file = "cffi-1.14.6-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6d6169cb3c6c2ad50db5b868db6491a790300ade1ed5d1da29289d73bbe40b56"},
- {file = "cffi-1.14.6-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5d4b68e216fc65e9fe4f524c177b54964af043dde734807586cf5435af84045c"},
- {file = "cffi-1.14.6-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33791e8a2dc2953f28b8d8d300dde42dd929ac28f974c4b4c6272cb2955cb762"},
- {file = "cffi-1.14.6-cp37-cp37m-win32.whl", hash = "sha256:0c0591bee64e438883b0c92a7bed78f6290d40bf02e54c5bf0978eaf36061771"},
- {file = "cffi-1.14.6-cp37-cp37m-win_amd64.whl", hash = "sha256:8eb687582ed7cd8c4bdbff3df6c0da443eb89c3c72e6e5dcdd9c81729712791a"},
- {file = "cffi-1.14.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ba6f2b3f452e150945d58f4badd92310449876c4c954836cfb1803bdd7b422f0"},
- {file = "cffi-1.14.6-cp38-cp38-manylinux1_i686.whl", hash = "sha256:64fda793737bc4037521d4899be780534b9aea552eb673b9833b01f945904c2e"},
- {file = "cffi-1.14.6-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:9f3e33c28cd39d1b655ed1ba7247133b6f7fc16fa16887b120c0c670e35ce346"},
- {file = "cffi-1.14.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26bb2549b72708c833f5abe62b756176022a7b9a7f689b571e74c8478ead51dc"},
- {file = "cffi-1.14.6-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eb687a11f0a7a1839719edd80f41e459cc5366857ecbed383ff376c4e3cc6afd"},
- {file = "cffi-1.14.6-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d2ad4d668a5c0645d281dcd17aff2be3212bc109b33814bbb15c4939f44181cc"},
- {file = "cffi-1.14.6-cp38-cp38-win32.whl", hash = "sha256:487d63e1454627c8e47dd230025780e91869cfba4c753a74fda196a1f6ad6548"},
- {file = "cffi-1.14.6-cp38-cp38-win_amd64.whl", hash = "sha256:c33d18eb6e6bc36f09d793c0dc58b0211fccc6ae5149b808da4a62660678b156"},
- {file = "cffi-1.14.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:06c54a68935738d206570b20da5ef2b6b6d92b38ef3ec45c5422c0ebaf338d4d"},
- {file = "cffi-1.14.6-cp39-cp39-manylinux1_i686.whl", hash = "sha256:f174135f5609428cc6e1b9090f9268f5c8935fddb1b25ccb8255a2d50de6789e"},
- {file = "cffi-1.14.6-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:f3ebe6e73c319340830a9b2825d32eb6d8475c1dac020b4f0aa774ee3b898d1c"},
- {file = "cffi-1.14.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c8d896becff2fa653dc4438b54a5a25a971d1f4110b32bd3068db3722c80202"},
- {file = "cffi-1.14.6-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4922cd707b25e623b902c86188aca466d3620892db76c0bdd7b99a3d5e61d35f"},
- {file = "cffi-1.14.6-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c9e005e9bd57bc987764c32a1bee4364c44fdc11a3cc20a40b93b444984f2b87"},
- {file = "cffi-1.14.6-cp39-cp39-win32.whl", hash = "sha256:eb9e2a346c5238a30a746893f23a9535e700f8192a68c07c0258e7ece6ff3728"},
- {file = "cffi-1.14.6-cp39-cp39-win_amd64.whl", hash = "sha256:818014c754cd3dba7229c0f5884396264d51ffb87ec86e927ef0be140bfdb0d2"},
- {file = "cffi-1.14.6.tar.gz", hash = "sha256:c9a875ce9d7fe32887784274dd533c57909b7b1dcadcc128a2ac21331a9765dd"},
-]
click = [
{file = "click-8.0.1-py3-none-any.whl", hash = "sha256:fba402a4a47334742d782209a7c79bc448911afe1149d07bdabdf480b3e2f4b6"},
{file = "click-8.0.1.tar.gz", hash = "sha256:8c04c11192119b1ef78ea049e0a6f0463e4c48ef00a30160c704337586f3ad7a"},
@@ -958,6 +877,10 @@ 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"},
]
+passlib = [
+ {file = "passlib-1.7.4-py2.py3-none-any.whl", hash = "sha256:aa6bca462b8d8bda89c70b382f0c298a20b5560af6cbfa2dce410c0a2fb669f1"},
+ {file = "passlib-1.7.4.tar.gz", hash = "sha256:defd50f72b65c5402ab2c573830a6978e5f202ad0d984793c8dde2c4152ebe04"},
+]
pathspec = [
{file = "pathspec-0.9.0-py2.py3-none-any.whl", hash = "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a"},
{file = "pathspec-0.9.0.tar.gz", hash = "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"},
@@ -970,10 +893,6 @@ pycodestyle = [
{file = "pycodestyle-2.7.0-py2.py3-none-any.whl", hash = "sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068"},
{file = "pycodestyle-2.7.0.tar.gz", hash = "sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef"},
]
-pycparser = [
- {file = "pycparser-2.20-py2.py3-none-any.whl", hash = "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"},
- {file = "pycparser-2.20.tar.gz", hash = "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0"},
-]
pydantic = [
{file = "pydantic-1.8.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:05ddfd37c1720c392f4e0d43c484217b7521558302e7069ce8d318438d297739"},
{file = "pydantic-1.8.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:a7c6002203fe2c5a1b5cbb141bb85060cbff88c2d78eccbc72d97eb7022c43e4"},
diff --git a/reddit-clone/pyproject.toml b/reddit-clone/pyproject.toml
index 55ff1b14..3ddcc6d4 100644
--- a/reddit-clone/pyproject.toml
+++ b/reddit-clone/pyproject.toml
@@ -16,7 +16,7 @@ starlette = "^0.14"
graphql-relay = "^3.1.0"
aiosmtplib = "^1.1.6"
schematics = "^2.1.1"
-argon2-cffi = "^21.1.0"
+passlib = "^1.7.4"
[tool.poetry.dev-dependencies]
diff --git a/reddit-clone/reddit/users/models.py b/reddit-clone/reddit/users/models.py
index df4b8601..8c4168b0 100644
--- a/reddit-clone/reddit/users/models.py
+++ b/reddit-clone/reddit/users/models.py
@@ -1,8 +1,10 @@
from __future__ import annotations
-from typing import List
+from typing import List, Optional
-from sqlalchemy import Column, String, Integer, ForeignKey
+from passlib.hash import argon2
+from sqlalchemy import select, Column, String, Integer, ForeignKey
+from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import relationship
from ..database import Base
@@ -41,6 +43,28 @@ class User(Base):
def __repr__(self) -> str:
return f""
+ @classmethod
+ async def by_username(cls, session: AsyncSession, username: str) -> Optional[User]:
+ """
+ Gets an user by their username.
+ """
+ query = select(User).filter_by(username=username).first()
+ return await session.execute(query)
+
+ def set_password(self, password: str) -> None:
+ """
+ Sets a hashed version of the provided
+ password on the user instance.
+ """
+ self.password = argon2.hash(password)
+
+ def check_password(self, password: str) -> bool:
+ """
+ Checks whether the provided password
+ matches the user's password hash.
+ """
+ return argon2.verify(password, self.password)
+
class SubredditUser(Base):
"""
diff --git a/reddit-clone/reddit/users/services.py b/reddit-clone/reddit/users/services.py
index 639f8613..f1a579c8 100644
--- a/reddit-clone/reddit/users/services.py
+++ b/reddit-clone/reddit/users/services.py
@@ -9,8 +9,14 @@ async def authenticate(
session: AsyncSession, username: str, password: str
) -> Optional[User]:
"""
- Checks if the provided credentials are valid.
+ Checks if the provided user credentials are valid.
"""
+ user = await User.by_username(session=session, username=username)
+ if user is None:
+ return user
+ if user.check_password(password=password):
+ return user
+ return None
async def create_user(
@@ -19,7 +25,8 @@ async def create_user(
"""
Creates a new user instance.
"""
- user = User(email=email, username=username, password=password)
+ user = User(email=email, username=username)
+ user.set_password(password=password)
session.add(instance=user)
await session.commit()
await session.refresh(instance=user)
@@ -32,10 +39,15 @@ async def update_user(session: AsyncSession, user: User):
"""
-async def remove_avatar(session: AsyncSession, user: User):
+async def remove_avatar(session: AsyncSession, user: User) -> User:
"""
Removes the avatar for the given user instance.
"""
+ user.avatar = None
+ session.add(instance=user)
+ await session.commit()
+ await session.refresh(instance=user)
+ return user
async def request_change_email(session: AsyncSession, email: str, password: str):
From 2d28665fe1693ebec85cc909060eb7b7a6bdd287 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Sun, 10 Oct 2021 19:01:23 +0530
Subject: [PATCH 073/150] add email template: change email
---
.../reddit/templates/emails/change_email.html | 11 +++++++++++
1 file changed, 11 insertions(+)
create mode 100644 reddit-clone/reddit/templates/emails/change_email.html
diff --git a/reddit-clone/reddit/templates/emails/change_email.html b/reddit-clone/reddit/templates/emails/change_email.html
new file mode 100644
index 00000000..266d02ee
--- /dev/null
+++ b/reddit-clone/reddit/templates/emails/change_email.html
@@ -0,0 +1,11 @@
+{% extends "emails/base_email.html" %} {% block title %} Confirm Email {%
+endblock %} {% block content %}
+
+ Someone tried to change their {{ config.SITE_NAME }} account
+ email to {{ email }}. If this was you,
+ enter this confirmation code in the app:
+
+{{ code }}
+{% endblock %} {% block content_more %}
+If this wasn't you, this email can be safely ignored.
+{% endblock %}
From 3e58450259e3509344cfeb3dbf665d35abc1b1de Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Sun, 10 Oct 2021 19:10:20 +0530
Subject: [PATCH 074/150] update user service: deactivate_user
---
reddit-clone/reddit/users/models.py | 3 +++
reddit-clone/reddit/users/services.py | 20 ++++++++++++++------
2 files changed, 17 insertions(+), 6 deletions(-)
diff --git a/reddit-clone/reddit/users/models.py b/reddit-clone/reddit/users/models.py
index 8c4168b0..0137fb6f 100644
--- a/reddit-clone/reddit/users/models.py
+++ b/reddit-clone/reddit/users/models.py
@@ -6,6 +6,7 @@
from sqlalchemy import select, Column, String, Integer, ForeignKey
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import relationship
+from sqlalchemy.sql.sqltypes import Boolean
from ..database import Base
from ..comments.models import Comment
@@ -30,6 +31,8 @@ class User(Base):
avatar: str = Column(String(255), nullable=False, default="default.jpg")
+ is_active: bool = Column(Boolean, nullable=False, default=False)
+
posts: List[Post] = relationship("Post", back_populates="user", lazy="dynamic")
subreddits: List[Subreddit] = relationship(
diff --git a/reddit-clone/reddit/users/services.py b/reddit-clone/reddit/users/services.py
index f1a579c8..5411c2dc 100644
--- a/reddit-clone/reddit/users/services.py
+++ b/reddit-clone/reddit/users/services.py
@@ -12,11 +12,10 @@ async def authenticate(
Checks if the provided user credentials are valid.
"""
user = await User.by_username(session=session, username=username)
- if user is None:
- return user
- if user.check_password(password=password):
- return user
- return None
+ if user is None or not user.check_password(password=password):
+ # TODO: handle exception here.
+ pass
+ return user
async def create_user(
@@ -27,6 +26,7 @@ async def create_user(
"""
user = User(email=email, username=username)
user.set_password(password=password)
+ # TODO: validate input data here.
session.add(instance=user)
await session.commit()
await session.refresh(instance=user)
@@ -85,7 +85,15 @@ async def block_user(session: AsyncSession, user_id: int, user: User):
"""
-async def deactivate_user(session: AsyncSession, password: str, user: User):
+async def deactivate_user(session: AsyncSession, password: str, user: User) -> User:
"""
Deactivates the given user instance.
"""
+ if not user.check_password(password=password):
+ # TODO: handle exception here.
+ pass
+ user.is_active = False
+ session.add(instance=user)
+ await session.commit()
+ await session.refresh(instance=user)
+ return user
From ba08a498c622fabf2bb992215b654cfe7592ce99 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Sun, 10 Oct 2021 19:18:09 +0530
Subject: [PATCH 075/150] simplify schema design
---
reddit-clone/reddit/users/models.py | 8 ++++
.../reddit/users/mutations/__init__.py | 2 -
.../reddit/users/mutations/user_block.py | 39 -------------------
reddit-clone/reddit/users/services.py | 23 +++++++----
reddit-clone/reddit/users/types.py | 6 ---
5 files changed, 24 insertions(+), 54 deletions(-)
delete mode 100644 reddit-clone/reddit/users/mutations/user_block.py
diff --git a/reddit-clone/reddit/users/models.py b/reddit-clone/reddit/users/models.py
index 0137fb6f..87aa74b6 100644
--- a/reddit-clone/reddit/users/models.py
+++ b/reddit-clone/reddit/users/models.py
@@ -54,6 +54,14 @@ async def by_username(cls, session: AsyncSession, username: str) -> Optional[Use
query = select(User).filter_by(username=username).first()
return await session.execute(query)
+ @classmethod
+ async def by_email(cls, session: AsyncSession, email: str) -> Optional[User]:
+ """
+ Gets an user by their email.
+ """
+ query = select(User).filter_by(email=email).first()
+ return await session.execute(query)
+
def set_password(self, password: str) -> None:
"""
Sets a hashed version of the provided
diff --git a/reddit-clone/reddit/users/mutations/__init__.py b/reddit-clone/reddit/users/mutations/__init__.py
index 83a8adab..91432cc5 100644
--- a/reddit-clone/reddit/users/mutations/__init__.py
+++ b/reddit-clone/reddit/users/mutations/__init__.py
@@ -6,7 +6,6 @@
from .email_change import email_change
from .password_reset_request import password_reset_request
from .password_reset import password_reset
-from .user_block import user_block
from .user_create import user_create
from .user_deactivate import user_deactivate
from .user_update import user_update
@@ -20,7 +19,6 @@
email_change,
password_reset_request,
password_reset,
- user_block,
user_create,
user_deactivate,
user_update,
diff --git a/reddit-clone/reddit/users/mutations/user_block.py b/reddit-clone/reddit/users/mutations/user_block.py
deleted file mode 100644
index b3b7b6bc..00000000
--- a/reddit-clone/reddit/users/mutations/user_block.py
+++ /dev/null
@@ -1,39 +0,0 @@
-from typing import Union
-
-import strawberry
-from strawberry.types import Info
-
-from reddit.users.types import UserType
-
-
-__all__ = ("user_block",)
-
-
-@strawberry.input
-class UserBlockInput:
- user_id: strawberry.ID
-
-
-@strawberry.type
-class UserBlockSuccess:
- user: UserType
-
-
-@strawberry.type
-class UserBlockError:
- error: str
-
-
-UserBlockResult = Union[UserBlockSuccess, UserBlockError]
-
-
-async def resolve_user_block(info: Info, input: UserBlockInput) -> UserBlockResult:
- pass
-
-
-user_block = strawberry.mutation(
- resolver=resolve_user_block,
- description="""
- Blocks an user account.
- """,
-)
diff --git a/reddit-clone/reddit/users/services.py b/reddit-clone/reddit/users/services.py
index 5411c2dc..15f3161a 100644
--- a/reddit-clone/reddit/users/services.py
+++ b/reddit-clone/reddit/users/services.py
@@ -50,10 +50,21 @@ async def remove_avatar(session: AsyncSession, user: User) -> User:
return user
-async def request_change_email(session: AsyncSession, email: str, password: str):
+async def request_change_email(
+ session: AsyncSession, email: str, password: str, user: User
+):
"""
Sends an email change code to the user's new email.
"""
+ if not user.check_password(password=password):
+ # TODO: handle exception here.
+ pass
+ user = await User.by_email(session=session, email=email)
+ if user is not None:
+ # TODO: handle exception here.
+ pass
+ # TODO: store email change code and send
+ # change request email.
async def change_email(
@@ -69,6 +80,10 @@ async def request_reset_password(session: AsyncSession, email: str):
Sends a password reset code to the given email, if it
actually exists.
"""
+ user = await User.by_email(session=session, email=email)
+ if user is not None:
+ # TODO: send password reset email here.
+ pass
async def reset_password(
@@ -79,12 +94,6 @@ async def reset_password(
"""
-async def block_user(session: AsyncSession, user_id: int, user: User):
- """
- Blocks the user account for the given user instance.
- """
-
-
async def deactivate_user(session: AsyncSession, password: str, user: User) -> User:
"""
Deactivates the given user instance.
diff --git a/reddit-clone/reddit/users/types.py b/reddit-clone/reddit/users/types.py
index 09df0b6d..496e694f 100644
--- a/reddit-clone/reddit/users/types.py
+++ b/reddit-clone/reddit/users/types.py
@@ -28,12 +28,6 @@ class UserType(NodeType):
"""
)
- blocked: List[UserType] = strawberry.field(
- description="""
- The accounts blocked by the user.
- """
- )
-
posts: List[PostType] = strawberry.field(
description="""
The posts for the user.
From 1f7b0530de55b040a230b18143eec2923d76e5d9 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Sun, 10 Oct 2021 19:22:54 +0530
Subject: [PATCH 076/150] update deps: add jinja2
---
reddit-clone/poetry.lock | 242 +++++++++++++++++++++++++++---------
reddit-clone/pyproject.toml | 5 +-
2 files changed, 184 insertions(+), 63 deletions(-)
diff --git a/reddit-clone/poetry.lock b/reddit-clone/poetry.lock
index 9f44abcf..ebf8ea00 100644
--- a/reddit-clone/poetry.lock
+++ b/reddit-clone/poetry.lock
@@ -12,14 +12,14 @@ docs = ["sphinx (>=2,<4)", "sphinx_autodoc_typehints (>=1.7.0,<2.0.0)"]
[[package]]
name = "alembic"
-version = "1.7.3"
+version = "1.7.4"
description = "A database migration tool for SQLAlchemy."
category = "main"
optional = false
python-versions = ">=3.6"
[package.dependencies]
-importlib-metadata = {version = "*", markers = "python_version < \"3.8\""}
+importlib-metadata = {version = "*", markers = "python_version < \"3.9\""}
importlib-resources = {version = "*", markers = "python_version < \"3.9\""}
Mako = "*"
SQLAlchemy = ">=1.3.0"
@@ -27,6 +27,22 @@ SQLAlchemy = ">=1.3.0"
[package.extras]
tz = ["python-dateutil"]
+[[package]]
+name = "argon2-cffi"
+version = "21.1.0"
+description = "The secure Argon2 password hashing algorithm."
+category = "main"
+optional = false
+python-versions = ">=3.5"
+
+[package.dependencies]
+cffi = ">=1.0.0"
+
+[package.extras]
+dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pytest", "sphinx", "furo", "wheel", "pre-commit"]
+docs = ["sphinx", "furo"]
+tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pytest"]
+
[[package]]
name = "asgiref"
version = "3.4.1"
@@ -107,9 +123,20 @@ category = "main"
optional = false
python-versions = "*"
+[[package]]
+name = "cffi"
+version = "1.14.6"
+description = "Foreign Function Interface for Python calling C code."
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+pycparser = "*"
+
[[package]]
name = "click"
-version = "8.0.1"
+version = "8.0.2"
description = "Composable command line interface toolkit"
category = "main"
optional = false
@@ -129,21 +156,21 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
[[package]]
name = "fastapi"
-version = "0.68.1"
+version = "0.68.2"
description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production"
category = "main"
optional = false
-python-versions = ">=3.6"
+python-versions = ">=3.6.1"
[package.dependencies]
pydantic = ">=1.6.2,<1.7 || >1.7,<1.7.1 || >1.7.1,<1.7.2 || >1.7.2,<1.7.3 || >1.7.3,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0"
starlette = "0.14.2"
[package.extras]
-all = ["requests (>=2.24.0,<3.0.0)", "aiofiles (>=0.5.0,<0.6.0)", "jinja2 (>=2.11.2,<3.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "itsdangerous (>=1.1.0,<2.0.0)", "pyyaml (>=5.3.1,<6.0.0)", "graphene (>=2.1.8,<3.0.0)", "ujson (>=4.0.1,<5.0.0)", "orjson (>=3.2.1,<4.0.0)", "email_validator (>=1.1.1,<2.0.0)", "uvicorn[standard] (>=0.12.0,<0.14.0)", "async_exit_stack (>=1.0.1,<2.0.0)", "async_generator (>=1.10,<2.0.0)"]
-dev = ["python-jose[cryptography] (>=3.3.0,<4.0.0)", "passlib[bcrypt] (>=1.7.2,<2.0.0)", "autoflake (>=1.3.1,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)", "uvicorn[standard] (>=0.12.0,<0.14.0)", "graphene (>=2.1.8,<3.0.0)"]
-doc = ["mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=7.1.9,<8.0.0)", "mdx-include (>=1.4.1,<2.0.0)", "mkdocs-markdownextradata-plugin (>=0.1.7,<0.2.0)", "typer-cli (>=0.0.12,<0.0.13)", "pyyaml (>=5.3.1,<6.0.0)"]
-test = ["pytest (>=6.2.4,<7.0.0)", "pytest-cov (>=2.12.0,<3.0.0)", "pytest-asyncio (>=0.14.0,<0.15.0)", "mypy (==0.812)", "flake8 (>=3.8.3,<4.0.0)", "black (==20.8b1)", "isort (>=5.0.6,<6.0.0)", "requests (>=2.24.0,<3.0.0)", "httpx (>=0.14.0,<0.15.0)", "email_validator (>=1.1.1,<2.0.0)", "sqlalchemy (>=1.3.18,<1.4.0)", "peewee (>=3.13.3,<4.0.0)", "databases[sqlite] (>=0.3.2,<0.4.0)", "orjson (>=3.2.1,<4.0.0)", "ujson (>=4.0.1,<5.0.0)", "async_exit_stack (>=1.0.1,<2.0.0)", "async_generator (>=1.10,<2.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "aiofiles (>=0.5.0,<0.6.0)", "flask (>=1.1.2,<2.0.0)"]
+all = ["requests (>=2.24.0,<3.0.0)", "aiofiles (>=0.5.0,<0.8.0)", "jinja2 (>=2.11.2,<3.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "itsdangerous (>=1.1.0,<2.0.0)", "pyyaml (>=5.3.1,<6.0.0)", "graphene (>=2.1.8,<3.0.0)", "ujson (>=4.0.1,<5.0.0)", "orjson (>=3.2.1,<4.0.0)", "email_validator (>=1.1.1,<2.0.0)", "uvicorn[standard] (>=0.12.0,<0.16.0)", "async_exit_stack (>=1.0.1,<2.0.0)", "async_generator (>=1.10,<2.0.0)"]
+dev = ["python-jose[cryptography] (>=3.3.0,<4.0.0)", "passlib[bcrypt] (>=1.7.2,<2.0.0)", "autoflake (>=1.4.0,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)", "uvicorn[standard] (>=0.12.0,<0.16.0)", "graphene (>=2.1.8,<3.0.0)"]
+doc = ["mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=7.1.9,<8.0.0)", "mdx-include (>=1.4.1,<2.0.0)", "mkdocs-markdownextradata-plugin (>=0.1.7,<0.3.0)", "typer-cli (>=0.0.12,<0.0.13)", "pyyaml (>=5.3.1,<6.0.0)"]
+test = ["pytest (>=6.2.4,<7.0.0)", "pytest-cov (>=2.12.0,<4.0.0)", "pytest-asyncio (>=0.14.0,<0.16.0)", "mypy (==0.910)", "flake8 (>=3.8.3,<4.0.0)", "black (==21.9b0)", "isort (>=5.0.6,<6.0.0)", "requests (>=2.24.0,<3.0.0)", "httpx (>=0.14.0,<0.19.0)", "email_validator (>=1.1.1,<2.0.0)", "sqlalchemy (>=1.3.18,<1.5.0)", "peewee (>=3.13.3,<4.0.0)", "databases[sqlite] (>=0.3.2,<0.6.0)", "orjson (>=3.2.1,<4.0.0)", "ujson (>=4.0.1,<5.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "aiofiles (>=0.5.0,<0.8.0)", "flask (>=1.1.2,<2.0.0)", "async_exit_stack (>=1.0.1,<2.0.0)", "async_generator (>=1.10,<2.0.0)", "types-ujson (==0.1.1)", "types-orjson (==3.6.0)", "types-dataclasses (==0.1.7)"]
[[package]]
name = "flake8"
@@ -269,6 +296,20 @@ zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""}
docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"]
testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-black (>=0.3.7)", "pytest-mypy"]
+[[package]]
+name = "jinja2"
+version = "3.0.2"
+description = "A very fast and expressive template engine."
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+MarkupSafe = ">=2.0"
+
+[package.extras]
+i18n = ["Babel (>=2.7)"]
+
[[package]]
name = "mako"
version = "1.1.5"
@@ -334,6 +375,9 @@ category = "main"
optional = false
python-versions = "*"
+[package.dependencies]
+argon2-cffi = {version = ">=18.2.0", optional = true, markers = "extra == \"argon2\""}
+
[package.extras]
argon2 = ["argon2-cffi (>=18.2.0)"]
bcrypt = ["bcrypt (>=3.1.0)"]
@@ -368,6 +412,14 @@ category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+[[package]]
+name = "pycparser"
+version = "2.20"
+description = "C parser in Python"
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+
[[package]]
name = "pydantic"
version = "1.8.2"
@@ -412,7 +464,7 @@ six = ">=1.5"
[[package]]
name = "python-dotenv"
-version = "0.19.0"
+version = "0.19.1"
description = "Read key-value pairs from a .env file and set them as environment variables"
category = "main"
optional = false
@@ -442,7 +494,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
[[package]]
name = "regex"
-version = "2021.9.30"
+version = "2021.10.8"
description = "Alternative regular expression module, to replace re."
category = "dev"
optional = false
@@ -649,7 +701,7 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytes
[metadata]
lock-version = "1.1"
python-versions = "^3.7"
-content-hash = "03a61f70a8c276ecdeb0f5dcd3c519f5b4dbe2187ce15e4a68c607f5e594c7e0"
+content-hash = "2c06d6d834957f2c4efe2573570f18ee4bde2f8c41f227317761789709c4a736"
[metadata.files]
aiosmtplib = [
@@ -657,8 +709,21 @@ aiosmtplib = [
{file = "aiosmtplib-1.1.6.tar.gz", hash = "sha256:d138fe6ffecbc9e6320269690b9ac0b75e540ef96e8f5c77d4a306760014dce2"},
]
alembic = [
- {file = "alembic-1.7.3-py3-none-any.whl", hash = "sha256:d0c580041f9f6487d5444df672a83da9be57398f39d6c1802bbedec6fefbeef6"},
- {file = "alembic-1.7.3.tar.gz", hash = "sha256:bc5bdf03d1b9814ee4d72adc0b19df2123f6c50a60c1ea761733f3640feedb8d"},
+ {file = "alembic-1.7.4-py3-none-any.whl", hash = "sha256:e3cab9e59778b3b6726bb2da9ced451c6622d558199fd3ef914f3b1e8f4ef704"},
+ {file = "alembic-1.7.4.tar.gz", hash = "sha256:9d33f3ff1488c4bfab1e1a6dfebbf085e8a8e1a3e047a43ad29ad1f67f012a1d"},
+]
+argon2-cffi = [
+ {file = "argon2-cffi-21.1.0.tar.gz", hash = "sha256:f710b61103d1a1f692ca3ecbd1373e28aa5e545ac625ba067ff2feca1b2bb870"},
+ {file = "argon2_cffi-21.1.0-cp35-abi3-macosx_10_14_x86_64.whl", hash = "sha256:217b4f0f853ccbbb5045242946ad2e162e396064575860141b71a85eb47e475a"},
+ {file = "argon2_cffi-21.1.0-cp35-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:fa7e7d1fc22514a32b1761fdfa1882b6baa5c36bb3ef557bdd69e6fc9ba14a41"},
+ {file = "argon2_cffi-21.1.0-cp35-abi3-win32.whl", hash = "sha256:e4d8f0ae1524b7b0372a3e574a2561cbdddb3fdb6c28b70a72868189bda19659"},
+ {file = "argon2_cffi-21.1.0-cp35-abi3-win_amd64.whl", hash = "sha256:65213a9174320a1aee03fe826596e0620783966b49eb636955958b3074e87ff9"},
+ {file = "argon2_cffi-21.1.0-pp36-pypy36_pp73-macosx_10_7_x86_64.whl", hash = "sha256:245f64a203012b144b7b8c8ea6d468cb02b37caa5afee5ba4a10c80599334f6a"},
+ {file = "argon2_cffi-21.1.0-pp36-pypy36_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4ad152c418f7eb640eac41ac815534e6aa61d1624530b8e7779114ecfbf327f8"},
+ {file = "argon2_cffi-21.1.0-pp36-pypy36_pp73-win32.whl", hash = "sha256:bc513db2283c385ea4da31a2cd039c33380701f376f4edd12fe56db118a3b21a"},
+ {file = "argon2_cffi-21.1.0-pp37-pypy37_pp73-macosx_10_7_x86_64.whl", hash = "sha256:c7a7c8cc98ac418002090e4add5bebfff1b915ea1cb459c578cd8206fef10378"},
+ {file = "argon2_cffi-21.1.0-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:165cadae5ac1e26644f5ade3bd9c18d89963be51d9ea8817bd671006d7909057"},
+ {file = "argon2_cffi-21.1.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:566ffb581bbd9db5562327aee71b2eda24a1c15b23a356740abe3c011bbe0dcb"},
]
asgiref = [
{file = "asgiref-3.4.1-py3-none-any.whl", hash = "sha256:ffc141aa908e6f175673e7b1b3b7af4fdb0ecb738fc5c8b88f69f055c2415214"},
@@ -691,17 +756,64 @@ 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"},
]
+cffi = [
+ {file = "cffi-1.14.6-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:22b9c3c320171c108e903d61a3723b51e37aaa8c81255b5e7ce102775bd01e2c"},
+ {file = "cffi-1.14.6-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:f0c5d1acbfca6ebdd6b1e3eded8d261affb6ddcf2186205518f1428b8569bb99"},
+ {file = "cffi-1.14.6-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:99f27fefe34c37ba9875f224a8f36e31d744d8083e00f520f133cab79ad5e819"},
+ {file = "cffi-1.14.6-cp27-cp27m-win32.whl", hash = "sha256:55af55e32ae468e9946f741a5d51f9896da6b9bf0bbdd326843fec05c730eb20"},
+ {file = "cffi-1.14.6-cp27-cp27m-win_amd64.whl", hash = "sha256:7bcac9a2b4fdbed2c16fa5681356d7121ecabf041f18d97ed5b8e0dd38a80224"},
+ {file = "cffi-1.14.6-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:ed38b924ce794e505647f7c331b22a693bee1538fdf46b0222c4717b42f744e7"},
+ {file = "cffi-1.14.6-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:e22dcb48709fc51a7b58a927391b23ab37eb3737a98ac4338e2448bef8559b33"},
+ {file = "cffi-1.14.6-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:aedb15f0a5a5949ecb129a82b72b19df97bbbca024081ed2ef88bd5c0a610534"},
+ {file = "cffi-1.14.6-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:48916e459c54c4a70e52745639f1db524542140433599e13911b2f329834276a"},
+ {file = "cffi-1.14.6-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:f627688813d0a4140153ff532537fbe4afea5a3dffce1f9deb7f91f848a832b5"},
+ {file = "cffi-1.14.6-cp35-cp35m-win32.whl", hash = "sha256:f0010c6f9d1a4011e429109fda55a225921e3206e7f62a0c22a35344bfd13cca"},
+ {file = "cffi-1.14.6-cp35-cp35m-win_amd64.whl", hash = "sha256:57e555a9feb4a8460415f1aac331a2dc833b1115284f7ded7278b54afc5bd218"},
+ {file = "cffi-1.14.6-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:e8c6a99be100371dbb046880e7a282152aa5d6127ae01783e37662ef73850d8f"},
+ {file = "cffi-1.14.6-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:19ca0dbdeda3b2615421d54bef8985f72af6e0c47082a8d26122adac81a95872"},
+ {file = "cffi-1.14.6-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:d950695ae4381ecd856bcaf2b1e866720e4ab9a1498cba61c602e56630ca7195"},
+ {file = "cffi-1.14.6-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9dc245e3ac69c92ee4c167fbdd7428ec1956d4e754223124991ef29eb57a09d"},
+ {file = "cffi-1.14.6-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a8661b2ce9694ca01c529bfa204dbb144b275a31685a075ce123f12331be790b"},
+ {file = "cffi-1.14.6-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b315d709717a99f4b27b59b021e6207c64620790ca3e0bde636a6c7f14618abb"},
+ {file = "cffi-1.14.6-cp36-cp36m-win32.whl", hash = "sha256:80b06212075346b5546b0417b9f2bf467fea3bfe7352f781ffc05a8ab24ba14a"},
+ {file = "cffi-1.14.6-cp36-cp36m-win_amd64.whl", hash = "sha256:a9da7010cec5a12193d1af9872a00888f396aba3dc79186604a09ea3ee7c029e"},
+ {file = "cffi-1.14.6-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:4373612d59c404baeb7cbd788a18b2b2a8331abcc84c3ba40051fcd18b17a4d5"},
+ {file = "cffi-1.14.6-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:f10afb1004f102c7868ebfe91c28f4a712227fe4cb24974350ace1f90e1febbf"},
+ {file = "cffi-1.14.6-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:fd4305f86f53dfd8cd3522269ed7fc34856a8ee3709a5e28b2836b2db9d4cd69"},
+ {file = "cffi-1.14.6-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6d6169cb3c6c2ad50db5b868db6491a790300ade1ed5d1da29289d73bbe40b56"},
+ {file = "cffi-1.14.6-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5d4b68e216fc65e9fe4f524c177b54964af043dde734807586cf5435af84045c"},
+ {file = "cffi-1.14.6-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33791e8a2dc2953f28b8d8d300dde42dd929ac28f974c4b4c6272cb2955cb762"},
+ {file = "cffi-1.14.6-cp37-cp37m-win32.whl", hash = "sha256:0c0591bee64e438883b0c92a7bed78f6290d40bf02e54c5bf0978eaf36061771"},
+ {file = "cffi-1.14.6-cp37-cp37m-win_amd64.whl", hash = "sha256:8eb687582ed7cd8c4bdbff3df6c0da443eb89c3c72e6e5dcdd9c81729712791a"},
+ {file = "cffi-1.14.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ba6f2b3f452e150945d58f4badd92310449876c4c954836cfb1803bdd7b422f0"},
+ {file = "cffi-1.14.6-cp38-cp38-manylinux1_i686.whl", hash = "sha256:64fda793737bc4037521d4899be780534b9aea552eb673b9833b01f945904c2e"},
+ {file = "cffi-1.14.6-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:9f3e33c28cd39d1b655ed1ba7247133b6f7fc16fa16887b120c0c670e35ce346"},
+ {file = "cffi-1.14.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26bb2549b72708c833f5abe62b756176022a7b9a7f689b571e74c8478ead51dc"},
+ {file = "cffi-1.14.6-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eb687a11f0a7a1839719edd80f41e459cc5366857ecbed383ff376c4e3cc6afd"},
+ {file = "cffi-1.14.6-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d2ad4d668a5c0645d281dcd17aff2be3212bc109b33814bbb15c4939f44181cc"},
+ {file = "cffi-1.14.6-cp38-cp38-win32.whl", hash = "sha256:487d63e1454627c8e47dd230025780e91869cfba4c753a74fda196a1f6ad6548"},
+ {file = "cffi-1.14.6-cp38-cp38-win_amd64.whl", hash = "sha256:c33d18eb6e6bc36f09d793c0dc58b0211fccc6ae5149b808da4a62660678b156"},
+ {file = "cffi-1.14.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:06c54a68935738d206570b20da5ef2b6b6d92b38ef3ec45c5422c0ebaf338d4d"},
+ {file = "cffi-1.14.6-cp39-cp39-manylinux1_i686.whl", hash = "sha256:f174135f5609428cc6e1b9090f9268f5c8935fddb1b25ccb8255a2d50de6789e"},
+ {file = "cffi-1.14.6-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:f3ebe6e73c319340830a9b2825d32eb6d8475c1dac020b4f0aa774ee3b898d1c"},
+ {file = "cffi-1.14.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c8d896becff2fa653dc4438b54a5a25a971d1f4110b32bd3068db3722c80202"},
+ {file = "cffi-1.14.6-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4922cd707b25e623b902c86188aca466d3620892db76c0bdd7b99a3d5e61d35f"},
+ {file = "cffi-1.14.6-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c9e005e9bd57bc987764c32a1bee4364c44fdc11a3cc20a40b93b444984f2b87"},
+ {file = "cffi-1.14.6-cp39-cp39-win32.whl", hash = "sha256:eb9e2a346c5238a30a746893f23a9535e700f8192a68c07c0258e7ece6ff3728"},
+ {file = "cffi-1.14.6-cp39-cp39-win_amd64.whl", hash = "sha256:818014c754cd3dba7229c0f5884396264d51ffb87ec86e927ef0be140bfdb0d2"},
+ {file = "cffi-1.14.6.tar.gz", hash = "sha256:c9a875ce9d7fe32887784274dd533c57909b7b1dcadcc128a2ac21331a9765dd"},
+]
click = [
- {file = "click-8.0.1-py3-none-any.whl", hash = "sha256:fba402a4a47334742d782209a7c79bc448911afe1149d07bdabdf480b3e2f4b6"},
- {file = "click-8.0.1.tar.gz", hash = "sha256:8c04c11192119b1ef78ea049e0a6f0463e4c48ef00a30160c704337586f3ad7a"},
+ {file = "click-8.0.2-py3-none-any.whl", hash = "sha256:3fab8aeb8f15f5452ae7511ad448977b3417325bceddd53df87e0bb81f3a8cf8"},
+ {file = "click-8.0.2.tar.gz", hash = "sha256:7027bc7bbafaab8b2c2816861d8eb372429ee3c02e193fc2f93d6c4ab9de49c5"},
]
colorama = [
{file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"},
{file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"},
]
fastapi = [
- {file = "fastapi-0.68.1-py3-none-any.whl", hash = "sha256:94d2820906c36b9b8303796fb7271337ec89c74223229e3cfcf056b5a7d59e23"},
- {file = "fastapi-0.68.1.tar.gz", hash = "sha256:644bb815bae326575c4b2842469fb83053a4b974b82fa792ff9283d17fbbd99d"},
+ {file = "fastapi-0.68.2-py3-none-any.whl", hash = "sha256:36bcdd3dbea87c586061005e4a40b9bd0145afd766655b4e0ec1d8870b32555c"},
+ {file = "fastapi-0.68.2.tar.gz", hash = "sha256:38526fc46bda73f7ec92033952677323c16061e70a91d15c95f18b11895da494"},
]
flake8 = [
{file = "flake8-3.9.2-py2.py3-none-any.whl", hash = "sha256:bf8fd333346d844f616e8d47905ef3a3384edae6b4e9beb0c5101e25e3110907"},
@@ -804,6 +916,10 @@ importlib-resources = [
{file = "importlib_resources-5.2.2-py3-none-any.whl", hash = "sha256:2480d8e07d1890056cb53c96e3de44fead9c62f2ba949b0f2e4c4345f4afa977"},
{file = "importlib_resources-5.2.2.tar.gz", hash = "sha256:a65882a4d0fe5fbf702273456ba2ce74fe44892c25e42e057aca526b702a6d4b"},
]
+jinja2 = [
+ {file = "Jinja2-3.0.2-py3-none-any.whl", hash = "sha256:8569982d3f0889eed11dd620c706d39b60c36d6d25843961f33f77fb6bc6b20c"},
+ {file = "Jinja2-3.0.2.tar.gz", hash = "sha256:827a0e32839ab1600d4eb1c4c33ec5a8edfbc5cb42dafa13b81f182f97784b45"},
+]
mako = [
{file = "Mako-1.1.5-py2.py3-none-any.whl", hash = "sha256:6804ee66a7f6a6416910463b00d76a7b25194cd27f1918500c5bd7be2a088a23"},
{file = "Mako-1.1.5.tar.gz", hash = "sha256:169fa52af22a91900d852e937400e79f535496191c63712e3b9fda5a9bed6fc3"},
@@ -893,6 +1009,10 @@ pycodestyle = [
{file = "pycodestyle-2.7.0-py2.py3-none-any.whl", hash = "sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068"},
{file = "pycodestyle-2.7.0.tar.gz", hash = "sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef"},
]
+pycparser = [
+ {file = "pycparser-2.20-py2.py3-none-any.whl", hash = "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"},
+ {file = "pycparser-2.20.tar.gz", hash = "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0"},
+]
pydantic = [
{file = "pydantic-1.8.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:05ddfd37c1720c392f4e0d43c484217b7521558302e7069ce8d318438d297739"},
{file = "pydantic-1.8.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:a7c6002203fe2c5a1b5cbb141bb85060cbff88c2d78eccbc72d97eb7022c43e4"},
@@ -930,8 +1050,8 @@ python-dateutil = [
{file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"},
]
python-dotenv = [
- {file = "python-dotenv-0.19.0.tar.gz", hash = "sha256:f521bc2ac9a8e03c736f62911605c5d83970021e3fa95b37d769e2bbbe9b6172"},
- {file = "python_dotenv-0.19.0-py2.py3-none-any.whl", hash = "sha256:aae25dc1ebe97c420f50b81fb0e5c949659af713f31fdb63c749ca68748f34b1"},
+ {file = "python-dotenv-0.19.1.tar.gz", hash = "sha256:14f8185cc8d494662683e6914addcb7e95374771e707601dfc70166946b4c4b8"},
+ {file = "python_dotenv-0.19.1-py2.py3-none-any.whl", hash = "sha256:bbd3da593fc49c249397cbfbcc449cf36cb02e75afc8157fcc6a81df6fb7750a"},
]
python-multipart = [
{file = "python-multipart-0.0.5.tar.gz", hash = "sha256:f7bb5f611fc600d15fa47b3974c8aa16e93724513b49b5f95c81e6624c83fa43"},
@@ -968,47 +1088,47 @@ pyyaml = [
{file = "PyYAML-5.4.1.tar.gz", hash = "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e"},
]
regex = [
- {file = "regex-2021.9.30-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:66696c8336a1b5d1182464f3af3427cc760118f26d0b09a2ddc16a976a4d2637"},
- {file = "regex-2021.9.30-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d87459ad3ab40cd8493774f8a454b2e490d8e729e7e402a0625867a983e4e02"},
- {file = "regex-2021.9.30-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78cf6a1e023caf5e9a982f5377414e1aeac55198831b852835732cfd0a0ca5ff"},
- {file = "regex-2021.9.30-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:255791523f80ea8e48e79af7120b4697ef3b74f6886995dcdb08c41f8e516be0"},
- {file = "regex-2021.9.30-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e502f8d4e5ef714bcc2c94d499684890c94239526d61fdf1096547db91ca6aa6"},
- {file = "regex-2021.9.30-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4907fb0f9b9309a5bded72343e675a252c2589a41871874feace9a05a540241e"},
- {file = "regex-2021.9.30-cp310-cp310-win32.whl", hash = "sha256:3be40f720af170a6b20ddd2ad7904c58b13d2b56f6734ee5d09bbdeed2fa4816"},
- {file = "regex-2021.9.30-cp310-cp310-win_amd64.whl", hash = "sha256:c2b180ed30856dfa70cfe927b0fd38e6b68198a03039abdbeb1f2029758d87e7"},
- {file = "regex-2021.9.30-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:e6f2d2f93001801296fe3ca86515eb04915472b5380d4d8752f09f25f0b9b0ed"},
- {file = "regex-2021.9.30-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4fa7ba9ab2eba7284e0d7d94f61df7af86015b0398e123331362270d71fab0b9"},
- {file = "regex-2021.9.30-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28040e89a04b60d579c69095c509a4f6a1a5379cd865258e3a186b7105de72c6"},
- {file = "regex-2021.9.30-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f588209d3e4797882cd238195c175290dbc501973b10a581086b5c6bcd095ffb"},
- {file = "regex-2021.9.30-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:42952d325439ef223e4e9db7ee6d9087b5c68c5c15b1f9de68e990837682fc7b"},
- {file = "regex-2021.9.30-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cae4099031d80703954c39680323dabd87a69b21262303160776aa0e55970ca0"},
- {file = "regex-2021.9.30-cp36-cp36m-win32.whl", hash = "sha256:0de8ad66b08c3e673b61981b9e3626f8784d5564f8c3928e2ad408c0eb5ac38c"},
- {file = "regex-2021.9.30-cp36-cp36m-win_amd64.whl", hash = "sha256:b345ecde37c86dd7084c62954468a4a655fd2d24fd9b237949dd07a4d0dd6f4c"},
- {file = "regex-2021.9.30-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a6f08187136f11e430638c2c66e1db091105d7c2e9902489f0dbc69b44c222b4"},
- {file = "regex-2021.9.30-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b55442650f541d195a535ccec33078c78a9521973fb960923da7515e9ed78fa6"},
- {file = "regex-2021.9.30-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87e9c489aa98f50f367fb26cc9c8908d668e9228d327644d7aa568d47e456f47"},
- {file = "regex-2021.9.30-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:e2cb7d4909ed16ed35729d38af585673f1f0833e73dfdf0c18e5be0061107b99"},
- {file = "regex-2021.9.30-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d0861e7f6325e821d5c40514c551fd538b292f8cc3960086e73491b9c5d8291d"},
- {file = "regex-2021.9.30-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:81fdc90f999b2147fc62e303440c424c47e5573a9b615ed5d43a5b832efcca9e"},
- {file = "regex-2021.9.30-cp37-cp37m-win32.whl", hash = "sha256:8c1ad61fa024195136a6b7b89538030bd00df15f90ac177ca278df9b2386c96f"},
- {file = "regex-2021.9.30-cp37-cp37m-win_amd64.whl", hash = "sha256:e3770781353a4886b68ef10cec31c1f61e8e3a0be5f213c2bb15a86efd999bc4"},
- {file = "regex-2021.9.30-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9c065d95a514a06b92a5026766d72ac91bfabf581adb5b29bc5c91d4b3ee9b83"},
- {file = "regex-2021.9.30-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9925985be05d54b3d25fd6c1ea8e50ff1f7c2744c75bdc4d3b45c790afa2bcb3"},
- {file = "regex-2021.9.30-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:470f2c882f2672d8eeda8ab27992aec277c067d280b52541357e1acd7e606dae"},
- {file = "regex-2021.9.30-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:ad0517df22a97f1da20d8f1c8cb71a5d1997fa383326b81f9cf22c9dadfbdf34"},
- {file = "regex-2021.9.30-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c9e30838df7bfd20db6466fd309d9b580d32855f8e2c2e6d74cf9da27dcd9b63"},
- {file = "regex-2021.9.30-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5b34d2335d6aedec7dcadd3f8283b9682fadad8b9b008da8788d2fce76125ebe"},
- {file = "regex-2021.9.30-cp38-cp38-win32.whl", hash = "sha256:e07049cece3462c626d650e8bf42ddbca3abf4aa08155002c28cb6d9a5a281e2"},
- {file = "regex-2021.9.30-cp38-cp38-win_amd64.whl", hash = "sha256:37868075eda024470bd0feab872c692ac4ee29db1e14baec103257bf6cc64346"},
- {file = "regex-2021.9.30-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d331f238a7accfbbe1c4cd1ba610d4c087b206353539331e32a8f05345c74aec"},
- {file = "regex-2021.9.30-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6348a7ab2a502cbdd0b7fd0496d614007489adb7361956b38044d1d588e66e04"},
- {file = "regex-2021.9.30-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce7b1cca6c23f19bee8dc40228d9c314d86d1e51996b86f924aca302fc8f8bf9"},
- {file = "regex-2021.9.30-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1f1125bc5172ab3a049bc6f4b9c0aae95a2a2001a77e6d6e4239fa3653e202b5"},
- {file = "regex-2021.9.30-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:638e98d069b14113e8afba6a54d1ca123f712c0d105e67c1f9211b2a825ef926"},
- {file = "regex-2021.9.30-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9a0b0db6b49da7fa37ca8eddf9f40a8dbc599bad43e64f452284f37b6c34d91c"},
- {file = "regex-2021.9.30-cp39-cp39-win32.whl", hash = "sha256:9910869c472e5a6728680ca357b5846546cbbd2ab3ad5bef986ef0bc438d0aa6"},
- {file = "regex-2021.9.30-cp39-cp39-win_amd64.whl", hash = "sha256:3b71213ec3bad9a5a02e049f2ec86b3d7c3e350129ae0f4e2f99c12b5da919ed"},
- {file = "regex-2021.9.30.tar.gz", hash = "sha256:81e125d9ba54c34579e4539a967e976a3c56150796674aec318b1b2f49251be7"},
+ {file = "regex-2021.10.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:981c786293a3115bc14c103086ae54e5ee50ca57f4c02ce7cf1b60318d1e8072"},
+ {file = "regex-2021.10.8-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:51feefd58ac38eb91a21921b047da8644155e5678e9066af7bcb30ee0dca7361"},
+ {file = "regex-2021.10.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea8de658d7db5987b11097445f2b1f134400e2232cb40e614e5f7b6f5428710e"},
+ {file = "regex-2021.10.8-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1ce02f420a7ec3b2480fe6746d756530f69769292eca363218c2291d0b116a01"},
+ {file = "regex-2021.10.8-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:39079ebf54156be6e6902f5c70c078f453350616cfe7bfd2dd15bdb3eac20ccc"},
+ {file = "regex-2021.10.8-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ff24897f6b2001c38a805d53b6ae72267025878d35ea225aa24675fbff2dba7f"},
+ {file = "regex-2021.10.8-cp310-cp310-win32.whl", hash = "sha256:c6569ba7b948c3d61d27f04e2b08ebee24fec9ff8e9ea154d8d1e975b175bfa7"},
+ {file = "regex-2021.10.8-cp310-cp310-win_amd64.whl", hash = "sha256:45cb0f7ff782ef51bc79e227a87e4e8f24bc68192f8de4f18aae60b1d60bc152"},
+ {file = "regex-2021.10.8-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:fab3ab8aedfb443abb36729410403f0fe7f60ad860c19a979d47fb3eb98ef820"},
+ {file = "regex-2021.10.8-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:74e55f8d66f1b41d44bc44c891bcf2c7fad252f8f323ee86fba99d71fd1ad5e3"},
+ {file = "regex-2021.10.8-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d52c5e089edbdb6083391faffbe70329b804652a53c2fdca3533e99ab0580d9"},
+ {file = "regex-2021.10.8-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1abbd95cbe9e2467cac65c77b6abd9223df717c7ae91a628502de67c73bf6838"},
+ {file = "regex-2021.10.8-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b9b5c215f3870aa9b011c00daeb7be7e1ae4ecd628e9beb6d7e6107e07d81287"},
+ {file = "regex-2021.10.8-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f540f153c4f5617bc4ba6433534f8916d96366a08797cbbe4132c37b70403e92"},
+ {file = "regex-2021.10.8-cp36-cp36m-win32.whl", hash = "sha256:1f51926db492440e66c89cd2be042f2396cf91e5b05383acd7372b8cb7da373f"},
+ {file = "regex-2021.10.8-cp36-cp36m-win_amd64.whl", hash = "sha256:5f55c4804797ef7381518e683249310f7f9646da271b71cb6b3552416c7894ee"},
+ {file = "regex-2021.10.8-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:fb2baff66b7d2267e07ef71e17d01283b55b3cc51a81b54cc385e721ae172ba4"},
+ {file = "regex-2021.10.8-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9e527ab1c4c7cf2643d93406c04e1d289a9d12966529381ce8163c4d2abe4faf"},
+ {file = "regex-2021.10.8-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36c98b013273e9da5790ff6002ab326e3f81072b4616fd95f06c8fa733d2745f"},
+ {file = "regex-2021.10.8-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:55ef044899706c10bc0aa052f2fc2e58551e2510694d6aae13f37c50f3f6ff61"},
+ {file = "regex-2021.10.8-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa0ab3530a279a3b7f50f852f1bab41bc304f098350b03e30a3876b7dd89840e"},
+ {file = "regex-2021.10.8-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a37305eb3199d8f0d8125ec2fb143ba94ff6d6d92554c4b8d4a8435795a6eccd"},
+ {file = "regex-2021.10.8-cp37-cp37m-win32.whl", hash = "sha256:2efd47704bbb016136fe34dfb74c805b1ef5c7313aef3ce6dcb5ff844299f432"},
+ {file = "regex-2021.10.8-cp37-cp37m-win_amd64.whl", hash = "sha256:924079d5590979c0e961681507eb1773a142553564ccae18d36f1de7324e71ca"},
+ {file = "regex-2021.10.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b09d3904bf312d11308d9a2867427479d277365b1617e48ad09696fa7dfcdf59"},
+ {file = "regex-2021.10.8-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7f125fce0a0ae4fd5c3388d369d7a7d78f185f904c90dd235f7ecf8fe13fa741"},
+ {file = "regex-2021.10.8-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f199419a81c1016e0560c39773c12f0bd924c37715bffc64b97140d2c314354"},
+ {file = "regex-2021.10.8-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:09e1031e2059abd91177c302da392a7b6859ceda038be9e015b522a182c89e4f"},
+ {file = "regex-2021.10.8-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9c070d5895ac6aeb665bd3cd79f673775caf8d33a0b569e98ac434617ecea57d"},
+ {file = "regex-2021.10.8-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:176796cb7f82a7098b0c436d6daac82f57b9101bb17b8e8119c36eecf06a60a3"},
+ {file = "regex-2021.10.8-cp38-cp38-win32.whl", hash = "sha256:5e5796d2f36d3c48875514c5cd9e4325a1ca172fc6c78b469faa8ddd3d770593"},
+ {file = "regex-2021.10.8-cp38-cp38-win_amd64.whl", hash = "sha256:e4204708fa116dd03436a337e8e84261bc8051d058221ec63535c9403a1582a1"},
+ {file = "regex-2021.10.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b8b6ee6555b6fbae578f1468b3f685cdfe7940a65675611365a7ea1f8d724991"},
+ {file = "regex-2021.10.8-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:973499dac63625a5ef9dfa4c791aa33a502ddb7615d992bdc89cf2cc2285daa3"},
+ {file = "regex-2021.10.8-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88dc3c1acd3f0ecfde5f95c32fcb9beda709dbdf5012acdcf66acbc4794468eb"},
+ {file = "regex-2021.10.8-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:4786dae85c1f0624ac77cb3813ed99267c9adb72e59fdc7297e1cf4d6036d493"},
+ {file = "regex-2021.10.8-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fe6ce4f3d3c48f9f402da1ceb571548133d3322003ce01b20d960a82251695d2"},
+ {file = "regex-2021.10.8-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9e3e2cea8f1993f476a6833ef157f5d9e8c75a59a8d8b0395a9a6887a097243b"},
+ {file = "regex-2021.10.8-cp39-cp39-win32.whl", hash = "sha256:82cfb97a36b1a53de32b642482c6c46b6ce80803854445e19bc49993655ebf3b"},
+ {file = "regex-2021.10.8-cp39-cp39-win_amd64.whl", hash = "sha256:b04e512eb628ea82ed86eb31c0f7fc6842b46bf2601b66b1356a7008327f7700"},
+ {file = "regex-2021.10.8.tar.gz", hash = "sha256:26895d7c9bbda5c52b3635ce5991caa90fbb1ddfac9c9ff1c7ce505e2282fb2a"},
]
schematics = [
{file = "schematics-2.1.1-py2.py3-none-any.whl", hash = "sha256:be2d451bfb86789975e5ec0864aec569b63cea9010f0d24cbbd992a4e564c647"},
diff --git a/reddit-clone/pyproject.toml b/reddit-clone/pyproject.toml
index 3ddcc6d4..aaebbbce 100644
--- a/reddit-clone/pyproject.toml
+++ b/reddit-clone/pyproject.toml
@@ -8,15 +8,16 @@ authors = ["Aryan Iyappan "]
python = "^3.7"
uvicorn = {extras = ["standard"], version = "^0.15.0"}
SQLAlchemy = {extras = ["mypy"], version = "^1.4.23"}
+strawberry-graphql = {extras = ["asgi"], version = "^0.77.0"}
+passlib = {extras = ["argon2"], version = "^1.7.4"}
alembic = "^1.7.1"
asyncpg = "^0.24"
-strawberry-graphql = {extras = ["asgi"], version = "^0.77.0"}
fastapi = "^0.68.1"
starlette = "^0.14"
graphql-relay = "^3.1.0"
aiosmtplib = "^1.1.6"
schematics = "^2.1.1"
-passlib = "^1.7.4"
+jinja2 = "^3.0.2"
[tool.poetry.dev-dependencies]
From 6ea08f4a40f6109bd9b19ec2f7292712786ff176 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Sun, 10 Oct 2021 19:49:15 +0530
Subject: [PATCH 077/150] add templating helpers
---
reddit-clone/reddit/templates.py | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
create mode 100644 reddit-clone/reddit/templates.py
diff --git a/reddit-clone/reddit/templates.py b/reddit-clone/reddit/templates.py
new file mode 100644
index 00000000..7240f333
--- /dev/null
+++ b/reddit-clone/reddit/templates.py
@@ -0,0 +1,16 @@
+from jinja2 import Environment, FileSystemLoader, select_autoescape
+
+__all__ = ("render_template",)
+
+environment = Environment(
+ loader=FileSystemLoader(searchpath="./templates"),
+ autoescape=select_autoescape(("html", "xml")),
+)
+
+
+def render_template(template_name: str, *args, **kwargs):
+ """
+ Renders the html template with the given variables.
+ """
+ template = environment.get_template(name=template_name)
+ return template.render(*args, **kwargs)
From 4f719f29e2131bb3f52b2b8d8702f36794e1b480 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Mon, 11 Oct 2021 08:57:09 +0530
Subject: [PATCH 078/150] add file storage helpers
---
.gitignore | 1 -
reddit-clone/poetry.lock | 14 +++++++++++++-
reddit-clone/pyproject.toml | 3 ++-
reddit-clone/reddit/storage.py | 20 ++++++++++++++++++++
4 files changed, 35 insertions(+), 3 deletions(-)
create mode 100644 reddit-clone/reddit/storage.py
diff --git a/.gitignore b/.gitignore
index 50af712e..f13d7bae 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,7 +14,6 @@ dist/
downloads/
eggs/
.eggs/
-lib/
lib64/
parts/
sdist/
diff --git a/reddit-clone/poetry.lock b/reddit-clone/poetry.lock
index ebf8ea00..ffd769f8 100644
--- a/reddit-clone/poetry.lock
+++ b/reddit-clone/poetry.lock
@@ -1,3 +1,11 @@
+[[package]]
+name = "aiofiles"
+version = "0.7.0"
+description = "File support for asyncio."
+category = "main"
+optional = false
+python-versions = ">=3.6,<4.0"
+
[[package]]
name = "aiosmtplib"
version = "1.1.6"
@@ -701,9 +709,13 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytes
[metadata]
lock-version = "1.1"
python-versions = "^3.7"
-content-hash = "2c06d6d834957f2c4efe2573570f18ee4bde2f8c41f227317761789709c4a736"
+content-hash = "de1904503b60be0c64385f92a9c8ab428553b7a6274eff9246e5ce1d22a5c1e3"
[metadata.files]
+aiofiles = [
+ {file = "aiofiles-0.7.0-py3-none-any.whl", hash = "sha256:c67a6823b5f23fcab0a2595a289cec7d8c863ffcb4322fb8cd6b90400aedfdbc"},
+ {file = "aiofiles-0.7.0.tar.gz", hash = "sha256:a1c4fc9b2ff81568c83e21392a82f344ea9d23da906e4f6a52662764545e19d4"},
+]
aiosmtplib = [
{file = "aiosmtplib-1.1.6-py3-none-any.whl", hash = "sha256:84174765778b2c5e0e207fbce0a769202fcf0c3de81faa87cc03551a6333bfa9"},
{file = "aiosmtplib-1.1.6.tar.gz", hash = "sha256:d138fe6ffecbc9e6320269690b9ac0b75e540ef96e8f5c77d4a306760014dce2"},
diff --git a/reddit-clone/pyproject.toml b/reddit-clone/pyproject.toml
index aaebbbce..83a57c39 100644
--- a/reddit-clone/pyproject.toml
+++ b/reddit-clone/pyproject.toml
@@ -1,7 +1,7 @@
[tool.poetry]
name = "reddit-graphql"
version = "0.1.0"
-description = "A reddit API clone built with GraphQL."
+description = "A Reddit API clone built with GraphQL."
authors = ["Aryan Iyappan "]
[tool.poetry.dependencies]
@@ -18,6 +18,7 @@ graphql-relay = "^3.1.0"
aiosmtplib = "^1.1.6"
schematics = "^2.1.1"
jinja2 = "^3.0.2"
+aiofiles = "^0.7.0"
[tool.poetry.dev-dependencies]
diff --git a/reddit-clone/reddit/storage.py b/reddit-clone/reddit/storage.py
new file mode 100644
index 00000000..6ca01fb6
--- /dev/null
+++ b/reddit-clone/reddit/storage.py
@@ -0,0 +1,20 @@
+from pathlib import Path
+
+import aiofiles
+
+__all__ = ("save_file",)
+
+
+def generate_file_name(file_name: str) -> str:
+ """
+ Generates a secure file name for the given file.
+ """
+
+
+async def save_file(file, path: Path) -> None:
+ """
+ Stores the file at the given file-path.
+ """
+ async with aiofiles.open(path, "w") as out:
+ await out.write(file)
+ await out.flush()
From e17eb2844de14ffcfe0228ff431267507fdd5cdf Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Mon, 11 Oct 2021 09:00:07 +0530
Subject: [PATCH 079/150] update storage helper: generate-file-name
---
reddit-clone/reddit/storage.py | 2 ++
1 file changed, 2 insertions(+)
diff --git a/reddit-clone/reddit/storage.py b/reddit-clone/reddit/storage.py
index 6ca01fb6..d7ca389c 100644
--- a/reddit-clone/reddit/storage.py
+++ b/reddit-clone/reddit/storage.py
@@ -1,4 +1,5 @@
from pathlib import Path
+from hashlib import sha256
import aiofiles
@@ -9,6 +10,7 @@ def generate_file_name(file_name: str) -> str:
"""
Generates a secure file name for the given file.
"""
+ return sha256(file_name.encode("utf-8")).hexdigest()
async def save_file(file, path: Path) -> None:
From 141951d4b45ea1557b2154e66906d1a7959a59d5 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Mon, 11 Oct 2021 11:09:12 +0530
Subject: [PATCH 080/150] switch to starlette
why? It's because FastAPI introduces a lot of other dependencies like pydantic which we don't really need at the moment.
---
reddit-clone/README.md | 8 +--
reddit-clone/poetry.lock | 121 +++++++++++++++-----------------
reddit-clone/pyproject.toml | 3 +-
reddit-clone/reddit/__init__.py | 14 ++--
4 files changed, 65 insertions(+), 81 deletions(-)
diff --git a/reddit-clone/README.md b/reddit-clone/README.md
index 391d7c20..4f45270b 100644
--- a/reddit-clone/README.md
+++ b/reddit-clone/README.md
@@ -8,16 +8,16 @@ most of the features that Strawberry gives us.
## Tech Stack used:
- [Strawberry GraphQL](https://github.com/strawberry-graphql/strawberry)
-- [FastAPI](https://github.com/tiangolo/fastapi) w/ [Starlette](https://github.com/encode/starlette)
-- [SQLAlchemy](https://github.com/sqlalchemy/sqlalchemy) (asyncio)
-- [Alembic](https://github.com/sqlalchemy/alembic) for migrations
+- [Starlette](https://github.com/encode/starlette) web framework
+- [SQLAlchemy](https://github.com/sqlalchemy/sqlalchemy) core/ mapper (asyncio)
+- [Alembic](https://github.com/sqlalchemy/alembic) migrations
## Features at a glance
+- [ ] Implements the Relay spec
- [ ] Error modelling within the schema
- [ ] Authorization with the permissions API
- [ ] Batch loading with dataloaders
-- [ ] Pagination with SQLAlchemy
- [x] modular codebase
## How to use
diff --git a/reddit-clone/poetry.lock b/reddit-clone/poetry.lock
index ffd769f8..6736a64e 100644
--- a/reddit-clone/poetry.lock
+++ b/reddit-clone/poetry.lock
@@ -35,6 +35,24 @@ SQLAlchemy = ">=1.3.0"
[package.extras]
tz = ["python-dateutil"]
+[[package]]
+name = "anyio"
+version = "3.3.3"
+description = "High level compatibility layer for multiple asynchronous event loop implementations"
+category = "main"
+optional = false
+python-versions = ">=3.6.2"
+
+[package.dependencies]
+idna = ">=2.8"
+sniffio = ">=1.1"
+typing-extensions = {version = "*", markers = "python_version < \"3.8\""}
+
+[package.extras]
+doc = ["sphinx-rtd-theme", "sphinx-autodoc-typehints (>=1.2.0)"]
+test = ["coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "pytest (>=6.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (<0.15)", "mock (>=4)", "uvloop (>=0.15)"]
+trio = ["trio (>=0.16)"]
+
[[package]]
name = "argon2-cffi"
version = "21.1.0"
@@ -162,24 +180,6 @@ category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
-[[package]]
-name = "fastapi"
-version = "0.68.2"
-description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production"
-category = "main"
-optional = false
-python-versions = ">=3.6.1"
-
-[package.dependencies]
-pydantic = ">=1.6.2,<1.7 || >1.7,<1.7.1 || >1.7.1,<1.7.2 || >1.7.2,<1.7.3 || >1.7.3,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0"
-starlette = "0.14.2"
-
-[package.extras]
-all = ["requests (>=2.24.0,<3.0.0)", "aiofiles (>=0.5.0,<0.8.0)", "jinja2 (>=2.11.2,<3.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "itsdangerous (>=1.1.0,<2.0.0)", "pyyaml (>=5.3.1,<6.0.0)", "graphene (>=2.1.8,<3.0.0)", "ujson (>=4.0.1,<5.0.0)", "orjson (>=3.2.1,<4.0.0)", "email_validator (>=1.1.1,<2.0.0)", "uvicorn[standard] (>=0.12.0,<0.16.0)", "async_exit_stack (>=1.0.1,<2.0.0)", "async_generator (>=1.10,<2.0.0)"]
-dev = ["python-jose[cryptography] (>=3.3.0,<4.0.0)", "passlib[bcrypt] (>=1.7.2,<2.0.0)", "autoflake (>=1.4.0,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)", "uvicorn[standard] (>=0.12.0,<0.16.0)", "graphene (>=2.1.8,<3.0.0)"]
-doc = ["mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=7.1.9,<8.0.0)", "mdx-include (>=1.4.1,<2.0.0)", "mkdocs-markdownextradata-plugin (>=0.1.7,<0.3.0)", "typer-cli (>=0.0.12,<0.0.13)", "pyyaml (>=5.3.1,<6.0.0)"]
-test = ["pytest (>=6.2.4,<7.0.0)", "pytest-cov (>=2.12.0,<4.0.0)", "pytest-asyncio (>=0.14.0,<0.16.0)", "mypy (==0.910)", "flake8 (>=3.8.3,<4.0.0)", "black (==21.9b0)", "isort (>=5.0.6,<6.0.0)", "requests (>=2.24.0,<3.0.0)", "httpx (>=0.14.0,<0.19.0)", "email_validator (>=1.1.1,<2.0.0)", "sqlalchemy (>=1.3.18,<1.5.0)", "peewee (>=3.13.3,<4.0.0)", "databases[sqlite] (>=0.3.2,<0.6.0)", "orjson (>=3.2.1,<4.0.0)", "ujson (>=4.0.1,<5.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "aiofiles (>=0.5.0,<0.8.0)", "flask (>=1.1.2,<2.0.0)", "async_exit_stack (>=1.0.1,<2.0.0)", "async_generator (>=1.10,<2.0.0)", "types-ujson (==0.1.1)", "types-orjson (==3.6.0)", "types-dataclasses (==0.1.7)"]
-
[[package]]
name = "flake8"
version = "3.9.2"
@@ -272,6 +272,14 @@ python-versions = "*"
[package.extras]
test = ["Cython (==0.29.22)"]
+[[package]]
+name = "idna"
+version = "3.2"
+description = "Internationalized Domain Names in Applications (IDNA)"
+category = "main"
+optional = false
+python-versions = ">=3.5"
+
[[package]]
name = "importlib-metadata"
version = "4.8.1"
@@ -428,21 +436,6 @@ category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
-[[package]]
-name = "pydantic"
-version = "1.8.2"
-description = "Data validation and settings management using python 3.6 type hinting"
-category = "main"
-optional = false
-python-versions = ">=3.6.1"
-
-[package.dependencies]
-typing-extensions = ">=3.7.4.3"
-
-[package.extras]
-dotenv = ["python-dotenv (>=0.10.4)"]
-email = ["email-validator (>=1.0.3)"]
-
[[package]]
name = "pyflakes"
version = "2.3.1"
@@ -524,6 +517,14 @@ category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
+[[package]]
+name = "sniffio"
+version = "1.2.0"
+description = "Sniff out which async library your code is running under"
+category = "main"
+optional = false
+python-versions = ">=3.5"
+
[[package]]
name = "sqlalchemy"
version = "1.4.25"
@@ -572,14 +573,18 @@ typing-extensions = ">=3.7.4"
[[package]]
name = "starlette"
-version = "0.14.2"
+version = "0.16.0"
description = "The little ASGI library that shines."
category = "main"
optional = false
python-versions = ">=3.6"
+[package.dependencies]
+anyio = ">=3.0.0,<4"
+typing-extensions = {version = "*", markers = "python_version < \"3.8\""}
+
[package.extras]
-full = ["aiofiles", "graphene", "itsdangerous", "jinja2", "python-multipart", "pyyaml", "requests"]
+full = ["itsdangerous", "jinja2", "python-multipart", "pyyaml", "requests", "graphene"]
[[package]]
name = "strawberry-graphql"
@@ -709,7 +714,7 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytes
[metadata]
lock-version = "1.1"
python-versions = "^3.7"
-content-hash = "de1904503b60be0c64385f92a9c8ab428553b7a6274eff9246e5ce1d22a5c1e3"
+content-hash = "9077eb8388b8be7ad3b4c11297b10ba19989b6c33bdd4acb57de30bd2d8090e6"
[metadata.files]
aiofiles = [
@@ -724,6 +729,10 @@ alembic = [
{file = "alembic-1.7.4-py3-none-any.whl", hash = "sha256:e3cab9e59778b3b6726bb2da9ced451c6622d558199fd3ef914f3b1e8f4ef704"},
{file = "alembic-1.7.4.tar.gz", hash = "sha256:9d33f3ff1488c4bfab1e1a6dfebbf085e8a8e1a3e047a43ad29ad1f67f012a1d"},
]
+anyio = [
+ {file = "anyio-3.3.3-py3-none-any.whl", hash = "sha256:56ceaeed2877723578b1341f4f68c29081db189cfb40a97d1922b9513f6d7db6"},
+ {file = "anyio-3.3.3.tar.gz", hash = "sha256:8eccec339cb4a856c94a75d50fc1d451faf32a05ef406be462e2efc59c9838b0"},
+]
argon2-cffi = [
{file = "argon2-cffi-21.1.0.tar.gz", hash = "sha256:f710b61103d1a1f692ca3ecbd1373e28aa5e545ac625ba067ff2feca1b2bb870"},
{file = "argon2_cffi-21.1.0-cp35-abi3-macosx_10_14_x86_64.whl", hash = "sha256:217b4f0f853ccbbb5045242946ad2e162e396064575860141b71a85eb47e475a"},
@@ -823,10 +832,6 @@ colorama = [
{file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"},
{file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"},
]
-fastapi = [
- {file = "fastapi-0.68.2-py3-none-any.whl", hash = "sha256:36bcdd3dbea87c586061005e4a40b9bd0145afd766655b4e0ec1d8870b32555c"},
- {file = "fastapi-0.68.2.tar.gz", hash = "sha256:38526fc46bda73f7ec92033952677323c16061e70a91d15c95f18b11895da494"},
-]
flake8 = [
{file = "flake8-3.9.2-py2.py3-none-any.whl", hash = "sha256:bf8fd333346d844f616e8d47905ef3a3384edae6b4e9beb0c5101e25e3110907"},
{file = "flake8-3.9.2.tar.gz", hash = "sha256:07528381786f2a6237b061f6e96610a4167b226cb926e2aa2b6b1d78057c576b"},
@@ -920,6 +925,10 @@ httptools = [
{file = "httptools-0.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:200fc1cdf733a9ff554c0bb97a4047785cfaad9875307d6087001db3eb2b417f"},
{file = "httptools-0.2.0.tar.gz", hash = "sha256:94505026be56652d7a530ab03d89474dc6021019d6b8682281977163b3471ea0"},
]
+idna = [
+ {file = "idna-3.2-py3-none-any.whl", hash = "sha256:14475042e284991034cb48e06f6851428fb14c4dc953acd9be9a5e95c7b6dd7a"},
+ {file = "idna-3.2.tar.gz", hash = "sha256:467fbad99067910785144ce333826c71fb0e63a425657295239737f7ecd125f3"},
+]
importlib-metadata = [
{file = "importlib_metadata-4.8.1-py3-none-any.whl", hash = "sha256:b618b6d2d5ffa2f16add5697cf57a46c76a56229b0ed1c438322e4e95645bd15"},
{file = "importlib_metadata-4.8.1.tar.gz", hash = "sha256:f284b3e11256ad1e5d03ab86bb2ccd6f5339688ff17a4d797a0fe7df326f23b1"},
@@ -1025,30 +1034,6 @@ pycparser = [
{file = "pycparser-2.20-py2.py3-none-any.whl", hash = "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"},
{file = "pycparser-2.20.tar.gz", hash = "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0"},
]
-pydantic = [
- {file = "pydantic-1.8.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:05ddfd37c1720c392f4e0d43c484217b7521558302e7069ce8d318438d297739"},
- {file = "pydantic-1.8.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:a7c6002203fe2c5a1b5cbb141bb85060cbff88c2d78eccbc72d97eb7022c43e4"},
- {file = "pydantic-1.8.2-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:589eb6cd6361e8ac341db97602eb7f354551482368a37f4fd086c0733548308e"},
- {file = "pydantic-1.8.2-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:10e5622224245941efc193ad1d159887872776df7a8fd592ed746aa25d071840"},
- {file = "pydantic-1.8.2-cp36-cp36m-win_amd64.whl", hash = "sha256:99a9fc39470010c45c161a1dc584997f1feb13f689ecf645f59bb4ba623e586b"},
- {file = "pydantic-1.8.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a83db7205f60c6a86f2c44a61791d993dff4b73135df1973ecd9eed5ea0bda20"},
- {file = "pydantic-1.8.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:41b542c0b3c42dc17da70554bc6f38cbc30d7066d2c2815a94499b5684582ecb"},
- {file = "pydantic-1.8.2-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:ea5cb40a3b23b3265f6325727ddfc45141b08ed665458be8c6285e7b85bd73a1"},
- {file = "pydantic-1.8.2-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:18b5ea242dd3e62dbf89b2b0ec9ba6c7b5abaf6af85b95a97b00279f65845a23"},
- {file = "pydantic-1.8.2-cp37-cp37m-win_amd64.whl", hash = "sha256:234a6c19f1c14e25e362cb05c68afb7f183eb931dd3cd4605eafff055ebbf287"},
- {file = "pydantic-1.8.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:021ea0e4133e8c824775a0cfe098677acf6fa5a3cbf9206a376eed3fc09302cd"},
- {file = "pydantic-1.8.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:e710876437bc07bd414ff453ac8ec63d219e7690128d925c6e82889d674bb505"},
- {file = "pydantic-1.8.2-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:ac8eed4ca3bd3aadc58a13c2aa93cd8a884bcf21cb019f8cfecaae3b6ce3746e"},
- {file = "pydantic-1.8.2-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:4a03cbbe743e9c7247ceae6f0d8898f7a64bb65800a45cbdc52d65e370570820"},
- {file = "pydantic-1.8.2-cp38-cp38-win_amd64.whl", hash = "sha256:8621559dcf5afacf0069ed194278f35c255dc1a1385c28b32dd6c110fd6531b3"},
- {file = "pydantic-1.8.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8b223557f9510cf0bfd8b01316bf6dd281cf41826607eada99662f5e4963f316"},
- {file = "pydantic-1.8.2-cp39-cp39-manylinux1_i686.whl", hash = "sha256:244ad78eeb388a43b0c927e74d3af78008e944074b7d0f4f696ddd5b2af43c62"},
- {file = "pydantic-1.8.2-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:05ef5246a7ffd2ce12a619cbb29f3307b7c4509307b1b49f456657b43529dc6f"},
- {file = "pydantic-1.8.2-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:54cd5121383f4a461ff7644c7ca20c0419d58052db70d8791eacbbe31528916b"},
- {file = "pydantic-1.8.2-cp39-cp39-win_amd64.whl", hash = "sha256:4be75bebf676a5f0f87937c6ddb061fa39cbea067240d98e298508c1bda6f3f3"},
- {file = "pydantic-1.8.2-py3-none-any.whl", hash = "sha256:fec866a0b59f372b7e776f2d7308511784dace622e0992a0b59ea3ccee0ae833"},
- {file = "pydantic-1.8.2.tar.gz", hash = "sha256:26464e57ccaafe72b7ad156fdaa4e9b9ef051f69e175dbbb463283000c05ab7b"},
-]
pyflakes = [
{file = "pyflakes-2.3.1-py2.py3-none-any.whl", hash = "sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3"},
{file = "pyflakes-2.3.1.tar.gz", hash = "sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db"},
@@ -1150,6 +1135,10 @@ six = [
{file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
]
+sniffio = [
+ {file = "sniffio-1.2.0-py3-none-any.whl", hash = "sha256:471b71698eac1c2112a40ce2752bb2f4a4814c22a54a3eed3676bc0f5ca9f663"},
+ {file = "sniffio-1.2.0.tar.gz", hash = "sha256:c4666eecec1d3f50960c6bdf61ab7bc350648da6c126e3cf6898d8cd4ddcd3de"},
+]
sqlalchemy = [
{file = "SQLAlchemy-1.4.25-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:a36ea43919e51b0de0c0bc52bcfdad7683f6ea9fb81b340cdabb9df0e045e0f7"},
{file = "SQLAlchemy-1.4.25-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:75cd5d48389a7635393ff5a9214b90695c06b3d74912109c3b00ce7392b69c6c"},
@@ -1187,8 +1176,8 @@ sqlalchemy2-stubs = [
{file = "sqlalchemy2_stubs-0.0.2a17-py3-none-any.whl", hash = "sha256:6f112f9381a29575676c3012ce10ed6bceabfad11f1d5569d7072ccf66cfa060"},
]
starlette = [
- {file = "starlette-0.14.2-py3-none-any.whl", hash = "sha256:3c8e48e52736b3161e34c9f0e8153b4f32ec5d8995a3ee1d59410d92f75162ed"},
- {file = "starlette-0.14.2.tar.gz", hash = "sha256:7d49f4a27f8742262ef1470608c59ddbc66baf37c148e938c7038e6bc7a998aa"},
+ {file = "starlette-0.16.0-py3-none-any.whl", hash = "sha256:38eb24bf705a2c317e15868e384c1b8a12ca396e5a3c3a003db7e667c43f939f"},
+ {file = "starlette-0.16.0.tar.gz", hash = "sha256:e1904b5d0007aee24bdd3c43994be9b3b729f4f58e740200de1d623f8c3a8870"},
]
strawberry-graphql = [
{file = "strawberry-graphql-0.77.12.tar.gz", hash = "sha256:1adc770643fa8423345a8c4c7a9956172f49a2e01559981be9e4ead6e80611fe"},
diff --git a/reddit-clone/pyproject.toml b/reddit-clone/pyproject.toml
index 83a57c39..250792bf 100644
--- a/reddit-clone/pyproject.toml
+++ b/reddit-clone/pyproject.toml
@@ -12,13 +12,12 @@ strawberry-graphql = {extras = ["asgi"], version = "^0.77.0"}
passlib = {extras = ["argon2"], version = "^1.7.4"}
alembic = "^1.7.1"
asyncpg = "^0.24"
-fastapi = "^0.68.1"
-starlette = "^0.14"
graphql-relay = "^3.1.0"
aiosmtplib = "^1.1.6"
schematics = "^2.1.1"
jinja2 = "^3.0.2"
aiofiles = "^0.7.0"
+starlette = "^0.16.0"
[tool.poetry.dev-dependencies]
diff --git a/reddit-clone/reddit/__init__.py b/reddit-clone/reddit/__init__.py
index 1fd94fb2..cc799d90 100644
--- a/reddit-clone/reddit/__init__.py
+++ b/reddit-clone/reddit/__init__.py
@@ -1,13 +1,12 @@
from typing import Union, Optional, Any
-from fastapi import FastAPI
+from starlette.applications import Starlette
from starlette.requests import Request
from starlette.responses import Response
from starlette.websockets import WebSocket
from strawberry.dataloader import DataLoader
from strawberry.asgi import GraphQL
-from reddit import settings
from reddit.schema import schema
from reddit.users.loaders import load_users
@@ -23,19 +22,16 @@ async def get_context(
return context
-def create_application() -> FastAPI:
+def create_application() -> Starlette:
"""
Creates an application instance.
:return: The created application.
"""
- application = FastAPI(title="Reddit GraphQL", debug=settings.DEBUG)
+ app = Starlette()
+ app.add_route(path="/graphql", route=GraphQL(schema=schema, graphiql=True))
- graphql_app = MyGraphQL(schema=schema, graphiql=True, debug=settings.DEBUG)
-
- application.add_route(path="/graphql", route=graphql_app)
-
- return application
+ return app
app = create_application()
From 3687f4069086ef0635fa88a4cc8bfceae6a49c23 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Mon, 11 Oct 2021 11:10:34 +0530
Subject: [PATCH 081/150] pass debug value
---
reddit-clone/reddit/__init__.py | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/reddit-clone/reddit/__init__.py b/reddit-clone/reddit/__init__.py
index cc799d90..f65beec6 100644
--- a/reddit-clone/reddit/__init__.py
+++ b/reddit-clone/reddit/__init__.py
@@ -7,6 +7,7 @@
from strawberry.dataloader import DataLoader
from strawberry.asgi import GraphQL
+from reddit import settings
from reddit.schema import schema
from reddit.users.loaders import load_users
@@ -28,8 +29,11 @@ def create_application() -> Starlette:
:return: The created application.
"""
- app = Starlette()
- app.add_route(path="/graphql", route=GraphQL(schema=schema, graphiql=True))
+ app = Starlette(debug=settings.DEBUG)
+ app.add_route(
+ path="/graphql",
+ route=GraphQL(schema=schema, graphiql=True, debug=settings.DEBUG),
+ )
return app
From ffe32b1e5c6b40757546ecb68227a523796f60ac Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Mon, 11 Oct 2021 11:12:00 +0530
Subject: [PATCH 082/150] update graphql app
---
reddit-clone/reddit/__init__.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/reddit-clone/reddit/__init__.py b/reddit-clone/reddit/__init__.py
index f65beec6..fbf729db 100644
--- a/reddit-clone/reddit/__init__.py
+++ b/reddit-clone/reddit/__init__.py
@@ -30,9 +30,10 @@ def create_application() -> Starlette:
:return: The created application.
"""
app = Starlette(debug=settings.DEBUG)
+ graphql_app = GraphQL(schema=schema, graphiql=True, debug=settings.DEBUG)
app.add_route(
path="/graphql",
- route=GraphQL(schema=schema, graphiql=True, debug=settings.DEBUG),
+ route=graphql_app,
)
return app
From 3f16c6466a3f049819e3d40a1c0c402092e44b69 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Mon, 11 Oct 2021 11:20:55 +0530
Subject: [PATCH 083/150] create dir: utils
---
reddit-clone/reddit/utils/__init__.py | 0
reddit-clone/reddit/{ => utils}/emails.py | 0
reddit-clone/reddit/{ => utils}/storage.py | 0
reddit-clone/reddit/{ => utils}/templates.py | 0
4 files changed, 0 insertions(+), 0 deletions(-)
create mode 100644 reddit-clone/reddit/utils/__init__.py
rename reddit-clone/reddit/{ => utils}/emails.py (100%)
rename reddit-clone/reddit/{ => utils}/storage.py (100%)
rename reddit-clone/reddit/{ => utils}/templates.py (100%)
diff --git a/reddit-clone/reddit/utils/__init__.py b/reddit-clone/reddit/utils/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/reddit-clone/reddit/emails.py b/reddit-clone/reddit/utils/emails.py
similarity index 100%
rename from reddit-clone/reddit/emails.py
rename to reddit-clone/reddit/utils/emails.py
diff --git a/reddit-clone/reddit/storage.py b/reddit-clone/reddit/utils/storage.py
similarity index 100%
rename from reddit-clone/reddit/storage.py
rename to reddit-clone/reddit/utils/storage.py
diff --git a/reddit-clone/reddit/templates.py b/reddit-clone/reddit/utils/templates.py
similarity index 100%
rename from reddit-clone/reddit/templates.py
rename to reddit-clone/reddit/utils/templates.py
From bc586951a2617ed43ac3ca7eace1017fc036b662 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Mon, 11 Oct 2021 11:23:36 +0530
Subject: [PATCH 084/150] describe mail config values
---
reddit-clone/reddit/settings.py | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/reddit-clone/reddit/settings.py b/reddit-clone/reddit/settings.py
index 12ac8eee..9d57003c 100644
--- a/reddit-clone/reddit/settings.py
+++ b/reddit-clone/reddit/settings.py
@@ -11,9 +11,17 @@
# SQLAlchemy database URL.
DATABASE_URI: str = config("DATABASE_URI", cast=str)
-# mail client configuration.
+# mail client host name.
MAIL_HOST: str = config("MAIL_HOST", cast=str)
+
+# mail client port.
MAIL_PORT: int = config("MAIL_PORT", cast=int)
+
+# mail client auth username.
MAIL_USERNAME: Optional[str] = config("MAIL_USERNAME", cast=str, default=None)
+
+# mail client auth password.
MAIL_PASSWORD: Optional[str] = config("MAIL_PASSWORD", cast=str, default=None)
+
+# mail client sender address.
MAIL_SENDER: Optional[str] = config("MAIL_SENDER", cast=str, default=None)
From ec80bcae18f3a20e29142b60745f37f2e9bdd745 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Mon, 11 Oct 2021 11:32:18 +0530
Subject: [PATCH 085/150] update README.md
---
reddit-clone/README.md | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/reddit-clone/README.md b/reddit-clone/README.md
index 4f45270b..dbd24545 100644
--- a/reddit-clone/README.md
+++ b/reddit-clone/README.md
@@ -11,11 +11,13 @@ most of the features that Strawberry gives us.
- [Starlette](https://github.com/encode/starlette) web framework
- [SQLAlchemy](https://github.com/sqlalchemy/sqlalchemy) core/ mapper (asyncio)
- [Alembic](https://github.com/sqlalchemy/alembic) migrations
+- PostgreSQL database
## Features at a glance
- [ ] Implements the Relay spec
-- [ ] Error modelling within the schema
+- [x] data modelling with relations
+- [x] Error modelling within the schema
- [ ] Authorization with the permissions API
- [ ] Batch loading with dataloaders
- [x] modular codebase
From a2a6b8b040a57a7391712fdf64b8c1952d5739d4 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Mon, 11 Oct 2021 14:24:16 +0530
Subject: [PATCH 086/150] update README.md
---
reddit-clone/README.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/reddit-clone/README.md b/reddit-clone/README.md
index dbd24545..35168a8c 100644
--- a/reddit-clone/README.md
+++ b/reddit-clone/README.md
@@ -12,6 +12,7 @@ most of the features that Strawberry gives us.
- [SQLAlchemy](https://github.com/sqlalchemy/sqlalchemy) core/ mapper (asyncio)
- [Alembic](https://github.com/sqlalchemy/alembic) migrations
- PostgreSQL database
+- [Schematics](https://github.com/schematics/schematics) data validation
## Features at a glance
From 6e0dbc3897c26dc5d9e3f43acf75232ea0cbfad8 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+codebyaryan@users.noreply.github.com>
Date: Tue, 12 Oct 2021 14:34:03 +0530
Subject: [PATCH 087/150] Update templates.py
---
reddit-clone/reddit/utils/templates.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/reddit-clone/reddit/utils/templates.py b/reddit-clone/reddit/utils/templates.py
index 7240f333..567e1793 100644
--- a/reddit-clone/reddit/utils/templates.py
+++ b/reddit-clone/reddit/utils/templates.py
@@ -10,7 +10,7 @@
def render_template(template_name: str, *args, **kwargs):
"""
- Renders the html template with the given variables.
+ Renders the HTML template with the given variables.
"""
template = environment.get_template(name=template_name)
return template.render(*args, **kwargs)
From a7c2b0cfaeb8b962b1a2b381268b9dd61c00af84 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Wed, 13 Oct 2021 08:56:54 +0530
Subject: [PATCH 088/150] update strawberry (we need to access schema from
resolver info)
---
reddit-clone/poetry.lock | 36 ++++++++++++++++++++++++++----------
reddit-clone/pyproject.toml | 2 +-
2 files changed, 27 insertions(+), 11 deletions(-)
diff --git a/reddit-clone/poetry.lock b/reddit-clone/poetry.lock
index 6736a64e..46072db4 100644
--- a/reddit-clone/poetry.lock
+++ b/reddit-clone/poetry.lock
@@ -162,7 +162,7 @@ pycparser = "*"
[[package]]
name = "click"
-version = "8.0.2"
+version = "8.0.3"
description = "Composable command line interface toolkit"
category = "main"
optional = false
@@ -274,7 +274,7 @@ test = ["Cython (==0.29.22)"]
[[package]]
name = "idna"
-version = "3.2"
+version = "3.3"
description = "Internationalized Domain Names in Applications (IDNA)"
category = "main"
optional = false
@@ -509,6 +509,17 @@ category = "main"
optional = false
python-versions = "*"
+[[package]]
+name = "sentinel"
+version = "0.3.0"
+description = "Create sentinel objects, akin to None, NotImplemented, Ellipsis"
+category = "main"
+optional = false
+python-versions = ">=3.6,<4.0"
+
+[package.extras]
+varname = ["varname (>=0.1)"]
+
[[package]]
name = "six"
version = "1.16.0"
@@ -588,7 +599,7 @@ full = ["itsdangerous", "jinja2", "python-multipart", "pyyaml", "requests", "gra
[[package]]
name = "strawberry-graphql"
-version = "0.77.12"
+version = "0.82.2"
description = "A library for creating GraphQL APIs"
category = "main"
optional = false
@@ -601,6 +612,7 @@ graphql-core = ">=3.1.0,<3.2.0"
pygments = ">=2.3,<3.0"
python-dateutil = ">=2.7.0,<3.0.0"
python-multipart = ">=0.0.5,<0.0.6"
+sentinel = ">=0.3.0,<0.4.0"
starlette = {version = ">=0.13.6,<0.17.0", optional = true, markers = "extra == \"asgi\" or extra == \"debug-server\""}
typing_extensions = ">=3.7.4,<4.0.0"
@@ -714,7 +726,7 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytes
[metadata]
lock-version = "1.1"
python-versions = "^3.7"
-content-hash = "9077eb8388b8be7ad3b4c11297b10ba19989b6c33bdd4acb57de30bd2d8090e6"
+content-hash = "8d178b08b5558fedfba56530d5cedcd21cfc44a333d374c37487d542fa7be916"
[metadata.files]
aiofiles = [
@@ -825,8 +837,8 @@ cffi = [
{file = "cffi-1.14.6.tar.gz", hash = "sha256:c9a875ce9d7fe32887784274dd533c57909b7b1dcadcc128a2ac21331a9765dd"},
]
click = [
- {file = "click-8.0.2-py3-none-any.whl", hash = "sha256:3fab8aeb8f15f5452ae7511ad448977b3417325bceddd53df87e0bb81f3a8cf8"},
- {file = "click-8.0.2.tar.gz", hash = "sha256:7027bc7bbafaab8b2c2816861d8eb372429ee3c02e193fc2f93d6c4ab9de49c5"},
+ {file = "click-8.0.3-py3-none-any.whl", hash = "sha256:353f466495adaeb40b6b5f592f9f91cb22372351c84caeb068132442a4518ef3"},
+ {file = "click-8.0.3.tar.gz", hash = "sha256:410e932b050f5eed773c4cda94de75971c89cdb3155a72a0831139a79e5ecb5b"},
]
colorama = [
{file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"},
@@ -926,8 +938,8 @@ httptools = [
{file = "httptools-0.2.0.tar.gz", hash = "sha256:94505026be56652d7a530ab03d89474dc6021019d6b8682281977163b3471ea0"},
]
idna = [
- {file = "idna-3.2-py3-none-any.whl", hash = "sha256:14475042e284991034cb48e06f6851428fb14c4dc953acd9be9a5e95c7b6dd7a"},
- {file = "idna-3.2.tar.gz", hash = "sha256:467fbad99067910785144ce333826c71fb0e63a425657295239737f7ecd125f3"},
+ {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"},
+ {file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"},
]
importlib-metadata = [
{file = "importlib_metadata-4.8.1-py3-none-any.whl", hash = "sha256:b618b6d2d5ffa2f16add5697cf57a46c76a56229b0ed1c438322e4e95645bd15"},
@@ -1131,6 +1143,10 @@ schematics = [
{file = "schematics-2.1.1-py2.py3-none-any.whl", hash = "sha256:be2d451bfb86789975e5ec0864aec569b63cea9010f0d24cbbd992a4e564c647"},
{file = "schematics-2.1.1.tar.gz", hash = "sha256:34c87f51a25063bb498ae1cc201891b134cfcb329baf9e9f4f3ae869b767560f"},
]
+sentinel = [
+ {file = "sentinel-0.3.0-py3-none-any.whl", hash = "sha256:bd8710dd26752039c668604f6be2aaf741b56f7811c5924a4dcdfd74359244f3"},
+ {file = "sentinel-0.3.0.tar.gz", hash = "sha256:f28143aa4716dbc8f6193f5682176a3c33cd26aaae05d9ecf66c186a9887cc2d"},
+]
six = [
{file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
@@ -1180,8 +1196,8 @@ starlette = [
{file = "starlette-0.16.0.tar.gz", hash = "sha256:e1904b5d0007aee24bdd3c43994be9b3b729f4f58e740200de1d623f8c3a8870"},
]
strawberry-graphql = [
- {file = "strawberry-graphql-0.77.12.tar.gz", hash = "sha256:1adc770643fa8423345a8c4c7a9956172f49a2e01559981be9e4ead6e80611fe"},
- {file = "strawberry_graphql-0.77.12-py3-none-any.whl", hash = "sha256:2080d93c1a80139b10ae5248ba2e8878c0182362ad9853424eecdd5aebdf3331"},
+ {file = "strawberry-graphql-0.82.2.tar.gz", hash = "sha256:36ed3844d1a7fbaebf22698a72eaeaad2468e5c0ce4463ef678582ed70ca5883"},
+ {file = "strawberry_graphql-0.82.2-py3-none-any.whl", hash = "sha256:000f6e74c026e8defab37b6967a1e7491aa646251ddef265369e6ad510088077"},
]
toml = [
{file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
diff --git a/reddit-clone/pyproject.toml b/reddit-clone/pyproject.toml
index 250792bf..a08c906c 100644
--- a/reddit-clone/pyproject.toml
+++ b/reddit-clone/pyproject.toml
@@ -8,7 +8,7 @@ authors = ["Aryan Iyappan "]
python = "^3.7"
uvicorn = {extras = ["standard"], version = "^0.15.0"}
SQLAlchemy = {extras = ["mypy"], version = "^1.4.23"}
-strawberry-graphql = {extras = ["asgi"], version = "^0.77.0"}
+strawberry-graphql = {extras = ["asgi"], version = "^0.82.2"}
passlib = {extras = ["argon2"], version = "^1.7.4"}
alembic = "^1.7.1"
asyncpg = "^0.24"
From 9776bd336d13decbc36ec1bca3b1d6de3c6fd092 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Wed, 13 Oct 2021 09:01:21 +0530
Subject: [PATCH 089/150] update user queries
---
reddit-clone/reddit/comments/types.py | 4 ----
reddit-clone/reddit/posts/types.py | 4 ----
reddit-clone/reddit/subreddits/types.py | 4 ----
reddit-clone/reddit/users/queries.py | 10 ++++++++--
reddit-clone/reddit/users/types.py | 4 ----
5 files changed, 8 insertions(+), 18 deletions(-)
diff --git a/reddit-clone/reddit/comments/types.py b/reddit-clone/reddit/comments/types.py
index e5cc7697..ea79e755 100644
--- a/reddit-clone/reddit/comments/types.py
+++ b/reddit-clone/reddit/comments/types.py
@@ -47,7 +47,3 @@ async def resolve_node(cls, info: Info, comment_id: str) -> Optional[CommentType
comment = await session.execute(query)
if comment is not None:
return cls.from_instance(comment)
-
- @classmethod
- def from_instance(cls, instance: Comment) -> CommentType:
- pass
diff --git a/reddit-clone/reddit/posts/types.py b/reddit-clone/reddit/posts/types.py
index c9a1cb30..478ab205 100644
--- a/reddit-clone/reddit/posts/types.py
+++ b/reddit-clone/reddit/posts/types.py
@@ -72,7 +72,3 @@ async def resolve_node(cls, info: Info, post_id: str) -> Optional[PostType]:
post = await session.execute(query)
if post is not None:
return cls.from_instance(post)
-
- @classmethod
- def from_instance(cls, instance: Post) -> PostType:
- pass
diff --git a/reddit-clone/reddit/subreddits/types.py b/reddit-clone/reddit/subreddits/types.py
index 7bf23b32..3530fc56 100644
--- a/reddit-clone/reddit/subreddits/types.py
+++ b/reddit-clone/reddit/subreddits/types.py
@@ -63,7 +63,3 @@ async def resolve_node(
subreddit = await session.execute(query)
if subreddit is not None:
return cls.from_instance(subreddit)
-
- @classmethod
- def from_instance(cls, instance: Subreddit) -> SubredditType:
- pass
diff --git a/reddit-clone/reddit/users/queries.py b/reddit-clone/reddit/users/queries.py
index 50fe6e13..6f776a1b 100644
--- a/reddit-clone/reddit/users/queries.py
+++ b/reddit-clone/reddit/users/queries.py
@@ -1,14 +1,20 @@
-from typing import Optional
+from typing import Optional, cast
import strawberry
from strawberry.tools import create_type
from strawberry.types import Info
+from reddit.database import get_session
from reddit.users.types import UserType
+from reddit.users.models import User
async def resolve_user(info: Info, username: str) -> Optional[UserType]:
- pass
+ async with get_session() as session:
+ user = await User.by_username(session=session, username=username)
+ if user is not None:
+ return cast(UserType, user)
+ return user
user = strawberry.field(
diff --git a/reddit-clone/reddit/users/types.py b/reddit-clone/reddit/users/types.py
index 496e694f..997f92bf 100644
--- a/reddit-clone/reddit/users/types.py
+++ b/reddit-clone/reddit/users/types.py
@@ -56,7 +56,3 @@ async def resolve_node(cls, info: Info, user_id: str) -> Optional[UserType]:
user = await session.execute(query)
if user is not None:
return cls.from_instance(user)
-
- @classmethod
- def from_instance(cls, instance: User) -> UserType:
- pass
From 2f97af79d4e4d4c7e083b4787ea044af1dbbfb13 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Wed, 13 Oct 2021 09:06:08 +0530
Subject: [PATCH 090/150] update user queries
---
reddit-clone/reddit/users/queries.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/reddit-clone/reddit/users/queries.py b/reddit-clone/reddit/users/queries.py
index 6f776a1b..f6eef9e9 100644
--- a/reddit-clone/reddit/users/queries.py
+++ b/reddit-clone/reddit/users/queries.py
@@ -25,7 +25,7 @@ async def resolve_user(info: Info, username: str) -> Optional[UserType]:
)
-async def resolve_current_user(info: Info) -> UserType:
+async def resolve_current_user(info: Info) -> Optional[UserType]:
pass
From e2d796b90c9eace61139fd40f6aac26a4b620941 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Wed, 13 Oct 2021 11:05:28 +0530
Subject: [PATCH 091/150] add connection types
---
reddit-clone/reddit/base/types.py | 24 +++++++++++++++++++++++-
1 file changed, 23 insertions(+), 1 deletion(-)
diff --git a/reddit-clone/reddit/base/types.py b/reddit-clone/reddit/base/types.py
index 7d3096e7..452d4eef 100644
--- a/reddit-clone/reddit/base/types.py
+++ b/reddit-clone/reddit/base/types.py
@@ -1,6 +1,6 @@
from __future__ import annotations
-from typing import Optional, Type
+from typing import List, Optional, Type
import strawberry
from strawberry.types import Info
@@ -51,3 +51,25 @@ def from_global_id(cls, global_id: str):
@classmethod
def to_global_id(cls, schema_type: str, id: str) -> str:
return to_global_id(schema_type, id)
+
+
+@strawberry.type(name="Edge")
+class EdgeType:
+ cursor: str
+ node: NodeType
+
+
+@strawberry.type(name="PageInfo")
+class PageInfoType:
+ end_cursor: Optional[str]
+ has_next_page: bool
+ has_previous_page: bool
+ start_cursor: Optional[str]
+
+
+@strawberry.type(name="Connection")
+class ConnectionType:
+ edges: List[EdgeType]
+ nodes: List[NodeType]
+ page_info: PageInfoType
+ total_count: int
From feb613e630d06ca484c873d4b8d74a91b7bfb50a Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Wed, 13 Oct 2021 11:17:28 +0530
Subject: [PATCH 092/150] update node type
---
reddit-clone/reddit/base/types.py | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/reddit-clone/reddit/base/types.py b/reddit-clone/reddit/base/types.py
index 452d4eef..0218b30c 100644
--- a/reddit-clone/reddit/base/types.py
+++ b/reddit-clone/reddit/base/types.py
@@ -20,16 +20,16 @@ def resolve(
cls, info: Info, global_id: str, only_type=None
) -> Optional[Type[NodeType]]:
try:
- _type, _id = cls.from_global_id(global_id)
+ type_name, _id = cls.from_global_id(global_id)
except Exception as e:
raise Exception(
f'Unable to parse global ID "{global_id}". '
f"Exception message: {str(e)}"
)
- schema_type = info.schema.get_type(_type)
+ schema_type = info.schema.get_type_by_name(type_name)
if schema_type is None:
- raise Exception(f'Relay Node "{_type}" not found in schema')
+ raise Exception(f'Relay Node "{type_name}" not found in schema')
if only_type:
assert schema_type == only_type
@@ -37,7 +37,7 @@ def resolve(
# We make sure the ObjectType implements the "Node" interface
if cls not in schema_type._type_definition.interfaces:
raise Exception(
- f'ObjectType "{_type}" does not implement the "{cls}" interface.'
+ f'ObjectType "{type_name}" does not implement the "{cls}" interface.'
)
resolver = getattr(schema_type, "resolve_node", None)
From 4fea150d0e744e805e3f064d86712777c4a0cc84 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Wed, 13 Oct 2021 11:27:43 +0530
Subject: [PATCH 093/150] document relay types
---
reddit-clone/reddit/base/types.py | 67 ++++++++++++++++++++++++++-----
1 file changed, 57 insertions(+), 10 deletions(-)
diff --git a/reddit-clone/reddit/base/types.py b/reddit-clone/reddit/base/types.py
index 0218b30c..037cdaab 100644
--- a/reddit-clone/reddit/base/types.py
+++ b/reddit-clone/reddit/base/types.py
@@ -55,21 +55,68 @@ def to_global_id(cls, schema_type: str, id: str) -> str:
@strawberry.type(name="Edge")
class EdgeType:
- cursor: str
- node: NodeType
+ cursor: str = strawberry.field(
+ description="""
+ A cursor for use in pagination.
+ """
+ )
+
+ node: NodeType = strawberry.field(
+ description="""
+ The item at the end of the edge.
+ """
+ )
@strawberry.type(name="PageInfo")
class PageInfoType:
- end_cursor: Optional[str]
- has_next_page: bool
- has_previous_page: bool
- start_cursor: Optional[str]
+ end_cursor: Optional[str] = strawberry.field(
+ description="""
+ When paginating forwards, the cursor to continue.
+ """
+ )
+
+ has_next_page: bool = strawberry.field(
+ description="""
+ When paginating forwards, are there more items?
+ """
+ )
+
+ has_previous_page: bool = strawberry.field(
+ description="""
+ When paginating backwards, are there more items?
+ """
+ )
+
+ start_cursor: Optional[str] = strawberry.field(
+ description="""
+ When paginating backwards, the cursor to continue.
+ """
+ )
@strawberry.type(name="Connection")
class ConnectionType:
- edges: List[EdgeType]
- nodes: List[NodeType]
- page_info: PageInfoType
- total_count: int
+ edges: List[EdgeType] = strawberry.field(
+ description="""
+ Contains the edges in this connection.
+ """
+ )
+
+ nodes: List[NodeType] = strawberry.field(
+ description="""
+ Contains the nodes in this connection.
+ """
+ )
+
+ page_info: PageInfoType = strawberry.field(
+ description="""
+ Information to aid in pagination.
+ """
+ )
+
+ total_count: int = strawberry.field(
+ description="""
+ Identifies the total count of items in the connection.
+ """
+ )
From 4051849e7febee29c45efce8ccd3b83cfda0812a Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Wed, 13 Oct 2021 11:28:48 +0530
Subject: [PATCH 094/150] document relay types
---
reddit-clone/reddit/base/types.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/reddit-clone/reddit/base/types.py b/reddit-clone/reddit/base/types.py
index 037cdaab..002bb4c2 100644
--- a/reddit-clone/reddit/base/types.py
+++ b/reddit-clone/reddit/base/types.py
@@ -99,13 +99,13 @@ class PageInfoType:
class ConnectionType:
edges: List[EdgeType] = strawberry.field(
description="""
- Contains the edges in this connection.
+ Contains the edges in the connection.
"""
)
nodes: List[NodeType] = strawberry.field(
description="""
- Contains the nodes in this connection.
+ Contains the nodes in the connection.
"""
)
From f9ab261c3ce17fe6809a78dfa65eef13dfbded96 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Wed, 13 Oct 2021 11:36:32 +0530
Subject: [PATCH 095/150] simplify node type
---
reddit-clone/reddit/base/types.py | 7 +------
1 file changed, 1 insertion(+), 6 deletions(-)
diff --git a/reddit-clone/reddit/base/types.py b/reddit-clone/reddit/base/types.py
index 002bb4c2..5d7f1ff2 100644
--- a/reddit-clone/reddit/base/types.py
+++ b/reddit-clone/reddit/base/types.py
@@ -16,9 +16,7 @@ class NodeType:
)
@classmethod
- def resolve(
- cls, info: Info, global_id: str, only_type=None
- ) -> Optional[Type[NodeType]]:
+ def resolve(cls, info: Info, global_id: str) -> Optional[Type[NodeType]]:
try:
type_name, _id = cls.from_global_id(global_id)
except Exception as e:
@@ -31,9 +29,6 @@ def resolve(
if schema_type is None:
raise Exception(f'Relay Node "{type_name}" not found in schema')
- if only_type:
- assert schema_type == only_type
-
# We make sure the ObjectType implements the "Node" interface
if cls not in schema_type._type_definition.interfaces:
raise Exception(
From 667dad572e69a8dd40d5d3a66e7c69b83ce9c7bf Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Thu, 14 Oct 2021 14:13:26 +0530
Subject: [PATCH 096/150] update services
---
reddit-clone/reddit/users/models.py | 21 ++-------------------
reddit-clone/reddit/users/services.py | 23 ++++++++++++++++++++---
2 files changed, 22 insertions(+), 22 deletions(-)
diff --git a/reddit-clone/reddit/users/models.py b/reddit-clone/reddit/users/models.py
index 87aa74b6..8e23be22 100644
--- a/reddit-clone/reddit/users/models.py
+++ b/reddit-clone/reddit/users/models.py
@@ -1,10 +1,9 @@
from __future__ import annotations
-from typing import List, Optional
+from typing import List
from passlib.hash import argon2
-from sqlalchemy import select, Column, String, Integer, ForeignKey
-from sqlalchemy.ext.asyncio import AsyncSession
+from sqlalchemy import Column, String, Integer, ForeignKey
from sqlalchemy.orm import relationship
from sqlalchemy.sql.sqltypes import Boolean
@@ -46,22 +45,6 @@ class User(Base):
def __repr__(self) -> str:
return f""
- @classmethod
- async def by_username(cls, session: AsyncSession, username: str) -> Optional[User]:
- """
- Gets an user by their username.
- """
- query = select(User).filter_by(username=username).first()
- return await session.execute(query)
-
- @classmethod
- async def by_email(cls, session: AsyncSession, email: str) -> Optional[User]:
- """
- Gets an user by their email.
- """
- query = select(User).filter_by(email=email).first()
- return await session.execute(query)
-
def set_password(self, password: str) -> None:
"""
Sets a hashed version of the provided
diff --git a/reddit-clone/reddit/users/services.py b/reddit-clone/reddit/users/services.py
index 15f3161a..9668fd5f 100644
--- a/reddit-clone/reddit/users/services.py
+++ b/reddit-clone/reddit/users/services.py
@@ -1,17 +1,34 @@
from typing import Optional
+from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from reddit.users.models import User
+async def user_by_email(session: AsyncSession, email: str) -> Optional[User]:
+ """
+ Gets an user by their email.
+ """
+ query = select(User).filter_by(email=email).first()
+ return await session.execute(query)
+
+
+async def user_by_username(session: AsyncSession, username: str) -> Optional[User]:
+ """
+ Gets an user by their username.
+ """
+ query = select(User).filter_by(username=username).first()
+ return await session.execute(query)
+
+
async def authenticate(
session: AsyncSession, username: str, password: str
) -> Optional[User]:
"""
Checks if the provided user credentials are valid.
"""
- user = await User.by_username(session=session, username=username)
+ user = await user_by_username(session=session, username=username)
if user is None or not user.check_password(password=password):
# TODO: handle exception here.
pass
@@ -59,7 +76,7 @@ async def request_change_email(
if not user.check_password(password=password):
# TODO: handle exception here.
pass
- user = await User.by_email(session=session, email=email)
+ user = await user_by_email(session=session, email=email)
if user is not None:
# TODO: handle exception here.
pass
@@ -80,7 +97,7 @@ async def request_reset_password(session: AsyncSession, email: str):
Sends a password reset code to the given email, if it
actually exists.
"""
- user = await User.by_email(session=session, email=email)
+ user = await user_by_email(session=session, email=email)
if user is not None:
# TODO: send password reset email here.
pass
From 58f2da054873db823ccb3917b3597b617e3b3e99 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Thu, 14 Oct 2021 14:25:19 +0530
Subject: [PATCH 097/150] update user services
---
reddit-clone/reddit/users/services.py | 8 ++++++++
reddit-clone/reddit/users/types.py | 10 ++++------
2 files changed, 12 insertions(+), 6 deletions(-)
diff --git a/reddit-clone/reddit/users/services.py b/reddit-clone/reddit/users/services.py
index 9668fd5f..060cb069 100644
--- a/reddit-clone/reddit/users/services.py
+++ b/reddit-clone/reddit/users/services.py
@@ -6,6 +6,14 @@
from reddit.users.models import User
+async def user_by_id(session: AsyncSession, id: int) -> Optional[User]:
+ """
+ Gets an user by their ID.
+ """
+ query = select(User).filter_by(id=id).first()
+ return await session.execute(query)
+
+
async def user_by_email(session: AsyncSession, email: str) -> Optional[User]:
"""
Gets an user by their email.
diff --git a/reddit-clone/reddit/users/types.py b/reddit-clone/reddit/users/types.py
index 997f92bf..07dc9bc5 100644
--- a/reddit-clone/reddit/users/types.py
+++ b/reddit-clone/reddit/users/types.py
@@ -1,12 +1,11 @@
from __future__ import annotations
-from typing import List, Optional
+from typing import List, Optional, cast
import strawberry
from strawberry.types import Info
-from sqlalchemy import select
-from reddit.users.models import User
+from reddit.users.services import user_by_id
from reddit.base.types import NodeType
from reddit.posts.types import PostType
from reddit.subreddits.types import SubredditType
@@ -51,8 +50,7 @@ async def resolve_node(cls, info: Info, user_id: str) -> Optional[UserType]:
"""
Gets an user with the given ID.
"""
- query = select(User).filter_by(id=user_id).first()
async with get_session() as session:
- user = await session.execute(query)
+ user = await user_by_id(session=session, id=user_id)
if user is not None:
- return cls.from_instance(user)
+ return cast(UserType, user)
From eeacb09f97c673e9d1bdba6709788ffaea45bf14 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Thu, 14 Oct 2021 15:02:00 +0530
Subject: [PATCH 098/150] add more dataloaders
---
reddit-clone/reddit/__init__.py | 10 +++++++++-
reddit-clone/reddit/comments/loaders.py | 20 ++++++++++++++++++++
reddit-clone/reddit/comments/types.py | 13 ++++---------
reddit-clone/reddit/posts/loaders.py | 18 ++++++++++++++++++
reddit-clone/reddit/posts/types.py | 13 ++++---------
reddit-clone/reddit/subreddits/loaders.py | 20 ++++++++++++++++++++
reddit-clone/reddit/subreddits/types.py | 13 ++++---------
reddit-clone/reddit/users/services.py | 8 --------
reddit-clone/reddit/users/types.py | 9 +++------
9 files changed, 82 insertions(+), 42 deletions(-)
create mode 100644 reddit-clone/reddit/comments/loaders.py
create mode 100644 reddit-clone/reddit/posts/loaders.py
create mode 100644 reddit-clone/reddit/subreddits/loaders.py
diff --git a/reddit-clone/reddit/__init__.py b/reddit-clone/reddit/__init__.py
index fbf729db..0378f785 100644
--- a/reddit-clone/reddit/__init__.py
+++ b/reddit-clone/reddit/__init__.py
@@ -10,6 +10,9 @@
from reddit import settings
from reddit.schema import schema
from reddit.users.loaders import load_users
+from reddit.subreddits.loaders import load_subreddits
+from reddit.posts.loaders import load_posts
+from reddit.comments.loaders import load_comments
__all__ = ("app",)
@@ -19,7 +22,12 @@ async def get_context(
self, request: Union[Request, WebSocket], response: Optional[Response] = None
) -> Optional[Any]:
context: dict = await super().get_context(request, response=response)
- context.update(user_loader=DataLoader(load_fn=load_users))
+ context.update(
+ user_loader=DataLoader(load_fn=load_users),
+ subreddit_loader=DataLoader(load_fn=load_subreddits),
+ post_loader=DataLoader(load_fn=load_posts),
+ comment_loader=DataLoader(load_fn=load_comments),
+ )
return context
diff --git a/reddit-clone/reddit/comments/loaders.py b/reddit-clone/reddit/comments/loaders.py
new file mode 100644
index 00000000..50b28da0
--- /dev/null
+++ b/reddit-clone/reddit/comments/loaders.py
@@ -0,0 +1,20 @@
+from typing import List, Optional, Dict
+
+from sqlalchemy import select
+from sqlalchemy.engine import Result
+
+from reddit.database import get_session
+from reddit.comments.models import Comment
+
+
+async def load_comments(comment_ids: List[int]) -> List[Optional[Comment]]:
+ """
+ Batch-loads comments by their IDs.
+ """
+ query = select(Comment).filter(Comment.id.in_(comment_ids))
+ async with get_session() as session:
+ result: Result = await session.execute(query)
+ comment_map: Dict[int, Comment] = {
+ comment.id: comment for comment in result.scalars()
+ }
+ return [comment_map.get(comment_id) for comment_id in comment_ids]
diff --git a/reddit-clone/reddit/comments/types.py b/reddit-clone/reddit/comments/types.py
index ea79e755..777d7da0 100644
--- a/reddit-clone/reddit/comments/types.py
+++ b/reddit-clone/reddit/comments/types.py
@@ -1,14 +1,11 @@
from __future__ import annotations
-from typing import List, Optional
+from typing import List, Optional, cast
import strawberry
from strawberry.types import Info
-from sqlalchemy import select
from reddit.base.types import NodeType
-from reddit.comments.models import Comment
-from reddit.database import get_session
@strawberry.type(name="Comment")
@@ -42,8 +39,6 @@ async def resolve_node(cls, info: Info, comment_id: str) -> Optional[CommentType
"""
Gets a comment with the given ID.
"""
- query = select(Comment).filter_by(id=comment_id).first()
- async with get_session() as session:
- comment = await session.execute(query)
- if comment is not None:
- return cls.from_instance(comment)
+ loader = info.context.get("comment_loader")
+ comment = await loader.load(comment_id)
+ return cast(CommentType, comment)
diff --git a/reddit-clone/reddit/posts/loaders.py b/reddit-clone/reddit/posts/loaders.py
new file mode 100644
index 00000000..bf8cb95e
--- /dev/null
+++ b/reddit-clone/reddit/posts/loaders.py
@@ -0,0 +1,18 @@
+from typing import List, Optional, Dict
+
+from sqlalchemy import select
+from sqlalchemy.engine import Result
+
+from reddit.database import get_session
+from reddit.posts.models import Post
+
+
+async def load_posts(post_ids: List[int]) -> List[Optional[Post]]:
+ """
+ Batch-loads posts by their IDs.
+ """
+ query = select(Post).filter(Post.id.in_(post_ids))
+ async with get_session() as session:
+ result: Result = await session.execute(query)
+ post_map: Dict[int, Post] = {post.id: post for post in result.scalars()}
+ return [post_map.get(post_id) for post_id in post_ids]
diff --git a/reddit-clone/reddit/posts/types.py b/reddit-clone/reddit/posts/types.py
index 478ab205..67a58baa 100644
--- a/reddit-clone/reddit/posts/types.py
+++ b/reddit-clone/reddit/posts/types.py
@@ -1,15 +1,12 @@
from __future__ import annotations
-from typing import List, Optional
+from typing import List, Optional, cast
import strawberry
from strawberry.types import Info
-from sqlalchemy import select
from reddit.base.types import NodeType
-from reddit.posts.models import Post
from reddit.comments.types import CommentType
-from reddit.database import get_session
@strawberry.type(name="Post")
@@ -67,8 +64,6 @@ async def resolve_node(cls, info: Info, post_id: str) -> Optional[PostType]:
"""
Gets a post with the given ID.
"""
- query = select(Post).filter_by(id=post_id).first()
- async with get_session() as session:
- post = await session.execute(query)
- if post is not None:
- return cls.from_instance(post)
+ loader = info.context.get("post_loader")
+ post = await loader.load(post_id)
+ return cast(PostType, post)
diff --git a/reddit-clone/reddit/subreddits/loaders.py b/reddit-clone/reddit/subreddits/loaders.py
new file mode 100644
index 00000000..39d96a3a
--- /dev/null
+++ b/reddit-clone/reddit/subreddits/loaders.py
@@ -0,0 +1,20 @@
+from typing import List, Optional, Dict
+
+from sqlalchemy import select
+from sqlalchemy.engine import Result
+
+from reddit.database import get_session
+from reddit.subreddits.models import Subreddit
+
+
+async def load_subreddits(subreddit_ids: List[int]) -> List[Optional[Subreddit]]:
+ """
+ Batch-loads subreddits by their IDs.
+ """
+ query = select(Subreddit).filter(Subreddit.id.in_(subreddit_ids))
+ async with get_session() as session:
+ result: Result = await session.execute(query)
+ subreddit_map: Dict[int, Subreddit] = {
+ subreddit.id: subreddit for subreddit in result.scalars()
+ }
+ return [subreddit_map.get(subreddit_id) for subreddit_id in subreddit_ids]
diff --git a/reddit-clone/reddit/subreddits/types.py b/reddit-clone/reddit/subreddits/types.py
index 3530fc56..43fb65e1 100644
--- a/reddit-clone/reddit/subreddits/types.py
+++ b/reddit-clone/reddit/subreddits/types.py
@@ -1,15 +1,12 @@
from __future__ import annotations
-from typing import List, Optional
+from typing import List, Optional, cast
import strawberry
from strawberry.types import Info
-from sqlalchemy import select
from reddit.base.types import NodeType
-from reddit.subreddits.models import Subreddit
from reddit.posts.types import PostType
-from reddit.database import get_session
@strawberry.type(name="Subreddit")
@@ -58,8 +55,6 @@ async def resolve_node(
"""
Gets a Subreddit with the given ID.
"""
- query = select(Subreddit).filter_by(id=subreddit_id).first()
- async with get_session() as session:
- subreddit = await session.execute(query)
- if subreddit is not None:
- return cls.from_instance(subreddit)
+ loader = info.context.get("subreddit_loader")
+ subreddit = await loader.load(subreddit_id)
+ return cast(SubredditType, subreddit)
diff --git a/reddit-clone/reddit/users/services.py b/reddit-clone/reddit/users/services.py
index 060cb069..9668fd5f 100644
--- a/reddit-clone/reddit/users/services.py
+++ b/reddit-clone/reddit/users/services.py
@@ -6,14 +6,6 @@
from reddit.users.models import User
-async def user_by_id(session: AsyncSession, id: int) -> Optional[User]:
- """
- Gets an user by their ID.
- """
- query = select(User).filter_by(id=id).first()
- return await session.execute(query)
-
-
async def user_by_email(session: AsyncSession, email: str) -> Optional[User]:
"""
Gets an user by their email.
diff --git a/reddit-clone/reddit/users/types.py b/reddit-clone/reddit/users/types.py
index 07dc9bc5..726b3aff 100644
--- a/reddit-clone/reddit/users/types.py
+++ b/reddit-clone/reddit/users/types.py
@@ -5,12 +5,10 @@
import strawberry
from strawberry.types import Info
-from reddit.users.services import user_by_id
from reddit.base.types import NodeType
from reddit.posts.types import PostType
from reddit.subreddits.types import SubredditType
from reddit.comments.types import CommentType
-from reddit.database import get_session
@strawberry.type(name="User")
@@ -50,7 +48,6 @@ async def resolve_node(cls, info: Info, user_id: str) -> Optional[UserType]:
"""
Gets an user with the given ID.
"""
- async with get_session() as session:
- user = await user_by_id(session=session, id=user_id)
- if user is not None:
- return cast(UserType, user)
+ loader = info.context.get("user_loader")
+ user = await loader.load(user_id)
+ return cast(UserType, user)
From 699a09a071088ebe0a376458c37d3b75da3884d4 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Thu, 14 Oct 2021 15:02:48 +0530
Subject: [PATCH 099/150] fix graphql asgi app
---
reddit-clone/reddit/__init__.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/reddit-clone/reddit/__init__.py b/reddit-clone/reddit/__init__.py
index 0378f785..0f2b396c 100644
--- a/reddit-clone/reddit/__init__.py
+++ b/reddit-clone/reddit/__init__.py
@@ -38,7 +38,7 @@ def create_application() -> Starlette:
:return: The created application.
"""
app = Starlette(debug=settings.DEBUG)
- graphql_app = GraphQL(schema=schema, graphiql=True, debug=settings.DEBUG)
+ graphql_app = MyGraphQL(schema=schema, graphiql=True, debug=settings.DEBUG)
app.add_route(
path="/graphql",
route=graphql_app,
From 1a33dc34b4250011bfcc36a707b8c1a087c4f4d3 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Thu, 14 Oct 2021 15:17:44 +0530
Subject: [PATCH 100/150] simplify base query
---
reddit-clone/reddit/__init__.py | 2 +-
reddit-clone/reddit/base/queries.py | 20 +++++++++++++++-
reddit-clone/reddit/base/types.py | 36 +----------------------------
3 files changed, 21 insertions(+), 37 deletions(-)
diff --git a/reddit-clone/reddit/__init__.py b/reddit-clone/reddit/__init__.py
index 0f2b396c..de20772a 100644
--- a/reddit-clone/reddit/__init__.py
+++ b/reddit-clone/reddit/__init__.py
@@ -21,7 +21,7 @@ class MyGraphQL(GraphQL):
async def get_context(
self, request: Union[Request, WebSocket], response: Optional[Response] = None
) -> Optional[Any]:
- context: dict = await super().get_context(request, response=response)
+ context = await super().get_context(request, response=response)
context.update(
user_loader=DataLoader(load_fn=load_users),
subreddit_loader=DataLoader(load_fn=load_subreddits),
diff --git a/reddit-clone/reddit/base/queries.py b/reddit-clone/reddit/base/queries.py
index b70f4744..3baeb032 100644
--- a/reddit-clone/reddit/base/queries.py
+++ b/reddit-clone/reddit/base/queries.py
@@ -3,12 +3,30 @@
import strawberry
from strawberry.tools import create_type
from strawberry.types import Info
+from graphql_relay import from_global_id
from reddit.base.types import NodeType
def resolve_node(info: Info, id: strawberry.ID) -> Optional[NodeType]:
- return NodeType.resolve(info=info, global_id=id)
+ try:
+ type_name, _id = from_global_id(id)
+ except Exception:
+ raise Exception(f'Unable to parse global ID "{id}".')
+
+ schema_type = info.schema.get_type_by_name(type_name)
+ if schema_type is None:
+ raise Exception(f'Relay Node "{type_name}" not found in schema')
+
+ # We make sure the ObjectType implements the "Node" interface
+ if NodeType not in schema_type._type_definition.interfaces:
+ raise Exception(
+ f'ObjectType "{type_name}" does not implement the "{NodeType}" interface.'
+ )
+
+ resolver = getattr(schema_type, "resolve_node", None)
+ if resolver is not None:
+ return resolver(info, _id)
node = strawberry.field(
diff --git a/reddit-clone/reddit/base/types.py b/reddit-clone/reddit/base/types.py
index 5d7f1ff2..47547737 100644
--- a/reddit-clone/reddit/base/types.py
+++ b/reddit-clone/reddit/base/types.py
@@ -1,10 +1,8 @@
from __future__ import annotations
-from typing import List, Optional, Type
+from typing import List, Optional
import strawberry
-from strawberry.types import Info
-from graphql_relay import from_global_id, to_global_id
@strawberry.interface(name="Node", description="An object with an ID.")
@@ -15,38 +13,6 @@ class NodeType:
"""
)
- @classmethod
- def resolve(cls, info: Info, global_id: str) -> Optional[Type[NodeType]]:
- try:
- type_name, _id = cls.from_global_id(global_id)
- except Exception as e:
- raise Exception(
- f'Unable to parse global ID "{global_id}". '
- f"Exception message: {str(e)}"
- )
-
- schema_type = info.schema.get_type_by_name(type_name)
- if schema_type is None:
- raise Exception(f'Relay Node "{type_name}" not found in schema')
-
- # We make sure the ObjectType implements the "Node" interface
- if cls not in schema_type._type_definition.interfaces:
- raise Exception(
- f'ObjectType "{type_name}" does not implement the "{cls}" interface.'
- )
-
- resolver = getattr(schema_type, "resolve_node", None)
- if resolver is not None:
- return resolver(info, _id)
-
- @classmethod
- def from_global_id(cls, global_id: str):
- return from_global_id(global_id)
-
- @classmethod
- def to_global_id(cls, schema_type: str, id: str) -> str:
- return to_global_id(schema_type, id)
-
@strawberry.type(name="Edge")
class EdgeType:
From 9fe4063e4eaec23748df63d0b7a1d05898ca1b0a Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Thu, 14 Oct 2021 15:20:51 +0530
Subject: [PATCH 101/150] update services
---
reddit-clone/reddit/users/services.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/reddit-clone/reddit/users/services.py b/reddit-clone/reddit/users/services.py
index 9668fd5f..121eea4a 100644
--- a/reddit-clone/reddit/users/services.py
+++ b/reddit-clone/reddit/users/services.py
@@ -99,7 +99,8 @@ async def request_reset_password(session: AsyncSession, email: str):
"""
user = await user_by_email(session=session, email=email)
if user is not None:
- # TODO: send password reset email here.
+ # TODO: store password reset code and
+ # send password reset email here.
pass
From 2ca82499e377c5d5e13d78b2e536b925608fa06d Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Thu, 14 Oct 2021 15:38:13 +0530
Subject: [PATCH 102/150] flag abstract types
---
reddit-clone/reddit/base/types.py | 21 +++++++++++++++------
1 file changed, 15 insertions(+), 6 deletions(-)
diff --git a/reddit-clone/reddit/base/types.py b/reddit-clone/reddit/base/types.py
index 47547737..04b6ca78 100644
--- a/reddit-clone/reddit/base/types.py
+++ b/reddit-clone/reddit/base/types.py
@@ -1,6 +1,6 @@
from __future__ import annotations
-from typing import List, Optional
+from typing import Generic, List, Optional, TypeVar
import strawberry
@@ -14,9 +14,17 @@ class NodeType:
)
+Node = TypeVar("Node")
+
+Cursor = TypeVar("Cursor")
+
+Edge = TypeVar("Edge")
+
+
+# TODO: make an abstract type
@strawberry.type(name="Edge")
-class EdgeType:
- cursor: str = strawberry.field(
+class EdgeType(Generic[Node, Cursor]):
+ cursor: Cursor = strawberry.field(
description="""
A cursor for use in pagination.
"""
@@ -56,15 +64,16 @@ class PageInfoType:
)
+# TODO: make an abstract type
@strawberry.type(name="Connection")
-class ConnectionType:
- edges: List[EdgeType] = strawberry.field(
+class ConnectionType(Generic[Node, Edge]):
+ edges: List[Edge] = strawberry.field(
description="""
Contains the edges in the connection.
"""
)
- nodes: List[NodeType] = strawberry.field(
+ nodes: List[Node] = strawberry.field(
description="""
Contains the nodes in the connection.
"""
From 19f5c3a5305a8a98d1f0c68093cae6439786cb05 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Thu, 14 Oct 2021 15:39:20 +0530
Subject: [PATCH 103/150] make pageinfo-cursor generic
---
reddit-clone/reddit/base/types.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/reddit-clone/reddit/base/types.py b/reddit-clone/reddit/base/types.py
index 04b6ca78..fc022445 100644
--- a/reddit-clone/reddit/base/types.py
+++ b/reddit-clone/reddit/base/types.py
@@ -38,8 +38,8 @@ class EdgeType(Generic[Node, Cursor]):
@strawberry.type(name="PageInfo")
-class PageInfoType:
- end_cursor: Optional[str] = strawberry.field(
+class PageInfoType(Generic[Cursor]):
+ end_cursor: Optional[Cursor] = strawberry.field(
description="""
When paginating forwards, the cursor to continue.
"""
@@ -57,7 +57,7 @@ class PageInfoType:
"""
)
- start_cursor: Optional[str] = strawberry.field(
+ start_cursor: Optional[Cursor] = strawberry.field(
description="""
When paginating backwards, the cursor to continue.
"""
From 2d170ffc09ddc4d208a81b9abad0da931f83881f Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Thu, 14 Oct 2021 15:41:04 +0530
Subject: [PATCH 104/150] pass kwargs to type var
---
reddit-clone/reddit/base/types.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/reddit-clone/reddit/base/types.py b/reddit-clone/reddit/base/types.py
index fc022445..6c33bf37 100644
--- a/reddit-clone/reddit/base/types.py
+++ b/reddit-clone/reddit/base/types.py
@@ -14,11 +14,11 @@ class NodeType:
)
-Node = TypeVar("Node")
+Node = TypeVar(name="Node")
-Cursor = TypeVar("Cursor")
+Cursor = TypeVar(name="Cursor")
-Edge = TypeVar("Edge")
+Edge = TypeVar(name="Edge")
# TODO: make an abstract type
From ca92138cdff858d542a1a3ee7fad669aef4d661b Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Thu, 14 Oct 2021 16:02:01 +0530
Subject: [PATCH 105/150] add skeletons for services
---
reddit-clone/reddit/comments/models.py | 2 +-
reddit-clone/reddit/comments/services.py | 38 ++++++++++++++++++
reddit-clone/reddit/comments/types.py | 2 +-
reddit-clone/reddit/posts/models.py | 2 +-
reddit-clone/reddit/posts/services.py | 45 ++++++++++++++++++++++
reddit-clone/reddit/posts/types.py | 2 +-
reddit-clone/reddit/subreddits/models.py | 6 +--
reddit-clone/reddit/subreddits/services.py | 34 ++++++++++++++++
reddit-clone/reddit/subreddits/types.py | 2 +-
reddit-clone/reddit/users/services.py | 4 +-
10 files changed, 126 insertions(+), 11 deletions(-)
create mode 100644 reddit-clone/reddit/comments/services.py
create mode 100644 reddit-clone/reddit/posts/services.py
create mode 100644 reddit-clone/reddit/subreddits/services.py
diff --git a/reddit-clone/reddit/comments/models.py b/reddit-clone/reddit/comments/models.py
index 80ec648e..3f8582e2 100644
--- a/reddit-clone/reddit/comments/models.py
+++ b/reddit-clone/reddit/comments/models.py
@@ -21,7 +21,7 @@ class Comment(Base):
votes: int = Column(Integer, default=1)
- user_id: Optional[int] = Column(Integer, ForeignKey("users.id"))
+ owner_id: Optional[int] = Column(Integer, ForeignKey("users.id"))
post_id: int = Column(Integer, ForeignKey("posts.id"))
diff --git a/reddit-clone/reddit/comments/services.py b/reddit-clone/reddit/comments/services.py
new file mode 100644
index 00000000..29a2935f
--- /dev/null
+++ b/reddit-clone/reddit/comments/services.py
@@ -0,0 +1,38 @@
+from sqlalchemy.ext.asyncio import AsyncSession
+
+from reddit.users.models import User
+from reddit.comments.models import Comment
+
+
+async def create_comment(
+ session: AsyncSession, content: str, owner_id: int, post_id: int
+) -> Comment:
+ """
+ Creates a new comment instance.
+ """
+ comment = Comment(content=content, owner_id=owner_id, post_id=post_id)
+ # TODO: validate input data here.
+ session.add(instance=comment)
+ await session.commit()
+ await session.refresh(instance=comment)
+ return comment
+
+
+async def vote_comment(session: AsyncSession, comment: Comment, user: User) -> Comment:
+ """
+ Creates a vote on the given comment.
+ """
+
+
+async def update_comment(session: AsyncSession, comment: Comment) -> Comment:
+ """
+ Updates the given comment instance.
+ """
+
+
+async def delete_comment(
+ session: AsyncSession, comment: Comment, user: User
+) -> Comment:
+ """
+ Deletes the given comment instance.
+ """
diff --git a/reddit-clone/reddit/comments/types.py b/reddit-clone/reddit/comments/types.py
index 777d7da0..f2050dd0 100644
--- a/reddit-clone/reddit/comments/types.py
+++ b/reddit-clone/reddit/comments/types.py
@@ -22,7 +22,7 @@ class CommentType(NodeType):
"""
)
- user_id: Optional[int] = strawberry.field(
+ owner_id: Optional[int] = strawberry.field(
description="""
The owner ID of the comment.
"""
diff --git a/reddit-clone/reddit/posts/models.py b/reddit-clone/reddit/posts/models.py
index 0d314006..85dcda92 100644
--- a/reddit-clone/reddit/posts/models.py
+++ b/reddit-clone/reddit/posts/models.py
@@ -24,7 +24,7 @@ class Post(Base):
thumbnail: Optional[str] = Column(String(255), default=None)
- user_id: int = Column(Integer, ForeignKey("users.id"))
+ owner_id: int = Column(Integer, ForeignKey("users.id"))
subreddit_id: int = Column(Integer, ForeignKey("subreddits.id"))
diff --git a/reddit-clone/reddit/posts/services.py b/reddit-clone/reddit/posts/services.py
new file mode 100644
index 00000000..8e2db89a
--- /dev/null
+++ b/reddit-clone/reddit/posts/services.py
@@ -0,0 +1,45 @@
+from typing import Optional
+
+from sqlalchemy.ext.asyncio import AsyncSession
+
+from reddit.users.models import User
+from reddit.posts.models import Post
+
+
+async def create_post(
+ session: AsyncSession,
+ title: str,
+ owner_id: int,
+ subreddit_id: int,
+ link: Optional[str] = None,
+ text: Optional[str] = None,
+) -> Post:
+ """
+ Creates a new post instance.
+ """
+ post = Post(
+ title=title, text=text, link=link, owner_id=owner_id, subreddit_id=subreddit_id
+ )
+ # TODO: validate input data here.
+ session.add(instance=post)
+ await session.commit()
+ await session.refresh(instance=post)
+ return post
+
+
+async def vote_post(session: AsyncSession, post: Post, user: User) -> Post:
+ """
+ Creates a vote on the given post.
+ """
+
+
+async def update_post(session: AsyncSession, post: Post) -> Post:
+ """
+ Updates the given post instance.
+ """
+
+
+async def delete_post(session: AsyncSession, post: Post, user: User) -> Post:
+ """
+ Deletes the given post instance.
+ """
diff --git a/reddit-clone/reddit/posts/types.py b/reddit-clone/reddit/posts/types.py
index 67a58baa..83ca086e 100644
--- a/reddit-clone/reddit/posts/types.py
+++ b/reddit-clone/reddit/posts/types.py
@@ -35,7 +35,7 @@ class PostType(NodeType):
"""
)
- user_id: int = strawberry.field(
+ owner_id: int = strawberry.field(
description="""
The owner ID of the post.
"""
diff --git a/reddit-clone/reddit/subreddits/models.py b/reddit-clone/reddit/subreddits/models.py
index cba9449a..0546f25b 100644
--- a/reddit-clone/reddit/subreddits/models.py
+++ b/reddit-clone/reddit/subreddits/models.py
@@ -1,6 +1,6 @@
from typing import Optional, List
-from sqlalchemy import Column, String, Integer, SmallInteger, ForeignKey
+from sqlalchemy import Column, ForeignKey, Integer, String
from sqlalchemy.orm import relationship
from ..database import Base
@@ -20,9 +20,7 @@ class Subreddit(Base):
description: Optional[str] = Column(String(255), default=None)
- admin_id: int = Column(Integer, ForeignKey("users.id"))
-
- status: int = Column(SmallInteger)
+ owner_id: int = Column(Integer, ForeignKey("users.id"))
icon: Optional[str] = Column(String(255), default=None)
diff --git a/reddit-clone/reddit/subreddits/services.py b/reddit-clone/reddit/subreddits/services.py
new file mode 100644
index 00000000..0a9d5a8f
--- /dev/null
+++ b/reddit-clone/reddit/subreddits/services.py
@@ -0,0 +1,34 @@
+from typing import Optional
+
+from sqlalchemy.ext.asyncio import AsyncSession
+
+from reddit.users.models import User
+from reddit.subreddits.models import Subreddit
+
+
+async def create_subreddit(
+ session: AsyncSession, owner_id: int, name: str, description: Optional[str] = None
+) -> Subreddit:
+ """
+ Creates a new subreddit instance.
+ """
+ subreddit = Subreddit(name=name, description=description, owner_id=owner_id)
+ # TODO: validate input data here.
+ session.add(instance=subreddit)
+ await session.commit()
+ await session.refresh(instance=subreddit)
+ return subreddit
+
+
+async def update_subreddit(session: AsyncSession, subreddit: Subreddit) -> Subreddit:
+ """
+ Updates the given subreddit instance.
+ """
+
+
+async def delete_subreddit(
+ session: AsyncSession, subreddit: Subreddit, user: User
+) -> Subreddit:
+ """
+ Deletes the given subreddit instance.
+ """
diff --git a/reddit-clone/reddit/subreddits/types.py b/reddit-clone/reddit/subreddits/types.py
index 43fb65e1..aaf45159 100644
--- a/reddit-clone/reddit/subreddits/types.py
+++ b/reddit-clone/reddit/subreddits/types.py
@@ -23,7 +23,7 @@ class SubredditType(NodeType):
"""
)
- admin_id: int = strawberry.field(
+ owner_id: int = strawberry.field(
description="""
The owner ID of the Subreddit.
"""
diff --git a/reddit-clone/reddit/users/services.py b/reddit-clone/reddit/users/services.py
index 121eea4a..b4434d36 100644
--- a/reddit-clone/reddit/users/services.py
+++ b/reddit-clone/reddit/users/services.py
@@ -50,13 +50,13 @@ async def create_user(
return user
-async def update_user(session: AsyncSession, user: User):
+async def update_user(session: AsyncSession, user: User) -> User:
"""
Updates the given user instance.
"""
-async def remove_avatar(session: AsyncSession, user: User) -> User:
+async def remove_user_avatar(session: AsyncSession, user: User) -> User:
"""
Removes the avatar for the given user instance.
"""
From d6c37d2646962c782e15e7e1b54cf895a1fbf88e Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Thu, 14 Oct 2021 16:05:28 +0530
Subject: [PATCH 106/150] recreate migrations
---
...cd7_initial.py => fcae25f2abf6_initial.py} | 55 ++++++++++---------
1 file changed, 30 insertions(+), 25 deletions(-)
rename reddit-clone/migrations/versions/{6fd9b8fa9cd7_initial.py => fcae25f2abf6_initial.py} (82%)
diff --git a/reddit-clone/migrations/versions/6fd9b8fa9cd7_initial.py b/reddit-clone/migrations/versions/fcae25f2abf6_initial.py
similarity index 82%
rename from reddit-clone/migrations/versions/6fd9b8fa9cd7_initial.py
rename to reddit-clone/migrations/versions/fcae25f2abf6_initial.py
index 6a2baa12..507d8d89 100644
--- a/reddit-clone/migrations/versions/6fd9b8fa9cd7_initial.py
+++ b/reddit-clone/migrations/versions/fcae25f2abf6_initial.py
@@ -1,8 +1,8 @@
"""initial
-Revision ID: 6fd9b8fa9cd7
+Revision ID: fcae25f2abf6
Revises:
-Create Date: 2021-09-30 11:01:49.083637
+Create Date: 2021-10-14 16:05:02.497467
"""
from alembic import op
@@ -10,7 +10,7 @@
# revision identifiers, used by Alembic.
-revision = "6fd9b8fa9cd7"
+revision = "fcae25f2abf6"
down_revision = None
branch_labels = None
depends_on = None
@@ -24,33 +24,21 @@ def upgrade():
sa.Column("username", sa.String(length=32), nullable=False),
sa.Column("email", sa.String(length=255), nullable=False),
sa.Column("password", sa.String(length=255), nullable=False),
- sa.Column("avatar", sa.String(length=255), nullable=True),
+ sa.Column("avatar", sa.String(length=255), nullable=False),
+ sa.Column("is_active", sa.Boolean(), nullable=False),
sa.PrimaryKeyConstraint("id"),
sa.UniqueConstraint("email"),
sa.UniqueConstraint("username"),
)
- op.create_table(
- "comments",
- sa.Column("id", sa.Integer(), nullable=False),
- sa.Column("content", sa.Text(), nullable=False),
- sa.Column("votes", sa.Integer(), nullable=True),
- sa.Column("user_id", sa.Integer(), nullable=True),
- sa.ForeignKeyConstraint(
- ["user_id"],
- ["users.id"],
- ),
- sa.PrimaryKeyConstraint("id"),
- )
op.create_table(
"subreddits",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("name", sa.String(length=75), nullable=False),
sa.Column("description", sa.String(length=255), nullable=True),
- sa.Column("admin_id", sa.Integer(), nullable=True),
- sa.Column("status", sa.SmallInteger(), nullable=True),
+ sa.Column("owner_id", sa.Integer(), nullable=True),
sa.Column("icon", sa.String(length=255), nullable=True),
sa.ForeignKeyConstraint(
- ["admin_id"],
+ ["owner_id"],
["users.id"],
),
sa.PrimaryKeyConstraint("id"),
@@ -63,16 +51,16 @@ def upgrade():
sa.Column("text", sa.String(length=1024), nullable=True),
sa.Column("link", sa.String(length=255), nullable=True),
sa.Column("thumbnail", sa.String(length=255), nullable=True),
- sa.Column("user_id", sa.Integer(), nullable=True),
+ sa.Column("owner_id", sa.Integer(), nullable=True),
sa.Column("subreddit_id", sa.Integer(), nullable=True),
sa.Column("votes", sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(
- ["subreddit_id"],
- ["subreddits.id"],
+ ["owner_id"],
+ ["users.id"],
),
sa.ForeignKeyConstraint(
- ["user_id"],
- ["users.id"],
+ ["subreddit_id"],
+ ["subreddits.id"],
),
sa.PrimaryKeyConstraint("id"),
sa.UniqueConstraint("link"),
@@ -91,14 +79,31 @@ def upgrade():
),
sa.PrimaryKeyConstraint("user_id", "subreddit_id"),
)
+ op.create_table(
+ "comments",
+ sa.Column("id", sa.Integer(), nullable=False),
+ sa.Column("content", sa.Text(), nullable=False),
+ sa.Column("votes", sa.Integer(), nullable=True),
+ sa.Column("owner_id", sa.Integer(), nullable=True),
+ sa.Column("post_id", sa.Integer(), nullable=True),
+ sa.ForeignKeyConstraint(
+ ["owner_id"],
+ ["users.id"],
+ ),
+ sa.ForeignKeyConstraint(
+ ["post_id"],
+ ["posts.id"],
+ ),
+ sa.PrimaryKeyConstraint("id"),
+ )
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
+ op.drop_table("comments")
op.drop_table("subreddit_users")
op.drop_table("posts")
op.drop_table("subreddits")
- op.drop_table("comments")
op.drop_table("users")
# ### end Alembic commands ###
From cc26b5571b4d820c624602267b0accda122b86c6 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Thu, 14 Oct 2021 16:06:41 +0530
Subject: [PATCH 107/150] review migrations
---
reddit-clone/migrations/versions/fcae25f2abf6_initial.py | 4 ----
1 file changed, 4 deletions(-)
diff --git a/reddit-clone/migrations/versions/fcae25f2abf6_initial.py b/reddit-clone/migrations/versions/fcae25f2abf6_initial.py
index 507d8d89..3fc823e6 100644
--- a/reddit-clone/migrations/versions/fcae25f2abf6_initial.py
+++ b/reddit-clone/migrations/versions/fcae25f2abf6_initial.py
@@ -17,7 +17,6 @@
def upgrade():
- # ### commands auto generated by Alembic - please adjust! ###
op.create_table(
"users",
sa.Column("id", sa.Integer(), nullable=False),
@@ -96,14 +95,11 @@ def upgrade():
),
sa.PrimaryKeyConstraint("id"),
)
- # ### end Alembic commands ###
def downgrade():
- # ### commands auto generated by Alembic - please adjust! ###
op.drop_table("comments")
op.drop_table("subreddit_users")
op.drop_table("posts")
op.drop_table("subreddits")
op.drop_table("users")
- # ### end Alembic commands ###
From aea21ef0ba867690a2c4fe25adb1d8876db57d06 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+codebyaryan@users.noreply.github.com>
Date: Thu, 14 Oct 2021 17:06:37 +0530
Subject: [PATCH 108/150] Update README.md
---
reddit-clone/README.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/reddit-clone/README.md b/reddit-clone/README.md
index 35168a8c..eb95b1f9 100644
--- a/reddit-clone/README.md
+++ b/reddit-clone/README.md
@@ -13,6 +13,7 @@ most of the features that Strawberry gives us.
- [Alembic](https://github.com/sqlalchemy/alembic) migrations
- PostgreSQL database
- [Schematics](https://github.com/schematics/schematics) data validation
+- [Celery](https://github.com/celery/celery) task management
## Features at a glance
From 1f0a1ce9bcb68826de140376d96efb142e303216 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Thu, 14 Oct 2021 20:02:34 +0530
Subject: [PATCH 109/150] add celery
---
reddit-clone/.env.example | 1 +
reddit-clone/README.md | 3 +-
reddit-clone/poetry.lock | 243 +++++++++++++++++++++++++---
reddit-clone/pyproject.toml | 2 +-
reddit-clone/reddit/celery.py | 12 ++
reddit-clone/reddit/settings.py | 6 +
reddit-clone/reddit/utils/emails.py | 25 ++-
7 files changed, 253 insertions(+), 39 deletions(-)
create mode 100644 reddit-clone/reddit/celery.py
diff --git a/reddit-clone/.env.example b/reddit-clone/.env.example
index c120265a..2b6d7cef 100644
--- a/reddit-clone/.env.example
+++ b/reddit-clone/.env.example
@@ -1,6 +1,7 @@
DATABASE_URI=postgresql+asyncpg://localhost/db_name?user=postgres&password=postgres
MAIL_HOST=127.0.0.1
MAIL_PORT=25
+CELERY_BROKER=redis://localhost:6379
DEBUG=
MAIL_USERNAME=
MAIL_PASSWORD=
diff --git a/reddit-clone/README.md b/reddit-clone/README.md
index eb95b1f9..8a472ae9 100644
--- a/reddit-clone/README.md
+++ b/reddit-clone/README.md
@@ -9,9 +9,10 @@ most of the features that Strawberry gives us.
- [Strawberry GraphQL](https://github.com/strawberry-graphql/strawberry)
- [Starlette](https://github.com/encode/starlette) web framework
+- [Uvicorn](https://github.com/encode/uvicorn) ASGI server
- [SQLAlchemy](https://github.com/sqlalchemy/sqlalchemy) core/ mapper (asyncio)
- [Alembic](https://github.com/sqlalchemy/alembic) migrations
-- PostgreSQL database
+- [PostgreSQL](https://github.com/postgres/postgres) database instance
- [Schematics](https://github.com/schematics/schematics) data validation
- [Celery](https://github.com/celery/celery) task management
diff --git a/reddit-clone/poetry.lock b/reddit-clone/poetry.lock
index 46072db4..dd0e275a 100644
--- a/reddit-clone/poetry.lock
+++ b/reddit-clone/poetry.lock
@@ -6,18 +6,6 @@ category = "main"
optional = false
python-versions = ">=3.6,<4.0"
-[[package]]
-name = "aiosmtplib"
-version = "1.1.6"
-description = "asyncio SMTP client"
-category = "main"
-optional = false
-python-versions = ">=3.5.2,<4.0.0"
-
-[package.extras]
-uvloop = ["uvloop (>=0.13,<0.15)"]
-docs = ["sphinx (>=2,<4)", "sphinx_autodoc_typehints (>=1.7.0,<2.0.0)"]
-
[[package]]
name = "alembic"
version = "1.7.4"
@@ -35,6 +23,17 @@ SQLAlchemy = ">=1.3.0"
[package.extras]
tz = ["python-dateutil"]
+[[package]]
+name = "amqp"
+version = "5.0.6"
+description = "Low-level AMQP client for Python (fork of amqplib)."
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+vine = "5.0.0"
+
[[package]]
name = "anyio"
version = "3.3.3"
@@ -113,6 +112,14 @@ 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"]
tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins"]
+[[package]]
+name = "billiard"
+version = "3.6.4.0"
+description = "Python multiprocessing fork with improvements and bugfixes"
+category = "main"
+optional = false
+python-versions = "*"
+
[[package]]
name = "black"
version = "21.9b0"
@@ -149,6 +156,57 @@ category = "main"
optional = false
python-versions = "*"
+[[package]]
+name = "celery"
+version = "5.1.2"
+description = "Distributed Task Queue."
+category = "main"
+optional = false
+python-versions = ">=3.6,"
+
+[package.dependencies]
+billiard = ">=3.6.4.0,<4.0"
+click = ">=7.0,<8.0"
+click-didyoumean = ">=0.0.3"
+click-plugins = ">=1.1.1"
+click-repl = ">=0.1.6"
+kombu = ">=5.1.0,<6.0"
+pytz = ">0.0-dev"
+vine = ">=5.0.0,<6.0"
+
+[package.extras]
+arangodb = ["pyArango (>=1.3.2)"]
+auth = ["cryptography"]
+azureblockblob = ["azure-storage-blob (==12.6.0)"]
+brotli = ["brotli (>=1.0.0)", "brotlipy (>=0.7.0)"]
+cassandra = ["cassandra-driver (<3.21.0)"]
+consul = ["python-consul2"]
+cosmosdbsql = ["pydocumentdb (==2.3.2)"]
+couchbase = ["couchbase (>=3.0.0)"]
+couchdb = ["pycouchdb"]
+django = ["Django (>=1.11)"]
+dynamodb = ["boto3 (>=1.9.178)"]
+elasticsearch = ["elasticsearch"]
+eventlet = ["eventlet (>=0.26.1)"]
+gevent = ["gevent (>=1.0.0)"]
+librabbitmq = ["librabbitmq (>=1.5.0)"]
+memcache = ["pylibmc"]
+mongodb = ["pymongo[srv] (>=3.3.0)"]
+msgpack = ["msgpack"]
+pymemcache = ["python-memcached"]
+pyro = ["pyro4"]
+pytest = ["pytest-celery"]
+redis = ["redis (>=3.2.0)"]
+s3 = ["boto3 (>=1.9.125)"]
+slmq = ["softlayer-messaging (>=1.0.3)"]
+solar = ["ephem"]
+sqlalchemy = ["sqlalchemy"]
+sqs = ["boto3 (>=1.9.125)", "pycurl (==7.43.0.5)"]
+tblib = ["tblib (>=1.3.0)", "tblib (>=1.5.0)"]
+yaml = ["PyYAML (>=3.10)"]
+zookeeper = ["kazoo (>=1.3.1)"]
+zstd = ["zstandard"]
+
[[package]]
name = "cffi"
version = "1.14.6"
@@ -162,15 +220,49 @@ pycparser = "*"
[[package]]
name = "click"
-version = "8.0.3"
+version = "7.1.2"
description = "Composable command line interface toolkit"
category = "main"
optional = false
-python-versions = ">=3.6"
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+
+[[package]]
+name = "click-didyoumean"
+version = "0.3.0"
+description = "Enables git-like *did-you-mean* feature in click"
+category = "main"
+optional = false
+python-versions = ">=3.6.2,<4.0.0"
[package.dependencies]
-colorama = {version = "*", markers = "platform_system == \"Windows\""}
-importlib-metadata = {version = "*", markers = "python_version < \"3.8\""}
+click = ">=7"
+
+[[package]]
+name = "click-plugins"
+version = "1.1.1"
+description = "An extension module for click to enable registering CLI commands via setuptools entry-points."
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+click = ">=4.0"
+
+[package.extras]
+dev = ["pytest (>=3.6)", "pytest-cov", "wheel", "coveralls"]
+
+[[package]]
+name = "click-repl"
+version = "0.2.0"
+description = "REPL plugin for Click"
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+click = "*"
+prompt-toolkit = "*"
+six = "*"
[[package]]
name = "colorama"
@@ -326,6 +418,36 @@ MarkupSafe = ">=2.0"
[package.extras]
i18n = ["Babel (>=2.7)"]
+[[package]]
+name = "kombu"
+version = "5.1.0"
+description = "Messaging library for Python."
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+amqp = ">=5.0.6,<6.0.0"
+cached-property = {version = "*", markers = "python_version < \"3.8\""}
+importlib-metadata = {version = ">=0.18", markers = "python_version < \"3.8\""}
+vine = "*"
+
+[package.extras]
+azureservicebus = ["azure-servicebus (>=7.0.0)"]
+azurestoragequeues = ["azure-storage-queue"]
+consul = ["python-consul (>=0.6.0)"]
+librabbitmq = ["librabbitmq (>=1.5.2)"]
+mongodb = ["pymongo (>=3.3.0)"]
+msgpack = ["msgpack"]
+pyro = ["pyro4"]
+qpid = ["qpid-python (>=0.26)", "qpid-tools (>=0.26)"]
+redis = ["redis (>=3.3.11)"]
+slmq = ["softlayer-messaging (>=1.0.3)"]
+sqlalchemy = ["sqlalchemy"]
+sqs = ["boto3 (>=1.4.4)", "pycurl (==7.43.0.2)", "urllib3 (<1.26)"]
+yaml = ["PyYAML (>=3.10)"]
+zookeeper = ["kazoo (>=1.3.1)"]
+
[[package]]
name = "mako"
version = "1.1.5"
@@ -420,6 +542,17 @@ python-versions = ">=3.6"
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 = "prompt-toolkit"
+version = "3.0.20"
+description = "Library for building powerful interactive command lines in Python"
+category = "main"
+optional = false
+python-versions = ">=3.6.2"
+
+[package.dependencies]
+wcwidth = "*"
+
[[package]]
name = "pycodestyle"
version = "2.7.0"
@@ -485,6 +618,14 @@ python-versions = "*"
[package.dependencies]
six = ">=1.4.0"
+[[package]]
+name = "pytz"
+version = "2021.3"
+description = "World timezone definitions, modern and historical"
+category = "main"
+optional = false
+python-versions = "*"
+
[[package]]
name = "pyyaml"
version = "5.4.1"
@@ -695,6 +836,14 @@ dev = ["Cython (>=0.29.24,<0.30.0)", "pytest (>=3.6.0)", "Sphinx (>=4.1.2,<4.2.0
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 = "vine"
+version = "5.0.0"
+description = "Promises, promises, promises."
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
[[package]]
name = "watchgod"
version = "0.7"
@@ -703,6 +852,14 @@ category = "main"
optional = false
python-versions = ">=3.5"
+[[package]]
+name = "wcwidth"
+version = "0.2.5"
+description = "Measures the displayed width of unicode strings in a terminal"
+category = "main"
+optional = false
+python-versions = "*"
+
[[package]]
name = "websockets"
version = "10.0"
@@ -726,21 +883,21 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytes
[metadata]
lock-version = "1.1"
python-versions = "^3.7"
-content-hash = "8d178b08b5558fedfba56530d5cedcd21cfc44a333d374c37487d542fa7be916"
+content-hash = "8753228c24cfc820ae979004bf108c0f179adbba378a2612180dc1b38e120a4d"
[metadata.files]
aiofiles = [
{file = "aiofiles-0.7.0-py3-none-any.whl", hash = "sha256:c67a6823b5f23fcab0a2595a289cec7d8c863ffcb4322fb8cd6b90400aedfdbc"},
{file = "aiofiles-0.7.0.tar.gz", hash = "sha256:a1c4fc9b2ff81568c83e21392a82f344ea9d23da906e4f6a52662764545e19d4"},
]
-aiosmtplib = [
- {file = "aiosmtplib-1.1.6-py3-none-any.whl", hash = "sha256:84174765778b2c5e0e207fbce0a769202fcf0c3de81faa87cc03551a6333bfa9"},
- {file = "aiosmtplib-1.1.6.tar.gz", hash = "sha256:d138fe6ffecbc9e6320269690b9ac0b75e540ef96e8f5c77d4a306760014dce2"},
-]
alembic = [
{file = "alembic-1.7.4-py3-none-any.whl", hash = "sha256:e3cab9e59778b3b6726bb2da9ced451c6622d558199fd3ef914f3b1e8f4ef704"},
{file = "alembic-1.7.4.tar.gz", hash = "sha256:9d33f3ff1488c4bfab1e1a6dfebbf085e8a8e1a3e047a43ad29ad1f67f012a1d"},
]
+amqp = [
+ {file = "amqp-5.0.6-py3-none-any.whl", hash = "sha256:493a2ac6788ce270a2f6a765b017299f60c1998f5a8617908ee9be082f7300fb"},
+ {file = "amqp-5.0.6.tar.gz", hash = "sha256:03e16e94f2b34c31f8bf1206d8ddd3ccaa4c315f7f6a1879b7b1210d229568c2"},
+]
anyio = [
{file = "anyio-3.3.3-py3-none-any.whl", hash = "sha256:56ceaeed2877723578b1341f4f68c29081db189cfb40a97d1922b9513f6d7db6"},
{file = "anyio-3.3.3.tar.gz", hash = "sha256:8eccec339cb4a856c94a75d50fc1d451faf32a05ef406be462e2efc59c9838b0"},
@@ -781,6 +938,10 @@ attrs = [
{file = "attrs-21.2.0-py2.py3-none-any.whl", hash = "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1"},
{file = "attrs-21.2.0.tar.gz", hash = "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"},
]
+billiard = [
+ {file = "billiard-3.6.4.0-py3-none-any.whl", hash = "sha256:87103ea78fa6ab4d5c751c4909bcff74617d985de7fa8b672cf8618afd5a875b"},
+ {file = "billiard-3.6.4.0.tar.gz", hash = "sha256:299de5a8da28a783d51b197d496bef4f1595dd023a93a4f59dde1886ae905547"},
+]
black = [
{file = "black-21.9b0-py3-none-any.whl", hash = "sha256:380f1b5da05e5a1429225676655dddb96f5ae8c75bdf91e53d798871b902a115"},
{file = "black-21.9b0.tar.gz", hash = "sha256:7de4cfc7eb6b710de325712d40125689101d21d25283eed7e9998722cf10eb91"},
@@ -789,6 +950,10 @@ 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"},
]
+celery = [
+ {file = "celery-5.1.2-py3-none-any.whl", hash = "sha256:9dab2170b4038f7bf10ef2861dbf486ddf1d20592290a1040f7b7a1259705d42"},
+ {file = "celery-5.1.2.tar.gz", hash = "sha256:8d9a3de9162965e97f8e8cc584c67aad83b3f7a267584fa47701ed11c3e0d4b0"},
+]
cffi = [
{file = "cffi-1.14.6-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:22b9c3c320171c108e903d61a3723b51e37aaa8c81255b5e7ce102775bd01e2c"},
{file = "cffi-1.14.6-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:f0c5d1acbfca6ebdd6b1e3eded8d261affb6ddcf2186205518f1428b8569bb99"},
@@ -837,8 +1002,20 @@ cffi = [
{file = "cffi-1.14.6.tar.gz", hash = "sha256:c9a875ce9d7fe32887784274dd533c57909b7b1dcadcc128a2ac21331a9765dd"},
]
click = [
- {file = "click-8.0.3-py3-none-any.whl", hash = "sha256:353f466495adaeb40b6b5f592f9f91cb22372351c84caeb068132442a4518ef3"},
- {file = "click-8.0.3.tar.gz", hash = "sha256:410e932b050f5eed773c4cda94de75971c89cdb3155a72a0831139a79e5ecb5b"},
+ {file = "click-7.1.2-py2.py3-none-any.whl", hash = "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"},
+ {file = "click-7.1.2.tar.gz", hash = "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a"},
+]
+click-didyoumean = [
+ {file = "click-didyoumean-0.3.0.tar.gz", hash = "sha256:f184f0d851d96b6d29297354ed981b7dd71df7ff500d82fa6d11f0856bee8035"},
+ {file = "click_didyoumean-0.3.0-py3-none-any.whl", hash = "sha256:a0713dc7a1de3f06bc0df5a9567ad19ead2d3d5689b434768a6145bff77c0667"},
+]
+click-plugins = [
+ {file = "click-plugins-1.1.1.tar.gz", hash = "sha256:46ab999744a9d831159c3411bb0c79346d94a444df9a3a3742e9ed63645f264b"},
+ {file = "click_plugins-1.1.1-py2.py3-none-any.whl", hash = "sha256:5d262006d3222f5057fd81e1623d4443e41dcda5dc815c06b442aa3c02889fc8"},
+]
+click-repl = [
+ {file = "click-repl-0.2.0.tar.gz", hash = "sha256:cd12f68d745bf6151210790540b4cb064c7b13e571bc64b6957d98d120dacfd8"},
+ {file = "click_repl-0.2.0-py3-none-any.whl", hash = "sha256:94b3fbbc9406a236f176e0506524b2937e4b23b6f4c0c0b2a0a83f8a64e9194b"},
]
colorama = [
{file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"},
@@ -953,6 +1130,10 @@ jinja2 = [
{file = "Jinja2-3.0.2-py3-none-any.whl", hash = "sha256:8569982d3f0889eed11dd620c706d39b60c36d6d25843961f33f77fb6bc6b20c"},
{file = "Jinja2-3.0.2.tar.gz", hash = "sha256:827a0e32839ab1600d4eb1c4c33ec5a8edfbc5cb42dafa13b81f182f97784b45"},
]
+kombu = [
+ {file = "kombu-5.1.0-py3-none-any.whl", hash = "sha256:e2dedd8a86c9077c350555153825a31e456a0dc20c15d5751f00137ec9c75f0a"},
+ {file = "kombu-5.1.0.tar.gz", hash = "sha256:01481d99f4606f6939cdc9b637264ed353ee9e3e4f62cfb582324142c41a572d"},
+]
mako = [
{file = "Mako-1.1.5-py2.py3-none-any.whl", hash = "sha256:6804ee66a7f6a6416910463b00d76a7b25194cd27f1918500c5bd7be2a088a23"},
{file = "Mako-1.1.5.tar.gz", hash = "sha256:169fa52af22a91900d852e937400e79f535496191c63712e3b9fda5a9bed6fc3"},
@@ -1038,6 +1219,10 @@ platformdirs = [
{file = "platformdirs-2.4.0-py3-none-any.whl", hash = "sha256:8868bbe3c3c80d42f20156f22e7131d2fb321f5bc86a2a345375c6481a67021d"},
{file = "platformdirs-2.4.0.tar.gz", hash = "sha256:367a5e80b3d04d2428ffa76d33f124cf11e8fff2acdaa9b43d545f5c7d661ef2"},
]
+prompt-toolkit = [
+ {file = "prompt_toolkit-3.0.20-py3-none-any.whl", hash = "sha256:6076e46efae19b1e0ca1ec003ed37a933dc94b4d20f486235d436e64771dcd5c"},
+ {file = "prompt_toolkit-3.0.20.tar.gz", hash = "sha256:eb71d5a6b72ce6db177af4a7d4d7085b99756bf656d98ffcc4fecd36850eea6c"},
+]
pycodestyle = [
{file = "pycodestyle-2.7.0-py2.py3-none-any.whl", hash = "sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068"},
{file = "pycodestyle-2.7.0.tar.gz", hash = "sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef"},
@@ -1065,6 +1250,10 @@ python-dotenv = [
python-multipart = [
{file = "python-multipart-0.0.5.tar.gz", hash = "sha256:f7bb5f611fc600d15fa47b3974c8aa16e93724513b49b5f95c81e6624c83fa43"},
]
+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-5.4.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922"},
{file = "PyYAML-5.4.1-cp27-cp27m-win32.whl", hash = "sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393"},
@@ -1266,10 +1455,18 @@ uvloop = [
{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"},
]
+vine = [
+ {file = "vine-5.0.0-py2.py3-none-any.whl", hash = "sha256:4c9dceab6f76ed92105027c49c823800dd33cacce13bdedc5b914e3514b7fb30"},
+ {file = "vine-5.0.0.tar.gz", hash = "sha256:7d3b1624a953da82ef63462013bbd271d3eb75751489f9807598e8f340bd637e"},
+]
watchgod = [
{file = "watchgod-0.7-py3-none-any.whl", hash = "sha256:d6c1ea21df37847ac0537ca0d6c2f4cdf513562e95f77bb93abbcf05573407b7"},
{file = "watchgod-0.7.tar.gz", hash = "sha256:48140d62b0ebe9dd9cf8381337f06351e1f2e70b2203fa9c6eff4e572ca84f29"},
]
+wcwidth = [
+ {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"},
+ {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"},
+]
websockets = [
{file = "websockets-10.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cd8c6f2ec24aedace251017bc7a414525171d4e6578f914acab9349362def4da"},
{file = "websockets-10.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:1f6b814cff6aadc4288297cb3a248614829c6e4ff5556593c44a115e9dd49939"},
diff --git a/reddit-clone/pyproject.toml b/reddit-clone/pyproject.toml
index a08c906c..2f6df1d8 100644
--- a/reddit-clone/pyproject.toml
+++ b/reddit-clone/pyproject.toml
@@ -13,11 +13,11 @@ passlib = {extras = ["argon2"], version = "^1.7.4"}
alembic = "^1.7.1"
asyncpg = "^0.24"
graphql-relay = "^3.1.0"
-aiosmtplib = "^1.1.6"
schematics = "^2.1.1"
jinja2 = "^3.0.2"
aiofiles = "^0.7.0"
starlette = "^0.16.0"
+celery = "^5.1.2"
[tool.poetry.dev-dependencies]
diff --git a/reddit-clone/reddit/celery.py b/reddit-clone/reddit/celery.py
new file mode 100644
index 00000000..0e6d43b8
--- /dev/null
+++ b/reddit-clone/reddit/celery.py
@@ -0,0 +1,12 @@
+from celery import Celery
+
+from reddit import settings
+
+__all__ = ("celery",)
+
+celery = Celery(
+ main=__name__, backend=settings.CELERY_BACKEND, broker=settings.CELERY_BROKER
+)
+
+if __name__ == "__main__":
+ celery.start()
diff --git a/reddit-clone/reddit/settings.py b/reddit-clone/reddit/settings.py
index 9d57003c..a2db8c97 100644
--- a/reddit-clone/reddit/settings.py
+++ b/reddit-clone/reddit/settings.py
@@ -25,3 +25,9 @@
# mail client sender address.
MAIL_SENDER: Optional[str] = config("MAIL_SENDER", cast=str, default=None)
+
+# celery broker URL.
+CELERY_BROKER: str = config("CELERY_BROKER_URL", cast=str)
+
+# celery result backend URL.
+CELERY_BACKEND: str = config("CELERY_BACKEND", cast=str)
diff --git a/reddit-clone/reddit/utils/emails.py b/reddit-clone/reddit/utils/emails.py
index 7a222fab..0d628729 100644
--- a/reddit-clone/reddit/utils/emails.py
+++ b/reddit-clone/reddit/utils/emails.py
@@ -1,23 +1,23 @@
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
+from smtplib import SMTP
from typing import Optional
-from aiosmtplib import send
-
from reddit import settings
+from reddit.celery import celery
__all__ = ("send_mail",)
-async def send_mail(
+@celery.task(name="send_email")
+def send_mail(
recipient: str, subject: str, content: str, html_content: Optional[str] = None
) -> None:
"""
- Sends a multipart email to the provided
- recipient asynchronously.
+ Sends a multipart email to the provided recipient.
"""
message = MIMEMultipart()
- message["From"] = None
+ message["From"] = settings.MAIL_SENDER
message["To"] = recipient
message["Subject"] = subject
@@ -28,11 +28,8 @@ async def send_mail(
html_text = MIMEText(html_content, "html")
message.attach(html_text)
- await send(
- message=message,
- sender=settings.MAIL_SENDER,
- username=settings.MAIL_USERNAME,
- password=settings.MAIL_PASSWORD,
- hostname=settings.MAIL_HOST,
- port=settings.MAIL_PORT,
- )
+ server = SMTP(host=settings.MAIL_HOST, port=settings.MAIL_PORT)
+ server.login(user=settings.MAIL_USERNAME, password=settings.MAIL_PASSWORD)
+ server.starttls()
+ server.send_message(message)
+ server.quit()
From 4fbe70a50b6b26cadd6a98e03c6fceca07941a91 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Thu, 14 Oct 2021 20:12:55 +0530
Subject: [PATCH 110/150] update celery worker
---
reddit-clone/reddit/celery.py | 12 ------------
reddit-clone/reddit/tasks.py | 12 ++++++++++++
reddit-clone/reddit/utils/emails.py | 2 +-
3 files changed, 13 insertions(+), 13 deletions(-)
delete mode 100644 reddit-clone/reddit/celery.py
create mode 100644 reddit-clone/reddit/tasks.py
diff --git a/reddit-clone/reddit/celery.py b/reddit-clone/reddit/celery.py
deleted file mode 100644
index 0e6d43b8..00000000
--- a/reddit-clone/reddit/celery.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from celery import Celery
-
-from reddit import settings
-
-__all__ = ("celery",)
-
-celery = Celery(
- main=__name__, backend=settings.CELERY_BACKEND, broker=settings.CELERY_BROKER
-)
-
-if __name__ == "__main__":
- celery.start()
diff --git a/reddit-clone/reddit/tasks.py b/reddit-clone/reddit/tasks.py
new file mode 100644
index 00000000..d2c470fd
--- /dev/null
+++ b/reddit-clone/reddit/tasks.py
@@ -0,0 +1,12 @@
+from celery import Celery
+
+from reddit import settings
+
+__all__ = ("celery",)
+
+celery = Celery(__name__)
+celery.conf.result_backend = settings.CELERY_BACKEND
+celery.conf.broker_url = settings.CELERY_BROKER
+
+if __name__ == "__main__":
+ celery.start()
diff --git a/reddit-clone/reddit/utils/emails.py b/reddit-clone/reddit/utils/emails.py
index 0d628729..31d490ad 100644
--- a/reddit-clone/reddit/utils/emails.py
+++ b/reddit-clone/reddit/utils/emails.py
@@ -4,7 +4,7 @@
from typing import Optional
from reddit import settings
-from reddit.celery import celery
+from reddit.tasks import celery
__all__ = ("send_mail",)
From 44798ec9cd4c4b260768cb76e14ff53b542f0d7c Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Thu, 14 Oct 2021 20:14:19 +0530
Subject: [PATCH 111/150] update README.md
---
reddit-clone/README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/reddit-clone/README.md b/reddit-clone/README.md
index 8a472ae9..ec827f76 100644
--- a/reddit-clone/README.md
+++ b/reddit-clone/README.md
@@ -12,7 +12,7 @@ most of the features that Strawberry gives us.
- [Uvicorn](https://github.com/encode/uvicorn) ASGI server
- [SQLAlchemy](https://github.com/sqlalchemy/sqlalchemy) core/ mapper (asyncio)
- [Alembic](https://github.com/sqlalchemy/alembic) migrations
-- [PostgreSQL](https://github.com/postgres/postgres) database instance
+- [PostgreSQL](https://github.com/postgres/postgres) database server
- [Schematics](https://github.com/schematics/schematics) data validation
- [Celery](https://github.com/celery/celery) task management
From c3ac1a7a2af01e0af91472a09240f51138cb39e2 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Thu, 14 Oct 2021 20:27:38 +0530
Subject: [PATCH 112/150] update README.md
---
reddit-clone/README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/reddit-clone/README.md b/reddit-clone/README.md
index ec827f76..59c0a2c6 100644
--- a/reddit-clone/README.md
+++ b/reddit-clone/README.md
@@ -14,7 +14,7 @@ most of the features that Strawberry gives us.
- [Alembic](https://github.com/sqlalchemy/alembic) migrations
- [PostgreSQL](https://github.com/postgres/postgres) database server
- [Schematics](https://github.com/schematics/schematics) data validation
-- [Celery](https://github.com/celery/celery) task management
+- [Celery](https://github.com/celery/celery) tasks ([Redis](https://github.com/redis/redis) store)
## Features at a glance
From 93f4d655495fd4ddb15f8c1525357f4ffa367128 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Thu, 14 Oct 2021 20:47:40 +0530
Subject: [PATCH 113/150] add dockerfile
---
reddit-clone/Dockerfile | 13 +++++++++++++
1 file changed, 13 insertions(+)
create mode 100644 reddit-clone/Dockerfile
diff --git a/reddit-clone/Dockerfile b/reddit-clone/Dockerfile
new file mode 100644
index 00000000..0237e693
--- /dev/null
+++ b/reddit-clone/Dockerfile
@@ -0,0 +1,13 @@
+FROM python:3.7
+
+RUN mkdir /reddit
+
+COPY /reddit /reddit
+COPY /pyproject.toml /reddit
+
+WORKDIR /reddit
+
+RUN pip install poetry
+RUN poetry install
+
+ENTRYPOINT [ "poetry", "run", "uvicorn", "app" ]
From 1b8bcd19366363568479664e1858d7816b6693a0 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Thu, 14 Oct 2021 21:24:37 +0530
Subject: [PATCH 114/150] prepare to use docker compose
---
reddit-clone/Dockerfile | 10 +++++-----
reddit-clone/reddit/settings.py | 2 +-
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/reddit-clone/Dockerfile b/reddit-clone/Dockerfile
index 0237e693..ede730f1 100644
--- a/reddit-clone/Dockerfile
+++ b/reddit-clone/Dockerfile
@@ -1,13 +1,13 @@
FROM python:3.7
-RUN mkdir /reddit
+RUN mkdir /app
-COPY /reddit /reddit
-COPY /pyproject.toml /reddit
+COPY /reddit /app
+COPY /pyproject.toml /app
-WORKDIR /reddit
+WORKDIR /app
RUN pip install poetry
RUN poetry install
-ENTRYPOINT [ "poetry", "run", "uvicorn", "app" ]
+ENTRYPOINT [ "poetry", "run", "uvicorn", "reddit:app" ]
diff --git a/reddit-clone/reddit/settings.py b/reddit-clone/reddit/settings.py
index a2db8c97..4b12d8be 100644
--- a/reddit-clone/reddit/settings.py
+++ b/reddit-clone/reddit/settings.py
@@ -27,7 +27,7 @@
MAIL_SENDER: Optional[str] = config("MAIL_SENDER", cast=str, default=None)
# celery broker URL.
-CELERY_BROKER: str = config("CELERY_BROKER_URL", cast=str)
+CELERY_BROKER: str = config("CELERY_BROKER", cast=str)
# celery result backend URL.
CELERY_BACKEND: str = config("CELERY_BACKEND", cast=str)
From 645026f5aabe89e6413764fe3ff420f4c6e23537 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Fri, 15 Oct 2021 07:34:00 +0530
Subject: [PATCH 115/150] add docker-compose.yml
---
reddit-clone/.env.example | 1 +
reddit-clone/Dockerfile | 2 +-
reddit-clone/README.md | 2 +-
reddit-clone/docker-compose.yml | 31 +++++++++++++++++++++++++++++++
4 files changed, 34 insertions(+), 2 deletions(-)
create mode 100644 reddit-clone/docker-compose.yml
diff --git a/reddit-clone/.env.example b/reddit-clone/.env.example
index 2b6d7cef..e4299e35 100644
--- a/reddit-clone/.env.example
+++ b/reddit-clone/.env.example
@@ -2,6 +2,7 @@ DATABASE_URI=postgresql+asyncpg://localhost/db_name?user=postgres&password=postg
MAIL_HOST=127.0.0.1
MAIL_PORT=25
CELERY_BROKER=redis://localhost:6379
+CELERY_BACKEND=
DEBUG=
MAIL_USERNAME=
MAIL_PASSWORD=
diff --git a/reddit-clone/Dockerfile b/reddit-clone/Dockerfile
index ede730f1..ab53b70e 100644
--- a/reddit-clone/Dockerfile
+++ b/reddit-clone/Dockerfile
@@ -3,7 +3,7 @@ FROM python:3.7
RUN mkdir /app
COPY /reddit /app
-COPY /pyproject.toml /app
+COPY /pyproject.toml /poetry.lock /app/
WORKDIR /app
diff --git a/reddit-clone/README.md b/reddit-clone/README.md
index 59c0a2c6..fdfb7843 100644
--- a/reddit-clone/README.md
+++ b/reddit-clone/README.md
@@ -14,7 +14,7 @@ most of the features that Strawberry gives us.
- [Alembic](https://github.com/sqlalchemy/alembic) migrations
- [PostgreSQL](https://github.com/postgres/postgres) database server
- [Schematics](https://github.com/schematics/schematics) data validation
-- [Celery](https://github.com/celery/celery) tasks ([Redis](https://github.com/redis/redis) store)
+- [Celery](https://github.com/celery/celery) tasks ([Redis](https://github.com/redis/redis) store, [RabbitMQ](https://github.com/rabbitmq/rabbitmq-server) broker)
## Features at a glance
diff --git a/reddit-clone/docker-compose.yml b/reddit-clone/docker-compose.yml
new file mode 100644
index 00000000..d2492aa7
--- /dev/null
+++ b/reddit-clone/docker-compose.yml
@@ -0,0 +1,31 @@
+services:
+ database:
+ image: postgres:13-alpine
+ env_file:
+ - ".env"
+
+ redis:
+ image: redis:6-alpine
+ env_file:
+ - ".env"
+
+ rabbitmq:
+ image: rabbitmq:latest
+ env_file:
+ - ".env"
+
+ app:
+ build:
+ context: .
+ env_file:
+ - ".env"
+ depends_on:
+ - database
+
+ celery:
+ restart: always
+ env_file:
+ - ".env"
+ depends_on:
+ - redis
+ - rabbitmq
From 1d2b56a336dd2fd8d8ec0a580cf49f2efb7f4faf Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Fri, 15 Oct 2021 07:50:25 +0530
Subject: [PATCH 116/150] update Dockerfile to python 3.9
---
reddit-clone/.env.example | 7 +++++++
reddit-clone/Dockerfile | 2 +-
2 files changed, 8 insertions(+), 1 deletion(-)
diff --git a/reddit-clone/.env.example b/reddit-clone/.env.example
index e4299e35..49adb150 100644
--- a/reddit-clone/.env.example
+++ b/reddit-clone/.env.example
@@ -1,3 +1,4 @@
+# app
DATABASE_URI=postgresql+asyncpg://localhost/db_name?user=postgres&password=postgres
MAIL_HOST=127.0.0.1
MAIL_PORT=25
@@ -7,3 +8,9 @@ DEBUG=
MAIL_USERNAME=
MAIL_PASSWORD=
MAIL_SENDER=
+
+# database
+POSTGRES_SERVER=
+POSTGRES_USER=
+POSTGRES_PASSWORD=
+POSTGRES_DB=
diff --git a/reddit-clone/Dockerfile b/reddit-clone/Dockerfile
index ab53b70e..551e640a 100644
--- a/reddit-clone/Dockerfile
+++ b/reddit-clone/Dockerfile
@@ -1,4 +1,4 @@
-FROM python:3.7
+FROM python:3.9-slim-buster
RUN mkdir /app
From 7333d2768121deb432da011d4d8605ac2900ae4e Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Fri, 15 Oct 2021 08:10:58 +0530
Subject: [PATCH 117/150] update docker file
---
reddit-clone/.env.example | 4 ++--
reddit-clone/Dockerfile | 2 +-
reddit-clone/docker-compose.yml | 23 +++++++++++++++--------
3 files changed, 18 insertions(+), 11 deletions(-)
diff --git a/reddit-clone/.env.example b/reddit-clone/.env.example
index 49adb150..51fa3201 100644
--- a/reddit-clone/.env.example
+++ b/reddit-clone/.env.example
@@ -2,8 +2,8 @@
DATABASE_URI=postgresql+asyncpg://localhost/db_name?user=postgres&password=postgres
MAIL_HOST=127.0.0.1
MAIL_PORT=25
-CELERY_BROKER=redis://localhost:6379
-CELERY_BACKEND=
+CELERY_BROKER=
+CELERY_BACKEND=redis://localhost:6379
DEBUG=
MAIL_USERNAME=
MAIL_PASSWORD=
diff --git a/reddit-clone/Dockerfile b/reddit-clone/Dockerfile
index 551e640a..304049b7 100644
--- a/reddit-clone/Dockerfile
+++ b/reddit-clone/Dockerfile
@@ -2,7 +2,7 @@ FROM python:3.9-slim-buster
RUN mkdir /app
-COPY /reddit /app
+COPY /reddit /migrations /app/
COPY /pyproject.toml /poetry.lock /app/
WORKDIR /app
diff --git a/reddit-clone/docker-compose.yml b/reddit-clone/docker-compose.yml
index d2492aa7..5f733564 100644
--- a/reddit-clone/docker-compose.yml
+++ b/reddit-clone/docker-compose.yml
@@ -8,11 +8,15 @@ services:
image: redis:6-alpine
env_file:
- ".env"
+ ports:
+ - "6379:6379"
rabbitmq:
- image: rabbitmq:latest
+ image: rabbitmq:3
env_file:
- ".env"
+ ports:
+ - "5672:5672"
app:
build:
@@ -22,10 +26,13 @@ services:
depends_on:
- database
- celery:
- restart: always
- env_file:
- - ".env"
- depends_on:
- - redis
- - rabbitmq
+ # celery:
+ # build:
+ # context: .
+ # restart: always
+ # env_file:
+ # - ".env"
+ # depends_on:
+ # - redis
+ # - rabbitmq
+ # - app
From 0c32ec89cc423318848493dcacaa582437d7936e Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Fri, 15 Oct 2021 09:13:13 +0530
Subject: [PATCH 118/150] update dockerfile
---
reddit-clone/Dockerfile | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/reddit-clone/Dockerfile b/reddit-clone/Dockerfile
index 304049b7..12731e2d 100644
--- a/reddit-clone/Dockerfile
+++ b/reddit-clone/Dockerfile
@@ -1,13 +1,12 @@
FROM python:3.9-slim-buster
-RUN mkdir /app
+WORKDIR /app/
-COPY /reddit /migrations /app/
-COPY /pyproject.toml /poetry.lock /app/
-
-WORKDIR /app
+COPY ./poetry.lock ./pyproject.toml /app/
RUN pip install poetry
RUN poetry install
+COPY ./reddit /app/reddit/
+
ENTRYPOINT [ "poetry", "run", "uvicorn", "reddit:app" ]
From c02786f3d7060f9b584036a2a70255e69d4963c4 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Fri, 15 Oct 2021 10:12:40 +0530
Subject: [PATCH 119/150] add .dockerignore
---
reddit-clone/.dockerignore | 7 +++++++
reddit-clone/Dockerfile | 8 ++++----
2 files changed, 11 insertions(+), 4 deletions(-)
create mode 100644 reddit-clone/.dockerignore
diff --git a/reddit-clone/.dockerignore b/reddit-clone/.dockerignore
new file mode 100644
index 00000000..9008115f
--- /dev/null
+++ b/reddit-clone/.dockerignore
@@ -0,0 +1,7 @@
+.git
+__pycache__
+*.pyc
+*.pyo
+*.pyd
+.Python
+env
diff --git a/reddit-clone/Dockerfile b/reddit-clone/Dockerfile
index 12731e2d..1ccfe6b3 100644
--- a/reddit-clone/Dockerfile
+++ b/reddit-clone/Dockerfile
@@ -1,12 +1,12 @@
-FROM python:3.9-slim-buster
+FROM python:3.9
-WORKDIR /app/
+WORKDIR /app
-COPY ./poetry.lock ./pyproject.toml /app/
+COPY ./pyproject.toml ./poetry.lock /app/
RUN pip install poetry
RUN poetry install
COPY ./reddit /app/reddit/
-ENTRYPOINT [ "poetry", "run", "uvicorn", "reddit:app" ]
+CMD [ "poetry", "run", "uvicorn", "reddit:app" ]
From 4fc9965d661370609de8bc475c638d4bd5a9babf Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Fri, 15 Oct 2021 10:19:36 +0530
Subject: [PATCH 120/150] create volume for postgres data
---
reddit-clone/docker-compose.yml | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/reddit-clone/docker-compose.yml b/reddit-clone/docker-compose.yml
index 5f733564..c105ff0a 100644
--- a/reddit-clone/docker-compose.yml
+++ b/reddit-clone/docker-compose.yml
@@ -1,8 +1,11 @@
services:
database:
image: postgres:13-alpine
+ restart: unless-stopped
env_file:
- ".env"
+ volumes:
+ - postgres-data:/var/lib/postgresql/data
redis:
image: redis:6-alpine
@@ -36,3 +39,6 @@ services:
# - redis
# - rabbitmq
# - app
+
+volumes:
+ postgres-data:
From fcfe7ca06ca5d13d0d044f39f0881f74782c030d Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Fri, 15 Oct 2021 10:46:52 +0530
Subject: [PATCH 121/150] make docker compose happy
---
reddit-clone/Dockerfile | 8 +++++---
reddit-clone/docker-compose.yml | 3 +--
2 files changed, 6 insertions(+), 5 deletions(-)
diff --git a/reddit-clone/Dockerfile b/reddit-clone/Dockerfile
index 1ccfe6b3..677beed7 100644
--- a/reddit-clone/Dockerfile
+++ b/reddit-clone/Dockerfile
@@ -2,11 +2,13 @@ FROM python:3.9
WORKDIR /app
-COPY ./pyproject.toml ./poetry.lock /app/
-
+# First install poetry
RUN pip install poetry
+
+# Then install dependencies
+COPY ./poetry.lock ./pyproject.toml ./
RUN poetry install
-COPY ./reddit /app/reddit/
+COPY ./ ./
CMD [ "poetry", "run", "uvicorn", "reddit:app" ]
diff --git a/reddit-clone/docker-compose.yml b/reddit-clone/docker-compose.yml
index c105ff0a..3245b6d3 100644
--- a/reddit-clone/docker-compose.yml
+++ b/reddit-clone/docker-compose.yml
@@ -1,7 +1,6 @@
services:
database:
image: postgres:13-alpine
- restart: unless-stopped
env_file:
- ".env"
volumes:
@@ -15,7 +14,7 @@ services:
- "6379:6379"
rabbitmq:
- image: rabbitmq:3
+ image: rabbitmq:3.8-alpine
env_file:
- ".env"
ports:
From 59ec70f55074cc67f70441c470903089730cd1ed Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Fri, 15 Oct 2021 10:48:01 +0530
Subject: [PATCH 122/150] update Dockerfile
---
reddit-clone/Dockerfile | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/reddit-clone/Dockerfile b/reddit-clone/Dockerfile
index 677beed7..39dad045 100644
--- a/reddit-clone/Dockerfile
+++ b/reddit-clone/Dockerfile
@@ -2,13 +2,14 @@ FROM python:3.9
WORKDIR /app
-# First install poetry
+# install poetry
RUN pip install poetry
-# Then install dependencies
+# install dependencies
COPY ./poetry.lock ./pyproject.toml ./
RUN poetry install
+# copy project files
COPY ./ ./
CMD [ "poetry", "run", "uvicorn", "reddit:app" ]
From 77fc17415e2a0fd0cd0eda351d0e990a8c0bac3c Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Fri, 15 Oct 2021 10:52:51 +0530
Subject: [PATCH 123/150] simplify setup instructions
---
reddit-clone/README.md | 33 ++++-----------------------------
1 file changed, 4 insertions(+), 29 deletions(-)
diff --git a/reddit-clone/README.md b/reddit-clone/README.md
index fdfb7843..df5ed4da 100644
--- a/reddit-clone/README.md
+++ b/reddit-clone/README.md
@@ -27,36 +27,11 @@ most of the features that Strawberry gives us.
## How to use
-1. Install dependencies
+You can use [Docker Compose](https://github.com/docker/compose) to run this example. Make sure you have it installed on your machine!
-Use [poetry](https://python-poetry.org/) to install dependencies:
-
-```bash
-poetry install
-```
-
-2. Setup database
- This example needs a PostgreSQL database. Make sure you have one on your machine.
-
-3. Configure envionment variables
- Next up, you'd need to setup your environment variables. There is an example [`.env`](.env.example) file
- which you can reference! (Blank variables are optional)
-
-4. Run migrations
-
-Run [alembic](https://alembic.sqlalchemy.org/en/latest/) to create the database
-and populate it with movie data:
-
-```bash
-poetry run alembic upgrade head
-```
-
-5. Run the server
-
-Run [uvicorn](https://www.uvicorn.org/) to run the server:
-
-```bash
-poetry run uvicorn reddit:app --reload
+```text
+cd reddit-clone
+docker compose up
```
You can now explore the GraphQL API here: http://localhost:8000/graphql
From 4178ea653cf8e25a805be2fbbd3425e9f28c22b9 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Fri, 15 Oct 2021 11:53:02 +0530
Subject: [PATCH 124/150] update docker compose config
---
reddit-clone/README.md | 5 ++++-
reddit-clone/docker-compose.yml | 29 +++++++++++++++++++++--------
2 files changed, 25 insertions(+), 9 deletions(-)
diff --git a/reddit-clone/README.md b/reddit-clone/README.md
index df5ed4da..5219eacb 100644
--- a/reddit-clone/README.md
+++ b/reddit-clone/README.md
@@ -27,7 +27,10 @@ most of the features that Strawberry gives us.
## How to use
-You can use [Docker Compose](https://github.com/docker/compose) to run this example. Make sure you have it installed on your machine!
+You'd need to setup environment variables first. These values will be read by docker containers when running the example.
+Make sure to create an `.env` file at the project root, referencing the [`example .env`](.env.example) file.
+
+Next up, can use [Docker Compose](https://github.com/docker/compose) to run this example. Make sure you have it installed on your machine!
```text
cd reddit-clone
diff --git a/reddit-clone/docker-compose.yml b/reddit-clone/docker-compose.yml
index 3245b6d3..357b9931 100644
--- a/reddit-clone/docker-compose.yml
+++ b/reddit-clone/docker-compose.yml
@@ -1,34 +1,45 @@
+version: "3.9"
services:
- database:
- image: postgres:13-alpine
+ postgres:
+ image: postgres:14-alpine
+ container_name: reddit-clone-postgres
+ restart: unless-stopped
env_file:
- ".env"
+ ports:
+ - "5432:5432"
volumes:
- postgres-data:/var/lib/postgresql/data
redis:
- image: redis:6-alpine
- env_file:
- - ".env"
+ image: redis:6.2-alpine
+ container_name: reddit-clone-redis
+ restart: unless-stopped
ports:
- "6379:6379"
+ volumes:
+ - redis-data:/data
rabbitmq:
image: rabbitmq:3.8-alpine
- env_file:
- - ".env"
+ container_name: reddit-clone-rabbitmq
+ restart: unless-stopped
ports:
- "5672:5672"
+ volumes:
+ - rabbitmq-data:/data
app:
build:
context: .
+ container_name: reddit-clone-app
env_file:
- ".env"
depends_on:
- - database
+ - postgres
# celery:
+ # container_name: reddit-clone-celery
# build:
# context: .
# restart: always
@@ -40,4 +51,6 @@ services:
# - app
volumes:
+ redis-data:
+ rabbitmq-data:
postgres-data:
From 7f2bb23c107827a90baef6d0a7dfb71a7881c658 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Fri, 15 Oct 2021 12:05:58 +0530
Subject: [PATCH 125/150] update tasks.py
---
reddit-clone/reddit/tasks.py | 3 ---
1 file changed, 3 deletions(-)
diff --git a/reddit-clone/reddit/tasks.py b/reddit-clone/reddit/tasks.py
index d2c470fd..60d5b489 100644
--- a/reddit-clone/reddit/tasks.py
+++ b/reddit-clone/reddit/tasks.py
@@ -7,6 +7,3 @@
celery = Celery(__name__)
celery.conf.result_backend = settings.CELERY_BACKEND
celery.conf.broker_url = settings.CELERY_BROKER
-
-if __name__ == "__main__":
- celery.start()
From 92bb3aa4df3069d58c2a292b7e38adb78766f8b0 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Fri, 15 Oct 2021 12:22:35 +0530
Subject: [PATCH 126/150] add PGadmin
---
reddit-clone/.env.example | 19 ++++++++++++-------
reddit-clone/docker-compose.yml | 11 +++++++++++
2 files changed, 23 insertions(+), 7 deletions(-)
diff --git a/reddit-clone/.env.example b/reddit-clone/.env.example
index 51fa3201..1f5f17ea 100644
--- a/reddit-clone/.env.example
+++ b/reddit-clone/.env.example
@@ -1,16 +1,21 @@
# app
-DATABASE_URI=postgresql+asyncpg://localhost/db_name?user=postgres&password=postgres
+DATABASE_URI=postgresql+asyncpg://localhost/reddit_clone?user=postgres&password=reddit_clone
MAIL_HOST=127.0.0.1
MAIL_PORT=25
-CELERY_BROKER=
+CELERY_BROKER=amqp://guest@queue//
CELERY_BACKEND=redis://localhost:6379
-DEBUG=
+DEBUG=False
MAIL_USERNAME=
MAIL_PASSWORD=
MAIL_SENDER=
# database
-POSTGRES_SERVER=
-POSTGRES_USER=
-POSTGRES_PASSWORD=
-POSTGRES_DB=
+POSTGRES_SERVER=localhost
+POSTGRES_USER=postgres
+POSTGRES_PASSWORD=reddit_clone
+POSTGRES_DB=reddit_clone
+
+# PGadmin
+PGADMIN_LISTEN_PORT=5050
+PGADMIN_DEFAULT_EMAIL=
+PGADMIN_DEFAULT_PASSWORD=
diff --git a/reddit-clone/docker-compose.yml b/reddit-clone/docker-compose.yml
index 357b9931..03762761 100644
--- a/reddit-clone/docker-compose.yml
+++ b/reddit-clone/docker-compose.yml
@@ -11,6 +11,17 @@ services:
volumes:
- postgres-data:/var/lib/postgresql/data
+ pgadmin:
+ image: dpage/pgadmin4
+ container_name: reddit-clone-admin
+ restart: unless-stopped
+ env_file:
+ - ".env"
+ ports:
+ - "5555:80"
+ depends_on:
+ - postgres
+
redis:
image: redis:6.2-alpine
container_name: reddit-clone-redis
From 0528e629bde1867cc6efc38c24f9d85ac921a3d8 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Fri, 15 Oct 2021 12:30:16 +0530
Subject: [PATCH 127/150] update pgadmin config
---
reddit-clone/.env.example | 4 ++--
reddit-clone/docker-compose.yml | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/reddit-clone/.env.example b/reddit-clone/.env.example
index 1f5f17ea..f6bdd198 100644
--- a/reddit-clone/.env.example
+++ b/reddit-clone/.env.example
@@ -17,5 +17,5 @@ POSTGRES_DB=reddit_clone
# PGadmin
PGADMIN_LISTEN_PORT=5050
-PGADMIN_DEFAULT_EMAIL=
-PGADMIN_DEFAULT_PASSWORD=
+PGADMIN_DEFAULT_EMAIL=admin@reddit-clone.com
+PGADMIN_DEFAULT_PASSWORD=admin
diff --git a/reddit-clone/docker-compose.yml b/reddit-clone/docker-compose.yml
index 03762761..85b94a20 100644
--- a/reddit-clone/docker-compose.yml
+++ b/reddit-clone/docker-compose.yml
@@ -13,7 +13,7 @@ services:
pgadmin:
image: dpage/pgadmin4
- container_name: reddit-clone-admin
+ container_name: reddit-clone-pgadmin
restart: unless-stopped
env_file:
- ".env"
From 89d8f8645868a78588fc1f2e69fa2040e8f51604 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Fri, 15 Oct 2021 14:14:25 +0530
Subject: [PATCH 128/150] update Dockerfile
---
reddit-clone/.env.example | 1 -
reddit-clone/Dockerfile | 2 +-
reddit-clone/docker-compose.yml | 2 ++
3 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/reddit-clone/.env.example b/reddit-clone/.env.example
index f6bdd198..3e3d9a94 100644
--- a/reddit-clone/.env.example
+++ b/reddit-clone/.env.example
@@ -16,6 +16,5 @@ POSTGRES_PASSWORD=reddit_clone
POSTGRES_DB=reddit_clone
# PGadmin
-PGADMIN_LISTEN_PORT=5050
PGADMIN_DEFAULT_EMAIL=admin@reddit-clone.com
PGADMIN_DEFAULT_PASSWORD=admin
diff --git a/reddit-clone/Dockerfile b/reddit-clone/Dockerfile
index 39dad045..562002f0 100644
--- a/reddit-clone/Dockerfile
+++ b/reddit-clone/Dockerfile
@@ -12,4 +12,4 @@ RUN poetry install
# copy project files
COPY ./ ./
-CMD [ "poetry", "run", "uvicorn", "reddit:app" ]
+CMD [ "poetry", "run", "uvicorn", "reddit:app", "--host=0.0.0.0" ]
diff --git a/reddit-clone/docker-compose.yml b/reddit-clone/docker-compose.yml
index 85b94a20..a8f32602 100644
--- a/reddit-clone/docker-compose.yml
+++ b/reddit-clone/docker-compose.yml
@@ -46,6 +46,8 @@ services:
container_name: reddit-clone-app
env_file:
- ".env"
+ ports:
+ - "8000:80"
depends_on:
- postgres
From cc6f6dc0e3ebbaf5147ad1e6c303e32b3721b14c Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Fri, 15 Oct 2021 14:43:50 +0530
Subject: [PATCH 129/150] expose correct docker port
---
reddit-clone/docker-compose.yml | 2 +-
reddit-clone/reddit/users/queries.py | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/reddit-clone/docker-compose.yml b/reddit-clone/docker-compose.yml
index a8f32602..8e7a667e 100644
--- a/reddit-clone/docker-compose.yml
+++ b/reddit-clone/docker-compose.yml
@@ -47,7 +47,7 @@ services:
env_file:
- ".env"
ports:
- - "8000:80"
+ - "8000:8000"
depends_on:
- postgres
diff --git a/reddit-clone/reddit/users/queries.py b/reddit-clone/reddit/users/queries.py
index f6eef9e9..a027af9f 100644
--- a/reddit-clone/reddit/users/queries.py
+++ b/reddit-clone/reddit/users/queries.py
@@ -6,12 +6,12 @@
from reddit.database import get_session
from reddit.users.types import UserType
-from reddit.users.models import User
+from reddit.users.services import user_by_username
async def resolve_user(info: Info, username: str) -> Optional[UserType]:
async with get_session() as session:
- user = await User.by_username(session=session, username=username)
+ user = await user_by_username(session=session, username=username)
if user is not None:
return cast(UserType, user)
return user
From b09177f9a460ca0901969ec6091bb49b81f393f5 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Fri, 15 Oct 2021 14:55:39 +0530
Subject: [PATCH 130/150] clear the clutter
---
reddit-clone/.env.example | 4 ----
reddit-clone/docker-compose.yml | 11 -----------
2 files changed, 15 deletions(-)
diff --git a/reddit-clone/.env.example b/reddit-clone/.env.example
index 3e3d9a94..40d7ed22 100644
--- a/reddit-clone/.env.example
+++ b/reddit-clone/.env.example
@@ -14,7 +14,3 @@ POSTGRES_SERVER=localhost
POSTGRES_USER=postgres
POSTGRES_PASSWORD=reddit_clone
POSTGRES_DB=reddit_clone
-
-# PGadmin
-PGADMIN_DEFAULT_EMAIL=admin@reddit-clone.com
-PGADMIN_DEFAULT_PASSWORD=admin
diff --git a/reddit-clone/docker-compose.yml b/reddit-clone/docker-compose.yml
index 8e7a667e..4a449006 100644
--- a/reddit-clone/docker-compose.yml
+++ b/reddit-clone/docker-compose.yml
@@ -11,17 +11,6 @@ services:
volumes:
- postgres-data:/var/lib/postgresql/data
- pgadmin:
- image: dpage/pgadmin4
- container_name: reddit-clone-pgadmin
- restart: unless-stopped
- env_file:
- - ".env"
- ports:
- - "5555:80"
- depends_on:
- - postgres
-
redis:
image: redis:6.2-alpine
container_name: reddit-clone-redis
From 9f1c97e1d8e434d03e151f232809b56ccb080625 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Fri, 15 Oct 2021 15:49:04 +0530
Subject: [PATCH 131/150] make builds slim
---
reddit-clone/Dockerfile | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/reddit-clone/Dockerfile b/reddit-clone/Dockerfile
index 562002f0..9b938443 100644
--- a/reddit-clone/Dockerfile
+++ b/reddit-clone/Dockerfile
@@ -1,4 +1,4 @@
-FROM python:3.9
+FROM python:3.9-slim
WORKDIR /app
@@ -12,4 +12,6 @@ RUN poetry install
# copy project files
COPY ./ ./
+EXPOSE 8000
+
CMD [ "poetry", "run", "uvicorn", "reddit:app", "--host=0.0.0.0" ]
From 2a1abd498a4df753c51c65074b276cf5ccc9cd29 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Fri, 15 Oct 2021 15:57:58 +0530
Subject: [PATCH 132/150] update docker-compose.yml
---
reddit-clone/docker-compose.yml | 1 -
1 file changed, 1 deletion(-)
diff --git a/reddit-clone/docker-compose.yml b/reddit-clone/docker-compose.yml
index 4a449006..91f75e7b 100644
--- a/reddit-clone/docker-compose.yml
+++ b/reddit-clone/docker-compose.yml
@@ -50,7 +50,6 @@ services:
# depends_on:
# - redis
# - rabbitmq
- # - app
volumes:
redis-data:
From 26b521eaec8b62657d44e321689208869a1112e2 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Sat, 16 Oct 2021 09:51:07 +0530
Subject: [PATCH 133/150] update docker-compose.yml
---
reddit-clone/.env.example | 16 ----------------
reddit-clone/Dockerfile | 2 +-
reddit-clone/README.md | 7 ++-----
reddit-clone/docker-compose.yml | 20 ++++++++++++++------
reddit-clone/reddit/database.py | 2 +-
reddit-clone/reddit/settings.py | 2 +-
6 files changed, 19 insertions(+), 30 deletions(-)
delete mode 100644 reddit-clone/.env.example
diff --git a/reddit-clone/.env.example b/reddit-clone/.env.example
deleted file mode 100644
index 40d7ed22..00000000
--- a/reddit-clone/.env.example
+++ /dev/null
@@ -1,16 +0,0 @@
-# app
-DATABASE_URI=postgresql+asyncpg://localhost/reddit_clone?user=postgres&password=reddit_clone
-MAIL_HOST=127.0.0.1
-MAIL_PORT=25
-CELERY_BROKER=amqp://guest@queue//
-CELERY_BACKEND=redis://localhost:6379
-DEBUG=False
-MAIL_USERNAME=
-MAIL_PASSWORD=
-MAIL_SENDER=
-
-# database
-POSTGRES_SERVER=localhost
-POSTGRES_USER=postgres
-POSTGRES_PASSWORD=reddit_clone
-POSTGRES_DB=reddit_clone
diff --git a/reddit-clone/Dockerfile b/reddit-clone/Dockerfile
index 9b938443..9503b186 100644
--- a/reddit-clone/Dockerfile
+++ b/reddit-clone/Dockerfile
@@ -14,4 +14,4 @@ COPY ./ ./
EXPOSE 8000
-CMD [ "poetry", "run", "uvicorn", "reddit:app", "--host=0.0.0.0" ]
+CMD poetry run uvicorn reddit:app --host=0.0.0.0
diff --git a/reddit-clone/README.md b/reddit-clone/README.md
index 5219eacb..8486a8ff 100644
--- a/reddit-clone/README.md
+++ b/reddit-clone/README.md
@@ -22,15 +22,12 @@ most of the features that Strawberry gives us.
- [x] data modelling with relations
- [x] Error modelling within the schema
- [ ] Authorization with the permissions API
-- [ ] Batch loading with dataloaders
+- [x] Batch loading with dataloaders
- [x] modular codebase
## How to use
-You'd need to setup environment variables first. These values will be read by docker containers when running the example.
-Make sure to create an `.env` file at the project root, referencing the [`example .env`](.env.example) file.
-
-Next up, can use [Docker Compose](https://github.com/docker/compose) to run this example. Make sure you have it installed on your machine!
+You can use [Docker Compose](https://github.com/docker/compose) to run this example. Make sure you have it installed on your machine!
```text
cd reddit-clone
diff --git a/reddit-clone/docker-compose.yml b/reddit-clone/docker-compose.yml
index 91f75e7b..6fac3710 100644
--- a/reddit-clone/docker-compose.yml
+++ b/reddit-clone/docker-compose.yml
@@ -4,8 +4,10 @@ services:
image: postgres:14-alpine
container_name: reddit-clone-postgres
restart: unless-stopped
- env_file:
- - ".env"
+ environment:
+ POSTGRES_DB: reddit_clone
+ POSTGRES_PASSWORD: reddit_clone
+ POSTGRES_USER: reddit_clone
ports:
- "5432:5432"
volumes:
@@ -33,8 +35,16 @@ services:
build:
context: .
container_name: reddit-clone-app
- env_file:
- - ".env"
+ environment:
+ DEBUG: "false"
+ CELERY_BROKER: amqp://guest@queue//
+ CELERY_BACKEND: redis://localhost:6379
+ DATABASE_URL: postgres://reddit_clone:reddit_clone@db:5432/reddit_clone
+ MAIL_HOST: 127.0.0.1
+ MAIL_PORT: 25
+ MAIL_USERNAME:
+ MAIL_PASSWORD:
+ MAIL_SENDER:
ports:
- "8000:8000"
depends_on:
@@ -45,8 +55,6 @@ services:
# build:
# context: .
# restart: always
- # env_file:
- # - ".env"
# depends_on:
# - redis
# - rabbitmq
diff --git a/reddit-clone/reddit/database.py b/reddit-clone/reddit/database.py
index 2f7b7c23..cadd3fb8 100644
--- a/reddit-clone/reddit/database.py
+++ b/reddit-clone/reddit/database.py
@@ -7,7 +7,7 @@
from reddit import settings
-engine = create_async_engine(settings.DATABASE_URI, future=True)
+engine = create_async_engine(settings.DATABASE_URL, future=True)
session_factory = sessionmaker(bind=engine, class_=AsyncSession)
diff --git a/reddit-clone/reddit/settings.py b/reddit-clone/reddit/settings.py
index 4b12d8be..ddffe396 100644
--- a/reddit-clone/reddit/settings.py
+++ b/reddit-clone/reddit/settings.py
@@ -9,7 +9,7 @@
DEBUG: bool = config("DEBUG", cast=bool, default=False)
# SQLAlchemy database URL.
-DATABASE_URI: str = config("DATABASE_URI", cast=str)
+DATABASE_URL: str = config("DATABASE_URL", cast=str)
# mail client host name.
MAIL_HOST: str = config("MAIL_HOST", cast=str)
From fc1dd3bfefe7bcd8ff089e461e0302c8da5d0b56 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Sat, 16 Oct 2021 09:54:24 +0530
Subject: [PATCH 134/150] update README.md
---
reddit-clone/README.md | 1 -
1 file changed, 1 deletion(-)
diff --git a/reddit-clone/README.md b/reddit-clone/README.md
index 8486a8ff..a0e07896 100644
--- a/reddit-clone/README.md
+++ b/reddit-clone/README.md
@@ -30,7 +30,6 @@ most of the features that Strawberry gives us.
You can use [Docker Compose](https://github.com/docker/compose) to run this example. Make sure you have it installed on your machine!
```text
-cd reddit-clone
docker compose up
```
From 9c9aab6064deda4e20d12d17da99c4957beb6698 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Sat, 16 Oct 2021 12:24:29 +0530
Subject: [PATCH 135/150] update celery deps
---
reddit-clone/Dockerfile | 3 +-
reddit-clone/docker-compose.yml | 41 +++++--
reddit-clone/poetry.lock | 208 +++++++++++++++++++-------------
reddit-clone/pyproject.toml | 2 +-
4 files changed, 158 insertions(+), 96 deletions(-)
diff --git a/reddit-clone/Dockerfile b/reddit-clone/Dockerfile
index 9503b186..d6ab08d3 100644
--- a/reddit-clone/Dockerfile
+++ b/reddit-clone/Dockerfile
@@ -1,4 +1,5 @@
FROM python:3.9-slim
+LABEL maintainer="Aryan Iyappan "
WORKDIR /app
@@ -7,7 +8,7 @@ RUN pip install poetry
# install dependencies
COPY ./poetry.lock ./pyproject.toml ./
-RUN poetry install
+RUN poetry install --no-dev
# copy project files
COPY ./ ./
diff --git a/reddit-clone/docker-compose.yml b/reddit-clone/docker-compose.yml
index 6fac3710..9f3fae20 100644
--- a/reddit-clone/docker-compose.yml
+++ b/reddit-clone/docker-compose.yml
@@ -31,15 +31,15 @@ services:
volumes:
- rabbitmq-data:/data
- app:
+ server:
build:
- context: .
- container_name: reddit-clone-app
+ context: "."
+ container_name: reddit-clone-server
environment:
DEBUG: "false"
- CELERY_BROKER: amqp://guest@queue//
+ CELERY_BROKER: amqp://guest:guest@localhost:5672//
CELERY_BACKEND: redis://localhost:6379
- DATABASE_URL: postgres://reddit_clone:reddit_clone@db:5432/reddit_clone
+ DATABASE_URL: postgresql+asyncpg://reddit_clone:reddit_clone@db:5432/reddit_clone
MAIL_HOST: 127.0.0.1
MAIL_PORT: 25
MAIL_USERNAME:
@@ -50,16 +50,31 @@ services:
depends_on:
- postgres
- # celery:
- # container_name: reddit-clone-celery
- # build:
- # context: .
- # restart: always
- # depends_on:
- # - redis
- # - rabbitmq
+ celery:
+ build:
+ context: "."
+ container_name: reddit-clone-celery
+ restart: on-failure
+ command: poetry run celery -A reddit.tasks worker
+ environment:
+ DEBUG: "false"
+ CELERY_BROKER: amqp://guest:guest@localhost:5672//
+ CELERY_BACKEND: redis://localhost:6379
+ DATABASE_URL: postgresql+asyncpg://reddit_clone:reddit_clone@db:5432/reddit_clone
+ MAIL_HOST: 127.0.0.1
+ MAIL_PORT: 25
+ MAIL_USERNAME:
+ MAIL_PASSWORD:
+ MAIL_SENDER:
+ depends_on:
+ - server
+ - rabbitmq
+ - redis
volumes:
redis-data:
+ driver: local
rabbitmq-data:
+ driver: local
postgres-data:
+ driver: local
diff --git a/reddit-clone/poetry.lock b/reddit-clone/poetry.lock
index dd0e275a..d5f7a2c7 100644
--- a/reddit-clone/poetry.lock
+++ b/reddit-clone/poetry.lock
@@ -171,7 +171,9 @@ click-didyoumean = ">=0.0.3"
click-plugins = ">=1.1.1"
click-repl = ">=0.1.6"
kombu = ">=5.1.0,<6.0"
+librabbitmq = {version = ">=1.5.0", optional = true, markers = "extra == \"librabbitmq\""}
pytz = ">0.0-dev"
+redis = {version = ">=3.2.0", optional = true, markers = "extra == \"redis\""}
vine = ">=5.0.0,<6.0"
[package.extras]
@@ -209,7 +211,7 @@ zstd = ["zstandard"]
[[package]]
name = "cffi"
-version = "1.14.6"
+version = "1.15.0"
description = "Foreign Function Interface for Python calling C code."
category = "main"
optional = false
@@ -448,6 +450,18 @@ sqs = ["boto3 (>=1.4.4)", "pycurl (==7.43.0.2)", "urllib3 (<1.26)"]
yaml = ["PyYAML (>=3.10)"]
zookeeper = ["kazoo (>=1.3.1)"]
+[[package]]
+name = "librabbitmq"
+version = "2.0.0"
+description = "AMQP Client using the rabbitmq-c library."
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+amqp = ">=1.4.6"
+six = ">=1.0.0"
+
[[package]]
name = "mako"
version = "1.1.5"
@@ -628,11 +642,22 @@ python-versions = "*"
[[package]]
name = "pyyaml"
-version = "5.4.1"
+version = "6.0"
description = "YAML parser and emitter for Python"
category = "main"
optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
+python-versions = ">=3.6"
+
+[[package]]
+name = "redis"
+version = "3.5.3"
+description = "Python client for Redis key-value store"
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+
+[package.extras]
+hiredis = ["hiredis (>=0.1.3)"]
[[package]]
name = "regex"
@@ -714,7 +739,7 @@ sqlcipher = ["sqlcipher3-binary"]
[[package]]
name = "sqlalchemy2-stubs"
-version = "0.0.2a17"
+version = "0.0.2a18"
description = "Typing Stubs for SQLAlchemy 1.4"
category = "main"
optional = false
@@ -883,7 +908,7 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytes
[metadata]
lock-version = "1.1"
python-versions = "^3.7"
-content-hash = "8753228c24cfc820ae979004bf108c0f179adbba378a2612180dc1b38e120a4d"
+content-hash = "700aac6bc63fb865ad58061cecb0089dd106cc48be7646f6590aefbf748adb96"
[metadata.files]
aiofiles = [
@@ -955,51 +980,56 @@ celery = [
{file = "celery-5.1.2.tar.gz", hash = "sha256:8d9a3de9162965e97f8e8cc584c67aad83b3f7a267584fa47701ed11c3e0d4b0"},
]
cffi = [
- {file = "cffi-1.14.6-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:22b9c3c320171c108e903d61a3723b51e37aaa8c81255b5e7ce102775bd01e2c"},
- {file = "cffi-1.14.6-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:f0c5d1acbfca6ebdd6b1e3eded8d261affb6ddcf2186205518f1428b8569bb99"},
- {file = "cffi-1.14.6-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:99f27fefe34c37ba9875f224a8f36e31d744d8083e00f520f133cab79ad5e819"},
- {file = "cffi-1.14.6-cp27-cp27m-win32.whl", hash = "sha256:55af55e32ae468e9946f741a5d51f9896da6b9bf0bbdd326843fec05c730eb20"},
- {file = "cffi-1.14.6-cp27-cp27m-win_amd64.whl", hash = "sha256:7bcac9a2b4fdbed2c16fa5681356d7121ecabf041f18d97ed5b8e0dd38a80224"},
- {file = "cffi-1.14.6-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:ed38b924ce794e505647f7c331b22a693bee1538fdf46b0222c4717b42f744e7"},
- {file = "cffi-1.14.6-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:e22dcb48709fc51a7b58a927391b23ab37eb3737a98ac4338e2448bef8559b33"},
- {file = "cffi-1.14.6-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:aedb15f0a5a5949ecb129a82b72b19df97bbbca024081ed2ef88bd5c0a610534"},
- {file = "cffi-1.14.6-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:48916e459c54c4a70e52745639f1db524542140433599e13911b2f329834276a"},
- {file = "cffi-1.14.6-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:f627688813d0a4140153ff532537fbe4afea5a3dffce1f9deb7f91f848a832b5"},
- {file = "cffi-1.14.6-cp35-cp35m-win32.whl", hash = "sha256:f0010c6f9d1a4011e429109fda55a225921e3206e7f62a0c22a35344bfd13cca"},
- {file = "cffi-1.14.6-cp35-cp35m-win_amd64.whl", hash = "sha256:57e555a9feb4a8460415f1aac331a2dc833b1115284f7ded7278b54afc5bd218"},
- {file = "cffi-1.14.6-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:e8c6a99be100371dbb046880e7a282152aa5d6127ae01783e37662ef73850d8f"},
- {file = "cffi-1.14.6-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:19ca0dbdeda3b2615421d54bef8985f72af6e0c47082a8d26122adac81a95872"},
- {file = "cffi-1.14.6-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:d950695ae4381ecd856bcaf2b1e866720e4ab9a1498cba61c602e56630ca7195"},
- {file = "cffi-1.14.6-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9dc245e3ac69c92ee4c167fbdd7428ec1956d4e754223124991ef29eb57a09d"},
- {file = "cffi-1.14.6-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a8661b2ce9694ca01c529bfa204dbb144b275a31685a075ce123f12331be790b"},
- {file = "cffi-1.14.6-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b315d709717a99f4b27b59b021e6207c64620790ca3e0bde636a6c7f14618abb"},
- {file = "cffi-1.14.6-cp36-cp36m-win32.whl", hash = "sha256:80b06212075346b5546b0417b9f2bf467fea3bfe7352f781ffc05a8ab24ba14a"},
- {file = "cffi-1.14.6-cp36-cp36m-win_amd64.whl", hash = "sha256:a9da7010cec5a12193d1af9872a00888f396aba3dc79186604a09ea3ee7c029e"},
- {file = "cffi-1.14.6-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:4373612d59c404baeb7cbd788a18b2b2a8331abcc84c3ba40051fcd18b17a4d5"},
- {file = "cffi-1.14.6-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:f10afb1004f102c7868ebfe91c28f4a712227fe4cb24974350ace1f90e1febbf"},
- {file = "cffi-1.14.6-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:fd4305f86f53dfd8cd3522269ed7fc34856a8ee3709a5e28b2836b2db9d4cd69"},
- {file = "cffi-1.14.6-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6d6169cb3c6c2ad50db5b868db6491a790300ade1ed5d1da29289d73bbe40b56"},
- {file = "cffi-1.14.6-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5d4b68e216fc65e9fe4f524c177b54964af043dde734807586cf5435af84045c"},
- {file = "cffi-1.14.6-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33791e8a2dc2953f28b8d8d300dde42dd929ac28f974c4b4c6272cb2955cb762"},
- {file = "cffi-1.14.6-cp37-cp37m-win32.whl", hash = "sha256:0c0591bee64e438883b0c92a7bed78f6290d40bf02e54c5bf0978eaf36061771"},
- {file = "cffi-1.14.6-cp37-cp37m-win_amd64.whl", hash = "sha256:8eb687582ed7cd8c4bdbff3df6c0da443eb89c3c72e6e5dcdd9c81729712791a"},
- {file = "cffi-1.14.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ba6f2b3f452e150945d58f4badd92310449876c4c954836cfb1803bdd7b422f0"},
- {file = "cffi-1.14.6-cp38-cp38-manylinux1_i686.whl", hash = "sha256:64fda793737bc4037521d4899be780534b9aea552eb673b9833b01f945904c2e"},
- {file = "cffi-1.14.6-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:9f3e33c28cd39d1b655ed1ba7247133b6f7fc16fa16887b120c0c670e35ce346"},
- {file = "cffi-1.14.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26bb2549b72708c833f5abe62b756176022a7b9a7f689b571e74c8478ead51dc"},
- {file = "cffi-1.14.6-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eb687a11f0a7a1839719edd80f41e459cc5366857ecbed383ff376c4e3cc6afd"},
- {file = "cffi-1.14.6-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d2ad4d668a5c0645d281dcd17aff2be3212bc109b33814bbb15c4939f44181cc"},
- {file = "cffi-1.14.6-cp38-cp38-win32.whl", hash = "sha256:487d63e1454627c8e47dd230025780e91869cfba4c753a74fda196a1f6ad6548"},
- {file = "cffi-1.14.6-cp38-cp38-win_amd64.whl", hash = "sha256:c33d18eb6e6bc36f09d793c0dc58b0211fccc6ae5149b808da4a62660678b156"},
- {file = "cffi-1.14.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:06c54a68935738d206570b20da5ef2b6b6d92b38ef3ec45c5422c0ebaf338d4d"},
- {file = "cffi-1.14.6-cp39-cp39-manylinux1_i686.whl", hash = "sha256:f174135f5609428cc6e1b9090f9268f5c8935fddb1b25ccb8255a2d50de6789e"},
- {file = "cffi-1.14.6-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:f3ebe6e73c319340830a9b2825d32eb6d8475c1dac020b4f0aa774ee3b898d1c"},
- {file = "cffi-1.14.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c8d896becff2fa653dc4438b54a5a25a971d1f4110b32bd3068db3722c80202"},
- {file = "cffi-1.14.6-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4922cd707b25e623b902c86188aca466d3620892db76c0bdd7b99a3d5e61d35f"},
- {file = "cffi-1.14.6-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c9e005e9bd57bc987764c32a1bee4364c44fdc11a3cc20a40b93b444984f2b87"},
- {file = "cffi-1.14.6-cp39-cp39-win32.whl", hash = "sha256:eb9e2a346c5238a30a746893f23a9535e700f8192a68c07c0258e7ece6ff3728"},
- {file = "cffi-1.14.6-cp39-cp39-win_amd64.whl", hash = "sha256:818014c754cd3dba7229c0f5884396264d51ffb87ec86e927ef0be140bfdb0d2"},
- {file = "cffi-1.14.6.tar.gz", hash = "sha256:c9a875ce9d7fe32887784274dd533c57909b7b1dcadcc128a2ac21331a9765dd"},
+ {file = "cffi-1.15.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:c2502a1a03b6312837279c8c1bd3ebedf6c12c4228ddbad40912d671ccc8a962"},
+ {file = "cffi-1.15.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:23cfe892bd5dd8941608f93348c0737e369e51c100d03718f108bf1add7bd6d0"},
+ {file = "cffi-1.15.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:41d45de54cd277a7878919867c0f08b0cf817605e4eb94093e7516505d3c8d14"},
+ {file = "cffi-1.15.0-cp27-cp27m-win32.whl", hash = "sha256:4a306fa632e8f0928956a41fa8e1d6243c71e7eb59ffbd165fc0b41e316b2474"},
+ {file = "cffi-1.15.0-cp27-cp27m-win_amd64.whl", hash = "sha256:e7022a66d9b55e93e1a845d8c9eba2a1bebd4966cd8bfc25d9cd07d515b33fa6"},
+ {file = "cffi-1.15.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:14cd121ea63ecdae71efa69c15c5543a4b5fbcd0bbe2aad864baca0063cecf27"},
+ {file = "cffi-1.15.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:d4d692a89c5cf08a8557fdeb329b82e7bf609aadfaed6c0d79f5a449a3c7c023"},
+ {file = "cffi-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0104fb5ae2391d46a4cb082abdd5c69ea4eab79d8d44eaaf79f1b1fd806ee4c2"},
+ {file = "cffi-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:91ec59c33514b7c7559a6acda53bbfe1b283949c34fe7440bcf917f96ac0723e"},
+ {file = "cffi-1.15.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f5c7150ad32ba43a07c4479f40241756145a1f03b43480e058cfd862bf5041c7"},
+ {file = "cffi-1.15.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:00c878c90cb53ccfaae6b8bc18ad05d2036553e6d9d1d9dbcf323bbe83854ca3"},
+ {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abb9a20a72ac4e0fdb50dae135ba5e77880518e742077ced47eb1499e29a443c"},
+ {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a5263e363c27b653a90078143adb3d076c1a748ec9ecc78ea2fb916f9b861962"},
+ {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f54a64f8b0c8ff0b64d18aa76675262e1700f3995182267998c31ae974fbc382"},
+ {file = "cffi-1.15.0-cp310-cp310-win32.whl", hash = "sha256:c21c9e3896c23007803a875460fb786118f0cdd4434359577ea25eb556e34c55"},
+ {file = "cffi-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:5e069f72d497312b24fcc02073d70cb989045d1c91cbd53979366077959933e0"},
+ {file = "cffi-1.15.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:64d4ec9f448dfe041705426000cc13e34e6e5bb13736e9fd62e34a0b0c41566e"},
+ {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2756c88cbb94231c7a147402476be2c4df2f6078099a6f4a480d239a8817ae39"},
+ {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b96a311ac60a3f6be21d2572e46ce67f09abcf4d09344c49274eb9e0bf345fc"},
+ {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75e4024375654472cc27e91cbe9eaa08567f7fbdf822638be2814ce059f58032"},
+ {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:59888172256cac5629e60e72e86598027aca6bf01fa2465bdb676d37636573e8"},
+ {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:27c219baf94952ae9d50ec19651a687b826792055353d07648a5695413e0c605"},
+ {file = "cffi-1.15.0-cp36-cp36m-win32.whl", hash = "sha256:4958391dbd6249d7ad855b9ca88fae690783a6be9e86df65865058ed81fc860e"},
+ {file = "cffi-1.15.0-cp36-cp36m-win_amd64.whl", hash = "sha256:f6f824dc3bce0edab5f427efcfb1d63ee75b6fcb7282900ccaf925be84efb0fc"},
+ {file = "cffi-1.15.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:06c48159c1abed75c2e721b1715c379fa3200c7784271b3c46df01383b593636"},
+ {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:c2051981a968d7de9dd2d7b87bcb9c939c74a34626a6e2f8181455dd49ed69e4"},
+ {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:fd8a250edc26254fe5b33be00402e6d287f562b6a5b2152dec302fa15bb3e997"},
+ {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91d77d2a782be4274da750752bb1650a97bfd8f291022b379bb8e01c66b4e96b"},
+ {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:45db3a33139e9c8f7c09234b5784a5e33d31fd6907800b316decad50af323ff2"},
+ {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:263cc3d821c4ab2213cbe8cd8b355a7f72a8324577dc865ef98487c1aeee2bc7"},
+ {file = "cffi-1.15.0-cp37-cp37m-win32.whl", hash = "sha256:17771976e82e9f94976180f76468546834d22a7cc404b17c22df2a2c81db0c66"},
+ {file = "cffi-1.15.0-cp37-cp37m-win_amd64.whl", hash = "sha256:3415c89f9204ee60cd09b235810be700e993e343a408693e80ce7f6a40108029"},
+ {file = "cffi-1.15.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4238e6dab5d6a8ba812de994bbb0a79bddbdf80994e4ce802b6f6f3142fcc880"},
+ {file = "cffi-1.15.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0808014eb713677ec1292301ea4c81ad277b6cdf2fdd90fd540af98c0b101d20"},
+ {file = "cffi-1.15.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:57e9ac9ccc3101fac9d6014fba037473e4358ef4e89f8e181f8951a2c0162024"},
+ {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b6c2ea03845c9f501ed1313e78de148cd3f6cad741a75d43a29b43da27f2e1e"},
+ {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:10dffb601ccfb65262a27233ac273d552ddc4d8ae1bf93b21c94b8511bffe728"},
+ {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:786902fb9ba7433aae840e0ed609f45c7bcd4e225ebb9c753aa39725bb3e6ad6"},
+ {file = "cffi-1.15.0-cp38-cp38-win32.whl", hash = "sha256:da5db4e883f1ce37f55c667e5c0de439df76ac4cb55964655906306918e7363c"},
+ {file = "cffi-1.15.0-cp38-cp38-win_amd64.whl", hash = "sha256:181dee03b1170ff1969489acf1c26533710231c58f95534e3edac87fff06c443"},
+ {file = "cffi-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:45e8636704eacc432a206ac7345a5d3d2c62d95a507ec70d62f23cd91770482a"},
+ {file = "cffi-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:31fb708d9d7c3f49a60f04cf5b119aeefe5644daba1cd2a0fe389b674fd1de37"},
+ {file = "cffi-1.15.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6dc2737a3674b3e344847c8686cf29e500584ccad76204efea14f451d4cc669a"},
+ {file = "cffi-1.15.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:74fdfdbfdc48d3f47148976f49fab3251e550a8720bebc99bf1483f5bfb5db3e"},
+ {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffaa5c925128e29efbde7301d8ecaf35c8c60ffbcd6a1ffd3a552177c8e5e796"},
+ {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f7d084648d77af029acb79a0ff49a0ad7e9d09057a9bf46596dac9514dc07df"},
+ {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ef1f279350da2c586a69d32fc8733092fd32cc8ac95139a00377841f59a3f8d8"},
+ {file = "cffi-1.15.0-cp39-cp39-win32.whl", hash = "sha256:2a23af14f408d53d5e6cd4e3d9a24ff9e05906ad574822a10563efcef137979a"},
+ {file = "cffi-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:3773c4d81e6e818df2efbc7dd77325ca0dcb688116050fb2b3011218eda36139"},
+ {file = "cffi-1.15.0.tar.gz", hash = "sha256:920f0d66a896c2d99f0adbb391f990a84091179542c205fa53ce5787aff87954"},
]
click = [
{file = "click-7.1.2-py2.py3-none-any.whl", hash = "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"},
@@ -1134,6 +1164,14 @@ kombu = [
{file = "kombu-5.1.0-py3-none-any.whl", hash = "sha256:e2dedd8a86c9077c350555153825a31e456a0dc20c15d5751f00137ec9c75f0a"},
{file = "kombu-5.1.0.tar.gz", hash = "sha256:01481d99f4606f6939cdc9b637264ed353ee9e3e4f62cfb582324142c41a572d"},
]
+librabbitmq = [
+ {file = "librabbitmq-2.0.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:c2a8113d3c831808d1d940fdf43e4882636a1efe2864df7ab3bb709a45016b37"},
+ {file = "librabbitmq-2.0.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:3116e40c02d4285b8dd69834e4cbcb1a89ea534ca9147e865f11d44e7cc56eea"},
+ {file = "librabbitmq-2.0.0-cp34-cp34m-manylinux1_x86_64.whl", hash = "sha256:cd9cc09343b193d7cf2cff6c6a578061863bd986a4bdf38f922e9dc32e15d944"},
+ {file = "librabbitmq-2.0.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:98e355f486964dadae7e8b51c9a60e9aa0653bbe27f6b14542687f305c4c3652"},
+ {file = "librabbitmq-2.0.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:5cdfb473573396d43d54cef9e9b4c74fa3d1516da51d04a7b261f6ef4e0bd8be"},
+ {file = "librabbitmq-2.0.0.tar.gz", hash = "sha256:ffa2363a860ab5dcc3ce3703247e05e940c73d776c03a3f3f9deaf3cf43bb96c"},
+]
mako = [
{file = "Mako-1.1.5-py2.py3-none-any.whl", hash = "sha256:6804ee66a7f6a6416910463b00d76a7b25194cd27f1918500c5bd7be2a088a23"},
{file = "Mako-1.1.5.tar.gz", hash = "sha256:169fa52af22a91900d852e937400e79f535496191c63712e3b9fda5a9bed6fc3"},
@@ -1255,35 +1293,43 @@ pytz = [
{file = "pytz-2021.3.tar.gz", hash = "sha256:acad2d8b20a1af07d4e4c9d2e9285c5ed9104354062f275f3fcd88dcef4f1326"},
]
pyyaml = [
- {file = "PyYAML-5.4.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922"},
- {file = "PyYAML-5.4.1-cp27-cp27m-win32.whl", hash = "sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393"},
- {file = "PyYAML-5.4.1-cp27-cp27m-win_amd64.whl", hash = "sha256:4465124ef1b18d9ace298060f4eccc64b0850899ac4ac53294547536533800c8"},
- {file = "PyYAML-5.4.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185"},
- {file = "PyYAML-5.4.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253"},
- {file = "PyYAML-5.4.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc"},
- {file = "PyYAML-5.4.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:72a01f726a9c7851ca9bfad6fd09ca4e090a023c00945ea05ba1638c09dc3347"},
- {file = "PyYAML-5.4.1-cp36-cp36m-manylinux2014_s390x.whl", hash = "sha256:895f61ef02e8fed38159bb70f7e100e00f471eae2bc838cd0f4ebb21e28f8541"},
- {file = "PyYAML-5.4.1-cp36-cp36m-win32.whl", hash = "sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5"},
- {file = "PyYAML-5.4.1-cp36-cp36m-win_amd64.whl", hash = "sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df"},
- {file = "PyYAML-5.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018"},
- {file = "PyYAML-5.4.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63"},
- {file = "PyYAML-5.4.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:cb333c16912324fd5f769fff6bc5de372e9e7a202247b48870bc251ed40239aa"},
- {file = "PyYAML-5.4.1-cp37-cp37m-manylinux2014_s390x.whl", hash = "sha256:fe69978f3f768926cfa37b867e3843918e012cf83f680806599ddce33c2c68b0"},
- {file = "PyYAML-5.4.1-cp37-cp37m-win32.whl", hash = "sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b"},
- {file = "PyYAML-5.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf"},
- {file = "PyYAML-5.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46"},
- {file = "PyYAML-5.4.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb"},
- {file = "PyYAML-5.4.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:fd7f6999a8070df521b6384004ef42833b9bd62cfee11a09bda1079b4b704247"},
- {file = "PyYAML-5.4.1-cp38-cp38-manylinux2014_s390x.whl", hash = "sha256:bfb51918d4ff3d77c1c856a9699f8492c612cde32fd3bcd344af9be34999bfdc"},
- {file = "PyYAML-5.4.1-cp38-cp38-win32.whl", hash = "sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc"},
- {file = "PyYAML-5.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696"},
- {file = "PyYAML-5.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77"},
- {file = "PyYAML-5.4.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183"},
- {file = "PyYAML-5.4.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:d483ad4e639292c90170eb6f7783ad19490e7a8defb3e46f97dfe4bacae89122"},
- {file = "PyYAML-5.4.1-cp39-cp39-manylinux2014_s390x.whl", hash = "sha256:fdc842473cd33f45ff6bce46aea678a54e3d21f1b61a7750ce3c498eedfe25d6"},
- {file = "PyYAML-5.4.1-cp39-cp39-win32.whl", hash = "sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10"},
- {file = "PyYAML-5.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db"},
- {file = "PyYAML-5.4.1.tar.gz", hash = "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e"},
+ {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"},
+]
+redis = [
+ {file = "redis-3.5.3-py2.py3-none-any.whl", hash = "sha256:432b788c4530cfe16d8d943a09d40ca6c16149727e4afe8c2c9d5580c59d9f24"},
+ {file = "redis-3.5.3.tar.gz", hash = "sha256:0e7e0cfca8660dea8b7d5cd8c4f6c5e29e11f31158c0b0ae91a397f00e5a05a2"},
]
regex = [
{file = "regex-2021.10.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:981c786293a3115bc14c103086ae54e5ee50ca57f4c02ce7cf1b60318d1e8072"},
@@ -1377,8 +1423,8 @@ sqlalchemy = [
{file = "SQLAlchemy-1.4.25.tar.gz", hash = "sha256:1adf3d25e2e33afbcd48cfad8076f9378793be43e7fec3e4334306cac6bec138"},
]
sqlalchemy2-stubs = [
- {file = "sqlalchemy2-stubs-0.0.2a17.tar.gz", hash = "sha256:d7e1f63f82711c83d49eb6adccbf6bbf3cc94783436c0d34cf7ae49820023046"},
- {file = "sqlalchemy2_stubs-0.0.2a17-py3-none-any.whl", hash = "sha256:6f112f9381a29575676c3012ce10ed6bceabfad11f1d5569d7072ccf66cfa060"},
+ {file = "sqlalchemy2-stubs-0.0.2a18.tar.gz", hash = "sha256:513f8f504e7a869e6a584b9cbfa65b7d817d017ff01af61855f62087735561a9"},
+ {file = "sqlalchemy2_stubs-0.0.2a18-py3-none-any.whl", hash = "sha256:75ec8ce53db5a85884adcb9f249751bc3aefb4c24fd2b5bd62860113eea4b37a"},
]
starlette = [
{file = "starlette-0.16.0-py3-none-any.whl", hash = "sha256:38eb24bf705a2c317e15868e384c1b8a12ca396e5a3c3a003db7e667c43f939f"},
diff --git a/reddit-clone/pyproject.toml b/reddit-clone/pyproject.toml
index 2f6df1d8..0b6e8593 100644
--- a/reddit-clone/pyproject.toml
+++ b/reddit-clone/pyproject.toml
@@ -10,6 +10,7 @@ uvicorn = {extras = ["standard"], version = "^0.15.0"}
SQLAlchemy = {extras = ["mypy"], version = "^1.4.23"}
strawberry-graphql = {extras = ["asgi"], version = "^0.82.2"}
passlib = {extras = ["argon2"], version = "^1.7.4"}
+celery = {extras = ["redis", "librabbitmq"], version = "^5.1.2"}
alembic = "^1.7.1"
asyncpg = "^0.24"
graphql-relay = "^3.1.0"
@@ -17,7 +18,6 @@ schematics = "^2.1.1"
jinja2 = "^3.0.2"
aiofiles = "^0.7.0"
starlette = "^0.16.0"
-celery = "^5.1.2"
[tool.poetry.dev-dependencies]
From 823ce995b4de46ec16665f06a5c6b213ab98f08b Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Sat, 16 Oct 2021 12:58:18 +0530
Subject: [PATCH 136/150] simplify docker config
---
reddit-clone/Dockerfile | 4 ----
reddit-clone/docker-compose.yml | 39 +++++++++++++++++++--------------
2 files changed, 23 insertions(+), 20 deletions(-)
diff --git a/reddit-clone/Dockerfile b/reddit-clone/Dockerfile
index d6ab08d3..da91f476 100644
--- a/reddit-clone/Dockerfile
+++ b/reddit-clone/Dockerfile
@@ -12,7 +12,3 @@ RUN poetry install --no-dev
# copy project files
COPY ./ ./
-
-EXPOSE 8000
-
-CMD poetry run uvicorn reddit:app --host=0.0.0.0
diff --git a/reddit-clone/docker-compose.yml b/reddit-clone/docker-compose.yml
index 9f3fae20..e82e56a4 100644
--- a/reddit-clone/docker-compose.yml
+++ b/reddit-clone/docker-compose.yml
@@ -2,12 +2,12 @@ version: "3.9"
services:
postgres:
image: postgres:14-alpine
- container_name: reddit-clone-postgres
+ container_name: reddit-postgres
restart: unless-stopped
environment:
- POSTGRES_DB: reddit_clone
- POSTGRES_PASSWORD: reddit_clone
- POSTGRES_USER: reddit_clone
+ POSTGRES_DB: reddit
+ POSTGRES_PASSWORD: reddit
+ POSTGRES_USER: reddit
ports:
- "5432:5432"
volumes:
@@ -15,17 +15,22 @@ services:
redis:
image: redis:6.2-alpine
- container_name: reddit-clone-redis
+ container_name: reddit-redis
restart: unless-stopped
+ environment:
+ REDIS_PASSWORD: reddit
ports:
- "6379:6379"
volumes:
- redis-data:/data
rabbitmq:
- image: rabbitmq:3.8-alpine
- container_name: reddit-clone-rabbitmq
+ image: rabbitmq:3.9-alpine
+ container_name: reddit-rabbitmq
restart: unless-stopped
+ environment:
+ RABBITMQ_DEFAULT_USER: reddit
+ RABBITMQ_DEFAULT_PASS: reddit
ports:
- "5672:5672"
volumes:
@@ -34,12 +39,14 @@ services:
server:
build:
context: "."
- container_name: reddit-clone-server
+ container_name: reddit-server
+ restart: unless-stopped
+ command: poetry run uvicorn reddit:app --host=0.0.0.0
environment:
DEBUG: "false"
- CELERY_BROKER: amqp://guest:guest@localhost:5672//
- CELERY_BACKEND: redis://localhost:6379
- DATABASE_URL: postgresql+asyncpg://reddit_clone:reddit_clone@db:5432/reddit_clone
+ CELERY_BROKER: amqp://reddit:reddit@rabbitmq:5672/
+ CELERY_BACKEND: redis://:reddit@localhost:6379/
+ DATABASE_URL: postgresql+asyncpg://reddit:reddit@db:5432/reddit
MAIL_HOST: 127.0.0.1
MAIL_PORT: 25
MAIL_USERNAME:
@@ -53,23 +60,23 @@ services:
celery:
build:
context: "."
- container_name: reddit-clone-celery
+ container_name: reddit-celery
restart: on-failure
command: poetry run celery -A reddit.tasks worker
environment:
DEBUG: "false"
- CELERY_BROKER: amqp://guest:guest@localhost:5672//
- CELERY_BACKEND: redis://localhost:6379
- DATABASE_URL: postgresql+asyncpg://reddit_clone:reddit_clone@db:5432/reddit_clone
+ CELERY_BROKER: amqp://reddit:reddit@rabbitmq:5672/
+ CELERY_BACKEND: redis://:reddit@localhost:6379/
+ DATABASE_URL: postgresql+asyncpg://reddit:reddit@db:5432/reddit
MAIL_HOST: 127.0.0.1
MAIL_PORT: 25
MAIL_USERNAME:
MAIL_PASSWORD:
MAIL_SENDER:
depends_on:
- - server
- rabbitmq
- redis
+ - server
volumes:
redis-data:
From 675650af9e1650e0fec4fbea661698bca09304a2 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Sat, 16 Oct 2021 13:24:06 +0530
Subject: [PATCH 137/150] reduce duplication
---
reddit-clone/docker-compose.yml | 21 ++++++---------------
1 file changed, 6 insertions(+), 15 deletions(-)
diff --git a/reddit-clone/docker-compose.yml b/reddit-clone/docker-compose.yml
index e82e56a4..e2988d8d 100644
--- a/reddit-clone/docker-compose.yml
+++ b/reddit-clone/docker-compose.yml
@@ -37,12 +37,12 @@ services:
- rabbitmq-data:/data
server:
- build:
- context: "."
+ build: "."
+ image: reddit-server
container_name: reddit-server
restart: unless-stopped
command: poetry run uvicorn reddit:app --host=0.0.0.0
- environment:
+ environment: &env
DEBUG: "false"
CELERY_BROKER: amqp://reddit:reddit@rabbitmq:5672/
CELERY_BACKEND: redis://:reddit@localhost:6379/
@@ -58,21 +58,12 @@ services:
- postgres
celery:
- build:
- context: "."
+ build: "."
+ image: reddit-server
container_name: reddit-celery
restart: on-failure
command: poetry run celery -A reddit.tasks worker
- environment:
- DEBUG: "false"
- CELERY_BROKER: amqp://reddit:reddit@rabbitmq:5672/
- CELERY_BACKEND: redis://:reddit@localhost:6379/
- DATABASE_URL: postgresql+asyncpg://reddit:reddit@db:5432/reddit
- MAIL_HOST: 127.0.0.1
- MAIL_PORT: 25
- MAIL_USERNAME:
- MAIL_PASSWORD:
- MAIL_SENDER:
+ environment: *env
depends_on:
- rabbitmq
- redis
From 1e901e02b73c8bcde5e6fc8064d558ae43ed8c1f Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Sat, 16 Oct 2021 13:25:38 +0530
Subject: [PATCH 138/150] update docker-compose.yml
---
reddit-clone/docker-compose.yml | 1 -
1 file changed, 1 deletion(-)
diff --git a/reddit-clone/docker-compose.yml b/reddit-clone/docker-compose.yml
index e2988d8d..6f47f692 100644
--- a/reddit-clone/docker-compose.yml
+++ b/reddit-clone/docker-compose.yml
@@ -58,7 +58,6 @@ services:
- postgres
celery:
- build: "."
image: reddit-server
container_name: reddit-celery
restart: on-failure
From dfb2f864a032805a4a7fc8a8736d148f94f04870 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Sat, 16 Oct 2021 14:26:15 +0530
Subject: [PATCH 139/150] update database url
---
reddit-clone/docker-compose.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/reddit-clone/docker-compose.yml b/reddit-clone/docker-compose.yml
index 6f47f692..84f9db3e 100644
--- a/reddit-clone/docker-compose.yml
+++ b/reddit-clone/docker-compose.yml
@@ -46,7 +46,7 @@ services:
DEBUG: "false"
CELERY_BROKER: amqp://reddit:reddit@rabbitmq:5672/
CELERY_BACKEND: redis://:reddit@localhost:6379/
- DATABASE_URL: postgresql+asyncpg://reddit:reddit@db:5432/reddit
+ DATABASE_URL: postgresql+asyncpg://reddit:reddit@localhost:5432/reddit/
MAIL_HOST: 127.0.0.1
MAIL_PORT: 25
MAIL_USERNAME:
From 56ab62cdfb986584bf56c267dd85dbfa0f0b8be6 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Sat, 16 Oct 2021 17:43:02 +0530
Subject: [PATCH 140/150] update docker-compose.yml
---
reddit-clone/docker-compose.yml | 45 ++++++++++++++++++++++-----------
reddit-clone/reddit/settings.py | 3 ++-
2 files changed, 32 insertions(+), 16 deletions(-)
diff --git a/reddit-clone/docker-compose.yml b/reddit-clone/docker-compose.yml
index 84f9db3e..a72e7569 100644
--- a/reddit-clone/docker-compose.yml
+++ b/reddit-clone/docker-compose.yml
@@ -1,9 +1,20 @@
version: "3.9"
+x-environment: &base-environment
+ DEBUG: "false"
+ CELERY_BROKER: amqp://reddit:reddit@rabbitmq:5672/
+ CELERY_BACKEND: redis://:reddit@localhost:6379/
+ DATABASE_URL: postgresql+asyncpg://reddit:reddit@localhost:5432/reddit/
+ MAIL_HOST: 127.0.0.1
+ MAIL_PORT: 25
+ MAIL_USERNAME:
+ MAIL_PASSWORD:
+ MAIL_SENDER:
+
services:
postgres:
image: postgres:14-alpine
container_name: reddit-postgres
- restart: unless-stopped
+ restart: always
environment:
POSTGRES_DB: reddit
POSTGRES_PASSWORD: reddit
@@ -16,13 +27,18 @@ services:
redis:
image: redis:6.2-alpine
container_name: reddit-redis
- restart: unless-stopped
+ restart: always
environment:
REDIS_PASSWORD: reddit
ports:
- "6379:6379"
volumes:
- redis-data:/data
+ healthcheck:
+ test: redis-cli ping
+ interval: 15s
+ retries: 5
+ timeout: 5s
rabbitmq:
image: rabbitmq:3.9-alpine
@@ -35,34 +51,33 @@ services:
- "5672:5672"
volumes:
- rabbitmq-data:/data
+ healthcheck:
+ test: rabbitmq-diagnostics -q ping
+ interval: 15s
+ retries: 5
+ timeout: 5s
server:
build: "."
image: reddit-server
container_name: reddit-server
- restart: unless-stopped
+ restart: always
command: poetry run uvicorn reddit:app --host=0.0.0.0
- environment: &env
- DEBUG: "false"
- CELERY_BROKER: amqp://reddit:reddit@rabbitmq:5672/
- CELERY_BACKEND: redis://:reddit@localhost:6379/
- DATABASE_URL: postgresql+asyncpg://reddit:reddit@localhost:5432/reddit/
- MAIL_HOST: 127.0.0.1
- MAIL_PORT: 25
- MAIL_USERNAME:
- MAIL_PASSWORD:
- MAIL_SENDER:
+ environment: *base-environment
ports:
- "8000:8000"
+ volumes:
+ - .:/server
depends_on:
- postgres
celery:
+ # TODO: run celery with different user
image: reddit-server
container_name: reddit-celery
- restart: on-failure
+ restart: always
command: poetry run celery -A reddit.tasks worker
- environment: *env
+ environment: *base-environment
depends_on:
- rabbitmq
- redis
diff --git a/reddit-clone/reddit/settings.py b/reddit-clone/reddit/settings.py
index ddffe396..8a23f94a 100644
--- a/reddit-clone/reddit/settings.py
+++ b/reddit-clone/reddit/settings.py
@@ -1,6 +1,7 @@
from typing import Optional
from starlette.config import Config
+from starlette.datastructures import Secret
config = Config(env_file=".env")
@@ -21,7 +22,7 @@
MAIL_USERNAME: Optional[str] = config("MAIL_USERNAME", cast=str, default=None)
# mail client auth password.
-MAIL_PASSWORD: Optional[str] = config("MAIL_PASSWORD", cast=str, default=None)
+MAIL_PASSWORD: Optional[Secret] = config("MAIL_PASSWORD", cast=Secret, default=None)
# mail client sender address.
MAIL_SENDER: Optional[str] = config("MAIL_SENDER", cast=str, default=None)
From b98389e5da2ae8ee2a1b389210b3fb420ad5d451 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Sat, 16 Oct 2021 18:46:28 +0530
Subject: [PATCH 141/150] update docker config
---
reddit-clone/Dockerfile | 7 ++++---
reddit-clone/docker-compose.yml | 17 ++++++++++++++++-
2 files changed, 20 insertions(+), 4 deletions(-)
diff --git a/reddit-clone/Dockerfile b/reddit-clone/Dockerfile
index da91f476..e4eec895 100644
--- a/reddit-clone/Dockerfile
+++ b/reddit-clone/Dockerfile
@@ -1,14 +1,15 @@
FROM python:3.9-slim
LABEL maintainer="Aryan Iyappan "
-WORKDIR /app
+ARG APP_HOME=/app/
+WORKDIR ${APP_HOME}
# install poetry
RUN pip install poetry
# install dependencies
-COPY ./poetry.lock ./pyproject.toml ./
+COPY ./poetry.lock ./pyproject.toml ${APP_HOME}
RUN poetry install --no-dev
# copy project files
-COPY ./ ./
+COPY ./ ${APP_HOME}
diff --git a/reddit-clone/docker-compose.yml b/reddit-clone/docker-compose.yml
index a72e7569..0129f22a 100644
--- a/reddit-clone/docker-compose.yml
+++ b/reddit-clone/docker-compose.yml
@@ -19,6 +19,8 @@ services:
POSTGRES_DB: reddit
POSTGRES_PASSWORD: reddit
POSTGRES_USER: reddit
+ networks:
+ - reddit-main
ports:
- "5432:5432"
volumes:
@@ -30,6 +32,8 @@ services:
restart: always
environment:
REDIS_PASSWORD: reddit
+ networks:
+ - reddit-main
ports:
- "6379:6379"
volumes:
@@ -47,6 +51,8 @@ services:
environment:
RABBITMQ_DEFAULT_USER: reddit
RABBITMQ_DEFAULT_PASS: reddit
+ networks:
+ - reddit-main
ports:
- "5672:5672"
volumes:
@@ -64,6 +70,8 @@ services:
restart: always
command: poetry run uvicorn reddit:app --host=0.0.0.0
environment: *base-environment
+ networks:
+ - reddit-main
ports:
- "8000:8000"
volumes:
@@ -72,12 +80,15 @@ services:
- postgres
celery:
- # TODO: run celery with different user
image: reddit-server
container_name: reddit-celery
restart: always
command: poetry run celery -A reddit.tasks worker
environment: *base-environment
+ networks:
+ - reddit-main
+ volumes:
+ - .:/server
depends_on:
- rabbitmq
- redis
@@ -90,3 +101,7 @@ volumes:
driver: local
postgres-data:
driver: local
+
+networks:
+ reddit-main:
+ driver: bridge
From b51cd543b82e24f310001311a6e717b41082e8fe Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Sat, 16 Oct 2021 19:51:09 +0530
Subject: [PATCH 142/150] prepare to add nginx
---
reddit-clone/docker-compose.yml | 78 +++++++++++++++++++--------------
reddit-clone/nginx/nginx.conf | 6 +++
2 files changed, 52 insertions(+), 32 deletions(-)
create mode 100644 reddit-clone/nginx/nginx.conf
diff --git a/reddit-clone/docker-compose.yml b/reddit-clone/docker-compose.yml
index 0129f22a..fef929e2 100644
--- a/reddit-clone/docker-compose.yml
+++ b/reddit-clone/docker-compose.yml
@@ -11,6 +11,49 @@ x-environment: &base-environment
MAIL_SENDER:
services:
+ nginx:
+ image: nginx:1.21-alpine
+ container_name: reddit-nginx
+ restart: always
+ networks:
+ - reddit-proxy
+ ports:
+ - "8000:8000"
+ volumes:
+ - ./nginx/nginx.conf:/tmp/nginx.conf
+ depends_on:
+ - app
+
+ app:
+ build: "."
+ image: reddit-app
+ container_name: reddit-app
+ restart: always
+ command: poetry run uvicorn reddit:app --host=0.0.0.0 --port=5000
+ environment: *base-environment
+ networks:
+ - reddit-main
+ - reddit-proxy
+ volumes:
+ - .:/app
+ depends_on:
+ - postgres
+
+ celery:
+ image: reddit-app
+ container_name: reddit-celery
+ restart: always
+ command: poetry run celery -A reddit.tasks worker
+ environment: *base-environment
+ networks:
+ - reddit-main
+ volumes:
+ - .:/app
+ depends_on:
+ - rabbitmq
+ - redis
+ - app
+
postgres:
image: postgres:14-alpine
container_name: reddit-postgres
@@ -47,7 +90,7 @@ services:
rabbitmq:
image: rabbitmq:3.9-alpine
container_name: reddit-rabbitmq
- restart: unless-stopped
+ restart: always
environment:
RABBITMQ_DEFAULT_USER: reddit
RABBITMQ_DEFAULT_PASS: reddit
@@ -63,37 +106,6 @@ services:
retries: 5
timeout: 5s
- server:
- build: "."
- image: reddit-server
- container_name: reddit-server
- restart: always
- command: poetry run uvicorn reddit:app --host=0.0.0.0
- environment: *base-environment
- networks:
- - reddit-main
- ports:
- - "8000:8000"
- volumes:
- - .:/server
- depends_on:
- - postgres
-
- celery:
- image: reddit-server
- container_name: reddit-celery
- restart: always
- command: poetry run celery -A reddit.tasks worker
- environment: *base-environment
- networks:
- - reddit-main
- volumes:
- - .:/server
- depends_on:
- - rabbitmq
- - redis
- - server
-
volumes:
redis-data:
driver: local
@@ -105,3 +117,5 @@ volumes:
networks:
reddit-main:
driver: bridge
+ reddit-proxy:
+ driver: bridge
diff --git a/reddit-clone/nginx/nginx.conf b/reddit-clone/nginx/nginx.conf
new file mode 100644
index 00000000..f2561ac0
--- /dev/null
+++ b/reddit-clone/nginx/nginx.conf
@@ -0,0 +1,6 @@
+server {
+ listen 80;
+ location / {
+ proxy_pass http://5000;
+ }
+}
From 904b07cd287f3abdb9e83226ae1a53ab0c4d50dc Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Sun, 17 Oct 2021 07:37:23 +0530
Subject: [PATCH 143/150] get nginx proxy working
---
reddit-clone/README.md | 2 +-
reddit-clone/docker-compose.yml | 12 ++++++------
reddit-clone/nginx/nginx.conf | 9 +++++----
3 files changed, 12 insertions(+), 11 deletions(-)
diff --git a/reddit-clone/README.md b/reddit-clone/README.md
index a0e07896..0ab48163 100644
--- a/reddit-clone/README.md
+++ b/reddit-clone/README.md
@@ -33,4 +33,4 @@ You can use [Docker Compose](https://github.com/docker/compose) to run this exam
docker compose up
```
-You can now explore the GraphQL API here: http://localhost:8000/graphql
+You can now explore the GraphQL API here: http://localhost/graphql
diff --git a/reddit-clone/docker-compose.yml b/reddit-clone/docker-compose.yml
index fef929e2..cf776202 100644
--- a/reddit-clone/docker-compose.yml
+++ b/reddit-clone/docker-compose.yml
@@ -18,9 +18,9 @@ services:
networks:
- reddit-proxy
ports:
- - "8000:8000"
+ - 80:80
volumes:
- - ./nginx/nginx.conf:/tmp/nginx.conf
+ - ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf
depends_on:
- app
@@ -29,7 +29,7 @@ services:
image: reddit-app
container_name: reddit-app
restart: always
- command: poetry run uvicorn reddit:app --host=0.0.0.0 --port=5000
+ command: poetry run uvicorn reddit:app --host=0.0.0.0 --port=8080
environment: *base-environment
networks:
- reddit-main
@@ -65,7 +65,7 @@ services:
networks:
- reddit-main
ports:
- - "5432:5432"
+ - 5432:5432
volumes:
- postgres-data:/var/lib/postgresql/data
@@ -78,7 +78,7 @@ services:
networks:
- reddit-main
ports:
- - "6379:6379"
+ - 6379:6379
volumes:
- redis-data:/data
healthcheck:
@@ -97,7 +97,7 @@ services:
networks:
- reddit-main
ports:
- - "5672:5672"
+ - 5672:5672
volumes:
- rabbitmq-data:/data
healthcheck:
diff --git a/reddit-clone/nginx/nginx.conf b/reddit-clone/nginx/nginx.conf
index f2561ac0..6c3ca497 100644
--- a/reddit-clone/nginx/nginx.conf
+++ b/reddit-clone/nginx/nginx.conf
@@ -1,6 +1,7 @@
server {
- listen 80;
- location / {
- proxy_pass http://5000;
- }
+ listen 80;
+
+ location / {
+ proxy_pass http://app:8080;
+ }
}
From 632f5a7846aec71e71d3979ed6b21fd88897c0aa Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Sun, 17 Oct 2021 07:59:56 +0530
Subject: [PATCH 144/150] add more fields
---
reddit-clone/reddit/comments/types.py | 24 +++++++++++++++++++++++-
reddit-clone/reddit/posts/types.py | 18 +++++++++++++++++-
reddit-clone/reddit/subreddits/types.py | 11 ++++++++++-
3 files changed, 50 insertions(+), 3 deletions(-)
diff --git a/reddit-clone/reddit/comments/types.py b/reddit-clone/reddit/comments/types.py
index f2050dd0..b0ef9ef0 100644
--- a/reddit-clone/reddit/comments/types.py
+++ b/reddit-clone/reddit/comments/types.py
@@ -1,12 +1,16 @@
from __future__ import annotations
-from typing import List, Optional, cast
+from typing import TYPE_CHECKING, List, Optional, cast
import strawberry
from strawberry.types import Info
from reddit.base.types import NodeType
+if TYPE_CHECKING:
+ from reddit.users.types import UserType
+ from reddit.posts.types import PostType
+
@strawberry.type(name="Comment")
class CommentType(NodeType):
@@ -28,12 +32,30 @@ class CommentType(NodeType):
"""
)
+ post_id: int = strawberry.field(
+ description="""
+ The post ID of the comment.
+ """
+ )
+
replies: List[CommentType] = strawberry.field(
description="""
The replies for the comment.
"""
)
+ @strawberry.field(description="The owner of the comment.")
+ async def owner(self, info: Info) -> UserType:
+ loader = info.context.get("user_loader")
+ user = await loader.load(self.owner_id)
+ return cast(UserType, user)
+
+ @strawberry.field(description="The post of the comment.")
+ async def post(self, info: Info) -> PostType:
+ loader = info.context.get("post_loader")
+ post = await loader.load(self.post_id)
+ return cast(PostType, post)
+
@classmethod
async def resolve_node(cls, info: Info, comment_id: str) -> Optional[CommentType]:
"""
diff --git a/reddit-clone/reddit/posts/types.py b/reddit-clone/reddit/posts/types.py
index 83ca086e..4811c2e7 100644
--- a/reddit-clone/reddit/posts/types.py
+++ b/reddit-clone/reddit/posts/types.py
@@ -1,6 +1,6 @@
from __future__ import annotations
-from typing import List, Optional, cast
+from typing import TYPE_CHECKING, List, Optional, cast
import strawberry
from strawberry.types import Info
@@ -8,6 +8,10 @@
from reddit.base.types import NodeType
from reddit.comments.types import CommentType
+if TYPE_CHECKING:
+ from reddit.subreddits.types import SubredditType
+ from reddit.users.types import UserType
+
@strawberry.type(name="Post")
class PostType(NodeType):
@@ -59,6 +63,18 @@ class PostType(NodeType):
"""
)
+ @strawberry.field(description="The owner of the post.")
+ async def owner(self, info: Info) -> UserType:
+ loader = info.context.get("user_loader")
+ user = await loader.load(self.owner_id)
+ return cast(UserType, user)
+
+ @strawberry.field(description="The Subreddit of the post.")
+ async def subreddit(self, info: Info) -> SubredditType:
+ loader = info.context.get("subreddit_loader")
+ subreddit = await loader.load(self.subreddit_id)
+ return cast(SubredditType, subreddit)
+
@classmethod
async def resolve_node(cls, info: Info, post_id: str) -> Optional[PostType]:
"""
diff --git a/reddit-clone/reddit/subreddits/types.py b/reddit-clone/reddit/subreddits/types.py
index aaf45159..4234a72d 100644
--- a/reddit-clone/reddit/subreddits/types.py
+++ b/reddit-clone/reddit/subreddits/types.py
@@ -1,6 +1,6 @@
from __future__ import annotations
-from typing import List, Optional, cast
+from typing import TYPE_CHECKING, List, Optional, cast
import strawberry
from strawberry.types import Info
@@ -8,6 +8,9 @@
from reddit.base.types import NodeType
from reddit.posts.types import PostType
+if TYPE_CHECKING:
+ from reddit.users.types import UserType
+
@strawberry.type(name="Subreddit")
class SubredditType(NodeType):
@@ -48,6 +51,12 @@ class SubredditType(NodeType):
"""
)
+ @strawberry.field(description="The owner of the Subreddit.")
+ async def owner(self, info: Info) -> UserType:
+ loader = info.context.get("user_loader")
+ user = await loader.load(self.owner_id)
+ return cast(UserType, user)
+
@classmethod
async def resolve_node(
cls, info: Info, subreddit_id: str
From a4b971e19ebff68df6928e86b13cb02c7a4ea689 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Sun, 17 Oct 2021 08:17:44 +0530
Subject: [PATCH 145/150] fix circular import issues
---
reddit-clone/reddit/comments/types.py | 9 +++++++--
reddit-clone/reddit/posts/types.py | 9 +++++++--
reddit-clone/reddit/subreddits/types.py | 5 ++++-
3 files changed, 18 insertions(+), 5 deletions(-)
diff --git a/reddit-clone/reddit/comments/types.py b/reddit-clone/reddit/comments/types.py
index b0ef9ef0..51616264 100644
--- a/reddit-clone/reddit/comments/types.py
+++ b/reddit-clone/reddit/comments/types.py
@@ -4,6 +4,7 @@
import strawberry
from strawberry.types import Info
+from strawberry.lazy_type import LazyType
from reddit.base.types import NodeType
@@ -45,13 +46,17 @@ class CommentType(NodeType):
)
@strawberry.field(description="The owner of the comment.")
- async def owner(self, info: Info) -> UserType:
+ async def owner(
+ self, info: Info
+ ) -> LazyType["UserType", "reddit.users.types"]: # noqa: F821
loader = info.context.get("user_loader")
user = await loader.load(self.owner_id)
return cast(UserType, user)
@strawberry.field(description="The post of the comment.")
- async def post(self, info: Info) -> PostType:
+ async def post(
+ self, info: Info
+ ) -> LazyType["PostType", "reddit.posts.types"]: # noqa: F821
loader = info.context.get("post_loader")
post = await loader.load(self.post_id)
return cast(PostType, post)
diff --git a/reddit-clone/reddit/posts/types.py b/reddit-clone/reddit/posts/types.py
index 4811c2e7..b1d97fa8 100644
--- a/reddit-clone/reddit/posts/types.py
+++ b/reddit-clone/reddit/posts/types.py
@@ -4,6 +4,7 @@
import strawberry
from strawberry.types import Info
+from strawberry.lazy_type import LazyType
from reddit.base.types import NodeType
from reddit.comments.types import CommentType
@@ -64,13 +65,17 @@ class PostType(NodeType):
)
@strawberry.field(description="The owner of the post.")
- async def owner(self, info: Info) -> UserType:
+ async def owner(
+ self, info: Info
+ ) -> LazyType["UserType", "reddit.users.types"]: # noqa: F821
loader = info.context.get("user_loader")
user = await loader.load(self.owner_id)
return cast(UserType, user)
@strawberry.field(description="The Subreddit of the post.")
- async def subreddit(self, info: Info) -> SubredditType:
+ async def subreddit(
+ self, info: Info
+ ) -> LazyType["SubredditType", "reddit.subreddits.types"]: # noqa: F821
loader = info.context.get("subreddit_loader")
subreddit = await loader.load(self.subreddit_id)
return cast(SubredditType, subreddit)
diff --git a/reddit-clone/reddit/subreddits/types.py b/reddit-clone/reddit/subreddits/types.py
index 4234a72d..dd2b4999 100644
--- a/reddit-clone/reddit/subreddits/types.py
+++ b/reddit-clone/reddit/subreddits/types.py
@@ -4,6 +4,7 @@
import strawberry
from strawberry.types import Info
+from strawberry.lazy_type import LazyType
from reddit.base.types import NodeType
from reddit.posts.types import PostType
@@ -52,7 +53,9 @@ class SubredditType(NodeType):
)
@strawberry.field(description="The owner of the Subreddit.")
- async def owner(self, info: Info) -> UserType:
+ async def owner(
+ self, info: Info
+ ) -> LazyType["UserType", "reddit.users.types"]: # noqa: F821
loader = info.context.get("user_loader")
user = await loader.load(self.owner_id)
return cast(UserType, user)
From 57fae72a84662af711d319a3de8bd7add023e664 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Sun, 17 Oct 2021 09:26:10 +0530
Subject: [PATCH 146/150] use helper: merge_types
---
reddit-clone/poetry.lock | 76 +++++++++++++++++++++++++++++++----
reddit-clone/pyproject.toml | 22 +++++-----
reddit-clone/reddit/schema.py | 18 ++++-----
3 files changed, 88 insertions(+), 28 deletions(-)
diff --git a/reddit-clone/poetry.lock b/reddit-clone/poetry.lock
index d5f7a2c7..2a5283e2 100644
--- a/reddit-clone/poetry.lock
+++ b/reddit-clone/poetry.lock
@@ -36,7 +36,7 @@ vine = "5.0.0"
[[package]]
name = "anyio"
-version = "3.3.3"
+version = "3.3.4"
description = "High level compatibility layer for multiple asynchronous event loop implementations"
category = "main"
optional = false
@@ -274,6 +274,24 @@ category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+[[package]]
+name = "fastapi"
+version = "0.70.0"
+description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production"
+category = "main"
+optional = false
+python-versions = ">=3.6.1"
+
+[package.dependencies]
+pydantic = ">=1.6.2,<1.7 || >1.7,<1.7.1 || >1.7.1,<1.7.2 || >1.7.2,<1.7.3 || >1.7.3,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0"
+starlette = "0.16.0"
+
+[package.extras]
+all = ["requests (>=2.24.0,<3.0.0)", "jinja2 (>=2.11.2,<4.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "itsdangerous (>=1.1.0,<3.0.0)", "pyyaml (>=5.3.1,<6.0.0)", "ujson (>=4.0.1,<5.0.0)", "orjson (>=3.2.1,<4.0.0)", "email_validator (>=1.1.1,<2.0.0)", "uvicorn[standard] (>=0.12.0,<0.16.0)"]
+dev = ["python-jose[cryptography] (>=3.3.0,<4.0.0)", "passlib[bcrypt] (>=1.7.2,<2.0.0)", "autoflake (>=1.4.0,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)", "uvicorn[standard] (>=0.12.0,<0.16.0)"]
+doc = ["mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=7.1.9,<8.0.0)", "mdx-include (>=1.4.1,<2.0.0)", "mkdocs-markdownextradata-plugin (>=0.1.7,<0.3.0)", "typer-cli (>=0.0.12,<0.0.13)", "pyyaml (>=5.3.1,<6.0.0)"]
+test = ["pytest (>=6.2.4,<7.0.0)", "pytest-cov (>=2.12.0,<4.0.0)", "mypy (==0.910)", "flake8 (>=3.8.3,<4.0.0)", "black (==21.9b0)", "isort (>=5.0.6,<6.0.0)", "requests (>=2.24.0,<3.0.0)", "httpx (>=0.14.0,<0.19.0)", "email_validator (>=1.1.1,<2.0.0)", "sqlalchemy (>=1.3.18,<1.5.0)", "peewee (>=3.13.3,<4.0.0)", "databases[sqlite] (>=0.3.2,<0.6.0)", "orjson (>=3.2.1,<4.0.0)", "ujson (>=4.0.1,<5.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "flask (>=1.1.2,<3.0.0)", "anyio[trio] (>=3.2.1,<4.0.0)", "types-ujson (==0.1.1)", "types-orjson (==3.6.0)", "types-dataclasses (==0.1.7)"]
+
[[package]]
name = "flake8"
version = "3.9.2"
@@ -583,6 +601,21 @@ category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+[[package]]
+name = "pydantic"
+version = "1.8.2"
+description = "Data validation and settings management using python 3.6 type hinting"
+category = "main"
+optional = false
+python-versions = ">=3.6.1"
+
+[package.dependencies]
+typing-extensions = ">=3.7.4.3"
+
+[package.extras]
+dotenv = ["python-dotenv (>=0.10.4)"]
+email = ["email-validator (>=1.0.3)"]
+
[[package]]
name = "pyflakes"
version = "2.3.1"
@@ -765,7 +798,7 @@ full = ["itsdangerous", "jinja2", "python-multipart", "pyyaml", "requests", "gra
[[package]]
name = "strawberry-graphql"
-version = "0.82.2"
+version = "0.84.0"
description = "A library for creating GraphQL APIs"
category = "main"
optional = false
@@ -774,6 +807,7 @@ python-versions = ">=3.7,<4.0"
[package.dependencies]
cached-property = ">=1.5.2,<2.0.0"
click = ">=7.0,<9.0"
+fastapi = ">=0.65.2"
graphql-core = ">=3.1.0,<3.2.0"
pygments = ">=2.3,<3.0"
python-dateutil = ">=2.7.0,<3.0.0"
@@ -908,7 +942,7 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytes
[metadata]
lock-version = "1.1"
python-versions = "^3.7"
-content-hash = "700aac6bc63fb865ad58061cecb0089dd106cc48be7646f6590aefbf748adb96"
+content-hash = "32406d1d907beea69d86c7c1fdb7e883d1092c4ab9af451c55412a051e1223d3"
[metadata.files]
aiofiles = [
@@ -924,8 +958,8 @@ amqp = [
{file = "amqp-5.0.6.tar.gz", hash = "sha256:03e16e94f2b34c31f8bf1206d8ddd3ccaa4c315f7f6a1879b7b1210d229568c2"},
]
anyio = [
- {file = "anyio-3.3.3-py3-none-any.whl", hash = "sha256:56ceaeed2877723578b1341f4f68c29081db189cfb40a97d1922b9513f6d7db6"},
- {file = "anyio-3.3.3.tar.gz", hash = "sha256:8eccec339cb4a856c94a75d50fc1d451faf32a05ef406be462e2efc59c9838b0"},
+ {file = "anyio-3.3.4-py3-none-any.whl", hash = "sha256:4fd09a25ab7fa01d34512b7249e366cd10358cdafc95022c7ff8c8f8a5026d66"},
+ {file = "anyio-3.3.4.tar.gz", hash = "sha256:67da67b5b21f96b9d3d65daa6ea99f5d5282cb09f50eb4456f8fb51dffefc3ff"},
]
argon2-cffi = [
{file = "argon2-cffi-21.1.0.tar.gz", hash = "sha256:f710b61103d1a1f692ca3ecbd1373e28aa5e545ac625ba067ff2feca1b2bb870"},
@@ -1051,6 +1085,10 @@ colorama = [
{file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"},
{file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"},
]
+fastapi = [
+ {file = "fastapi-0.70.0-py3-none-any.whl", hash = "sha256:a36d5f2fad931aa3575c07a3472c784e81f3e664e3bb5c8b9c88d0ec1104f59c"},
+ {file = "fastapi-0.70.0.tar.gz", hash = "sha256:66da43cfe5185ea1df99552acffd201f1832c6b364e0f4136c0a99f933466ced"},
+]
flake8 = [
{file = "flake8-3.9.2-py2.py3-none-any.whl", hash = "sha256:bf8fd333346d844f616e8d47905ef3a3384edae6b4e9beb0c5101e25e3110907"},
{file = "flake8-3.9.2.tar.gz", hash = "sha256:07528381786f2a6237b061f6e96610a4167b226cb926e2aa2b6b1d78057c576b"},
@@ -1269,6 +1307,30 @@ pycparser = [
{file = "pycparser-2.20-py2.py3-none-any.whl", hash = "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"},
{file = "pycparser-2.20.tar.gz", hash = "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0"},
]
+pydantic = [
+ {file = "pydantic-1.8.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:05ddfd37c1720c392f4e0d43c484217b7521558302e7069ce8d318438d297739"},
+ {file = "pydantic-1.8.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:a7c6002203fe2c5a1b5cbb141bb85060cbff88c2d78eccbc72d97eb7022c43e4"},
+ {file = "pydantic-1.8.2-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:589eb6cd6361e8ac341db97602eb7f354551482368a37f4fd086c0733548308e"},
+ {file = "pydantic-1.8.2-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:10e5622224245941efc193ad1d159887872776df7a8fd592ed746aa25d071840"},
+ {file = "pydantic-1.8.2-cp36-cp36m-win_amd64.whl", hash = "sha256:99a9fc39470010c45c161a1dc584997f1feb13f689ecf645f59bb4ba623e586b"},
+ {file = "pydantic-1.8.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a83db7205f60c6a86f2c44a61791d993dff4b73135df1973ecd9eed5ea0bda20"},
+ {file = "pydantic-1.8.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:41b542c0b3c42dc17da70554bc6f38cbc30d7066d2c2815a94499b5684582ecb"},
+ {file = "pydantic-1.8.2-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:ea5cb40a3b23b3265f6325727ddfc45141b08ed665458be8c6285e7b85bd73a1"},
+ {file = "pydantic-1.8.2-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:18b5ea242dd3e62dbf89b2b0ec9ba6c7b5abaf6af85b95a97b00279f65845a23"},
+ {file = "pydantic-1.8.2-cp37-cp37m-win_amd64.whl", hash = "sha256:234a6c19f1c14e25e362cb05c68afb7f183eb931dd3cd4605eafff055ebbf287"},
+ {file = "pydantic-1.8.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:021ea0e4133e8c824775a0cfe098677acf6fa5a3cbf9206a376eed3fc09302cd"},
+ {file = "pydantic-1.8.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:e710876437bc07bd414ff453ac8ec63d219e7690128d925c6e82889d674bb505"},
+ {file = "pydantic-1.8.2-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:ac8eed4ca3bd3aadc58a13c2aa93cd8a884bcf21cb019f8cfecaae3b6ce3746e"},
+ {file = "pydantic-1.8.2-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:4a03cbbe743e9c7247ceae6f0d8898f7a64bb65800a45cbdc52d65e370570820"},
+ {file = "pydantic-1.8.2-cp38-cp38-win_amd64.whl", hash = "sha256:8621559dcf5afacf0069ed194278f35c255dc1a1385c28b32dd6c110fd6531b3"},
+ {file = "pydantic-1.8.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8b223557f9510cf0bfd8b01316bf6dd281cf41826607eada99662f5e4963f316"},
+ {file = "pydantic-1.8.2-cp39-cp39-manylinux1_i686.whl", hash = "sha256:244ad78eeb388a43b0c927e74d3af78008e944074b7d0f4f696ddd5b2af43c62"},
+ {file = "pydantic-1.8.2-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:05ef5246a7ffd2ce12a619cbb29f3307b7c4509307b1b49f456657b43529dc6f"},
+ {file = "pydantic-1.8.2-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:54cd5121383f4a461ff7644c7ca20c0419d58052db70d8791eacbbe31528916b"},
+ {file = "pydantic-1.8.2-cp39-cp39-win_amd64.whl", hash = "sha256:4be75bebf676a5f0f87937c6ddb061fa39cbea067240d98e298508c1bda6f3f3"},
+ {file = "pydantic-1.8.2-py3-none-any.whl", hash = "sha256:fec866a0b59f372b7e776f2d7308511784dace622e0992a0b59ea3ccee0ae833"},
+ {file = "pydantic-1.8.2.tar.gz", hash = "sha256:26464e57ccaafe72b7ad156fdaa4e9b9ef051f69e175dbbb463283000c05ab7b"},
+]
pyflakes = [
{file = "pyflakes-2.3.1-py2.py3-none-any.whl", hash = "sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3"},
{file = "pyflakes-2.3.1.tar.gz", hash = "sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db"},
@@ -1431,8 +1493,8 @@ starlette = [
{file = "starlette-0.16.0.tar.gz", hash = "sha256:e1904b5d0007aee24bdd3c43994be9b3b729f4f58e740200de1d623f8c3a8870"},
]
strawberry-graphql = [
- {file = "strawberry-graphql-0.82.2.tar.gz", hash = "sha256:36ed3844d1a7fbaebf22698a72eaeaad2468e5c0ce4463ef678582ed70ca5883"},
- {file = "strawberry_graphql-0.82.2-py3-none-any.whl", hash = "sha256:000f6e74c026e8defab37b6967a1e7491aa646251ddef265369e6ad510088077"},
+ {file = "strawberry-graphql-0.84.0.tar.gz", hash = "sha256:c122d657540314a4a5d5366954ed687d4f12a96907395241c3285f597785a934"},
+ {file = "strawberry_graphql-0.84.0-py3-none-any.whl", hash = "sha256:f1ddec0ebe75a639bdc16bf4815ce0aaa75c703fe0a0873a6b153adb0800be9b"},
]
toml = [
{file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
diff --git a/reddit-clone/pyproject.toml b/reddit-clone/pyproject.toml
index 0b6e8593..6b25cb7f 100644
--- a/reddit-clone/pyproject.toml
+++ b/reddit-clone/pyproject.toml
@@ -6,18 +6,18 @@ authors = ["Aryan Iyappan "]
[tool.poetry.dependencies]
python = "^3.7"
-uvicorn = {extras = ["standard"], version = "^0.15.0"}
-SQLAlchemy = {extras = ["mypy"], version = "^1.4.23"}
-strawberry-graphql = {extras = ["asgi"], version = "^0.82.2"}
-passlib = {extras = ["argon2"], version = "^1.7.4"}
-celery = {extras = ["redis", "librabbitmq"], version = "^5.1.2"}
-alembic = "^1.7.1"
+uvicorn = {extras = ["standard"], version = "^0.15"}
+SQLAlchemy = {extras = ["mypy"], version = "^1.4"}
+strawberry-graphql = {extras = ["asgi"], version = "^0.84"}
+passlib = {extras = ["argon2"], version = "^1.7"}
+celery = {extras = ["redis", "librabbitmq"], version = "^5.1"}
+alembic = "^1.7"
asyncpg = "^0.24"
-graphql-relay = "^3.1.0"
-schematics = "^2.1.1"
-jinja2 = "^3.0.2"
-aiofiles = "^0.7.0"
-starlette = "^0.16.0"
+graphql-relay = "^3.1"
+schematics = "^2.1"
+jinja2 = "^3.0"
+aiofiles = "^0.7"
+starlette = "^0.16"
[tool.poetry.dev-dependencies]
diff --git a/reddit-clone/reddit/schema.py b/reddit-clone/reddit/schema.py
index 1298ec23..ca459432 100644
--- a/reddit-clone/reddit/schema.py
+++ b/reddit-clone/reddit/schema.py
@@ -1,4 +1,5 @@
-import strawberry
+from strawberry import Schema
+from strawberry.tools import merge_types
from reddit.base.queries import BaseQuery
from reddit.subreddits.queries import SubredditQuery
@@ -10,15 +11,12 @@
__all__ = ("schema",)
+Query = merge_types(name="Query", types=(BaseQuery, UserQuery, SubredditQuery))
-@strawberry.type
-class Query(BaseQuery, UserQuery, SubredditQuery):
- pass
+Mutation = merge_types(
+ name="Mutation",
+ types=(CommentMutation, PostMutation, UserMutation, SubredditMutation),
+)
-@strawberry.type
-class Mutation(CommentMutation, PostMutation, UserMutation, SubredditMutation):
- pass
-
-
-schema = strawberry.Schema(query=Query, mutation=Mutation)
+schema = Schema(query=Query, mutation=Mutation)
From db85096dbfbcd69e800d50fe67384c405d2a80cd Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Sun, 17 Oct 2021 09:27:04 +0530
Subject: [PATCH 147/150] use helper: merge_types
---
reddit-clone/reddit/schema.py | 13 ++++++-------
1 file changed, 6 insertions(+), 7 deletions(-)
diff --git a/reddit-clone/reddit/schema.py b/reddit-clone/reddit/schema.py
index ca459432..d6185f2d 100644
--- a/reddit-clone/reddit/schema.py
+++ b/reddit-clone/reddit/schema.py
@@ -11,12 +11,11 @@
__all__ = ("schema",)
-Query = merge_types(name="Query", types=(BaseQuery, UserQuery, SubredditQuery))
-Mutation = merge_types(
- name="Mutation",
- types=(CommentMutation, PostMutation, UserMutation, SubredditMutation),
+schema = Schema(
+ query=merge_types(name="Query", types=(BaseQuery, UserQuery, SubredditQuery)),
+ mutation=merge_types(
+ name="Mutation",
+ types=(CommentMutation, PostMutation, UserMutation, SubredditMutation),
+ ),
)
-
-
-schema = Schema(query=Query, mutation=Mutation)
From c6fb4b8bd819cec8229a5d916e1d7789fc3cb638 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Mon, 18 Oct 2021 11:23:02 +0530
Subject: [PATCH 148/150] fix schema fields
---
reddit-clone/reddit/__init__.py | 17 +++++-----
reddit-clone/reddit/comments/loaders.py | 2 +-
.../comments/mutations/comment_create.py | 13 ++------
.../comments/mutations/comment_delete.py | 13 ++------
.../comments/mutations/comment_update.py | 13 ++------
.../reddit/comments/mutations/comment_vote.py | 13 ++------
reddit-clone/reddit/comments/types.py | 6 ++--
reddit-clone/reddit/posts/loaders.py | 2 +-
.../reddit/posts/mutations/post_create.py | 11 ++-----
.../reddit/posts/mutations/post_delete.py | 11 ++-----
.../reddit/posts/mutations/post_update.py | 11 ++-----
.../reddit/posts/mutations/post_vote.py | 11 ++-----
reddit-clone/reddit/posts/types.py | 6 ++--
reddit-clone/reddit/subreddits/loaders.py | 2 +-
.../subreddits/mutations/subreddit_create.py | 11 ++-----
.../subreddits/mutations/subreddit_delete.py | 11 ++-----
.../subreddits/mutations/subreddit_join.py | 15 +++------
.../subreddits/mutations/subreddit_leave.py | 15 ++++-----
.../subreddits/mutations/subreddit_update.py | 11 ++-----
reddit-clone/reddit/subreddits/queries.py | 10 ++----
reddit-clone/reddit/subreddits/types.py | 4 +--
reddit-clone/reddit/users/loaders.py | 13 +++++++-
.../reddit/users/mutations/authenticate.py | 13 ++------
.../reddit/users/mutations/avatar_remove.py | 11 ++-----
.../reddit/users/mutations/email_change.py | 13 +++-----
.../users/mutations/email_change_request.py | 15 ++++-----
.../reddit/users/mutations/password_reset.py | 13 +++-----
.../users/mutations/password_reset_request.py | 15 ++++-----
.../reddit/users/mutations/user_create.py | 11 ++-----
.../reddit/users/mutations/user_deactivate.py | 11 ++-----
.../reddit/users/mutations/user_update.py | 11 ++-----
reddit-clone/reddit/users/queries.py | 31 +++++--------------
reddit-clone/reddit/users/types.py | 2 +-
33 files changed, 105 insertions(+), 262 deletions(-)
diff --git a/reddit-clone/reddit/__init__.py b/reddit-clone/reddit/__init__.py
index de20772a..21bd1d35 100644
--- a/reddit-clone/reddit/__init__.py
+++ b/reddit-clone/reddit/__init__.py
@@ -9,10 +9,10 @@
from reddit import settings
from reddit.schema import schema
-from reddit.users.loaders import load_users
-from reddit.subreddits.loaders import load_subreddits
-from reddit.posts.loaders import load_posts
-from reddit.comments.loaders import load_comments
+from reddit.users.loaders import load_users_by_id, load_users_by_username
+from reddit.subreddits.loaders import load_subreddits_by_id
+from reddit.posts.loaders import load_posts_by_id
+from reddit.comments.loaders import load_comments_by_id
__all__ = ("app",)
@@ -23,10 +23,11 @@ async def get_context(
) -> Optional[Any]:
context = await super().get_context(request, response=response)
context.update(
- user_loader=DataLoader(load_fn=load_users),
- subreddit_loader=DataLoader(load_fn=load_subreddits),
- post_loader=DataLoader(load_fn=load_posts),
- comment_loader=DataLoader(load_fn=load_comments),
+ user_id_loader=DataLoader(load_fn=load_users_by_id),
+ user_username_loader=DataLoader(load_fn=load_users_by_username),
+ subreddit_id_loader=DataLoader(load_fn=load_subreddits_by_id),
+ post_id_loader=DataLoader(load_fn=load_posts_by_id),
+ comment_id_loader=DataLoader(load_fn=load_comments_by_id),
)
return context
diff --git a/reddit-clone/reddit/comments/loaders.py b/reddit-clone/reddit/comments/loaders.py
index 50b28da0..b69be7c3 100644
--- a/reddit-clone/reddit/comments/loaders.py
+++ b/reddit-clone/reddit/comments/loaders.py
@@ -7,7 +7,7 @@
from reddit.comments.models import Comment
-async def load_comments(comment_ids: List[int]) -> List[Optional[Comment]]:
+async def load_comments_by_id(comment_ids: List[int]) -> List[Optional[Comment]]:
"""
Batch-loads comments by their IDs.
"""
diff --git a/reddit-clone/reddit/comments/mutations/comment_create.py b/reddit-clone/reddit/comments/mutations/comment_create.py
index 81143ae4..3419e0cc 100644
--- a/reddit-clone/reddit/comments/mutations/comment_create.py
+++ b/reddit-clone/reddit/comments/mutations/comment_create.py
@@ -29,15 +29,6 @@ class CommentCreateError:
CommentCreateResult = Union[CommentCreateSuccess, CommentCreateError]
-async def resolve_comment_create(
- info: Info, input: CommentCreateInput
-) -> CommentCreateResult:
+@strawberry.field(description="Creates a new comment on a post.")
+async def comment_create(info: Info, input: CommentCreateInput) -> CommentCreateResult:
pass
-
-
-comment_create = strawberry.mutation(
- resolver=resolve_comment_create,
- description="""
- Creates a new comment on a post.
- """,
-)
diff --git a/reddit-clone/reddit/comments/mutations/comment_delete.py b/reddit-clone/reddit/comments/mutations/comment_delete.py
index 4b903360..ba3c1451 100644
--- a/reddit-clone/reddit/comments/mutations/comment_delete.py
+++ b/reddit-clone/reddit/comments/mutations/comment_delete.py
@@ -29,15 +29,6 @@ class CommentDeleteError:
CommentDeleteResult = Union[CommentDeleteSuccess, CommentDeleteError]
-async def resolve_comment_delete(
- info: Info, input: CommentDeleteInput
-) -> CommentDeleteResult:
+@strawberry.field(description="Deletes a comment on a post.")
+async def comment_delete(info: Info, input: CommentDeleteInput) -> CommentDeleteResult:
pass
-
-
-comment_delete = strawberry.mutation(
- resolver=resolve_comment_delete,
- description="""
- Deletes a comment on a post.
- """,
-)
diff --git a/reddit-clone/reddit/comments/mutations/comment_update.py b/reddit-clone/reddit/comments/mutations/comment_update.py
index 2d5e738a..6a80c2c3 100644
--- a/reddit-clone/reddit/comments/mutations/comment_update.py
+++ b/reddit-clone/reddit/comments/mutations/comment_update.py
@@ -30,15 +30,6 @@ class CommentUpdateError:
CommentUpdateResult = Union[CommentUpdateSuccess, CommentUpdateError]
-async def resolve_comment_update(
- info: Info, input: CommentUpdateInput
-) -> CommentUpdateResult:
+@strawberry.field(description="Updates a comment on a post.")
+async def comment_update(info: Info, input: CommentUpdateInput) -> CommentUpdateResult:
pass
-
-
-comment_update = strawberry.mutation(
- resolver=resolve_comment_update,
- description="""
- Updates a comment on a post.
- """,
-)
diff --git a/reddit-clone/reddit/comments/mutations/comment_vote.py b/reddit-clone/reddit/comments/mutations/comment_vote.py
index 5912656b..6ff8213e 100644
--- a/reddit-clone/reddit/comments/mutations/comment_vote.py
+++ b/reddit-clone/reddit/comments/mutations/comment_vote.py
@@ -29,15 +29,6 @@ class CommentVoteError:
CommentVoteResult = Union[CommentVoteSuccess, CommentVoteError]
-async def resolve_comment_vote(
- info: Info, input: CommentVoteInput
-) -> CommentVoteResult:
+@strawberry.field(description="Creates a vote on a comment.")
+async def comment_vote(info: Info, input: CommentVoteInput) -> CommentVoteResult:
pass
-
-
-comment_vote = strawberry.mutation(
- resolver=resolve_comment_vote,
- description="""
- Creates a vote on a comment.
- """,
-)
diff --git a/reddit-clone/reddit/comments/types.py b/reddit-clone/reddit/comments/types.py
index 51616264..5947161f 100644
--- a/reddit-clone/reddit/comments/types.py
+++ b/reddit-clone/reddit/comments/types.py
@@ -49,7 +49,7 @@ class CommentType(NodeType):
async def owner(
self, info: Info
) -> LazyType["UserType", "reddit.users.types"]: # noqa: F821
- loader = info.context.get("user_loader")
+ loader = info.context.get("user_id_loader")
user = await loader.load(self.owner_id)
return cast(UserType, user)
@@ -57,7 +57,7 @@ async def owner(
async def post(
self, info: Info
) -> LazyType["PostType", "reddit.posts.types"]: # noqa: F821
- loader = info.context.get("post_loader")
+ loader = info.context.get("post_id_loader")
post = await loader.load(self.post_id)
return cast(PostType, post)
@@ -66,6 +66,6 @@ async def resolve_node(cls, info: Info, comment_id: str) -> Optional[CommentType
"""
Gets a comment with the given ID.
"""
- loader = info.context.get("comment_loader")
+ loader = info.context.get("comment_id_loader")
comment = await loader.load(comment_id)
return cast(CommentType, comment)
diff --git a/reddit-clone/reddit/posts/loaders.py b/reddit-clone/reddit/posts/loaders.py
index bf8cb95e..66dade6d 100644
--- a/reddit-clone/reddit/posts/loaders.py
+++ b/reddit-clone/reddit/posts/loaders.py
@@ -7,7 +7,7 @@
from reddit.posts.models import Post
-async def load_posts(post_ids: List[int]) -> List[Optional[Post]]:
+async def load_posts_by_id(post_ids: List[int]) -> List[Optional[Post]]:
"""
Batch-loads posts by their IDs.
"""
diff --git a/reddit-clone/reddit/posts/mutations/post_create.py b/reddit-clone/reddit/posts/mutations/post_create.py
index 2bec4f6b..5fcdace5 100644
--- a/reddit-clone/reddit/posts/mutations/post_create.py
+++ b/reddit-clone/reddit/posts/mutations/post_create.py
@@ -29,13 +29,6 @@ class PostCreateError:
PostCreateResult = Union[PostCreateSuccess, PostCreateError]
-async def resolve_post_create(info: Info, input: PostCreateInput) -> PostCreateResult:
+@strawberry.mutation(description="Creates a new post in a subreddit.")
+async def post_create(info: Info, input: PostCreateInput) -> PostCreateResult:
pass
-
-
-post_create = strawberry.mutation(
- resolver=resolve_post_create,
- description="""
- Creates a new post in a Subreddit.
- """,
-)
diff --git a/reddit-clone/reddit/posts/mutations/post_delete.py b/reddit-clone/reddit/posts/mutations/post_delete.py
index 369967c2..31c578f9 100644
--- a/reddit-clone/reddit/posts/mutations/post_delete.py
+++ b/reddit-clone/reddit/posts/mutations/post_delete.py
@@ -28,13 +28,6 @@ class PostDeleteError:
PostDeleteResult = Union[PostDeleteSuccess, PostDeleteError]
-async def resolve_post_delete(info: Info, input: PostDeleteInput) -> PostDeleteResult:
+@strawberry.mutation(description="Deletes a post in a subreddit.")
+async def post_delete(info: Info, input: PostDeleteInput) -> PostDeleteResult:
pass
-
-
-post_delete = strawberry.mutation(
- resolver=resolve_post_delete,
- description="""
- Deletes a post in a Subreddit.
- """,
-)
diff --git a/reddit-clone/reddit/posts/mutations/post_update.py b/reddit-clone/reddit/posts/mutations/post_update.py
index 3e318437..c0d0df86 100644
--- a/reddit-clone/reddit/posts/mutations/post_update.py
+++ b/reddit-clone/reddit/posts/mutations/post_update.py
@@ -30,13 +30,6 @@ class PostUpdateError:
PostUpdateResult = Union[PostUpdateSuccess, PostUpdateError]
-async def resolve_post_update(info: Info, input: PostUpdateInput) -> PostUpdateResult:
+@strawberry.mutation(description="Updates a post in a subreddit.")
+async def post_update(info: Info, input: PostUpdateInput) -> PostUpdateResult:
pass
-
-
-post_update = strawberry.mutation(
- resolver=resolve_post_update,
- description="""
- Updates a post in a Subreddit.
- """,
-)
diff --git a/reddit-clone/reddit/posts/mutations/post_vote.py b/reddit-clone/reddit/posts/mutations/post_vote.py
index 4d82fcea..4e434a33 100644
--- a/reddit-clone/reddit/posts/mutations/post_vote.py
+++ b/reddit-clone/reddit/posts/mutations/post_vote.py
@@ -28,13 +28,6 @@ class PostVoteError:
PostVoteResult = Union[PostVoteSuccess, PostVoteError]
-async def resolve_post_vote(info: Info, input: PostVoteInput) -> PostVoteResult:
+@strawberry.mutation(description="Creates a vote on a post.")
+async def post_vote(info: Info, input: PostVoteInput) -> PostVoteResult:
pass
-
-
-post_vote = strawberry.mutation(
- resolver=resolve_post_vote,
- description="""
- Creates a vote on a post.
- """,
-)
diff --git a/reddit-clone/reddit/posts/types.py b/reddit-clone/reddit/posts/types.py
index b1d97fa8..14a7665d 100644
--- a/reddit-clone/reddit/posts/types.py
+++ b/reddit-clone/reddit/posts/types.py
@@ -68,7 +68,7 @@ class PostType(NodeType):
async def owner(
self, info: Info
) -> LazyType["UserType", "reddit.users.types"]: # noqa: F821
- loader = info.context.get("user_loader")
+ loader = info.context.get("user_id_loader")
user = await loader.load(self.owner_id)
return cast(UserType, user)
@@ -76,7 +76,7 @@ async def owner(
async def subreddit(
self, info: Info
) -> LazyType["SubredditType", "reddit.subreddits.types"]: # noqa: F821
- loader = info.context.get("subreddit_loader")
+ loader = info.context.get("subreddit_id_loader")
subreddit = await loader.load(self.subreddit_id)
return cast(SubredditType, subreddit)
@@ -85,6 +85,6 @@ async def resolve_node(cls, info: Info, post_id: str) -> Optional[PostType]:
"""
Gets a post with the given ID.
"""
- loader = info.context.get("post_loader")
+ loader = info.context.get("post_id_loader")
post = await loader.load(post_id)
return cast(PostType, post)
diff --git a/reddit-clone/reddit/subreddits/loaders.py b/reddit-clone/reddit/subreddits/loaders.py
index 39d96a3a..c1978b33 100644
--- a/reddit-clone/reddit/subreddits/loaders.py
+++ b/reddit-clone/reddit/subreddits/loaders.py
@@ -7,7 +7,7 @@
from reddit.subreddits.models import Subreddit
-async def load_subreddits(subreddit_ids: List[int]) -> List[Optional[Subreddit]]:
+async def load_subreddits_by_id(subreddit_ids: List[int]) -> List[Optional[Subreddit]]:
"""
Batch-loads subreddits by their IDs.
"""
diff --git a/reddit-clone/reddit/subreddits/mutations/subreddit_create.py b/reddit-clone/reddit/subreddits/mutations/subreddit_create.py
index 0625b8a9..8fd4e0f2 100644
--- a/reddit-clone/reddit/subreddits/mutations/subreddit_create.py
+++ b/reddit-clone/reddit/subreddits/mutations/subreddit_create.py
@@ -28,15 +28,8 @@ class SubredditCreateError:
SubredditCreateResult = Union[SubredditCreateSuccess, SubredditCreateError]
-async def resolve_subreddit_create(
+@strawberry.mutation(description="Creates a new subreddit.")
+async def subreddit_create(
info: Info, input: SubredditCreateInput
) -> SubredditCreateResult:
pass
-
-
-subreddit_create = strawberry.mutation(
- resolver=resolve_subreddit_create,
- description="""
- Creates a new Subreddit.
- """,
-)
diff --git a/reddit-clone/reddit/subreddits/mutations/subreddit_delete.py b/reddit-clone/reddit/subreddits/mutations/subreddit_delete.py
index 4ce08dc9..39bd8250 100644
--- a/reddit-clone/reddit/subreddits/mutations/subreddit_delete.py
+++ b/reddit-clone/reddit/subreddits/mutations/subreddit_delete.py
@@ -27,15 +27,8 @@ class SubredditDeleteError:
SubredditDeleteResult = Union[SubredditDeleteSuccess, SubredditDeleteError]
-async def resolve_subreddit_delete(
+@strawberry.mutation(description="Deletes a subreddit.")
+async def subreddit_delete(
info: Info, input: SubredditDeleteInput
) -> SubredditDeleteResult:
pass
-
-
-subreddit_delete = strawberry.mutation(
- resolver=resolve_subreddit_delete,
- description="""
- Deletes a Subreddit.
- """,
-)
diff --git a/reddit-clone/reddit/subreddits/mutations/subreddit_join.py b/reddit-clone/reddit/subreddits/mutations/subreddit_join.py
index a81c9b57..1bbe9939 100644
--- a/reddit-clone/reddit/subreddits/mutations/subreddit_join.py
+++ b/reddit-clone/reddit/subreddits/mutations/subreddit_join.py
@@ -27,15 +27,10 @@ class SubredditJoinError:
SubredditJoinResult = Union[SubredditJoinSuccess, SubredditJoinError]
-async def resolve_subreddit_join(
- info: Info, input: SubredditJoinInput
-) -> SubredditJoinResult:
- pass
-
-
-subreddit_join = strawberry.mutation(
- resolver=resolve_subreddit_join,
+@strawberry.mutation(
description="""
- Creates a new Subreddit-User relationship.
- """,
+ Creates a new subreddit-user relationship.
+ """
)
+async def subreddit_join(info: Info, input: SubredditJoinInput) -> SubredditJoinResult:
+ pass
diff --git a/reddit-clone/reddit/subreddits/mutations/subreddit_leave.py b/reddit-clone/reddit/subreddits/mutations/subreddit_leave.py
index 582be748..7ca43834 100644
--- a/reddit-clone/reddit/subreddits/mutations/subreddit_leave.py
+++ b/reddit-clone/reddit/subreddits/mutations/subreddit_leave.py
@@ -27,15 +27,12 @@ class SubredditLeaveError:
SubredditLeaveResult = Union[SubredditLeaveSuccess, SubredditLeaveError]
-async def resolve_subreddit_leave(
+@strawberry.field(
+ description="""
+ Deletes a subreddit-user relationship.
+ """
+)
+async def subreddit_leave(
info: Info, input: SubredditLeaveInput
) -> SubredditLeaveResult:
pass
-
-
-subreddit_leave = strawberry.mutation(
- resolver=resolve_subreddit_leave,
- description="""
- Deletes a Subreddit-User relationship.
- """,
-)
diff --git a/reddit-clone/reddit/subreddits/mutations/subreddit_update.py b/reddit-clone/reddit/subreddits/mutations/subreddit_update.py
index 0c97b382..ed4d3633 100644
--- a/reddit-clone/reddit/subreddits/mutations/subreddit_update.py
+++ b/reddit-clone/reddit/subreddits/mutations/subreddit_update.py
@@ -29,15 +29,8 @@ class SubredditUpdateError:
SubredditUpdateResult = Union[SubredditUpdateSuccess, SubredditUpdateError]
-async def resolve_subreddit_update(
+@strawberry.mutation(description="Updates a subreddit.")
+async def subreddit_update(
info: Info, input: SubredditUpdateInput
) -> SubredditUpdateResult:
pass
-
-
-subreddit_update = strawberry.mutation(
- resolver=resolve_subreddit_update,
- description="""
- Updates a Subreddit.
- """,
-)
diff --git a/reddit-clone/reddit/subreddits/queries.py b/reddit-clone/reddit/subreddits/queries.py
index 77998552..6b1ad2b4 100644
--- a/reddit-clone/reddit/subreddits/queries.py
+++ b/reddit-clone/reddit/subreddits/queries.py
@@ -7,15 +7,9 @@
from reddit.subreddits.types import SubredditType
-async def resolve_subreddits(info: Info) -> List[SubredditType]:
+@strawberry.field(description="Gets the available subreddits.")
+async def subreddits(info: Info) -> List[SubredditType]:
pass
-subreddits = strawberry.field(
- resolver=resolve_subreddits,
- description="""
- Gets the available subreddits.
- """,
-)
-
SubredditQuery = create_type(name="SubredditQuery", fields=(subreddits,))
diff --git a/reddit-clone/reddit/subreddits/types.py b/reddit-clone/reddit/subreddits/types.py
index dd2b4999..873fa26f 100644
--- a/reddit-clone/reddit/subreddits/types.py
+++ b/reddit-clone/reddit/subreddits/types.py
@@ -56,7 +56,7 @@ class SubredditType(NodeType):
async def owner(
self, info: Info
) -> LazyType["UserType", "reddit.users.types"]: # noqa: F821
- loader = info.context.get("user_loader")
+ loader = info.context.get("user_id_loader")
user = await loader.load(self.owner_id)
return cast(UserType, user)
@@ -67,6 +67,6 @@ async def resolve_node(
"""
Gets a Subreddit with the given ID.
"""
- loader = info.context.get("subreddit_loader")
+ loader = info.context.get("subreddit_id_loader")
subreddit = await loader.load(subreddit_id)
return cast(SubredditType, subreddit)
diff --git a/reddit-clone/reddit/users/loaders.py b/reddit-clone/reddit/users/loaders.py
index 36617a1c..b9e801bf 100644
--- a/reddit-clone/reddit/users/loaders.py
+++ b/reddit-clone/reddit/users/loaders.py
@@ -7,7 +7,7 @@
from reddit.users.models import User
-async def load_users(user_ids: List[int]) -> List[Optional[User]]:
+async def load_users_by_id(user_ids: List[int]) -> List[Optional[User]]:
"""
Batch-loads users by their IDs.
"""
@@ -16,3 +16,14 @@ async def load_users(user_ids: List[int]) -> List[Optional[User]]:
result: Result = await session.execute(query)
user_map: Dict[int, User] = {user.id: user for user in result.scalars()}
return [user_map.get(user_id) for user_id in user_ids]
+
+
+async def load_users_by_username(usernames: List[str]) -> List[Optional[User]]:
+ """
+ Batch-loads users by their usernames.
+ """
+ query = select(User).filter(User.username.in_(usernames))
+ async with get_session() as session:
+ result: Result = await session.execute(query)
+ user_map: Dict[str, User] = {user.username: user for user in result.scalars()}
+ return [user_map.get(username) for username in usernames]
diff --git a/reddit-clone/reddit/users/mutations/authenticate.py b/reddit-clone/reddit/users/mutations/authenticate.py
index 8983d5b8..4d9a34d5 100644
--- a/reddit-clone/reddit/users/mutations/authenticate.py
+++ b/reddit-clone/reddit/users/mutations/authenticate.py
@@ -28,15 +28,6 @@ class AuthenticateError:
AuthenticateResult = Union[AuthenticateSuccess, AuthenticateError]
-async def resolve_authenticate(
- info: Info, input: AuthenticateInput
-) -> AuthenticateResult:
+@strawberry.mutation(description="Logs the current user in.")
+async def authenticate(info: Info, input: AuthenticateInput) -> AuthenticateResult:
pass
-
-
-authenticate = strawberry.mutation(
- resolver=resolve_authenticate,
- description="""
- Logs the current user in.
- """,
-)
diff --git a/reddit-clone/reddit/users/mutations/avatar_remove.py b/reddit-clone/reddit/users/mutations/avatar_remove.py
index 9505484f..5d4f64f7 100644
--- a/reddit-clone/reddit/users/mutations/avatar_remove.py
+++ b/reddit-clone/reddit/users/mutations/avatar_remove.py
@@ -21,13 +21,6 @@ class AvatarRemoveError:
AvatarRemoveResult = Union[AvatarRemoveSuccess, AvatarRemoveError]
-async def resolve_avatar_remove(info: Info) -> AvatarRemoveResult:
+@strawberry.mutation(description="Removes the current user's avatar.")
+async def avatar_remove(info: Info) -> AvatarRemoveResult:
pass
-
-
-avatar_remove = strawberry.mutation(
- resolver=resolve_avatar_remove,
- description="""
- Removes the current user's avatar.
- """,
-)
diff --git a/reddit-clone/reddit/users/mutations/email_change.py b/reddit-clone/reddit/users/mutations/email_change.py
index 5abd905d..4648e5d4 100644
--- a/reddit-clone/reddit/users/mutations/email_change.py
+++ b/reddit-clone/reddit/users/mutations/email_change.py
@@ -28,16 +28,11 @@ class EmailChangeError:
EmailChangeResult = Union[EmailChangeSuccess, EmailChangeError]
-async def resolve_email_change(
- info: Info, input: EmailChangeInput
-) -> EmailChangeResult:
- pass
-
-
-email_change = strawberry.mutation(
- resolver=resolve_email_change,
+@strawberry.mutation(
description="""
Changes the email for the user account
associated with the given email.
- """,
+ """
)
+async def email_change(info: Info, input: EmailChangeInput) -> EmailChangeResult:
+ pass
diff --git a/reddit-clone/reddit/users/mutations/email_change_request.py b/reddit-clone/reddit/users/mutations/email_change_request.py
index b16f3a60..e530da4c 100644
--- a/reddit-clone/reddit/users/mutations/email_change_request.py
+++ b/reddit-clone/reddit/users/mutations/email_change_request.py
@@ -28,16 +28,13 @@ class EmailChangeRequestError:
EmailChangeRequestResult = Union[EmailChangeRequestSuccess, EmailChangeRequestError]
-async def resolve_email_change_request(
- info: Info, input: EmailChangeRequestInput
-) -> EmailChangeRequestResult:
- pass
-
-
-email_change_request = strawberry.mutation(
- resolver=resolve_email_change_request,
+@strawberry.mutation(
description="""
Sends an email change code to
the given email address.
- """,
+ """
)
+async def email_change_request(
+ info: Info, input: EmailChangeRequestInput
+) -> EmailChangeRequestResult:
+ pass
diff --git a/reddit-clone/reddit/users/mutations/password_reset.py b/reddit-clone/reddit/users/mutations/password_reset.py
index 16e25ebf..7b2f68c0 100644
--- a/reddit-clone/reddit/users/mutations/password_reset.py
+++ b/reddit-clone/reddit/users/mutations/password_reset.py
@@ -28,16 +28,11 @@ class PasswordResetError:
PasswordResetResult = Union[PasswordResetSuccess, PasswordResetError]
-async def resolve_password_reset(
- info: Info, input: PasswordResetInput
-) -> PasswordResetResult:
- pass
-
-
-password_reset = strawberry.mutation(
- resolver=resolve_password_reset,
+@strawberry.mutation(
description="""
Resets the password for the user account
associated with the given email.
- """,
+ """
)
+async def password_reset(info: Info, input: PasswordResetInput) -> PasswordResetResult:
+ pass
diff --git a/reddit-clone/reddit/users/mutations/password_reset_request.py b/reddit-clone/reddit/users/mutations/password_reset_request.py
index 6e2104ef..d23af8f9 100644
--- a/reddit-clone/reddit/users/mutations/password_reset_request.py
+++ b/reddit-clone/reddit/users/mutations/password_reset_request.py
@@ -28,16 +28,13 @@ class PasswordResetRequestError:
]
-async def resolve_password_reset_request(
- info: Info, input: PasswordResetRequestInput
-) -> PasswordResetRequestResult:
- pass
-
-
-password_reset_request = strawberry.mutation(
- resolver=resolve_password_reset_request,
+@strawberry.mutation(
description="""
Sends a password reset code to the
provided email, if it actually exists.
- """,
+ """
)
+async def password_reset_request(
+ info: Info, input: PasswordResetRequestInput
+) -> PasswordResetRequestResult:
+ pass
diff --git a/reddit-clone/reddit/users/mutations/user_create.py b/reddit-clone/reddit/users/mutations/user_create.py
index 30ca3003..d40164c8 100644
--- a/reddit-clone/reddit/users/mutations/user_create.py
+++ b/reddit-clone/reddit/users/mutations/user_create.py
@@ -28,13 +28,6 @@ class UserCreateError:
UserCreateResult = Union[UserCreateSuccess, UserCreateError]
-async def resolve_user_create(info: Info, input: UserCreateInput) -> UserCreateResult:
+@strawberry.mutation(description="Creates a new user.")
+async def user_create(info: Info, input: UserCreateInput) -> UserCreateResult:
pass
-
-
-user_create = strawberry.mutation(
- resolver=resolve_user_create,
- description="""
- Creates a new user.
- """,
-)
diff --git a/reddit-clone/reddit/users/mutations/user_deactivate.py b/reddit-clone/reddit/users/mutations/user_deactivate.py
index 8d339687..d2825ab7 100644
--- a/reddit-clone/reddit/users/mutations/user_deactivate.py
+++ b/reddit-clone/reddit/users/mutations/user_deactivate.py
@@ -26,15 +26,8 @@ class UserDeactivateError:
UserDeactivateResult = Union[UserDeactivateSuccess, UserDeactivateError]
-async def resolve_user_deactivate(
+@strawberry.mutation(description="Deactivates the current user.")
+async def user_deactivate(
info: Info, input: UserDeactivateInput
) -> UserDeactivateResult:
pass
-
-
-user_deactivate = strawberry.mutation(
- resolver=resolve_user_deactivate,
- description="""
- Deactivates the current user.
- """,
-)
diff --git a/reddit-clone/reddit/users/mutations/user_update.py b/reddit-clone/reddit/users/mutations/user_update.py
index 3740cebb..8f2fea98 100644
--- a/reddit-clone/reddit/users/mutations/user_update.py
+++ b/reddit-clone/reddit/users/mutations/user_update.py
@@ -28,13 +28,6 @@ class UserUpdateError:
UserUpdateResult = Union[UserUpdateSuccess, UserUpdateError]
-async def resolve_user_update(info: Info, input: UserUpdateInput) -> UserUpdateResult:
+@strawberry.mutation(description="Updates the current user.")
+async def user_update(info: Info, input: UserUpdateInput) -> UserUpdateResult:
pass
-
-
-user_update = strawberry.mutation(
- resolver=resolve_user_update,
- description="""
- Updates the current user.
- """,
-)
diff --git a/reddit-clone/reddit/users/queries.py b/reddit-clone/reddit/users/queries.py
index a027af9f..129e5978 100644
--- a/reddit-clone/reddit/users/queries.py
+++ b/reddit-clone/reddit/users/queries.py
@@ -4,36 +4,19 @@
from strawberry.tools import create_type
from strawberry.types import Info
-from reddit.database import get_session
from reddit.users.types import UserType
-from reddit.users.services import user_by_username
-async def resolve_user(info: Info, username: str) -> Optional[UserType]:
- async with get_session() as session:
- user = await user_by_username(session=session, username=username)
- if user is not None:
- return cast(UserType, user)
- return user
+@strawberry.field(description="Gets an user by username.")
+async def user(info: Info, username: str) -> Optional[UserType]:
+ loader = info.context.get("user_username_loader")
+ user = await loader.load(username)
+ return cast(UserType, user)
-user = strawberry.field(
- resolver=resolve_user,
- description="""
- Gets an user by username.
- """,
-)
-
-
-async def resolve_current_user(info: Info) -> Optional[UserType]:
+@strawberry.field(description="Gets the current user.")
+async def current_user(info: Info) -> Optional[UserType]:
pass
-current_user = strawberry.field(
- resolver=resolve_current_user,
- description="""
- Gets the current user.
- """,
-)
-
UserQuery = create_type(name="UserQuery", fields=(user, current_user))
diff --git a/reddit-clone/reddit/users/types.py b/reddit-clone/reddit/users/types.py
index 726b3aff..ba68aa05 100644
--- a/reddit-clone/reddit/users/types.py
+++ b/reddit-clone/reddit/users/types.py
@@ -48,6 +48,6 @@ async def resolve_node(cls, info: Info, user_id: str) -> Optional[UserType]:
"""
Gets an user with the given ID.
"""
- loader = info.context.get("user_loader")
+ loader = info.context.get("user_id_loader")
user = await loader.load(user_id)
return cast(UserType, user)
From 311b93d3e6a4070fa71e855baf4b061b3d0710f4 Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Tue, 19 Oct 2021 20:35:39 +0530
Subject: [PATCH 149/150] use marshmallow for serialization
---
reddit-clone/README.md | 2 +-
reddit-clone/docker-compose.yml | 2 +-
reddit-clone/poetry.lock | 101 ++++--------------
reddit-clone/pyproject.toml | 2 +-
reddit-clone/reddit/base/types.py | 1 +
.../reddit/users/mutations/user_create.py | 14 ++-
reddit-clone/reddit/users/serializers.py | 37 +++++++
7 files changed, 77 insertions(+), 82 deletions(-)
create mode 100644 reddit-clone/reddit/users/serializers.py
diff --git a/reddit-clone/README.md b/reddit-clone/README.md
index 0ab48163..16deb4d3 100644
--- a/reddit-clone/README.md
+++ b/reddit-clone/README.md
@@ -13,7 +13,7 @@ most of the features that Strawberry gives us.
- [SQLAlchemy](https://github.com/sqlalchemy/sqlalchemy) core/ mapper (asyncio)
- [Alembic](https://github.com/sqlalchemy/alembic) migrations
- [PostgreSQL](https://github.com/postgres/postgres) database server
-- [Schematics](https://github.com/schematics/schematics) data validation
+- [Marshmallow](https://github.com/marshmallow-code/marshmallow) data validation
- [Celery](https://github.com/celery/celery) tasks ([Redis](https://github.com/redis/redis) store, [RabbitMQ](https://github.com/rabbitmq/rabbitmq-server) broker)
## Features at a glance
diff --git a/reddit-clone/docker-compose.yml b/reddit-clone/docker-compose.yml
index cf776202..409acd13 100644
--- a/reddit-clone/docker-compose.yml
+++ b/reddit-clone/docker-compose.yml
@@ -3,7 +3,7 @@ x-environment: &base-environment
DEBUG: "false"
CELERY_BROKER: amqp://reddit:reddit@rabbitmq:5672/
CELERY_BACKEND: redis://:reddit@localhost:6379/
- DATABASE_URL: postgresql+asyncpg://reddit:reddit@localhost:5432/reddit/
+ DATABASE_URL: postgresql+asyncpg://reddit:reddit@postgres:5432/reddit/
MAIL_HOST: 127.0.0.1
MAIL_PORT: 25
MAIL_USERNAME:
diff --git a/reddit-clone/poetry.lock b/reddit-clone/poetry.lock
index 2a5283e2..012f68fc 100644
--- a/reddit-clone/poetry.lock
+++ b/reddit-clone/poetry.lock
@@ -274,24 +274,6 @@ category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
-[[package]]
-name = "fastapi"
-version = "0.70.0"
-description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production"
-category = "main"
-optional = false
-python-versions = ">=3.6.1"
-
-[package.dependencies]
-pydantic = ">=1.6.2,<1.7 || >1.7,<1.7.1 || >1.7.1,<1.7.2 || >1.7.2,<1.7.3 || >1.7.3,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0"
-starlette = "0.16.0"
-
-[package.extras]
-all = ["requests (>=2.24.0,<3.0.0)", "jinja2 (>=2.11.2,<4.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "itsdangerous (>=1.1.0,<3.0.0)", "pyyaml (>=5.3.1,<6.0.0)", "ujson (>=4.0.1,<5.0.0)", "orjson (>=3.2.1,<4.0.0)", "email_validator (>=1.1.1,<2.0.0)", "uvicorn[standard] (>=0.12.0,<0.16.0)"]
-dev = ["python-jose[cryptography] (>=3.3.0,<4.0.0)", "passlib[bcrypt] (>=1.7.2,<2.0.0)", "autoflake (>=1.4.0,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)", "uvicorn[standard] (>=0.12.0,<0.16.0)"]
-doc = ["mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=7.1.9,<8.0.0)", "mdx-include (>=1.4.1,<2.0.0)", "mkdocs-markdownextradata-plugin (>=0.1.7,<0.3.0)", "typer-cli (>=0.0.12,<0.0.13)", "pyyaml (>=5.3.1,<6.0.0)"]
-test = ["pytest (>=6.2.4,<7.0.0)", "pytest-cov (>=2.12.0,<4.0.0)", "mypy (==0.910)", "flake8 (>=3.8.3,<4.0.0)", "black (==21.9b0)", "isort (>=5.0.6,<6.0.0)", "requests (>=2.24.0,<3.0.0)", "httpx (>=0.14.0,<0.19.0)", "email_validator (>=1.1.1,<2.0.0)", "sqlalchemy (>=1.3.18,<1.5.0)", "peewee (>=3.13.3,<4.0.0)", "databases[sqlite] (>=0.3.2,<0.6.0)", "orjson (>=3.2.1,<4.0.0)", "ujson (>=4.0.1,<5.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "flask (>=1.1.2,<3.0.0)", "anyio[trio] (>=3.2.1,<4.0.0)", "types-ujson (==0.1.1)", "types-orjson (==3.6.0)", "types-dataclasses (==0.1.7)"]
-
[[package]]
name = "flake8"
version = "3.9.2"
@@ -503,6 +485,20 @@ category = "main"
optional = false
python-versions = ">=3.6"
+[[package]]
+name = "marshmallow"
+version = "3.14.0"
+description = "A lightweight library for converting complex datatypes to and from native Python datatypes."
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.extras]
+dev = ["pytest", "pytz", "simplejson", "mypy (==0.910)", "flake8 (==4.0.1)", "flake8-bugbear (==21.9.2)", "pre-commit (>=2.4,<3.0)", "tox"]
+docs = ["sphinx (==4.2.0)", "sphinx-issues (==1.2.0)", "alabaster (==0.7.12)", "sphinx-version-warning (==1.1.2)", "autodocsumm (==0.2.7)"]
+lint = ["mypy (==0.910)", "flake8 (==4.0.1)", "flake8-bugbear (==21.9.2)", "pre-commit (>=2.4,<3.0)"]
+tests = ["pytest", "pytz", "simplejson"]
+
[[package]]
name = "mccabe"
version = "0.6.1"
@@ -601,21 +597,6 @@ category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
-[[package]]
-name = "pydantic"
-version = "1.8.2"
-description = "Data validation and settings management using python 3.6 type hinting"
-category = "main"
-optional = false
-python-versions = ">=3.6.1"
-
-[package.dependencies]
-typing-extensions = ">=3.7.4.3"
-
-[package.extras]
-dotenv = ["python-dotenv (>=0.10.4)"]
-email = ["email-validator (>=1.0.3)"]
-
[[package]]
name = "pyflakes"
version = "2.3.1"
@@ -700,14 +681,6 @@ category = "dev"
optional = false
python-versions = "*"
-[[package]]
-name = "schematics"
-version = "2.1.1"
-description = "Python Data Structures for Humans"
-category = "main"
-optional = false
-python-versions = "*"
-
[[package]]
name = "sentinel"
version = "0.3.0"
@@ -798,7 +771,7 @@ full = ["itsdangerous", "jinja2", "python-multipart", "pyyaml", "requests", "gra
[[package]]
name = "strawberry-graphql"
-version = "0.84.0"
+version = "0.84.2"
description = "A library for creating GraphQL APIs"
category = "main"
optional = false
@@ -807,7 +780,6 @@ python-versions = ">=3.7,<4.0"
[package.dependencies]
cached-property = ">=1.5.2,<2.0.0"
click = ">=7.0,<9.0"
-fastapi = ">=0.65.2"
graphql-core = ">=3.1.0,<3.2.0"
pygments = ">=2.3,<3.0"
python-dateutil = ">=2.7.0,<3.0.0"
@@ -825,6 +797,7 @@ opentelemetry = ["opentelemetry-api (<2)", "opentelemetry-sdk (<2)"]
pydantic = ["pydantic (<2)"]
sanic = ["sanic (>=20.12.2,<22.0.0)"]
aiohttp = ["aiohttp (>=3.7.4.post0,<4.0.0)"]
+fastapi = ["fastapi (>=0.65.2)"]
[[package]]
name = "toml"
@@ -942,7 +915,7 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytes
[metadata]
lock-version = "1.1"
python-versions = "^3.7"
-content-hash = "32406d1d907beea69d86c7c1fdb7e883d1092c4ab9af451c55412a051e1223d3"
+content-hash = "342061d49c9020a1037e202b9786df0be94a2e1febcc0010c875e99e70eecebe"
[metadata.files]
aiofiles = [
@@ -1085,10 +1058,6 @@ colorama = [
{file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"},
{file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"},
]
-fastapi = [
- {file = "fastapi-0.70.0-py3-none-any.whl", hash = "sha256:a36d5f2fad931aa3575c07a3472c784e81f3e664e3bb5c8b9c88d0ec1104f59c"},
- {file = "fastapi-0.70.0.tar.gz", hash = "sha256:66da43cfe5185ea1df99552acffd201f1832c6b364e0f4136c0a99f933466ced"},
-]
flake8 = [
{file = "flake8-3.9.2-py2.py3-none-any.whl", hash = "sha256:bf8fd333346d844f616e8d47905ef3a3384edae6b4e9beb0c5101e25e3110907"},
{file = "flake8-3.9.2.tar.gz", hash = "sha256:07528381786f2a6237b061f6e96610a4167b226cb926e2aa2b6b1d78057c576b"},
@@ -1250,6 +1219,10 @@ markupsafe = [
{file = "MarkupSafe-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8"},
{file = "MarkupSafe-2.0.1.tar.gz", hash = "sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a"},
]
+marshmallow = [
+ {file = "marshmallow-3.14.0-py3-none-any.whl", hash = "sha256:6d00e42d6d6289f8cd3e77618a01689d57a078fe324ee579b00fa206d32e9b07"},
+ {file = "marshmallow-3.14.0.tar.gz", hash = "sha256:bba1a940985c052c5cc7849f97da196ebc81f3b85ec10c56ef1f3228aa9cbe74"},
+]
mccabe = [
{file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"},
{file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"},
@@ -1307,30 +1280,6 @@ pycparser = [
{file = "pycparser-2.20-py2.py3-none-any.whl", hash = "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"},
{file = "pycparser-2.20.tar.gz", hash = "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0"},
]
-pydantic = [
- {file = "pydantic-1.8.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:05ddfd37c1720c392f4e0d43c484217b7521558302e7069ce8d318438d297739"},
- {file = "pydantic-1.8.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:a7c6002203fe2c5a1b5cbb141bb85060cbff88c2d78eccbc72d97eb7022c43e4"},
- {file = "pydantic-1.8.2-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:589eb6cd6361e8ac341db97602eb7f354551482368a37f4fd086c0733548308e"},
- {file = "pydantic-1.8.2-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:10e5622224245941efc193ad1d159887872776df7a8fd592ed746aa25d071840"},
- {file = "pydantic-1.8.2-cp36-cp36m-win_amd64.whl", hash = "sha256:99a9fc39470010c45c161a1dc584997f1feb13f689ecf645f59bb4ba623e586b"},
- {file = "pydantic-1.8.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a83db7205f60c6a86f2c44a61791d993dff4b73135df1973ecd9eed5ea0bda20"},
- {file = "pydantic-1.8.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:41b542c0b3c42dc17da70554bc6f38cbc30d7066d2c2815a94499b5684582ecb"},
- {file = "pydantic-1.8.2-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:ea5cb40a3b23b3265f6325727ddfc45141b08ed665458be8c6285e7b85bd73a1"},
- {file = "pydantic-1.8.2-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:18b5ea242dd3e62dbf89b2b0ec9ba6c7b5abaf6af85b95a97b00279f65845a23"},
- {file = "pydantic-1.8.2-cp37-cp37m-win_amd64.whl", hash = "sha256:234a6c19f1c14e25e362cb05c68afb7f183eb931dd3cd4605eafff055ebbf287"},
- {file = "pydantic-1.8.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:021ea0e4133e8c824775a0cfe098677acf6fa5a3cbf9206a376eed3fc09302cd"},
- {file = "pydantic-1.8.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:e710876437bc07bd414ff453ac8ec63d219e7690128d925c6e82889d674bb505"},
- {file = "pydantic-1.8.2-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:ac8eed4ca3bd3aadc58a13c2aa93cd8a884bcf21cb019f8cfecaae3b6ce3746e"},
- {file = "pydantic-1.8.2-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:4a03cbbe743e9c7247ceae6f0d8898f7a64bb65800a45cbdc52d65e370570820"},
- {file = "pydantic-1.8.2-cp38-cp38-win_amd64.whl", hash = "sha256:8621559dcf5afacf0069ed194278f35c255dc1a1385c28b32dd6c110fd6531b3"},
- {file = "pydantic-1.8.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8b223557f9510cf0bfd8b01316bf6dd281cf41826607eada99662f5e4963f316"},
- {file = "pydantic-1.8.2-cp39-cp39-manylinux1_i686.whl", hash = "sha256:244ad78eeb388a43b0c927e74d3af78008e944074b7d0f4f696ddd5b2af43c62"},
- {file = "pydantic-1.8.2-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:05ef5246a7ffd2ce12a619cbb29f3307b7c4509307b1b49f456657b43529dc6f"},
- {file = "pydantic-1.8.2-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:54cd5121383f4a461ff7644c7ca20c0419d58052db70d8791eacbbe31528916b"},
- {file = "pydantic-1.8.2-cp39-cp39-win_amd64.whl", hash = "sha256:4be75bebf676a5f0f87937c6ddb061fa39cbea067240d98e298508c1bda6f3f3"},
- {file = "pydantic-1.8.2-py3-none-any.whl", hash = "sha256:fec866a0b59f372b7e776f2d7308511784dace622e0992a0b59ea3ccee0ae833"},
- {file = "pydantic-1.8.2.tar.gz", hash = "sha256:26464e57ccaafe72b7ad156fdaa4e9b9ef051f69e175dbbb463283000c05ab7b"},
-]
pyflakes = [
{file = "pyflakes-2.3.1-py2.py3-none-any.whl", hash = "sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3"},
{file = "pyflakes-2.3.1.tar.gz", hash = "sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db"},
@@ -1436,10 +1385,6 @@ regex = [
{file = "regex-2021.10.8-cp39-cp39-win_amd64.whl", hash = "sha256:b04e512eb628ea82ed86eb31c0f7fc6842b46bf2601b66b1356a7008327f7700"},
{file = "regex-2021.10.8.tar.gz", hash = "sha256:26895d7c9bbda5c52b3635ce5991caa90fbb1ddfac9c9ff1c7ce505e2282fb2a"},
]
-schematics = [
- {file = "schematics-2.1.1-py2.py3-none-any.whl", hash = "sha256:be2d451bfb86789975e5ec0864aec569b63cea9010f0d24cbbd992a4e564c647"},
- {file = "schematics-2.1.1.tar.gz", hash = "sha256:34c87f51a25063bb498ae1cc201891b134cfcb329baf9e9f4f3ae869b767560f"},
-]
sentinel = [
{file = "sentinel-0.3.0-py3-none-any.whl", hash = "sha256:bd8710dd26752039c668604f6be2aaf741b56f7811c5924a4dcdfd74359244f3"},
{file = "sentinel-0.3.0.tar.gz", hash = "sha256:f28143aa4716dbc8f6193f5682176a3c33cd26aaae05d9ecf66c186a9887cc2d"},
@@ -1493,8 +1438,8 @@ starlette = [
{file = "starlette-0.16.0.tar.gz", hash = "sha256:e1904b5d0007aee24bdd3c43994be9b3b729f4f58e740200de1d623f8c3a8870"},
]
strawberry-graphql = [
- {file = "strawberry-graphql-0.84.0.tar.gz", hash = "sha256:c122d657540314a4a5d5366954ed687d4f12a96907395241c3285f597785a934"},
- {file = "strawberry_graphql-0.84.0-py3-none-any.whl", hash = "sha256:f1ddec0ebe75a639bdc16bf4815ce0aaa75c703fe0a0873a6b153adb0800be9b"},
+ {file = "strawberry-graphql-0.84.2.tar.gz", hash = "sha256:20dd45ce98b2e1d0a03186c1beda0faaade95c8bdde0f77cdda2ebc0ba22e8cf"},
+ {file = "strawberry_graphql-0.84.2-py3-none-any.whl", hash = "sha256:b1f80788e783f0f4213a17055fd26107c34b5c2731bcdee2b97634c43a391a4a"},
]
toml = [
{file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
diff --git a/reddit-clone/pyproject.toml b/reddit-clone/pyproject.toml
index 6b25cb7f..df2abfca 100644
--- a/reddit-clone/pyproject.toml
+++ b/reddit-clone/pyproject.toml
@@ -14,10 +14,10 @@ celery = {extras = ["redis", "librabbitmq"], version = "^5.1"}
alembic = "^1.7"
asyncpg = "^0.24"
graphql-relay = "^3.1"
-schematics = "^2.1"
jinja2 = "^3.0"
aiofiles = "^0.7"
starlette = "^0.16"
+marshmallow = "^3.14.0"
[tool.poetry.dev-dependencies]
diff --git a/reddit-clone/reddit/base/types.py b/reddit-clone/reddit/base/types.py
index 6c33bf37..f9323022 100644
--- a/reddit-clone/reddit/base/types.py
+++ b/reddit-clone/reddit/base/types.py
@@ -7,6 +7,7 @@
@strawberry.interface(name="Node", description="An object with an ID.")
class NodeType:
+ # TODO: need to make custom ID resolver
id: strawberry.ID = strawberry.field(
description="""
ID of the object.
diff --git a/reddit-clone/reddit/users/mutations/user_create.py b/reddit-clone/reddit/users/mutations/user_create.py
index d40164c8..1d4d0dd1 100644
--- a/reddit-clone/reddit/users/mutations/user_create.py
+++ b/reddit-clone/reddit/users/mutations/user_create.py
@@ -1,9 +1,12 @@
from typing import Union
+from marshmallow.exceptions import ValidationError
import strawberry
from strawberry.types import Info
from reddit.users.types import UserType
+from reddit.users.serializers import user_schema
+from reddit.users.services import user_by_email, user_by_username
__all__ = ("user_create",)
@@ -30,4 +33,13 @@ class UserCreateError:
@strawberry.mutation(description="Creates a new user.")
async def user_create(info: Info, input: UserCreateInput) -> UserCreateResult:
- pass
+ try:
+ data = user_schema.load(input)
+ except ValidationError as err:
+ # TODO: handle validation errors here.
+ print(err)
+
+ if await user_by_email(email=data.get("email")):
+ return UserCreateError(error="Email already exists.")
+ if await user_by_username(username=data.get("username")):
+ return UserCreateError(error="Username already exists.")
diff --git a/reddit-clone/reddit/users/serializers.py b/reddit-clone/reddit/users/serializers.py
new file mode 100644
index 00000000..1796fbb7
--- /dev/null
+++ b/reddit-clone/reddit/users/serializers.py
@@ -0,0 +1,37 @@
+from marshmallow import Schema, pre_load
+from marshmallow.fields import String, Integer, Boolean
+from marshmallow.validate import Email, Length
+
+
+__all__ = ("user_schema",)
+
+
+class UserSchema(Schema):
+ id = Integer(dump_only=True)
+ email = String(
+ required=True,
+ validate=Email(
+ error="Not a valid email address.",
+ ),
+ )
+ username = String(
+ required=True,
+ validate=(Length(min=2, max=32),),
+ )
+ password = String(
+ required=True,
+ load_only=True,
+ validate=(Length(min=8),),
+ )
+ avatar = String(dump_only=True)
+ is_active = Boolean(dump_only=True)
+
+ def process_input(self, data, **kwargs):
+ # clean user emails before storing them.
+ data["email"] = data["email"].lower().strip()
+ return data
+
+ pre_load(process_input)
+
+
+user_schema = UserSchema()
From f7c0d6d87b70d58f7468c86e489308b739ee692b Mon Sep 17 00:00:00 2001
From: Aryan Iyappan <69184573+aryan340@users.noreply.github.com>
Date: Tue, 19 Oct 2021 21:19:00 +0530
Subject: [PATCH 150/150] update volumes
---
reddit-clone/docker-compose.yml | 18 +++-------
.../reddit/users/mutations/user_create.py | 33 ++++++++++++++-----
reddit-clone/reddit/users/services.py | 8 ++---
3 files changed, 34 insertions(+), 25 deletions(-)
diff --git a/reddit-clone/docker-compose.yml b/reddit-clone/docker-compose.yml
index 409acd13..bf2f4231 100644
--- a/reddit-clone/docker-compose.yml
+++ b/reddit-clone/docker-compose.yml
@@ -59,15 +59,15 @@ services:
container_name: reddit-postgres
restart: always
environment:
- POSTGRES_DB: reddit
- POSTGRES_PASSWORD: reddit
POSTGRES_USER: reddit
+ POSTGRES_PASSWORD: reddit
+ POSTGRES_DB: reddit
networks:
- reddit-main
ports:
- 5432:5432
volumes:
- - postgres-data:/var/lib/postgresql/data
+ - ./data/postgres:/var/lib/postgresql/data
redis:
image: redis:6.2-alpine
@@ -80,7 +80,7 @@ services:
ports:
- 6379:6379
volumes:
- - redis-data:/data
+ - ./data/redis:/data
healthcheck:
test: redis-cli ping
interval: 15s
@@ -99,21 +99,13 @@ services:
ports:
- 5672:5672
volumes:
- - rabbitmq-data:/data
+ - ./data/rabbitmq:/data
healthcheck:
test: rabbitmq-diagnostics -q ping
interval: 15s
retries: 5
timeout: 5s
-volumes:
- redis-data:
- driver: local
- rabbitmq-data:
- driver: local
- postgres-data:
- driver: local
-
networks:
reddit-main:
driver: bridge
diff --git a/reddit-clone/reddit/users/mutations/user_create.py b/reddit-clone/reddit/users/mutations/user_create.py
index 1d4d0dd1..0b8d0fff 100644
--- a/reddit-clone/reddit/users/mutations/user_create.py
+++ b/reddit-clone/reddit/users/mutations/user_create.py
@@ -1,12 +1,13 @@
-from typing import Union
+from typing import Union, cast
from marshmallow.exceptions import ValidationError
import strawberry
from strawberry.types import Info
+from reddit.database import get_session
from reddit.users.types import UserType
from reddit.users.serializers import user_schema
-from reddit.users.services import user_by_email, user_by_username
+from reddit.users.services import user_by_email, user_by_username, create_user
__all__ = ("user_create",)
@@ -34,12 +35,28 @@ class UserCreateError:
@strawberry.mutation(description="Creates a new user.")
async def user_create(info: Info, input: UserCreateInput) -> UserCreateResult:
try:
- data = user_schema.load(input)
+ data = user_schema.load(
+ {
+ "username": input.username,
+ "password": input.password,
+ "email": input.email,
+ }
+ )
except ValidationError as err:
# TODO: handle validation errors here.
print(err)
-
- if await user_by_email(email=data.get("email")):
- return UserCreateError(error="Email already exists.")
- if await user_by_username(username=data.get("username")):
- return UserCreateError(error="Username already exists.")
+ return UserCreateError(error=str(err))
+
+ async with get_session() as session:
+ if await user_by_email(session=session, email=data.get("email")):
+ return UserCreateError(error="Email already exists.")
+ if await user_by_username(session=session, username=data.get("username")):
+ return UserCreateError(error="Username already exists.")
+
+ user = await create_user(
+ session=session,
+ email=data.get("email"),
+ username=data.get("username"),
+ password=data.get("password"),
+ )
+ return UserCreateSuccess(user=cast(UserType, user))
diff --git a/reddit-clone/reddit/users/services.py b/reddit-clone/reddit/users/services.py
index b4434d36..425d17d9 100644
--- a/reddit-clone/reddit/users/services.py
+++ b/reddit-clone/reddit/users/services.py
@@ -10,16 +10,16 @@ async def user_by_email(session: AsyncSession, email: str) -> Optional[User]:
"""
Gets an user by their email.
"""
- query = select(User).filter_by(email=email).first()
- return await session.execute(query)
+ query = select(User).filter(User.email == email)
+ return (await session.execute(query)).scalar_one()
async def user_by_username(session: AsyncSession, username: str) -> Optional[User]:
"""
Gets an user by their username.
"""
- query = select(User).filter_by(username=username).first()
- return await session.execute(query)
+ query = select(User).filter(User.username == username)
+ return (await session.execute(query)).scalar_one()
async def authenticate(