-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Adding tutorial stub * Adding part0 application * Adding more structure for the base application
- Loading branch information
Showing
27 changed files
with
440 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,3 +9,4 @@ coverage.json | |
htmlcov | ||
.DS_Store | ||
reports | ||
db.sqlite |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
Tutorial | ||
======== | ||
|
||
How to make an application with Cannula. In this tutorial we will create | ||
a dashboard application. This will use `Cannula` and `FastAPI` as the backend | ||
and a simple UI with `Jinja` templates. Then we will add a reactive UI with | ||
a small set of Javascript libraries `VanJS` and `graphql-request`. | ||
|
||
One of the great things about GraphQL schema first design is there is a ton | ||
of tooling in place to auto generate much of the code. This can be as simple | ||
as the base types you use or as complex as Apollo client bindings for | ||
`React` or `Angular`. | ||
|
||
Since `Cannula` is a Python library we'll focus more on the server-side, and | ||
not too much on the UI. Our application will have a number of detail views | ||
plus a dashboard to show some high level view of all the data. All the code | ||
samples are in the git repo so you can try each step or just skip to the end | ||
and run the full application (Chester Cheata'). | ||
|
||
Head over to :doc:`part0` | ||
|
||
Contents | ||
-------- | ||
|
||
.. toctree:: | ||
:maxdepth: 1 | ||
|
||
part0 | ||
part1 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
Installation and Setup | ||
====================== | ||
|
||
.. note:: | ||
|
||
To follow along with this tutorial you'll need a few things: | ||
|
||
* Python 3.8+ | ||
* Node 18+ (hint use nvm) | ||
* (Optional) GNU Make | ||
|
||
Checkout the Code | ||
----------------- | ||
|
||
The easiest way to follow along is to checkout the `Cannula` repo and open up | ||
the `examples/tutorial` folders. We have the code for each section as we add | ||
complexity to the application. You'll want to start at `part0` and move back | ||
and forth as needed until you have mastered GraphQL:: | ||
|
||
git clone git@github.com:rmyers/cannula.git | ||
cd cannula/examples/tutorial | ||
|
||
We like `GNU Make` and use `Makefiles` for all our projects as it simplifies | ||
setup especially for beginners. In this folder you'll find a `Makefile` that | ||
has `setup` and `help` target:: | ||
|
||
make setup | ||
|
||
.. note:: | ||
|
||
If you are on MacOS you may need to install the xcode command line tools:: | ||
|
||
xcode-select --install | ||
|
||
This will create a virtualenv `venv` with all the dependencies we need installed: | ||
|
||
.. literalinclude:: ../examples/tutorial/requirements.txt | ||
|
||
|
||
Create the initial application | ||
------------------------------ | ||
|
||
We'll call our application `dashboard` since we are not creative. This will just | ||
need the following stucture:: | ||
|
||
dashboard/ | ||
core/ | ||
app.py # The FastAPI application | ||
config.py # Configuration settings for the application | ||
database.py # Database schema | ||
part1...n/ # Remaining sections of this tutorial | ||
templates/ | ||
index.html | ||
__init__.py | ||
__main__.py # Click commands to run tasks `python -m dashboard <command>` | ||
|
||
We are going to use FastAPI since it is a very good ASGI application base. Since | ||
it does not have any actual webserver process, we will need to serve the application | ||
with uvicorn. `Jinja2` is used for the templates so there is a little bit of setup | ||
we have to do in order to server the application. | ||
|
||
To start up the application just run the command:: | ||
|
||
make run | ||
|
||
Then open your browser and visit `http://localhost:8000`:(http://localhost:8000) | ||
|
||
For this tutorial we have setup 100% unit test coverage. We feel like it is best to show | ||
by example, and the best developers write tests. Maintaining 100% coverage is easier if | ||
you start with full coverage. This can then be enforced with a single line `fail_under = 100`. | ||
What we really like about this is that it makes CI do the dirty work of scolding developers | ||
that do not write tests. Nobody likes that person on the team who constantly nit picks PR's. | ||
It is best for that person to be a machine, one it is less personal, and two they can't be | ||
bribed for a +1. | ||
|
||
For our tests we are using pytest and a few plugins, the most important one being | ||
`pytest-asyncio`. This plugin makes it easy to write tests for our async handlers we have | ||
this set to `auto` mode in our configuration: | ||
|
||
.. literalinclude:: ../examples/tutorial/setup.cfg | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
Setup Schema | ||
============ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
REQUIREMENTS := requirements.txt | ||
SHELL := /bin/bash | ||
VIRTUAL_ENV ?= venv | ||
|
||
# PHONY just means this target does not make any files | ||
.PHONY: setup clean test help | ||
|
||
default: help | ||
|
||
# Make sure the virtualenv exists, create it if not. | ||
$(VIRTUAL_ENV): | ||
python3 -m venv $(VIRTUAL_ENV) | ||
|
||
# Check for the existence/timestamp of .reqs-installed if the | ||
# file is missing or older than the requirements.txt this will run pip | ||
$(VIRTUAL_ENV)/.reqs-installed: $(REQUIREMENTS) | ||
$(VIRTUAL_ENV)/bin/pip install -r $(REQUIREMENTS) | ||
touch $(VIRTUAL_ENV)/.reqs-installed | ||
|
||
setup: $(VIRTUAL_ENV) $(VIRTUAL_ENV)/.reqs-installed ## Setup local environment | ||
|
||
clean: ## Clean your local workspace | ||
rm -rf $(VIRTUAL_ENV) | ||
rm -rf htmlcov | ||
rm -rf .coverage | ||
rm -rf *.egg-info | ||
rm -f db.sqlite | ||
find . -name '*.py[co]' -delete | ||
|
||
test: setup ## Test the code | ||
DATABASE_URI="sqlite+aiosqlite:///:memory:" $(VIRTUAL_ENV)/bin/pytest --cov dashboard --cov-config=setup.cfg | ||
|
||
format: ## Format the code with black | ||
$(VIRTUAL_ENV)/bin/black . | ||
|
||
run: setup ## Run the application | ||
$(VIRTUAL_ENV)/bin/python -m dashboard run | ||
|
||
initdb: setup ## Create database tables | ||
$(VIRTUAL_ENV)/bin/python -m dashboard initdb | ||
|
||
help: ## Show the available commands | ||
@grep '^[a-zA-Z]' $(MAKEFILE_LIST) | awk -F ':.*?## ' 'NF==2 {printf " %-20s%s\n", $$1, $$2}' | sort |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import asyncio | ||
|
||
import click | ||
import uvicorn | ||
|
||
from dashboard.core.app import app | ||
from dashboard.core.database import create_tables | ||
|
||
|
||
@click.group() | ||
def cli(): # pragma: no cover | ||
pass | ||
|
||
|
||
@click.command() | ||
def initdb(): # pragma: no cover | ||
click.echo("Initialized the database") | ||
loop = asyncio.get_event_loop() | ||
loop.run_until_complete(create_tables()) | ||
|
||
|
||
@click.command() | ||
def run(): # pragma: no cover | ||
uvicorn.run(app) | ||
|
||
|
||
cli.add_command(initdb) | ||
cli.add_command(run) | ||
|
||
if __name__ == "__main__": # pragma: no cover | ||
cli() |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
from contextlib import asynccontextmanager | ||
|
||
from fastapi import FastAPI, Request | ||
|
||
from dashboard.core.config import config | ||
from dashboard.core.database import create_tables | ||
|
||
|
||
@asynccontextmanager | ||
async def lifespan(app: FastAPI): | ||
# Make sure the database has been created | ||
await create_tables() | ||
# Run the app | ||
yield | ||
# tear down things right now there is nothing to do | ||
|
||
|
||
app = FastAPI( | ||
debug=config.debug, | ||
lifespan=lifespan, | ||
) | ||
|
||
|
||
@app.get("/") | ||
def home(request: Request): | ||
return config.templates.TemplateResponse(request, "index.html") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import pathlib | ||
import functools | ||
|
||
from fastapi.templating import Jinja2Templates | ||
from pydantic_settings import BaseSettings | ||
from sqlalchemy.ext.asyncio import create_async_engine | ||
|
||
DASHBOARD_ROOT = pathlib.Path(__file__).parent.parent | ||
|
||
|
||
class Config(BaseSettings): | ||
database_uri: str = "sqlite+aiosqlite:///db.sqlite" | ||
debug: bool = True | ||
template_dir: str = "templates" | ||
|
||
@functools.cached_property | ||
def root(self): | ||
return DASHBOARD_ROOT | ||
|
||
@functools.cached_property | ||
def templates(self): | ||
return Jinja2Templates(self.root / self.template_dir) | ||
|
||
@functools.cached_property | ||
def engine(self): | ||
return create_async_engine(self.database_uri) | ||
|
||
|
||
config = Config() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
from sqlalchemy import MetaData, Table, Column, Integer, String | ||
|
||
from dashboard.core.config import config | ||
|
||
|
||
metadata = MetaData() | ||
|
||
|
||
user_table = Table( | ||
"user_account", | ||
metadata, | ||
Column("id", Integer, primary_key=True), | ||
Column("name", String(100), nullable=False), | ||
Column("email", String(255), nullable=False), | ||
Column("password", String(30), nullable=False), | ||
) | ||
|
||
project = Table( | ||
"project", | ||
metadata, | ||
Column("id", Integer, primary_key=True), | ||
Column("user_id", Integer, nullable=False), | ||
Column("name", String(100), nullable=False), | ||
Column("title", String(255)), | ||
Column("type", String(30)), | ||
) | ||
|
||
|
||
async def create_tables() -> None: | ||
async with config.engine.begin() as conn: | ||
await conn.run_sync(metadata.create_all) |
Oops, something went wrong.