Skip to content

Commit

Permalink
add app context template (#386)
Browse files Browse the repository at this point in the history
* add app context template
* added tests for creating a context template
  • Loading branch information
DJWOMS authored Aug 23, 2024
1 parent 5465358 commit b805a26
Show file tree
Hide file tree
Showing 13 changed files with 245 additions and 6 deletions.
5 changes: 5 additions & 0 deletions docs/en/docs/directives/directives.md
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,11 @@ a basic controller example.

<sup>Default: `False`</sup>

* **--context** - Flag indicating whether the application creation should be accompanied
by a sample base controller, service, repository and dtos.

<sup>Default: `False`</sup>

```shell
$ esmerald createapp <YOUR-APP-NAME>
```
Expand Down
Empty file.
57 changes: 57 additions & 0 deletions esmerald/conf/directives/app_template_context/controllers.py-tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
"""
Generated by 'esmerald createapp' using Esmerald {{ esmerald_version }}.
"""
{% if with_basic_controller == True %}
from __future__ import annotations

from typing import TYPE_CHECKING, TypeVar

from esmerald import APIView, Inject, delete, get, post, put, status

from .service import {{ name|capitalize }}Service
from .repository import {{ name|capitalize }}Repository

if TYPE_CHECKING:
from esmerald.types import Dependencies


T = TypeVar("T")


class {{ name|capitalize }}APIController(APIView):
"""
A {{ name|lower }} controller that defines the basic
operations of the CRUD.

This serves as an example how to simply initialise the
controller out of the box.

!!! Note
Make sure you update the return signatures to your
defined ones.
"""

path: str = "/{{ name|lower }}"
tags: list["str"] = ["{{ name|capitalize }}"]
dependencies: "Dependencies" = {
"repository": Inject({{ name|capitalize }}Repository),
"service": Inject({{ name|capitalize }}Service)
}

@post("/")
async def create(self, service: "{{ name|capitalize }}Service") -> T: ...

@get("/{pk:int}")
async def get_by_id(self, pk: int, service: "{{ name|capitalize }}Service") -> T: ...

@get("/")
async def get_all(self, service: "{{ name|capitalize }}Service") -> list[T]: ...

@put("/{pk:int}")
async def update(self, pk: int, service: "{{ name|capitalize }}Service") -> T: ...

@delete("/{pk:int}")
async def delete_by_id(self, pk: int, service: "{{ name|capitalize }}Service") -> T: ...
{% else %}
# Create your controllers here
{% endif %}
Empty file.
Empty file.
6 changes: 6 additions & 0 deletions esmerald/conf/directives/app_template_context/dtos.py-tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
"""
Generated by 'esmerald createapp' using Esmerald {{ esmerald_version }}.
"""
from pydantic import BaseModel

# create your pydantic models here
20 changes: 20 additions & 0 deletions esmerald/conf/directives/app_template_context/repository.py-tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from typing import TypeVar

T = TypeVar("T")


class {{ name|capitalize }}Repository:
"""
A {{ name|lower }} repository that defines the basic operations of the CRUD.

This serves as an example how to simply initialise the repository out of the box.
"""
async def create(self) -> T: ...

async def get_by_id(self, pk: int) -> T: ...

async def get_all(self) -> list[T]: ...

async def update(self, pk: int) -> T: ...

async def delete_by_id(self, pk: int) -> T: ...
28 changes: 28 additions & 0 deletions esmerald/conf/directives/app_template_context/service.py-tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from typing import TYPE_CHECKING, TypeVar


if TYPE_CHECKING:
from .repository import {{ name|capitalize }}Repository

T = TypeVar("T")


class {{ name|capitalize }}Service:
"""
A {{ name|lower }} service that defines the basic operations of the CRUD.

This serves as an example how to simply initialise the service out of the box.
"""

def __init__(self, repository: "{{ name|capitalize }}Repository"):
self.repository = repository

async def create(self) -> T: ...

async def get_by_id(self, pk: int) -> T: ...

async def get_all(self) -> list[T]: ...

async def update(self, pk: int) -> T: ...

async def delete_by_id(self, pk: int) -> T: ...
6 changes: 6 additions & 0 deletions esmerald/conf/directives/app_template_context/tests.py-tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
"""
Generated by 'esmerald createapp' using Esmerald {{ esmerald_version }}.
"""
from esmerald.testclient import EsmeraldTestClient

# Create your tests here.
17 changes: 17 additions & 0 deletions esmerald/conf/directives/app_template_context/urls.py-tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
"""
Generated by 'esmerald createapp' using Esmerald {{ esmerald_version }}.
"""
from esmerald import Include, Gateway

# Create your routes here.
{% if not with_basic_controller == True %}
route_patterns = [
]
{% else %}
from .controllers import {{ name|capitalize }}APIController


route_patterns = [
Gateway(handler={{ name|capitalize }}APIController)
]
{% endif %}
10 changes: 9 additions & 1 deletion esmerald/core/directives/operations/createapp.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,16 @@
type=bool,
help="Should generate a basic controller",
)
@click.option(
"--context",
default=False,
is_flag=True,
type=bool,
help="Should generate an application with a controller, service, repository, dto.",
)
@click.argument("name", type=str)
@click.command(name="createapp")
def create_app(name: str, with_basic_controller: bool, verbosity: int) -> None:
def create_app(name: str, with_basic_controller: bool, context: bool, verbosity: int) -> None:
"""Creates the scaffold of an application
How to run: `esmerald createapp <NAME>`
Expand All @@ -31,6 +38,7 @@ def create_app(name: str, with_basic_controller: bool, verbosity: int) -> None:
"verbosity": verbosity,
"with_basic_controller": with_basic_controller,
"simple": False,
"app_context": context,
}
directive = TemplateDirective()

Expand Down
14 changes: 9 additions & 5 deletions esmerald/core/directives/templates.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ def handle(self, app_or_project: str, name: str, **options: Any) -> Any:
self.deployment_folder_name = options.get("deployment_folder_name", None)
self.with_basic_controller = options.get("with_basic_controller", False)
self.simple = options.get("simple", False)
self.app_context = options.get("app_context", False)

if self.app_or_project not in TREAT_AS_PROJECT_DIRECTIVE:
self.validate_name(name)
Expand All @@ -55,11 +56,14 @@ def handle(self, app_or_project: str, name: str, **options: Any) -> Any:
else:
base_name = "project_name"

base_subdir = (
f"{app_or_project}_template_simple"
if self.simple and self.app_or_project not in TREAT_AS_PROJECT_DIRECTIVE
else f"{app_or_project}_template"
)
if self.simple and self.app_or_project not in TREAT_AS_PROJECT_DIRECTIVE:
base_subdir = f"{app_or_project}_template_simple"
elif self.app_context and self.app_or_project not in TREAT_AS_PROJECT_DIRECTIVE:
base_subdir = f"{app_or_project}_template_context"
self.with_basic_controller = True
else:
base_subdir = f"{app_or_project}_template"

base_deployment = "deployment_template"

context = {
Expand Down
88 changes: 88 additions & 0 deletions tests/cli/test_createapp_context.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import os
import shutil

import pytest

from esmerald import Esmerald
from tests.cli.utils import run_cmd

app = Esmerald(routes=[])


@pytest.fixture(scope="module")
def create_folders():
os.chdir(os.path.split(os.path.abspath(__file__))[0])
try:
os.remove("app.db")
except OSError:
pass
try:
shutil.rmtree("myproject")
except OSError:
pass
try:
shutil.rmtree("temp_folder")
except OSError:
pass

yield

try:
os.remove("app.db")
except OSError:
pass
try:
shutil.rmtree("myproject")
except OSError:
pass
try:
shutil.rmtree("temp_folder")
except OSError:
pass


def _run_asserts():
assert os.path.isfile("myapp/__init__.py") is True
assert os.path.isfile("myapp/tests.py") is True
assert os.path.isfile("myapp/dtos.py") is True
assert os.path.isfile("myapp/urls.py") is True
assert os.path.isfile("myapp/controllers.py") is True
assert os.path.isfile("myapp/repository.py") is True
assert os.path.isfile("myapp/service.py") is True
assert os.path.isfile("myapp/directives/__init__.py") is True
assert os.path.isfile("myapp/directives/operations/__init__.py") is True


def test_create_app_with_env_var(create_folders):
(o, e, ss) = run_cmd("tests.cli.main:app", "esmerald createproject myproject")
assert ss == 0

os.chdir("myproject/myproject/apps")

(o, e, ss) = run_cmd("tests.cli.main:app", "esmerald createapp myapp --context")

_run_asserts()


def test_create_app_without_env_var(create_folders):
(o, e, ss) = run_cmd("tests.cli.main:app", "esmerald createproject myproject", is_app=False)
assert ss == 0

os.chdir("myproject/myproject/apps")

(o, e, ss) = run_cmd("tests.cli.main:app", "esmerald createapp myapp --context", is_app=False)

_run_asserts()


def test_create_app_without_env_var_with_app_flag(create_folders):
(o, e, ss) = run_cmd("tests.cli.main:app", "esmerald createproject myproject", is_app=False)
assert ss == 0

os.chdir("myproject/myproject/apps")

(o, e, ss) = run_cmd(
"tests.cli.main:app", "esmerald --app tests.cli.main:app createapp myapp --context", is_app=False
)

_run_asserts()

0 comments on commit b805a26

Please sign in to comment.