Repository: dotX12/pyfa-converter (language: python)

Issue:
added tests, change typehints 3.10 to old syntax
Fix: https://github.com/dotX12/pyfa-converter/issues/11


Discussion / hints:

Fix patch:
diff --git a/LICENSE.md b/LICENSE.md
new file mode 100644
index 0000000..c810b15
--- /dev/null
+++ b/LICENSE.md
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2022 dotX12
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
index fafe391..5be0fdf 100644
--- a/README.md
+++ b/README.md
@@ -1,18 +1,56 @@
 # pyfa-converter
-Makes it pretty easy to create a model based on Field [pydantic] and use the model for www-form-data.
+Allows you to convert pydantic models for fastapi param models - query, form, header, cookie, body, etc.
+
 
 
 ### How to install?
 `pip install pyfa_converter`
 
 ### How to simplify your life?
-![image](https://user-images.githubusercontent.com/64792903/161491444-60e211fe-26c3-44ea-aade-a7c4177eaa74.png)
+```python3
+from datetime import datetime
+from typing import Optional
+
+from fastapi import FastAPI, UploadFile, File, Form
+from pydantic import BaseModel, Field
+
+from pyfa_converter import FormDepends, PyFaDepends
+
+app = FastAPI()
+
+
+class PostContractBodySchema(BaseModel):
+    title: str = Field(..., description="Description title")
+    date: Optional[datetime] = Field(
+        None, description="Example: 2021-12-14T09:56:31.056Z"
+    )
+
+
+@app.post("/form-data-body")
+async def example_foo_body_handler(
+    data: PostContractBodySchema = FormDepends(PostContractBodySchema),
+    # data1: PostContractBodySchema = PyFaDepends( # OR
+    #         model= PostContractBodySchema, _type=Form
+    #     ),
+    document: UploadFile = File(...),
+):
+    return {"title": data.title, "date": data.date, "file_name": document.filename}
+```
 
 ---
 
-### What do I need to do with the model?
-* We put the decorator `@PydanticConverter.body` for the model and enjoy.
-* `data: YourPydanticModel = FormBody()`
+### What do I need to do?
+```python3
+from pyfa_converter import PyFaDepends, FormDepends, QueryDepends
+from fastapi import Header, Form
+...
+
+async def foo(data: MyCustomModel = PyFaDepends(MyCustomModel, _type=Header)): ...
+async def foo(data: MyCustomModel = PyFaDepends(MyCustomModel, _type=Form)): ...
+
+async def foo(data: MyCustomModel = FormDepends(MyCustomModel)): ...
+async def foo(data: MyCustomModel = QueryDepends(MyCustomModel)): ...
+```
 
 ---
 
diff --git a/examples/main.py b/examples/main.py
index 3c05ac9..f016f65 100644
--- a/examples/main.py
+++ b/examples/main.py
@@ -1,9 +1,16 @@
-from fastapi import FastAPI, Depends
+from fastapi import FastAPI, Header
 from fastapi import File
 from fastapi import UploadFile
 
-from examples.models import PostContractJSONSchema, PostContractBodySchema
-from pyfa_converter import FormBody
+from examples.models import (
+    PostContractBodySchema,
+    ExampleSchemaForHeader,
+)
+from examples.models import PostContractJSONSchema
+from examples.models import PostContractSmallDoubleBodySchema
+from examples.models import PostContractSmallDoubleQuerySchema
+from examples.models import PostSchemaIntegerGE
+from pyfa_converter.depends import QueryDepends, FormDepends, PyFaDepends
 
 app = FastAPI()
 
@@ -18,25 +25,55 @@ async def example_json_body_handler(
     }
 
 
+#
+
+
 @app.post("/form-data-body")
 async def example_foo_body_handler(
-    data: PostContractBodySchema = FormBody(),
+    data: PostContractBodySchema = FormDepends(PostContractBodySchema),
     document: UploadFile = File(...),
 ):
-    return {
-        "title": data.title,
-        "date": data.date,
-        "file_name": document.filename
-    }
+    return {"title": data.title, "date": data.date, "file_name": document.filename}
 
 
 @app.post("/form-data-body-two")
 async def example_foo_body_handler(
-    data: PostContractBodySchema = FormBody(),
+    data: PostContractBodySchema = FormDepends(PostContractBodySchema),
     document: UploadFile = File(...),
 ):
-    return {
-        "title": data.title,
-        "date": data.date,
-        "file_name": document.filename
-    }
\ No newline at end of file
+    return {"title": data.title, "date": data.date, "file_name": document.filename}
+
+
+@app.post("/test")
+async def foo(
+    data: PostContractSmallDoubleBodySchema = FormDepends(
+        PostContractSmallDoubleBodySchema
+    ),
+):
+    return {"bar": "bar"}
+
+
+@app.post("/test_query_list")
+async def test_list_form(
+    data: PostContractSmallDoubleQuerySchema = QueryDepends(
+        PostContractSmallDoubleQuerySchema
+    ),
+):
+    return {"data": data}
+
+
+@app.post("/test_header_list")
+async def test_list_form(
+    data: ExampleSchemaForHeader = PyFaDepends(
+        model=ExampleSchemaForHeader, _type=Header
+    ),
+):
+
+    return {"data": data}
+
+
+@app.post("/test_int_ge")
+async def test_post_int_ge(
+    data: PostSchemaIntegerGE = FormDepends(PostSchemaIntegerGE),
+):
+    return {"id": data.id}
diff --git a/examples/models.py b/examples/models.py
index 7d2c220..0168e87 100644
--- a/examples/models.py
+++ b/examples/models.py
@@ -1,13 +1,12 @@
 from datetime import datetime
 from decimal import Decimal
+from typing import List
 from typing import Optional
 
 from pydantic import BaseModel
 from pydantic import Field
 from pydantic import validator
 
-from pyfa_converter import PydanticConverter
-
 
 class PostContractJSONSchema(BaseModel):
     title: str = Field(..., description="Description title")
@@ -17,7 +16,7 @@ class PostContractJSONSchema(BaseModel):
     amount: Optional[Decimal] = Field(None, description="Description amount")
     unit_price: Optional[Decimal] = Field(None, description="Description unit_price")
 
-    @validator('date', each_item=True)
+    @validator("date", each_item=True)
     def date_validator(cls, v: datetime):
         return v.date()
 
@@ -29,16 +28,34 @@ class PostContractSmallJSONSchema(BaseModel):
     )
 
 
-@PydanticConverter.body
 class PostContractBodySchema(PostContractJSONSchema):
     pass
 
 
-@PydanticConverter.body
 class PostContractSmallBodySchema(PostContractSmallJSONSchema):
     pass
 
 
-@PydanticConverter.body
-class PostContractSmallDoubleBodySchema(PostContractSmallJSONSchema):
-    pass
\ No newline at end of file
+class PostContractSmallDoubleBodySchema(BaseModel):
+    id: Optional[int] = Field(None, description="gwa")
+    title: Optional[str] = Field(None)
+    data: Optional[List[int]]
+
+
+class PostContractSmallDoubleQuerySchema(BaseModel):
+    id: Optional[int] = Field(None, description="gwa")
+    title: Optional[str] = Field(None)
+    data: Optional[List[int]] = Field(default=[1, 2, 3])
+
+
+class ExampleSchemaForHeader(BaseModel):
+    strange_header: Optional[str] = Field(None, convert_underscores=True)
+    query: str = Field(...)
+    form: str = Field(...)
+    body: str = Field(...)
+
+
+class PostSchemaIntegerGE(BaseModel):
+    id: int = Field(..., description="gwa", ge=10)
+    title: Optional[str] = Field(None)
+    data: Optional[List[int]]
diff --git a/poetry.lock b/poetry.lock
new file mode 100644
index 0000000..d485896
--- /dev/null
+++ b/poetry.lock
@@ -0,0 +1,455 @@
+[[package]]
+name = "anyio"
+version = "3.6.1"
+description = "High level compatibility layer for multiple asynchronous event loop implementations"
+category = "main"
+optional = false
+python-versions = ">=3.6.2"
+
+[package.dependencies]
+idna = ">=2.8"
+sniffio = ">=1.1"
+
+[package.extras]
+doc = ["packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"]
+test = ["contextlib2", "coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "mock (>=4)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (<0.15)", "uvloop (>=0.15)"]
+trio = ["trio (>=0.16)"]
+
+[[package]]
+name = "atomicwrites"
+version = "1.4.1"
+description = "Atomic file writes."
+category = "dev"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+
+[[package]]
+name = "attrs"
+version = "22.1.0"
+description = "Classes Without Boilerplate"
+category = "dev"
+optional = false
+python-versions = ">=3.5"
+
+[package.extras]
+dev = ["cloudpickle", "coverage[toml] (>=5.0.2)", "furo", "hypothesis", "mypy (>=0.900,!=0.940)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "sphinx", "sphinx-notfound-page", "zope.interface"]
+docs = ["furo", "sphinx", "sphinx-notfound-page", "zope.interface"]
+tests = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "zope.interface"]
+tests_no_zope = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins"]
+
+[[package]]
+name = "certifi"
+version = "2022.6.15.1"
+description = "Python package for providing Mozilla's CA Bundle."
+category = "dev"
+optional = false
+python-versions = ">=3.6"
+
+[[package]]
+name = "colorama"
+version = "0.4.5"
+description = "Cross-platform colored terminal text."
+category = "dev"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+
+[[package]]
+name = "fastapi"
+version = "0.83.0"
+description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production"
+category = "main"
+optional = false
+python-versions = ">=3.6.1"
+
+[package.dependencies]
+pydantic = ">=1.6.2,<1.7 || >1.7,<1.7.1 || >1.7.1,<1.7.2 || >1.7.2,<1.7.3 || >1.7.3,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0"
+starlette = "0.19.1"
+
+[package.extras]
+all = ["email_validator (>=1.1.1,<2.0.0)", "itsdangerous (>=1.1.0,<3.0.0)", "jinja2 (>=2.11.2,<4.0.0)", "orjson (>=3.2.1,<4.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "pyyaml (>=5.3.1,<7.0.0)", "requests (>=2.24.0,<3.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)", "uvicorn[standard] (>=0.12.0,<0.18.0)"]
+dev = ["autoflake (>=1.4.0,<2.0.0)", "flake8 (>=3.8.3,<6.0.0)", "passlib[bcrypt] (>=1.7.2,<2.0.0)", "pre-commit (>=2.17.0,<3.0.0)", "python-jose[cryptography] (>=3.3.0,<4.0.0)", "uvicorn[standard] (>=0.12.0,<0.18.0)"]
+doc = ["mdx-include (>=1.4.1,<2.0.0)", "mkdocs (>=1.1.2,<2.0.0)", "mkdocs-markdownextradata-plugin (>=0.1.7,<0.3.0)", "mkdocs-material (>=8.1.4,<9.0.0)", "pyyaml (>=5.3.1,<7.0.0)", "typer (>=0.4.1,<0.5.0)"]
+test = ["anyio[trio] (>=3.2.1,<4.0.0)", "black (==22.3.0)", "databases[sqlite] (>=0.3.2,<0.6.0)", "email_validator (>=1.1.1,<2.0.0)", "flake8 (>=3.8.3,<6.0.0)", "flask (>=1.1.2,<3.0.0)", "httpx (>=0.14.0,<0.19.0)", "isort (>=5.0.6,<6.0.0)", "mypy (==0.910)", "orjson (>=3.2.1,<4.0.0)", "peewee (>=3.13.3,<4.0.0)", "pytest (>=6.2.4,<7.0.0)", "pytest-cov (>=2.12.0,<4.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "requests (>=2.24.0,<3.0.0)", "sqlalchemy (>=1.3.18,<1.5.0)", "types-dataclasses (==0.6.5)", "types-orjson (==3.6.2)", "types-ujson (==4.2.1)", "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)"]
+
+[[package]]
+name = "h11"
+version = "0.12.0"
+description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1"
+category = "dev"
+optional = false
+python-versions = ">=3.6"
+
+[[package]]
+name = "httpcore"
+version = "0.15.0"
+description = "A minimal low-level HTTP client."
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+
+[package.dependencies]
+anyio = ">=3.0.0,<4.0.0"
+certifi = "*"
+h11 = ">=0.11,<0.13"
+sniffio = ">=1.0.0,<2.0.0"
+
+[package.extras]
+http2 = ["h2 (>=3,<5)"]
+socks = ["socksio (>=1.0.0,<2.0.0)"]
+
+[[package]]
+name = "httpx"
+version = "0.23.0"
+description = "The next generation HTTP client."
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+
+[package.dependencies]
+certifi = "*"
+httpcore = ">=0.15.0,<0.16.0"
+rfc3986 = {version = ">=1.3,<2", extras = ["idna2008"]}
+sniffio = "*"
+
+[package.extras]
+brotli = ["brotli", "brotlicffi"]
+cli = ["click (>=8.0.0,<9.0.0)", "pygments (>=2.0.0,<3.0.0)", "rich (>=10,<13)"]
+http2 = ["h2 (>=3,<5)"]
+socks = ["socksio (>=1.0.0,<2.0.0)"]
+
+[[package]]
+name = "idna"
+version = "3.3"
+description = "Internationalized Domain Names in Applications (IDNA)"
+category = "main"
+optional = false
+python-versions = ">=3.5"
+
+[[package]]
+name = "iniconfig"
+version = "1.1.1"
+description = "iniconfig: brain-dead simple config-ini parsing"
+category = "dev"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "packaging"
+version = "21.3"
+description = "Core utilities for Python packages"
+category = "dev"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+pyparsing = ">=2.0.2,<3.0.5 || >3.0.5"
+
+[[package]]
+name = "pluggy"
+version = "1.0.0"
+description = "plugin and hook calling mechanisms for python"
+category = "dev"
+optional = false
+python-versions = ">=3.6"
+
+[package.extras]
+dev = ["pre-commit", "tox"]
+testing = ["pytest", "pytest-benchmark"]
+
+[[package]]
+name = "py"
+version = "1.11.0"
+description = "library with cross-python path, ini-parsing, io, code, log facilities"
+category = "dev"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+
+[[package]]
+name = "pydantic"
+version = "1.10.2"
+description = "Data validation and settings management using python type hints"
+category = "main"
+optional = false
+python-versions = ">=3.7"
+
+[package.dependencies]
+typing-extensions = ">=4.1.0"
+
+[package.extras]
+dotenv = ["python-dotenv (>=0.10.4)"]
+email = ["email-validator (>=1.0.3)"]
+
+[[package]]
+name = "pyparsing"
+version = "3.0.9"
+description = "pyparsing module - Classes and methods to define and execute parsing grammars"
+category = "dev"
+optional = false
+python-versions = ">=3.6.8"
+
+[package.extras]
+diagrams = ["jinja2", "railroad-diagrams"]
+
+[[package]]
+name = "pytest"
+version = "6.2.5"
+description = "pytest: simple powerful testing with Python"
+category = "dev"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""}
+attrs = ">=19.2.0"
+colorama = {version = "*", markers = "sys_platform == \"win32\""}
+iniconfig = "*"
+packaging = "*"
+pluggy = ">=0.12,<2.0"
+py = ">=1.8.2"
+toml = "*"
+
+[package.extras]
+testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"]
+
+[[package]]
+name = "pytest-asyncio"
+version = "0.19.0"
+description = "Pytest support for asyncio"
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+
+[package.dependencies]
+pytest = ">=6.1.0"
+
+[package.extras]
+testing = ["coverage (>=6.2)", "flaky (>=3.5.0)", "hypothesis (>=5.7.1)", "mypy (>=0.931)", "pytest-trio (>=0.7.0)"]
+
+[[package]]
+name = "pytest-mock"
+version = "3.8.2"
+description = "Thin-wrapper around the mock package for easier use with pytest"
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+
+[package.dependencies]
+pytest = ">=5.0"
+
+[package.extras]
+dev = ["pre-commit", "pytest-asyncio", "tox"]
+
+[[package]]
+name = "python-multipart"
+version = "0.0.5"
+description = "A streaming multipart parser for Python"
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+six = ">=1.4.0"
+
+[[package]]
+name = "rfc3986"
+version = "1.5.0"
+description = "Validating URI References per RFC 3986"
+category = "dev"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+idna = {version = "*", optional = true, markers = "extra == \"idna2008\""}
+
+[package.extras]
+idna2008 = ["idna"]
+
+[[package]]
+name = "six"
+version = "1.16.0"
+description = "Python 2 and 3 compatibility utilities"
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
+
+[[package]]
+name = "sniffio"
+version = "1.3.0"
+description = "Sniff out which async library your code is running under"
+category = "main"
+optional = false
+python-versions = ">=3.7"
+
+[[package]]
+name = "starlette"
+version = "0.19.1"
+description = "The little ASGI library that shines."
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+anyio = ">=3.4.0,<5"
+typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""}
+
+[package.extras]
+full = ["itsdangerous", "jinja2", "python-multipart", "pyyaml", "requests"]
+
+[[package]]
+name = "toml"
+version = "0.10.2"
+description = "Python Library for Tom's Obvious, Minimal Language"
+category = "dev"
+optional = false
+python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
+
+[[package]]
+name = "typing-extensions"
+version = "4.3.0"
+description = "Backported and Experimental Type Hints for Python 3.7+"
+category = "main"
+optional = false
+python-versions = ">=3.7"
+
+[metadata]
+lock-version = "1.1"
+python-versions = ">= 3.8"
+content-hash = "6d7ed8af0de53e078518be0afecc1b7d8bc3f283351772b5f278dd05042eba75"
+
+[metadata.files]
+anyio = [
+    {file = "anyio-3.6.1-py3-none-any.whl", hash = "sha256:cb29b9c70620506a9a8f87a309591713446953302d7d995344d0d7c6c0c9a7be"},
+    {file = "anyio-3.6.1.tar.gz", hash = "sha256:413adf95f93886e442aea925f3ee43baa5a765a64a0f52c6081894f9992fdd0b"},
+]
+atomicwrites = [
+    {file = "atomicwrites-1.4.1.tar.gz", hash = "sha256:81b2c9071a49367a7f770170e5eec8cb66567cfbbc8c73d20ce5ca4a8d71cf11"},
+]
+attrs = [
+    {file = "attrs-22.1.0-py2.py3-none-any.whl", hash = "sha256:86efa402f67bf2df34f51a335487cf46b1ec130d02b8d39fd248abfd30da551c"},
+    {file = "attrs-22.1.0.tar.gz", hash = "sha256:29adc2665447e5191d0e7c568fde78b21f9672d344281d0c6e1ab085429b22b6"},
+]
+certifi = [
+    {file = "certifi-2022.6.15.1-py3-none-any.whl", hash = "sha256:43dadad18a7f168740e66944e4fa82c6611848ff9056ad910f8f7a3e46ab89e0"},
+    {file = "certifi-2022.6.15.1.tar.gz", hash = "sha256:cffdcd380919da6137f76633531a5817e3a9f268575c128249fb637e4f9e73fb"},
+]
+colorama = [
+    {file = "colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"},
+    {file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"},
+]
+fastapi = [
+    {file = "fastapi-0.83.0-py3-none-any.whl", hash = "sha256:694a2b6c2607a61029a4be1c6613f84d74019cb9f7a41c7a475dca8e715f9368"},
+    {file = "fastapi-0.83.0.tar.gz", hash = "sha256:96eb692350fe13d7a9843c3c87a874f0d45102975257dd224903efd6c0fde3bd"},
+]
+h11 = [
+    {file = "h11-0.12.0-py3-none-any.whl", hash = "sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6"},
+    {file = "h11-0.12.0.tar.gz", hash = "sha256:47222cb6067e4a307d535814917cd98fd0a57b6788ce715755fa2b6c28b56042"},
+]
+httpcore = [
+    {file = "httpcore-0.15.0-py3-none-any.whl", hash = "sha256:1105b8b73c025f23ff7c36468e4432226cbb959176eab66864b8e31c4ee27fa6"},
+    {file = "httpcore-0.15.0.tar.gz", hash = "sha256:18b68ab86a3ccf3e7dc0f43598eaddcf472b602aba29f9aa6ab85fe2ada3980b"},
+]
+httpx = [
+    {file = "httpx-0.23.0-py3-none-any.whl", hash = "sha256:42974f577483e1e932c3cdc3cd2303e883cbfba17fe228b0f63589764d7b9c4b"},
+    {file = "httpx-0.23.0.tar.gz", hash = "sha256:f28eac771ec9eb4866d3fb4ab65abd42d38c424739e80c08d8d20570de60b0ef"},
+]
+idna = [
+    {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"},
+    {file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"},
+]
+iniconfig = [
+    {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"},
+    {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"},
+]
+packaging = [
+    {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"},
+    {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"},
+]
+pluggy = [
+    {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"},
+    {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"},
+]
+py = [
+    {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"},
+    {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"},
+]
+pydantic = [
+    {file = "pydantic-1.10.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bb6ad4489af1bac6955d38ebcb95079a836af31e4c4f74aba1ca05bb9f6027bd"},
+    {file = "pydantic-1.10.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a1f5a63a6dfe19d719b1b6e6106561869d2efaca6167f84f5ab9347887d78b98"},
+    {file = "pydantic-1.10.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:352aedb1d71b8b0736c6d56ad2bd34c6982720644b0624462059ab29bd6e5912"},
+    {file = "pydantic-1.10.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:19b3b9ccf97af2b7519c42032441a891a5e05c68368f40865a90eb88833c2559"},
+    {file = "pydantic-1.10.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e9069e1b01525a96e6ff49e25876d90d5a563bc31c658289a8772ae186552236"},
+    {file = "pydantic-1.10.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:355639d9afc76bcb9b0c3000ddcd08472ae75318a6eb67a15866b87e2efa168c"},
+    {file = "pydantic-1.10.2-cp310-cp310-win_amd64.whl", hash = "sha256:ae544c47bec47a86bc7d350f965d8b15540e27e5aa4f55170ac6a75e5f73b644"},
+    {file = "pydantic-1.10.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a4c805731c33a8db4b6ace45ce440c4ef5336e712508b4d9e1aafa617dc9907f"},
+    {file = "pydantic-1.10.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d49f3db871575e0426b12e2f32fdb25e579dea16486a26e5a0474af87cb1ab0a"},
+    {file = "pydantic-1.10.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37c90345ec7dd2f1bcef82ce49b6235b40f282b94d3eec47e801baf864d15525"},
+    {file = "pydantic-1.10.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7b5ba54d026c2bd2cb769d3468885f23f43710f651688e91f5fb1edcf0ee9283"},
+    {file = "pydantic-1.10.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:05e00dbebbe810b33c7a7362f231893183bcc4251f3f2ff991c31d5c08240c42"},
+    {file = "pydantic-1.10.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2d0567e60eb01bccda3a4df01df677adf6b437958d35c12a3ac3e0f078b0ee52"},
+    {file = "pydantic-1.10.2-cp311-cp311-win_amd64.whl", hash = "sha256:c6f981882aea41e021f72779ce2a4e87267458cc4d39ea990729e21ef18f0f8c"},
+    {file = "pydantic-1.10.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c4aac8e7103bf598373208f6299fa9a5cfd1fc571f2d40bf1dd1955a63d6eeb5"},
+    {file = "pydantic-1.10.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81a7b66c3f499108b448f3f004801fcd7d7165fb4200acb03f1c2402da73ce4c"},
+    {file = "pydantic-1.10.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bedf309630209e78582ffacda64a21f96f3ed2e51fbf3962d4d488e503420254"},
+    {file = "pydantic-1.10.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:9300fcbebf85f6339a02c6994b2eb3ff1b9c8c14f502058b5bf349d42447dcf5"},
+    {file = "pydantic-1.10.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:216f3bcbf19c726b1cc22b099dd409aa371f55c08800bcea4c44c8f74b73478d"},
+    {file = "pydantic-1.10.2-cp37-cp37m-win_amd64.whl", hash = "sha256:dd3f9a40c16daf323cf913593083698caee97df2804aa36c4b3175d5ac1b92a2"},
+    {file = "pydantic-1.10.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b97890e56a694486f772d36efd2ba31612739bc6f3caeee50e9e7e3ebd2fdd13"},
+    {file = "pydantic-1.10.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9cabf4a7f05a776e7793e72793cd92cc865ea0e83a819f9ae4ecccb1b8aa6116"},
+    {file = "pydantic-1.10.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06094d18dd5e6f2bbf93efa54991c3240964bb663b87729ac340eb5014310624"},
+    {file = "pydantic-1.10.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cc78cc83110d2f275ec1970e7a831f4e371ee92405332ebfe9860a715f8336e1"},
+    {file = "pydantic-1.10.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ee433e274268a4b0c8fde7ad9d58ecba12b069a033ecc4645bb6303c062d2e9"},
+    {file = "pydantic-1.10.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:7c2abc4393dea97a4ccbb4ec7d8658d4e22c4765b7b9b9445588f16c71ad9965"},
+    {file = "pydantic-1.10.2-cp38-cp38-win_amd64.whl", hash = "sha256:0b959f4d8211fc964772b595ebb25f7652da3f22322c007b6fed26846a40685e"},
+    {file = "pydantic-1.10.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c33602f93bfb67779f9c507e4d69451664524389546bacfe1bee13cae6dc7488"},
+    {file = "pydantic-1.10.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5760e164b807a48a8f25f8aa1a6d857e6ce62e7ec83ea5d5c5a802eac81bad41"},
+    {file = "pydantic-1.10.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6eb843dcc411b6a2237a694f5e1d649fc66c6064d02b204a7e9d194dff81eb4b"},
+    {file = "pydantic-1.10.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b8795290deaae348c4eba0cebb196e1c6b98bdbe7f50b2d0d9a4a99716342fe"},
+    {file = "pydantic-1.10.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:e0bedafe4bc165ad0a56ac0bd7695df25c50f76961da29c050712596cf092d6d"},
+    {file = "pydantic-1.10.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2e05aed07fa02231dbf03d0adb1be1d79cabb09025dd45aa094aa8b4e7b9dcda"},
+    {file = "pydantic-1.10.2-cp39-cp39-win_amd64.whl", hash = "sha256:c1ba1afb396148bbc70e9eaa8c06c1716fdddabaf86e7027c5988bae2a829ab6"},
+    {file = "pydantic-1.10.2-py3-none-any.whl", hash = "sha256:1b6ee725bd6e83ec78b1aa32c5b1fa67a3a65badddde3976bca5fe4568f27709"},
+    {file = "pydantic-1.10.2.tar.gz", hash = "sha256:91b8e218852ef6007c2b98cd861601c6a09f1aa32bbbb74fab5b1c33d4a1e410"},
+]
+pyparsing = [
+    {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"},
+    {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"},
+]
+pytest = [
+    {file = "pytest-6.2.5-py3-none-any.whl", hash = "sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134"},
+    {file = "pytest-6.2.5.tar.gz", hash = "sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89"},
+]
+pytest-asyncio = [
+    {file = "pytest-asyncio-0.19.0.tar.gz", hash = "sha256:ac4ebf3b6207259750bc32f4c1d8fcd7e79739edbc67ad0c58dd150b1d072fed"},
+    {file = "pytest_asyncio-0.19.0-py3-none-any.whl", hash = "sha256:7a97e37cfe1ed296e2e84941384bdd37c376453912d397ed39293e0916f521fa"},
+]
+pytest-mock = [
+    {file = "pytest-mock-3.8.2.tar.gz", hash = "sha256:77f03f4554392558700295e05aed0b1096a20d4a60a4f3ddcde58b0c31c8fca2"},
+    {file = "pytest_mock-3.8.2-py3-none-any.whl", hash = "sha256:8a9e226d6c0ef09fcf20c94eb3405c388af438a90f3e39687f84166da82d5948"},
+]
+python-multipart = [
+    {file = "python-multipart-0.0.5.tar.gz", hash = "sha256:f7bb5f611fc600d15fa47b3974c8aa16e93724513b49b5f95c81e6624c83fa43"},
+]
+rfc3986 = [
+    {file = "rfc3986-1.5.0-py2.py3-none-any.whl", hash = "sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97"},
+    {file = "rfc3986-1.5.0.tar.gz", hash = "sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835"},
+]
+six = [
+    {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
+    {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
+]
+sniffio = [
+    {file = "sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"},
+    {file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"},
+]
+starlette = [
+    {file = "starlette-0.19.1-py3-none-any.whl", hash = "sha256:5a60c5c2d051f3a8eb546136aa0c9399773a689595e099e0877704d5888279bf"},
+    {file = "starlette-0.19.1.tar.gz", hash = "sha256:c6d21096774ecb9639acad41b86b7706e52ba3bf1dc13ea4ed9ad593d47e24c7"},
+]
+toml = [
+    {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
+    {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},
+]
+typing-extensions = [
+    {file = "typing_extensions-4.3.0-py3-none-any.whl", hash = "sha256:25642c956049920a5aa49edcdd6ab1e06d7e5d467fc00e0506c44ac86fbfca02"},
+    {file = "typing_extensions-4.3.0.tar.gz", hash = "sha256:e6d2677a32f47fc7eb2795db1dd15c1f34eff616bcaf2cfb5e997f854fa1c4a6"},
+]
diff --git a/pyfa_converter/__init__.py b/pyfa_converter/__init__.py
index dc10b6d..3b37662 100644
--- a/pyfa_converter/__init__.py
+++ b/pyfa_converter/__init__.py
@@ -1,7 +1,15 @@
-from .utils import PydanticConverter
-from .depends import FormBody
+from .main import PydanticConverter
+from .depends import (
+    FormDepends,
+    QueryDepends,
+    BodyDepends,
+    PyFaDepends,
+)
 
 __all__ = (
-    'PydanticConverter',
-    'FormBody',
+    "PydanticConverter",
+    "FormDepends",
+    "QueryDepends",
+    "PyFaDepends",
+    "BodyDepends",
 )
diff --git a/pyfa_converter/depends.py b/pyfa_converter/depends.py
index 9da18b5..c2ce251 100644
--- a/pyfa_converter/depends.py
+++ b/pyfa_converter/depends.py
@@ -1,7 +1,47 @@
-from fastapi import Depends
+from typing import Callable
+from typing import Type
+from typing import Union
+
+from fastapi import Form, Query, Body, Depends
+from pydantic import BaseModel
+from pydantic.fields import FieldInfo
+
 from pyfa_converter import PydanticConverter
 
 
-class FormBody:
-    def __new__(cls):
-        return Depends(PydanticConverter.body)
+class PyFaDepends:
+    def __new__(
+        cls,
+        model: Type[Union[BaseModel, PydanticConverter]],
+        _type: Callable[..., FieldInfo],
+    ):
+        return cls.generate(model=model, _type=_type)
+
+    @classmethod
+    def generate(
+        cls,
+        model: Type[Union[BaseModel, PydanticConverter]],
+        _type: Callable[..., FieldInfo],
+    ):
+        obj = PydanticConverter.reformat_model_signature(model_cls=model, _type=_type)
+        attr = getattr(obj, str(_type.__name__).lower())
+        return Depends(attr)
+
+
+class BasePyFaDepends(PyFaDepends):
+    _TYPE: ...
+
+    def __new__(cls, model_type: Type[Union[BaseModel, PydanticConverter]]):
+        return super().generate(model=model_type, _type=cls._TYPE)
+
+
+class BodyDepends(BasePyFaDepends):
+    _TYPE = Body
+
+
+class FormDepends(BasePyFaDepends):
+    _TYPE = Form
+
+
+class QueryDepends(BasePyFaDepends):
+    _TYPE = Query
diff --git a/pyfa_converter/main.py b/pyfa_converter/main.py
new file mode 100644
index 0000000..d309374
--- /dev/null
+++ b/pyfa_converter/main.py
@@ -0,0 +1,114 @@
+import inspect
+from typing import Any
+from typing import Callable
+from typing import List
+from typing import Type
+from typing import Union
+
+from fastapi import Depends
+from pydantic import BaseModel
+from pydantic.fields import FieldInfo
+from pydantic.fields import ModelField
+
+
+class PydanticConverterUtils:
+    @classmethod
+    def param_maker(
+        cls, field: ModelField, _type: Callable[..., FieldInfo]
+    ) -> FieldInfo:
+        if field.required is True:
+            return cls.__fill_params(param=_type, model_field=field, default=...)
+        return cls.__fill_params(param=_type, model_field=field, default=field.default)
+
+    @classmethod
+    def __fill_params(
+        cls, param: Callable[..., FieldInfo], model_field: ModelField, default: Any
+    ) -> FieldInfo:
+        return param(
+            default=default or None,
+            alias=model_field.field_info.alias or None,
+            title=model_field.field_info.title or None,
+            description=model_field.field_info.description or None,
+            gt=model_field.field_info.gt or None,
+            ge=model_field.field_info.ge or None,
+            lt=model_field.field_info.lt or None,
+            le=model_field.field_info.le or None,
+            min_length=model_field.field_info.min_length or None,
+            max_length=model_field.field_info.max_length or None,
+            regex=model_field.field_info.regex or None,
+            **model_field.field_info.extra,
+        )
+
+    @classmethod
+    def override_signature_parameters(
+        cls,
+        model: Union[Type[BaseModel], Type["PydanticConverter"]],
+        param_maker: Callable[[ModelField], Any],
+    ) -> List[inspect.Parameter]:
+        return [
+            inspect.Parameter(
+                name=field.alias,
+                kind=inspect.Parameter.POSITIONAL_ONLY,
+                default=param_maker(field),
+                annotation=model.__annotations__.get(field.name) or field.annotation,
+            )
+            for field in model.__fields__.values()
+        ]
+
+
+class PydanticConverter(PydanticConverterUtils):
+    @classmethod
+    def reformat_model_signature(
+        cls,
+        model_cls: Union[Type[BaseModel], Type["PydanticConverter"]],
+        _type: Any,
+    ) -> Union[Type[BaseModel], Type["PydanticConverter"]]:
+        """
+        Adds an `query` class method to decorated models. The `query` class
+        method can be used with `FastAPI` endpoints.
+
+        Args:
+            model_cls: The model class to decorate.
+            _type: literal query, form, body, etc...
+
+        Returns:
+            The decorated class.
+        """
+        _type_var_name = str(_type.__name__).lower()
+
+        def make_form_parameter(field: ModelField) -> Any:
+            """
+            Converts a field from a `Pydantic` model to the appropriate `FastAPI`
+            parameter type.
+
+            Args:
+                field: The field to convert.
+
+            Returns:
+                Either the result of `Query`, if the field is not a sub-model, or
+                the result of `Depends on` if it is.
+
+            """
+            if issubclass(field.type_, BaseModel):
+                # This is a sub-model.
+                assert hasattr(field.type_, _type_var_name), (
+                    f"Sub-model class for {field.name} field must be decorated with"
+                    f" `as_form` too."
+                )
+                attr = getattr(field.type_, _type_var_name)
+                return Depends(attr)  # noqa
+            else:
+                return cls.param_maker(field=field, _type=_type)
+
+        new_params = PydanticConverterUtils.override_signature_parameters(
+            model=model_cls, param_maker=make_form_parameter
+        )
+
+        def _as_form(**data) -> Union[BaseModel, PydanticConverter]:
+            return model_cls(**data)
+
+        sig = inspect.signature(_as_form)
+        sig = sig.replace(parameters=new_params)
+        _as_form.__signature__ = sig
+        setattr(model_cls, _type_var_name, _as_form)
+        return model_cls
diff --git a/pyfa_converter/utils.py b/pyfa_converter/utils.py
index 3d1e481..b220cb2 100644
--- a/pyfa_converter/utils.py
+++ b/pyfa_converter/utils.py
@@ -9,7 +9,6 @@
 
 
 class PydanticConverter:
-
     @classmethod
     def form(cls, field: ModelField) -> Body:
         if field.required is True:
@@ -54,6 +53,5 @@ def as_form_func(*args, **kwargs):
         sig = inspect.signature(as_form_func)
         sig = sig.replace(parameters=new_parameters)
         as_form_func.__signature__ = sig
-        setattr(cls, 'body', as_form_func)
+        setattr(cls, "body", as_form_func)
         return cls
-
diff --git a/pyfa_converter/utils/__init__.py b/pyfa_converter/utils/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/pyfa_converter/utils/deprecation.py b/pyfa_converter/utils/deprecation.py
new file mode 100644
index 0000000..fe07868
--- /dev/null
+++ b/pyfa_converter/utils/deprecation.py
@@ -0,0 +1,175 @@
+import asyncio
+import inspect
+import warnings
+import functools
+from typing import Callable, Generic, TypeVar, Type, Optional
+
+
+def deprecated(reason, stacklevel=2) -> Callable:
+    """
+    This is a decorator which can be used to mark functions
+    as deprecated. It will result in a warning being emitted
+    when the function is used.
+
+    Source: https://stackoverflow.com/questions/2536307/decorators-in-the-python-standard-lib-deprecated-specifically
+    """
+
+    if isinstance(reason, str):
+
+        # The @deprecated is used with a 'reason'.
+        #
+        # .. code-block:: python
+        #
+        #    @deprecated("please, use another function")
+        #    def old_function(x, y):
+        #      pass
+
+        def decorator(func):
+
+            if inspect.isclass(func):
+                msg = "Call to deprecated class {name} ({reason})."
+            else:
+                msg = "Call to deprecated function {name} ({reason})."
+
+            @functools.wraps(func)
+            def wrapper(*args, **kwargs):
+                warn_deprecated(
+                    msg.format(name=func.__name__, reason=reason), stacklevel=stacklevel
+                )
+                warnings.simplefilter("default", DeprecationWarning)
+                return func(*args, **kwargs)
+
+            return wrapper
+
+        return decorator
+
+    if inspect.isclass(reason) or inspect.isfunction(reason):
+
+        # The @deprecated is used without any 'reason'.
+        #
+        # .. code-block:: python
+        #
+        #    @deprecated
+        #    def old_function(x, y):
+        #      pass
+
+        func1 = reason
+
+        if inspect.isclass(func1):
+            msg1 = "Call to deprecated class {name}."
+        else:
+            msg1 = "Call to deprecated function {name}."
+
+        @functools.wraps(func1)
+        def wrapper1(*args, **kwargs):
+            warn_deprecated(msg1.format(name=func1.__name__), stacklevel=stacklevel)
+            return func1(*args, **kwargs)
+
+        return wrapper1
+
+    raise TypeError(repr(type(reason)))
+
+
+def warn_deprecated(message, warning=DeprecationWarning, stacklevel=2):
+    warnings.simplefilter("always", warning)
+    warnings.warn(message, category=warning, stacklevel=stacklevel)
+    warnings.simplefilter("default", warning)
+
+
+def renamed_argument(
+    old_name: str, new_name: str, until_version: str, stacklevel: int = 3
+):
+    """
+    A meta-decorator to mark an argument as deprecated.
+
+    .. code-block:: python3
+
+        @renamed_argument("chat", "chat_id", "3.0")  # stacklevel=3 by default
+        @renamed_argument("user", "user_id", "3.0", stacklevel=4)
+        def some_function(user_id, chat_id=None):
+            print(f"user_id={user_id}, chat_id={chat_id}")
+
+        some_function(user=123)  #  prints 'user_id=123, chat_id=None' with warning
+        some_function(123)  #  prints 'user_id=123, chat_id=None' without warning
+        some_function(user_id=123)  #  prints 'user_id=123, chat_id=None' without warning
+
+
+    :param old_name:
+    :param new_name:
+    :param until_version: the version in which the argument is scheduled to be removed
+    :param stacklevel: leave it to default if it's the first decorator used.
+    Increment with any new decorator used.
+    :return: decorator
+    """
+
+    def decorator(func):
+        is_coroutine = asyncio.iscoroutinefunction(func)
+
+        def _handling(kwargs):
+            """
+            Returns updated version of kwargs.
+            """
+            routine_type = "coroutine" if is_coroutine else "function"
+            if old_name in kwargs:
+                warn_deprecated(
+                    f"In {routine_type} '{func.__name__}' argument '{old_name}' "
+                    f"is renamed to '{new_name}' "
+                    f"and will be removed in aiogram {until_version}",
+                    stacklevel=stacklevel,
+                )
+                kwargs = kwargs.copy()
+                kwargs.update({new_name: kwargs.pop(old_name)})
+            return kwargs
+
+        if is_coroutine:
+
+            @functools.wraps(func)
+            async def wrapped(*args, **kwargs):
+                kwargs = _handling(kwargs)
+                return await func(*args, **kwargs)
+
+        else:
+
+            @functools.wraps(func)
+            def wrapped(*args, **kwargs):
+                kwargs = _handling(kwargs)
+                return func(*args, **kwargs)
+
+        return wrapped
+
+    return decorator
+
+
+_VT = TypeVar("_VT")
+_OwnerCls = TypeVar("_OwnerCls")
+
+
+class DeprecatedReadOnlyClassVar(Generic[_OwnerCls, _VT]):
+    """
+    DeprecatedReadOnlyClassVar[Owner, ValueType]
+
+    :param warning_message: Warning message when getter gets called
+    :param new_value_getter: Any callable with (owner_class: Type[Owner]) -> ValueType
+                             signature that will be executed
+
+    Usage example:
+
+    >>> class MyClass:
+    ...     some_attribute: DeprecatedReadOnlyClassVar[MyClass, int] = \
+    ...            DeprecatedReadOnlyClassVar(
+    ...                  "Warning message.", lambda owner: 15)
+    ...
+    >>> MyClass.some_attribute  # does warning.warn with `Warning message` and returns 15 in the current case
+    """
+
+    __slots__ = "_new_value_getter", "_warning_message"
+
+    def __init__(
+        self, warning_message: str, new_value_getter: Callable[[_OwnerCls], _VT]
+    ):
+        self._warning_message = warning_message
+        self._new_value_getter = new_value_getter
+
+    def __get__(self, instance: Optional[_OwnerCls], owner: Type[_OwnerCls]):
+        warn_deprecated(self._warning_message, stacklevel=3)
+        return self._new_value_getter(owner)
diff --git a/pyfa_converter/utils/deprecation_message.py b/pyfa_converter/utils/deprecation_message.py
new file mode 100644
index 0000000..5fcdbe9
--- /dev/null
+++ b/pyfa_converter/utils/deprecation_message.py
@@ -0,0 +1,10 @@
+DEPRECATION_MESSAGE = (
+    "This decorator is deprecated.\n"
+    "The decorator above the model is no longer required and will be removed in the next version!\n"
+    "Use:\n"
+    "data: MyCustomModel = PyFaDepends(MyCustomModel, _type=Header)\n"
+    "data: MyCustomModel = PyFaDepends(MyCustomModel, _type=Form)\n"
+    "or\n\n"
+    "data: MyCustomModel = FormDepends(MyCustomModel)\n"
+    "data: MyCustomModel = QueryDepends(MyCustomModel)"
+)
diff --git a/pyproject.toml b/pyproject.toml
index 6f39c52..0b0e42e 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,17 +1,42 @@
 [tool.poetry]
 name = "pyfa-converter"
-version = "0.1.0"
+version = "1.0.1.0"
 description = "Pydantic to fastapi model converter."
 authors = ["dotX12 <dev@shitposting.team>"]
+license = "MIT License"
+homepage = "https://github.com/dotX12/pyfa-converter"
+repository = "https://github.com/dotX12/pyfa-converter"
+readme = "README.md"
+
+include = [
+    "README.md",
+    "LICENSE.md"
+]
 
 [tool.poetry.dependencies]
-python = "^3.7"
-fastapi = "^0.75.1"
-pydantic = "^1.9.0"
+python = ">= 3.8"
+fastapi = ">= 0.65"
+pydantic = ">= 1.6"
 python-multipart = "^0.0.5"
 
 [tool.poetry.dev-dependencies]
 
+[tool.poetry.group.tests]
+optional = true
+
+[tool.poetry.group.tests.dependencies]
+pytest = "^6.0.0"
+httpx = "^0.23.0"
+pytest_asyncio = "^0.19.0"
+pytest-mock = "*"
+
+
 [build-system]
-requires = ["poetry-core>=1.0.0"]
+requires = ["poetry-core>=1.2.0"]
 build-backend = "poetry.core.masonry.api"
+
+
+[tool.pytest.ini_options]
+addopts = "-scoped"
+asyncio_mode = "auto"
+filterwarnings = ["ignore::DeprecationWarning"]


Test patch:
diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml
new file mode 100644
index 0000000..8f08165
--- /dev/null
+++ b/.github/workflows/pytest.yml
@@ -0,0 +1,69 @@
+name: test
+
+on: pull_request
+
+jobs:
+  test:
+    strategy:
+      fail-fast: true
+      matrix:
+        os: [ "ubuntu-latest", "macos-latest" ]
+        python-version: [ '3.7', '3.8', '3.9', '3.10' ]
+    name: Python ${{ matrix.python-version }} OS ${{ matrix.os }}
+    runs-on: ${{ matrix.os }}
+    steps:
+      #----------------------------------------------
+      #       check-out repo and set-up python
+      #----------------------------------------------
+      - name: Check out repository
+        uses: actions/checkout@v2
+      - name: Set up python ${{ matrix.python-version }}
+        id: setup-python
+        uses: actions/setup-python@v2
+        with:
+          python-version: ${{ matrix.python-version }}
+
+      #----------------------------------------------
+      #  -----  install & configure poetry  -----
+      #----------------------------------------------
+      - name: Install Poetry
+        uses: snok/install-poetry@v1
+        with:
+          version: 1.2.0
+          virtualenvs-path: .venv
+          virtualenvs-create: true
+          virtualenvs-in-project: false
+          installer-parallel: true
+
+      #----------------------------------------------
+      #       load cached venv if cache exists
+      #----------------------------------------------
+      - name: Load cached venv
+        id: cached-poetry-dependencies
+        uses: actions/cache@v2
+        with:
+          path: .venv
+          key: venv-${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('**/poetry.lock') }}
+      #----------------------------------------------
+      # install dependencies if cache does not exist
+      #----------------------------------------------
+
+      - name: Install dependencies
+        if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true'
+        run: poetry install --no-interaction --no-root
+      #----------------------------------------------
+      # install your root project, if required
+      #----------------------------------------------
+      - name: Install library
+        run: poetry install --no-interaction --with tests
+      #----------------------------------------------
+      #              run test suite
+      #----------------------------------------------
+      - name: Check black
+        run: |
+          poetry add --dev black --allow-prereleases
+          poetry run black . --check
+
+      - name: Run tests
+        run: |
+          poetry run pytest
\ No newline at end of file
diff --git a/tests/body_test.py b/tests/body_test.py
new file mode 100644
index 0000000..9c03f91
--- /dev/null
+++ b/tests/body_test.py
@@ -0,0 +1,29 @@
+import datetime
+
+import pytest
+from httpx import AsyncClient
+
+
+@pytest.mark.asyncio
+async def tests_one(async_client: AsyncClient):
+
+    data = {
+        "title": "KEY1",
+        "date": datetime.datetime.now().isoformat(),
+        "amount": 10212.11,
+        "unit_price": 12012.12,
+    }
+    files = {"document": (".gitignore", open(".gitignore", "rb"), "text/plain")}
+
+    response_post = await async_client.post(
+        url="/form-data-body",
+        data=data,
+        files=files,
+    )
+
+    assert response_post.status_code in [200, 201]
+    assert response_post.json() == {
+        "title": "KEY1",
+        "date": "2022-09-13",
+        "file_name": ".gitignore",
+    }
diff --git a/tests/conftest.py b/tests/conftest.py
new file mode 100644
index 0000000..3d6e5ea
--- /dev/null
+++ b/tests/conftest.py
@@ -0,0 +1,20 @@
+import asyncio
+from typing import Generator
+from httpx import AsyncClient
+import pytest_asyncio
+
+from examples.main import app
+
+
+@pytest_asyncio.fixture(scope="session", autouse=True)
+def event_loop():
+    loop = asyncio.get_event_loop_policy().new_event_loop()
+    yield loop
+
+    loop.close()
+
+
+@pytest_asyncio.fixture(scope="module")
+async def async_client() -> Generator:
+    async with AsyncClient(app=app, base_url="http://testserver") as client_:
+        yield client_
diff --git a/tests/query_test.py b/tests/query_test.py
new file mode 100644
index 0000000..d950463
--- /dev/null
+++ b/tests/query_test.py
@@ -0,0 +1,17 @@
+import pytest
+from httpx import AsyncClient
+
+
+@pytest.mark.asyncio
+async def tests_one(async_client: AsyncClient):
+    params = {"id": 100, "title": "test", "data": [1, 2, 3, 4, 5]}
+
+    response_post = await async_client.post(
+        url="/test_query_list",
+        params=params,
+    )
+
+    assert response_post.status_code in [200, 201]
+    assert response_post.json() == {
+        "data": {"id": 100, "title": "test", "data": [1, 2, 3, 4, 5]}
+    }