diff --git a/pkgs/clan-cli/clan_cli/webui/app.py b/pkgs/clan-cli/clan_cli/webui/app.py index de13526..8397d16 100644 --- a/pkgs/clan-cli/clan_cli/webui/app.py +++ b/pkgs/clan-cli/clan_cli/webui/app.py @@ -14,6 +14,7 @@ from .assets import asset_path from .error_handlers import clan_error_handler from .routers import health, root, socket_manager2, sql_connect # sql router hinzufügen from .sql_db import engine +from .tags import tags_metadata origins = [ "http://localhost:3000", @@ -55,6 +56,9 @@ def setup_app() -> FastAPI: app.mount("/static", StaticFiles(directory=asset_path()), name="static") + # Add tag descriptions to the OpenAPI schema + app.openapi_tags = tags_metadata + for route in app.routes: if isinstance(route, APIRoute): route.operation_id = route.name # in this case, 'read_items' diff --git a/pkgs/clan-cli/clan_cli/webui/routers/health.py b/pkgs/clan-cli/clan_cli/webui/routers/health.py index b74b71b..ab0edb4 100644 --- a/pkgs/clan-cli/clan_cli/webui/routers/health.py +++ b/pkgs/clan-cli/clan_cli/webui/routers/health.py @@ -1,6 +1,6 @@ from fastapi import APIRouter -from ..api_outputs import Machine, Status +from ..schemas import Machine, Status router = APIRouter() diff --git a/pkgs/clan-cli/clan_cli/webui/routers/socket_manager.py b/pkgs/clan-cli/clan_cli/webui/routers/socket_manager.py deleted file mode 100644 index cdfa46e..0000000 --- a/pkgs/clan-cli/clan_cli/webui/routers/socket_manager.py +++ /dev/null @@ -1,79 +0,0 @@ -from fastapi import FastAPI, WebSocket, WebSocketDisconnect -from fastapi.responses import HTMLResponse -from fastapi import APIRouter, Response - -router = APIRouter() - -html = """ - - - - Chat - - -

WebSocket Chat

-

Your ID:

-
- - -
- - - - -""" - -class ConnectionManager: - def __init__(self) -> None: - self.active_connections: list[WebSocket] = [] - - async def connect(self, websocket: WebSocket) -> None: - await websocket.accept() - self.active_connections.append(websocket) - - def disconnect(self, websocket: WebSocket) -> None: - self.active_connections.remove(websocket) - - async def send_personal_message(self, message: str, websocket: WebSocket) -> None: - await websocket.send_text(message) - - async def broadcast(self, message: str) -> None: - for connection in self.active_connections: - await connection.send_text(message) - - -manager = ConnectionManager() - -@router.get("/ws_example") -async def get() -> HTMLResponse: - return HTMLResponse(html) - -@router.websocket("/ws/{client_id}") -async def websocket_endpoint(websocket: WebSocket, client_id: int) -> None: - await manager.connect(websocket) - try: - while True: - data = await websocket.receive_text() - await manager.send_personal_message(f"You wrote: {data}", websocket) - await manager.broadcast(f"Client #{client_id} says: {data}") - except WebSocketDisconnect: - manager.disconnect(websocket) - await manager.broadcast(f"Client #{client_id} left the chat") \ No newline at end of file diff --git a/pkgs/clan-cli/clan_cli/webui/routers/sql_connect.py b/pkgs/clan-cli/clan_cli/webui/routers/sql_connect.py index 72307de..46263e5 100644 --- a/pkgs/clan-cli/clan_cli/webui/routers/sql_connect.py +++ b/pkgs/clan-cli/clan_cli/webui/routers/sql_connect.py @@ -4,22 +4,39 @@ from fastapi import APIRouter, Depends from sqlalchemy.orm import Session from .. import sql_crud, sql_db, sql_models -from ..api_outputs import Producer, ProducerCreate +from ..schemas import Entity, EntityCreate +from ..tags import Tags router = APIRouter() -@router.get("/get_producers", response_model=List[Producer]) -def get_producers( - skip: int = 0, limit: int = 100, db: Session = Depends(sql_db.get_db) -) -> List[sql_models.Producer]: - producers = sql_crud.get_producers(db, skip=skip, limit=limit) - return producers +# @router.get("/api/v1/get_producers", response_model=List[Producer], tags=[Tags.producers]) +# def get_producers( +# skip: int = 0, limit: int = 100, db: Session = Depends(sql_db.get_db) +# ) -> List[sql_models.Producer]: +# producers = sql_crud.get_producers(db, skip=skip, limit=limit) +# return producers -@router.post("/create_producers", response_model=Producer) -def create_producers( - producer: ProducerCreate, db: Session = Depends(sql_db.get_db) -) -> Producer: +# @router.post("/api/v1/create_producer", response_model=Producer, tags=[Tags.producers]) +# def create_producer( +# producer: ProducerCreate, db: Session = Depends(sql_db.get_db) +# ) -> Producer: +# # todo checken ob schon da ... +# return sql_crud.create_producer(db=db, producer=producer) + + +@router.post("/api/v1/create_entity", response_model=Entity, tags=[Tags.entities]) +def create_entity( + entity: EntityCreate, db: Session = Depends(sql_db.get_db) +) -> EntityCreate: # todo checken ob schon da ... - return sql_crud.create_producer(db=db, producer=producer) + return sql_crud.create_entity(db, entity) + + +@router.get("/api/v1/get_entities", response_model=List[Entity], tags=[Tags.entities]) +def get_entities( + skip: int = 0, limit: int = 100, db: Session = Depends(sql_db.get_db) +) -> List[sql_models.Entity]: + entities = sql_crud.get_entities(db, skip=skip, limit=limit) + return entities diff --git a/pkgs/clan-cli/clan_cli/webui/api_outputs.py b/pkgs/clan-cli/clan_cli/webui/schemas.py similarity index 74% rename from pkgs/clan-cli/clan_cli/webui/api_outputs.py rename to pkgs/clan-cli/clan_cli/webui/schemas.py index f0f2632..cb118f2 100644 --- a/pkgs/clan-cli/clan_cli/webui/api_outputs.py +++ b/pkgs/clan-cli/clan_cli/webui/schemas.py @@ -14,6 +14,23 @@ class Machine(BaseModel): status: Status +class EntityBase(BaseModel): + did: str = "did:sov:test:1234" + name: str = "C1" + ip: str = "127.0.0.1" + attached: bool = False + other: dict = {"test": "test"} + + +class Entity(EntityBase): + class Config: + orm_mode = True + + +class EntityCreate(EntityBase): + pass + + class RepositoryBase(BaseModel): title: str description: str | None = None diff --git a/pkgs/clan-cli/clan_cli/webui/sql_crud.py b/pkgs/clan-cli/clan_cli/webui/sql_crud.py index 0c66d52..c3d5a61 100644 --- a/pkgs/clan-cli/clan_cli/webui/sql_crud.py +++ b/pkgs/clan-cli/clan_cli/webui/sql_crud.py @@ -2,37 +2,51 @@ from typing import List from sqlalchemy.orm import Session -from . import api_outputs, sql_models +from . import schemas, sql_models -def get_producers( - db: Session, skip: int = 0, limit: int = 100 -) -> List[sql_models.Producer]: - return db.query(sql_models.Producer).offset(skip).limit(limit).all() - - -def create_producer( - db: Session, producer: api_outputs.ProducerCreate -) -> sql_models.Producer: - jsonblob_init = {"test_repo": "jsonblob_create"} - db_producer = sql_models.Producer(jsonblob=jsonblob_init) - db.add(db_producer) +def create_entity(db: Session, entity: schemas.EntityCreate) -> sql_models.Entity: + db_entity = sql_models.Entity(**entity.dict()) + db.add(db_entity) db.commit() - db.refresh(db_producer) - return db_producer + db.refresh(db_entity) + return db_entity -def get_repositories( +def get_entities( db: Session, skip: int = 0, limit: int = 100 -) -> List[sql_models.Repository]: - return db.query(sql_models.Repository).offset(skip).limit(limit).all() +) -> List[sql_models.Entity]: + return db.query(sql_models.Entity).offset(skip).limit(limit).all() -def create_repository( - db: Session, repository: api_outputs.RepositoryCreate, producers_id: int -) -> sql_models.Repository: - db_repository = sql_models.Repository(**repository.dict(), prod_id=producers_id) - db.add(db_repository) - db.commit() - db.refresh(db_repository) - return db_repository +# def get_producers( +# db: Session, skip: int = 0, limit: int = 100 +# ) -> List[sql_models.Producer]: +# return db.query(sql_models.Producer).offset(skip).limit(limit).all() + + +# def create_producer( +# db: Session, producer: schemas.ProducerCreate +# ) -> sql_models.Producer: +# jsonblob_init = {"test_repo": "jsonblob_create"} +# db_producer = sql_models.Producer(jsonblob=jsonblob_init) +# db.add(db_producer) +# db.commit() +# db.refresh(db_producer) +# return db_producer + + +# def get_repositories( +# db: Session, skip: int = 0, limit: int = 100 +# ) -> List[sql_models.Repository]: +# return db.query(sql_models.Repository).offset(skip).limit(limit).all() + + +# def create_repository( +# db: Session, repository: schemas.RepositoryCreate, producers_id: int +# ) -> sql_models.Repository: +# db_repository = sql_models.Repository(**repository.dict(), prod_id=producers_id) +# db.add(db_repository) +# db.commit() +# db.refresh(db_repository) +# return db_repository diff --git a/pkgs/clan-cli/clan_cli/webui/sql_models.py b/pkgs/clan-cli/clan_cli/webui/sql_models.py index f357238..d9cf59f 100644 --- a/pkgs/clan-cli/clan_cli/webui/sql_models.py +++ b/pkgs/clan-cli/clan_cli/webui/sql_models.py @@ -1,23 +1,90 @@ -from sqlalchemy import JSON, Column, ForeignKey, Integer +from sqlalchemy import JSON, Boolean, Column, DateTime, ForeignKey, Integer, String from sqlalchemy.orm import relationship from .sql_db import Base +# Relationsship example +# https://dev.to/freddiemazzilli/flask-sqlalchemy-relationships-exploring-relationship-associations-igo -class Producer(Base): + +class Entity(Base): + __tablename__ = "entities" + + ## Queryable body ## + did = Column(String, primary_key=True, index=True) + name = Column(String, index=True) + ip = Column(String, index=True) + attached = Column(Boolean, index=True) + + ## Non queryable body ## + # In here we deposit: Network, Roles, Visible, etc. + other = Column(JSON) + + ## Relations ## + producers = relationship("Producer", back_populates="entity") + consumers = relationship("Consumer", back_populates="entity") + # repository = relationship("Repository", uselist=False, back_populates="entity") + + +class ProducerAbstract(Base): + __abstract__ = True + + # Queryable body + id = Column(Integer, primary_key=True, index=True) + service_name = Column(String, unique=True, index=True) + service_type = Column(String, index=True) + endpoint_url = Column(String, index=True) + status = Column(String, index=True) + + ## Non queryable body ## + # In here we deposit: Action + other = Column(JSON) + + +class Producer(ProducerAbstract): __tablename__ = "producers" + # Usage is the consumers column + + ## Relations ## + # One entity can have many producers + entity = relationship("Entity", back_populates="producers") + entity_did = Column(Integer, ForeignKey("entities.did")) + + # One producer has many consumers + consumers = relationship("Consumer", back_populates="producer") + + +class Consumer(Base): + __tablename__ = "consumers" + + ## Queryable body ## id = Column(Integer, primary_key=True, index=True) - jsonblob = Column(JSON) - repos = relationship("Repository", back_populates="producer") + ## Relations ## + # one entity can have many consumers + entity = relationship("Entity", back_populates="consumers") + entity_did = Column(Integer, ForeignKey("entities.did")) + + # one consumer has one producer + producer = relationship("Producer", back_populates="consumers") + producer_id = Column(Integer, ForeignKey("producers.id")) -class Repository(Base): - __tablename__ = "repositories" +# class Repository(ProducerAbstract): +# __tablename__ = "repositories" - id = Column(Integer, primary_key=True, index=True) - jsonblob = Column(JSON) - prod_id = Column(Integer, ForeignKey("producers.id")) +# # one repository has one entity +# entity = relationship("Entity", back_populates="repository") +# entity_did = Column(Integer, ForeignKey("entities.did")) - producer = relationship("Producer", back_populates="repos") + +# TODO: Ask how this works exactly +class Resolution(Base): + __tablename__ = "resolutions" + + id = Column(Integer, primary_key=True) + requester_name = Column(String, index=True) + requester_did = Column(String, index=True) + resolved_did = Column(String, index=True) + timestamp = Column(DateTime, index=True) diff --git a/pkgs/clan-cli/clan_cli/webui/tags.py b/pkgs/clan-cli/clan_cli/webui/tags.py new file mode 100644 index 0000000..78f7968 --- /dev/null +++ b/pkgs/clan-cli/clan_cli/webui/tags.py @@ -0,0 +1,32 @@ +from enum import Enum +from typing import Any, Dict, List + + +class Tags(Enum): + producers = "producers" + consumers = "consumers" + entities = "entities" + repositories = "repositories" + + def __str__(self) -> str: + return self.value + + +tags_metadata: List[Dict[str, Any]] = [ + { + "name": str(Tags.producers), + "description": "Operations on a producer.", + }, + { + "name": str(Tags.consumers), + "description": "Operations on a consumer.", + }, + { + "name": str(Tags.entities), + "description": "Operations on an entity.", + }, + { + "name": str(Tags.repositories), + "description": "Operations on a repository.", + }, +]