Skip to content

Commit

Permalink
chore(examples): Provide Todo-Backend impelmentation
Browse files Browse the repository at this point in the history
This will help to ensure that further class based views support works as
expected.

Issue: #15, #45
  • Loading branch information
playpauseandstop committed Apr 19, 2020
1 parent 3c46dce commit 68756f4
Show file tree
Hide file tree
Showing 19 changed files with 789 additions and 17 deletions.
9 changes: 9 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,15 @@ jobs:
if: "matrix.python-version == env.DEV_PYTHON_VERSION"
uses: "pre-commit/action@v1.0.1"

- name: "Validate OpenAPI schemas"
if: "matrix.python-version == env.DEV_PYTHON_VERSION"
run: |
python -m openapi_spec_validator examples/hobotnica/openapi.yaml
python -m openapi_spec_validator examples/petstore/petstore-expanded.yaml
python -m openapi_spec_validator examples/todobackend/openapi.yaml
python -m openapi_spec_validator tests/openapi.json
python -m openapi_spec_validator tests/openapi.yaml
- name: "Run tests"
run: |
python -m tox
Expand Down
9 changes: 8 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,14 @@ list-outdated: install
open-docs: docs
open $(DOCS_DIR)/_build/html/index.html

test: install clean lint test-only
test: install clean lint validate test-only

test-only:
TOXENV=$(TOXENV) $(TOX) $(TOX_ARGS) -- $(TEST_ARGS)

validate: install
$(PYTHON) -m openapi_spec_validator examples/hobotnica/openapi.yaml
$(PYTHON) -m openapi_spec_validator examples/petstore/petstore-expanded.yaml
$(PYTHON) -m openapi_spec_validator examples/todobackend/openapi.yaml
$(PYTHON) -m openapi_spec_validator tests/openapi.json
$(PYTHON) -m openapi_spec_validator tests/openapi.yaml
20 changes: 20 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,26 @@ schema file.
)
return app
Class Based Views
-----------------

``@operations.register`` supports decorating `class based views <https://docs.aiohttp.org/en/stable/web_quickstart.html#aiohttp-web-class-based-views>`_
as well.

.. code-block:: python
class UserView(web.View):
@operation.register("me")
async def get(self) -> web.Response:
...
@operation.register("update_profile")
async def put(self) -> web.Response:
...
Examples
--------

Check
`examples <https://github.com/playpauseandstop/rororo/tree/master/examples>`_
folder to see other examples on how to use OpenAPI 3 schemas with aiohttp.web
Expand Down
14 changes: 14 additions & 0 deletions examples/hobotnica/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
=========
Hobotnica
=========

OpenAPI Schema for lightweight CI/CD tool, named Hobotnica.

Usage
=====

.. code-block:: bash
make EXAMPLE=hobotnica example
**IMPORTANT:** To run from ``rororo`` root directory.
4 changes: 3 additions & 1 deletion examples/petstore/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,6 @@ Usage

.. code-block:: bash
python -m aiohttp.web petstore.app:create_app
make EXAMPLE=petstore example
**IMPORTANT:** To run from ``rororo`` root directory.
24 changes: 24 additions & 0 deletions examples/todobackend/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
============
Todo-Backend
============

`Todo-Backend <http://todobackend.com>`_ implementation built on top of
``rororo``, which highlights class based views usage.

Requirements
============

- `redis <https://redis.io>`_ 5.0 or later

Usage
=====

.. code-block:: bash
make EXAMPLE=todobackend example
**IMPORTANT:** To run from ``rororo`` root directory.

After, feel free to run Todo-Backend tests by opening
http://www.todobackend.com/specs/index.html?http://localhost:8080/todos in
your browser.
Empty file.
54 changes: 54 additions & 0 deletions examples/todobackend/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
from pathlib import Path
from typing import AsyncIterator, List

from aiohttp import web
from aioredis import create_redis

from rororo import setup_openapi, setup_settings
from rororo.settings import APP_SETTINGS_KEY
from . import views
from .constants import APP_REDIS_KEY, APP_STORAGE_KEY
from .settings import Settings
from .storage import Storage


def create_app(
argv: List[str] = None, *, settings: Settings = None
) -> web.Application:
if settings is None:
settings = Settings.from_environ() # type: ignore

app = setup_openapi(
setup_settings(
web.Application(),
settings,
loggers=(
"aiohttp",
"aiohttp_middlewares",
"rororo",
"todobackend",
),
remove_root_handlers=True,
),
Path(__file__).parent / "openapi.yaml",
views.operations,
cors_middleware_kwargs={"origins": ("http://www.todobackend.com",)},
)
app.cleanup_ctx.append(storage_context)
return app


async def storage_context(app: web.Application) -> AsyncIterator[None]:
settings: Settings = app[APP_SETTINGS_KEY]

redis = app[APP_REDIS_KEY] = await create_redis(
settings.redis_url, encoding="utf-8"
)
app[APP_STORAGE_KEY] = Storage(
redis=redis, data_key=settings.redis_data_key
)

yield

redis.close()
await redis.wait_closed()
2 changes: 2 additions & 0 deletions examples/todobackend/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
APP_REDIS_KEY = "redis"
APP_STORAGE_KEY = "storage"
58 changes: 58 additions & 0 deletions examples/todobackend/data.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import uuid
from random import choice
from typing import TypedDict

import attr
from aiohttp import web
from yarl import URL

from rororo.annotations import DictStrStr
from rororo.utils import to_bool


class TodoAPIDict(TypedDict):
uid: str
title: str
order: int
url: str
completed: bool


@attr.dataclass
class Todo:
title: str

uid: uuid.UUID = attr.Factory(uuid.uuid4)
order: int = 0
completed: bool = False

@classmethod
def from_storage(cls, data: DictStrStr, *, uid: uuid.UUID) -> "Todo":
return cls(
uid=uid,
title=data["title"],
order=int(data["order"]),
completed=to_bool(data["completed"]),
)

def get_absolute_url(self, *, request: web.Request) -> URL:
route_name = choice(["retrieve_todo", "update_todo", "delete_todo"])
return request.url.with_path( # type: ignore
str(request.app.router[route_name].url_for(todo_uid=str(self.uid)))
)

def to_api_dict(self, *, request: web.Request) -> TodoAPIDict:
return {
"uid": str(self.uid),
"title": self.title,
"order": self.order,
"url": str(self.get_absolute_url(request=request)),
"completed": self.completed,
}

def to_storage(self) -> DictStrStr:
return {
"title": self.title,
"order": str(self.order),
"completed": str(int(self.completed)),
}
Loading

0 comments on commit 68756f4

Please sign in to comment.