Skip to content

Commit cd6386b

Browse files
authored
Merge pull request #27 from eadwinCode/quick_start_doc
Quick-Start Doc
2 parents 3c24879 + 3028c51 commit cd6386b

File tree

28 files changed

+591
-66
lines changed

28 files changed

+591
-66
lines changed

.github/workflows/publish.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,5 @@ jobs:
2323
FLIT_USERNAME: ${{ secrets.FLIT_USERNAME }}
2424
FLIT_PASSWORD: ${{ secrets.FLIT_PASSWORD }}
2525
run: flit publish
26+
- name: Deploy Documentation
27+
run: make doc-deploy

.github/workflows/test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,6 @@ jobs:
2020
- name: Install Dependencies
2121
run: flit install --symlink
2222
- name: Test
23-
run: pytest --cov=ellar --cov-report=xml tests
23+
run: make test-cov
2424
- name: Coverage
2525
uses: codecov/codecov-action@v3.1.1

README.md

Lines changed: 191 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -33,79 +33,226 @@
3333
- Injector
3434

3535
## Installation
36-
36+
### Poetry Installation
37+
For [Poetry](https://python-poetry.org/) usages
38+
```shell
39+
poetry add ellar
3740
```
41+
42+
### Pip Installation
43+
For normal pip installation
44+
```shell
3845
pip install ellar
3946
```
4047

41-
## Usage
48+
## Create a project
49+
To create an ellar project, you need to have a `pyproject.toml` available on your root directory.
50+
This is necessary for ellar to store some `metadata` about your project.
51+
52+
### Step 1
53+
For Pip Users, you need to create `pyproject.toml` file
54+
```shell
55+
touch pyproject.toml
56+
```
57+
If you are using `Poetry`, you are ready to go
58+
59+
### Step 2
60+
Run the ellar create project cli command,
61+
```shell
62+
ellar create-project carsite
63+
```
64+
65+
## Run your project
66+
Ellar runs [UVICORN - ASGI Server](https://www.uvicorn.org/) under the hood.
67+
```shell
68+
ellar runserver --reload
69+
```
70+
`--reload` is to watch for file changes
71+
72+
Now go to [http://127.0.0.1:8000](http://127.0.0.1:8000)
73+
![Swagger UI](docs/img/ellar_framework.png)
74+
75+
76+
## Create a project module
77+
A project module is a project app defining a group of controllers or services including templates and static files.
78+
So, now we have a project created, lets add an app to the project.
79+
```shell
80+
ellar create-module car
81+
```
82+
83+
## Add Schema
84+
In `car.schema.py`, lets add some serializer for car input and output data
85+
```python
86+
from ellar.serializer import Serializer
87+
88+
class CarSerializer(Serializer):
89+
name: str
90+
model: str
91+
brand: str
92+
93+
94+
class RetrieveCarSerializer(CarSerializer):
95+
pk: str
96+
```
4297

43-
Create a file `controller.py`:
98+
## Add Services
99+
In `car.services.py`, lets create a dummy repository `CarDummyDB` to manage our car data.
100+
```python
101+
import typing as t
102+
import uuid
103+
from ellar.di import injectable, singleton_scope
44104

45-
```Python
46-
from ellar.common import ModuleRouter, Controller, get
47105

48-
router = ModuleRouter('', tag='Math')
106+
@injectable(scope=singleton_scope)
107+
class CarDummyDB:
108+
class CarDummyDBItem:
109+
pk: str
49110

111+
def __init__(self, **data: t.Dict) -> None:
112+
self.__dict__ = data
50113

51-
@router.get("/add")
52-
def add(request, a: int, b: int):
53-
return {"result": a + b}
114+
def __eq__(self, other):
115+
if isinstance(other, CarDummyDB.CarDummyDBItem):
116+
return self.pk == other.pk
117+
return self.pk == str(other)
54118

119+
def __init__(self) -> None:
120+
self._data: t.List[CarDummyDB.CarDummyDBItem] = []
55121

56-
@Controller("", tag='Math')
57-
class MathAPI:
122+
def add_car(self, data: t.Dict) -> str:
123+
pk = uuid.uuid4()
124+
_data = dict(data)
125+
_data.update(pk=str(pk))
126+
item = self.CarDummyDBItem(**_data)
127+
self._data.append(item)
128+
return item.pk
58129

59-
@get('/subtract', )
60-
def subtract(self, a: int, b: int):
61-
"""Subtracts a from b"""
62-
return {"result": a - b}
130+
def list(self) -> t.List["CarDummyDB.CarDummyDBItem"]:
131+
return self._data
63132

64-
@get('/divide', )
65-
def divide(self, a: int, b: int):
66-
"""Divides a by b"""
67-
return {"result": a / b}
133+
def update(self, car_id: str, data: t.Dict) -> t.Optional["CarDummyDB.CarDummyDBItem"]:
134+
idx = self._data.index(car_id)
135+
if idx >= 0:
136+
_data = dict(data)
137+
_data.update(pk=str(car_id))
138+
self._data[idx] = self.CarDummyDBItem(**_data)
139+
return self._data[idx]
68140

69-
@get('/multiple', )
70-
def multiple(self, a: int, b: int):
71-
"""Multiples a with b"""
72-
return {"result": a * b}
141+
def get(self, car_id: str) -> t.Optional["CarDummyDB.CarDummyDBItem"]:
142+
idx = self._data.index(car_id)
143+
if idx >= 0:
144+
return self._data[idx]
73145

146+
def remove(self, car_id: str) -> t.Optional["CarDummyDB.CarDummyDBItem"]:
147+
idx = self._data.index(car_id)
148+
if idx >= 0:
149+
return self._data.pop(idx)
74150
```
151+
## Add Controller
152+
In `car.controllers.py`, lets create `CarController`
75153

76-
Create another file `server.py`:
154+
```python
155+
import typing as t
156+
from ellar.common import Controller, delete, get, put, post
157+
from ellar.core import ControllerBase
158+
from ellar.exceptions import NotFound
159+
from .schemas import CarSerializer, RetrieveCarSerializer
160+
from .services import CarDummyDB
77161

78-
```Python
79-
from ellar.core import AppFactory
80-
from ellar.openapi import OpenAPIDocumentBuilder, OpenAPIDocumentModule
81-
from .controller import router, MathAPI
82162

163+
@Controller
164+
class CarController(ControllerBase):
165+
def __init__(self, db: CarDummyDB) -> None:
166+
self.car_db = db
83167

84-
app = AppFactory.create_app(routers=(router, ), controllers=(MathAPI, ))
168+
@post("/create", response={200: str})
169+
async def create_cat(self, payload: CarSerializer):
170+
pk = self.car_db.add_car(payload.dict())
171+
return pk
85172

86-
document_builder = OpenAPIDocumentBuilder()
87-
document_builder.set_title('Your Title')\
88-
.set_version('1.0.2')\
89-
.set_contact(name='Eadwin', url='https://www.yahoo.com', email='eadwin@gmail.com')\
90-
.set_license('MIT Licence', url='https://www.google.com')
173+
@put("/{car_id:str}", response={200: RetrieveCarSerializer})
174+
async def update_cat(self, car_id: str, payload: CarSerializer):
175+
car = self.car_db.update(car_id, payload.dict())
176+
if not car:
177+
raise NotFound("Item not found")
178+
return car
179+
180+
@get("/{car_id:str}", response={200: RetrieveCarSerializer})
181+
async def get_car_by_id(self, car_id: str):
182+
car = self.car_db.get(car_id)
183+
if not car:
184+
raise NotFound('Item not found.')
185+
return car
186+
187+
@delete("/{car_id:str}", response={204: dict})
188+
async def deleted_cat(self, car_id: str):
189+
car = self.car_db.remove(car_id)
190+
if not car:
191+
raise NotFound('Item not found.')
192+
return 204, {}
193+
194+
@get("/", response={200: t.List[RetrieveCarSerializer]})
195+
async def list(self):
196+
return self.car_db.list()
91197

92-
document = document_builder.build_document(app)
93-
module = app.install_module(OpenAPIDocumentModule, document=document)
94-
module.setup_swagger_doc()
95198
```
199+
## Register Service and Controller
200+
In `car.module.py`, lets register `CarController` and `CarDummyDB`
201+
202+
```python
203+
from ellar.common import Module
204+
from ellar.core import ModuleBase
205+
from ellar.di import Container
206+
207+
from .controllers import CarController
208+
from .services import CarDummyDB
96209

97-
### Start up Server
98-
```bash
99-
uvicorn server:app --reload
210+
211+
@Module(
212+
controllers=[CarController],
213+
providers=[CarDummyDB],
214+
routers=[],
215+
)
216+
class CarModule(ModuleBase):
217+
def register_providers(self, container: Container) -> None:
218+
# for more complicated provider registrations
219+
# container.register_instance(...)
220+
pass
100221
```
101222

102-
### Interactive API docs
103223

104-
Now go to <a href="http://localhost:8000/docs/" target="_blank">http://localhost:8000/docs/</a>
224+
## Enabling OpenAPI Docs
225+
To start up openapi, we need to go back to project folder in the `carsite.server.py`
226+
then add the following below.
227+
```python
228+
import os
229+
230+
from ellar.constants import ELLAR_CONFIG_MODULE
231+
from ellar.core.factory import AppFactory
232+
from ellar.openapi import OpenAPIDocumentModule, OpenAPIDocumentBuilder
233+
from .root_module import ApplicationModule
234+
235+
application = AppFactory.create_from_app_module(
236+
ApplicationModule,
237+
config_module=os.environ.get(
238+
ELLAR_CONFIG_MODULE, "carsite.config:DevelopmentConfig"
239+
),
240+
)
241+
242+
document_builder = OpenAPIDocumentBuilder()
243+
document_builder.set_title('CarSite API') \
244+
.set_version('1.0.0') \
245+
.set_contact(name='Eadwin', url='https://www.yahoo.com', email='eadwin@gmail.com') \
246+
.set_license('MIT Licence', url='https://www.google.com')
105247

106-
You will see the automatic interactive API documentation (provided by <a href="https://github.com/swagger-api/swagger-ui" target="_blank">Swagger UI</a>):
248+
document = document_builder.build_document(application)
249+
module = application.install_module(OpenAPIDocumentModule, document=document)
250+
module.setup_swagger_doc()
251+
```
107252

108-
![Swagger UI](docs/img/ellar_demo.gif)
253+
Now we can test our API at [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs#/)
254+
Please ensure your server is running
255+
![Swagger UI](docs/img/car_api.png)
109256

110257
## Status
111258
Project is still in development

docs/img/car_api.png

53.4 KB
Loading

docs/img/ellar_framework.png

68.3 KB
Loading

0 commit comments

Comments
 (0)