Skip to content

Commit

Permalink
Merge branch 'service-pages' of https://github.com/tactful-ai/microce…
Browse files Browse the repository at this point in the history
…ntral into CI-CD-pipline
  • Loading branch information
norali12 committed Sep 25, 2024
2 parents 03b2958 + 07911d5 commit 308d628
Show file tree
Hide file tree
Showing 71 changed files with 2,722 additions and 223 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,8 @@ sudo docker-compose run --rm app poetry run alembic revision --autogenerate -m "
sudo docker-compose run --rm app poetry run alembic upgrade head
# and you are good to go
```


# to solve the error of ':5432 fetal error: auth failed'
netstat -aon | findstr :5432
taskkill /PID 15732 /F
6 changes: 4 additions & 2 deletions app/api/api.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from . import teams, metrics,microservice ,microservice_info ,metric_info
from . import teams, metrics,microservice ,microservice_info ,metric_info, scorecard
from fastapi import APIRouter

apiRouter = APIRouter()
Expand All @@ -11,4 +11,6 @@

apiRouter.include_router(microservice_info.router , prefix="/serviceinfo",tags=["microserviceinfo"])

apiRouter.include_router(metric_info.router , prefix="/metricinfo",tags=["metricinfo"])
apiRouter.include_router(metric_info.router , prefix="/metricinfo",tags=["metricinfo"])

apiRouter.include_router(scorecard.router, prefix="/scorecard", tags=["scorecards"])
8 changes: 6 additions & 2 deletions app/api/handling_exception.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
from fastapi import Request, HTTPException, FastAPI
from fastapi import Request, HTTPException
from fastapi.responses import JSONResponse
from starlette import status
from fastapi.encoders import jsonable_encoder
from fastapi.exceptions import RequestValidationError
from app.schemas.apiException import CustomException


def my_exception_handler(request: Request, exc: HTTPException):
Expand All @@ -28,5 +29,8 @@ def validation_exception_handler(request: Request, exc: RequestValidationError):
)
return JSONResponse(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
content=jsonable_encoder({"message": "error in the datatypes", "details" : modified_details}),
content = jsonable_encoder(CustomException(
message="error in the datatypes",
details=modified_details
))
)
59 changes: 25 additions & 34 deletions app/api/metrics.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,16 @@
from fastapi.exceptions import RequestValidationError
from app.schemas import ServiceMetricCreate, MetricCreate
from fastapi import APIRouter, Depends, Request, exception_handlers, status, Response, HTTPException, FastAPI
from fastapi.responses import JSONResponse, PlainTextResponse
from fastapi import APIRouter, Depends
from fastapi.encoders import jsonable_encoder
from pydantic import BaseModel, Field
from app.crud import CRUDMetric, CRUDServiceMetric, CRUDMicroserviceScoreCard, CRUDMicroservice
from app.core.security import JWTBearer, decodeJWT
from app import schemas, models, crud, dependencies
from typing import Any, Callable
from app import schemas, crud, dependencies
from typing import Any
import json
from fastapi.routing import APIRoute
from .exceptions import HTTPResponseCustomized
from app.utils.base import format_code
from app.schemas.apiResponse import CustomResponse
from .responses import ResponseCustomized


metric_type = ["integer", "boolean"]
metric_type = ["integer", "boolean", "string", "float"]


class Value(BaseModel):
Expand All @@ -26,7 +22,7 @@ class Value(BaseModel):


# ADD NEW METRIC HERE
@router.post("/", response_model=schemas.Metric)
@router.post("/", response_model=schemas.Metric, response_class=ResponseCustomized)
def createMetric(metric: schemas.MetricCreate, metricCrud: crud.CRUDMetric = Depends(dependencies.getMetricsCrud)) -> schemas.Metric:
metricObj = metric
# change ' ' with '-'
Expand All @@ -51,13 +47,12 @@ def createMetric(metric: schemas.MetricCreate, metricCrud: crud.CRUDMetric = Dep
# Handling the datatype of the field type to be integer or boolean
if (metricObj.type not in metric_type):
raise HTTPResponseCustomized(
status_code=422, detail="type must be integer or boolean")
status_code=422, detail="type must be valid")
metricCrud.create(metricObj)
raise HTTPResponseCustomized(
status_code=200, detail="Success in creating metric")
return ResponseCustomized("Success in creating metric")

# Get Any Metric Here By ID with parsing the list to be a real list not just stringified
@router.get("/{metricID}", response_model=schemas.Metric)
@router.get("/{metricID}", response_model=schemas.Metric, response_class=ResponseCustomized)
def getMetric(metricID: int, metricCrud: crud.CRUDMetric = Depends(dependencies.getMetricsCrud)) -> Any:
metric = metricCrud.get(metricID)
try:
Expand All @@ -66,58 +61,54 @@ def getMetric(metricID: int, metricCrud: crud.CRUDMetric = Depends(dependencies.
metric.area = []
metric.area = json.loads(metric.area)
metricOBJ = jsonable_encoder(metric)
raise HTTPResponseCustomized(status_code=200, detail=metricOBJ)
return ResponseCustomized(metricOBJ)

# Delete any metric using its own ID
@router.delete("/{metricID}")
@router.delete("/{metricID}", response_model=CustomResponse, response_class=ResponseCustomized)
def deleteMetric(metricID: int, metricCrud: crud.CRUDMetric = Depends(dependencies.getMetricsCrud)) -> Any:
# this line is used to check if the metric is found to be deleted as
# if it is not found this will auto raise an error "Not Found"
metric = metricCrud.get(metricID)
metricCrud.delete(metricID)
raise HTTPResponseCustomized(
status_code=200, detail="deleted successfully")
return ResponseCustomized("Metric is deleted successfully")


@router.put("/{metricID}")
@router.put("/{metricID}", response_model=CustomResponse, response_class=ResponseCustomized)
def editMetric(metricID: int, metricInput: schemas.MetricUpdate, metricCrud: crud.CRUDMetric = Depends(dependencies.getMetricsCrud)) -> Any:
metric = metricCrud.get(metricID)
metricObj = metricInput
if (metricInput.name):
metricObj.code = format_code(metricInput.name)
if (metricCrud.getByCode(metricObj.code) and metricObj.id == metric.id):
raise HTTPResponseCustomized(
status_code=422, detail="name already exists")
if (metricInput.area != None):
raise HTTPResponseCustomized(status_code=422, detail="name already exists")
# Stringfying the list of areas to be saved as string
if (metricObj.area != None):
if any(item == "" for item in metricObj.area):
raise HTTPResponseCustomized(
status_code=422, detail="area can not have an empty string or more")
try:
# I use set to remove all duplicates and return it back to list cuz set is not json serializable
metricObj.area = list(set(metricInput.area))
metricObj.area = json.dumps(metricInput.area)
# Well it will throw the error but i can not test it as i dont know how the error will come as XD
# need help for testing it.
except (TypeError, ValueError) as e:
raise ValueError(f"Invalid area datatype: {e}")
elif(metric.area != None):
metricObj.area = metric.area
else:
metric.area = []
metricObj.area = json.dumps(metric.area)
if (metricObj.type and metricObj.type not in metric_type):
if (metricObj.type not in metric_type and metricObj.type):
raise HTTPResponseCustomized(
status_code=422, detail="type must be integer or boolean")
status_code=422, detail="type must be valid")
metricCrud.update(metricID, metricObj)
raise HTTPResponseCustomized(status_code=200, detail="Success in Editing")
return ResponseCustomized(f"Success, The metric {metricID} has been edited successfully.")

# get all the metrics


@router.get("/", response_model=schemas.List[schemas.Metric])
@router.get("/", response_model=schemas.List[schemas.Metric], response_class=ResponseCustomized)
def getAllMetrics(metricCrud: crud.CRUDMetric = Depends(dependencies.getMetricsCrud)) -> Any:
metrics = metricCrud.list()
metricsOBJ = []
print (metrics)
for metric in metrics:
metric.area = json.loads(metric.area)
metricsOBJ.append(metric)
raise HTTPResponseCustomized(
status_code=200, detail=jsonable_encoder(metricsOBJ))
return ResponseCustomized(jsonable_encoder(metricsOBJ))
118 changes: 87 additions & 31 deletions app/api/microservice.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
from fastapi import APIRouter, Depends, status
from app.schemas import MicroserviceInDBBase, MicroserviceCreate, MicroserviceTeamScorecardBase, MicroserviceCreateApi, MicroserviceScoreCardCreate, MicroserviceUpdate,ServiceMetricReading
from app.crud import CRUDMicroservice, CRUDMicroserviceTeamScorecard, CRUDTeam, CRUDScoreCard, CRUDMicroserviceScoreCard ,CRUDServiceMetric
from typing import List , Optional
from datetime import datetime
from app.schemas import MicroserviceInDBBase, MicroserviceCreate, MicroserviceTeamScorecardBase, MicroserviceCreateApi, MicroserviceScoreCardCreate, MicroserviceUpdate, ServiceMetricReading, ServiceMetricCreate
from app.crud import CRUDMicroservice, CRUDMicroserviceTeamScorecard, CRUDTeam, CRUDScoreCard, CRUDMicroserviceScoreCard, CRUDMetric, CRUDServiceMetric
from typing import List, Optional
from datetime import datetime, timezone
from app import dependencies
from pydantic import BaseModel, Field
from sqlalchemy.orm import Session
from .exceptions import HTTPResponseCustomized
from app.utils.base import format_code
from app.utils import utity_datatype


class Value(BaseModel):
Expand Down Expand Up @@ -46,6 +46,7 @@ async def get_one_service(service_id: int, microServices: CRUDMicroservice = Dep
name=service.name,
description=service.description,
code=service.code,
teamId= service.teamId,
team_name=teamobj.name if teamobj else None,
)

Expand Down Expand Up @@ -109,7 +110,7 @@ def create_microservice(newmicroservice: MicroserviceCreateApi,
pass

if newmicroservice.scorecardids:
scorecard_objs = scorecard.getByScoreCradIds(
scorecard_objs = scorecard.getByScoreCardIds(
newmicroservice.scorecardids)
if len(scorecard_objs) != len(newmicroservice.scorecardids):
missing_ids = set(newmicroservice.scorecardids) - \
Expand All @@ -123,6 +124,7 @@ def create_microservice(newmicroservice: MicroserviceCreateApi,
code=formatted_code))
if newmicroservice.scorecardids is not None:
for scorecard_obj in scorecard_objs:

try:
servicescorecard.create(MicroserviceScoreCardCreate(
microserviceId=created_microservice.id,
Expand All @@ -135,7 +137,9 @@ def create_microservice(newmicroservice: MicroserviceCreateApi,
return created_microservice

# update operation
@router.put("/{servise_id}", response_model=None)


@router.put("/{microservice_id}", response_model=None)
def update_microservice(microservice_id: int, updatemicroservice: MicroserviceCreateApi,
microservice: CRUDMicroservice = Depends(
dependencies.getMicroservicesCrud),
Expand All @@ -144,10 +148,16 @@ def update_microservice(microservice_id: int, updatemicroservice: MicroserviceCr
servicescorecard: CRUDMicroserviceScoreCard = Depends(
dependencies.getMicroserviceScoreCardsCrud),
scorecard: CRUDScoreCard = Depends(dependencies.getScoreCardsCrud)):

existing_microservice = microservice.get(microservice_id)
if not existing_microservice:
raise HTTPResponseCustomized(status_code=404, detail="Microservice not found")

if not updatemicroservice.name:
raise HTTPResponseCustomized(
status_code=400, detail="Name cannot be empty")
updatemicroservice.name = existing_microservice.name

description = updatemicroservice.description or existing_microservice.description
teamId = updatemicroservice.teamId or existing_microservice.teamId

if len(updatemicroservice.name) < 3:
raise HTTPResponseCustomized(
Expand All @@ -161,30 +171,26 @@ def update_microservice(microservice_id: int, updatemicroservice: MicroserviceCr
raise HTTPResponseCustomized(
status_code=400, detail="Description cannot exceed 500 characters")

try:
teamobj = teamservice.get(updatemicroservice.teamId)
if teamobj is None:
raise HTTPResponseCustomized(
status_code=404, detail="Not Found")
except Exception as x:
error_message = 'Team Id was not found'
raise HTTPResponseCustomized(status_code=404, detail=error_message)

scorecard_objs = []
if updatemicroservice.scorecardids:
scorecard_objs = scorecard.getByScoreCradIds(
scorecard_objs = scorecard.getByScoreCardIds(
updatemicroservice.scorecardids)
if len(scorecard_objs) != len(updatemicroservice.scorecardids):
missing_ids = set(updatemicroservice.scorecardids) - \
{sc.id for sc in scorecard_objs}
raise HTTPResponseCustomized(
status_code=404, detail=f"ScoreCard(s) with ID(s): {missing_ids} were not found")

else:
existing_scorecards = servicescorecard.getByServiceId(microservice_id)
updatemicroservice.scorecardids = [sc.scoreCardId for sc in existing_scorecards]


formatted_code = format_code(updatemicroservice.name)
updated_microservice = microservice.update(microservice_id, MicroserviceUpdate(
name=updatemicroservice.name,
description=updatemicroservice.description,
teamId=updatemicroservice.teamId,
description=description,
teamId=teamId,
code=formatted_code
))
servicescorecard.deleteByServiceId(microservice_id)
Expand All @@ -195,7 +201,7 @@ def update_microservice(microservice_id: int, updatemicroservice: MicroserviceCr
return updated_microservice


@router.delete("/{service_id}")
@router.delete("/{microservice_id}")
def delete_microservice(
microservice_id: int,
microservice: CRUDMicroservice = Depends(
Expand All @@ -216,15 +222,65 @@ def delete_microservice(
status_code=404, detail="Can't delete Microservice ScoreCard")

return {"message": "Microservice and associated scorecards successfully deleted"}


@router.get("/{service_id}/metric_reading", response_model=list[ServiceMetricReading])
def get_metrics(service_id: int, from_date: Optional[datetime] = None,
to_date: Optional[datetime] = None, service_metric_crud: CRUDServiceMetric = Depends(dependencies.getServiceMetricsCrud)):

metrics = service_metric_crud.get_metric_values_by_service(service_id, from_date,to_date)


@router.post("/{service_id}/metric_reading", response_model=list[ServiceMetricReading])
def get_metrics(service_id: int, from_date: Optional[datetime] = None,
to_date: Optional[datetime] = None, service_metric_crud: CRUDServiceMetric = Depends(dependencies.getServiceMetricsCrud)):

metrics = service_metric_crud.get_metric_values_by_service(
service_id, from_date, to_date)

if not metrics:
raise HTTPResponseCustomized(status_code=404, detail="Metrics not found for this service and metric.")
raise HTTPResponseCustomized(
status_code=404, detail="Metrics not found for this service and metric.")

return metrics


@router.post("/{service_id}/{metric_id}/reading", response_model=None)
def create_metric_reading(
service_id: int,
metric_id: int,
newmservicemetric: ServiceMetricCreate,
microservice: CRUDMicroservice = Depends(
dependencies.getMicroservicesCrud),
servicemetric: CRUDServiceMetric = Depends(
dependencies.getServiceMetricsCrud),
metric: CRUDMetric = Depends(dependencies.getMetricsCrud),
):

microservice_obj = microservice.get(service_id)
if not microservice_obj:
raise HTTPResponseCustomized(
status_code=404, detail="Service not found")

metric_obj = metric.get(metric_id)
if not metric_obj:
raise HTTPResponseCustomized(
status_code=404, detail="Metric not found")

try:
metric_value = utity_datatype.parse_stringified_value(newmservicemetric.value, metric_obj.type)
except ValueError as e:
raise HTTPResponseCustomized(
status_code=400,
detail=f"Invalid value for metric '{metric_obj.name}': {str(e)}. Expected type: {metric_obj.type}"
)

date = newmservicemetric.timestamp or datetime.now()
date_utc = date.astimezone(timezone.utc)
if date_utc > datetime.now(timezone.utc):
raise HTTPResponseCustomized(
status_code=400, detail="Timestamp cannot be in the future")

service_metric = servicemetric.create(
ServiceMetricCreate(
serviceId= service_id,
metricId= metric_id,
value=metric_value,
timestamp=date,
)
)

return metrics
return service_metric
5 changes: 2 additions & 3 deletions app/api/microservice_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from typing import List
from app import dependencies
from pydantic import BaseModel, Field
from .exceptions import HTTPResponseCustomized
from app.api.exceptions import HTTPResponseCustomized


class Value(BaseModel):
Expand All @@ -17,8 +17,7 @@ class Value(BaseModel):

@router.get("/{service_id}", response_model=MicroserviceInfoBase)
async def getmicroservice_info(service_id: int, microServiceinfo: CRUDMicroserviceInfo = Depends(dependencies.getMicroserviceInfoCrud)):
service = microServiceinfo.getServiceInfo(
service_id)
service = microServiceinfo.getServiceInfo(service_id)
if service is None:
raise HTTPResponseCustomized(status_code=404, detail="Service not found")
return service
Loading

0 comments on commit 308d628

Please sign in to comment.