Merge pull request 'backend: Fully working roles field. Added get_entity_by_roles' (#50) from Qubasa-main into main
All checks were successful
checks-impure / test (push) Successful in 25s
checks / test (push) Successful in 1m18s
assets1 / test (push) Successful in 21s

This commit was merged in pull request #50.
This commit is contained in:
2024-01-14 13:57:43 +01:00
36 changed files with 748 additions and 707 deletions

View File

@@ -5,7 +5,7 @@ from types import ModuleType
from typing import Optional
from . import webui
from .custom_logger import register
from .custom_logger import setup_logging
log = logging.getLogger(__name__)
@@ -30,10 +30,6 @@ def create_parser(prog: Optional[str] = None) -> argparse.ArgumentParser:
parser_webui = subparsers.add_parser("webui", help="start webui")
webui.register_parser(parser_webui)
# if args.debug:
register(logging.DEBUG)
log.debug("Debug log activated")
if argcomplete:
argcomplete.autocomplete(parser)
@@ -47,6 +43,12 @@ def main() -> None:
parser = create_parser()
args = parser.parse_args()
if args.debug:
setup_logging(logging.DEBUG)
log.debug("Debug log activated")
else:
setup_logging(logging.INFO)
if not hasattr(args, "func"):
return

View File

@@ -0,0 +1,8 @@
host = "127.0.0.1"
port_dlg = 7000
port_ap = 7500
port_client_base = 8000
dlg_url = f"http://{host}:{port_dlg}/docs"
ap_url = f"http://{host}:{port_ap}/docs"
c1_url = f"http://{host}:{port_client_base}/docs"
c2_url = f"http://{host}:{port_client_base + 1}/docs"

View File

@@ -61,10 +61,19 @@ def get_caller() -> str:
return ret
def register(level: Any) -> None:
handler = logging.StreamHandler()
handler.setLevel(level)
handler.setFormatter(CustomFormatter())
logger = logging.getLogger("registerHandler")
logger.addHandler(handler)
# logging.basicConfig(level=level, handlers=[handler])
def setup_logging(level: Any) -> None:
# Get the root logger and set its level
main_logger = logging.getLogger("clan_cli")
main_logger.setLevel(level)
# Create and add the default handler
default_handler = logging.StreamHandler()
# Create and add your custom handler
default_handler.setLevel(level)
default_handler.setFormatter(CustomFormatter())
main_logger.addHandler(default_handler)
# Set logging level for other modules used by this module
logging.getLogger("asyncio").setLevel(logging.INFO)
logging.getLogger("httpx").setLevel(level=logging.WARNING)

View File

@@ -0,0 +1,178 @@
import sys
import time
import urllib
from datetime import datetime
from fastapi import FastAPI
from fastapi.responses import HTMLResponse, JSONResponse
import clan_cli.config as config
from clan_cli.webui.schemas import Resolution
app_dlg = FastAPI(swagger_ui_parameters={"tryItOutEnabled": True})
app_ap = FastAPI(swagger_ui_parameters={"tryItOutEnabled": True})
app_c1 = FastAPI(swagger_ui_parameters={"tryItOutEnabled": True})
app_c2 = FastAPI(swagger_ui_parameters={"tryItOutEnabled": True})
apps = [
(app_dlg, config.port_dlg),
(app_ap, config.port_ap),
(app_c1, config.port_client_base),
(app_c2, config.port_client_base + 1),
]
#### HEALTHCHECK
@app_c1.get("/")
async def root_c1() -> str:
return "C1 is alive"
@app_c1.get("/health")
async def healthcheck_c1() -> str:
return "200 OK"
@app_c2.get("/")
async def root_c2() -> str:
return "C2 is alive"
@app_c2.get("/health")
async def healthcheck_c2() -> str:
return "200 OK"
@app_dlg.get("/")
async def root_dlg() -> str:
return "DLG is alive"
@app_dlg.get("/health")
async def healthcheck_dlg() -> str:
return "200 OK"
@app_ap.get("/")
async def root_ap() -> str:
return "AP is alive"
@app_ap.get("/health")
async def healthcheck_ap() -> str:
return "200 OK"
def get_health(*, url: str, max_retries: int = 20, delay: float = 0.2) -> str | None:
for attempt in range(max_retries):
try:
with urllib.request.urlopen(url) as response: # type: ignore
return response.read()
except urllib.error.URLError as e: # type: ignore
print(f"Attempt {attempt + 1} failed: {e.reason}", file=sys.stderr)
time.sleep(delay)
return None
#### CONSUME SERVICE
# TODO send_msg???
@app_c1.get("/consume_service_from_other_entity", response_class=HTMLResponse)
async def consume_service_from_other_entity_c1() -> HTMLResponse:
html_content = """
<html>
<body>
<div style="width:480px"><iframe allow="fullscreen" frameBorder="0" height="270" src="https://giphy.com/embed/IOWD3uknMxYyh7CsgN/video" width="480"></iframe></div>
</body>
</html>
"""
time.sleep(3)
return HTMLResponse(content=html_content, status_code=200)
@app_c2.get("/consume_service_from_other_entity", response_class=HTMLResponse)
async def consume_service_from_other_entity_c2() -> HTMLResponse:
html_content = """
<html>
<body>
<div style="width:480px"><iframe allow="fullscreen" frameBorder="0" height="270" src="https://giphy.com/embed/IOWD3uknMxYyh7CsgN/video" width="480"></iframe></div>
</body>
</html>
"""
time.sleep(3)
return HTMLResponse(content=html_content, status_code=200)
@app_ap.get("/ap_list_of_services", response_class=JSONResponse)
async def ap_list_of_services() -> JSONResponse:
res = [
{
"uuid": "98ae4334-6c12-ace8-ae34-0454cac5b68c",
"service_name": "Carlos Printing46",
"service_type": "3D Printing",
"endpoint_url": "127.0.0.1:6600/v1/print_daemon46",
"status": "unknown",
"other": {"action": ["register", "deregister", "delete", "create"]},
"entity_did": "did:sov:test:6600",
"entity": {
"did": "did:sov:test:6600",
"name": "AP",
"ip": "127.0.0.1:6600",
"network": "255.255.0.0",
"visible": True,
"other": {},
"attached": False,
"stop_health_task": False,
"roles": ["AP"],
},
},
{
"uuid": "988c24c9-61b1-cd22-6280-1c4510435a10",
"service_name": "Carlos Printing47",
"service_type": "3D Printing",
"endpoint_url": "127.0.0.1:6600/v1/print_daemon47",
"status": "unknown",
"other": {"action": ["register", "deregister", "delete", "create"]},
"entity_did": "did:sov:test:6600",
"entity": {
"did": "did:sov:test:6600",
"name": "AP",
"ip": "127.0.0.1:6600",
"network": "255.255.0.0",
"visible": True,
"other": {},
"attached": False,
"stop_health_task": False,
"roles": ["AP"],
},
},
]
# resp = json.dumps(obj=res)
return JSONResponse(content=res, status_code=200)
@app_dlg.get("/dlg_list_of_did_resolutions", response_model=list[Resolution])
async def dlg_list_of_did_resolutions() -> list[Resolution]:
res = []
res.append(
Resolution(
timestamp=datetime.fromisoformat("2021-10-12T12:52:00.000Z"),
requester_name="C1",
requester_did="did:sov:test:1122",
resolved_did="did:sov:test:1234",
other={"test": "test"},
)
)
res.append(
Resolution(
timestamp=datetime.fromisoformat("2021-10-12T12:53:00.000Z"),
requester_name="C2",
requester_did="did:sov:test:1123",
resolved_did="did:sov:test:1234",
other={"test": "test"},
)
)
return res

View File

@@ -1,11 +1,15 @@
import argparse
import logging
from typing import Callable, NoReturn, Optional
log = logging.getLogger(__name__)
start_server: Optional[Callable] = None
ServerImportError: Optional[ImportError] = None
try:
from .server import start_server
except ImportError as e:
log.exception(e)
ServerImportError = e

View File

@@ -1,10 +1,10 @@
import logging
import time
from datetime import datetime
from typing import List, Optional
from typing import Any, List, Optional
import httpx
from fastapi import APIRouter, BackgroundTasks, Depends
from fastapi import APIRouter, BackgroundTasks, Depends, Query
from fastapi.responses import HTMLResponse
from sqlalchemy.orm import Session
from ...errors import ClanError
@@ -15,6 +15,7 @@ from ..schemas import (
Eventmessage,
EventmessageCreate,
Resolution,
Role,
Service,
ServiceCreate,
)
@@ -48,7 +49,7 @@ def get_all_services(
@router.get("/api/v1/service", response_model=List[Service], tags=[Tags.services])
def get_service_by_did(
entity_did: str = "did:sov:test:1234",
entity_did: str = "did:sov:test:120",
skip: int = 0,
limit: int = 100,
db: Session = Depends(sql_db.get_db),
@@ -63,7 +64,7 @@ def get_service_by_did(
tags=[Tags.services],
)
def get_services_without_entity(
entity_did: str = "did:sov:test:1234",
entity_did: str = "did:sov:test:120",
skip: int = 0,
limit: int = 100,
db: Session = Depends(sql_db.get_db),
@@ -74,30 +75,13 @@ def get_services_without_entity(
@router.delete("/api/v1/service", tags=[Tags.services])
def delete_service(
entity_did: str = "did:sov:test:1234",
entity_did: str = "did:sov:test:120",
db: Session = Depends(sql_db.get_db),
) -> dict[str, str]:
sql_crud.delete_service_by_entity_did(db, entity_did)
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
@@ -130,7 +126,7 @@ def get_all_entities(
@router.get("/api/v1/entity", response_model=Optional[Entity], tags=[Tags.entities])
def get_entity_by_did(
entity_did: str = "did:sov:test:1234",
entity_did: str = "did:sov:test:120",
db: Session = Depends(sql_db.get_db),
) -> Optional[sql_models.Entity]:
entity = sql_crud.get_entity_by_did(db, did=entity_did)
@@ -149,10 +145,10 @@ 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",
entity_did: str = "did:sov:test:120",
skip: int = 0,
limit: int = 100,
db: Session = Depends(sql_db.get_db),
@@ -164,10 +160,10 @@ 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",
entity_did: str = "did:sov:test:120",
skip: int = 0,
limit: int = 100,
db: Session = Depends(sql_db.get_db),
@@ -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)
@@ -184,7 +180,7 @@ def attach_entity(
@router.get("/api/v1/is_attached", tags=[Tags.entities])
def is_attached(
entity_did: str = "did:sov:test:1234", db: Session = Depends(sql_db.get_db)
entity_did: str = "did:sov:test:120", db: Session = Depends(sql_db.get_db)
) -> dict[str, str]:
entity = sql_crud.get_entity_by_did(db, did=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)
@@ -233,14 +229,34 @@ def attach_entity_loc(db: Session, entity_did: str) -> None:
@router.delete("/api/v1/entity", tags=[Tags.entities])
async def delete_entity(
entity_did: str = "did:sov:test:1234",
def delete_entity(
entity_did: str = "did:sov:test:120",
db: Session = Depends(sql_db.get_db),
) -> dict[str, str]:
sql_crud.delete_entity_by_did_recursive(db, did=entity_did)
return {"message": "Entity deleted and all relations to that entity"}
def get_rpc_by_role(db: Session, role: Role, path: str) -> Any:
matching_entities = sql_crud.get_entity_by_role(db, roles=[role])
if matching_entities is None:
raise ClanError(f"No {role} found")
if len(matching_entities) > 1:
raise ClanError(f"More than one {role} found")
dlg = matching_entities[0]
url = f"http://{dlg.ip}/{path}"
try:
response = httpx.get(url, timeout=2)
except httpx.HTTPError as e:
raise ClanError(f"{role} not reachable at {url}") from e
if response.status_code != 200:
raise ClanError(f"{role} returned {response.status_code}")
return response.json()
#########################
# #
# Resolution #
@@ -249,21 +265,24 @@ async def delete_entity(
@router.get(
"/api/v1/resolutions", response_model=List[Resolution], tags=[Tags.resolutions]
)
async def get_all_resolutions(
def get_all_resolutions(
skip: int = 0, limit: int = 100, db: Session = Depends(sql_db.get_db)
) -> List[Resolution]:
# TODO: Get resolutions from DLG entity
return get_rpc_by_role(db, Role("DLG"), "dlg_list_of_did_resolutions")
return [
Resolution(
requester_name="C1",
requester_did="did:sov:test:1122",
resolved_did="did:sov:test:1234",
other={"test": "test"},
timestamp=datetime.now(),
id=1,
)
]
#########################
# #
# Repository #
# #
#########################
@router.get(
"/api/v1/repositories", tags=[Tags.repositories], response_model=List[Service]
)
def get_all_repositories(
skip: int = 0, limit: int = 100, db: Session = Depends(sql_db.get_db)
) -> List[Service]:
return get_rpc_by_role(db, Role("AP"), "ap_list_of_services")
#########################
@@ -271,10 +290,10 @@ async def get_all_resolutions(
# Eventmessage #
# #
#########################
@router.post("/api/v1/send_msg", response_model=Eventmessage, tags=[Tags.eventmessages])
async def create_eventmessage(
@router.post(
"/api/v1/event_message", response_model=Eventmessage, tags=[Tags.eventmessages]
)
def create_eventmessage(
eventmsg: EventmessageCreate, db: Session = Depends(sql_db.get_db)
) -> EventmessageCreate:
return sql_crud.create_eventmessage(db, eventmsg)
@@ -285,8 +304,36 @@ async def create_eventmessage(
response_model=List[Eventmessage],
tags=[Tags.eventmessages],
)
async def get_all_eventmessages(
def get_all_eventmessages(
skip: int = 0, limit: int = 100, db: Session = Depends(sql_db.get_db)
) -> List[sql_models.Eventmessage]:
eventmessages = sql_crud.get_eventmessages(db, skip=skip, limit=limit)
return eventmessages
##############################
# #
# EMULATED API ENDPOINTS #
# #
##############################
@router.get("/emulate", response_class=HTMLResponse)
@router.get("/emu", response_class=HTMLResponse)
def get_emulated_enpoints() -> HTMLResponse:
from clan_cli.config import ap_url, c1_url, c2_url, dlg_url
html_content = f"""
<html>
<head>
<title>Emulated API</title>
</head>
<body>
<h1>Emulated API</h1>
<p>Emulated API endpoints for testing purposes.</p>
<p>DLG: <a href="{dlg_url}" >{dlg_url} </a></p>
<p>AP: <a href="{ap_url}">{ap_url}</a></p>
<p>C1: <a href="{c1_url}">{c1_url} </a></p>
<p>C2: <a href="{c2_url}">{c2_url}</a></p>
</body>
</html>
"""
return HTMLResponse(content=html_content, status_code=200)

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")
did: str = Field(..., example="did:sov:test:120")
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]
#########################
# #
@@ -74,7 +93,7 @@ class ServiceBase(BaseModel):
class ServiceCreate(ServiceBase):
entity_did: str = Field(..., example="did:sov:test:1234")
entity_did: str = Field(..., example="did:sov:test:120")
class Service(ServiceCreate):
@@ -100,7 +119,7 @@ class ServicesByName(BaseModel):
class ResolutionBase(BaseModel):
requester_name: str = Field(..., example="C1")
requester_did: str = Field(..., example="did:sov:test:1122")
resolved_did: str = Field(..., example="did:sov:test:1234")
resolved_did: str = Field(..., example="did:sov:test:120")
other: dict = Field(..., example={"test": "test"})
@@ -110,7 +129,6 @@ class ResolutionCreate(ResolutionBase):
class Resolution(ResolutionCreate):
timestamp: datetime
id: int
class Config:
orm_mode = True
@@ -122,7 +140,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(
@@ -130,14 +147,15 @@ class EventmessageBase(BaseModel):
) # specific to one group needed to enable visually nested groups
msg_type: int = Field(..., example=1) # message type for the label
src_did: str = Field(..., example="did:sov:test:2234")
des_did: str = Field(..., example="did:sov:test:1234")
des_did: str = Field(..., example="did:sov:test:120")
class EventmessageCreate(EventmessageBase):
msg: dict = Field(..., example={"optinal": "values"}) # optional
pass
class Eventmessage(EventmessageCreate):
id: int = Field(...)
class Config:
orm_mode = True

View File

@@ -1,5 +1,6 @@
import argparse
import logging
import multiprocessing as mp
import shutil
import subprocess
import time
@@ -14,6 +15,8 @@ import uvicorn
from pydantic import AnyUrl, IPvAnyAddress
from pydantic.tools import parse_obj_as
import clan_cli.config as config
from clan_cli.emulate_fastapi import apps, get_health
from clan_cli.errors import ClanError
log = logging.getLogger(__name__)
@@ -126,31 +129,22 @@ def start_server(args: argparse.Namespace) -> None:
cmd = ["pytest", "-s", str(test_db_api)]
subprocess.run(cmd, check=True)
config.host = args.host
if args.emulate:
import multiprocessing as mp
from config import host, port_ap, port_client_base, port_dlg
from emulate_fastapi import app_ap, app_c1, app_c2, app_dlg, get_health
app_ports = [
(app_dlg, port_dlg),
(app_ap, port_ap),
(app_c1, port_client_base),
(app_c2, port_client_base + 1),
]
urls = list()
# start servers as processes (dlg, ap, c1 and c2 for tests)
for app, port in app_ports:
for app, port in apps:
proc = mp.Process(
target=uvicorn.run,
args=(app,),
kwargs={"host": host, "port": port, "log_level": "info"},
kwargs={
"host": args.host,
"port": port,
"log_level": args.log_level,
},
daemon=True,
)
proc.start()
urls.append(f"http://{host}:{port}")
# check server health
for url in urls:
url = f"http://{args.host}:{port}"
res = get_health(url=url + "/health")
if res is None:
raise Exception(f"Couldn't reach {url} after starting server")

View File

@@ -1,5 +1,6 @@
from typing import List, Optional
from sqlalchemy import func
from sqlalchemy.orm import Session
from sqlalchemy.sql.expression import true
@@ -65,8 +66,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 +97,34 @@ 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]:
# create a subquery to count the matching roles for each entity
subquery = (
db.query(
sql_models.EntityRoles.entity_did,
func.count(sql_models.EntityRoles.role).label("role_count"),
)
.filter(sql_models.EntityRoles.role.in_(roles))
.group_by(sql_models.EntityRoles.entity_did)
.subquery()
)
# join the subquery with the entity table and filter by the role count
return (
db.query(sql_models.Entity)
.join(subquery, sql_models.Entity.did == subquery.c.entity_did)
.filter(subquery.c.role_count == len(roles))
.all()
)
# get attached
@@ -107,12 +147,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 +159,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 +186,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

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

View File

@@ -1,4 +0,0 @@
host = "127.0.0.1"
port_dlg = 6000
port_ap = 6600
port_client_base = 7000

View File

@@ -1,3 +0,0 @@
#!/usr/bin/env bash
while true ; do printf 'HTTP/1.1 200 OK\r\n\r\ncool, thanks' | nc -l -N 127.0.0.1 7002; done

View File

@@ -1,205 +0,0 @@
import sys
import time
import urllib
from fastapi import FastAPI
from fastapi.responses import HTMLResponse
app_dlg = FastAPI()
app_ap = FastAPI()
app_c1 = FastAPI()
app_c2 = FastAPI()
# bash tests: curl localhost:6600/ap_list_of_services
# curl localhost:7001/consume_service_from_other_entity
#### HEALTH
@app_c1.get("/health")
async def healthcheck_c1() -> str:
return "200 OK"
@app_c2.get("/health")
async def healthcheck_c2() -> str:
return "200 OK"
@app_dlg.get("/health")
async def healthcheck_dlg() -> str:
return "200 OK"
@app_ap.get("/health")
async def healthcheck_ap() -> str:
return "200 OK"
def get_health(*, url: str, max_retries: int = 20, delay: float = 0.2) -> str | None:
for attempt in range(max_retries):
try:
with urllib.request.urlopen(url) as response:
return response.read()
except urllib.error.URLError as e:
print(f"Attempt {attempt + 1} failed: {e.reason}", file=sys.stderr)
time.sleep(delay)
return None
#### CONSUME SERVICE
# TODO send_msg???
@app_c1.get("/consume_service_from_other_entity", response_class=HTMLResponse)
async def consume_service_from_other_entity_c1() -> HTMLResponse:
html_content = """
<html>
<body>
<div style="width:480px"><iframe allow="fullscreen" frameBorder="0" height="270" src="https://giphy.com/embed/IOWD3uknMxYyh7CsgN/video" width="480"></iframe></div>
</body>
</html>
"""
time.sleep(3)
return HTMLResponse(content=html_content, status_code=200)
@app_c2.get("/consume_service_from_other_entity", response_class=HTMLResponse)
async def consume_service_from_other_entity_c2() -> HTMLResponse:
html_content = """
<html>
<body>
<div style="width:480px"><iframe allow="fullscreen" frameBorder="0" height="270" src="https://giphy.com/embed/IOWD3uknMxYyh7CsgN/video" width="480"></iframe></div>
</body>
</html>
"""
time.sleep(3)
return HTMLResponse(content=html_content, status_code=200)
#### ap_list_of_services
@app_ap.get("/ap_list_of_services", response_class=HTMLResponse)
async def ap_list_of_services() -> HTMLResponse:
html_content = b"""HTTP/1.1 200 OK\r\n\r\n[[
{
"uuid": "8e285c0c-4e40-430a-a477-26b3b81e30df",
"service_name": "Carlos Printing",
"service_type": "3D Printing",
"endpoint_url": "http://127.0.0.1:8000",
"status": "unknown",
"other": {
"action": [
"register",
"deregister",
"delete",
"create"
]
},
"entity_did": "did:sov:test:1234"
},
{
"uuid": "8e285c0c-4e40-430a-a477-26b3b81e30d1",
"service_name": "Luiss Fax",
"service_type": "Fax",
"endpoint_url": "http://127.0.0.1:8000",
"status": "unknown",
"other": {
"action": [
"register",
"deregister",
"delete",
"create"
]
},
"entity_did": "did:sov:test:1235"
},
{
"uuid": "8e285c0c-4e40-430a-a477-26b3b81e30d2",
"service_name": "Erdems VR-Stream",
"service_type": "VR-Stream",
"endpoint_url": "http://127.0.0.1:8000",
"status": "unknown",
"other": {
"action": [
"register",
"deregister",
"delete",
"create"
]
},
"entity_did": "did:sov:test:1236"
},
{
"uuid": "8e285c0c-4e40-430a-a477-26b3b81e30d3",
"service_name": "Onurs gallary",
"service_type": "gallary",
"endpoint_url": "http://127.0.0.1:8000",
"status": "unknown",
"other": {
"action": [
"register",
"deregister",
"delete",
"create"
]
},
"entity_did": "did:sov:test:1237"
},
{
"uuid": "8e285c0c-4e40-430a-a477-26b3b81e30d4",
"service_name": "Saras Game-Shop",
"service_type": "Game-Shop",
"endpoint_url": "http://127.0.0.1:8000",
"status": "unknown",
"other": {
"action": [
"register",
"deregister",
"delete",
"create"
]
},
"entity_did": "did:sov:test:1238"
}
]]"""
return HTMLResponse(content=html_content, status_code=200)
@app_dlg.get("/dlg_list_of_did_resolutions", response_class=HTMLResponse)
async def dlg_list_of_did_resolutions() -> HTMLResponse:
html_content = b"""HTTP/1.1 200 OK\r\n\r\n
[
{
"did": "did:sov:test:1234",
"name": "C1",
"ip": "127.0.0.1:5100",
"attached": false,
"visible": true,
"other": {
"network": "Carlo1's Home Network",
"roles": [
"service repository",
"service consumer"
]
}
},
{
"did": "did:sov:test:1235",
"name": "C2",
"ip": "127.0.0.1:5100",
"attached": false,
"visible": true,
"other": {
"network": "Carlo2's Home Network",
"roles": [
"service repository",
"service prosumer"
]
}
}
]"""
return HTMLResponse(content=html_content, status_code=200)

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

@@ -11,7 +11,7 @@ from fastapi.testclient import TestClient
from openapi_client import ApiClient, Configuration
from ports import PortFunction
import config
import clan_cli.config as config
from clan_cli.webui.app import app

View File

@@ -20,7 +20,6 @@ __version__ = "1.0.0"
from openapi_client.api.default_api import DefaultApi
from openapi_client.api.entities_api import EntitiesApi
from openapi_client.api.eventmessages_api import EventmessagesApi
from openapi_client.api.repositories_api import RepositoriesApi
from openapi_client.api.resolution_api import ResolutionApi
from openapi_client.api.services_api import ServicesApi
@@ -43,7 +42,7 @@ from openapi_client.models.eventmessage_create import EventmessageCreate
from openapi_client.models.http_validation_error import HTTPValidationError
from openapi_client.models.machine import Machine
from openapi_client.models.resolution import Resolution
from openapi_client.models.roles import Roles
from openapi_client.models.role import Role
from openapi_client.models.service import Service
from openapi_client.models.service_create import ServiceCreate
from openapi_client.models.status import Status

View File

@@ -4,7 +4,6 @@
from openapi_client.api.default_api import DefaultApi
from openapi_client.api.entities_api import EntitiesApi
from openapi_client.api.eventmessages_api import EventmessagesApi
from openapi_client.api.repositories_api import RepositoriesApi
from openapi_client.api.resolution_api import ResolutionApi
from openapi_client.api.services_api import ServicesApi

View File

@@ -18,12 +18,13 @@ import warnings
from pydantic import validate_arguments, ValidationError
from pydantic import StrictInt, StrictStr
from pydantic import StrictInt, StrictStr, conlist
from typing import Any, List, Optional, Dict
from openapi_client.models.entity import Entity
from openapi_client.models.entity_create import EntityCreate
from openapi_client.models.role import Role
from openapi_client.api_client import ApiClient
from openapi_client.api_response import ApiResponse
@@ -184,7 +185,7 @@ class EntitiesApi:
}
return self.api_client.call_api(
'/api/v1/attach', 'POST',
'/api/v1/attach', 'PUT',
_path_params,
_query_params,
_header_params,
@@ -624,7 +625,7 @@ class EntitiesApi:
}
return self.api_client.call_api(
'/api/v1/detach', 'POST',
'/api/v1/detach', 'PUT',
_path_params,
_query_params,
_header_params,
@@ -1074,17 +1075,17 @@ class EntitiesApi:
_request_auth=_params.get('_request_auth'))
@validate_arguments
def get_entity_by_name(self, entity_name : StrictStr, **kwargs) -> Entity: # noqa: E501
"""Get Entity By Name # noqa: E501
def get_entity_by_name_or_did(self, entity_name_or_did : Optional[StrictStr] = None, **kwargs) -> Entity: # noqa: E501
"""Get Entity By Name Or Did # noqa: E501
This method makes a synchronous HTTP request by default. To make an
asynchronous HTTP request, please pass async_req=True
>>> thread = api.get_entity_by_name(entity_name, async_req=True)
>>> thread = api.get_entity_by_name_or_did(entity_name_or_did, async_req=True)
>>> result = thread.get()
:param entity_name: (required)
:type entity_name: str
:param entity_name_or_did:
:type entity_name_or_did: str
:param async_req: Whether to execute the request asynchronously.
:type async_req: bool, optional
:param _request_timeout: timeout setting for this request.
@@ -1098,22 +1099,22 @@ class EntitiesApi:
"""
kwargs['_return_http_data_only'] = True
if '_preload_content' in kwargs:
message = "Error! Please call the get_entity_by_name_with_http_info method with `_preload_content` instead and obtain raw data from ApiResponse.raw_data" # noqa: E501
message = "Error! Please call the get_entity_by_name_or_did_with_http_info method with `_preload_content` instead and obtain raw data from ApiResponse.raw_data" # noqa: E501
raise ValueError(message)
return self.get_entity_by_name_with_http_info(entity_name, **kwargs) # noqa: E501
return self.get_entity_by_name_or_did_with_http_info(entity_name_or_did, **kwargs) # noqa: E501
@validate_arguments
def get_entity_by_name_with_http_info(self, entity_name : StrictStr, **kwargs) -> ApiResponse: # noqa: E501
"""Get Entity By Name # noqa: E501
def get_entity_by_name_or_did_with_http_info(self, entity_name_or_did : Optional[StrictStr] = None, **kwargs) -> ApiResponse: # noqa: E501
"""Get Entity By Name Or Did # noqa: E501
This method makes a synchronous HTTP request by default. To make an
asynchronous HTTP request, please pass async_req=True
>>> thread = api.get_entity_by_name_with_http_info(entity_name, async_req=True)
>>> thread = api.get_entity_by_name_or_did_with_http_info(entity_name_or_did, async_req=True)
>>> result = thread.get()
:param entity_name: (required)
:type entity_name: str
:param entity_name_or_did:
:type entity_name_or_did: str
:param async_req: Whether to execute the request asynchronously.
:type async_req: bool, optional
:param _preload_content: if False, the ApiResponse.data will
@@ -1142,7 +1143,7 @@ class EntitiesApi:
_params = locals()
_all_params = [
'entity_name'
'entity_name_or_did'
]
_all_params.extend(
[
@@ -1161,7 +1162,7 @@ class EntitiesApi:
if _key not in _all_params:
raise ApiTypeError(
"Got an unexpected keyword argument '%s'"
" to method get_entity_by_name" % _key
" to method get_entity_by_name_or_did" % _key
)
_params[_key] = _val
del _params['kwargs']
@@ -1173,8 +1174,8 @@ class EntitiesApi:
# process the query parameters
_query_params = []
if _params.get('entity_name') is not None: # noqa: E501
_query_params.append(('entity_name', _params['entity_name']))
if _params.get('entity_name_or_did') is not None: # noqa: E501
_query_params.append(('entity_name_or_did', _params['entity_name_or_did']))
# process the header parameters
_header_params = dict(_params.get('_headers', {}))
@@ -1196,7 +1197,147 @@ class EntitiesApi:
}
return self.api_client.call_api(
'/api/v1/entity_by_name', 'GET',
'/api/v1/entity_by_name_or_did', 'GET',
_path_params,
_query_params,
_header_params,
body=_body_params,
post_params=_form_params,
files=_files,
response_types_map=_response_types_map,
auth_settings=_auth_settings,
async_req=_params.get('async_req'),
_return_http_data_only=_params.get('_return_http_data_only'), # noqa: E501
_preload_content=_params.get('_preload_content', True),
_request_timeout=_params.get('_request_timeout'),
collection_formats=_collection_formats,
_request_auth=_params.get('_request_auth'))
@validate_arguments
def get_entity_by_roles(self, roles : conlist(Role), **kwargs) -> List[Entity]: # noqa: E501
"""Get Entity By Roles # noqa: E501
This method makes a synchronous HTTP request by default. To make an
asynchronous HTTP request, please pass async_req=True
>>> thread = api.get_entity_by_roles(roles, async_req=True)
>>> result = thread.get()
:param roles: (required)
:type roles: List[Role]
:param async_req: Whether to execute the request asynchronously.
:type async_req: bool, optional
:param _request_timeout: timeout setting for this request.
If one number provided, it will be total request
timeout. It can also be a pair (tuple) of
(connection, read) timeouts.
:return: Returns the result object.
If the method is called asynchronously,
returns the request thread.
:rtype: List[Entity]
"""
kwargs['_return_http_data_only'] = True
if '_preload_content' in kwargs:
message = "Error! Please call the get_entity_by_roles_with_http_info method with `_preload_content` instead and obtain raw data from ApiResponse.raw_data" # noqa: E501
raise ValueError(message)
return self.get_entity_by_roles_with_http_info(roles, **kwargs) # noqa: E501
@validate_arguments
def get_entity_by_roles_with_http_info(self, roles : conlist(Role), **kwargs) -> ApiResponse: # noqa: E501
"""Get Entity By Roles # noqa: E501
This method makes a synchronous HTTP request by default. To make an
asynchronous HTTP request, please pass async_req=True
>>> thread = api.get_entity_by_roles_with_http_info(roles, async_req=True)
>>> result = thread.get()
:param roles: (required)
:type roles: List[Role]
:param async_req: Whether to execute the request asynchronously.
:type async_req: bool, optional
:param _preload_content: if False, the ApiResponse.data will
be set to none and raw_data will store the
HTTP response body without reading/decoding.
Default is True.
:type _preload_content: bool, optional
:param _return_http_data_only: response data instead of ApiResponse
object with status code, headers, etc
:type _return_http_data_only: bool, optional
:param _request_timeout: timeout setting for this request. If one
number provided, it will be total request
timeout. It can also be a pair (tuple) of
(connection, read) timeouts.
:param _request_auth: set to override the auth_settings for an a single
request; this effectively ignores the authentication
in the spec for a single request.
:type _request_auth: dict, optional
:type _content_type: string, optional: force content-type for the request
:return: Returns the result object.
If the method is called asynchronously,
returns the request thread.
:rtype: tuple(List[Entity], status_code(int), headers(HTTPHeaderDict))
"""
_params = locals()
_all_params = [
'roles'
]
_all_params.extend(
[
'async_req',
'_return_http_data_only',
'_preload_content',
'_request_timeout',
'_request_auth',
'_content_type',
'_headers'
]
)
# validate the arguments
for _key, _val in _params['kwargs'].items():
if _key not in _all_params:
raise ApiTypeError(
"Got an unexpected keyword argument '%s'"
" to method get_entity_by_roles" % _key
)
_params[_key] = _val
del _params['kwargs']
_collection_formats = {}
# process the path parameters
_path_params = {}
# process the query parameters
_query_params = []
if _params.get('roles') is not None: # noqa: E501
_query_params.append(('roles', _params['roles']))
_collection_formats['roles'] = 'multi'
# process the header parameters
_header_params = dict(_params.get('_headers', {}))
# process the form parameters
_form_params = []
_files = {}
# process the body parameter
_body_params = None
# set the HTTP header `Accept`
_header_params['Accept'] = self.api_client.select_header_accept(
['application/json']) # noqa: E501
# authentication setting
_auth_settings = [] # noqa: E501
_response_types_map = {
'200': "List[Entity]",
'422': "HTTPValidationError",
}
return self.api_client.call_api(
'/api/v1/entity_by_roles', 'GET',
_path_params,
_query_params,
_header_params,

View File

@@ -175,7 +175,7 @@ class EventmessagesApi:
}
return self.api_client.call_api(
'/api/v1/send_msg', 'POST',
'/api/v1/event_message', 'POST',
_path_params,
_query_params,
_header_params,

View File

@@ -1,192 +0,0 @@
# coding: utf-8
"""
FastAPI
No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
The version of the OpenAPI document: 0.1.0
Generated by OpenAPI Generator (https://openapi-generator.tech)
Do not edit the class manually.
""" # noqa: E501
import re # noqa: F401
import io
import warnings
from pydantic import validate_arguments, ValidationError
from pydantic import StrictInt
from typing import List, Optional
from openapi_client.models.service import Service
from openapi_client.api_client import ApiClient
from openapi_client.api_response import ApiResponse
from openapi_client.exceptions import ( # noqa: F401
ApiTypeError,
ApiValueError
)
class RepositoriesApi:
"""NOTE: This class is auto generated by OpenAPI Generator
Ref: https://openapi-generator.tech
Do not edit the class manually.
"""
def __init__(self, api_client=None) -> None:
if api_client is None:
api_client = ApiClient.get_default()
self.api_client = api_client
@validate_arguments
def get_all_repositories(self, skip : Optional[StrictInt] = None, limit : Optional[StrictInt] = None, **kwargs) -> List[Service]: # noqa: E501
"""Get All Repositories # noqa: E501
This method makes a synchronous HTTP request by default. To make an
asynchronous HTTP request, please pass async_req=True
>>> thread = api.get_all_repositories(skip, limit, async_req=True)
>>> result = thread.get()
:param skip:
:type skip: int
:param limit:
:type limit: int
:param async_req: Whether to execute the request asynchronously.
:type async_req: bool, optional
:param _request_timeout: timeout setting for this request.
If one number provided, it will be total request
timeout. It can also be a pair (tuple) of
(connection, read) timeouts.
:return: Returns the result object.
If the method is called asynchronously,
returns the request thread.
:rtype: List[Service]
"""
kwargs['_return_http_data_only'] = True
if '_preload_content' in kwargs:
message = "Error! Please call the get_all_repositories_with_http_info method with `_preload_content` instead and obtain raw data from ApiResponse.raw_data" # noqa: E501
raise ValueError(message)
return self.get_all_repositories_with_http_info(skip, limit, **kwargs) # noqa: E501
@validate_arguments
def get_all_repositories_with_http_info(self, skip : Optional[StrictInt] = None, limit : Optional[StrictInt] = None, **kwargs) -> ApiResponse: # noqa: E501
"""Get All Repositories # noqa: E501
This method makes a synchronous HTTP request by default. To make an
asynchronous HTTP request, please pass async_req=True
>>> thread = api.get_all_repositories_with_http_info(skip, limit, async_req=True)
>>> result = thread.get()
:param skip:
:type skip: int
:param limit:
:type limit: int
:param async_req: Whether to execute the request asynchronously.
:type async_req: bool, optional
:param _preload_content: if False, the ApiResponse.data will
be set to none and raw_data will store the
HTTP response body without reading/decoding.
Default is True.
:type _preload_content: bool, optional
:param _return_http_data_only: response data instead of ApiResponse
object with status code, headers, etc
:type _return_http_data_only: bool, optional
:param _request_timeout: timeout setting for this request. If one
number provided, it will be total request
timeout. It can also be a pair (tuple) of
(connection, read) timeouts.
:param _request_auth: set to override the auth_settings for an a single
request; this effectively ignores the authentication
in the spec for a single request.
:type _request_auth: dict, optional
:type _content_type: string, optional: force content-type for the request
:return: Returns the result object.
If the method is called asynchronously,
returns the request thread.
:rtype: tuple(List[Service], status_code(int), headers(HTTPHeaderDict))
"""
_params = locals()
_all_params = [
'skip',
'limit'
]
_all_params.extend(
[
'async_req',
'_return_http_data_only',
'_preload_content',
'_request_timeout',
'_request_auth',
'_content_type',
'_headers'
]
)
# validate the arguments
for _key, _val in _params['kwargs'].items():
if _key not in _all_params:
raise ApiTypeError(
"Got an unexpected keyword argument '%s'"
" to method get_all_repositories" % _key
)
_params[_key] = _val
del _params['kwargs']
_collection_formats = {}
# process the path parameters
_path_params = {}
# process the query parameters
_query_params = []
if _params.get('skip') is not None: # noqa: E501
_query_params.append(('skip', _params['skip']))
if _params.get('limit') is not None: # noqa: E501
_query_params.append(('limit', _params['limit']))
# process the header parameters
_header_params = dict(_params.get('_headers', {}))
# process the form parameters
_form_params = []
_files = {}
# process the body parameter
_body_params = None
# set the HTTP header `Accept`
_header_params['Accept'] = self.api_client.select_header_accept(
['application/json']) # noqa: E501
# authentication setting
_auth_settings = [] # noqa: E501
_response_types_map = {
'200': "List[Service]",
'422': "HTTPValidationError",
}
return self.api_client.call_api(
'/api/v1/repositories', 'GET',
_path_params,
_query_params,
_header_params,
body=_body_params,
post_params=_form_params,
files=_files,
response_types_map=_response_types_map,
auth_settings=_auth_settings,
async_req=_params.get('async_req'),
_return_http_data_only=_params.get('_return_http_data_only'), # noqa: E501
_preload_content=_params.get('_preload_content', True),
_request_timeout=_params.get('_request_timeout'),
collection_formats=_collection_formats,
_request_auth=_params.get('_request_auth'))

View File

@@ -2,17 +2,18 @@
All URIs are relative to _http://localhost_
| Method | HTTP request | Description |
| ----------------------------------------------------------------- | --------------------------------- | --------------------- |
| [**attach_entity**](EntitiesApi.md#attach_entity) | **POST** /api/v1/attach | Attach Entity |
| [**create_entity**](EntitiesApi.md#create_entity) | **POST** /api/v1/entity | Create Entity |
| [**delete_entity**](EntitiesApi.md#delete_entity) | **DELETE** /api/v1/entity | Delete Entity |
| [**detach_entity**](EntitiesApi.md#detach_entity) | **POST** /api/v1/detach | Detach Entity |
| [**get_all_entities**](EntitiesApi.md#get_all_entities) | **GET** /api/v1/entities | Get All Entities |
| [**get_attached_entities**](EntitiesApi.md#get_attached_entities) | **GET** /api/v1/attached_entities | Get Attached Entities |
| [**get_entity_by_did**](EntitiesApi.md#get_entity_by_did) | **GET** /api/v1/entity | Get Entity By Did |
| [**get_entity_by_name**](EntitiesApi.md#get_entity_by_name) | **GET** /api/v1/entity_by_name | Get Entity By Name |
| [**is_attached**](EntitiesApi.md#is_attached) | **GET** /api/v1/is_attached | Is Attached |
| Method | HTTP request | Description |
| ------------------------------------------------------------------------- | ------------------------------------- | ------------------------- |
| [**attach_entity**](EntitiesApi.md#attach_entity) | **PUT** /api/v1/attach | Attach Entity |
| [**create_entity**](EntitiesApi.md#create_entity) | **POST** /api/v1/entity | Create Entity |
| [**delete_entity**](EntitiesApi.md#delete_entity) | **DELETE** /api/v1/entity | Delete Entity |
| [**detach_entity**](EntitiesApi.md#detach_entity) | **PUT** /api/v1/detach | Detach Entity |
| [**get_all_entities**](EntitiesApi.md#get_all_entities) | **GET** /api/v1/entities | Get All Entities |
| [**get_attached_entities**](EntitiesApi.md#get_attached_entities) | **GET** /api/v1/attached_entities | Get Attached Entities |
| [**get_entity_by_did**](EntitiesApi.md#get_entity_by_did) | **GET** /api/v1/entity | Get Entity By Did |
| [**get_entity_by_name_or_did**](EntitiesApi.md#get_entity_by_name_or_did) | **GET** /api/v1/entity_by_name_or_did | Get Entity By Name Or Did |
| [**get_entity_by_roles**](EntitiesApi.md#get_entity_by_roles) | **GET** /api/v1/entity_by_roles | Get Entity By Roles |
| [**is_attached**](EntitiesApi.md#is_attached) | **GET** /api/v1/is_attached | Is Attached |
# **attach_entity**
@@ -486,11 +487,11 @@ No authorization required
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
# **get_entity_by_name**
# **get_entity_by_name_or_did**
> Entity get_entity_by_name(entity_name)
> Entity get_entity_by_name_or_did(entity_name_or_did=entity_name_or_did)
Get Entity By Name
Get Entity By Name Or Did
### Example
@@ -513,22 +514,22 @@ configuration = openapi_client.Configuration(
with openapi_client.ApiClient(configuration) as api_client:
# Create an instance of the API class
api_instance = openapi_client.EntitiesApi(api_client)
entity_name = 'entity_name_example' # str |
entity_name_or_did = 'C1' # str | (optional) (default to 'C1')
try:
# Get Entity By Name
api_response = api_instance.get_entity_by_name(entity_name)
print("The response of EntitiesApi->get_entity_by_name:\n")
# Get Entity By Name Or Did
api_response = api_instance.get_entity_by_name_or_did(entity_name_or_did=entity_name_or_did)
print("The response of EntitiesApi->get_entity_by_name_or_did:\n")
pprint(api_response)
except Exception as e:
print("Exception when calling EntitiesApi->get_entity_by_name: %s\n" % e)
print("Exception when calling EntitiesApi->get_entity_by_name_or_did: %s\n" % e)
```
### Parameters
| Name | Type | Description | Notes |
| --------------- | ------- | ----------- | ----- |
| **entity_name** | **str** | |
| Name | Type | Description | Notes |
| ---------------------- | ------- | ----------- | ------------------------------------ |
| **entity_name_or_did** | **str** | | [optional] [default to &#39;C1&#39;] |
### Return type
@@ -552,6 +553,73 @@ No authorization required
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
# **get_entity_by_roles**
> List[Entity] get_entity_by_roles(roles)
Get Entity By Roles
### Example
```python
import time
import os
import openapi_client
from openapi_client.models.entity import Entity
from openapi_client.models.role import Role
from openapi_client.rest import ApiException
from pprint import pprint
# Defining the host is optional and defaults to http://localhost
# See configuration.py for a list of all supported configuration parameters.
configuration = openapi_client.Configuration(
host = "http://localhost"
)
# Enter a context with an instance of the API client
with openapi_client.ApiClient(configuration) as api_client:
# Create an instance of the API class
api_instance = openapi_client.EntitiesApi(api_client)
roles = [openapi_client.Role()] # List[Role] |
try:
# Get Entity By Roles
api_response = api_instance.get_entity_by_roles(roles)
print("The response of EntitiesApi->get_entity_by_roles:\n")
pprint(api_response)
except Exception as e:
print("Exception when calling EntitiesApi->get_entity_by_roles: %s\n" % e)
```
### Parameters
| Name | Type | Description | Notes |
| --------- | ------------------------- | ----------- | ----- |
| **roles** | [**List[Role]**](Role.md) | |
### Return type
[**List[Entity]**](Entity.md)
### Authorization
No authorization required
### HTTP request headers
- **Content-Type**: Not defined
- **Accept**: application/json
### HTTP response details
| Status code | Description | Response headers |
| ----------- | ------------------- | ---------------- |
| **200** | Successful Response | - |
| **422** | Validation Error | - |
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
# **is_attached**
> Dict[str, str] is_attached(entity_did=entity_did)

View File

@@ -2,17 +2,17 @@
## Properties
| Name | Type | Description | Notes |
| -------------------- | --------------------- | ----------- | ----- |
| **did** | **str** | |
| **name** | **str** | |
| **ip** | **str** | |
| **network** | **str** | |
| **role** | [**Roles**](Roles.md) | |
| **visible** | **bool** | |
| **other** | **object** | |
| **attached** | **bool** | |
| **stop_health_task** | **bool** | |
| Name | Type | Description | Notes |
| -------------------- | ------------------------- | ----------- | ----- |
| **did** | **str** | |
| **name** | **str** | |
| **ip** | **str** | |
| **network** | **str** | |
| **visible** | **bool** | |
| **other** | **object** | |
| **attached** | **bool** | |
| **stop_health_task** | **bool** | |
| **roles** | [**List[Role]**](Role.md) | |
## Example

View File

@@ -2,15 +2,15 @@
## Properties
| Name | Type | Description | Notes |
| ----------- | --------------------- | ----------- | ----- |
| **did** | **str** | |
| **name** | **str** | |
| **ip** | **str** | |
| **network** | **str** | |
| **role** | [**Roles**](Roles.md) | |
| **visible** | **bool** | |
| **other** | **object** | |
| Name | Type | Description | Notes |
| ----------- | ------------------------- | ----------- | ----- |
| **did** | **str** | |
| **name** | **str** | |
| **ip** | **str** | |
| **network** | **str** | |
| **visible** | **bool** | |
| **other** | **object** | |
| **roles** | [**List[Role]**](Role.md) | |
## Example

View File

@@ -4,7 +4,6 @@
| Name | Type | Description | Notes |
| ------------- | ---------- | ----------- | ----- |
| **id** | **int** | |
| **timestamp** | **int** | |
| **group** | **int** | |
| **group_id** | **int** | |
@@ -12,6 +11,7 @@
| **src_did** | **str** | |
| **des_did** | **str** | |
| **msg** | **object** | |
| **id** | **int** | |
## Example

View File

@@ -4,7 +4,6 @@
| Name | Type | Description | Notes |
| ------------- | ---------- | ----------- | ----- |
| **id** | **int** | |
| **timestamp** | **int** | |
| **group** | **int** | |
| **group_id** | **int** | |

View File

@@ -4,7 +4,7 @@ All URIs are relative to _http://localhost_
| Method | HTTP request | Description |
| ---------------------------------------------------------------------- | ------------------------------ | --------------------- |
| [**create_eventmessage**](EventmessagesApi.md#create_eventmessage) | **POST** /api/v1/send_msg | Create Eventmessage |
| [**create_eventmessage**](EventmessagesApi.md#create_eventmessage) | **POST** /api/v1/event_message | Create Eventmessage |
| [**get_all_eventmessages**](EventmessagesApi.md#get_all_eventmessages) | **GET** /api/v1/event_messages | Get All Eventmessages |
# **create_eventmessage**

View File

@@ -1,75 +0,0 @@
# openapi_client.RepositoriesApi
All URIs are relative to _http://localhost_
| Method | HTTP request | Description |
| ------------------------------------------------------------------- | ---------------------------- | -------------------- |
| [**get_all_repositories**](RepositoriesApi.md#get_all_repositories) | **GET** /api/v1/repositories | Get All Repositories |
# **get_all_repositories**
> List[Service] get_all_repositories(skip=skip, limit=limit)
Get All Repositories
### Example
```python
import time
import os
import openapi_client
from openapi_client.models.service import Service
from openapi_client.rest import ApiException
from pprint import pprint
# Defining the host is optional and defaults to http://localhost
# See configuration.py for a list of all supported configuration parameters.
configuration = openapi_client.Configuration(
host = "http://localhost"
)
# Enter a context with an instance of the API client
with openapi_client.ApiClient(configuration) as api_client:
# Create an instance of the API class
api_instance = openapi_client.RepositoriesApi(api_client)
skip = 0 # int | (optional) (default to 0)
limit = 100 # int | (optional) (default to 100)
try:
# Get All Repositories
api_response = api_instance.get_all_repositories(skip=skip, limit=limit)
print("The response of RepositoriesApi->get_all_repositories:\n")
pprint(api_response)
except Exception as e:
print("Exception when calling RepositoriesApi->get_all_repositories: %s\n" % e)
```
### Parameters
| Name | Type | Description | Notes |
| --------- | ------- | ----------- | --------------------------- |
| **skip** | **int** | | [optional] [default to 0] |
| **limit** | **int** | | [optional] [default to 100] |
### Return type
[**List[Service]**](Service.md)
### Authorization
No authorization required
### HTTP request headers
- **Content-Type**: Not defined
- **Accept**: application/json
### HTTP response details
| Status code | Description | Response headers |
| ----------- | ------------------- | ---------------- |
| **200** | Successful Response | - |
| **422** | Validation Error | - |
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)

View File

@@ -21,7 +21,7 @@ from openapi_client.models.eventmessage_create import EventmessageCreate
from openapi_client.models.http_validation_error import HTTPValidationError
from openapi_client.models.machine import Machine
from openapi_client.models.resolution import Resolution
from openapi_client.models.roles import Roles
from openapi_client.models.role import Role
from openapi_client.models.service import Service
from openapi_client.models.service_create import ServiceCreate
from openapi_client.models.status import Status

View File

@@ -18,9 +18,9 @@ import re # noqa: F401
import json
from typing import Any, Dict
from pydantic import BaseModel, Field, StrictBool, StrictStr
from openapi_client.models.roles import Roles
from typing import Any, Dict, List
from pydantic import BaseModel, Field, StrictBool, StrictStr, conlist
from openapi_client.models.role import Role
class Entity(BaseModel):
"""
@@ -30,12 +30,12 @@ class Entity(BaseModel):
name: StrictStr = Field(...)
ip: StrictStr = Field(...)
network: StrictStr = Field(...)
role: Roles = Field(...)
visible: StrictBool = Field(...)
other: Dict[str, Any] = Field(...)
attached: StrictBool = Field(...)
stop_health_task: StrictBool = Field(...)
__properties = ["did", "name", "ip", "network", "role", "visible", "other", "attached", "stop_health_task"]
roles: conlist(Role) = Field(...)
__properties = ["did", "name", "ip", "network", "visible", "other", "attached", "stop_health_task", "roles"]
class Config:
"""Pydantic configuration"""
@@ -77,11 +77,11 @@ class Entity(BaseModel):
"name": obj.get("name"),
"ip": obj.get("ip"),
"network": obj.get("network"),
"role": obj.get("role"),
"visible": obj.get("visible"),
"other": obj.get("other"),
"attached": obj.get("attached"),
"stop_health_task": obj.get("stop_health_task")
"stop_health_task": obj.get("stop_health_task"),
"roles": obj.get("roles")
})
return _obj

View File

@@ -18,9 +18,9 @@ import re # noqa: F401
import json
from typing import Any, Dict
from pydantic import BaseModel, Field, StrictBool, StrictStr
from openapi_client.models.roles import Roles
from typing import Any, Dict, List
from pydantic import BaseModel, Field, StrictBool, StrictStr, conlist
from openapi_client.models.role import Role
class EntityCreate(BaseModel):
"""
@@ -30,10 +30,10 @@ class EntityCreate(BaseModel):
name: StrictStr = Field(...)
ip: StrictStr = Field(...)
network: StrictStr = Field(...)
role: Roles = Field(...)
visible: StrictBool = Field(...)
other: Dict[str, Any] = Field(...)
__properties = ["did", "name", "ip", "network", "role", "visible", "other"]
roles: conlist(Role) = Field(...)
__properties = ["did", "name", "ip", "network", "visible", "other", "roles"]
class Config:
"""Pydantic configuration"""
@@ -75,9 +75,9 @@ class EntityCreate(BaseModel):
"name": obj.get("name"),
"ip": obj.get("ip"),
"network": obj.get("network"),
"role": obj.get("role"),
"visible": obj.get("visible"),
"other": obj.get("other")
"other": obj.get("other"),
"roles": obj.get("roles")
})
return _obj

View File

@@ -25,7 +25,6 @@ class Eventmessage(BaseModel):
"""
Eventmessage
"""
id: StrictInt = Field(...)
timestamp: StrictInt = Field(...)
group: StrictInt = Field(...)
group_id: StrictInt = Field(...)
@@ -33,7 +32,8 @@ class Eventmessage(BaseModel):
src_did: StrictStr = Field(...)
des_did: StrictStr = Field(...)
msg: Dict[str, Any] = Field(...)
__properties = ["id", "timestamp", "group", "group_id", "msg_type", "src_did", "des_did", "msg"]
id: StrictInt = Field(...)
__properties = ["timestamp", "group", "group_id", "msg_type", "src_did", "des_did", "msg", "id"]
class Config:
"""Pydantic configuration"""
@@ -71,14 +71,14 @@ class Eventmessage(BaseModel):
return Eventmessage.parse_obj(obj)
_obj = Eventmessage.parse_obj({
"id": obj.get("id"),
"timestamp": obj.get("timestamp"),
"group": obj.get("group"),
"group_id": obj.get("group_id"),
"msg_type": obj.get("msg_type"),
"src_did": obj.get("src_did"),
"des_did": obj.get("des_did"),
"msg": obj.get("msg")
"msg": obj.get("msg"),
"id": obj.get("id")
})
return _obj

View File

@@ -25,7 +25,6 @@ class EventmessageCreate(BaseModel):
"""
EventmessageCreate
"""
id: StrictInt = Field(...)
timestamp: StrictInt = Field(...)
group: StrictInt = Field(...)
group_id: StrictInt = Field(...)
@@ -33,7 +32,7 @@ class EventmessageCreate(BaseModel):
src_did: StrictStr = Field(...)
des_did: StrictStr = Field(...)
msg: Dict[str, Any] = Field(...)
__properties = ["id", "timestamp", "group", "group_id", "msg_type", "src_did", "des_did", "msg"]
__properties = ["timestamp", "group", "group_id", "msg_type", "src_did", "des_did", "msg"]
class Config:
"""Pydantic configuration"""
@@ -71,7 +70,6 @@ class EventmessageCreate(BaseModel):
return EventmessageCreate.parse_obj(obj)
_obj = EventmessageCreate.parse_obj({
"id": obj.get("id"),
"timestamp": obj.get("timestamp"),
"group": obj.get("group"),
"group_id": obj.get("group_id"),

View File

@@ -21,7 +21,7 @@ from aenum import Enum, no_arg
class Roles(str, Enum):
class Role(str, Enum):
"""
An enumeration.
"""
@@ -34,8 +34,8 @@ class Roles(str, Enum):
DLG = 'DLG'
@classmethod
def from_json(cls, json_str: str) -> Roles:
"""Create an instance of Roles from a JSON string"""
return Roles(json.loads(json_str))
def from_json(cls, json_str: str) -> Role:
"""Create an instance of Role from a JSON string"""
return Role(json.loads(json_str))

View File

@@ -13,12 +13,12 @@ from openapi_client.models import (
Eventmessage,
EventmessageCreate,
Machine,
Roles,
Role,
ServiceCreate,
Status,
)
import config
import clan_cli.config as config
random.seed(42)
@@ -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(
@@ -46,7 +46,7 @@ def create_entities(num: int = 10) -> list[EntityCreate]:
name=f"C{i}",
ip=f"{host}:{port_client_base+i}",
network="255.255.0.0",
role=Roles("service_prosumer"),
roles=[Role("service_prosumer")],
visible=True,
other={},
)
@@ -54,9 +54,9 @@ def create_entities(num: int = 10) -> list[EntityCreate]:
dlg = EntityCreate(
did=f"did:sov:test:{port_dlg}",
name="DLG",
ip=f"{host}:{port_dlg}/health",
ip=f"{host}:{port_dlg}",
network="255.255.0.0",
role=Roles("DLG"),
roles=[Role("DLG")],
visible=True,
other={},
)
@@ -64,9 +64,9 @@ def create_entities(num: int = 10) -> list[EntityCreate]:
ap = EntityCreate(
did=f"did:sov:test:{port_ap}",
name="AP",
ip=f"{host}:{port_ap}/health",
ip=f"{host}:{port_ap}",
network="255.255.0.0",
role=Roles("AP"),
roles=[Role("AP")],
visible=True,
other={},
)
@@ -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
@@ -115,7 +116,6 @@ def create_eventmessages(num: int = 2) -> list[EventmessageCreate]:
for i in range(num):
group_id = i % 5 + random.getrandbits(6)
em_req_send = EventmessageCreate(
id=random.getrandbits(18),
timestamp=starttime + i * 10,
group=i % 5,
group_id=group_id,
@@ -126,7 +126,6 @@ def create_eventmessages(num: int = 2) -> list[EventmessageCreate]:
)
res.append(em_req_send)
em_req_rec = EventmessageCreate(
id=random.getrandbits(18),
timestamp=starttime + (i * 10) + 2,
group=i % 5,
group_id=group_id,
@@ -138,7 +137,6 @@ def create_eventmessages(num: int = 2) -> list[EventmessageCreate]:
res.append(em_req_rec)
group_id = i % 5 + random.getrandbits(6)
em_res_send = EventmessageCreate(
id=random.getrandbits(18),
timestamp=starttime + i * 10 + 4,
group=i % 5,
group_id=group_id,
@@ -149,7 +147,6 @@ def create_eventmessages(num: int = 2) -> list[EventmessageCreate]:
)
res.append(em_res_send)
em_res_rec = EventmessageCreate(
id=random.getrandbits(6),
timestamp=starttime + (i * 10) + 8,
group=i % 5,
group_id=group_id,
@@ -165,8 +162,10 @@ def create_eventmessages(num: int = 2) -> list[EventmessageCreate]:
def test_create_eventmessages(api_client: ApiClient) -> None:
api = EventmessagesApi(api_client=api_client)
assert [] == api.get_all_eventmessages()
for own_eventmsg in create_eventmessages():
for idx, own_eventmsg in enumerate(create_eventmessages()):
res: Eventmessage = api.create_eventmessage(own_eventmsg)
# breakpoint()
assert res.id == own_eventmsg.id
assert res.msg == own_eventmsg.msg
assert res.src_did == own_eventmsg.src_did
assert res.des_did == own_eventmsg.des_did
assert [] != api.get_all_eventmessages()