Skip to content

Commit

Permalink
feat: Add view controllers (#407)
Browse files Browse the repository at this point in the history
* feat: Add view controllers

* fix: remove = character

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* test: add integration tests for views

* fix: pytest namespacing options

* fix: tests

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* style: Refactor code to make things cleaner

---------

Co-authored-by: Mikaeel <[email protected]>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Sanskar Jethi <[email protected]>
  • Loading branch information
4 people authored Feb 19, 2023
1 parent 505b012 commit 5f437d1
Show file tree
Hide file tree
Showing 9 changed files with 136 additions and 6 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/python-CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,4 @@ jobs:
pip install --no-index --find-links=dist/ robyn
- name: Test with pytest
run: |
pytest ./integration_tests
pytest
27 changes: 27 additions & 0 deletions integration_tests/base_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
from robyn.robyn import Response
from robyn.templating import JinjaTemplate

from views import SyncView, AsyncView

app = Robyn(__file__)
websocket = WS(app, "/web_socket")

Expand Down Expand Up @@ -482,6 +484,29 @@ async def async_raise():
raise Exception()


# ===== Views =====


@app.view("/sync/view/decorator")
def sync_decorator_view():
def get():
return "Hello, world!"

def post(request):
body = bytearray(request["body"]).decode("utf-8")
return {"status_code": 200, "body": body}


@app.view("/async/view/decorator")
def async_decorator_view():
async def get():
return "Hello, world!"

async def post(request):
body = bytearray(request["body"]).decode("utf-8")
return {"status_code": 200, "body": body}


# ===== Main =====


Expand All @@ -493,4 +518,6 @@ async def async_raise():
index_file="index.html",
)
app.startup_handler(startup_handler)
app.add_view("/sync/view", SyncView)
app.add_view("/async/view", AsyncView)
app.start(port=8080)
41 changes: 41 additions & 0 deletions integration_tests/test_views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
from helpers.http_methods_helpers import get, post


def test_get_sync_view(session):
r = get("/sync/view")
assert r.text == "Hello, world!"


def test_post_sync_view(session):
r = post("/sync/view", data={"name": "John"})
assert "John" in r.text


def test_get_sync_decorator_view(session):
r = get("/sync/view/decorator")
assert r.text == "Hello, world!"


def test_post_sync_decorator_view(session):
r = post("/sync/view/decorator", data={"name": "John"})
assert "John" in r.text


def test_get_async_view(session):
r = get("/async/view")
assert r.text == "Hello, world!"


def test_post_async_view(session):
r = post("/async/view", data={"name": "John"})
assert "John" in r.text


def test_get_async_decorator_view(session):
r = get("/async/view/decorator")
assert r.text == "Hello, world!"


def test_post_async_decorator_view(session):
r = post("/async/view/decorator", data={"name": "John"})
assert "John" in r.text
4 changes: 4 additions & 0 deletions integration_tests/views/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from .sync_view import SyncView
from .async_view import AsyncView

__all__ = ["SyncView", "AsyncView"]
11 changes: 11 additions & 0 deletions integration_tests/views/async_view.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
def AsyncView():
async def get():
return "Hello, world!"

async def post(request):
body = bytes(request["body"]).decode("utf-8")
return {
"status": 200,
"body": body,
"headers": {"Content-Type": "text/json"},
}
11 changes: 11 additions & 0 deletions integration_tests/views/sync_view.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
def SyncView():
def get():
return "Hello, world!"

def post(request):
body = bytes(request["body"]).decode("utf-8")
return {
"status": 200,
"body": body,
"headers": {"Content-Type": "text/json"},
}
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ name = "robyn"
dependencies = [
'watchdog == 2.2.1',
'multiprocess == 0.70.14',
'nestd==0.3.1',
# conditional
'uvloop == 0.17.0; sys_platform == "darwin"',
'uvloop == 0.17.0; platform_machine == "x86_64"',
Expand Down Expand Up @@ -43,7 +44,6 @@ Changelog = "https://github.com/sansyrox/robyn/blob/main/CHANGELOG.md"
[project.optional-dependencies]
"templating" = ["jinja2 == 3.0.1"]


[tool.ruff]
line-length = 160
exclude = ["src/*" , ".git" , "docs"]
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ uvloop; platform_system!="Windows"
watchdog==2.2.1
multiprocess==0.70.14
jinja2==3.1.2
nestd==0.3.1
43 changes: 39 additions & 4 deletions robyn/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import os
import signal
from typing import Callable, List, Optional
from nestd import get_all_nested

from watchdog.observers import Observer

Expand Down Expand Up @@ -39,11 +40,11 @@ def __init__(self, file_object: str) -> None:

def _add_route(self, route_type, endpoint, handler, is_const=False):
"""
[This is base handler for all the decorators]
This is base handler for all the decorators
:param route_type [str]: [route type between GET/POST/PUT/DELETE/PATCH]
:param endpoint [str]: [endpoint for the route added]
:param handler [function]: [represents the sync or async function passed as a handler for the route]
:param route_type str: route type between GET/POST/PUT/DELETE/PATCH
:param endpoint str: endpoint for the route added
:param handler function: represents the sync or async function passed as a handler for the route
"""

""" We will add the status code here only
Expand Down Expand Up @@ -165,6 +166,40 @@ def terminating_signal_handler(_sig, _frame):
observer.stop()
observer.join()

def add_view(self, endpoint: str, view: Callable, const: bool = False):
"""
This is base handler for the view decorators
:param endpoint str: endpoint for the route added
:param handler function: represents the function passed as a parent handler for single route with different route types
"""
http_methods = {"GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"}

def get_functions(view):
functions = get_all_nested(view)
output = []
for name, handler in functions:
route_type = name.upper()
if route_type in http_methods:
output.append((route_type, handler))
return output

handlers = get_functions(view)
for route_type, handler in handlers:
self._add_route(route_type, endpoint, handler, const)

def view(self, endpoint: str, const: bool = False):
"""
The @app.view decorator to add a view with the GET/POST/PUT/DELETE/PATCH/HEAD/OPTIONS method
:param endpoint str: endpoint to server the route
"""

def inner(handler):
return self.add_view(endpoint, handler, const)

return inner

def get(self, endpoint: str, const: bool = False):
"""
The @app.get decorator to add a route with the GET method
Expand Down

0 comments on commit 5f437d1

Please sign in to comment.