Skip to content

Commit fe8b658

Browse files
authored
Merge pull request #7 from eadwinCode/example_fix
ellar example and renamed route operations
2 parents f3d2f02 + 7cfa924 commit fe8b658

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+365
-265
lines changed

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) 2021 Ezeudoh Tochukwu
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in
13+
all copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
THE SOFTWARE.

README.md

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,5 +33,79 @@
3333
pip install ellar
3434
```
3535

36+
## Usage
37+
38+
Create a file `controller.py`:
39+
40+
```Python
41+
from ellar.common import ModuleRouter, Controller, get
42+
43+
router = ModuleRouter('', tag='Math')
44+
45+
46+
@router.get("/add")
47+
def add(request, a: int, b: int):
48+
return {"result": a + b}
49+
50+
51+
@Controller("", tag='Math')
52+
class MathAPI:
53+
54+
@get('/subtract', )
55+
def subtract(self, a: int, b: int):
56+
"""Subtracts a from b"""
57+
return {"result": a - b}
58+
59+
@get('/divide', )
60+
def divide(self, a: int, b: int):
61+
"""Divides a by b"""
62+
return {"result": a / b}
63+
64+
@get('/multiple', )
65+
def multiple(self, a: int, b: int):
66+
"""Multiples a with b"""
67+
return {"result": a * b}
68+
69+
```
70+
71+
Create another file `server.py`:
72+
73+
```Python
74+
from ellar.core import AppFactory
75+
from ellar.openapi.builder import OpenAPIDocumentBuilder
76+
from ellar.openapi.module import OpenAPIDocumentModule
77+
from .controller import router, MathAPI
78+
79+
80+
app = AppFactory.create_app(routers=(router, ), controllers=(MathAPI, ))
81+
82+
document_builder = OpenAPIDocumentBuilder()
83+
document_builder.set_title('Your Title')\
84+
.set_version('1.0.2')\
85+
.set_contact(name='Eadwin', url='https://www.yahoo.com', email='eadwin@gmail.com')\
86+
.set_license('MIT Licence', url='https://www.google.com')
87+
88+
document = document_builder.build_document(app)
89+
module = app.install_module(OpenAPIDocumentModule, document=document)
90+
module.setup_swagger_doc()
91+
```
92+
93+
### Interactive API docs
94+
95+
Now go to <a href="http://localhost:8000/docs/" target="_blank">http://localhost:8000/docs/</a>
96+
97+
You will see the automatic interactive API documentation (provided by <a href="https://github.com/swagger-api/swagger-ui" target="_blank">Swagger UI</a>):
98+
99+
![Swagger UI](docs/img/ellar_demo.gif)
100+
36101
## Status
37102
Project is still in development
103+
- Remaining testing modules:
104+
- common
105+
- configuration
106+
- guard
107+
- openapi
108+
- Project CLI scaffolding
109+
- Database Plugin with [Encode/ORM](https://github.com/encode/orm)
110+
- Caching
111+
- API Throttling

docs/img/ellar_demo.gif

1.26 MB
Loading

ellar/common/__init__.py

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,24 @@
55
from .decorators.controller import Controller
66
from .decorators.exception import exception_handler
77
from .decorators.guards import guards
8-
from .decorators.html import Render, template_filter, template_global
8+
from .decorators.html import render, template_filter, template_global
99
from .decorators.middleware import middleware
1010
from .decorators.modules import Module
1111
from .decorators.openapi import openapi
1212
from .decorators.request import on_shutdown, on_startup
1313
from .decorators.serializer import serializer_filter
1414
from .decorators.versioning import version
1515
from .routing import (
16-
Delete,
17-
Get,
18-
Head,
19-
HttpRoute,
20-
Options,
21-
Patch,
22-
Post,
23-
Put,
24-
Trace,
25-
WsRoute,
16+
delete,
17+
get,
18+
head,
19+
http_route,
20+
options,
21+
patch,
22+
post,
23+
put,
24+
trace,
25+
ws_route,
2626
)
2727
from .routing.params import (
2828
Body,
@@ -41,7 +41,7 @@
4141

4242
__all__ = [
4343
"ModuleRouter",
44-
"Render",
44+
"render",
4545
"Module",
4646
"guards",
4747
"Param",
@@ -50,16 +50,16 @@
5050
"Controller",
5151
"openapi",
5252
"version",
53-
"Delete",
54-
"Get",
55-
"Head",
56-
"HttpRoute",
57-
"Options",
58-
"Patch",
59-
"Post",
60-
"Put",
61-
"Trace",
62-
"WsRoute",
53+
"delete",
54+
"get",
55+
"head",
56+
"http_route",
57+
"options",
58+
"patch",
59+
"post",
60+
"put",
61+
"trace",
62+
"ws_route",
6363
"Body",
6464
"Cookie",
6565
"File",

ellar/common/decorators/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from .controller import Controller # noqa
33
from .exception import exception_handler # noqa
44
from .guards import guards # noqa
5-
from .html import Render, template_filter, template_global # noqa
5+
from .html import render, template_filter, template_global # noqa
66
from .middleware import middleware # noqa
77
from .modules import Module # noqa
88
from .openapi import openapi # noqa

ellar/common/decorators/controller.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ def Controller(
8282
] = None,
8383
include_in_schema: bool = True,
8484
) -> t.Union[t.Type[ControllerBase], t.Callable[..., t.Any], t.Any]:
85-
_prefix: t.Optional[t.Any] = prefix or NOT_SET
85+
_prefix: t.Optional[t.Any] = prefix if prefix is not None else NOT_SET
8686
if prefix and isinstance(prefix, type):
8787
_prefix = NOT_SET
8888

@@ -118,7 +118,7 @@ def _decorator(cls: t.Type) -> t.Type[ControllerBase]:
118118
_tag = _controller_type.controller_class_name()
119119

120120
if not kwargs.openapi.tag: # type: ignore
121-
kwargs.openapi.tag = _tag # type: ignore
121+
kwargs["openapi"]["tag"] = _tag # type: ignore
122122

123123
if kwargs["path"] is NOT_SET:
124124
kwargs["path"] = f"/{_tag}"

ellar/common/decorators/html.py

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -24,18 +24,17 @@ class RenderDecoratorException(Exception):
2424
pass
2525

2626

27-
class Render:
28-
def __init__(self, template_name: t.Optional[str] = NOT_SET) -> None:
29-
if template_name is not NOT_SET:
30-
assert isinstance(
31-
template_name, str
32-
), "Render Operation must invoked eg. @Render()"
33-
self.template_name = None if template_name is NOT_SET else template_name
34-
self.class_base_function_regex = re.compile(
35-
"<\\w+ (\\w+)\\.(\\w+) at \\w+>", re.IGNORECASE
36-
)
37-
38-
def __call__(self, func: t.Union[t.Callable, t.Any]) -> t.Union[t.Callable, t.Any]:
27+
def render(template_name: t.Optional[str] = NOT_SET) -> t.Callable:
28+
if template_name is not NOT_SET:
29+
assert isinstance(
30+
template_name, str
31+
), "Render Operation must invoked eg. @Render()"
32+
template_name = None if template_name is NOT_SET else template_name
33+
class_base_function_regex = re.compile(
34+
"<\\w+ (\\w+)\\.(\\w+) at \\w+>", re.IGNORECASE
35+
)
36+
37+
def _decorator(func: t.Union[t.Callable, t.Any]) -> t.Union[t.Callable, t.Any]:
3938
if not callable(func) or isinstance(func, RouteOperationBase):
4039
warnings.warn_explicit(
4140
UserWarning(
@@ -52,23 +51,25 @@ def __call__(self, func: t.Union[t.Callable, t.Any]) -> t.Union[t.Callable, t.An
5251
endpoint_name = get_name(func)
5352
is_class_base_function = use_mvc = False
5453

55-
if self.class_base_function_regex.match(repr(func)):
54+
if class_base_function_regex.match(repr(func)):
5655
is_class_base_function = True
5756

58-
if not self.template_name and is_class_base_function:
57+
if not template_name and is_class_base_function:
5958
use_mvc = True
6059

61-
if not self.template_name and not is_class_base_function:
60+
if not template_name and not is_class_base_function:
6261
raise RenderDecoratorException(
6362
f"template_name is required for function endpoints. {func}"
6463
)
6564

6665
response = HTMLResponseModel(
67-
template_name=self.template_name or endpoint_name, use_mvc=use_mvc
66+
template_name=template_name or endpoint_name, use_mvc=use_mvc
6867
)
6968
target_decorator = set_meta(RESPONSE_OVERRIDE_KEY, {200: response})
7069
return target_decorator(func)
7170

71+
return _decorator
72+
7273

7374
def _validate_template_function(f: t.Any) -> None:
7475
if asyncio.iscoroutinefunction(f):

ellar/common/routing/__init__.py

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,20 @@
1818

1919
_route_definitions = OperationDefinitions()
2020

21-
Get = _route_definitions.get
22-
Post = _route_definitions.post
21+
get = _route_definitions.get
22+
post = _route_definitions.post
2323

24-
Delete = _route_definitions.delete
25-
Patch = _route_definitions.patch
24+
delete = _route_definitions.delete
25+
patch = _route_definitions.patch
2626

27-
Put = _route_definitions.put
28-
Options = _route_definitions.options
27+
put = _route_definitions.put
28+
options = _route_definitions.options
2929

30-
Trace = _route_definitions.trace
31-
Head = _route_definitions.head
30+
trace = _route_definitions.trace
31+
head = _route_definitions.head
3232

33-
HttpRoute = _route_definitions.http_route
34-
WsRoute = _route_definitions.ws_route
33+
http_route = _route_definitions.http_route
34+
ws_route = _route_definitions.ws_route
3535

3636
__all__ = [
3737
"Ctx",
@@ -48,14 +48,14 @@
4848
"Query",
4949
"Param",
5050
"ParamTypes",
51-
"Get",
52-
"Post",
53-
"Delete",
54-
"Patch",
55-
"Put",
56-
"Options",
57-
"Trace",
58-
"Head",
59-
"HttpRoute",
60-
"WsRoute",
51+
"get",
52+
"post",
53+
"delete",
54+
"patch",
55+
"put",
56+
"options",
57+
"trace",
58+
"head",
59+
"http_route",
60+
"ws_route",
6161
]

ellar/compatible/contextmanager.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import sys
22

33
if sys.version_info >= (3, 7): # pragma: no cover
4-
from contextlib import asynccontextmanager as asynccontextmanager
4+
from contextlib import asynccontextmanager as asynccontextmanager # noqa
55
else:
66
from contextlib2 import asynccontextmanager as asynccontextmanager # noqa

ellar/core/routing/router/module.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -191,17 +191,17 @@ def __init__(
191191
)
192192
_route_definitions = self.operation_definition_class(t.cast(list, self.routes))
193193

194-
self.Get = _route_definitions.get
195-
self.Post = _route_definitions.post
194+
self.get = _route_definitions.get
195+
self.post = _route_definitions.post
196196

197-
self.Delete = _route_definitions.delete
198-
self.Patch = _route_definitions.patch
197+
self.delete = _route_definitions.delete
198+
self.patch = _route_definitions.patch
199199

200-
self.Put = _route_definitions.put
201-
self.Options = _route_definitions.options
200+
self.put = _route_definitions.put
201+
self.options = _route_definitions.options
202202

203-
self.Trace = _route_definitions.trace
204-
self.Head = _route_definitions.head
203+
self.trace = _route_definitions.trace
204+
self.head = _route_definitions.head
205205

206-
self.HttpRoute = _route_definitions.http_route
207-
self.WsRoute = _route_definitions.ws_route
206+
self.http_route = _route_definitions.http_route
207+
self.ws_route = _route_definitions.ws_route

ellar/openapi/module.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import typing as t
22

3-
from ellar.common import Get, Module, Render, guards as guards_decorator
3+
from ellar.common import Module, get, guards as guards_decorator, render
44
from ellar.core.guard import GuardCanActivate
55
from ellar.core.main import App
66
from ellar.core.modules import ModuleBase
@@ -26,7 +26,7 @@ def __init__(
2626
if not openapi_url and document:
2727
self._openapi_url = "/openapi.json"
2828

29-
@Get(self._openapi_url, include_in_schema=False)
29+
@get(self._openapi_url, include_in_schema=False)
3030
@guards_decorator(*self._guards)
3131
def openapi_schema() -> t.Any:
3232
assert document and isinstance(document, OpenAPI), "Invalid Document"
@@ -39,8 +39,8 @@ def _setup_docs(
3939
) -> None:
4040
_path = path.lstrip("/").rstrip("/")
4141

42-
@Get(f"/{_path}", include_in_schema=False)
43-
@Render(template_name)
42+
@get(f"/{_path}", include_in_schema=False)
43+
@render(template_name)
4444
@guards_decorator(*self._guards)
4545
def _doc() -> t.Any:
4646
return template_context

0 commit comments

Comments
 (0)