From 334bbd3761bcc6cfebefa109a2698a1cf502e54d Mon Sep 17 00:00:00 2001 From: Luis-Hebendanz Date: Sat, 13 Jan 2024 16:10:21 +0100 Subject: [PATCH 1/7] backend: Fully working roles field. Added get_entity_by_roles --- .../clan_cli/webui/routers/endpoints.py | 54 +++++++++---------- pkgs/clan-cli/clan_cli/webui/schemas.py | 39 ++++++++++---- pkgs/clan-cli/clan_cli/webui/sql_crud.py | 49 ++++++++++++++--- pkgs/clan-cli/clan_cli/webui/sql_models.py | 19 +++++-- pkgs/clan-cli/clan_cli/webui/tags.py | 4 -- pkgs/clan-cli/pyproject.toml | 2 +- pkgs/clan-cli/tests/test_db_api.py | 3 +- 7 files changed, 114 insertions(+), 56 deletions(-) diff --git a/pkgs/clan-cli/clan_cli/webui/routers/endpoints.py b/pkgs/clan-cli/clan_cli/webui/routers/endpoints.py index 6a16fda..e8d90fe 100644 --- a/pkgs/clan-cli/clan_cli/webui/routers/endpoints.py +++ b/pkgs/clan-cli/clan_cli/webui/routers/endpoints.py @@ -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: diff --git a/pkgs/clan-cli/clan_cli/webui/schemas.py b/pkgs/clan-cli/clan_cli/webui/schemas.py index ea2e3e3..169a560 100644 --- a/pkgs/clan-cli/clan_cli/webui/schemas.py +++ b/pkgs/clan-cli/clan_cli/webui/schemas.py @@ -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 diff --git a/pkgs/clan-cli/clan_cli/webui/sql_crud.py b/pkgs/clan-cli/clan_cli/webui/sql_crud.py index 64b968c..d8186e0 100644 --- a/pkgs/clan-cli/clan_cli/webui/sql_crud.py +++ b/pkgs/clan-cli/clan_cli/webui/sql_crud.py @@ -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) diff --git a/pkgs/clan-cli/clan_cli/webui/sql_models.py b/pkgs/clan-cli/clan_cli/webui/sql_models.py index d7c4f2a..d83918d 100644 --- a/pkgs/clan-cli/clan_cli/webui/sql_models.py +++ b/pkgs/clan-cli/clan_cli/webui/sql_models.py @@ -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) diff --git a/pkgs/clan-cli/clan_cli/webui/tags.py b/pkgs/clan-cli/clan_cli/webui/tags.py index 3d52367..619b1f0 100644 --- a/pkgs/clan-cli/clan_cli/webui/tags.py +++ b/pkgs/clan-cli/clan_cli/webui/tags.py @@ -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.", diff --git a/pkgs/clan-cli/pyproject.toml b/pkgs/clan-cli/pyproject.toml index adbf1ca..547a4e3 100644 --- a/pkgs/clan-cli/pyproject.toml +++ b/pkgs/clan-cli/pyproject.toml @@ -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] diff --git a/pkgs/clan-cli/tests/test_db_api.py b/pkgs/clan-cli/tests/test_db_api.py index 5cf4324..3238d40 100644 --- a/pkgs/clan-cli/tests/test_db_api.py +++ b/pkgs/clan-cli/tests/test_db_api.py @@ -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 From e7ec0a593c4a989588d2a65a2b76574dd67d952e Mon Sep 17 00:00:00 2001 From: Luis-Hebendanz Date: Sat, 13 Jan 2024 16:16:27 +0100 Subject: [PATCH 2/7] Added exclusive get_entity_by_role --- pkgs/clan-cli/clan_cli/webui/sql_crud.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/pkgs/clan-cli/clan_cli/webui/sql_crud.py b/pkgs/clan-cli/clan_cli/webui/sql_crud.py index d8186e0..81c81de 100644 --- a/pkgs/clan-cli/clan_cli/webui/sql_crud.py +++ b/pkgs/clan-cli/clan_cli/webui/sql_crud.py @@ -1,5 +1,6 @@ from typing import List, Optional +from sqlalchemy import func from sqlalchemy.orm import Session from sqlalchemy.sql.expression import true @@ -107,9 +108,21 @@ def get_entity_by_name_or_did(db: Session, name: str) -> Optional[sql_models.Ent 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) - .filter(sql_models.Entity.roles.any(sql_models.EntityRoles.role.in_(roles))) + .join(subquery, sql_models.Entity.did == subquery.c.entity_did) + .filter(subquery.c.role_count == len(roles)) .all() ) From a51e94fef34aee250b5e0b6a21c2cefd682beef2 Mon Sep 17 00:00:00 2001 From: Luis-Hebendanz Date: Sat, 13 Jan 2024 19:02:55 +0100 Subject: [PATCH 3/7] Fixed up test_db_api --- pkgs/clan-cli/emulate_entity.sh | 3 - .../clan-cli/tests/openapi_client/__init__.py | 3 +- .../tests/openapi_client/api/__init__.py | 1 - .../tests/openapi_client/api/entities_api.py | 181 +++++++++++++++-- .../openapi_client/api/eventmessages_api.py | 2 +- .../openapi_client/api/repositories_api.py | 192 ------------------ .../tests/openapi_client/docs/EntitiesApi.md | 112 ++++++++-- .../tests/openapi_client/docs/Entity.md | 22 +- .../tests/openapi_client/docs/EntityCreate.md | 18 +- .../tests/openapi_client/docs/Eventmessage.md | 2 +- .../openapi_client/docs/EventmessageCreate.md | 1 - .../openapi_client/docs/EventmessagesApi.md | 2 +- .../openapi_client/docs/RepositoriesApi.md | 75 ------- .../openapi_client/docs/{Roles.md => Role.md} | 2 +- .../tests/openapi_client/models/__init__.py | 2 +- .../tests/openapi_client/models/entity.py | 14 +- .../openapi_client/models/entity_create.py | 14 +- .../openapi_client/models/eventmessage.py | 8 +- .../models/eventmessage_create.py | 4 +- .../models/{roles.py => role.py} | 8 +- pkgs/clan-cli/tests/test_db_api.py | 20 +- 21 files changed, 309 insertions(+), 377 deletions(-) delete mode 100755 pkgs/clan-cli/emulate_entity.sh delete mode 100644 pkgs/clan-cli/tests/openapi_client/api/repositories_api.py delete mode 100644 pkgs/clan-cli/tests/openapi_client/docs/RepositoriesApi.md rename pkgs/clan-cli/tests/openapi_client/docs/{Roles.md => Role.md} (97%) rename pkgs/clan-cli/tests/openapi_client/models/{roles.py => role.py} (76%) diff --git a/pkgs/clan-cli/emulate_entity.sh b/pkgs/clan-cli/emulate_entity.sh deleted file mode 100755 index 047880a..0000000 --- a/pkgs/clan-cli/emulate_entity.sh +++ /dev/null @@ -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 diff --git a/pkgs/clan-cli/tests/openapi_client/__init__.py b/pkgs/clan-cli/tests/openapi_client/__init__.py index b3656f9..bf8d04e 100644 --- a/pkgs/clan-cli/tests/openapi_client/__init__.py +++ b/pkgs/clan-cli/tests/openapi_client/__init__.py @@ -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 diff --git a/pkgs/clan-cli/tests/openapi_client/api/__init__.py b/pkgs/clan-cli/tests/openapi_client/api/__init__.py index 87b34f9..88a80e5 100644 --- a/pkgs/clan-cli/tests/openapi_client/api/__init__.py +++ b/pkgs/clan-cli/tests/openapi_client/api/__init__.py @@ -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 diff --git a/pkgs/clan-cli/tests/openapi_client/api/entities_api.py b/pkgs/clan-cli/tests/openapi_client/api/entities_api.py index 82431a5..041bafe 100644 --- a/pkgs/clan-cli/tests/openapi_client/api/entities_api.py +++ b/pkgs/clan-cli/tests/openapi_client/api/entities_api.py @@ -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, diff --git a/pkgs/clan-cli/tests/openapi_client/api/eventmessages_api.py b/pkgs/clan-cli/tests/openapi_client/api/eventmessages_api.py index 092ef36..b509802 100644 --- a/pkgs/clan-cli/tests/openapi_client/api/eventmessages_api.py +++ b/pkgs/clan-cli/tests/openapi_client/api/eventmessages_api.py @@ -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, diff --git a/pkgs/clan-cli/tests/openapi_client/api/repositories_api.py b/pkgs/clan-cli/tests/openapi_client/api/repositories_api.py deleted file mode 100644 index bedb1e3..0000000 --- a/pkgs/clan-cli/tests/openapi_client/api/repositories_api.py +++ /dev/null @@ -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')) diff --git a/pkgs/clan-cli/tests/openapi_client/docs/EntitiesApi.md b/pkgs/clan-cli/tests/openapi_client/docs/EntitiesApi.md index dc621f8..4cc9b38 100644 --- a/pkgs/clan-cli/tests/openapi_client/docs/EntitiesApi.md +++ b/pkgs/clan-cli/tests/openapi_client/docs/EntitiesApi.md @@ -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 'C1'] | ### 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) diff --git a/pkgs/clan-cli/tests/openapi_client/docs/Entity.md b/pkgs/clan-cli/tests/openapi_client/docs/Entity.md index 807f6a6..83b3b80 100644 --- a/pkgs/clan-cli/tests/openapi_client/docs/Entity.md +++ b/pkgs/clan-cli/tests/openapi_client/docs/Entity.md @@ -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 diff --git a/pkgs/clan-cli/tests/openapi_client/docs/EntityCreate.md b/pkgs/clan-cli/tests/openapi_client/docs/EntityCreate.md index ea30366..2b06e83 100644 --- a/pkgs/clan-cli/tests/openapi_client/docs/EntityCreate.md +++ b/pkgs/clan-cli/tests/openapi_client/docs/EntityCreate.md @@ -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 diff --git a/pkgs/clan-cli/tests/openapi_client/docs/Eventmessage.md b/pkgs/clan-cli/tests/openapi_client/docs/Eventmessage.md index cbe65cb..96427cd 100644 --- a/pkgs/clan-cli/tests/openapi_client/docs/Eventmessage.md +++ b/pkgs/clan-cli/tests/openapi_client/docs/Eventmessage.md @@ -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 diff --git a/pkgs/clan-cli/tests/openapi_client/docs/EventmessageCreate.md b/pkgs/clan-cli/tests/openapi_client/docs/EventmessageCreate.md index eb6f16d..4ed2b69 100644 --- a/pkgs/clan-cli/tests/openapi_client/docs/EventmessageCreate.md +++ b/pkgs/clan-cli/tests/openapi_client/docs/EventmessageCreate.md @@ -4,7 +4,6 @@ | Name | Type | Description | Notes | | ------------- | ---------- | ----------- | ----- | -| **id** | **int** | | | **timestamp** | **int** | | | **group** | **int** | | | **group_id** | **int** | | diff --git a/pkgs/clan-cli/tests/openapi_client/docs/EventmessagesApi.md b/pkgs/clan-cli/tests/openapi_client/docs/EventmessagesApi.md index d6affb9..c8f26b1 100644 --- a/pkgs/clan-cli/tests/openapi_client/docs/EventmessagesApi.md +++ b/pkgs/clan-cli/tests/openapi_client/docs/EventmessagesApi.md @@ -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** diff --git a/pkgs/clan-cli/tests/openapi_client/docs/RepositoriesApi.md b/pkgs/clan-cli/tests/openapi_client/docs/RepositoriesApi.md deleted file mode 100644 index ff735f0..0000000 --- a/pkgs/clan-cli/tests/openapi_client/docs/RepositoriesApi.md +++ /dev/null @@ -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) diff --git a/pkgs/clan-cli/tests/openapi_client/docs/Roles.md b/pkgs/clan-cli/tests/openapi_client/docs/Role.md similarity index 97% rename from pkgs/clan-cli/tests/openapi_client/docs/Roles.md rename to pkgs/clan-cli/tests/openapi_client/docs/Role.md index 994fa4c..a60cc52 100644 --- a/pkgs/clan-cli/tests/openapi_client/docs/Roles.md +++ b/pkgs/clan-cli/tests/openapi_client/docs/Role.md @@ -1,4 +1,4 @@ -# Roles +# Role An enumeration. diff --git a/pkgs/clan-cli/tests/openapi_client/models/__init__.py b/pkgs/clan-cli/tests/openapi_client/models/__init__.py index f28cdd8..b211606 100644 --- a/pkgs/clan-cli/tests/openapi_client/models/__init__.py +++ b/pkgs/clan-cli/tests/openapi_client/models/__init__.py @@ -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 diff --git a/pkgs/clan-cli/tests/openapi_client/models/entity.py b/pkgs/clan-cli/tests/openapi_client/models/entity.py index a78e778..b60806c 100644 --- a/pkgs/clan-cli/tests/openapi_client/models/entity.py +++ b/pkgs/clan-cli/tests/openapi_client/models/entity.py @@ -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 diff --git a/pkgs/clan-cli/tests/openapi_client/models/entity_create.py b/pkgs/clan-cli/tests/openapi_client/models/entity_create.py index d7385e1..1775ccb 100644 --- a/pkgs/clan-cli/tests/openapi_client/models/entity_create.py +++ b/pkgs/clan-cli/tests/openapi_client/models/entity_create.py @@ -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 diff --git a/pkgs/clan-cli/tests/openapi_client/models/eventmessage.py b/pkgs/clan-cli/tests/openapi_client/models/eventmessage.py index ece8617..4f0eb94 100644 --- a/pkgs/clan-cli/tests/openapi_client/models/eventmessage.py +++ b/pkgs/clan-cli/tests/openapi_client/models/eventmessage.py @@ -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 diff --git a/pkgs/clan-cli/tests/openapi_client/models/eventmessage_create.py b/pkgs/clan-cli/tests/openapi_client/models/eventmessage_create.py index 34915bb..cb1a740 100644 --- a/pkgs/clan-cli/tests/openapi_client/models/eventmessage_create.py +++ b/pkgs/clan-cli/tests/openapi_client/models/eventmessage_create.py @@ -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"), diff --git a/pkgs/clan-cli/tests/openapi_client/models/roles.py b/pkgs/clan-cli/tests/openapi_client/models/role.py similarity index 76% rename from pkgs/clan-cli/tests/openapi_client/models/roles.py rename to pkgs/clan-cli/tests/openapi_client/models/role.py index 189cbd3..1af9d78 100644 --- a/pkgs/clan-cli/tests/openapi_client/models/roles.py +++ b/pkgs/clan-cli/tests/openapi_client/models/role.py @@ -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)) diff --git a/pkgs/clan-cli/tests/test_db_api.py b/pkgs/clan-cli/tests/test_db_api.py index 3238d40..8f04751 100644 --- a/pkgs/clan-cli/tests/test_db_api.py +++ b/pkgs/clan-cli/tests/test_db_api.py @@ -13,7 +13,7 @@ from openapi_client.models import ( Eventmessage, EventmessageCreate, Machine, - Roles, + Role, ServiceCreate, Status, ) @@ -46,7 +46,7 @@ def create_entities(num: int = 10, role: str = "entity") -> 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={}, ) @@ -56,7 +56,7 @@ def create_entities(num: int = 10, role: str = "entity") -> list[EntityCreate]: name="DLG", ip=f"{host}:{port_dlg}/health", network="255.255.0.0", - role=Roles("DLG"), + roles=[Role("DLG")], visible=True, other={}, ) @@ -66,7 +66,7 @@ def create_entities(num: int = 10, role: str = "entity") -> list[EntityCreate]: name="AP", ip=f"{host}:{port_ap}/health", network="255.255.0.0", - role=Roles("AP"), + roles=[Role("AP")], visible=True, other={}, ) @@ -116,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, @@ -127,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, @@ -139,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, @@ -150,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, @@ -166,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() From e72846440cfe08a443487f0b8b157ad38954df9e Mon Sep 17 00:00:00 2001 From: Luis-Hebendanz Date: Sat, 13 Jan 2024 19:28:54 +0100 Subject: [PATCH 4/7] Fixed incorrect imports --- pkgs/clan-cli/clan_cli/__init__.py | 12 ++++++---- pkgs/clan-cli/clan_cli/config.py | 12 ++++++++++ pkgs/clan-cli/clan_cli/custom_logger.py | 23 +++++++++++++------ .../{ => clan_cli}/emulate_fastapi.py | 13 +++++++++-- pkgs/clan-cli/clan_cli/webui/__init__.py | 4 ++++ pkgs/clan-cli/clan_cli/webui/server.py | 19 ++++----------- pkgs/clan-cli/config.py | 4 ---- pkgs/clan-cli/tests/api.py | 2 +- pkgs/clan-cli/tests/test_db_api.py | 2 +- 9 files changed, 57 insertions(+), 34 deletions(-) create mode 100644 pkgs/clan-cli/clan_cli/config.py rename pkgs/clan-cli/{ => clan_cli}/emulate_fastapi.py (93%) delete mode 100644 pkgs/clan-cli/config.py diff --git a/pkgs/clan-cli/clan_cli/__init__.py b/pkgs/clan-cli/clan_cli/__init__.py index 163a47d..02a9f5a 100644 --- a/pkgs/clan-cli/clan_cli/__init__.py +++ b/pkgs/clan-cli/clan_cli/__init__.py @@ -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 diff --git a/pkgs/clan-cli/clan_cli/config.py b/pkgs/clan-cli/clan_cli/config.py new file mode 100644 index 0000000..e45f338 --- /dev/null +++ b/pkgs/clan-cli/clan_cli/config.py @@ -0,0 +1,12 @@ +import dataclasses + + +@dataclasses.dataclass +class Config: + host: str + port_dlg: int + port_ap: int + port_client_base: int + + +config = Config(host="127.0.0.1", port_dlg=6000, port_ap=6600, port_client_base=7000) diff --git a/pkgs/clan-cli/clan_cli/custom_logger.py b/pkgs/clan-cli/clan_cli/custom_logger.py index f9f324e..a73a95d 100644 --- a/pkgs/clan-cli/clan_cli/custom_logger.py +++ b/pkgs/clan-cli/clan_cli/custom_logger.py @@ -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) diff --git a/pkgs/clan-cli/emulate_fastapi.py b/pkgs/clan-cli/clan_cli/emulate_fastapi.py similarity index 93% rename from pkgs/clan-cli/emulate_fastapi.py rename to pkgs/clan-cli/clan_cli/emulate_fastapi.py index d663c9a..7ce274b 100644 --- a/pkgs/clan-cli/emulate_fastapi.py +++ b/pkgs/clan-cli/clan_cli/emulate_fastapi.py @@ -5,11 +5,20 @@ import urllib from fastapi import FastAPI from fastapi.responses import HTMLResponse +from .config import config + app_dlg = FastAPI() app_ap = FastAPI() app_c1 = FastAPI() app_c2 = FastAPI() +apps = [ + (app_dlg, config.port_dlg), + (app_ap, config.port_ap), + (app_c1, config.port_client_base), + (app_c2, config.port_client_base + 1), +] + # bash tests: curl localhost:6600/ap_list_of_services # curl localhost:7001/consume_service_from_other_entity @@ -40,9 +49,9 @@ async def healthcheck_ap() -> str: 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: + with urllib.request.urlopen(url) as response: # type: ignore return response.read() - except urllib.error.URLError as e: + except urllib.error.URLError as e: # type: ignore print(f"Attempt {attempt + 1} failed: {e.reason}", file=sys.stderr) time.sleep(delay) return None diff --git a/pkgs/clan-cli/clan_cli/webui/__init__.py b/pkgs/clan-cli/clan_cli/webui/__init__.py index 3848f9d..5ba79aa 100644 --- a/pkgs/clan-cli/clan_cli/webui/__init__.py +++ b/pkgs/clan-cli/clan_cli/webui/__init__.py @@ -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 diff --git a/pkgs/clan-cli/clan_cli/webui/server.py b/pkgs/clan-cli/clan_cli/webui/server.py index 7cd9165..ae5b2ab 100644 --- a/pkgs/clan-cli/clan_cli/webui/server.py +++ b/pkgs/clan-cli/clan_cli/webui/server.py @@ -1,5 +1,6 @@ import argparse import logging +import multiprocessing as mp import shutil import subprocess import time @@ -14,6 +15,7 @@ import uvicorn from pydantic import AnyUrl, IPvAnyAddress from pydantic.tools import parse_obj_as +from clan_cli.emulate_fastapi import apps, get_health from clan_cli.errors import ClanError log = logging.getLogger(__name__) @@ -127,28 +129,17 @@ def start_server(args: argparse.Namespace) -> None: subprocess.run(cmd, check=True) 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": "info"}, daemon=True, ) proc.start() - urls.append(f"http://{host}:{port}") + urls.append(f"http://{args.host}:{port}") # check server health for url in urls: res = get_health(url=url + "/health") diff --git a/pkgs/clan-cli/config.py b/pkgs/clan-cli/config.py deleted file mode 100644 index 2874a7d..0000000 --- a/pkgs/clan-cli/config.py +++ /dev/null @@ -1,4 +0,0 @@ -host = "127.0.0.1" -port_dlg = 6000 -port_ap = 6600 -port_client_base = 7000 diff --git a/pkgs/clan-cli/tests/api.py b/pkgs/clan-cli/tests/api.py index 101f3e3..f3c600a 100644 --- a/pkgs/clan-cli/tests/api.py +++ b/pkgs/clan-cli/tests/api.py @@ -11,7 +11,7 @@ from fastapi.testclient import TestClient from openapi_client import ApiClient, Configuration from ports import PortFunction -import config +from clan_cli.config import config from clan_cli.webui.app import app diff --git a/pkgs/clan-cli/tests/test_db_api.py b/pkgs/clan-cli/tests/test_db_api.py index 8f04751..bcaf160 100644 --- a/pkgs/clan-cli/tests/test_db_api.py +++ b/pkgs/clan-cli/tests/test_db_api.py @@ -18,7 +18,7 @@ from openapi_client.models import ( Status, ) -import config +from clan_cli.config import config random.seed(42) From 4ae4557602e057db0da137b13097385549470f96 Mon Sep 17 00:00:00 2001 From: Luis-Hebendanz Date: Sat, 13 Jan 2024 20:19:00 +0100 Subject: [PATCH 5/7] Added http request to emulate_fastapi for GET resolutions --- pkgs/clan-cli/clan_cli/emulate_fastapi.py | 201 +++++++----------- .../clan_cli/webui/routers/endpoints.py | 55 ++--- pkgs/clan-cli/clan_cli/webui/schemas.py | 9 +- pkgs/clan-cli/clan_cli/webui/server.py | 6 +- pkgs/clan-cli/tests/test_db_api.py | 4 +- 5 files changed, 113 insertions(+), 162 deletions(-) diff --git a/pkgs/clan-cli/clan_cli/emulate_fastapi.py b/pkgs/clan-cli/clan_cli/emulate_fastapi.py index 7ce274b..496ccb8 100644 --- a/pkgs/clan-cli/clan_cli/emulate_fastapi.py +++ b/pkgs/clan-cli/clan_cli/emulate_fastapi.py @@ -1,9 +1,12 @@ import sys import time import urllib +from datetime import datetime from fastapi import FastAPI -from fastapi.responses import HTMLResponse +from fastapi.responses import HTMLResponse, JSONResponse + +from clan_cli.webui.schemas import Resolution from .config import config @@ -19,13 +22,8 @@ apps = [ (app_c2, config.port_client_base + 1), ] -# bash tests: curl localhost:6600/ap_list_of_services -# curl localhost:7001/consume_service_from_other_entity - - -#### HEALTH - +#### HEALTHCHECK @app_c1.get("/health") async def healthcheck_c1() -> str: return "200 OK" @@ -88,127 +86,74 @@ async def consume_service_from_other_entity_c2() -> HTMLResponse: return HTMLResponse(content=html_content, status_code=200) -#### ap_list_of_services +@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_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_model=list[Resolution]) +async def dlg_list_of_did_resolutions() -> list[Resolution]: + res = [] - -@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) + 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 diff --git a/pkgs/clan-cli/clan_cli/webui/routers/endpoints.py b/pkgs/clan-cli/clan_cli/webui/routers/endpoints.py index e8d90fe..326682d 100644 --- a/pkgs/clan-cli/clan_cli/webui/routers/endpoints.py +++ b/pkgs/clan-cli/clan_cli/webui/routers/endpoints.py @@ -1,6 +1,5 @@ import logging import time -from datetime import datetime from typing import List, Optional import httpx @@ -49,7 +48,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), @@ -64,7 +63,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), @@ -75,7 +74,7 @@ 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) @@ -126,7 +125,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) @@ -148,7 +147,7 @@ def get_attached_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), @@ -163,7 +162,7 @@ def detach_entity( @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), @@ -180,7 +179,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) @@ -229,8 +228,8 @@ 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) @@ -245,21 +244,27 @@ 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 + matching_entities = sql_crud.get_entity_by_role(db, roles=[Role("DLG")]) + if matching_entities is None: + raise ClanError("No DLG found") + if len(matching_entities) > 1: + raise ClanError("More than one DLG found") + dlg = matching_entities[0] - 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, - ) - ] + url = f"http://{dlg.ip}/dlg_list_of_did_resolutions" + try: + response = httpx.get(url, timeout=2) + except httpx.HTTPError as e: + raise ClanError(f"DLG not reachable at {url}") from e + + if response.status_code != 200: + raise ClanError(f"DLG returned {response.status_code}") + + resolutions = response.json() + return resolutions ######################### @@ -267,12 +272,10 @@ async def get_all_resolutions( # Eventmessage # # # ######################### - - @router.post( "/api/v1/event_message", response_model=Eventmessage, tags=[Tags.eventmessages] ) -async def create_eventmessage( +def create_eventmessage( eventmsg: EventmessageCreate, db: Session = Depends(sql_db.get_db) ) -> EventmessageCreate: return sql_crud.create_eventmessage(db, eventmsg) @@ -283,7 +286,7 @@ 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) diff --git a/pkgs/clan-cli/clan_cli/webui/schemas.py b/pkgs/clan-cli/clan_cli/webui/schemas.py index 169a560..a203d0f 100644 --- a/pkgs/clan-cli/clan_cli/webui/schemas.py +++ b/pkgs/clan-cli/clan_cli/webui/schemas.py @@ -45,7 +45,7 @@ class EntityRoles(EntityRolesBase): 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") @@ -93,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): @@ -119,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"}) @@ -129,7 +129,6 @@ class ResolutionCreate(ResolutionBase): class Resolution(ResolutionCreate): timestamp: datetime - id: int class Config: orm_mode = True @@ -148,7 +147,7 @@ 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): diff --git a/pkgs/clan-cli/clan_cli/webui/server.py b/pkgs/clan-cli/clan_cli/webui/server.py index ae5b2ab..cfc7ab5 100644 --- a/pkgs/clan-cli/clan_cli/webui/server.py +++ b/pkgs/clan-cli/clan_cli/webui/server.py @@ -135,7 +135,11 @@ def start_server(args: argparse.Namespace) -> None: proc = mp.Process( target=uvicorn.run, args=(app,), - kwargs={"host": args.host, "port": port, "log_level": "info"}, + kwargs={ + "host": args.host, + "port": port, + "log_level": args.log_level, + }, daemon=True, ) proc.start() diff --git a/pkgs/clan-cli/tests/test_db_api.py b/pkgs/clan-cli/tests/test_db_api.py index bcaf160..6961f20 100644 --- a/pkgs/clan-cli/tests/test_db_api.py +++ b/pkgs/clan-cli/tests/test_db_api.py @@ -54,7 +54,7 @@ def create_entities(num: int = 10, role: str = "entity") -> 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", roles=[Role("DLG")], visible=True, @@ -64,7 +64,7 @@ def create_entities(num: int = 10, role: str = "entity") -> 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", roles=[Role("AP")], visible=True, From f4cf817bf1cb2ced209a9d5ff0fa698c4a819064 Mon Sep 17 00:00:00 2001 From: Luis-Hebendanz Date: Sat, 13 Jan 2024 20:54:56 +0100 Subject: [PATCH 6/7] Better emulate metadata --- pkgs/clan-cli/clan_cli/config.py | 20 +++++------- pkgs/clan-cli/clan_cli/emulate_fastapi.py | 31 +++++++++++++++---- .../clan_cli/webui/routers/endpoints.py | 29 +++++++++++++++++ pkgs/clan-cli/clan_cli/webui/server.py | 7 ++--- pkgs/clan-cli/tests/api.py | 2 +- pkgs/clan-cli/tests/test_db_api.py | 2 +- 6 files changed, 67 insertions(+), 24 deletions(-) diff --git a/pkgs/clan-cli/clan_cli/config.py b/pkgs/clan-cli/clan_cli/config.py index e45f338..1891573 100644 --- a/pkgs/clan-cli/clan_cli/config.py +++ b/pkgs/clan-cli/clan_cli/config.py @@ -1,12 +1,8 @@ -import dataclasses - - -@dataclasses.dataclass -class Config: - host: str - port_dlg: int - port_ap: int - port_client_base: int - - -config = Config(host="127.0.0.1", port_dlg=6000, port_ap=6600, port_client_base=7000) +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" diff --git a/pkgs/clan-cli/clan_cli/emulate_fastapi.py b/pkgs/clan-cli/clan_cli/emulate_fastapi.py index 496ccb8..982a1fb 100644 --- a/pkgs/clan-cli/clan_cli/emulate_fastapi.py +++ b/pkgs/clan-cli/clan_cli/emulate_fastapi.py @@ -6,14 +6,13 @@ 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 -from .config import config - -app_dlg = FastAPI() -app_ap = FastAPI() -app_c1 = FastAPI() -app_c2 = FastAPI() +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), @@ -24,21 +23,41 @@ apps = [ #### 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" diff --git a/pkgs/clan-cli/clan_cli/webui/routers/endpoints.py b/pkgs/clan-cli/clan_cli/webui/routers/endpoints.py index 326682d..96fadc8 100644 --- a/pkgs/clan-cli/clan_cli/webui/routers/endpoints.py +++ b/pkgs/clan-cli/clan_cli/webui/routers/endpoints.py @@ -4,6 +4,7 @@ from typing import List, Optional import httpx from fastapi import APIRouter, BackgroundTasks, Depends, Query +from fastapi.responses import HTMLResponse from sqlalchemy.orm import Session from ...errors import ClanError @@ -291,3 +292,31 @@ def get_all_eventmessages( ) -> 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""" + + + Emulated API + + +

Emulated API

+

Emulated API endpoints for testing purposes.

+

DLG: {dlg_url}

+

AP: {ap_url}

+

C1: {c1_url}

+

C2: {c2_url}

+ + + """ + return HTMLResponse(content=html_content, status_code=200) diff --git a/pkgs/clan-cli/clan_cli/webui/server.py b/pkgs/clan-cli/clan_cli/webui/server.py index cfc7ab5..4dce764 100644 --- a/pkgs/clan-cli/clan_cli/webui/server.py +++ b/pkgs/clan-cli/clan_cli/webui/server.py @@ -15,6 +15,7 @@ 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 @@ -128,8 +129,8 @@ 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: - urls = list() # start servers as processes (dlg, ap, c1 and c2 for tests) for app, port in apps: proc = mp.Process( @@ -143,9 +144,7 @@ def start_server(args: argparse.Namespace) -> None: daemon=True, ) proc.start() - urls.append(f"http://{args.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") diff --git a/pkgs/clan-cli/tests/api.py b/pkgs/clan-cli/tests/api.py index f3c600a..5a3f376 100644 --- a/pkgs/clan-cli/tests/api.py +++ b/pkgs/clan-cli/tests/api.py @@ -11,7 +11,7 @@ from fastapi.testclient import TestClient from openapi_client import ApiClient, Configuration from ports import PortFunction -from clan_cli.config import config +import clan_cli.config as config from clan_cli.webui.app import app diff --git a/pkgs/clan-cli/tests/test_db_api.py b/pkgs/clan-cli/tests/test_db_api.py index 6961f20..6e2911b 100644 --- a/pkgs/clan-cli/tests/test_db_api.py +++ b/pkgs/clan-cli/tests/test_db_api.py @@ -18,7 +18,7 @@ from openapi_client.models import ( Status, ) -from clan_cli.config import config +import clan_cli.config as config random.seed(42) From 0a20931ba69bc7566a6fdea415f249261eeac1d6 Mon Sep 17 00:00:00 2001 From: Luis-Hebendanz Date: Sun, 14 Jan 2024 13:53:02 +0100 Subject: [PATCH 7/7] Added AP get_repository --- .../clan_cli/webui/routers/endpoints.py | 51 ++++++++++++------- pkgs/clan-cli/clan_cli/webui/tags.py | 4 ++ 2 files changed, 38 insertions(+), 17 deletions(-) diff --git a/pkgs/clan-cli/clan_cli/webui/routers/endpoints.py b/pkgs/clan-cli/clan_cli/webui/routers/endpoints.py index 96fadc8..8ce5e27 100644 --- a/pkgs/clan-cli/clan_cli/webui/routers/endpoints.py +++ b/pkgs/clan-cli/clan_cli/webui/routers/endpoints.py @@ -1,6 +1,6 @@ import logging import time -from typing import List, Optional +from typing import Any, List, Optional import httpx from fastapi import APIRouter, BackgroundTasks, Depends, Query @@ -237,6 +237,26 @@ def delete_entity( 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 # @@ -248,24 +268,21 @@ def delete_entity( def get_all_resolutions( skip: int = 0, limit: int = 100, db: Session = Depends(sql_db.get_db) ) -> List[Resolution]: - matching_entities = sql_crud.get_entity_by_role(db, roles=[Role("DLG")]) - if matching_entities is None: - raise ClanError("No DLG found") - if len(matching_entities) > 1: - raise ClanError("More than one DLG found") - dlg = matching_entities[0] + return get_rpc_by_role(db, Role("DLG"), "dlg_list_of_did_resolutions") - url = f"http://{dlg.ip}/dlg_list_of_did_resolutions" - try: - response = httpx.get(url, timeout=2) - except httpx.HTTPError as e: - raise ClanError(f"DLG not reachable at {url}") from e - if response.status_code != 200: - raise ClanError(f"DLG returned {response.status_code}") - - resolutions = response.json() - return resolutions +######################### +# # +# 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") ######################### diff --git a/pkgs/clan-cli/clan_cli/webui/tags.py b/pkgs/clan-cli/clan_cli/webui/tags.py index 619b1f0..941337c 100644 --- a/pkgs/clan-cli/clan_cli/webui/tags.py +++ b/pkgs/clan-cli/clan_cli/webui/tags.py @@ -18,6 +18,10 @@ tags_metadata: List[Dict[str, Any]] = [ "name": str(Tags.services), "description": "Operations on a service.", }, + { + "name": str(Tags.repositories), + "description": "Operations on a repository.", + }, { "name": str(Tags.entities), "description": "Operations on an entity.",