Skip to content

Commit

Permalink
Merge branch 'dev' into agent_websocket
Browse files Browse the repository at this point in the history
  • Loading branch information
JosephRana11 authored Apr 9, 2024
2 parents 38d7df1 + 8085390 commit 906de42
Show file tree
Hide file tree
Showing 32 changed files with 751 additions and 104 deletions.
28 changes: 28 additions & 0 deletions .github/workflows/build-backend-image.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: Backend Service Docker Image Builder

on:
push:
branches: ["dev"]
pull_request:
branches: ["dev"]

jobs:
build_on_pr:
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout code with submodules
uses: actions/checkout@v2
with:
submodules: "recursive"

- name: Setup Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Build Docker image
uses: docker/build-push-action@v5
with:
push: false
tags: backend:latest
context: autonomous_agent_api
41 changes: 41 additions & 0 deletions .github/workflows/test-backend.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@

name: Backend Service Testing ( Pytest )

on:
push:
branches: ["master", "dev"]
pull_request:
branches: ["dev"]

jobs:
run_backend_tests:
runs-on: ubuntu-latest

steps:
- name: Enable Access for Branch for Workflow
uses: actions/checkout@v2
with:
submodules: "recursive"

- name: setup python
uses: actions/setup-python@v2
with:
python-version: 3.12.2

- name: Install Poetry
working-directory: autonomous_agent_api
run: pip install poetry==1.8.2

- name: Install Dependencies
working-directory: autonomous_agent_api
run: |
poetry install
poetry run prisma generate
- name: Run test cases
working-directory: autonomous_agent_api
env:
DATABASE_URL : ""
run: poetry run pytest -m github_actions


25 changes: 17 additions & 8 deletions autonomous_agent_api/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,21 @@
- Created Agent prisma model
- Added agent_router
- created crud Api endpoints
POST api for agent creation
GET api for listing agents
GET api for getting agent properties.
PUT api to update agent properties.
DELETE api for soft deletion of agent.
-created dtos model for request and response inside model directory
- POST api for agent creation
- GET api for listing agents
- GET api for getting agent properties.
- PUT api to update agent properties.
- DELETE api for soft deletion of agent.
- created dtos model for request and response inside model directory

# TITLE - TEST Setup and Test cases for Agent Api , Date - 2024-03-28/29 - 04-01
- Made setup to run the test cases
- Wrote Unit test for API methods of Agent

# TITLE - AGENT Websocket , DATE- 2024-04-02
- Added websocket endpoint for agent websocket connection
# TITLE - Trigger/API crud operation , Date - 2024-04-01/02
- Made table for Triggers
- Created router,service,repository for trigger CRUD Action
- Created the validation function for cron expression and kafka topic while creating the trigger by agent

# TITLE - AGENT Websocket , DATE- 2024-04-09
- Added websocket endpoint for agent websocket connection
45 changes: 45 additions & 0 deletions autonomous_agent_api/backend/app/asgi.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""Application implementation - ASGI."""

import os
import logging
from contextlib import asynccontextmanager

Expand Down Expand Up @@ -27,6 +28,7 @@ async def lifespan(app: FastAPI):
logger.info("Starting Server")

await prisma_connection.connect()

yield
log.debug("Execute FastAPI shutdown event handler.")
# Gracefully close utilities.
Expand All @@ -36,9 +38,26 @@ async def lifespan(app: FastAPI):
await AiohttpClient.close_aiohttp_client()

await prisma_connection.disconnect()

logger.info("Stopping Server")


# Lifespan used for Test Environmnet : Configurations such as Live Database and Redis is Disabled.
# todo : Mock Database setup required for testing
@asynccontextmanager
async def test_lifespan(app: FastAPI):
log.debug("Execute FastAPI startup event handler.")

AiohttpClient.get_aiohttp_client()

logger.info("Starting Test Server")

yield
log.debug("Execute FastAPI shutdown event handler.")

logger.info("Stopping Test Server")


def get_application() -> FastAPI:
"""Initialize FastAPI application.
Expand All @@ -47,6 +66,7 @@ def get_application() -> FastAPI:
"""
log.debug("Initialize FastAPI application node.")

app = FastAPI(
title=settings.PROJECT_NAME,
debug=settings.DEBUG,
Expand All @@ -60,3 +80,28 @@ def get_application() -> FastAPI:
app.add_exception_handler(HTTPException, http_exception_handler)

return app


def get_test_application() -> FastAPI:
"""
Initialize FastApi application for testing environment
Returns:
FastAPI : Application object instance
"""
os.environ.__setattr__("TESTING", True)

logging.info("Setting up Test Environment")
app = FastAPI(
title=settings.PROJECT_NAME,
debug=settings.DEBUG,
version=settings.VERSION,
docs_url=settings.DOCS_URL,
lifespan=test_lifespan,
)
log.debug("Add application routes.")
app.include_router(root_api_router)
log.debug("Register global exception handler for custom HTTPException.")
app.add_exception_handler(HTTPException, http_exception_handler)
return app
15 changes: 6 additions & 9 deletions autonomous_agent_api/backend/app/controllers/agent_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
from typing import List

from classy_fastapi import Routable, get, post, put, delete
from fastapi import Query, HTTPException
from fastapi import HTTPException

from backend.app.models.agent_dto import AgentCreateDTO
from backend.app.models.agent.agent_dto import AgentCreateDTO
from backend.app.services.agent_service import AgentService
from backend.app.models.response_dto import AgentResponse
from backend.app.models.agent.response_dto import AgentResponse
from backend.dependency import get_agent_service


Expand All @@ -15,13 +15,10 @@ def __init__(self, agent_service: AgentService = get_agent_service(), *args, **k
super().__init__(*args, **kwargs)
self.agent_service = agent_service

@post("/create_agents/", status_code=HTTPStatus.CREATED)
@post("/agents/", status_code=HTTPStatus.CREATED)
async def create_agent(self, agent_data: AgentCreateDTO):
try:
agent = await self.agent_service.create_agent(agent_data)
return agent
except Exception as e:
raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=str(e))
agent = await self.agent_service.create_agent(agent_data)
return agent

@get("/agents/", response_model=List[AgentResponse])
async def list_agents(self):
Expand Down
42 changes: 42 additions & 0 deletions autonomous_agent_api/backend/app/controllers/trigger_router.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
from http import HTTPStatus
from typing import List

from classy_fastapi import Routable, get, post, put, delete
from fastapi import HTTPException

from backend.app.models import TriggerResponse
from backend.app.models import TriggerCreateDTO
from backend.app.services.trigger_service import TriggerService
from backend.dependency import get_trigger_service


class TriggerRouter(Routable):
def __init__(self, trigger_service: TriggerService = get_trigger_service(), *args, **kwargs):
super().__init__(*args, **kwargs)
self.trigger_service = trigger_service

@post("/agents/{agent_id}/triggers", status_code=HTTPStatus.CREATED)
async def create_trigger(self, agent_id: str, trigger_data: TriggerCreateDTO):
trigger = await self.trigger_service.create_trigger(agent_id, trigger_data)
return trigger

@get("/agents/{agent_id}/triggers", response_model=List[TriggerResponse])
async def list_triggers_by_agent_id(self, agent_id: str):
triggers = await self.trigger_service.list_triggers_by_agent_id(agent_id)
return triggers

@get("/triggers/{trigger_id}", response_model=TriggerResponse)
async def list_trigger_by_trigger_id(self, trigger_id: str):
trigger = await self.trigger_service.list_trigger_by_id(trigger_id)
return trigger

@put("/triggers/{trigger_id}", response_model=TriggerResponse)
async def update_trigger_by_trigger_id(self, trigger_id: str, trigger_data: TriggerCreateDTO):
trigger = await self.trigger_service.update_trigger_by_id(trigger_id, trigger_data)
return trigger

@delete("/triggers/{trigger_id}", status_code=HTTPStatus.NO_CONTENT)
async def delete_trigger_by_trigger_id(self, trigger_id: str):
success = await self.trigger_service.delete_by_id(trigger_id)
if not success:
raise HTTPException(status_code=HTTPStatus.NOT_FOUND, detail="Trigger not found")
5 changes: 4 additions & 1 deletion autonomous_agent_api/backend/app/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
"""Application implementation - models."""
# backend/app/models/__init__.py

from .trigger.resposne_dto import TriggerResponse
from .trigger.trigger_dto import TriggerCreateDTO, TopicTriggerDTO, CronTriggerDTO
Empty file.
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,3 @@
class AgentCreateDTO(BaseModel):
name: str = Field(..., description="Name of Agent", min_length=1)
action: List[str] = []
triggers: List[str] = []
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,3 @@ class AgentResponse(BaseModel):
id: str
name: str
action: List[str] = []
triggers: List[str] = []
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

17 changes: 17 additions & 0 deletions autonomous_agent_api/backend/app/models/trigger/resposne_dto.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from pydantic import BaseModel
from typing import Union

from backend.app.models.trigger.trigger_dto import CronTriggerDTO, TopicTriggerDTO


class TriggerResponse(BaseModel):
id: str
agent_id: str
type: str
data: Union[CronTriggerDTO, TopicTriggerDTO]


# class TriggerResponse_agent_id(BaseModel):
# agentId: str
# type: str
# data: Union[CronTriggerDTO, TopicTriggerDTO]
36 changes: 36 additions & 0 deletions autonomous_agent_api/backend/app/models/trigger/trigger_dto.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from fastapi import HTTPException
from typing import Union
from pydantic import BaseModel
from croniter import croniter


class CronTriggerDTO(BaseModel):
frequency: str
probability: float


class TopicTriggerDTO(BaseModel):
topic: str


class TriggerCreateDTO(BaseModel):
type: str
data: Union[CronTriggerDTO, TopicTriggerDTO]


# validation for cron expression
async def validate_type_CRON(cron_expression: str):
try:
croniter(cron_expression)
except ValueError as e:
raise HTTPException(400, f"Invalid CRON expression: {str(e)}")


# validation for Topic
async def validate_type_TOPIC(value: str):
try:
if value.isnumeric():
raise HTTPException(400, f"Invalid topic :")

except ValueError as e:
return f"Validation error: {e}"
Loading

0 comments on commit 906de42

Please sign in to comment.