diff --git a/apis/paios/openapi.yaml b/apis/paios/openapi.yaml index f17a928a..0c03edca 100644 --- a/apis/paios/openapi.yaml +++ b/apis/paios/openapi.yaml @@ -246,12 +246,12 @@ paths: description: Not Found parameters: - $ref: '#/components/parameters/id' - /channels: + /resources: get: tags: - - Channel Management - summary: Retrieve all channels - description: Retrieve the information of all channels. + - Resource Management + summary: Retrieve all resources + description: Retrieve the information of all resources. parameters: - $ref: '#/components/parameters/sort' - $ref: '#/components/parameters/range' @@ -264,31 +264,31 @@ paths: schema: type: array items: - $ref: '#/components/schemas/Channel' + $ref: '#/components/schemas/Resource' headers: X-Total-Count: $ref: '#/components/headers/X-Total-Count' post: - summary: Create new channel + summary: Create new resource tags: - - Channel Management + - Resource Management responses: '200': description: OK '400': description: Missing Required Information - description: Create a new channel. + description: Create a new resource. requestBody: content: application/json: schema: $ref: '#/components/schemas/ChannelCreate' - '/channels/{id}': + '/resources/{id}': get: tags: - - Channel Management - summary: Retrieve channel by id - description: Retrieve the information of the channel with the specified id. + - Resource Management + summary: Retrieve resource by id + description: Retrieve the information of the resource with the specified id. parameters: - $ref: '#/components/parameters/id' responses: @@ -297,27 +297,27 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/Channel' + $ref: '#/components/schemas/Resource' put: tags: - - Channel Management - summary: Update channel by key - description: Updates the channel with the specified id. + - Resource Management + summary: Update resource by key + description: Updates the resource with the specified id. parameters: - $ref: '#/components/parameters/id' requestBody: content: application/json: schema: - $ref: '#/components/schemas/Channel' + $ref: '#/components/schemas/Resource' responses: '200': description: OK delete: tags: - - Channel Management - summary: Delete channel by key - description: Delete a channel with the specified ID. + - Resource Management + summary: Delete resource by key + description: Delete a resource with the specified ID. parameters: - $ref: '#/components/parameters/id' responses: @@ -639,8 +639,8 @@ tags: description: Installation and configuration of abilities - name: Asset Management description: Management of assets - - name: Channel Management - description: Discovery and configuration of input/output interfaces/channels + - name: Resource Management + description: Discovery and configuration of input/output interfaces/resources - name: Configuration Management description: Management of configuration - name: Downloads Management @@ -908,10 +908,10 @@ components: $ref: '#/components/schemas/textShort' required: - title - Channel: + Resource: type: object - title: Channel - description: Channels (input/output) + title: Resource + description: Resources (input/output) properties: id: $ref: '#/components/schemas/uuid4ReadOnly' @@ -925,8 +925,8 @@ components: - uri ChannelCreate: type: object - title: Channel - description: Channels (input/output) + title: Resource + description: Resources (input/output) properties: name: $ref: '#/components/schemas/name' diff --git a/apis/paios/channel.http b/apis/paios/resource.http similarity index 61% rename from apis/paios/channel.http rename to apis/paios/resource.http index 039f9c63..79a8055f 100644 --- a/apis/paios/channel.http +++ b/apis/paios/resource.http @@ -1,27 +1,27 @@ # API testing with Visual Studio Code - REST Client Extention -POST http://localhost:3080/api/v1/channels +POST http://localhost:3080/api/v1/resources Authorization: Bearer {{PAIOS_BEARER_TOKEN}} Content-Type: application/json { - "name": "My Channel", - "uri": "http://localhost:3080/channels/my-channel" + "name": "My Resource", + "uri": "http://localhost:3080/resources/my-resource" } ### -PUT http://localhost:3080/api/v1/channels/llm-api +PUT http://localhost:3080/api/v1/resources/llm-api Authorization: Bearer {{PAIOS_BEARER_TOKEN}} Content-Type: application/json { "id": "llm-api", "name": "LLM API", - "uri": "http://localhost:3080/channels/llm-api" + "uri": "http://localhost:3080/resources/llm-api" } ### -PUT http://localhost:3080/api/v1/channels/imap-sync +PUT http://localhost:3080/api/v1/resources/imap-sync Authorization: Bearer {{PAIOS_BEARER_TOKEN}} Content-Type: application/json @@ -33,18 +33,18 @@ Content-Type: application/json ### -DELETE http://localhost:3080/api/v1/channels/llm-api +DELETE http://localhost:3080/api/v1/resources/llm-api Authorization: Bearer {{PAIOS_BEARER_TOKEN}} Content-Type: application/json ### -GET http://localhost:3080/api/v1/channels/llm-api +GET http://localhost:3080/api/v1/resources/llm-api Authorization: Bearer {{PAIOS_BEARER_TOKEN}} Content-Type: application/json ### -GET http://localhost:3080/api/v1/channels +GET http://localhost:3080/api/v1/resources Authorization: Bearer {{PAIOS_BEARER_TOKEN}} Content-Type: application/json diff --git a/backend/api/ChannelsView.py b/backend/api/ChannelsView.py deleted file mode 100644 index 0b720674..00000000 --- a/backend/api/ChannelsView.py +++ /dev/null @@ -1,46 +0,0 @@ -from starlette.responses import JSONResponse, Response -from common.paths import api_base_url -from backend.managers.ChannelsManager import ChannelsManager -from backend.pagination import parse_pagination_params -from backend.schemas import ChannelCreateSchema -from typing import List - -class ChannelsView: - def __init__(self): - self.cm = ChannelsManager() - - async def get(self, channel_id: str): - channel = await self.cm.retrieve_channel(channel_id) - if channel is None: - return JSONResponse({"error": "Channel not found"}, status_code=404) - return JSONResponse(channel.model_dump(), status_code=200) - - async def post(self, body: ChannelCreateSchema): - new_channel = await self.cm.create_channel(body) - return JSONResponse(new_channel.model_dump(), status_code=201, headers={'Location': f'{api_base_url}/channels/{new_channel.id}'}) - - async def put(self, channel_id: str, body: ChannelCreateSchema): - updated_channel = await self.cm.update_channel(channel_id, body) - if updated_channel is None: - return JSONResponse({"error": "Channel not found"}, status_code=404) - return JSONResponse(updated_channel.model_dump(), status_code=200) - - async def delete(self, channel_id: str): - success = await self.cm.delete_channel(channel_id) - if not success: - return JSONResponse({"error": "Channel not found"}, status_code=404) - return Response(status_code=204) - - async def search(self, filter: str = None, range: str = None, sort: str = None): - result = parse_pagination_params(filter, range, sort) - if isinstance(result, JSONResponse): - return result - - offset, limit, sort_by, sort_order, filters = result - - channels, total_count = await self.cm.retrieve_channels(limit=limit, offset=offset, sort_by=sort_by, sort_order=sort_order, filters=filters) - headers = { - 'X-Total-Count': str(total_count), - 'Content-Range': f'channels {offset}-{offset + len(channels) - 1}/{total_count}' - } - return JSONResponse([channel.model_dump() for channel in channels], status_code=200, headers=headers) diff --git a/backend/api/ResourcesView.py b/backend/api/ResourcesView.py new file mode 100644 index 00000000..e5cc379b --- /dev/null +++ b/backend/api/ResourcesView.py @@ -0,0 +1,46 @@ +from starlette.responses import JSONResponse, Response +from common.paths import api_base_url +from backend.managers.ResourcesManager import ResourcesManager +from backend.pagination import parse_pagination_params +from backend.schemas import ChannelCreateSchema +from typing import List + +class ResourcesView: + def __init__(self): + self.cm = ResourcesManager() + + async def get(self, resource_id: str): + resource = await self.cm.retrieve_resource(resource_id) + if resource is None: + return JSONResponse({"error": "Resource not found"}, status_code=404) + return JSONResponse(resource.model_dump(), status_code=200) + + async def post(self, body: ChannelCreateSchema): + new_resource = await self.cm.create_resource(body) + return JSONResponse(new_resource.model_dump(), status_code=201, headers={'Location': f'{api_base_url}/resources/{new_resource.id}'}) + + async def put(self, resource_id: str, body: ChannelCreateSchema): + updated_resource = await self.cm.update_resource(resource_id, body) + if updated_resource is None: + return JSONResponse({"error": "Resource not found"}, status_code=404) + return JSONResponse(updated_resource.model_dump(), status_code=200) + + async def delete(self, resource_id: str): + success = await self.cm.delete_resource(resource_id) + if not success: + return JSONResponse({"error": "Resource not found"}, status_code=404) + return Response(status_code=204) + + async def search(self, filter: str = None, range: str = None, sort: str = None): + result = parse_pagination_params(filter, range, sort) + if isinstance(result, JSONResponse): + return result + + offset, limit, sort_by, sort_order, filters = result + + resources, total_count = await self.cm.retrieve_resources(limit=limit, offset=offset, sort_by=sort_by, sort_order=sort_order, filters=filters) + headers = { + 'X-Total-Count': str(total_count), + 'Content-Range': f'resources {offset}-{offset + len(resources) - 1}/{total_count}' + } + return JSONResponse([resource.model_dump() for resource in resources], status_code=200, headers=headers) diff --git a/backend/api/__init__.py b/backend/api/__init__.py index 4fff6452..fa658af9 100644 --- a/backend/api/__init__.py +++ b/backend/api/__init__.py @@ -2,7 +2,7 @@ # otherwise connexion will throw "TypeError: 'module' object is not callable" from .AbilitiesView import AbilitiesView from .AssetsView import AssetsView -from .ChannelsView import ChannelsView +from .ResourcesView import ResourcesView from .ConfigView import ConfigView from .DownloadsView import DownloadsView from .UsersView import UsersView diff --git a/backend/managers/ChannelsManager.py b/backend/managers/ResourcesManager.py similarity index 53% rename from backend/managers/ChannelsManager.py rename to backend/managers/ResourcesManager.py index 753bcc2d..c676eb20 100644 --- a/backend/managers/ChannelsManager.py +++ b/backend/managers/ResourcesManager.py @@ -1,12 +1,12 @@ from uuid import uuid4 from threading import Lock from sqlalchemy import select, insert, update, delete, func -from backend.models import Channel +from backend.models import Resource from backend.db import db_session_context from backend.schemas import ChannelCreateSchema, ChannelSchema from typing import List, Tuple, Optional, Dict, Any -class ChannelsManager: +class ResourcesManager: _instance = None _lock = Lock() @@ -14,7 +14,7 @@ def __new__(cls, *args, **kwargs): if not cls._instance: with cls._lock: if not cls._instance: - cls._instance = super(ChannelsManager, cls).__new__(cls, *args, **kwargs) + cls._instance = super(ResourcesManager, cls).__new__(cls, *args, **kwargs) return cls._instance def __init__(self): @@ -24,71 +24,71 @@ def __init__(self): # db.init_db() self._initialized = True - async def create_channel(self, channel_data: ChannelCreateSchema) -> ChannelSchema: + async def create_resource(self, resource_data: ChannelCreateSchema) -> ChannelSchema: async with db_session_context() as session: - new_channel = Channel(id=str(uuid4()), **channel_data.model_dump()) - session.add(new_channel) + new_resource = Resource(id=str(uuid4()), **resource_data.model_dump()) + session.add(new_resource) await session.commit() - await session.refresh(new_channel) - return ChannelSchema(id=new_channel.id, **channel_data.model_dump()) + await session.refresh(new_resource) + return ChannelSchema(id=new_resource.id, **resource_data.model_dump()) - async def update_channel(self, id: str, channel_data: ChannelCreateSchema) -> Optional[ChannelSchema]: + async def update_resource(self, id: str, resource_data: ChannelCreateSchema) -> Optional[ChannelSchema]: async with db_session_context() as session: - stmt = update(Channel).where(Channel.id == id).values(**channel_data.dict()) + stmt = update(Resource).where(Resource.id == id).values(**resource_data.dict()) result = await session.execute(stmt) if result.rowcount > 0: await session.commit() - updated_channel = await session.get(Channel, id) - return ChannelSchema(id=updated_channel.id, **channel_data.model_dump()) + updated_resource = await session.get(Resource, id) + return ChannelSchema(id=updated_resource.id, **resource_data.model_dump()) return None - async def delete_channel(self, id: str) -> bool: + async def delete_resource(self, id: str) -> bool: async with db_session_context() as session: - stmt = delete(Channel).where(Channel.id == id) + stmt = delete(Resource).where(Resource.id == id) result = await session.execute(stmt) await session.commit() return result.rowcount > 0 - async def retrieve_channel(self, id: str) -> Optional[ChannelSchema]: + async def retrieve_resource(self, id: str) -> Optional[ChannelSchema]: async with db_session_context() as session: - result = await session.execute(select(Channel).filter(Channel.id == id)) - channel = result.scalar_one_or_none() - if channel: - return ChannelSchema(id=channel.id, name=channel.name, uri=channel.uri) + result = await session.execute(select(Resource).filter(Resource.id == id)) + resource = result.scalar_one_or_none() + if resource: + return ChannelSchema(id=resource.id, name=resource.name, uri=resource.uri) return None - async def retrieve_channels(self, offset: int = 0, limit: int = 100, sort_by: Optional[str] = None, + async def retrieve_resources(self, offset: int = 0, limit: int = 100, sort_by: Optional[str] = None, sort_order: str = 'asc', filters: Optional[Dict[str, Any]] = None) -> Tuple[List[ChannelSchema], int]: async with db_session_context() as session: - query = select(Channel) + query = select(Resource) if filters: for key, value in filters.items(): if isinstance(value, list): - query = query.filter(getattr(Channel, key).in_(value)) + query = query.filter(getattr(Resource, key).in_(value)) else: - query = query.filter(getattr(Channel, key) == value) + query = query.filter(getattr(Resource, key) == value) if sort_by and sort_by in ['id', 'name', 'uri']: - order_column = getattr(Channel, sort_by) + order_column = getattr(Resource, sort_by) query = query.order_by(order_column.desc() if sort_order.lower() == 'desc' else order_column) query = query.offset(offset).limit(limit) result = await session.execute(query) - channels = [ChannelSchema(id=channel.id, name=channel.name, uri=channel.uri) - for channel in result.scalars().all()] + resources = [ChannelSchema(id=resource.id, name=resource.name, uri=resource.uri) + for resource in result.scalars().all()] # Get total count - count_query = select(func.count()).select_from(Channel) + count_query = select(func.count()).select_from(Resource) if filters: for key, value in filters.items(): if isinstance(value, list): - count_query = count_query.filter(getattr(Channel, key).in_(value)) + count_query = count_query.filter(getattr(Resource, key).in_(value)) else: - count_query = count_query.filter(getattr(Channel, key) == value) + count_query = count_query.filter(getattr(Resource, key) == value) total_count = await session.execute(count_query) total_count = total_count.scalar() - return channels, total_count + return resources, total_count diff --git a/backend/managers/__init__.py b/backend/managers/__init__.py index ee21d4ef..7e9e9d33 100644 --- a/backend/managers/__init__.py +++ b/backend/managers/__init__.py @@ -1,6 +1,6 @@ from .AbilitiesManager import AbilitiesManager from .AssetsManager import AssetsManager -from .ChannelsManager import ChannelsManager +from .ResourcesManager import ResourcesManager from .ConfigManager import ConfigManager from .DownloadsManager import DownloadsManager from .UsersManager import UsersManager @@ -10,7 +10,7 @@ manager_classes = [ AbilitiesManager, AssetsManager, - ChannelsManager, + ResourcesManager, ConfigManager, DownloadsManager, UsersManager, diff --git a/backend/models.py b/backend/models.py index 39f0fcfa..b0b40b1b 100644 --- a/backend/models.py +++ b/backend/models.py @@ -6,8 +6,8 @@ class Config(Base): key = Column(String, primary_key=True) value = Column(String, nullable=True) -class Channel(Base): - __tablename__ = "channel" +class Resource(Base): + __tablename__ = "resource" id = Column(String, primary_key=True) name = Column(String, nullable=False) uri = Column(String, nullable=False) diff --git a/backend/schemas.py b/backend/schemas.py index 77a84d2e..4175cde2 100644 --- a/backend/schemas.py +++ b/backend/schemas.py @@ -12,7 +12,7 @@ class ConfigBaseSchema(BaseModel): class ConfigSchema(ConfigBaseSchema): key: str -# Channel schemas +# Resource schemas class ChannelBaseSchema(BaseModel): name: str uri: str @@ -29,9 +29,6 @@ class PersonaBaseSchema(BaseModel): description: Optional[str] = None voice_id: str = None face_id: str = None - class Config: - orm_mode = True - from_attributes = True class PersonaCreateSchema(PersonaBaseSchema): pass @@ -62,4 +59,4 @@ class AssetCreateSchema(AssetBaseSchema): pass class AssetSchema(AssetBaseSchema): - id: str \ No newline at end of file + id: str diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 81841a86..47d034da 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -3,7 +3,7 @@ import { Admin, Resource } from 'react-admin'; import { UserList, UserCreate, UserEdit, UserShow } from "./users"; import { AbilityList, AbilityShow } from "./abilities"; import { AssetList, AssetCreate, AssetEdit, AssetShow } from "./assets"; -import { ChannelList, ChannelShow } from "./channels"; +import { ChannelList, ChannelShow } from "./resources"; import { DownloadsList } from "./downloads"; import { dataProvider } from "./dataProvider"; import DocIcon from "@mui/icons-material/Book"; @@ -24,7 +24,7 @@ export const App = () => ( - + ); diff --git a/frontend/src/CustomMenu.tsx b/frontend/src/CustomMenu.tsx index d41def4f..b3630783 100644 --- a/frontend/src/CustomMenu.tsx +++ b/frontend/src/CustomMenu.tsx @@ -11,7 +11,7 @@ export const CustomMenu = () => ( - + diff --git a/frontend/src/channels.tsx b/frontend/src/resources.tsx similarity index 100% rename from frontend/src/channels.tsx rename to frontend/src/resources.tsx diff --git a/migrations/versions/75aaaf2cd1a2_added_channel_table.py b/migrations/versions/75aaaf2cd1a2_added_resource_table.py similarity index 91% rename from migrations/versions/75aaaf2cd1a2_added_channel_table.py rename to migrations/versions/75aaaf2cd1a2_added_resource_table.py index 2030a7e1..bb05e43d 100644 --- a/migrations/versions/75aaaf2cd1a2_added_channel_table.py +++ b/migrations/versions/75aaaf2cd1a2_added_resource_table.py @@ -1,4 +1,4 @@ -"""Added channel table +"""Added resource table Revision ID: 75aaaf2cd1a2 Revises: 56a640fb45b2 @@ -21,7 +21,7 @@ def upgrade() -> None: # ### commands auto generated by Alembic - please adjust! ### - op.create_table('channel', + op.create_table('resource', sa.Column('id', sqlmodel.sql.sqltypes.AutoString(), nullable=False), sa.Column('name', sqlmodel.sql.sqltypes.AutoString(), nullable=False), sa.Column('uri', sqlmodel.sql.sqltypes.AutoString(), nullable=False), @@ -32,5 +32,5 @@ def upgrade() -> None: def downgrade() -> None: # ### commands auto generated by Alembic - please adjust! ### - op.drop_table('channel') + op.drop_table('resource') # ### end Alembic commands ###