Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dev #10

Merged
merged 17 commits into from
Sep 24, 2024
Merged

dev #10

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions backend/.env.dist
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
USERNAME_MODEUS=test_username
PASSWORD_MODEUS=test_password
MODEUS_USERNAME=test_username
MODEUS_PASSWORD=test_password
9 changes: 6 additions & 3 deletions backend/app/auth.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
from blacksheep import Application
"""Auth module."""

from app.settings import Settings
from blacksheep import Application


def configure_authentication(app: Application, settings: Settings):
def configure_authentication(app: Application, settings: Settings) -> None:
"""
Configure authentication as desired. For reference:
Configure authentication as desired.

For reference:
https://www.neoteroi.dev/blacksheep/authentication/
"""
3 changes: 2 additions & 1 deletion backend/app/binders.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
This module contains definitions of custom binders, used to bind request input
parameters into instances of objects, injected to request handlers.
"""

from blacksheep import FromHeader, Request
from blacksheep.server.bindings import Binder

from domain.common import PageOptions


Expand All @@ -25,6 +25,7 @@ class PageOptionsBinder(Binder):
handle = PageOptions

async def get_value(self, request: Request) -> PageOptions:
"""Get value."""
pages = request.query.get("page")
limits = request.query.get("limit")
continuation_ids = request.query.get("continuation_id")
Expand Down
9 changes: 7 additions & 2 deletions backend/app/controllers/examples.py
Original file line number Diff line number Diff line change
@@ -1,29 +1,34 @@
"""
Example API implemented using a controller.
"""

from typing import List, Optional

from blacksheep.server.controllers import Controller, get, post


class ExamplesController(Controller):
"""Example API controller."""

@classmethod
def route(cls) -> Optional[str]:
"""Return example route."""
return "/api/examples"

@classmethod
def class_name(cls) -> str:
"""Class name."""
return "Examples"

@get()
async def get_examples(self) -> List[str]:
"""
Gets a list of examples.
"""
return list(f"example {i}" for i in range(3))
return [f"example {number}" for number in range(3)]

@post()
async def add_example(self, example: str):
async def add_example(self, example: str) -> None:
"""
Adds an example.
"""
23 changes: 0 additions & 23 deletions backend/app/controllers/home.py

This file was deleted.

31 changes: 31 additions & 0 deletions backend/app/controllers/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from datetime import datetime

from pydantic import BaseModel, Field


class NetologyCreds(BaseModel):
"""Netology creds."""

username: str
password: str


class ModeusCreds(BaseModel):
"""Modeus creds."""

username: str
password: str


"""
{"size":500,"timeMin":"2024-09-23T00:00:00+03:00","timeMax":"2024-09-29T23:59:59+03:00","attendeePersonId":["d69c87c8-aece-4f39-b6a2-7b467b968211"]}
_"""


class ModeusSearchEvents(BaseModel):
"""Modeus search events body."""

size: int
time_min: datetime = Field(alias="timeMin")
time_max: datetime = Field(alias="timeMax")
attendee_person_id: list[str] = Field(alias="attendeePersonId")
63 changes: 63 additions & 0 deletions backend/app/controllers/modeus.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
"""
Modeus API implemented using a controller.
"""

from typing import Optional

from blacksheep import Response
from blacksheep.server.bindings import FromJson, FromHeader
from blacksheep.server.controllers import Controller, post
from requests import RequestException

from integration import modeus
from integration.exceptions import ModeusError
from . import models


class FromAuthorizationHeader(FromHeader[str]):
name = "Authorization"


class ModeusController(Controller):
"""Controller for Modeus API."""

@classmethod
def route(cls) -> Optional[str]:
"""Get route."""
return "/api/modeus"

@classmethod
def class_name(cls) -> str:
"""Get class name."""
return "Modeus"

@post()
async def get_modeus_cookies(self, item: FromJson[models.ModeusCreds]) -> Response:
"""
Auth in Modeus and return cookies.
"""
try:
return self.json(
await modeus.login(item.value.username, item.value.password),
)
except (RequestException, ModeusError) as exception:
return self.json({"error": f"can't authenticate {exception}"}, status=400)

@post("/events/")
async def get_modeus_events(
self,
auth: FromAuthorizationHeader,
item: FromJson[models.ModeusSearchEvents],
) -> Response:
"""
Get events from Modeus.
"""
try:
jwt = auth.value.split()[1]
return self.json(await modeus.get_events(jwt, item.value))
except IndexError as exception:
return self.json(
{"error": f"cannot parse authorization header {exception}"},
)
except (RequestException, ModeusError) as exception:
return self.json({"error": f"can't authenticate {exception}"}, status=400)
31 changes: 20 additions & 11 deletions backend/app/controllers/netology.py
Original file line number Diff line number Diff line change
@@ -1,36 +1,45 @@
"""
Netology API implemented using a controller.
"""

from typing import Optional

from pydantic import BaseModel
from blacksheep.server.controllers import Controller, post
from blacksheep import Response
from blacksheep.server.bindings import FromJson
from blacksheep.server.controllers import Controller, post
from requests import RequestException

from integration import netology


class NetologyCreds(BaseModel):
username: str
password: str
from . import models


class NetologyController(Controller):
"""Controller for Netology API."""

@classmethod
def route(cls) -> Optional[str]:
"""Get route."""
return "/api/netology"

@classmethod
def class_name(cls) -> str:
"""Get class name."""
return "Netology"

@post()
async def get_netology_cookies(self, item: FromJson[NetologyCreds]):
async def get_netology_cookies(
self,
item: FromJson[models.NetologyCreds],
) -> Response:
"""
Auth in Netology and return cookies.
"""
try:
return self.json(netology.auth_netology(item.value.username, item.value.password))
except RequestException as e:
return self.json({"error": f"can't authenticate {e}"}, status=400)
return self.json(
netology.auth_netology(
item.value.username,
item.value.password,
),
)
except RequestException as exception:
return self.json({"error": f"can't authenticate {exception}"}, status=400)
8 changes: 4 additions & 4 deletions backend/app/docs/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@
It exposes a docs object that can be used to decorate request handlers with additional
information, used to generate OpenAPI documentation.
"""
from blacksheep import Application
from blacksheep.server.openapi.v3 import OpenAPIHandler
from openapidocs.v3 import Info # type: ignore[import-untyped]

from app.docs.binders import set_binders_docs
from app.settings import Settings
from blacksheep import Application
from blacksheep.server.openapi.v3 import OpenAPIHandler
from openapidocs.v3 import Info


def configure_docs(app: Application, settings: Settings):
def configure_docs(app: Application, settings: Settings) -> None:
docs = OpenAPIHandler(
info=Info(title=settings.info.title, version=settings.info.version),
anonymous_access=True,
Expand Down
17 changes: 13 additions & 4 deletions backend/app/docs/binders.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
"""
This module configures OpenAPI Documentation for custom binders.
"""
from blacksheep.server.openapi.v3 import OpenAPIHandler
from openapidocs.v3 import Parameter, ParameterLocation, Schema, ValueFormat, ValueType # type: ignore[import-untyped]

from app.binders import PageOptionsBinder
from blacksheep.server.openapi.v3 import OpenAPIHandler
from openapidocs.v3 import (
Parameter,
ParameterLocation,
Schema,
ValueFormat,
ValueType,
)


def set_binders_docs(docs: OpenAPIHandler):
def set_binders_docs(docs: OpenAPIHandler) -> None:
"""
This function configures OpenAPI Documentation for custom application binders.
"""
Expand All @@ -25,7 +31,10 @@ def set_binders_docs(docs: OpenAPIHandler):
ParameterLocation.QUERY,
description="Number of results per page.",
schema=Schema(
minimum=0, maximum=1000, format=ValueFormat.INT32, default=100
minimum=0,
maximum=1000,
format=ValueFormat.INT32,
default=100,
),
),
Parameter(
Expand Down
6 changes: 4 additions & 2 deletions backend/app/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@

def configure_error_handlers(app: Application) -> None:
async def not_found_handler(
app: Application, request: Request, exception: Exception
app: Application,
request: Request,
exception: Exception,
) -> Response:
return text(str(exception) or "Not found", 404)

Expand All @@ -37,5 +39,5 @@ async def accepted(*args: Any) -> Response:
UnauthorizedException: unauthorized,
ForbiddenException: forbidden,
AcceptedException: accepted,
}
},
)
12 changes: 5 additions & 7 deletions backend/app/main.py
Original file line number Diff line number Diff line change
@@ -1,30 +1,28 @@
"""
This module configures the BlackSheep application before it starts.
"""
from blacksheep import Application
from rodi import Container

from app.auth import configure_authentication
from app.docs import configure_docs
from app.errors import configure_error_handlers
from app.services import configure_services
from app.settings import load_settings, Settings
from app.templating import configure_templating
from app.settings import Settings, load_settings
from blacksheep import Application
from rodi import Container


def configure_application(
services: Container,
settings: Settings,
) -> Application:
app = Application(
services=services, show_error_details=settings.app.show_error_details
services=services,
show_error_details=settings.app.show_error_details,
)

app.serve_files("app/static")
configure_error_handlers(app)
configure_authentication(app, settings)
configure_docs(app, settings)
configure_templating(app, settings)
return app


Expand Down
9 changes: 4 additions & 5 deletions backend/app/services.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,21 @@
"""
Use this module to register required services.

Services registered inside a `rodi.Container` are automatically injected into request
handlers.

For more information and documentation, see `rodi` Wiki and examples:
https://github.com/Neoteroi/rodi/wiki
https://github.com/Neoteroi/rodi/tree/main/examples
"""
from typing import Tuple

from rodi import Container
from typing import Tuple

from app.settings import Settings
from rodi import Container


def configure_services(
settings: Settings,
) -> Tuple[Container, Settings]:
def configure_services(settings: Settings) -> Tuple[Container, Settings]:
container = Container()

container.add_instance(settings)
Expand Down
Loading
Loading