backend: Fully working roles field. Added get_entity_by_roles

This commit is contained in:
2024-01-13 16:10:21 +01:00
parent 9b37b42d3f
commit 334bbd3761
7 changed files with 114 additions and 56 deletions

View File

@@ -4,7 +4,7 @@ from datetime import datetime
from typing import List, Optional
import httpx
from fastapi import APIRouter, BackgroundTasks, Depends
from fastapi import APIRouter, BackgroundTasks, Depends, Query
from sqlalchemy.orm import Session
from ...errors import ClanError
@@ -15,6 +15,7 @@ from ..schemas import (
Eventmessage,
EventmessageCreate,
Resolution,
Role,
Service,
ServiceCreate,
)
@@ -81,23 +82,6 @@ def delete_service(
return {"message": "service deleted"}
#########################
# #
# REPOSITORY #
# #
#########################
@router.get(
"/api/v1/repositories",
response_model=List[Service],
tags=[Tags.repositories],
)
def get_all_repositories(
skip: int = 0, limit: int = 100, db: Session = Depends(sql_db.get_db)
) -> List[sql_models.Service]:
repositories = sql_crud.get_services(db, skip=skip, limit=limit)
return repositories
#########################
# #
# Entity #
@@ -106,17 +90,29 @@ def get_all_repositories(
@router.post("/api/v1/entity", response_model=Entity, tags=[Tags.entities])
def create_entity(
entity: EntityCreate, db: Session = Depends(sql_db.get_db)
) -> EntityCreate:
) -> sql_models.Entity:
return sql_crud.create_entity(db, entity)
@router.get(
"/api/v1/entity_by_name", response_model=Optional[Entity], tags=[Tags.entities]
"/api/v1/entity_by_name_or_did",
response_model=Optional[Entity],
tags=[Tags.entities],
)
def get_entity_by_name(
entity_name: str, db: Session = Depends(sql_db.get_db)
def get_entity_by_name_or_did(
entity_name_or_did: str = "C1", db: Session = Depends(sql_db.get_db)
) -> Optional[sql_models.Entity]:
entity = sql_crud.get_entity_by_name(db, name=entity_name)
entity = sql_crud.get_entity_by_name_or_did(db, name=entity_name_or_did)
return entity
@router.get(
"/api/v1/entity_by_roles", response_model=List[Entity], tags=[Tags.entities]
)
def get_entity_by_roles(
roles: List[Role] = Query(...), db: Session = Depends(sql_db.get_db)
) -> List[sql_models.Entity]:
entity = sql_crud.get_entity_by_role(db, roles=roles)
return entity
@@ -149,7 +145,7 @@ def get_attached_entities(
return entities
@router.post("/api/v1/detach", tags=[Tags.entities])
@router.put("/api/v1/detach", tags=[Tags.entities])
def detach_entity(
background_tasks: BackgroundTasks,
entity_did: str = "did:sov:test:1234",
@@ -164,7 +160,7 @@ def detach_entity(
return {"message": f"Detached {entity_did} successfully"}
@router.post("/api/v1/attach", tags=[Tags.entities])
@router.put("/api/v1/attach", tags=[Tags.entities])
def attach_entity(
background_tasks: BackgroundTasks,
entity_did: str = "did:sov:test:1234",
@@ -175,7 +171,7 @@ def attach_entity(
entity = sql_crud.get_entity_by_did(db, did=entity_did)
if entity is None:
raise ClanError(f"Entity with did '{entity_did}' not found")
url = f"http://{entity.ip}"
url = f"http://{entity.ip}/health"
sql_crud.set_stop_health_task(db, entity_did, False)
print("Start health query at", url)
background_tasks.add_task(attach_entity_loc, db, entity_did)
@@ -209,7 +205,7 @@ def attach_entity_loc(db: Session, entity_did: str) -> None:
entity = sql_crud.get_entity_by_did(db, did=entity_did)
try:
assert entity is not None
url = f"http://{entity.ip}"
url = f"http://{entity.ip}/health"
while entity.stop_health_task is False:
response = httpx.get(url, timeout=2)
@@ -273,7 +269,9 @@ async def get_all_resolutions(
#########################
@router.post("/api/v1/send_msg", response_model=Eventmessage, tags=[Tags.eventmessages])
@router.post(
"/api/v1/event_message", response_model=Eventmessage, tags=[Tags.eventmessages]
)
async def create_eventmessage(
eventmsg: EventmessageCreate, db: Session = Depends(sql_db.get_db)
) -> EventmessageCreate:

View File

@@ -1,8 +1,11 @@
import logging
from datetime import datetime
from enum import Enum
from typing import List
from pydantic import BaseModel, Field
from pydantic import BaseModel, Field, validator
log = logging.getLogger(__name__)
class Status(Enum):
@@ -11,7 +14,7 @@ class Status(Enum):
UNKNOWN = "unknown"
class Roles(Enum):
class Role(Enum):
PROSUMER = "service_prosumer"
AP = "AP"
DLG = "DLG"
@@ -27,35 +30,51 @@ class Machine(BaseModel):
# Entity #
# #
#########################
class EntityRolesBase(BaseModel):
role: Role = Field(..., example=Role("service_prosumer"))
class EntityRolesCreate(EntityRolesBase):
id: int = Field(...)
entity_did: str = Field(...)
class EntityRoles(EntityRolesBase):
class Config:
orm_mode = True
class EntityBase(BaseModel):
did: str = Field(..., example="did:sov:test:1234")
name: str = Field(..., example="C1")
ip: str = Field(..., example="127.0.0.1")
network: str = Field(..., example="255.255.0.0")
role: Roles = Field(
..., example=Roles("service_prosumer")
) # roles are needed for UI to show the correct view
visible: bool = Field(..., example=True)
other: dict = Field(
...,
example={
"network": "Carlos Home Network",
"roles": ["service repository", "service prosumer"],
},
)
class EntityCreate(EntityBase):
pass
roles: List[Role] = Field(..., example=[Role("service_prosumer"), Role("AP")])
class Entity(EntityCreate):
class Entity(EntityBase):
attached: bool = Field(...)
stop_health_task: bool = Field(...)
roles: List[Role]
class Config:
orm_mode = True
# define a custom getter function for roles
@validator("roles", pre=True)
def get_roles(cls, v: List[EntityRoles]) -> List[Role]:
return [x.role for x in v]
#########################
# #
@@ -122,7 +141,6 @@ class Resolution(ResolutionCreate):
# #
#########################
class EventmessageBase(BaseModel):
id: int = Field(..., example=123456)
timestamp: int = Field(..., example=1234123413)
group: int = Field(..., example=1) # event group type (for the label)
group_id: int = Field(
@@ -135,9 +153,10 @@ class EventmessageBase(BaseModel):
class EventmessageCreate(EventmessageBase):
msg: dict = Field(..., example={"optinal": "values"}) # optional
pass
class Eventmessage(EventmessageCreate):
id: int = Field(...)
class Config:
orm_mode = True

View File

@@ -65,8 +65,21 @@ def get_services_without_entity_id(
#########################
def create_entity(db: Session, entity: schemas.EntityCreate) -> sql_models.Entity:
db_entity = sql_models.Entity(
**entity.dict(), attached=False, stop_health_task=False
did=entity.did,
name=entity.name,
ip=entity.ip,
network=entity.network,
visible=entity.visible,
other=entity.other,
attached=False,
stop_health_task=False,
)
db_roles = []
for role in entity.roles:
db_roles.append(sql_models.EntityRoles(role=role))
db_entity.roles = db_roles
db.add(db_entity)
db.commit()
db.refresh(db_entity)
@@ -83,8 +96,22 @@ def get_entity_by_did(db: Session, did: str) -> Optional[sql_models.Entity]:
return db.query(sql_models.Entity).filter(sql_models.Entity.did == did).first()
def get_entity_by_name(db: Session, name: str) -> Optional[sql_models.Entity]:
return db.query(sql_models.Entity).filter(sql_models.Entity.name == name).first()
def get_entity_by_name_or_did(db: Session, name: str) -> Optional[sql_models.Entity]:
return (
db.query(sql_models.Entity)
.filter((sql_models.Entity.name == name) | (sql_models.Entity.did == name))
.first()
)
def get_entity_by_role(
db: Session, roles: List[schemas.Role]
) -> List[sql_models.Entity]:
return (
db.query(sql_models.Entity)
.filter(sql_models.Entity.roles.any(sql_models.EntityRoles.role.in_(roles)))
.all()
)
# get attached
@@ -107,12 +134,11 @@ def set_stop_health_task(db: Session, entity_did: str, value: bool) -> None:
if db_entity is None:
raise ClanError(f"Entity with did '{entity_did}' not found")
setattr(db_entity, "stop_health_task", value)
db_entity.stop_health_task = value # type: ignore
# save changes in db
db.add(db_entity)
db.commit()
db.refresh(db_entity)
def set_attached_by_entity_did(db: Session, entity_did: str, attached: bool) -> None:
@@ -120,12 +146,11 @@ def set_attached_by_entity_did(db: Session, entity_did: str, attached: bool) ->
if db_entity is None:
raise ClanError(f"Entity with did '{entity_did}' not found")
setattr(db_entity, "attached", attached)
db_entity.attached = attached # type: ignore
# save changes in db
db.add(db_entity)
db.commit()
db.refresh(db_entity)
def delete_entity_by_did(db: Session, did: str) -> None:
@@ -148,7 +173,15 @@ def delete_entity_by_did_recursive(db: Session, did: str) -> None:
def create_eventmessage(
db: Session, eventmsg: schemas.EventmessageCreate
) -> sql_models.Eventmessage:
db_eventmessage = sql_models.Eventmessage(**eventmsg.dict())
db_eventmessage = sql_models.Eventmessage(
timestamp=eventmsg.timestamp,
group=eventmsg.group,
group_id=eventmsg.group_id,
msg_type=eventmsg.msg_type,
src_did=eventmsg.src_did,
des_did=eventmsg.des_did,
msg=eventmsg.msg,
)
db.add(db_eventmessage)
db.commit()
db.refresh(db_eventmessage)

View File

@@ -1,7 +1,7 @@
from sqlalchemy import JSON, Boolean, Column, Enum, ForeignKey, Integer, String, Text
from sqlalchemy.orm import relationship
from .schemas import Roles
from .schemas import Role
from .sql_db import Base
# Relationsship example
@@ -16,8 +16,6 @@ class Entity(Base):
name = Column(String, index=True, unique=True)
ip = Column(String, index=True)
network = Column(String, index=True)
role = Column(Enum(Roles), index=True, nullable=False) # type: ignore
# role = Column(String, index=True, nullable=False)
attached = Column(Boolean, index=True)
visible = Column(Boolean, index=True)
stop_health_task = Column(Boolean)
@@ -28,6 +26,19 @@ class Entity(Base):
## Relations ##
services = relationship("Service", back_populates="entity")
roles = relationship("EntityRoles", back_populates="entity")
class EntityRoles(Base):
__tablename__ = "entity_roles"
## Queryable body ##
id = Column(Integer, primary_key=True, autoincrement=True)
entity_did = Column(String, ForeignKey("entities.did"))
role = Column(Enum(Role), index=True, nullable=False) # type: ignore
## Relations ##
entity = relationship("Entity", back_populates="roles")
class ServiceAbstract(Base):
@@ -58,7 +69,7 @@ class Eventmessage(Base):
__tablename__ = "eventmessages"
## Queryable body ##
id = Column(Integer, primary_key=True, index=True)
id = Column(Integer, primary_key=True, autoincrement=True)
timestamp = Column(Integer, unique=True, index=True)
group = Column(Integer, index=True)
group_id = Column(Integer, index=True)

View File

@@ -22,10 +22,6 @@ tags_metadata: List[Dict[str, Any]] = [
"name": str(Tags.entities),
"description": "Operations on an entity.",
},
{
"name": str(Tags.repositories),
"description": "Operations on a repository.",
},
{
"name": str(Tags.resolutions),
"description": "Operations on a resolution.",

View File

@@ -68,7 +68,7 @@ ignore_missing_imports = true
[tool.ruff]
line-length = 88
select = [ "E", "F", "I", "N"]
ignore = [ "E501" ]
ignore = [ "E501", "N805" ]
exclude = ["tests/openapi_client"]
[tool.black]

View File

@@ -38,7 +38,7 @@ def test_health(api_client: ApiClient) -> None:
assert res.status == Status.ONLINE
def create_entities(num: int = 10) -> list[EntityCreate]:
def create_entities(num: int = 10, role: str = "entity") -> list[EntityCreate]:
res = []
for i in range(num):
en = EntityCreate(
@@ -90,6 +90,7 @@ def create_service(idx: int, entity: Entity) -> ServiceCreate:
def test_create_entities(api_client: ApiClient) -> None:
api = EntitiesApi(api_client=api_client)
for own_entity in create_entities():
res: Entity = api.create_entity(own_entity)
assert res.did == own_entity.did