template project

This commit is contained in:
Ruidy 2020-10-22 13:07:38 +02:00
commit 4723d7734b
25 changed files with 399 additions and 0 deletions

148
.gitignore vendored Normal file
View file

@ -0,0 +1,148 @@
# Created by .ignore support plugin (hsz.mobi)
### Python template
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
.idea/.gitignore
.idea/dictionaries
.idea/inspectionProfiles/
.idea/misc.xml
.idea/modules.xml
.idea/todo-graphql.iml
Pipfile.lock

16
Pipfile Normal file
View file

@ -0,0 +1,16 @@
[[source]]
name = "pypi"
url = "https://pypi.org/simple"
verify_ssl = true
[dev-packages]
yapf = "*"
[packages]
fastapi = "*"
uvicorn = "*"
graphene = "*"
pydantic = "*"
[requires]
python_version = "3.8"

9
README.md Normal file
View file

@ -0,0 +1,9 @@
# Todo GraphQL
Python GraphQL API template application
## Built with
- [FastAPI](https://fastapi.tiangolo.com/)
- [Graphene](https://graphene-python.org/)

0
app/__init__.py Normal file
View file

11
app/main.py Normal file
View file

@ -0,0 +1,11 @@
import graphene
from fastapi import FastAPI
from starlette.graphql import GraphQLApp
from app.schema.mutations.mutations import Mutations
from app.schema.queries.todo import TodoQuery
app = FastAPI()
app.add_route(
"/",
GraphQLApp(schema=graphene.Schema(query=TodoQuery, mutation=Mutations)))

0
app/models/__init__.py Normal file
View file

14
app/models/todo.py Normal file
View file

@ -0,0 +1,14 @@
from uuid import uuid4
class Todo:
def __init__(self,
todo_id: str = None,
title: str = '',
is_done: bool = False):
self.todo_id = todo_id or str(uuid4())
self.title = title
self.is_done = is_done
def __repr__(self):
return f"Todo: {self.todo_id}, {self.title}, {self.is_done}"

View file

43
app/repositories/todos.py Normal file
View file

@ -0,0 +1,43 @@
from typing import List
from app.models.todo import Todo
todo_list: List[Todo] = [
Todo(title='First'),
Todo(todo_id="ec73296f-e108-46a3-bfb3-b4237cb072ba", title='Second')
]
def get_all_todos() -> List[Todo]:
return todo_list
def get_todo_by_id(todo_id: str) -> Todo:
return [todo for todo in todo_list if todo.todo_id == todo_id][0]
def add_todo(title: str) -> Todo:
todo = Todo(title=title)
todo_list.append(todo)
return todo
def edit_todo(todo_id: str, data) -> Todo:
todo = get_todo_by_id(todo_id)
if title := data.get("title"):
todo.title = title
if is_done := data.get("is_done"):
todo.is_done = is_done
return todo
def todo_exists(todo_id: str) -> bool:
return any([todo.todo_id == todo_id for todo in todo_list])
def remove_todo(todo_id: str) -> Todo:
todo = get_todo_by_id(todo_id)
index = todo_list.index(todo)
return todo_list.pop(index)

0
app/schema/__init__.py Normal file
View file

View file

@ -0,0 +1,3 @@
from .create_todo import CreateTodo
from .delete_todo import DeleteTodo
from .update_todo import UpdateTodo

View file

@ -0,0 +1,15 @@
import graphene
from app.schema.types.todo import TodoType
from app.usecases import create_todo
class CreateTodo(graphene.Mutation):
class Arguments:
title = graphene.String(default_value="")
todo = graphene.Field(TodoType)
def mutate(self, info, title: str):
todo = create_todo(title)
return CreateTodo(todo=todo)

View file

@ -0,0 +1,15 @@
import graphene
from app.schema.types.todo import TodoType
from app.usecases import delete_todo
class DeleteTodo(graphene.Mutation):
class Arguments:
todo_id = graphene.String(required=True)
todo = graphene.Field(TodoType)
def mutate(self, info, todo_id: str):
todo = delete_todo(todo_id)
return DeleteTodo(todo=todo)

View file

@ -0,0 +1,9 @@
import graphene
from . import CreateTodo, DeleteTodo, UpdateTodo
class Mutations(graphene.ObjectType):
create_todo = CreateTodo.Field()
update_todo = UpdateTodo.Field()
delete_todo = DeleteTodo.Field()

View file

@ -0,0 +1,16 @@
import graphene
from app.models.todo import Todo
from app.schema.types.todo import TodoInputType, TodoType
from app.usecases import update_todo
class UpdateTodo(graphene.Mutation):
class Arguments:
todo = TodoInputType()
todo = graphene.Field(TodoType)
def mutate(self, info, todo: Todo):
res = update_todo(todo.todo_id, todo.__dict__)
return UpdateTodo(todo=res)

View file

View file

@ -0,0 +1,28 @@
from typing import List
import graphene
from app.models.todo import Todo
from app.schema.types.todo import TodoType, TodoResponseField
from app.usecases import read_all_todos, read_todo_by_id
class TodoQuery(graphene.ObjectType):
"""
Defines the query and how to interact with
"""
list_todos = graphene.Field(graphene.List(TodoType))
def resolve_list_todos(self, info) -> List[Todo]:
return read_all_todos()
get_todo = graphene.Field(TodoResponseField,
todo_id=graphene.String(required=True))
def resolve_get_todo(self, info, todo_id: str) -> TodoResponseField:
todo, is_success = read_todo_by_id(todo_id)
error_message = "This element does not exist." if not is_success else None
return TodoResponseField(todo=todo,
is_success=is_success,
error_message=error_message)

View file

29
app/schema/types/todo.py Normal file
View file

@ -0,0 +1,29 @@
import graphene
class TodoType(graphene.ObjectType):
"""
Query Object Type
"""
todo_id = graphene.String()
title = graphene.String(default_value="")
is_done = graphene.Boolean(default_value=False)
class TodoInputType(graphene.InputObjectType):
"""
Mutation Input Object Type
"""
todo_id = graphene.String()
title = graphene.String(default_value="")
is_done = graphene.Boolean(default_value=False)
class ResponseField(graphene.ObjectType):
is_success = graphene.Boolean(default_value=True)
error_message = graphene.String()
class TodoResponseField(ResponseField):
todo = graphene.Field(TodoType)

4
app/usecases/__init__.py Normal file
View file

@ -0,0 +1,4 @@
from .create_todo import create_todo
from .delete_todo import delete_todo
from .read_all_todos import read_all_todos
from .read_todo_by_id import read_todo_by_id

View file

@ -0,0 +1,6 @@
from app.models.todo import Todo
from app.repositories.todos import add_todo
def create_todo(title: str = '') -> Todo:
return add_todo(title)

View file

@ -0,0 +1,6 @@
from app.repositories.todos import todo_exists, remove_todo
def delete_todo(todo_id: str) -> None:
if todo_exists(todo_id):
return remove_todo(todo_id)

View file

@ -0,0 +1,8 @@
from typing import List
from app.models.todo import Todo
from app.repositories.todos import get_all_todos
def read_all_todos() -> List[Todo]:
return get_all_todos()

View file

@ -0,0 +1,11 @@
from typing import Tuple
from app.models.todo import Todo
from app.repositories.todos import get_todo_by_id
def read_todo_by_id(todo_id: str) -> Tuple[Todo, bool]:
try:
return get_todo_by_id(todo_id), True
except IndexError:
return None, False

View file

@ -0,0 +1,8 @@
from app.models.todo import Todo
from app.repositories.todos import edit_todo, todo_exists
def update_todo(todo_id: str, data) -> Todo:
if todo_exists(todo_id):
return edit_todo(todo_id, data)
return None