├── .dockerignore
├── .env.test
├── .gcloudignore
├── .gitignore
├── Dockerfile
├── LICENSE
├── README.md
├── README.rst
├── assets
└── img
│ ├── fastapi-1.svg
│ ├── firebase-cloud.png
│ ├── google-cloud-run-icon.png
│ └── thul-876298A8-C3E1-487D-8AD6920174E16D78.png
├── google_application_credentials.json
├── logging_config.json
├── main.py
├── poetry.lock
├── pyproject.toml
├── python_fastapi_firebase_authentication
├── __init__.py
├── core
│ ├── configEnv.py
│ ├── custom_logging.py
│ ├── data
│ │ └── mongodb
│ │ │ └── db.py
│ ├── env.py
│ └── settings.py
├── modules
│ ├── dummy
│ │ ├── endpoints.py
│ │ └── model.py
│ └── user
│ │ ├── __init__.py
│ │ ├── constants.py
│ │ ├── controller
│ │ ├── __init__.py
│ │ ├── authVerify.py
│ │ └── player.py
│ │ ├── data
│ │ └── mongoDb
│ │ │ ├── dbConstants.py
│ │ │ ├── interface
│ │ │ ├── authProvider.py
│ │ │ ├── player.py
│ │ │ └── user.py
│ │ │ └── models
│ │ │ ├── authProvider.py
│ │ │ ├── player.py
│ │ │ └── user.py
│ │ ├── model
│ │ └── model.py
│ │ └── route
│ │ └── endpoints.py
└── utils
│ ├── bitwise.py
│ └── dependencies.py
├── serve_uvicorn.py
└── tests
├── __init__.py
└── test_python_fastapi_firebase_authentication.py
/.dockerignore:
--------------------------------------------------------------------------------
1 | .git
2 | .gitignore
3 | .dockerignore
4 | .gcloudignore
5 |
6 | **/__pycache__
7 | node_modules
8 | .serverless
9 | .env.*
10 | .DS_Store
11 | *.pyc
12 | *.pyo
13 | .venv/
14 | .vscode
15 | sample.txt
16 | *.md
17 | *.rst
18 | *.yaml
19 | *.yml
20 |
21 | !firebase_credentials.json
22 | !.env
--------------------------------------------------------------------------------
/.env.test:
--------------------------------------------------------------------------------
1 | TITLE=python_fastapi_firebase_authentication
2 | UVI_PORT=5000
3 | UVI_SERVER_HOST=0.0.0.0
4 | UVI_LOG_LEVE=info
5 | UVI_ACCESS_LOG=True
6 | UVI_RELOAD=True
7 | FASTAPI_DEBUG=True
8 | ENV=dev
9 | MONGODB_URL=
10 | DB_NAME=auth_dev
11 | GOOGLE_APPLICATION_CREDENTIALS=google_application_credentials.json
12 | CORS_ALLOWED_ORIGINS=localhost:5000,127.0.0.0:5000
13 | WEB_API_KEY=
--------------------------------------------------------------------------------
/.gcloudignore:
--------------------------------------------------------------------------------
1 | .gcloudignore
2 | .git
3 | .gitignore
4 |
5 | node_modules
6 | .serverless
7 |
8 | !firebase_credentials.json
9 | !.env
10 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | pip-wheel-metadata/
24 | share/python-wheels/
25 | *.egg-info/
26 | .installed.cfg
27 | *.egg
28 | MANIFEST
29 |
30 | # PyInstaller
31 | # Usually these files are written by a python script from a template
32 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
33 | *.manifest
34 | *.spec
35 |
36 | # Installer logs
37 | pip-log.txt
38 | pip-delete-this-directory.txt
39 |
40 | # Unit test / coverage reports
41 | htmlcov/
42 | .tox/
43 | .nox/
44 | .coverage
45 | .coverage.*
46 | .cache
47 | nosetests.xml
48 | coverage.xml
49 | *.cover
50 | *.py,cover
51 | .hypothesis/
52 | .pytest_cache/
53 |
54 | # Translations
55 | *.mo
56 | *.pot
57 |
58 | # Django stuff:
59 | *.log
60 | local_settings.py
61 | db.sqlite3
62 | db.sqlite3-journal
63 |
64 | # Flask stuff:
65 | instance/
66 | .webassets-cache
67 |
68 | # Scrapy stuff:
69 | .scrapy
70 |
71 | # Sphinx documentation
72 | docs/_build/
73 |
74 | # PyBuilder
75 | target/
76 |
77 | # Jupyter Notebook
78 | .ipynb_checkpoints
79 |
80 | # IPython
81 | profile_default/
82 | ipython_config.py
83 |
84 | # pyenv
85 | .python-version
86 |
87 | # pipenv
88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
91 | # install all needed dependencies.
92 | #Pipfile.lock
93 |
94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow
95 | __pypackages__/
96 |
97 | # Celery stuff
98 | celerybeat-schedule
99 | celerybeat.pid
100 |
101 | # SageMath parsed files
102 | *.sage.py
103 |
104 | # Environments
105 | .aws
106 | .aws/
107 | .aws/credentials
108 | .env
109 | .env.dev
110 | .venv
111 | env/
112 | .venv/
113 | ENV/
114 | env.bak/
115 | venv.bak/
116 |
117 | # Spyder project settings
118 | .spyderproject
119 | .spyproject
120 |
121 | # Rope project settings
122 | .ropeproject
123 |
124 | # mkdocs documentation
125 | /site
126 |
127 | # mypy
128 | .mypy_cache/
129 | .dmypy.json
130 | dmypy.json
131 |
132 | # Pyre type checker
133 | .pyre/
134 |
135 | # zappa stuff
136 | zappa_settings.json
137 |
138 | firebase_credentials.json
139 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | # syntax=docker/dockerfile:1
2 |
3 | FROM python:3.8-slim-buster as requirements-stage
4 |
5 |
6 | ENV PYTHONFAULTHANDLER=1 \
7 | PYTHONUNBUFFERED=1 \
8 | PYTHONHASHSEED=random \
9 | PIP_NO_CACHE_DIR=off \
10 | PIP_DISABLE_PIP_VERSION_CHECK=on \
11 | PIP_DEFAULT_TIMEOUT=100 \
12 | POETRY_VERSION=1.1.13
13 |
14 | # 1st stage
15 | WORKDIR /tmp
16 |
17 | #
18 | RUN pip install "poetry==$POETRY_VERSION"
19 |
20 | #
21 | COPY ./pyproject.toml ./poetry.lock* /tmp/
22 |
23 | #
24 | RUN poetry export -f requirements.txt --output requirements.txt --without-hashes
25 |
26 | # 2nd stage
27 | FROM python:3.8-slim-buster
28 |
29 | #
30 | WORKDIR /testauthapi-docker
31 |
32 | #
33 | COPY --from=requirements-stage /tmp/requirements.txt ./requirements.txt
34 |
35 | #
36 | RUN pip install --no-cache-dir --upgrade -r ./requirements.txt
37 |
38 |
39 | COPY . .
40 |
41 |
42 | EXPOSE $PORT
43 |
44 |
45 | CMD exec gunicorn --bind :$PORT --workers 2 --worker-class uvicorn.workers.UvicornWorker main:app
46 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Rahul Prakash
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Python-FastAPI-Firebase-Authentication-Google-Cloud-Run-Deploy
2 |
3 | A user authentication system, implementing Google's Firebase_admin auth module inside python's FastAPI based backend. Furthe we have deployed the code on Google Cloud Run using Docker.
4 |
5 |
6 | 
7 | 
8 | 
9 | 
10 | 
11 | 
12 |
13 | 
14 | 
15 |
16 |
17 | 

18 |
19 |
20 |
21 | # Technologies used in the project:
22 |
23 | - Google's firebase-admin:
24 | - to create/register dummy user, for token generation
25 | - to authenticate user, for access
26 | - FastAPI:
27 | - implementing the backend logic to access database
28 | - Poetry:
29 | - for dependency management of our python environment
30 | - MongoDb:
31 | - an open source NoSql database storing our user information
32 | - Google Cloud Platform:
33 | - for deployment on Cloud Run
34 |
35 |
36 | # Index
37 |
38 | - **[Installation Instructions](#installation-instructions)**
39 | - **[Using Poetry](#using-poetry)**
40 | - **[Usage Instructions](#usage-instructions)**
41 | - **[Setup FastApi RESTapi](#setup-fastapi-restapi)**
42 | - **[Setup Firebase Auth](#setup-firebase-auth)**
43 | - **[Google Cloud Run Deployment](#google-cloud-run-deployment)**
44 | - **[Further Help](#further-help)**
45 | - **[License](#license)**
46 |
47 |
48 |
49 | # Installation Instructions #
50 |
51 |
52 | We will be using [poetry](https://python-poetry.org/docs/) as our python package manager. you can follow the steps to get this project running on your system.
53 | So we will start by setting up poetry on our local machine.
54 |
55 |
56 | - First clone the repo to your local machine. In your git bash UI enter the commands:
57 |
58 | ```bash
59 | > git clone
60 | > cd this-project
61 | > code .
62 | ```
63 | ### [Using Poetry](https://python-poetry.org/docs/)
64 | - From the root directory we will install poetry if not already present in your system. Click [here](https://python-poetry.org/docs/#windows-powershell-install-instructions) for the windows install command.
65 | - After poetry gets installed, setup your virtual invironment.
66 |
67 | ```ps
68 | > poetry env use python
69 | > poetry env use 3.8
70 | ```
71 |
72 | - Next to install dependencies run:
73 | ```ps
74 | > poetry install
75 | ```
76 |
77 | Note : `Poetry takes care of installing these dependencies in your virtual environment. You don't have to activate your virtual environment manually every time.`
78 |
79 |
80 |
81 |
82 | **Before we could the test the setup we need following to be ready with us :**
83 |
84 |
85 | - **Mongodb Atlas**, to store user information to authenticate against. You can use [free service](https://www.mongodb.com/docs/atlas/tutorial/deploy-free-tier-cluster/) provided by MongoDb for this project.
86 | - [**Firebase Admin**](https://firebase.google.com/docs/admin/setup#set-up-project-and-service-account) credentials to be able to generate tokens and verify against.
87 | - create an empty **`.env`** file at the base directory of your project and update the [`.env.test`](.env.test) file.
88 | - provide [mongodb connection string](https://www.mongodb.com/docs/guides/atlas/connection-string/) to `MONGODB_URL`
89 | - create [firebase credentials.json](google_application_credentials.json) file in the root directory of the project or, otherwise, provide the absolute path to credentials file in .env to `GOOGLE_APPLICATION_CREDENTIALS`
90 |
91 | We are all set to run our test.
92 |
93 |
94 |
95 | # Usage Instructions #
96 |
97 | Poetry provides for custom scripts to be run in cli. You can access/write these scripts in [pyproject.toml](pyproject.toml).
98 |
99 | To run locally run these commands:
100 |
101 | ```ps
102 | > poetry run test (to setup environment test)
103 | > poetry run server (to serve uvicorn)
104 | ```
105 | FastApi provides Swagger UI: served at /docs.
106 | In your bowser go to :
107 | > http://localhost:5000/docs
108 |
109 | You can register a dummy user using email and password to authenticate further, via :
110 | >http://localhost:5000/docs#/dummy/create_user_dummy_register_post
111 |
112 | Next, login through above credentials to get token from firebase auth provider to authenticate, via :
113 | >http://localhost:5000/docs#/dummy/login
114 |
115 | Use the token obtained above to verify/register a user, via :
116 | > http://localhost:5000/docs#/auth/verify
117 |
118 | In response you get details of new player created in your database which can be acceesed/modified through token-based authentication only, via :
119 | > http://localhost:5000/docs#/player/{id}
120 |
121 |
122 |
123 |
124 | # Setup FastApi RESTapi
125 |
126 | To get more familier with Fastapi you can go through their [docs](https://fastapi.tiangolo.com/).
127 |
128 | Here, we will be using **[uvicorn](https://www.uvicorn.org/deployment/)** to serve our FastApi backend.
129 |
130 | We have also used **Beanie** as an ODM interface to MongoDb, with async capabilities. "Beanie - is an Asynchronous Python object-document mapper (ODM) for MongoDB, based on **Motor** and **Pydantic**."You can follow the Beanie's documentation [**here**](https://roman-right.github.io/beanie/).
131 |
132 |
133 |
134 | #
Setup Firebase Auth
135 |
136 | One can use the Firebase Admin SDK to manage your users or to manage authentication tokens. As stated in their [**documentation**](https://firebase.google.com/docs/auth/admin), You can also use the service to identify these users on your own server. This lets you securely perform server-side logic on behalf of users that have signed in with Firebase Authentication.
137 |
138 | To do this, you can retrieve an ID token from a client application signed in with Firebase Authentication and include the token in a request to your server. Your server then [**verifies the ID token**](python_fastapi_firebase_authentication\utils\dependencies.py) and extracts the claims that identify the user (including their uid, the identity provider they logged in with, etc.). This identity information can then be used by your server to carry out actions on behalf of the user.
139 |
140 |
141 |
142 | #
Google Cloud Run Deployment
143 |
144 | For this project we have used Google's Cloud Run to deploy my Dockerised FastApi.
145 | You can follow this link for more help:
146 | - https://towardsdatascience.com/deploy-a-dockerized-fastapi-app-to-google-cloud-platform-24f72266c7ef
147 |
148 | I will list the step-wise process involved in deployment:
149 | - **Pre-requisites :**
150 | - A Billing Account on GCP/GOOGLE CLOUD PLATFORM(though this setup will cost $0)
151 | - Create [**Dockerfile**](Dockerfile) to dockerise your fastapi code
152 | - Create [**.dockerignore**](.dockerignore) and [**.gcloudignore**](.gcloudignore) files(to enumerate what you need to push and what not)
153 | - Note : Here we have also used .gitignore to whose restrictions .gcloudignore [automatically inherits](https://cloud.google.com/sdk/gcloud/reference/topic/gcloudignore).
154 | - Make sure you have not ignored [.env](.env) and [google_application_credentials.json](google_application_credentials.json)
155 | - Next follow the instruction in the link provided above.
156 | - Also make sure [**gcloud cli**](https://cloud.google.com/sdk/docs/install) is installed and configured on your system.
157 | - gcloud cli commands to be entered for deployment:
158 |
159 | Replace PROJECT-ID with your GCP project ID
160 | ```ps
161 |
162 | TO view your project ID by running the command
163 | > gcloud config get-value project
164 |
165 | TO SET your project ID:
166 | gcloud config set project PROJECT-ID
167 | > gcloud config set project auth-test
168 |
169 | TO BUILD:
170 | gcloud builds submit --tag gcr.io/PROJECT-ID/container-name
171 | > gcloud builds submit --tag gcr.io/auth-test/auth-api-container
172 |
173 | TO DEPLOY:
174 | gcloud run deploy --image gcr.io/PROJECT-ID/container-name --platform managed
175 | > gcloud run deploy --image gcr.io/auth-test/auth-api-container --platform managed
176 |
177 | TO DELETE:
178 | gcloud deployment-manager deployments delete example-deployment --delete-policy=DELETE
179 |
180 | ```
181 |
182 |
183 |
184 |
185 | # Further Help
186 |
187 | This project is an open-source initiative by Junkie Labs team.
188 |
189 | For any questions or suggestions send a mail to junkielabs.dev@gmail.com or chat with the core-team on gitter.
190 |
191 |
192 | [](https://gitter.im/nubar-api/django-dynamodb-lambda-function?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
193 |
194 |
195 |
196 | # License
197 |
198 | [MIT License](/LICENSE).
199 |
200 |
201 |
202 |
203 |
204 |
205 |
--------------------------------------------------------------------------------
/README.rst:
--------------------------------------------------------------------------------
1 | gunicorn main:app --workers 4 --worker-class uvicorn.workers.UvicornWorker --bind 0.0.0.0:80
2 |
3 | poetry export -f requirements.txt --output requirements.txt --without-hashes
4 |
5 | gcloud builds submit --tag gcr.io/zone-india/auth-api-container
6 |
7 | gcloud run deploy --image gcr.io/zone-india/auth-api-container --platform managed
--------------------------------------------------------------------------------
/assets/img/fastapi-1.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/img/firebase-cloud.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rahulprakash11/Python-Firebase-Authentication-FastAPI/7341e6a223eca6388bb22047567777e52b088d9e/assets/img/firebase-cloud.png
--------------------------------------------------------------------------------
/assets/img/google-cloud-run-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rahulprakash11/Python-Firebase-Authentication-FastAPI/7341e6a223eca6388bb22047567777e52b088d9e/assets/img/google-cloud-run-icon.png
--------------------------------------------------------------------------------
/assets/img/thul-876298A8-C3E1-487D-8AD6920174E16D78.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rahulprakash11/Python-Firebase-Authentication-FastAPI/7341e6a223eca6388bb22047567777e52b088d9e/assets/img/thul-876298A8-C3E1-487D-8AD6920174E16D78.png
--------------------------------------------------------------------------------
/google_application_credentials.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "service_account",
3 | "project_id": "xxxxx",
4 | "private_key_id": "xxxxxxxxxxxxxxxxxx",
5 | "private_key": "-----BEGIN PRIVATE KEY-----\nxxxxxxxxxxxxx\n-----END PRIVATE KEY-----\n",
6 | "client_email": "firebase-adminsdk-asdf@.iam.gserviceaccount.com",
7 | "client_id": "xxxxxxxxxxxxxx",
8 | "auth_uri": "https://accounts.google.com/o/oauth2/auth",
9 | "token_uri": "https://oauth2.googleapis.com/token",
10 | "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
11 | "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-asdf%40.iam.gserviceaccount.com"
12 | }
13 |
--------------------------------------------------------------------------------
/logging_config.json:
--------------------------------------------------------------------------------
1 | {
2 | "logger": {
3 | "filename": "access.log",
4 | "level": "debug",
5 | "rotation": "20 days",
6 | "retention": "1 months",
7 | "format": "{level: <8} {time:YYYY-MM-DD HH:mm:ss.SSS} - {name}:{function}:{line} - {message}"
8 |
9 | }
10 | }
--------------------------------------------------------------------------------
/main.py:
--------------------------------------------------------------------------------
1 |
2 | from loguru import logger
3 | from firebase_admin import initialize_app
4 |
5 | from fastapi import FastAPI
6 | from fastapi.middleware.cors import CORSMiddleware
7 |
8 | from python_fastapi_firebase_authentication.core.env import TITLE, DEBUG, ALLOWED_ORIGINS, LOGGING_CONFIG_PATH
9 | from python_fastapi_firebase_authentication.core.custom_logging import CustomizeLogger
10 | from python_fastapi_firebase_authentication.core.data.mongodb.db import init_db, mongodb_client
11 | from python_fastapi_firebase_authentication.modules.user.route.endpoints import userRouter
12 | from python_fastapi_firebase_authentication.modules.dummy.endpoints import dummyRouter
13 |
14 |
15 | def get_application():
16 | _app = FastAPI(title=TITLE, debug=DEBUG)
17 |
18 | logger = CustomizeLogger.make_logger(LOGGING_CONFIG_PATH)
19 | _app.logger = logger
20 | _app.include_router(router=userRouter)
21 | _app.include_router(router=dummyRouter)
22 | _app.add_middleware(
23 | CORSMiddleware,
24 | allow_origins=["*"],#ALLOWED_ORIGINS
25 | allow_credentials=True,
26 | allow_methods=["*"],
27 | allow_headers=["*"],
28 | )
29 | return _app
30 |
31 | app = get_application()
32 | # ... some code skipped
33 |
34 | @app.get("/")
35 | def read_root():
36 | return {"Hello App Users": "Welcome to the app!"}
37 |
38 |
39 | @app.on_event("startup")
40 | async def app_init():
41 | logger.debug("---Initializing---")
42 | await init_db(mongodb_client)
43 | logger.debug("---Initialized the database---")
44 | initialize_app()
45 | logger.debug("---Initialized the firebase admin---")
46 |
--------------------------------------------------------------------------------
/poetry.lock:
--------------------------------------------------------------------------------
1 | [[package]]
2 | name = "anyio"
3 | version = "3.6.1"
4 | description = "High level compatibility layer for multiple asynchronous event loop implementations"
5 | category = "main"
6 | optional = false
7 | python-versions = ">=3.6.2"
8 |
9 | [package.dependencies]
10 | idna = ">=2.8"
11 | sniffio = ">=1.1"
12 |
13 | [package.extras]
14 | doc = ["packaging", "sphinx-rtd-theme", "sphinx-autodoc-typehints (>=1.2.0)"]
15 | test = ["coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "contextlib2", "uvloop (<0.15)", "mock (>=4)", "uvloop (>=0.15)"]
16 | trio = ["trio (>=0.16)"]
17 |
18 | [[package]]
19 | name = "atomicwrites"
20 | version = "1.4.1"
21 | description = "Atomic file writes."
22 | category = "dev"
23 | optional = false
24 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
25 |
26 | [[package]]
27 | name = "attrs"
28 | version = "22.1.0"
29 | description = "Classes Without Boilerplate"
30 | category = "dev"
31 | optional = false
32 | python-versions = ">=3.5"
33 |
34 | [package.extras]
35 | dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "mypy (>=0.900,!=0.940)", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit", "cloudpickle"]
36 | docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"]
37 | tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "mypy (>=0.900,!=0.940)", "pytest-mypy-plugins", "zope.interface", "cloudpickle"]
38 | tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "mypy (>=0.900,!=0.940)", "pytest-mypy-plugins", "cloudpickle"]
39 |
40 | [[package]]
41 | name = "beanie"
42 | version = "1.11.9"
43 | description = "Asynchronous Python ODM for MongoDB"
44 | category = "main"
45 | optional = false
46 | python-versions = ">=3.7,<4.0"
47 |
48 | [package.dependencies]
49 | click = ">=7"
50 | motor = ">=2.5,<4.0"
51 | pydantic = ">=1.9.0"
52 | toml = "*"
53 | yarl = ">=1.6"
54 |
55 | [[package]]
56 | name = "cachecontrol"
57 | version = "0.12.11"
58 | description = "httplib2 caching for requests"
59 | category = "main"
60 | optional = false
61 | python-versions = ">=3.6"
62 |
63 | [package.dependencies]
64 | msgpack = ">=0.5.2"
65 | requests = "*"
66 |
67 | [package.extras]
68 | filecache = ["lockfile (>=0.9)"]
69 | redis = ["redis (>=2.10.5)"]
70 |
71 | [[package]]
72 | name = "cachetools"
73 | version = "5.2.0"
74 | description = "Extensible memoizing collections and decorators"
75 | category = "main"
76 | optional = false
77 | python-versions = "~=3.7"
78 |
79 | [[package]]
80 | name = "certifi"
81 | version = "2022.6.15"
82 | description = "Python package for providing Mozilla's CA Bundle."
83 | category = "main"
84 | optional = false
85 | python-versions = ">=3.6"
86 |
87 | [[package]]
88 | name = "charset-normalizer"
89 | version = "2.1.1"
90 | description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
91 | category = "main"
92 | optional = false
93 | python-versions = ">=3.6.0"
94 |
95 | [package.extras]
96 | unicode_backport = ["unicodedata2"]
97 |
98 | [[package]]
99 | name = "click"
100 | version = "8.1.3"
101 | description = "Composable command line interface toolkit"
102 | category = "main"
103 | optional = false
104 | python-versions = ">=3.7"
105 |
106 | [package.dependencies]
107 | colorama = {version = "*", markers = "platform_system == \"Windows\""}
108 |
109 | [[package]]
110 | name = "colorama"
111 | version = "0.4.5"
112 | description = "Cross-platform colored terminal text."
113 | category = "main"
114 | optional = false
115 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
116 |
117 | [[package]]
118 | name = "dnspython"
119 | version = "2.2.1"
120 | description = "DNS toolkit"
121 | category = "main"
122 | optional = false
123 | python-versions = ">=3.6,<4.0"
124 |
125 | [package.extras]
126 | dnssec = ["cryptography (>=2.6,<37.0)"]
127 | curio = ["curio (>=1.2,<2.0)", "sniffio (>=1.1,<2.0)"]
128 | doh = ["h2 (>=4.1.0)", "httpx (>=0.21.1)", "requests (>=2.23.0,<3.0.0)", "requests-toolbelt (>=0.9.1,<0.10.0)"]
129 | idna = ["idna (>=2.1,<4.0)"]
130 | trio = ["trio (>=0.14,<0.20)"]
131 | wmi = ["wmi (>=1.5.1,<2.0.0)"]
132 |
133 | [[package]]
134 | name = "email-validator"
135 | version = "1.2.1"
136 | description = "A robust email syntax and deliverability validation library."
137 | category = "main"
138 | optional = false
139 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
140 |
141 | [package.dependencies]
142 | dnspython = ">=1.15.0"
143 | idna = ">=2.0.0"
144 |
145 | [[package]]
146 | name = "fastapi"
147 | version = "0.81.0"
148 | description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production"
149 | category = "main"
150 | optional = false
151 | python-versions = ">=3.6.1"
152 |
153 | [package.dependencies]
154 | 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"
155 | starlette = "0.19.1"
156 |
157 | [package.extras]
158 | 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,<7.0.0)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0,<6.0.0)", "orjson (>=3.2.1,<4.0.0)", "email_validator (>=1.1.1,<2.0.0)", "uvicorn[standard] (>=0.12.0,<0.18.0)"]
159 | 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,<6.0.0)", "uvicorn[standard] (>=0.12.0,<0.18.0)", "pre-commit (>=2.17.0,<3.0.0)"]
160 | doc = ["mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=8.1.4,<9.0.0)", "mdx-include (>=1.4.1,<2.0.0)", "mkdocs-markdownextradata-plugin (>=0.1.7,<0.3.0)", "typer (>=0.4.1,<0.5.0)", "pyyaml (>=5.3.1,<7.0.0)"]
161 | test = ["pytest (>=6.2.4,<7.0.0)", "pytest-cov (>=2.12.0,<4.0.0)", "mypy (==0.910)", "flake8 (>=3.8.3,<6.0.0)", "black (==22.3.0)", "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,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0,<6.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 (==4.2.1)", "types-orjson (==3.6.2)", "types-dataclasses (==0.6.5)"]
162 |
163 | [[package]]
164 | name = "firebase-admin"
165 | version = "5.3.0"
166 | description = "Firebase Admin Python SDK"
167 | category = "main"
168 | optional = false
169 | python-versions = ">=3.6"
170 |
171 | [package.dependencies]
172 | cachecontrol = ">=0.12.6"
173 | google-api-core = {version = ">=1.22.1,<3.0.0dev", extras = ["grpc"], markers = "platform_python_implementation != \"PyPy\""}
174 | google-api-python-client = ">=1.7.8"
175 | google-cloud-firestore = {version = ">=2.1.0", markers = "platform_python_implementation != \"PyPy\""}
176 | google-cloud-storage = ">=1.37.1"
177 |
178 | [[package]]
179 | name = "google-api-core"
180 | version = "2.8.2"
181 | description = "Google API client core library"
182 | category = "main"
183 | optional = false
184 | python-versions = ">=3.6"
185 |
186 | [package.dependencies]
187 | google-auth = ">=1.25.0,<3.0dev"
188 | googleapis-common-protos = ">=1.56.2,<2.0dev"
189 | grpcio = {version = ">=1.33.2,<2.0dev", optional = true, markers = "extra == \"grpc\""}
190 | grpcio-status = {version = ">=1.33.2,<2.0dev", optional = true, markers = "extra == \"grpc\""}
191 | protobuf = ">=3.15.0,<5.0.0dev"
192 | requests = ">=2.18.0,<3.0.0dev"
193 |
194 | [package.extras]
195 | grpc = ["grpcio (>=1.33.2,<2.0dev)", "grpcio-status (>=1.33.2,<2.0dev)"]
196 |
197 | [[package]]
198 | name = "google-api-python-client"
199 | version = "2.58.0"
200 | description = "Google API Client Library for Python"
201 | category = "main"
202 | optional = false
203 | python-versions = ">=3.7"
204 |
205 | [package.dependencies]
206 | google-api-core = ">=1.31.5,<2.0.0 || >2.3.0,<3.0.0dev"
207 | google-auth = ">=1.19.0,<3.0.0dev"
208 | google-auth-httplib2 = ">=0.1.0"
209 | httplib2 = ">=0.15.0,<1dev"
210 | uritemplate = ">=3.0.1,<5"
211 |
212 | [[package]]
213 | name = "google-auth"
214 | version = "2.11.0"
215 | description = "Google Authentication Library"
216 | category = "main"
217 | optional = false
218 | python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*"
219 |
220 | [package.dependencies]
221 | cachetools = ">=2.0.0,<6.0"
222 | pyasn1-modules = ">=0.2.1"
223 | rsa = {version = ">=3.1.4,<5", markers = "python_version >= \"3.6\""}
224 | six = ">=1.9.0"
225 |
226 | [package.extras]
227 | reauth = ["pyu2f (>=0.1.5)"]
228 | pyopenssl = ["pyopenssl (>=20.0.0)"]
229 | enterprise_cert = ["pyopenssl (==22.0.0)", "cryptography (==36.0.2)"]
230 | aiohttp = ["aiohttp (>=3.6.2,<4.0.0dev)", "requests (>=2.20.0,<3.0.0dev)"]
231 |
232 | [[package]]
233 | name = "google-auth-httplib2"
234 | version = "0.1.0"
235 | description = "Google Authentication Library: httplib2 transport"
236 | category = "main"
237 | optional = false
238 | python-versions = "*"
239 |
240 | [package.dependencies]
241 | google-auth = "*"
242 | httplib2 = ">=0.15.0"
243 | six = "*"
244 |
245 | [[package]]
246 | name = "google-cloud-core"
247 | version = "2.3.2"
248 | description = "Google Cloud API client core library"
249 | category = "main"
250 | optional = false
251 | python-versions = ">=3.7"
252 |
253 | [package.dependencies]
254 | google-api-core = ">=1.31.6,<2.0.0 || >2.3.0,<3.0.0dev"
255 | google-auth = ">=1.25.0,<3.0dev"
256 |
257 | [package.extras]
258 | grpc = ["grpcio (>=1.38.0,<2.0dev)"]
259 |
260 | [[package]]
261 | name = "google-cloud-firestore"
262 | version = "2.6.1"
263 | description = "Google Cloud Firestore API client library"
264 | category = "main"
265 | optional = false
266 | python-versions = ">=3.7"
267 |
268 | [package.dependencies]
269 | google-api-core = {version = ">=1.32.0,<2.0.0 || >=2.8.0,<3.0.0dev", extras = ["grpc"]}
270 | google-cloud-core = ">=1.4.1,<3.0.0dev"
271 | proto-plus = ">=1.22.0,<2.0.0dev"
272 | protobuf = ">=3.19.0,<5.0.0dev"
273 |
274 | [[package]]
275 | name = "google-cloud-storage"
276 | version = "2.5.0"
277 | description = "Google Cloud Storage API client library"
278 | category = "main"
279 | optional = false
280 | python-versions = ">=3.7"
281 |
282 | [package.dependencies]
283 | google-api-core = ">=1.31.5,<2.0.0 || >2.3.0,<3.0.0dev"
284 | google-auth = ">=1.25.0,<3.0dev"
285 | google-cloud-core = ">=2.3.0,<3.0dev"
286 | google-resumable-media = ">=2.3.2"
287 | requests = ">=2.18.0,<3.0.0dev"
288 |
289 | [package.extras]
290 | protobuf = ["protobuf (<5.0.0dev)"]
291 |
292 | [[package]]
293 | name = "google-crc32c"
294 | version = "1.3.0"
295 | description = "A python wrapper of the C library 'Google CRC32C'"
296 | category = "main"
297 | optional = false
298 | python-versions = ">=3.6"
299 |
300 | [package.extras]
301 | testing = ["pytest"]
302 |
303 | [[package]]
304 | name = "google-resumable-media"
305 | version = "2.3.3"
306 | description = "Utilities for Google Media Downloads and Resumable Uploads"
307 | category = "main"
308 | optional = false
309 | python-versions = ">= 3.6"
310 |
311 | [package.dependencies]
312 | google-crc32c = ">=1.0,<2.0dev"
313 |
314 | [package.extras]
315 | aiohttp = ["aiohttp (>=3.6.2,<4.0.0dev)"]
316 | requests = ["requests (>=2.18.0,<3.0.0dev)"]
317 |
318 | [[package]]
319 | name = "googleapis-common-protos"
320 | version = "1.56.4"
321 | description = "Common protobufs used in Google APIs"
322 | category = "main"
323 | optional = false
324 | python-versions = ">=3.7"
325 |
326 | [package.dependencies]
327 | protobuf = ">=3.15.0,<5.0.0dev"
328 |
329 | [package.extras]
330 | grpc = ["grpcio (>=1.0.0,<2.0.0dev)"]
331 |
332 | [[package]]
333 | name = "grpcio"
334 | version = "1.48.0"
335 | description = "HTTP/2-based RPC framework"
336 | category = "main"
337 | optional = false
338 | python-versions = ">=3.6"
339 |
340 | [package.dependencies]
341 | six = ">=1.5.2"
342 |
343 | [package.extras]
344 | protobuf = ["grpcio-tools (>=1.48.0)"]
345 |
346 | [[package]]
347 | name = "grpcio-status"
348 | version = "1.48.0"
349 | description = "Status proto mapping for gRPC"
350 | category = "main"
351 | optional = false
352 | python-versions = ">=3.6"
353 |
354 | [package.dependencies]
355 | googleapis-common-protos = ">=1.5.5"
356 | grpcio = ">=1.48.0"
357 | protobuf = ">=3.12.0"
358 |
359 | [[package]]
360 | name = "gunicorn"
361 | version = "20.1.0"
362 | description = "WSGI HTTP Server for UNIX"
363 | category = "main"
364 | optional = false
365 | python-versions = ">=3.5"
366 |
367 | [package.extras]
368 | eventlet = ["eventlet (>=0.24.1)"]
369 | gevent = ["gevent (>=1.4.0)"]
370 | setproctitle = ["setproctitle"]
371 | tornado = ["tornado (>=0.2)"]
372 |
373 | [[package]]
374 | name = "h11"
375 | version = "0.13.0"
376 | description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1"
377 | category = "main"
378 | optional = false
379 | python-versions = ">=3.6"
380 |
381 | [[package]]
382 | name = "httplib2"
383 | version = "0.20.4"
384 | description = "A comprehensive HTTP client library."
385 | category = "main"
386 | optional = false
387 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
388 |
389 | [package.dependencies]
390 | pyparsing = {version = ">=2.4.2,<3.0.0 || >3.0.0,<3.0.1 || >3.0.1,<3.0.2 || >3.0.2,<3.0.3 || >3.0.3,<4", markers = "python_version > \"3.0\""}
391 |
392 | [[package]]
393 | name = "httptools"
394 | version = "0.4.0"
395 | description = "A collection of framework independent HTTP protocol utils."
396 | category = "main"
397 | optional = false
398 | python-versions = ">=3.5.0"
399 |
400 | [package.extras]
401 | test = ["Cython (>=0.29.24,<0.30.0)"]
402 |
403 | [[package]]
404 | name = "idna"
405 | version = "3.3"
406 | description = "Internationalized Domain Names in Applications (IDNA)"
407 | category = "main"
408 | optional = false
409 | python-versions = ">=3.5"
410 |
411 | [[package]]
412 | name = "loguru"
413 | version = "0.6.0"
414 | description = "Python logging made (stupidly) simple"
415 | category = "main"
416 | optional = false
417 | python-versions = ">=3.5"
418 |
419 | [package.dependencies]
420 | colorama = {version = ">=0.3.4", markers = "sys_platform == \"win32\""}
421 | win32-setctime = {version = ">=1.0.0", markers = "sys_platform == \"win32\""}
422 |
423 | [package.extras]
424 | dev = ["colorama (>=0.3.4)", "docutils (==0.16)", "flake8 (>=3.7.7)", "tox (>=3.9.0)", "pytest (>=4.6.2)", "pytest-cov (>=2.7.1)", "black (>=19.10b0)", "isort (>=5.1.1)", "Sphinx (>=4.1.1)", "sphinx-autobuild (>=0.7.1)", "sphinx-rtd-theme (>=0.4.3)"]
425 |
426 | [[package]]
427 | name = "more-itertools"
428 | version = "8.14.0"
429 | description = "More routines for operating on iterables, beyond itertools"
430 | category = "dev"
431 | optional = false
432 | python-versions = ">=3.5"
433 |
434 | [[package]]
435 | name = "motor"
436 | version = "3.0.0"
437 | description = "Non-blocking MongoDB driver for Tornado or asyncio"
438 | category = "main"
439 | optional = false
440 | python-versions = ">=3.7"
441 |
442 | [package.dependencies]
443 | pymongo = ">=4.1,<5"
444 |
445 | [package.extras]
446 | aws = ["pymongo[aws] (>=4.1,<5)"]
447 | encryption = ["pymongo[encryption] (>=4.1,<5)"]
448 | gssapi = ["pymongo[gssapi] (>=4.1,<5)"]
449 | ocsp = ["pymongo[ocsp] (>=4.1,<5)"]
450 | snappy = ["pymongo[snappy] (>=4.1,<5)"]
451 | srv = ["pymongo[srv] (>=4.1,<5)"]
452 | zstd = ["pymongo[zstd] (>=4.1,<5)"]
453 |
454 | [[package]]
455 | name = "msgpack"
456 | version = "1.0.4"
457 | description = "MessagePack serializer"
458 | category = "main"
459 | optional = false
460 | python-versions = "*"
461 |
462 | [[package]]
463 | name = "multidict"
464 | version = "6.0.2"
465 | description = "multidict implementation"
466 | category = "main"
467 | optional = false
468 | python-versions = ">=3.7"
469 |
470 | [[package]]
471 | name = "packaging"
472 | version = "21.3"
473 | description = "Core utilities for Python packages"
474 | category = "dev"
475 | optional = false
476 | python-versions = ">=3.6"
477 |
478 | [package.dependencies]
479 | pyparsing = ">=2.0.2,<3.0.5 || >3.0.5"
480 |
481 | [[package]]
482 | name = "pluggy"
483 | version = "0.13.1"
484 | description = "plugin and hook calling mechanisms for python"
485 | category = "dev"
486 | optional = false
487 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
488 |
489 | [package.extras]
490 | dev = ["pre-commit", "tox"]
491 |
492 | [[package]]
493 | name = "proto-plus"
494 | version = "1.22.1"
495 | description = "Beautiful, Pythonic protocol buffers."
496 | category = "main"
497 | optional = false
498 | python-versions = ">=3.6"
499 |
500 | [package.dependencies]
501 | protobuf = ">=3.19.0,<5.0.0dev"
502 |
503 | [package.extras]
504 | testing = ["google-api-core[grpc] (>=1.31.5)"]
505 |
506 | [[package]]
507 | name = "protobuf"
508 | version = "4.21.5"
509 | description = ""
510 | category = "main"
511 | optional = false
512 | python-versions = ">=3.7"
513 |
514 | [[package]]
515 | name = "py"
516 | version = "1.11.0"
517 | description = "library with cross-python path, ini-parsing, io, code, log facilities"
518 | category = "dev"
519 | optional = false
520 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
521 |
522 | [[package]]
523 | name = "pyasn1"
524 | version = "0.4.8"
525 | description = "ASN.1 types and codecs"
526 | category = "main"
527 | optional = false
528 | python-versions = "*"
529 |
530 | [[package]]
531 | name = "pyasn1-modules"
532 | version = "0.2.8"
533 | description = "A collection of ASN.1-based protocols modules."
534 | category = "main"
535 | optional = false
536 | python-versions = "*"
537 |
538 | [package.dependencies]
539 | pyasn1 = ">=0.4.6,<0.5.0"
540 |
541 | [[package]]
542 | name = "pydantic"
543 | version = "1.10.1"
544 | description = "Data validation and settings management using python type hints"
545 | category = "main"
546 | optional = false
547 | python-versions = ">=3.7"
548 |
549 | [package.dependencies]
550 | typing-extensions = ">=4.1.0"
551 |
552 | [package.extras]
553 | dotenv = ["python-dotenv (>=0.10.4)"]
554 | email = ["email-validator (>=1.0.3)"]
555 |
556 | [[package]]
557 | name = "pymongo"
558 | version = "4.2.0"
559 | description = "Python driver for MongoDB "
560 | category = "main"
561 | optional = false
562 | python-versions = ">=3.7"
563 |
564 | [package.extras]
565 | aws = ["pymongo-auth-aws (<2.0.0)"]
566 | encryption = ["pymongocrypt (>=1.3.0,<2.0.0)"]
567 | gssapi = ["pykerberos"]
568 | ocsp = ["pyopenssl (>=17.2.0)", "requests (<3.0.0)", "service-identity (>=18.1.0)", "certifi"]
569 | snappy = ["python-snappy"]
570 | srv = ["dnspython (>=1.16.0,<3.0.0)"]
571 | zstd = ["zstandard"]
572 |
573 | [[package]]
574 | name = "pyparsing"
575 | version = "3.0.9"
576 | description = "pyparsing module - Classes and methods to define and execute parsing grammars"
577 | category = "main"
578 | optional = false
579 | python-versions = ">=3.6.8"
580 |
581 | [package.extras]
582 | diagrams = ["railroad-diagrams", "jinja2"]
583 |
584 | [[package]]
585 | name = "pytest"
586 | version = "5.4.3"
587 | description = "pytest: simple powerful testing with Python"
588 | category = "dev"
589 | optional = false
590 | python-versions = ">=3.5"
591 |
592 | [package.dependencies]
593 | atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""}
594 | attrs = ">=17.4.0"
595 | colorama = {version = "*", markers = "sys_platform == \"win32\""}
596 | more-itertools = ">=4.0.0"
597 | packaging = "*"
598 | pluggy = ">=0.12,<1.0"
599 | py = ">=1.5.0"
600 | wcwidth = "*"
601 |
602 | [package.extras]
603 | checkqa-mypy = ["mypy (==v0.761)"]
604 | testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"]
605 |
606 | [[package]]
607 | name = "python-dotenv"
608 | version = "0.20.0"
609 | description = "Read key-value pairs from a .env file and set them as environment variables"
610 | category = "main"
611 | optional = false
612 | python-versions = ">=3.5"
613 |
614 | [package.extras]
615 | cli = ["click (>=5.0)"]
616 |
617 | [[package]]
618 | name = "pyyaml"
619 | version = "6.0"
620 | description = "YAML parser and emitter for Python"
621 | category = "main"
622 | optional = false
623 | python-versions = ">=3.6"
624 |
625 | [[package]]
626 | name = "requests"
627 | version = "2.28.1"
628 | description = "Python HTTP for Humans."
629 | category = "main"
630 | optional = false
631 | python-versions = ">=3.7, <4"
632 |
633 | [package.dependencies]
634 | certifi = ">=2017.4.17"
635 | charset-normalizer = ">=2,<3"
636 | idna = ">=2.5,<4"
637 | urllib3 = ">=1.21.1,<1.27"
638 |
639 | [package.extras]
640 | socks = ["PySocks (>=1.5.6,!=1.5.7)"]
641 | use_chardet_on_py3 = ["chardet (>=3.0.2,<6)"]
642 |
643 | [[package]]
644 | name = "rsa"
645 | version = "4.9"
646 | description = "Pure-Python RSA implementation"
647 | category = "main"
648 | optional = false
649 | python-versions = ">=3.6,<4"
650 |
651 | [package.dependencies]
652 | pyasn1 = ">=0.1.3"
653 |
654 | [[package]]
655 | name = "six"
656 | version = "1.16.0"
657 | description = "Python 2 and 3 compatibility utilities"
658 | category = "main"
659 | optional = false
660 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
661 |
662 | [[package]]
663 | name = "sniffio"
664 | version = "1.2.0"
665 | description = "Sniff out which async library your code is running under"
666 | category = "main"
667 | optional = false
668 | python-versions = ">=3.5"
669 |
670 | [[package]]
671 | name = "starlette"
672 | version = "0.19.1"
673 | description = "The little ASGI library that shines."
674 | category = "main"
675 | optional = false
676 | python-versions = ">=3.6"
677 |
678 | [package.dependencies]
679 | anyio = ">=3.4.0,<5"
680 | typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""}
681 |
682 | [package.extras]
683 | full = ["itsdangerous", "jinja2", "python-multipart", "pyyaml", "requests"]
684 |
685 | [[package]]
686 | name = "toml"
687 | version = "0.10.2"
688 | description = "Python Library for Tom's Obvious, Minimal Language"
689 | category = "main"
690 | optional = false
691 | python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
692 |
693 | [[package]]
694 | name = "typing-extensions"
695 | version = "4.3.0"
696 | description = "Backported and Experimental Type Hints for Python 3.7+"
697 | category = "main"
698 | optional = false
699 | python-versions = ">=3.7"
700 |
701 | [[package]]
702 | name = "uritemplate"
703 | version = "4.1.1"
704 | description = "Implementation of RFC 6570 URI Templates"
705 | category = "main"
706 | optional = false
707 | python-versions = ">=3.6"
708 |
709 | [[package]]
710 | name = "urllib3"
711 | version = "1.26.12"
712 | description = "HTTP library with thread-safe connection pooling, file post, and more."
713 | category = "main"
714 | optional = false
715 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, <4"
716 |
717 | [package.extras]
718 | brotli = ["brotlicffi (>=0.8.0)", "brotli (>=1.0.9)", "brotlipy (>=0.6.0)"]
719 | secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "urllib3-secure-extra", "ipaddress"]
720 | socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
721 |
722 | [[package]]
723 | name = "uvicorn"
724 | version = "0.18.3"
725 | description = "The lightning-fast ASGI server."
726 | category = "main"
727 | optional = false
728 | python-versions = ">=3.7"
729 |
730 | [package.dependencies]
731 | click = ">=7.0"
732 | colorama = {version = ">=0.4", optional = true, markers = "sys_platform == \"win32\" and extra == \"standard\""}
733 | h11 = ">=0.8"
734 | httptools = {version = ">=0.4.0", optional = true, markers = "extra == \"standard\""}
735 | python-dotenv = {version = ">=0.13", optional = true, markers = "extra == \"standard\""}
736 | pyyaml = {version = ">=5.1", optional = true, markers = "extra == \"standard\""}
737 | 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\""}
738 | watchfiles = {version = ">=0.13", optional = true, markers = "extra == \"standard\""}
739 | websockets = {version = ">=10.0", optional = true, markers = "extra == \"standard\""}
740 |
741 | [package.extras]
742 | standard = ["colorama (>=0.4)", "httptools (>=0.4.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.0)"]
743 |
744 | [[package]]
745 | name = "uvloop"
746 | version = "0.16.0"
747 | description = "Fast implementation of asyncio event loop on top of libuv"
748 | category = "main"
749 | optional = false
750 | python-versions = ">=3.7"
751 |
752 | [package.extras]
753 | 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)"]
754 | 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)"]
755 | 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)"]
756 |
757 | [[package]]
758 | name = "watchfiles"
759 | version = "0.16.1"
760 | description = "Simple, modern and high performance file watching and code reload in python."
761 | category = "main"
762 | optional = false
763 | python-versions = ">=3.7"
764 |
765 | [package.dependencies]
766 | anyio = ">=3.0.0,<4"
767 |
768 | [[package]]
769 | name = "wcwidth"
770 | version = "0.2.5"
771 | description = "Measures the displayed width of unicode strings in a terminal"
772 | category = "dev"
773 | optional = false
774 | python-versions = "*"
775 |
776 | [[package]]
777 | name = "websockets"
778 | version = "10.3"
779 | description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)"
780 | category = "main"
781 | optional = false
782 | python-versions = ">=3.7"
783 |
784 | [[package]]
785 | name = "win32-setctime"
786 | version = "1.1.0"
787 | description = "A small Python utility to set file creation time on Windows"
788 | category = "main"
789 | optional = false
790 | python-versions = ">=3.5"
791 |
792 | [package.extras]
793 | dev = ["pytest (>=4.6.2)", "black (>=19.3b0)"]
794 |
795 | [[package]]
796 | name = "yarl"
797 | version = "1.8.1"
798 | description = "Yet another URL library"
799 | category = "main"
800 | optional = false
801 | python-versions = ">=3.7"
802 |
803 | [package.dependencies]
804 | idna = ">=2.0"
805 | multidict = ">=4.0"
806 |
807 | [metadata]
808 | lock-version = "1.1"
809 | python-versions = "^3.8"
810 | content-hash = "80b6f6dbd6d9189421cc98f0de9c75631ec1104bfcf56f7f2eaaec68a9326681"
811 |
812 | [metadata.files]
813 | anyio = [
814 | {file = "anyio-3.6.1-py3-none-any.whl", hash = "sha256:cb29b9c70620506a9a8f87a309591713446953302d7d995344d0d7c6c0c9a7be"},
815 | {file = "anyio-3.6.1.tar.gz", hash = "sha256:413adf95f93886e442aea925f3ee43baa5a765a64a0f52c6081894f9992fdd0b"},
816 | ]
817 | atomicwrites = []
818 | attrs = []
819 | beanie = []
820 | cachecontrol = [
821 | {file = "CacheControl-0.12.11-py2.py3-none-any.whl", hash = "sha256:2c75d6a8938cb1933c75c50184549ad42728a27e9f6b92fd677c3151aa72555b"},
822 | {file = "CacheControl-0.12.11.tar.gz", hash = "sha256:a5b9fcc986b184db101aa280b42ecdcdfc524892596f606858e0b7a8b4d9e144"},
823 | ]
824 | cachetools = [
825 | {file = "cachetools-5.2.0-py3-none-any.whl", hash = "sha256:f9f17d2aec496a9aa6b76f53e3b614c965223c061982d434d160f930c698a9db"},
826 | {file = "cachetools-5.2.0.tar.gz", hash = "sha256:6a94c6402995a99c3970cc7e4884bb60b4a8639938157eeed436098bf9831757"},
827 | ]
828 | certifi = []
829 | charset-normalizer = []
830 | click = [
831 | {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"},
832 | {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"},
833 | ]
834 | colorama = []
835 | dnspython = [
836 | {file = "dnspython-2.2.1-py3-none-any.whl", hash = "sha256:a851e51367fb93e9e1361732c1d60dab63eff98712e503ea7d92e6eccb109b4f"},
837 | {file = "dnspython-2.2.1.tar.gz", hash = "sha256:0f7569a4a6ff151958b64304071d370daa3243d15941a7beedf0c9fe5105603e"},
838 | ]
839 | email-validator = [
840 | {file = "email_validator-1.2.1-py2.py3-none-any.whl", hash = "sha256:c8589e691cf73eb99eed8d10ce0e9cbb05a0886ba920c8bcb7c82873f4c5789c"},
841 | {file = "email_validator-1.2.1.tar.gz", hash = "sha256:6757aea012d40516357c0ac2b1a4c31219ab2f899d26831334c5d069e8b6c3d8"},
842 | ]
843 | fastapi = []
844 | firebase-admin = []
845 | google-api-core = [
846 | {file = "google-api-core-2.8.2.tar.gz", hash = "sha256:06f7244c640322b508b125903bb5701bebabce8832f85aba9335ec00b3d02edc"},
847 | {file = "google_api_core-2.8.2-py3-none-any.whl", hash = "sha256:93c6a91ccac79079ac6bbf8b74ee75db970cc899278b97d53bc012f35908cf50"},
848 | ]
849 | google-api-python-client = []
850 | google-auth = []
851 | google-auth-httplib2 = [
852 | {file = "google-auth-httplib2-0.1.0.tar.gz", hash = "sha256:a07c39fd632becacd3f07718dfd6021bf396978f03ad3ce4321d060015cc30ac"},
853 | {file = "google_auth_httplib2-0.1.0-py2.py3-none-any.whl", hash = "sha256:31e49c36c6b5643b57e82617cb3e021e3e1d2df9da63af67252c02fa9c1f4a10"},
854 | ]
855 | google-cloud-core = []
856 | google-cloud-firestore = []
857 | google-cloud-storage = []
858 | google-crc32c = [
859 | {file = "google-crc32c-1.3.0.tar.gz", hash = "sha256:276de6273eb074a35bc598f8efbc00c7869c5cf2e29c90748fccc8c898c244df"},
860 | {file = "google_crc32c-1.3.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cb6994fff247987c66a8a4e550ef374671c2b82e3c0d2115e689d21e511a652d"},
861 | {file = "google_crc32c-1.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c9da0a39b53d2fab3e5467329ed50e951eb91386e9d0d5b12daf593973c3b168"},
862 | {file = "google_crc32c-1.3.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:eb0b14523758e37802f27b7f8cd973f5f3d33be7613952c0df904b68c4842f0e"},
863 | {file = "google_crc32c-1.3.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:95c68a4b9b7828ba0428f8f7e3109c5d476ca44996ed9a5f8aac6269296e2d59"},
864 | {file = "google_crc32c-1.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c3cf890c3c0ecfe1510a452a165431b5831e24160c5fcf2071f0f85ca5a47cd"},
865 | {file = "google_crc32c-1.3.0-cp310-cp310-win32.whl", hash = "sha256:3bbce1be3687bbfebe29abdb7631b83e6b25da3f4e1856a1611eb21854b689ea"},
866 | {file = "google_crc32c-1.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:c124b8c8779bf2d35d9b721e52d4adb41c9bfbde45e6a3f25f0820caa9aba73f"},
867 | {file = "google_crc32c-1.3.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:42ae4781333e331a1743445931b08ebdad73e188fd554259e772556fc4937c48"},
868 | {file = "google_crc32c-1.3.0-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:ff71073ebf0e42258a42a0b34f2c09ec384977e7f6808999102eedd5b49920e3"},
869 | {file = "google_crc32c-1.3.0-cp36-cp36m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:fe31de3002e7b08eb20823b3735b97c86c5926dd0581c7710a680b418a8709d4"},
870 | {file = "google_crc32c-1.3.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd7760a88a8d3d705ff562aa93f8445ead54f58fd482e4f9e2bafb7e177375d4"},
871 | {file = "google_crc32c-1.3.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:a0b9e622c3b2b8d0ce32f77eba617ab0d6768b82836391e4f8f9e2074582bf02"},
872 | {file = "google_crc32c-1.3.0-cp36-cp36m-win32.whl", hash = "sha256:779cbf1ce375b96111db98fca913c1f5ec11b1d870e529b1dc7354b2681a8c3a"},
873 | {file = "google_crc32c-1.3.0-cp36-cp36m-win_amd64.whl", hash = "sha256:04e7c220798a72fd0f08242bc8d7a05986b2a08a0573396187fd32c1dcdd58b3"},
874 | {file = "google_crc32c-1.3.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e7a539b9be7b9c00f11ef16b55486141bc2cdb0c54762f84e3c6fc091917436d"},
875 | {file = "google_crc32c-1.3.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:ca60076c388728d3b6ac3846842474f4250c91efbfe5afa872d3ffd69dd4b318"},
876 | {file = "google_crc32c-1.3.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:05340b60bf05b574159e9bd940152a47d38af3fb43803ffe71f11d704b7696a6"},
877 | {file = "google_crc32c-1.3.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:318f73f5484b5671f0c7f5f63741ab020a599504ed81d209b5c7129ee4667407"},
878 | {file = "google_crc32c-1.3.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:9f58099ad7affc0754ae42e6d87443299f15d739b0ce03c76f515153a5cda06c"},
879 | {file = "google_crc32c-1.3.0-cp37-cp37m-win32.whl", hash = "sha256:f52a4ad2568314ee713715b1e2d79ab55fab11e8b304fd1462ff5cccf4264b3e"},
880 | {file = "google_crc32c-1.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:bab4aebd525218bab4ee615786c4581952eadc16b1ff031813a2fd51f0cc7b08"},
881 | {file = "google_crc32c-1.3.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:dda4d8a3bb0b50f540f6ff4b6033f3a74e8bf0bd5320b70fab2c03e512a62812"},
882 | {file = "google_crc32c-1.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:fec221a051150eeddfdfcff162e6db92c65ecf46cb0f7bb1bf812a1520ec026b"},
883 | {file = "google_crc32c-1.3.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:226f2f9b8e128a6ca6a9af9b9e8384f7b53a801907425c9a292553a3a7218ce0"},
884 | {file = "google_crc32c-1.3.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a7f9cbea4245ee36190f85fe1814e2d7b1e5f2186381b082f5d59f99b7f11328"},
885 | {file = "google_crc32c-1.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a4db36f9721fdf391646685ecffa404eb986cbe007a3289499020daf72e88a2"},
886 | {file = "google_crc32c-1.3.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:12674a4c3b56b706153a358eaa1018c4137a5a04635b92b4652440d3d7386206"},
887 | {file = "google_crc32c-1.3.0-cp38-cp38-win32.whl", hash = "sha256:650e2917660e696041ab3dcd7abac160b4121cd9a484c08406f24c5964099829"},
888 | {file = "google_crc32c-1.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:58be56ae0529c664cc04a9c76e68bb92b091e0194d6e3c50bea7e0f266f73713"},
889 | {file = "google_crc32c-1.3.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:96a8918a78d5d64e07c8ea4ed2bc44354e3f93f46a4866a40e8db934e4c0d74b"},
890 | {file = "google_crc32c-1.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:13af315c3a0eec8bb8b8d80b8b128cb3fcd17d7e4edafc39647846345a3f003a"},
891 | {file = "google_crc32c-1.3.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6311853aa2bba4064d0c28ca54e7b50c4d48e3de04f6770f6c60ebda1e975267"},
892 | {file = "google_crc32c-1.3.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ed447680ff21c14aaceb6a9f99a5f639f583ccfe4ce1a5e1d48eb41c3d6b3217"},
893 | {file = "google_crc32c-1.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1c1d6236feab51200272d79b3d3e0f12cf2cbb12b208c835b175a21efdb0a73"},
894 | {file = "google_crc32c-1.3.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:e0f1ff55dde0ebcfbef027edc21f71c205845585fffe30d4ec4979416613e9b3"},
895 | {file = "google_crc32c-1.3.0-cp39-cp39-win32.whl", hash = "sha256:fbd60c6aaa07c31d7754edbc2334aef50601b7f1ada67a96eb1eb57c7c72378f"},
896 | {file = "google_crc32c-1.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:127f9cc3ac41b6a859bd9dc4321097b1a4f6aa7fdf71b4f9227b9e3ebffb4422"},
897 | {file = "google_crc32c-1.3.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:fc28e0db232c62ca0c3600884933178f0825c99be4474cdd645e378a10588125"},
898 | {file = "google_crc32c-1.3.0-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1926fd8de0acb9d15ee757175ce7242e235482a783cd4ec711cc999fc103c24e"},
899 | {file = "google_crc32c-1.3.0-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5da2c81575cc3ccf05d9830f9e8d3c70954819ca9a63828210498c0774fda1a3"},
900 | {file = "google_crc32c-1.3.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:891f712ce54e0d631370e1f4997b3f182f3368179198efc30d477c75d1f44942"},
901 | {file = "google_crc32c-1.3.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:7f6fe42536d9dcd3e2ffb9d3053f5d05221ae3bbcefbe472bdf2c71c793e3183"},
902 | ]
903 | google-resumable-media = [
904 | {file = "google-resumable-media-2.3.3.tar.gz", hash = "sha256:27c52620bd364d1c8116eaac4ea2afcbfb81ae9139fb3199652fcac1724bfb6c"},
905 | {file = "google_resumable_media-2.3.3-py2.py3-none-any.whl", hash = "sha256:5b52774ea7a829a8cdaa8bd2d4c3d4bc660c91b30857ab2668d0eb830f4ea8c5"},
906 | ]
907 | googleapis-common-protos = []
908 | grpcio = []
909 | grpcio-status = []
910 | gunicorn = []
911 | h11 = [
912 | {file = "h11-0.13.0-py3-none-any.whl", hash = "sha256:8ddd78563b633ca55346c8cd41ec0af27d3c79931828beffb46ce70a379e7442"},
913 | {file = "h11-0.13.0.tar.gz", hash = "sha256:70813c1135087a248a4d38cc0e1a0181ffab2188141a93eaf567940c3957ff06"},
914 | ]
915 | httplib2 = [
916 | {file = "httplib2-0.20.4-py3-none-any.whl", hash = "sha256:8b6a905cb1c79eefd03f8669fd993c36dc341f7c558f056cb5a33b5c2f458543"},
917 | {file = "httplib2-0.20.4.tar.gz", hash = "sha256:58a98e45b4b1a48273073f905d2961666ecf0fbac4250ea5b47aef259eb5c585"},
918 | ]
919 | httptools = [
920 | {file = "httptools-0.4.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fcddfe70553be717d9745990dfdb194e22ee0f60eb8f48c0794e7bfeda30d2d5"},
921 | {file = "httptools-0.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1ee0b459257e222b878a6c09ccf233957d3a4dcb883b0847640af98d2d9aac23"},
922 | {file = "httptools-0.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ceafd5e960b39c7e0d160a1936b68eb87c5e79b3979d66e774f0c77d4d8faaed"},
923 | {file = "httptools-0.4.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:fdb9f9ed79bc6f46b021b3319184699ba1a22410a82204e6e89c774530069683"},
924 | {file = "httptools-0.4.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:abe829275cdd4174b4c4e65ad718715d449e308d59793bf3a931ee1bf7e7b86c"},
925 | {file = "httptools-0.4.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7af6bdbd21a2a25d6784f6d67f44f5df33ef39b6159543b9f9064d365c01f919"},
926 | {file = "httptools-0.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:5d1fe6b6661022fd6cac541f54a4237496b246e6f1c0a6b41998ee08a1135afe"},
927 | {file = "httptools-0.4.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:48e48530d9b995a84d1d89ae6b3ec4e59ea7d494b150ac3bbc5e2ac4acce92cd"},
928 | {file = "httptools-0.4.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a113789e53ac1fa26edf99856a61e4c493868e125ae0dd6354cf518948fbbd5c"},
929 | {file = "httptools-0.4.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8e2eb957787cbb614a0f006bfc5798ff1d90ac7c4dd24854c84edbdc8c02369e"},
930 | {file = "httptools-0.4.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:7ee9f226acab9085037582c059d66769862706e8e8cd2340470ceb8b3850873d"},
931 | {file = "httptools-0.4.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:701e66b59dd21a32a274771238025d58db7e2b6ecebbab64ceff51b8e31527ae"},
932 | {file = "httptools-0.4.0-cp36-cp36m-win_amd64.whl", hash = "sha256:6a1a7dfc1f9c78a833e2c4904757a0f47ce25d08634dd2a52af394eefe5f9777"},
933 | {file = "httptools-0.4.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:903f739c9fb78dab8970b0f3ea51f21955b24b45afa77b22ff0e172fc11ef111"},
934 | {file = "httptools-0.4.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54bbd295f031b866b9799dd39cb45deee81aca036c9bff9f58ca06726f6494f1"},
935 | {file = "httptools-0.4.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3194f6d6443befa8d4db16c1946b2fc428a3ceb8ab32eb6f09a59f86104dc1a0"},
936 | {file = "httptools-0.4.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:cd1295f52971097f757edfbfce827b6dbbfb0f7a74901ee7d4933dff5ad4c9af"},
937 | {file = "httptools-0.4.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:20a45bcf22452a10fa8d58b7dbdb474381f6946bf5b8933e3662d572bc61bae4"},
938 | {file = "httptools-0.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d1f27bb0f75bef722d6e22dc609612bfa2f994541621cd2163f8c943b6463dfe"},
939 | {file = "httptools-0.4.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:7f7bfb74718f52d5ed47d608d507bf66d3bc01d4a8b3e6dd7134daaae129357b"},
940 | {file = "httptools-0.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a522d12e2ddbc2e91842ffb454a1aeb0d47607972c7d8fc88bd0838d97fb8a2a"},
941 | {file = "httptools-0.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2db44a0b294d317199e9f80123e72c6b005c55b625b57fae36de68670090fa48"},
942 | {file = "httptools-0.4.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c286985b5e194ca0ebb2908d71464b9be8f17cc66d6d3e330e8d5407248f56ad"},
943 | {file = "httptools-0.4.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:d3a4e165ca6204f34856b765d515d558dc84f1352033b8721e8d06c3e44930c3"},
944 | {file = "httptools-0.4.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:72aa3fbe636b16d22e04b5a9d24711b043495e0ecfe58080addf23a1a37f3409"},
945 | {file = "httptools-0.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:9967d9758df505975913304c434cb9ab21e2c609ad859eb921f2f615a038c8de"},
946 | {file = "httptools-0.4.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f72b5d24d6730035128b238decdc4c0f2104b7056a7ca55cf047c106842ec890"},
947 | {file = "httptools-0.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:29bf97a5c532da9c7a04de2c7a9c31d1d54f3abd65a464119b680206bbbb1055"},
948 | {file = "httptools-0.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:98993805f1e3cdb53de4eed02b55dcc953cdf017ba7bbb2fd89226c086a6d855"},
949 | {file = "httptools-0.4.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d9b90bf58f3ba04e60321a23a8723a1ff2a9377502535e70495e5ada8e6e6722"},
950 | {file = "httptools-0.4.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:1a99346ebcb801b213c591540837340bdf6fd060a8687518d01c607d338b7424"},
951 | {file = "httptools-0.4.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:645373c070080e632480a3d251d892cb795be3d3a15f86975d0f1aca56fd230d"},
952 | {file = "httptools-0.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:34d2903dd2a3dd85d33705b6fde40bf91fc44411661283763fd0746723963c83"},
953 | {file = "httptools-0.4.0.tar.gz", hash = "sha256:2c9a930c378b3d15d6b695fb95ebcff81a7395b4f9775c4f10a076beb0b2c1ff"},
954 | ]
955 | idna = [
956 | {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"},
957 | {file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"},
958 | ]
959 | loguru = [
960 | {file = "loguru-0.6.0-py3-none-any.whl", hash = "sha256:4e2414d534a2ab57573365b3e6d0234dfb1d84b68b7f3b948e6fb743860a77c3"},
961 | {file = "loguru-0.6.0.tar.gz", hash = "sha256:066bd06758d0a513e9836fd9c6b5a75bfb3fd36841f4b996bc60b547a309d41c"},
962 | ]
963 | more-itertools = []
964 | motor = [
965 | {file = "motor-3.0.0-py3-none-any.whl", hash = "sha256:b076de44970f518177f0eeeda8b183f52eafa557775bfe3294e93bda18867a71"},
966 | {file = "motor-3.0.0.tar.gz", hash = "sha256:3e36d29406c151b61342e6a8fa5e90c00c4723b76e30f11276a4373ea2064b7d"},
967 | ]
968 | msgpack = [
969 | {file = "msgpack-1.0.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:4ab251d229d10498e9a2f3b1e68ef64cb393394ec477e3370c457f9430ce9250"},
970 | {file = "msgpack-1.0.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:112b0f93202d7c0fef0b7810d465fde23c746a2d482e1e2de2aafd2ce1492c88"},
971 | {file = "msgpack-1.0.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:002b5c72b6cd9b4bafd790f364b8480e859b4712e91f43014fe01e4f957b8467"},
972 | {file = "msgpack-1.0.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:35bc0faa494b0f1d851fd29129b2575b2e26d41d177caacd4206d81502d4c6a6"},
973 | {file = "msgpack-1.0.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4733359808c56d5d7756628736061c432ded018e7a1dff2d35a02439043321aa"},
974 | {file = "msgpack-1.0.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb514ad14edf07a1dbe63761fd30f89ae79b42625731e1ccf5e1f1092950eaa6"},
975 | {file = "msgpack-1.0.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c23080fdeec4716aede32b4e0ef7e213c7b1093eede9ee010949f2a418ced6ba"},
976 | {file = "msgpack-1.0.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:49565b0e3d7896d9ea71d9095df15b7f75a035c49be733051c34762ca95bbf7e"},
977 | {file = "msgpack-1.0.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:aca0f1644d6b5a73eb3e74d4d64d5d8c6c3d577e753a04c9e9c87d07692c58db"},
978 | {file = "msgpack-1.0.4-cp310-cp310-win32.whl", hash = "sha256:0dfe3947db5fb9ce52aaea6ca28112a170db9eae75adf9339a1aec434dc954ef"},
979 | {file = "msgpack-1.0.4-cp310-cp310-win_amd64.whl", hash = "sha256:4dea20515f660aa6b7e964433b1808d098dcfcabbebeaaad240d11f909298075"},
980 | {file = "msgpack-1.0.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:e83f80a7fec1a62cf4e6c9a660e39c7f878f603737a0cdac8c13131d11d97f52"},
981 | {file = "msgpack-1.0.4-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c11a48cf5e59026ad7cb0dc29e29a01b5a66a3e333dc11c04f7e991fc5510a9"},
982 | {file = "msgpack-1.0.4-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1276e8f34e139aeff1c77a3cefb295598b504ac5314d32c8c3d54d24fadb94c9"},
983 | {file = "msgpack-1.0.4-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c9566f2c39ccced0a38d37c26cc3570983b97833c365a6044edef3574a00c08"},
984 | {file = "msgpack-1.0.4-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:fcb8a47f43acc113e24e910399376f7277cf8508b27e5b88499f053de6b115a8"},
985 | {file = "msgpack-1.0.4-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:76ee788122de3a68a02ed6f3a16bbcd97bc7c2e39bd4d94be2f1821e7c4a64e6"},
986 | {file = "msgpack-1.0.4-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:0a68d3ac0104e2d3510de90a1091720157c319ceeb90d74f7b5295a6bee51bae"},
987 | {file = "msgpack-1.0.4-cp36-cp36m-win32.whl", hash = "sha256:85f279d88d8e833ec015650fd15ae5eddce0791e1e8a59165318f371158efec6"},
988 | {file = "msgpack-1.0.4-cp36-cp36m-win_amd64.whl", hash = "sha256:c1683841cd4fa45ac427c18854c3ec3cd9b681694caf5bff04edb9387602d661"},
989 | {file = "msgpack-1.0.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a75dfb03f8b06f4ab093dafe3ddcc2d633259e6c3f74bb1b01996f5d8aa5868c"},
990 | {file = "msgpack-1.0.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9667bdfdf523c40d2511f0e98a6c9d3603be6b371ae9a238b7ef2dc4e7a427b0"},
991 | {file = "msgpack-1.0.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11184bc7e56fd74c00ead4f9cc9a3091d62ecb96e97653add7a879a14b003227"},
992 | {file = "msgpack-1.0.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ac5bd7901487c4a1dd51a8c58f2632b15d838d07ceedaa5e4c080f7190925bff"},
993 | {file = "msgpack-1.0.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1e91d641d2bfe91ba4c52039adc5bccf27c335356055825c7f88742c8bb900dd"},
994 | {file = "msgpack-1.0.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2a2df1b55a78eb5f5b7d2a4bb221cd8363913830145fad05374a80bf0877cb1e"},
995 | {file = "msgpack-1.0.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:545e3cf0cf74f3e48b470f68ed19551ae6f9722814ea969305794645da091236"},
996 | {file = "msgpack-1.0.4-cp37-cp37m-win32.whl", hash = "sha256:2cc5ca2712ac0003bcb625c96368fd08a0f86bbc1a5578802512d87bc592fe44"},
997 | {file = "msgpack-1.0.4-cp37-cp37m-win_amd64.whl", hash = "sha256:eba96145051ccec0ec86611fe9cf693ce55f2a3ce89c06ed307de0e085730ec1"},
998 | {file = "msgpack-1.0.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:7760f85956c415578c17edb39eed99f9181a48375b0d4a94076d84148cf67b2d"},
999 | {file = "msgpack-1.0.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:449e57cc1ff18d3b444eb554e44613cffcccb32805d16726a5494038c3b93dab"},
1000 | {file = "msgpack-1.0.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d603de2b8d2ea3f3bcb2efe286849aa7a81531abc52d8454da12f46235092bcb"},
1001 | {file = "msgpack-1.0.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48f5d88c99f64c456413d74a975bd605a9b0526293218a3b77220a2c15458ba9"},
1002 | {file = "msgpack-1.0.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6916c78f33602ecf0509cc40379271ba0f9ab572b066bd4bdafd7434dee4bc6e"},
1003 | {file = "msgpack-1.0.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:81fc7ba725464651190b196f3cd848e8553d4d510114a954681fd0b9c479d7e1"},
1004 | {file = "msgpack-1.0.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:d5b5b962221fa2c5d3a7f8133f9abffc114fe218eb4365e40f17732ade576c8e"},
1005 | {file = "msgpack-1.0.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:77ccd2af37f3db0ea59fb280fa2165bf1b096510ba9fe0cc2bf8fa92a22fdb43"},
1006 | {file = "msgpack-1.0.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b17be2478b622939e39b816e0aa8242611cc8d3583d1cd8ec31b249f04623243"},
1007 | {file = "msgpack-1.0.4-cp38-cp38-win32.whl", hash = "sha256:2bb8cdf50dd623392fa75525cce44a65a12a00c98e1e37bf0fb08ddce2ff60d2"},
1008 | {file = "msgpack-1.0.4-cp38-cp38-win_amd64.whl", hash = "sha256:26b8feaca40a90cbe031b03d82b2898bf560027160d3eae1423f4a67654ec5d6"},
1009 | {file = "msgpack-1.0.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:462497af5fd4e0edbb1559c352ad84f6c577ffbbb708566a0abaaa84acd9f3ae"},
1010 | {file = "msgpack-1.0.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2999623886c5c02deefe156e8f869c3b0aaeba14bfc50aa2486a0415178fce55"},
1011 | {file = "msgpack-1.0.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f0029245c51fd9473dc1aede1160b0a29f4a912e6b1dd353fa6d317085b219da"},
1012 | {file = "msgpack-1.0.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed6f7b854a823ea44cf94919ba3f727e230da29feb4a99711433f25800cf747f"},
1013 | {file = "msgpack-1.0.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0df96d6eaf45ceca04b3f3b4b111b86b33785683d682c655063ef8057d61fd92"},
1014 | {file = "msgpack-1.0.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6a4192b1ab40f8dca3f2877b70e63799d95c62c068c84dc028b40a6cb03ccd0f"},
1015 | {file = "msgpack-1.0.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0e3590f9fb9f7fbc36df366267870e77269c03172d086fa76bb4eba8b2b46624"},
1016 | {file = "msgpack-1.0.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:1576bd97527a93c44fa856770197dec00d223b0b9f36ef03f65bac60197cedf8"},
1017 | {file = "msgpack-1.0.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:63e29d6e8c9ca22b21846234913c3466b7e4ee6e422f205a2988083de3b08cae"},
1018 | {file = "msgpack-1.0.4-cp39-cp39-win32.whl", hash = "sha256:fb62ea4b62bfcb0b380d5680f9a4b3f9a2d166d9394e9bbd9666c0ee09a3645c"},
1019 | {file = "msgpack-1.0.4-cp39-cp39-win_amd64.whl", hash = "sha256:4d5834a2a48965a349da1c5a79760d94a1a0172fbb5ab6b5b33cbf8447e109ce"},
1020 | {file = "msgpack-1.0.4.tar.gz", hash = "sha256:f5d869c18f030202eb412f08b28d2afeea553d6613aee89e200d7aca7ef01f5f"},
1021 | ]
1022 | multidict = [
1023 | {file = "multidict-6.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b9e95a740109c6047602f4db4da9949e6c5945cefbad34a1299775ddc9a62e2"},
1024 | {file = "multidict-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ac0e27844758d7177989ce406acc6a83c16ed4524ebc363c1f748cba184d89d3"},
1025 | {file = "multidict-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:041b81a5f6b38244b34dc18c7b6aba91f9cdaf854d9a39e5ff0b58e2b5773b9c"},
1026 | {file = "multidict-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5fdda29a3c7e76a064f2477c9aab1ba96fd94e02e386f1e665bca1807fc5386f"},
1027 | {file = "multidict-6.0.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3368bf2398b0e0fcbf46d85795adc4c259299fec50c1416d0f77c0a843a3eed9"},
1028 | {file = "multidict-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f4f052ee022928d34fe1f4d2bc743f32609fb79ed9c49a1710a5ad6b2198db20"},
1029 | {file = "multidict-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:225383a6603c086e6cef0f2f05564acb4f4d5f019a4e3e983f572b8530f70c88"},
1030 | {file = "multidict-6.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50bd442726e288e884f7be9071016c15a8742eb689a593a0cac49ea093eef0a7"},
1031 | {file = "multidict-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:47e6a7e923e9cada7c139531feac59448f1f47727a79076c0b1ee80274cd8eee"},
1032 | {file = "multidict-6.0.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:0556a1d4ea2d949efe5fd76a09b4a82e3a4a30700553a6725535098d8d9fb672"},
1033 | {file = "multidict-6.0.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:626fe10ac87851f4cffecee161fc6f8f9853f0f6f1035b59337a51d29ff3b4f9"},
1034 | {file = "multidict-6.0.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:8064b7c6f0af936a741ea1efd18690bacfbae4078c0c385d7c3f611d11f0cf87"},
1035 | {file = "multidict-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2d36e929d7f6a16d4eb11b250719c39560dd70545356365b494249e2186bc389"},
1036 | {file = "multidict-6.0.2-cp310-cp310-win32.whl", hash = "sha256:fcb91630817aa8b9bc4a74023e4198480587269c272c58b3279875ed7235c293"},
1037 | {file = "multidict-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:8cbf0132f3de7cc6c6ce00147cc78e6439ea736cee6bca4f068bcf892b0fd658"},
1038 | {file = "multidict-6.0.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:05f6949d6169878a03e607a21e3b862eaf8e356590e8bdae4227eedadacf6e51"},
1039 | {file = "multidict-6.0.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2c2e459f7050aeb7c1b1276763364884595d47000c1cddb51764c0d8976e608"},
1040 | {file = "multidict-6.0.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d0509e469d48940147e1235d994cd849a8f8195e0bca65f8f5439c56e17872a3"},
1041 | {file = "multidict-6.0.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:514fe2b8d750d6cdb4712346a2c5084a80220821a3e91f3f71eec11cf8d28fd4"},
1042 | {file = "multidict-6.0.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:19adcfc2a7197cdc3987044e3f415168fc5dc1f720c932eb1ef4f71a2067e08b"},
1043 | {file = "multidict-6.0.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b9d153e7f1f9ba0b23ad1568b3b9e17301e23b042c23870f9ee0522dc5cc79e8"},
1044 | {file = "multidict-6.0.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:aef9cc3d9c7d63d924adac329c33835e0243b5052a6dfcbf7732a921c6e918ba"},
1045 | {file = "multidict-6.0.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:4571f1beddff25f3e925eea34268422622963cd8dc395bb8778eb28418248e43"},
1046 | {file = "multidict-6.0.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:d48b8ee1d4068561ce8033d2c344cf5232cb29ee1a0206a7b828c79cbc5982b8"},
1047 | {file = "multidict-6.0.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:45183c96ddf61bf96d2684d9fbaf6f3564d86b34cb125761f9a0ef9e36c1d55b"},
1048 | {file = "multidict-6.0.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:75bdf08716edde767b09e76829db8c1e5ca9d8bb0a8d4bd94ae1eafe3dac5e15"},
1049 | {file = "multidict-6.0.2-cp37-cp37m-win32.whl", hash = "sha256:a45e1135cb07086833ce969555df39149680e5471c04dfd6a915abd2fc3f6dbc"},
1050 | {file = "multidict-6.0.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6f3cdef8a247d1eafa649085812f8a310e728bdf3900ff6c434eafb2d443b23a"},
1051 | {file = "multidict-6.0.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0327292e745a880459ef71be14e709aaea2f783f3537588fb4ed09b6c01bca60"},
1052 | {file = "multidict-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e875b6086e325bab7e680e4316d667fc0e5e174bb5611eb16b3ea121c8951b86"},
1053 | {file = "multidict-6.0.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:feea820722e69451743a3d56ad74948b68bf456984d63c1a92e8347b7b88452d"},
1054 | {file = "multidict-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cc57c68cb9139c7cd6fc39f211b02198e69fb90ce4bc4a094cf5fe0d20fd8b0"},
1055 | {file = "multidict-6.0.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:497988d6b6ec6ed6f87030ec03280b696ca47dbf0648045e4e1d28b80346560d"},
1056 | {file = "multidict-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:89171b2c769e03a953d5969b2f272efa931426355b6c0cb508022976a17fd376"},
1057 | {file = "multidict-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:684133b1e1fe91eda8fa7447f137c9490a064c6b7f392aa857bba83a28cfb693"},
1058 | {file = "multidict-6.0.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fd9fc9c4849a07f3635ccffa895d57abce554b467d611a5009ba4f39b78a8849"},
1059 | {file = "multidict-6.0.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e07c8e79d6e6fd37b42f3250dba122053fddb319e84b55dd3a8d6446e1a7ee49"},
1060 | {file = "multidict-6.0.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4070613ea2227da2bfb2c35a6041e4371b0af6b0be57f424fe2318b42a748516"},
1061 | {file = "multidict-6.0.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:47fbeedbf94bed6547d3aa632075d804867a352d86688c04e606971595460227"},
1062 | {file = "multidict-6.0.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:5774d9218d77befa7b70d836004a768fb9aa4fdb53c97498f4d8d3f67bb9cfa9"},
1063 | {file = "multidict-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2957489cba47c2539a8eb7ab32ff49101439ccf78eab724c828c1a54ff3ff98d"},
1064 | {file = "multidict-6.0.2-cp38-cp38-win32.whl", hash = "sha256:e5b20e9599ba74391ca0cfbd7b328fcc20976823ba19bc573983a25b32e92b57"},
1065 | {file = "multidict-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:8004dca28e15b86d1b1372515f32eb6f814bdf6f00952699bdeb541691091f96"},
1066 | {file = "multidict-6.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2e4a0785b84fb59e43c18a015ffc575ba93f7d1dbd272b4cdad9f5134b8a006c"},
1067 | {file = "multidict-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6701bf8a5d03a43375909ac91b6980aea74b0f5402fbe9428fc3f6edf5d9677e"},
1068 | {file = "multidict-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a007b1638e148c3cfb6bf0bdc4f82776cef0ac487191d093cdc316905e504071"},
1069 | {file = "multidict-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:07a017cfa00c9890011628eab2503bee5872f27144936a52eaab449be5eaf032"},
1070 | {file = "multidict-6.0.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c207fff63adcdf5a485969131dc70e4b194327666b7e8a87a97fbc4fd80a53b2"},
1071 | {file = "multidict-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:373ba9d1d061c76462d74e7de1c0c8e267e9791ee8cfefcf6b0b2495762c370c"},
1072 | {file = "multidict-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfba7c6d5d7c9099ba21f84662b037a0ffd4a5e6b26ac07d19e423e6fdf965a9"},
1073 | {file = "multidict-6.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:19d9bad105dfb34eb539c97b132057a4e709919ec4dd883ece5838bcbf262b80"},
1074 | {file = "multidict-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:de989b195c3d636ba000ee4281cd03bb1234635b124bf4cd89eeee9ca8fcb09d"},
1075 | {file = "multidict-6.0.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7c40b7bbece294ae3a87c1bc2abff0ff9beef41d14188cda94ada7bcea99b0fb"},
1076 | {file = "multidict-6.0.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:d16cce709ebfadc91278a1c005e3c17dd5f71f5098bfae1035149785ea6e9c68"},
1077 | {file = "multidict-6.0.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:a2c34a93e1d2aa35fbf1485e5010337c72c6791407d03aa5f4eed920343dd360"},
1078 | {file = "multidict-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:feba80698173761cddd814fa22e88b0661e98cb810f9f986c54aa34d281e4937"},
1079 | {file = "multidict-6.0.2-cp39-cp39-win32.whl", hash = "sha256:23b616fdc3c74c9fe01d76ce0d1ce872d2d396d8fa8e4899398ad64fb5aa214a"},
1080 | {file = "multidict-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:4bae31803d708f6f15fd98be6a6ac0b6958fcf68fda3c77a048a4f9073704aae"},
1081 | {file = "multidict-6.0.2.tar.gz", hash = "sha256:5ff3bd75f38e4c43f1f470f2df7a4d430b821c4ce22be384e1459cb57d6bb013"},
1082 | ]
1083 | packaging = [
1084 | {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"},
1085 | {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"},
1086 | ]
1087 | pluggy = [
1088 | {file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"},
1089 | {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"},
1090 | ]
1091 | proto-plus = []
1092 | protobuf = []
1093 | py = [
1094 | {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"},
1095 | {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"},
1096 | ]
1097 | pyasn1 = [
1098 | {file = "pyasn1-0.4.8-py2.4.egg", hash = "sha256:fec3e9d8e36808a28efb59b489e4528c10ad0f480e57dcc32b4de5c9d8c9fdf3"},
1099 | {file = "pyasn1-0.4.8-py2.5.egg", hash = "sha256:0458773cfe65b153891ac249bcf1b5f8f320b7c2ce462151f8fa74de8934becf"},
1100 | {file = "pyasn1-0.4.8-py2.6.egg", hash = "sha256:5c9414dcfede6e441f7e8f81b43b34e834731003427e5b09e4e00e3172a10f00"},
1101 | {file = "pyasn1-0.4.8-py2.7.egg", hash = "sha256:6e7545f1a61025a4e58bb336952c5061697da694db1cae97b116e9c46abcf7c8"},
1102 | {file = "pyasn1-0.4.8-py2.py3-none-any.whl", hash = "sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d"},
1103 | {file = "pyasn1-0.4.8-py3.1.egg", hash = "sha256:78fa6da68ed2727915c4767bb386ab32cdba863caa7dbe473eaae45f9959da86"},
1104 | {file = "pyasn1-0.4.8-py3.2.egg", hash = "sha256:08c3c53b75eaa48d71cf8c710312316392ed40899cb34710d092e96745a358b7"},
1105 | {file = "pyasn1-0.4.8-py3.3.egg", hash = "sha256:03840c999ba71680a131cfaee6fab142e1ed9bbd9c693e285cc6aca0d555e576"},
1106 | {file = "pyasn1-0.4.8-py3.4.egg", hash = "sha256:7ab8a544af125fb704feadb008c99a88805126fb525280b2270bb25cc1d78a12"},
1107 | {file = "pyasn1-0.4.8-py3.5.egg", hash = "sha256:e89bf84b5437b532b0803ba5c9a5e054d21fec423a89952a74f87fa2c9b7bce2"},
1108 | {file = "pyasn1-0.4.8-py3.6.egg", hash = "sha256:014c0e9976956a08139dc0712ae195324a75e142284d5f87f1a87ee1b068a359"},
1109 | {file = "pyasn1-0.4.8-py3.7.egg", hash = "sha256:99fcc3c8d804d1bc6d9a099921e39d827026409a58f2a720dcdb89374ea0c776"},
1110 | {file = "pyasn1-0.4.8.tar.gz", hash = "sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba"},
1111 | ]
1112 | pyasn1-modules = [
1113 | {file = "pyasn1-modules-0.2.8.tar.gz", hash = "sha256:905f84c712230b2c592c19470d3ca8d552de726050d1d1716282a1f6146be65e"},
1114 | {file = "pyasn1_modules-0.2.8-py2.4.egg", hash = "sha256:0fe1b68d1e486a1ed5473f1302bd991c1611d319bba158e98b106ff86e1d7199"},
1115 | {file = "pyasn1_modules-0.2.8-py2.5.egg", hash = "sha256:fe0644d9ab041506b62782e92b06b8c68cca799e1a9636ec398675459e031405"},
1116 | {file = "pyasn1_modules-0.2.8-py2.6.egg", hash = "sha256:a99324196732f53093a84c4369c996713eb8c89d360a496b599fb1a9c47fc3eb"},
1117 | {file = "pyasn1_modules-0.2.8-py2.7.egg", hash = "sha256:0845a5582f6a02bb3e1bde9ecfc4bfcae6ec3210dd270522fee602365430c3f8"},
1118 | {file = "pyasn1_modules-0.2.8-py2.py3-none-any.whl", hash = "sha256:a50b808ffeb97cb3601dd25981f6b016cbb3d31fbf57a8b8a87428e6158d0c74"},
1119 | {file = "pyasn1_modules-0.2.8-py3.1.egg", hash = "sha256:f39edd8c4ecaa4556e989147ebf219227e2cd2e8a43c7e7fcb1f1c18c5fd6a3d"},
1120 | {file = "pyasn1_modules-0.2.8-py3.2.egg", hash = "sha256:b80486a6c77252ea3a3e9b1e360bc9cf28eaac41263d173c032581ad2f20fe45"},
1121 | {file = "pyasn1_modules-0.2.8-py3.3.egg", hash = "sha256:65cebbaffc913f4fe9e4808735c95ea22d7a7775646ab690518c056784bc21b4"},
1122 | {file = "pyasn1_modules-0.2.8-py3.4.egg", hash = "sha256:15b7c67fabc7fc240d87fb9aabf999cf82311a6d6fb2c70d00d3d0604878c811"},
1123 | {file = "pyasn1_modules-0.2.8-py3.5.egg", hash = "sha256:426edb7a5e8879f1ec54a1864f16b882c2837bfd06eee62f2c982315ee2473ed"},
1124 | {file = "pyasn1_modules-0.2.8-py3.6.egg", hash = "sha256:cbac4bc38d117f2a49aeedec4407d23e8866ea4ac27ff2cf7fb3e5b570df19e0"},
1125 | {file = "pyasn1_modules-0.2.8-py3.7.egg", hash = "sha256:c29a5e5cc7a3f05926aff34e097e84f8589cd790ce0ed41b67aed6857b26aafd"},
1126 | ]
1127 | pydantic = []
1128 | pymongo = []
1129 | pyparsing = [
1130 | {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"},
1131 | {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"},
1132 | ]
1133 | pytest = [
1134 | {file = "pytest-5.4.3-py3-none-any.whl", hash = "sha256:5c0db86b698e8f170ba4582a492248919255fcd4c79b1ee64ace34301fb589a1"},
1135 | {file = "pytest-5.4.3.tar.gz", hash = "sha256:7979331bfcba207414f5e1263b5a0f8f521d0f457318836a7355531ed1a4c7d8"},
1136 | ]
1137 | python-dotenv = [
1138 | {file = "python-dotenv-0.20.0.tar.gz", hash = "sha256:b7e3b04a59693c42c36f9ab1cc2acc46fa5df8c78e178fc33a8d4cd05c8d498f"},
1139 | {file = "python_dotenv-0.20.0-py3-none-any.whl", hash = "sha256:d92a187be61fe482e4fd675b6d52200e7be63a12b724abbf931a40ce4fa92938"},
1140 | ]
1141 | pyyaml = [
1142 | {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"},
1143 | {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"},
1144 | {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"},
1145 | {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"},
1146 | {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"},
1147 | {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"},
1148 | {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"},
1149 | {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"},
1150 | {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"},
1151 | {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"},
1152 | {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"},
1153 | {file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"},
1154 | {file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"},
1155 | {file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"},
1156 | {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"},
1157 | {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"},
1158 | {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"},
1159 | {file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"},
1160 | {file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"},
1161 | {file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"},
1162 | {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"},
1163 | {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"},
1164 | {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"},
1165 | {file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"},
1166 | {file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"},
1167 | {file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"},
1168 | {file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"},
1169 | {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"},
1170 | {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"},
1171 | {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"},
1172 | {file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"},
1173 | {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"},
1174 | {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"},
1175 | ]
1176 | requests = []
1177 | rsa = []
1178 | six = [
1179 | {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
1180 | {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
1181 | ]
1182 | sniffio = [
1183 | {file = "sniffio-1.2.0-py3-none-any.whl", hash = "sha256:471b71698eac1c2112a40ce2752bb2f4a4814c22a54a3eed3676bc0f5ca9f663"},
1184 | {file = "sniffio-1.2.0.tar.gz", hash = "sha256:c4666eecec1d3f50960c6bdf61ab7bc350648da6c126e3cf6898d8cd4ddcd3de"},
1185 | ]
1186 | starlette = [
1187 | {file = "starlette-0.19.1-py3-none-any.whl", hash = "sha256:5a60c5c2d051f3a8eb546136aa0c9399773a689595e099e0877704d5888279bf"},
1188 | {file = "starlette-0.19.1.tar.gz", hash = "sha256:c6d21096774ecb9639acad41b86b7706e52ba3bf1dc13ea4ed9ad593d47e24c7"},
1189 | ]
1190 | toml = [
1191 | {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
1192 | {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},
1193 | ]
1194 | typing-extensions = []
1195 | uritemplate = [
1196 | {file = "uritemplate-4.1.1-py2.py3-none-any.whl", hash = "sha256:830c08b8d99bdd312ea4ead05994a38e8936266f84b9a7878232db50b044e02e"},
1197 | {file = "uritemplate-4.1.1.tar.gz", hash = "sha256:4346edfc5c3b79f694bccd6d6099a322bbeb628dbf2cd86eea55a456ce5124f0"},
1198 | ]
1199 | urllib3 = []
1200 | uvicorn = []
1201 | uvloop = [
1202 | {file = "uvloop-0.16.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6224f1401025b748ffecb7a6e2652b17768f30b1a6a3f7b44660e5b5b690b12d"},
1203 | {file = "uvloop-0.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:30ba9dcbd0965f5c812b7c2112a1ddf60cf904c1c160f398e7eed3a6b82dcd9c"},
1204 | {file = "uvloop-0.16.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:bd53f7f5db562f37cd64a3af5012df8cac2c464c97e732ed556800129505bd64"},
1205 | {file = "uvloop-0.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:772206116b9b57cd625c8a88f2413df2fcfd0b496eb188b82a43bed7af2c2ec9"},
1206 | {file = "uvloop-0.16.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b572256409f194521a9895aef274cea88731d14732343da3ecdb175228881638"},
1207 | {file = "uvloop-0.16.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:04ff57aa137230d8cc968f03481176041ae789308b4d5079118331ab01112450"},
1208 | {file = "uvloop-0.16.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a19828c4f15687675ea912cc28bbcb48e9bb907c801873bd1519b96b04fb805"},
1209 | {file = "uvloop-0.16.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e814ac2c6f9daf4c36eb8e85266859f42174a4ff0d71b99405ed559257750382"},
1210 | {file = "uvloop-0.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bd8f42ea1ea8f4e84d265769089964ddda95eb2bb38b5cbe26712b0616c3edee"},
1211 | {file = "uvloop-0.16.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:647e481940379eebd314c00440314c81ea547aa636056f554d491e40503c8464"},
1212 | {file = "uvloop-0.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e0d26fa5875d43ddbb0d9d79a447d2ace4180d9e3239788208527c4784f7cab"},
1213 | {file = "uvloop-0.16.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:6ccd57ae8db17d677e9e06192e9c9ec4bd2066b77790f9aa7dede2cc4008ee8f"},
1214 | {file = "uvloop-0.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:089b4834fd299d82d83a25e3335372f12117a7d38525217c2258e9b9f4578897"},
1215 | {file = "uvloop-0.16.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98d117332cc9e5ea8dfdc2b28b0a23f60370d02e1395f88f40d1effd2cb86c4f"},
1216 | {file = "uvloop-0.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e5f2e2ff51aefe6c19ee98af12b4ae61f5be456cd24396953244a30880ad861"},
1217 | {file = "uvloop-0.16.0.tar.gz", hash = "sha256:f74bc20c7b67d1c27c72601c78cf95be99d5c2cdd4514502b4f3eb0933ff1228"},
1218 | ]
1219 | watchfiles = []
1220 | wcwidth = [
1221 | {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"},
1222 | {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"},
1223 | ]
1224 | websockets = [
1225 | {file = "websockets-10.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:661f641b44ed315556a2fa630239adfd77bd1b11cb0b9d96ed8ad90b0b1e4978"},
1226 | {file = "websockets-10.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b529fdfa881b69fe563dbd98acce84f3e5a67df13de415e143ef053ff006d500"},
1227 | {file = "websockets-10.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f351c7d7d92f67c0609329ab2735eee0426a03022771b00102816a72715bb00b"},
1228 | {file = "websockets-10.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:379e03422178436af4f3abe0aa8f401aa77ae2487843738542a75faf44a31f0c"},
1229 | {file = "websockets-10.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:e904c0381c014b914136c492c8fa711ca4cced4e9b3d110e5e7d436d0fc289e8"},
1230 | {file = "websockets-10.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e7e6f2d6fd48422071cc8a6f8542016f350b79cc782752de531577d35e9bd677"},
1231 | {file = "websockets-10.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b9c77f0d1436ea4b4dc089ed8335fa141e6a251a92f75f675056dac4ab47a71e"},
1232 | {file = "websockets-10.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e6fa05a680e35d0fcc1470cb070b10e6fe247af54768f488ed93542e71339d6f"},
1233 | {file = "websockets-10.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2f94fa3ae454a63ea3a19f73b95deeebc9f02ba2d5617ca16f0bbdae375cda47"},
1234 | {file = "websockets-10.3-cp310-cp310-win32.whl", hash = "sha256:6ed1d6f791eabfd9808afea1e068f5e59418e55721db8b7f3bfc39dc831c42ae"},
1235 | {file = "websockets-10.3-cp310-cp310-win_amd64.whl", hash = "sha256:347974105bbd4ea068106ec65e8e8ebd86f28c19e529d115d89bd8cc5cda3079"},
1236 | {file = "websockets-10.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:fab7c640815812ed5f10fbee7abbf58788d602046b7bb3af9b1ac753a6d5e916"},
1237 | {file = "websockets-10.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:994cdb1942a7a4c2e10098d9162948c9e7b235df755de91ca33f6e0481366fdb"},
1238 | {file = "websockets-10.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:aad5e300ab32036eb3fdc350ad30877210e2f51bceaca83fb7fef4d2b6c72b79"},
1239 | {file = "websockets-10.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e49ea4c1a9543d2bd8a747ff24411509c29e4bdcde05b5b0895e2120cb1a761d"},
1240 | {file = "websockets-10.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:6ea6b300a6bdd782e49922d690e11c3669828fe36fc2471408c58b93b5535a98"},
1241 | {file = "websockets-10.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:ef5ce841e102278c1c2e98f043db99d6755b1c58bde475516aef3a008ed7f28e"},
1242 | {file = "websockets-10.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d1655a6fc7aecd333b079d00fb3c8132d18988e47f19740c69303bf02e9883c6"},
1243 | {file = "websockets-10.3-cp37-cp37m-win32.whl", hash = "sha256:83e5ca0d5b743cde3d29fda74ccab37bdd0911f25bd4cdf09ff8b51b7b4f2fa1"},
1244 | {file = "websockets-10.3-cp37-cp37m-win_amd64.whl", hash = "sha256:da4377904a3379f0c1b75a965fff23b28315bcd516d27f99a803720dfebd94d4"},
1245 | {file = "websockets-10.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a1e15b230c3613e8ea82c9fc6941b2093e8eb939dd794c02754d33980ba81e36"},
1246 | {file = "websockets-10.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:31564a67c3e4005f27815634343df688b25705cccb22bc1db621c781ddc64c69"},
1247 | {file = "websockets-10.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c8d1d14aa0f600b5be363077b621b1b4d1eb3fbf90af83f9281cda668e6ff7fd"},
1248 | {file = "websockets-10.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8fbd7d77f8aba46d43245e86dd91a8970eac4fb74c473f8e30e9c07581f852b2"},
1249 | {file = "websockets-10.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:210aad7fdd381c52e58777560860c7e6110b6174488ef1d4b681c08b68bf7f8c"},
1250 | {file = "websockets-10.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6075fd24df23133c1b078e08a9b04a3bc40b31a8def4ee0b9f2c8865acce913e"},
1251 | {file = "websockets-10.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:7f6d96fdb0975044fdd7953b35d003b03f9e2bcf85f2d2cf86285ece53e9f991"},
1252 | {file = "websockets-10.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:c7250848ce69559756ad0086a37b82c986cd33c2d344ab87fea596c5ac6d9442"},
1253 | {file = "websockets-10.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:28dd20b938a57c3124028680dc1600c197294da5db4292c76a0b48efb3ed7f76"},
1254 | {file = "websockets-10.3-cp38-cp38-win32.whl", hash = "sha256:54c000abeaff6d8771a4e2cef40900919908ea7b6b6a30eae72752607c6db559"},
1255 | {file = "websockets-10.3-cp38-cp38-win_amd64.whl", hash = "sha256:7ab36e17af592eec5747c68ef2722a74c1a4a70f3772bc661079baf4ae30e40d"},
1256 | {file = "websockets-10.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a141de3d5a92188234afa61653ed0bbd2dde46ad47b15c3042ffb89548e77094"},
1257 | {file = "websockets-10.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:97bc9d41e69a7521a358f9b8e44871f6cdeb42af31815c17aed36372d4eec667"},
1258 | {file = "websockets-10.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d6353ba89cfc657a3f5beabb3b69be226adbb5c6c7a66398e17809b0ce3c4731"},
1259 | {file = "websockets-10.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec2b0ab7edc8cd4b0eb428b38ed89079bdc20c6bdb5f889d353011038caac2f9"},
1260 | {file = "websockets-10.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:85506b3328a9e083cc0a0fb3ba27e33c8db78341b3eb12eb72e8afd166c36680"},
1261 | {file = "websockets-10.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8af75085b4bc0b5c40c4a3c0e113fa95e84c60f4ed6786cbb675aeb1ee128247"},
1262 | {file = "websockets-10.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:07cdc0a5b2549bcfbadb585ad8471ebdc7bdf91e32e34ae3889001c1c106a6af"},
1263 | {file = "websockets-10.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:5b936bf552e4f6357f5727579072ff1e1324717902127ffe60c92d29b67b7be3"},
1264 | {file = "websockets-10.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e4e08305bfd76ba8edab08dcc6496f40674f44eb9d5e23153efa0a35750337e8"},
1265 | {file = "websockets-10.3-cp39-cp39-win32.whl", hash = "sha256:bb621ec2dbbbe8df78a27dbd9dd7919f9b7d32a73fafcb4d9252fc4637343582"},
1266 | {file = "websockets-10.3-cp39-cp39-win_amd64.whl", hash = "sha256:51695d3b199cd03098ae5b42833006a0f43dc5418d3102972addc593a783bc02"},
1267 | {file = "websockets-10.3-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:907e8247480f287aa9bbc9391bd6de23c906d48af54c8c421df84655eef66af7"},
1268 | {file = "websockets-10.3-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b1359aba0ff810d5830d5ab8e2c4a02bebf98a60aa0124fb29aa78cfdb8031f"},
1269 | {file = "websockets-10.3-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:93d5ea0b5da8d66d868b32c614d2b52d14304444e39e13a59566d4acb8d6e2e4"},
1270 | {file = "websockets-10.3-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7934e055fd5cd9dee60f11d16c8d79c4567315824bacb1246d0208a47eca9755"},
1271 | {file = "websockets-10.3-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:3eda1cb7e9da1b22588cefff09f0951771d6ee9fa8dbe66f5ae04cc5f26b2b55"},
1272 | {file = "websockets-10.3.tar.gz", hash = "sha256:fc06cc8073c8e87072138ba1e431300e2d408f054b27047d047b549455066ff4"},
1273 | ]
1274 | win32-setctime = [
1275 | {file = "win32_setctime-1.1.0-py3-none-any.whl", hash = "sha256:231db239e959c2fe7eb1d7dc129f11172354f98361c4fa2d6d2d7e278baa8aad"},
1276 | {file = "win32_setctime-1.1.0.tar.gz", hash = "sha256:15cf5750465118d6929ae4de4eb46e8edae9a5634350c01ba582df868e932cb2"},
1277 | ]
1278 | yarl = []
1279 |
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [tool.poetry]
2 | name = "python_fastapi_firebase_authentication"
3 | version = "0.1.0"
4 | description = ""
5 | authors = ["Rahul"]
6 |
7 | [tool.poetry.scripts]
8 | cli_script = "packagename.cli:main"
9 | dev = "python_fastapi_firebase_authentication.core.configEnv:dev"
10 | prod = "python_fastapi_firebase_authentication.core.configEnv:prod"
11 | test = "python_fastapi_firebase_authentication.core.configEnv:test"
12 | server = "serve_uvicorn:uvi_server"
13 |
14 |
15 | [tool.poetry.dependencies]
16 | python = "^3.8"
17 | fastapi = "^0.81.0"
18 | uvicorn = {extras = ["standard"], version = "^0.18.3"}
19 | loguru = "^0.6.0"
20 | firebase-admin = "^5.3.0"
21 | email-validator = "^1.2.1"
22 | beanie = "^1.11.9"
23 | gunicorn = "^20.1.0"
24 |
25 | [tool.poetry.dev-dependencies]
26 | pytest = "^5.2"
27 |
28 | [build-system]
29 | requires = ["poetry-core>=1.0.0"]
30 | build-backend = "poetry.core.masonry.api"
31 |
--------------------------------------------------------------------------------
/python_fastapi_firebase_authentication/__init__.py:
--------------------------------------------------------------------------------
1 | __version__ = '0.1.0'
2 |
--------------------------------------------------------------------------------
/python_fastapi_firebase_authentication/core/configEnv.py:
--------------------------------------------------------------------------------
1 |
2 | from loguru import logger
3 | from pathlib import Path
4 |
5 |
6 | def write_dotenv(env:str):
7 | logger.debug(f"------Setting up environment!----")
8 | logger.debug(f"------copying .env.{env} to .env file----")
9 |
10 | root = Path(__file__).parent.parent
11 |
12 | copyFrom = root.with_name(f".env.{env}")
13 | copyTo = root.with_name(".env")
14 |
15 | with open(copyFrom, "r", encoding = 'utf-8') as input:
16 | with open(copyTo, 'w', encoding = 'utf-8') as output:
17 | for line in input:
18 | output.write(line)
19 |
20 |
21 | def dev():
22 | write_dotenv(env="dev")
23 |
24 | def prod():
25 | write_dotenv(env="prod")
26 |
27 | def test():
28 | write_dotenv(env="test")
29 |
30 |
--------------------------------------------------------------------------------
/python_fastapi_firebase_authentication/core/custom_logging.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import sys
3 | from pathlib import Path
4 | from loguru import logger
5 | import json
6 |
7 |
8 | class InterceptHandler(logging.Handler):
9 | loglevel_mapping = {
10 | 50: 'CRITICAL',
11 | 40: 'ERROR',
12 | 30: 'WARNING',
13 | 20: 'INFO',
14 | 10: 'DEBUG',
15 | 0: 'NOTSET',
16 | }
17 |
18 | def emit(self, record):
19 | try:
20 | level = logger.level(record.levelname).name
21 | except AttributeError:
22 | level = self.loglevel_mapping[record.levelno]
23 |
24 | frame, depth = logging.currentframe(), 2
25 | while frame.f_code.co_filename == logging.__file__:
26 | frame = frame.f_back
27 | depth += 1
28 |
29 |
30 | logger.opt(
31 | depth=depth,
32 | exception=record.exc_info
33 | ).log(level,record.getMessage())
34 |
35 |
36 | class CustomizeLogger:
37 |
38 | @classmethod
39 | def make_logger(cls,config_path: Path):
40 |
41 | config = cls.load_logging_config(config_path)
42 | logging_config = config.get('logger')
43 |
44 | logger = cls.customize_logging(
45 | level=logging_config.get('level'),
46 | retention=logging_config.get('retention'),
47 | rotation=logging_config.get('rotation'),
48 | format=logging_config.get('format')
49 | )
50 | return logger
51 |
52 | @classmethod
53 | def customize_logging(cls,
54 | level: str,
55 | rotation: str,
56 | retention: str,
57 | format: str
58 | ):
59 |
60 | logger.remove(0)
61 | logger.add(
62 | # sys.stdout,
63 | sys.stderr,
64 | enqueue=True,
65 | backtrace=True,
66 | level=level.upper(),
67 | format=format
68 | )
69 | logging.basicConfig(handlers=[InterceptHandler()], level=0)
70 | logging.getLogger("uvicorn.access").handlers = [InterceptHandler()]
71 | for _log in ['uvicorn',
72 | 'uvicorn.error',
73 | 'fastapi'
74 | ]:
75 | _logger = logging.getLogger(_log)
76 | _logger.handlers = [InterceptHandler()]
77 |
78 | # return logger.bind(request_id=None, method=None)
79 | return logger
80 |
81 |
82 | @classmethod
83 | def load_logging_config(cls, config_path):
84 | config = None
85 | with open(config_path) as config_file:
86 | config = json.load(config_file)
87 | return config
88 |
89 |
90 |
91 |
--------------------------------------------------------------------------------
/python_fastapi_firebase_authentication/core/data/mongodb/db.py:
--------------------------------------------------------------------------------
1 |
2 | from motor.motor_asyncio import AsyncIOMotorClient
3 | from beanie import init_beanie
4 |
5 | from ...env import DB_NAME, MONGODB_URL
6 |
7 | from ....modules.user.data.mongoDb.models.authProvider import AuthProvider
8 | from ....modules.user.data.mongoDb.models.player import Player
9 | from ....modules.user.data.mongoDb.models.user import User
10 |
11 |
12 | __models = [
13 | AuthProvider,
14 | User,
15 | Player
16 | ]
17 |
18 | mongodb_client:AsyncIOMotorClient = AsyncIOMotorClient(MONGODB_URL)
19 |
20 | # logger.info(client.db)
21 |
22 |
23 | async def init_db(client:AsyncIOMotorClient):
24 | # client = AsyncIOMotorClient(MONGODB_URL)
25 | await init_beanie(client[DB_NAME], document_models=__models)
26 |
--------------------------------------------------------------------------------
/python_fastapi_firebase_authentication/core/env.py:
--------------------------------------------------------------------------------
1 |
2 | from loguru import logger
3 |
4 | from .settings import settings, BASE_DIR
5 |
6 |
7 | TITLE = settings.TITLE
8 | SECRET_KEY = settings.SECRET_KEY
9 | WEB_API_KEY = settings.WEB_API_KEY
10 | ENV = settings.ENV
11 | MONGODB_URL = settings.MONGODB_URL
12 | DB_NAME = settings.DB_NAME
13 | DEBUG = settings.FASTAPI_DEBUG
14 | ALLOWED_ORIGINS = settings.CORS_ALLOWED_ORIGINS.split(",")
15 | LOGGING_CONFIG_PATH = BASE_DIR.with_name("logging_config.json")
16 |
17 | UVI_PORT = settings.UVI_PORT
18 | UVI_SERVER_HOST = settings.UVI_SERVER_HOST
19 | UVI_RELOAD = settings.UVI_RELOAD
20 | UVI_LOG_LEVEL = settings.UVI_LOG_LEVEL
21 | UVI_ACCESS_LOG = settings.UVI_ACCESS_LOG
22 |
23 |
24 | logger.info(f"------Working inside {settings.ENV} environment!----")
25 |
--------------------------------------------------------------------------------
/python_fastapi_firebase_authentication/core/settings.py:
--------------------------------------------------------------------------------
1 | from pathlib import Path
2 | from dotenv import load_dotenv
3 | from functools import lru_cache
4 | from typing import Optional
5 | from pydantic import BaseSettings
6 |
7 |
8 | BASE_DIR: Path = Path(__file__).resolve().parent.parent
9 |
10 | load_dotenv(BASE_DIR.with_name(".env"))
11 |
12 |
13 | class GlobalConfig(BaseSettings):
14 | """Global configurations."""
15 | TITLE : str
16 | SECRET_KEY : Optional[str]
17 | WEB_API_KEY : Optional[str]
18 | MONGODB_URL : Optional[str]
19 | DB_NAME : Optional[str]
20 | FASTAPI_DEBUG : bool = False
21 | CORS_ALLOWED_ORIGINS : str
22 |
23 | UVI_PORT : Optional[int] = None
24 | UVI_SERVER_HOST : Optional[str] = None
25 | UVI_RELOAD : Optional[bool] = False
26 | UVI_LOG_LEVEL : Optional[str] = "info"
27 | UVI_ACCESS_LOG : Optional[bool] = False
28 | ENV : str
29 |
30 |
31 | @lru_cache()
32 | def get_settings():
33 | return GlobalConfig()
34 |
35 |
36 | settings = get_settings()
--------------------------------------------------------------------------------
/python_fastapi_firebase_authentication/modules/dummy/endpoints.py:
--------------------------------------------------------------------------------
1 |
2 | import json
3 | import requests
4 |
5 | from fastapi import APIRouter, status, HTTPException
6 | from loguru import logger
7 | from firebase_admin import auth
8 |
9 | from ...modules.dummy.model import Login, Register
10 | from ...core.env import WEB_API_KEY
11 | # requests
12 |
13 | dummyRouter = APIRouter(prefix="/dummy", tags=["dummy"])
14 |
15 |
16 | @dummyRouter.post("/register", status_code=status.HTTP_200_OK, response_model_exclude_none=True) # , responses=, response_model=PlayerUpdateOut
17 | async def create_user(pModel:Register):
18 | try:
19 | logger.debug(pModel)
20 | email = pModel.email
21 | password = pModel.password
22 |
23 | res = auth.create_user(email=email, email_verified=False, password=password)
24 | logger.info(res)
25 | resOut = {"msg" : "Logged in successfully. LOGIN WITH THE REGISTERED CREDETIALS TO GET ACCESS TOKEN TO USE API."}
26 | return resOut
27 | except Exception as err:
28 | logger.info(err.args[0])
29 | logger.info(type(err.args[0]))
30 | if "EMAIL_EXISTS" in err.args[0]:
31 | logger.debug("CAUGHT")
32 | raise HTTPException(status_code=status.HTTP_302_FOUND, detail="Email already registered!!! Please login.")
33 |
34 | logger.error(err)
35 | raise err
36 |
37 | @dummyRouter.post("/login", status_code=status.HTTP_200_OK, response_model_exclude_none=True) # , responses=
38 | async def login_user(pModel:Login):
39 | try:
40 | logger.debug(pModel)
41 | email = pModel.email
42 | password = pModel.password
43 |
44 | rest_api_url = f"https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword"
45 |
46 | payload = json.dumps({
47 | "email": email,
48 | "password": password,
49 | "returnSecureToken": True
50 | })
51 |
52 | r = requests.post(rest_api_url, params={"key": WEB_API_KEY}, data=payload)
53 | #check for errors in result
54 | # logger.info(r.json())
55 | if 'error' in r.json().keys():
56 | raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail={'status':'error','message':r.json()['error']['message']})
57 | # return {'status':'error','message':r.json()['error']['message']}
58 | #if the registration succeeded
59 | if 'idToken' in r.json().keys() :
60 | return {'status':'success','idToken':r.json()['idToken']}
61 |
62 | except Exception as err:
63 | logger.error(err)
64 | raise err
65 |
--------------------------------------------------------------------------------
/python_fastapi_firebase_authentication/modules/dummy/model.py:
--------------------------------------------------------------------------------
1 |
2 | from typing import Optional
3 | from pydantic import BaseModel, EmailStr
4 |
5 | class Login(BaseModel):
6 | email : Optional[EmailStr] = "dummy@email.com"
7 | password : Optional[str] = "password"
8 |
9 | class Register(BaseModel):
10 | email : Optional[EmailStr] = "dummy@email.com"
11 | password : Optional[str] = "password"
--------------------------------------------------------------------------------
/python_fastapi_firebase_authentication/modules/user/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rahulprakash11/Python-Firebase-Authentication-FastAPI/7341e6a223eca6388bb22047567777e52b088d9e/python_fastapi_firebase_authentication/modules/user/__init__.py
--------------------------------------------------------------------------------
/python_fastapi_firebase_authentication/modules/user/constants.py:
--------------------------------------------------------------------------------
1 | class UserConstant:
2 |
3 | class Auth:
4 |
5 | class AuthType:
6 | firebase = "firebase"
7 | # custom = "custom"
8 |
9 | class VerificationType:
10 | player = "player"
11 | admin = "admin"
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/python_fastapi_firebase_authentication/modules/user/controller/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rahulprakash11/Python-Firebase-Authentication-FastAPI/7341e6a223eca6388bb22047567777e52b088d9e/python_fastapi_firebase_authentication/modules/user/controller/__init__.py
--------------------------------------------------------------------------------
/python_fastapi_firebase_authentication/modules/user/controller/authVerify.py:
--------------------------------------------------------------------------------
1 |
2 | from typing import Optional
3 | from fastapi import APIRouter, status, HTTPException
4 | from loguru import logger
5 | from firebase_admin import auth
6 |
7 | from ..data.mongoDb.interface.authProvider import MongoDbAuthProvider
8 | from ..data.mongoDb.interface.player import MongoDbPlayer
9 | from ..data.mongoDb.interface.user import MongoDbUser
10 | from ..data.mongoDb.models.authProvider import AuthProvider
11 | from ..data.mongoDb.models.player import Player, Contact
12 | from ..data.mongoDb.models.user import User
13 | from ..constants import UserConstant
14 | from ..data.mongoDb.dbConstants import MongoDbConstant
15 | from ..model.model import AuthorisedUserOut, UserAuthRequest
16 |
17 | from ....utils.bitwise import Bitwise
18 |
19 |
20 | # logger = logging.getLogger(__name__)
21 |
22 |
23 | router = APIRouter()
24 |
25 | mongoDbAuthProvider = MongoDbAuthProvider()
26 | mongoDbPlayer = MongoDbPlayer()
27 | mongoDbUser = MongoDbUser()
28 |
29 |
30 | @router.post("/verify", status_code=status.HTTP_200_OK, response_model=AuthorisedUserOut , response_model_exclude_none=True)
31 | async def verify(pModel : UserAuthRequest):
32 |
33 | try:
34 | verificationType = pModel.verificationType
35 | token = pModel.token
36 |
37 | if (
38 | (verificationType != UserConstant.Auth.VerificationType.player) &
39 | (verificationType != UserConstant.Auth.VerificationType.admin)
40 | ):
41 |
42 | logger.debug(f"--wrong verificationType {verificationType}---")
43 | raise HTTPException(status_code=status.HTTP_404_NOT_FOUND , detail="Invalid verification Type")
44 |
45 |
46 | deToken : dict = auth.verify_id_token(id_token=token)
47 | logger.info(deToken)
48 |
49 | pUserId = deToken["user_id"]
50 | provider :str = deToken["firebase"]["sign_in_provider"] # google.com
51 |
52 |
53 | if pModel.name:
54 | name = pModel.name
55 | else:
56 | name = deToken.get("name","user")
57 |
58 | # email : str | None
59 | identifier : Optional[str]
60 | contact = Contact()
61 |
62 | if provider == MongoDbConstant.AuthProvider.Providers.google: # google.com
63 | # email = deToken.get("email")
64 | identifier = deToken.get("email")
65 | contact.email = identifier
66 | elif provider == MongoDbConstant.AuthProvider.Providers.phone: # phone
67 | # phone = deToken.get("phone")
68 | identifier = deToken.get("phone")
69 | contact.phone = identifier
70 |
71 | elif provider == MongoDbConstant.AuthProvider.Providers.password: # phone
72 | # phone = deToken.get("phone")
73 | identifier = deToken.get("email")
74 | contact.email = identifier
75 | else:
76 | logger.exception(f"---Invalid provider : {provider}---")
77 | raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Invalid provider")
78 |
79 | dbAuthProvider = await mongoDbAuthProvider.getByIdentifier(identifier=identifier, provider=provider)
80 |
81 | if dbAuthProvider is None:
82 |
83 | modelUser = User(
84 | name=name,
85 | role=MongoDbConstant.User.Role.player,
86 | status=MongoDbConstant.User.Status.verified
87 | )
88 | dbUser = await mongoDbUser.add(user=modelUser)
89 | logger.info(f"---user id {dbUser.id}")
90 |
91 | modelAuthProvider = AuthProvider(
92 | userId=dbUser.id,
93 | pUid=pUserId,
94 | credentials=identifier,
95 | provider=provider,
96 | authType=MongoDbConstant.AuthProvider.AuthType.firebase
97 | )
98 | dbAuthProvider = await mongoDbAuthProvider.add(modelAuth=modelAuthProvider)
99 | logger.info(f"---created authprovider : {dbAuthProvider}---")
100 |
101 | else:
102 | logger.debug("---Found dbAuthProvider---")
103 | userId = dbAuthProvider.userId
104 |
105 | dbUser = await mongoDbUser.getById(id=userId)
106 |
107 | # if status == MongoDbUserConstant.Status.unverified: first get value through masking then check
108 | if (
109 | Bitwise().containsValue(
110 | originalValue=dbUser.status,
111 | maskValue=MongoDbConstant.User.Status.statusMask,
112 | checkFor=MongoDbConstant.User.Status.unverified
113 | )
114 | ):
115 |
116 | userStatus = Bitwise().attachValue(
117 | originalValue=dbUser.status,
118 | maskValue=MongoDbConstant.User.Status.unverifiedMask,
119 | attachValue=MongoDbConstant.User.Status.verified
120 | )
121 | dbUser = await dbUser.set({User.status : userStatus})
122 |
123 | if dbAuthProvider.pUid is None:
124 | dbAuthProvider = await dbAuthProvider.set({AuthProvider.pUid : pUserId})
125 |
126 | # get player by uID
127 | logger.info(f"---user id : {dbAuthProvider.userId}, {dbUser.id}")
128 |
129 | dbPlayer = await mongoDbPlayer.getByUserId(userId=dbUser.id)
130 |
131 | if dbPlayer is None:
132 | if provider == MongoDbConstant.AuthProvider.Providers.google: # google.com
133 | modelPlayer = Player(userId=dbUser.id, contact=contact, name=name, verifiedEmail=identifier, imageUrl=deToken["picture"])
134 | elif provider == MongoDbConstant.AuthProvider.Providers.phone: # phone
135 | modelPlayer = Player(userId=dbUser.id, contact=contact, name=name, verifiedPhone=identifier)
136 | elif provider == MongoDbConstant.AuthProvider.Providers.password: # phone
137 | modelPlayer = Player(userId=dbUser.id, contact=contact, name=name, verifiedEmail=identifier)
138 | else:
139 | raise HTTPException(status_code=status.HTTP_405_METHOD_NOT_ALLOWED , detail="login method is not set")
140 | dbPlayer = await mongoDbPlayer.add(player=modelPlayer)
141 | else:
142 | if provider == MongoDbConstant.AuthProvider.Providers.google: # google.com
143 | await dbPlayer.set({Player.verifiedEmail : identifier})
144 | elif provider == MongoDbConstant.AuthProvider.Providers.phone: # phone
145 | await dbPlayer.set({Player.verifiedPhone : identifier})
146 | elif provider == MongoDbConstant.AuthProvider.Providers.password: # phone
147 | await dbPlayer.set({Player.verifiedEmail : identifier})
148 | else:
149 | raise HTTPException(status_code=status.HTTP_405_METHOD_NOT_ALLOWED , detail="login method is not set")
150 |
151 | logger.info(f"---player id : {dbPlayer.id}")
152 |
153 | response = AuthorisedUserOut(user=dbUser, player=dbPlayer)
154 | return response
155 | except Exception as err:
156 | logger.error(err)
157 | raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=err.args)
158 |
159 |
--------------------------------------------------------------------------------
/python_fastapi_firebase_authentication/modules/user/controller/player.py:
--------------------------------------------------------------------------------
1 | from beanie import PydanticObjectId
2 | from fastapi import Depends, APIRouter, status, HTTPException
3 | from loguru import logger
4 |
5 | from ...user.data.mongoDb.interface.player import MongoDbPlayer
6 | from ...user.data.mongoDb.models.player import Player
7 | from ...user.model.model import PlayerUpdate, PlayerUpdateOut
8 | from ....utils.dependencies import currentPlayer
9 |
10 |
11 |
12 | router = APIRouter()
13 |
14 | mongoDbPlayer = MongoDbPlayer()
15 |
16 |
17 | @router.get("/{id}", status_code=status.HTTP_200_OK, response_model=Player , response_model_exclude_none=True) # , responses=
18 | async def getPlayer(id :PydanticObjectId, currentPlayer : Player = Depends(currentPlayer)):
19 | try:
20 | objectId = id
21 | if objectId == currentPlayer.id:
22 | response = currentPlayer
23 | return response
24 | else:
25 | raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail={"msg":"Referenced Player not found", "id":objectId})
26 | except Exception as err:
27 | logger.error(err)
28 | raise err
29 |
30 |
31 |
32 | @router.put("/{id}", status_code=status.HTTP_200_OK, response_model=PlayerUpdateOut , response_model_exclude_none=True) # , responses=
33 | async def updatePlayer(id :PydanticObjectId, pModel:PlayerUpdate, currentPlayer : Player = Depends(currentPlayer)):
34 | try:
35 | logger.debug(id)
36 | logger.debug(currentPlayer.id)
37 | objectId = id
38 | if objectId != currentPlayer.id:
39 | raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail={"msg" : "Not Authorised. Can update only self player info."})
40 |
41 | dbPlayer = await mongoDbPlayer.update(dbPlayer=currentPlayer, pModel=pModel)
42 | response = PlayerUpdateOut(isUpdated=True, item=dbPlayer)
43 |
44 | logger.debug(response)
45 | return response
46 | except HTTPException as err:
47 | logger.error(err.detail)
48 | raise err
49 | except Exception as err:
50 | logger.error(err)
51 | raise err
52 |
--------------------------------------------------------------------------------
/python_fastapi_firebase_authentication/modules/user/data/mongoDb/dbConstants.py:
--------------------------------------------------------------------------------
1 | class MongoDbConstant:
2 |
3 | class AuthProvider:
4 |
5 | class AuthType:
6 | firebase = 0x1
7 |
8 | class Providers:
9 | google = "google.com"
10 | phone = "phone"
11 | password = "password"
12 |
13 | class User:
14 | class Status:
15 | unverified = 0x1
16 | unverifiedMask = 0x1
17 | verified = 0x2
18 | verifiedMask = 0x2
19 | statusMask = 0xf
20 |
21 | class Role:
22 | player = 0x1
23 | playerMask = 0x1
24 | admin = 0x2
25 | adminMask = 0x2
26 | roleMask = 0xf
27 |
28 |
29 |
--------------------------------------------------------------------------------
/python_fastapi_firebase_authentication/modules/user/data/mongoDb/interface/authProvider.py:
--------------------------------------------------------------------------------
1 |
2 | from typing import Optional
3 | from beanie import PydanticObjectId
4 | # from loguru import logger
5 |
6 | from ..models.authProvider import AuthProvider
7 | from ....model.model import UpdateAuthProvider
8 |
9 |
10 | class MongoDbAuthProvider:
11 |
12 | async def add(self, modelAuth : AuthProvider) -> AuthProvider:
13 | authUser = await modelAuth.create()
14 | return authUser
15 |
16 | async def getByProviderUserId(self, pUserId : PydanticObjectId) -> AuthProvider:
17 | authUser = await AuthProvider.find(AuthProvider.pUid == pUserId).first_or_none()
18 | return authUser
19 |
20 | async def getByIdentifier(self, identifier : str, provider : Optional[str]) -> AuthProvider:
21 | authUser = await AuthProvider.find(AuthProvider.credentials == identifier).find(AuthProvider.provider == provider).first_or_none()
22 | return authUser
23 |
24 | async def updateSelf(self, updateModel:UpdateAuthProvider) -> AuthProvider:
25 | authUser = await AuthProvider.get(document_id=updateModel.id)
26 | authUser = authUser.copy(update=updateModel.dict(exclude_unset=True))
27 | await authUser.save()
28 | return authUser
29 |
30 |
31 |
--------------------------------------------------------------------------------
/python_fastapi_firebase_authentication/modules/user/data/mongoDb/interface/player.py:
--------------------------------------------------------------------------------
1 |
2 | from beanie import PydanticObjectId
3 | from loguru import logger
4 |
5 | from ..models.player import Player
6 | from ....model.model import PlayerUpdate
7 |
8 |
9 | class MongoDbPlayer:
10 |
11 | async def add(self, player : Player) -> Player:
12 | player = await player.create()
13 | return player
14 |
15 | async def update(self, dbPlayer:Player, pModel:PlayerUpdate) -> Player:
16 | try:
17 | logger.debug(dbPlayer)
18 | await dbPlayer.set(expression=pModel.dict(exclude_unset=True))
19 | logger.debug(dbPlayer)
20 | return dbPlayer
21 | except Exception as err:
22 | logger.error('error in', err)
23 | raise err
24 |
25 |
26 | async def getById(self, id : PydanticObjectId) -> Player:
27 | try:
28 | player = await Player.get(document_id=id)
29 | return player
30 | except Exception as err:
31 | raise err
32 |
33 | async def getByUserId(self, userId : PydanticObjectId) -> Player:
34 | logger.info(userId)
35 | player = await Player.find(Player.userId == userId).first_or_none()
36 | # logger.info(player.id)
37 | return player
38 |
39 |
--------------------------------------------------------------------------------
/python_fastapi_firebase_authentication/modules/user/data/mongoDb/interface/user.py:
--------------------------------------------------------------------------------
1 |
2 | from beanie import PydanticObjectId
3 |
4 | from ..models.user import User
5 |
6 |
7 | class MongoDbUser:
8 | def __init__(self) -> None:
9 | self.dbInterface = User
10 |
11 | async def add(self, user : User) -> User:
12 | dbUser = await user.create()
13 | return dbUser
14 |
15 | async def getById(self, id : PydanticObjectId) -> User:
16 | dbUser = await User.get(document_id=id)
17 | return dbUser
18 |
19 |
--------------------------------------------------------------------------------
/python_fastapi_firebase_authentication/modules/user/data/mongoDb/models/authProvider.py:
--------------------------------------------------------------------------------
1 | from typing import Optional
2 | from beanie import Document, PydanticObjectId
3 | from pydantic import BaseModel
4 | import pymongo
5 |
6 |
7 |
8 | class AuthProvider(Document, BaseModel):
9 |
10 | userId : PydanticObjectId
11 | pUid : Optional[str]
12 | credentials : str
13 | provider : str
14 | authType : int
15 | status : Optional[int]
16 |
17 | class Settings:
18 | name = "authProviders" # mongodb collection name
19 | indexes = [
20 | [
21 | ("credentials", pymongo.ASCENDING),
22 | ("authType", pymongo.ASCENDING)
23 | ]
24 | ]
25 |
26 | class Config:
27 | schema_extra = {
28 | "example": {
29 | "userId": "_id",
30 | "pUid": "asdjkjfdsfdjj",
31 | "credentials": "email@email.com",
32 | "provider": "firebase",
33 | "authType": 0x0,
34 | "status" : None
35 | }
36 | }
37 |
38 |
39 |
--------------------------------------------------------------------------------
/python_fastapi_firebase_authentication/modules/user/data/mongoDb/models/player.py:
--------------------------------------------------------------------------------
1 | from typing import List, Optional
2 | from beanie import Document, PydanticObjectId
3 | from pydantic import BaseModel, EmailStr
4 |
5 |
6 | class Address(BaseModel):
7 | name: Optional[str]
8 | line1: Optional[str]
9 | line2: Optional[str]
10 | city: str
11 | state: str
12 | pin: str
13 |
14 | class Contact(BaseModel):
15 | phone: Optional[str]
16 | email: Optional[str]
17 |
18 |
19 | class Player(Document):
20 |
21 | # ref user table
22 | userId : PydanticObjectId
23 | name : str
24 | address : Optional[Address]
25 | contact : Contact
26 | communes : Optional[List[PydanticObjectId]]
27 | verifiedPhone : Optional[str]
28 | verifiedEmail : Optional[EmailStr]
29 | imageUrl : Optional[str]
30 | status : Optional[int] = None
31 |
32 | class Settings:
33 | name = "players" # mongodb collection name
34 |
35 |
36 | class Config:
37 | schema_extra = {
38 | "example": {
39 | "userId": "_id",
40 | "name": "rahul",
41 | "address": '{"city" : "adj", "state" : "adasd", "pin" : "111111"}',
42 | "contact": "[phone : 12334454, email : email@email.com]",
43 | "communes": "[asdfweesd, eawasdffe]",
44 | "status" : "s"
45 | }
46 | }
47 |
48 |
49 |
--------------------------------------------------------------------------------
/python_fastapi_firebase_authentication/modules/user/data/mongoDb/models/user.py:
--------------------------------------------------------------------------------
1 |
2 | from beanie import Document
3 |
4 |
5 | # role -> 0 : admin, 1 : player
6 | # status -> 0 : inactive, 1 : active
7 |
8 |
9 | class User(Document):
10 |
11 | name : str
12 | role : int
13 | status : int
14 |
15 | class Settings:
16 | name = "users" # mongodb collection name
17 |
18 | class Config:
19 | schema_extra = {
20 | "example": {
21 | "Id": "_id",
22 | "name":"rahul",
23 | "role": 1,
24 | "status" : 1
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/python_fastapi_firebase_authentication/modules/user/model/model.py:
--------------------------------------------------------------------------------
1 | from typing import Optional
2 | from pydantic import BaseModel
3 | from beanie import PydanticObjectId
4 |
5 | from ..data.mongoDb.models.user import User
6 | from ..data.mongoDb.models.player import Player
7 | from ..data.mongoDb.models.player import Address, Player, Contact
8 |
9 |
10 |
11 | class UserAuthRequest(BaseModel):
12 | token : str
13 | verificationType : str
14 | name : Optional[str] = None
15 |
16 | class Config:
17 | schema_extra = {
18 | "example": {
19 | "token" : "asdfdsiuffdsdfnlasfd6asf5f16dsf4645sdfsdfdsfdsj",
20 | "verificationType": "player",
21 | "name" : "rahul"
22 | }
23 | }
24 |
25 |
26 | class AuthorisedUserOut(BaseModel):
27 | user : User
28 | player : Player
29 |
30 | class UpdateAuthProvider(BaseModel):
31 | id : PydanticObjectId
32 | pUid : Optional[str]
33 | status : Optional[str]
34 |
35 |
36 |
37 |
38 | class PlayerUpdate(BaseModel):
39 | name : Optional[str]
40 | address : Optional[Address]
41 | contact : Optional[Contact]
42 | imageUrl : Optional[str]
43 |
44 | class PlayerUpdateOut(BaseModel):
45 | isUpdated : bool
46 | item : Player
47 |
48 | class UpdateUser(BaseModel):
49 | id : PydanticObjectId
50 | name : Optional[str]
51 | role : Optional[int]
52 | status : Optional[int]
53 |
--------------------------------------------------------------------------------
/python_fastapi_firebase_authentication/modules/user/route/endpoints.py:
--------------------------------------------------------------------------------
1 | from fastapi import APIRouter
2 |
3 | from ....modules.user.controller import authVerify, player
4 |
5 |
6 |
7 |
8 | userRouter = APIRouter()
9 |
10 | userRouter.include_router(authVerify.router, prefix="/auth", tags=["auth"])
11 | userRouter.include_router(player.router, prefix="/player", tags=["player"])
12 |
13 |
--------------------------------------------------------------------------------
/python_fastapi_firebase_authentication/utils/bitwise.py:
--------------------------------------------------------------------------------
1 |
2 |
3 | class Bitwise:
4 | mask = [0xf, 0xf0, 0xf00, 0xf000]
5 |
6 | def containsValue(self, originalValue:int, maskValue:int, checkFor:int) -> bool:
7 | n = self.mask.index(maskValue)
8 | return ((originalValue & maskValue) == (checkFor << 4*n))
9 |
10 |
11 | def clearValue(self, value:int, maskValue:int) -> int:
12 | return ((value & (~maskValue)) & maskValue)
13 |
14 |
15 | def attachValue(self, originalValue:int, maskValue:int, attachValue:int) -> int:
16 | n = self.mask.index(maskValue)
17 | return ((originalValue & (~maskValue)) | (attachValue << 4*n))
18 |
19 | def getValue(self, originalVal:int, maskVal:int) -> int:
20 | n = self.mask.index(maskVal)
21 |
22 | return ((originalVal & maskVal) >> 4*n)
23 |
--------------------------------------------------------------------------------
/python_fastapi_firebase_authentication/utils/dependencies.py:
--------------------------------------------------------------------------------
1 | from beanie import PydanticObjectId
2 | from fastapi import Depends, HTTPException, Header, status
3 | from firebase_admin import auth
4 | from loguru import logger
5 |
6 | from ..modules.user.data.mongoDb.interface.authProvider import MongoDbAuthProvider
7 | from ..modules.user.data.mongoDb.interface.player import MongoDbPlayer
8 |
9 | mongoDbAuthProvider = MongoDbAuthProvider()
10 | mongoDbPlayer = MongoDbPlayer()
11 |
12 | async def decodeToken(token : str = Header()):
13 | try:
14 | deToken : dict = auth.verify_id_token(id_token=token)
15 | pUserId:str = deToken.get("user_id")
16 | dbAuthProvider = await mongoDbAuthProvider.getByProviderUserId(pUserId=pUserId)
17 | userId = dbAuthProvider.userId
18 | return userId
19 | except Exception as e:
20 | logger.error(e)
21 | raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="user not authorised")
22 |
23 |
24 | async def currentPlayer(userId : PydanticObjectId = Depends(decodeToken)):
25 | player = await mongoDbPlayer.getByUserId(userId=userId)
26 | return player
27 |
--------------------------------------------------------------------------------
/serve_uvicorn.py:
--------------------------------------------------------------------------------
1 |
2 | import uvicorn
3 | # import gunicorn
4 | # import uvicorn.workers.UvicornWorker
5 |
6 | from python_fastapi_firebase_authentication.core.env import UVI_PORT, UVI_SERVER_HOST, UVI_LOG_LEVEL, UVI_ACCESS_LOG, UVI_RELOAD
7 |
8 |
9 | def uvi_server():
10 |
11 | uvicorn.run(
12 | "main:app",
13 | host=UVI_SERVER_HOST,
14 | port=UVI_PORT,
15 | reload=UVI_RELOAD,
16 | use_colors=True,
17 | access_log=UVI_ACCESS_LOG,
18 | log_level=UVI_LOG_LEVEL
19 | )
20 |
21 | # gunicorn main:app --workers 4 --worker-class uvicorn.workers.UvicornWorker --bind 0.0.0.0:80
22 |
--------------------------------------------------------------------------------
/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rahulprakash11/Python-Firebase-Authentication-FastAPI/7341e6a223eca6388bb22047567777e52b088d9e/tests/__init__.py
--------------------------------------------------------------------------------
/tests/test_python_fastapi_firebase_authentication.py:
--------------------------------------------------------------------------------
1 | from python_fastapi_firebase_authentication import __version__
2 |
3 |
4 | def test_version():
5 | assert __version__ == '0.1.0'
6 |
--------------------------------------------------------------------------------