feat: add workspace
This commit is contained in:
@@ -7,6 +7,7 @@ from src.controller.http.external_channels import external_channels_router
|
||||
from src.controller.http.placements import placements_router
|
||||
from src.controller.http.target_channels import target_channels_router
|
||||
from src.controller.http.views import views_router
|
||||
from src.controller.http.workspaces import workspaces_router
|
||||
|
||||
api_router = APIRouter()
|
||||
|
||||
@@ -19,5 +20,6 @@ api_v1_router.include_router(creatives_router)
|
||||
api_v1_router.include_router(placements_router)
|
||||
api_v1_router.include_router(views_router)
|
||||
api_v1_router.include_router(analytics_router)
|
||||
api_v1_router.include_router(workspaces_router)
|
||||
|
||||
api_router.include_router(api_v1_router)
|
||||
|
||||
@@ -8,16 +8,18 @@ from fastapi.routing import APIRouter
|
||||
from src import deps, dto
|
||||
from src.adapter.jwt import JWTPayload
|
||||
|
||||
analytics_router = APIRouter(prefix='/analytics', tags=['analytics'])
|
||||
analytics_router = APIRouter(prefix='/workspaces/{workspace_id}/analytics', tags=['analytics'])
|
||||
|
||||
|
||||
@analytics_router.get('/placements')
|
||||
async def get_placements_analytics(
|
||||
workspace_id: uuid.UUID,
|
||||
current_user: Annotated[JWTPayload, Depends(deps.get_current_user)],
|
||||
target_channel_id: uuid.UUID | None = None,
|
||||
) -> dto.GetPlacementsAnalyticsOutput:
|
||||
input = dto.GetPlacementsAnalyticsInput(
|
||||
user_id=current_user.user_id,
|
||||
workspace_id=workspace_id,
|
||||
target_channel_id=target_channel_id,
|
||||
)
|
||||
return await deps.get_usecase().get_placements_analytics(input)
|
||||
@@ -25,11 +27,13 @@ async def get_placements_analytics(
|
||||
|
||||
@analytics_router.get('/creatives')
|
||||
async def get_creatives_analytics(
|
||||
workspace_id: uuid.UUID,
|
||||
current_user: Annotated[JWTPayload, Depends(deps.get_current_user)],
|
||||
target_channel_id: uuid.UUID | None = None,
|
||||
) -> dto.GetCreativesAnalyticsOutput:
|
||||
input = dto.GetCreativesAnalyticsInput(
|
||||
user_id=current_user.user_id,
|
||||
workspace_id=workspace_id,
|
||||
target_channel_id=target_channel_id,
|
||||
)
|
||||
return await deps.get_usecase().get_creatives_analytics(input)
|
||||
@@ -37,11 +41,13 @@ async def get_creatives_analytics(
|
||||
|
||||
@analytics_router.get('/external-channels')
|
||||
async def get_external_channels_analytics(
|
||||
workspace_id: uuid.UUID,
|
||||
current_user: Annotated[JWTPayload, Depends(deps.get_current_user)],
|
||||
target_channel_id: uuid.UUID | None = None,
|
||||
) -> dto.GetExternalChannelsAnalyticsOutput:
|
||||
input = dto.GetExternalChannelsAnalyticsInput(
|
||||
user_id=current_user.user_id,
|
||||
workspace_id=workspace_id,
|
||||
target_channel_id=target_channel_id,
|
||||
)
|
||||
return await deps.get_usecase().get_external_channels_analytics(input)
|
||||
@@ -49,6 +55,7 @@ async def get_external_channels_analytics(
|
||||
|
||||
@analytics_router.get('/spending')
|
||||
async def get_spending_analytics(
|
||||
workspace_id: uuid.UUID,
|
||||
current_user: Annotated[JWTPayload, Depends(deps.get_current_user)],
|
||||
target_channel_id: uuid.UUID | None = None,
|
||||
date_from: datetime.datetime | None = None,
|
||||
@@ -57,6 +64,7 @@ async def get_spending_analytics(
|
||||
) -> dto.GetSpendingAnalyticsOutput:
|
||||
input = dto.GetSpendingAnalyticsInput(
|
||||
user_id=current_user.user_id,
|
||||
workspace_id=workspace_id,
|
||||
target_channel_id=target_channel_id,
|
||||
date_from=date_from,
|
||||
date_to=date_to,
|
||||
|
||||
@@ -7,17 +7,19 @@ from fastapi.routing import APIRouter
|
||||
from src import deps, dto
|
||||
from src.adapter.jwt import JWTPayload
|
||||
|
||||
creatives_router = APIRouter(prefix='/creatives', tags=['creatives'])
|
||||
creatives_router = APIRouter(prefix='/workspaces/{workspace_id}/creatives', tags=['creatives'])
|
||||
|
||||
|
||||
@creatives_router.get('')
|
||||
async def list_creatives(
|
||||
workspace_id: uuid.UUID,
|
||||
current_user: Annotated[JWTPayload, Depends(deps.get_current_user)],
|
||||
target_channel_id: uuid.UUID | None = None,
|
||||
include_archived: bool = False,
|
||||
) -> dto.GetCreativesOutput:
|
||||
input = dto.GetCreativesInput(
|
||||
user_id=current_user.user_id,
|
||||
workspace_id=workspace_id,
|
||||
target_channel_id=target_channel_id,
|
||||
include_archived=include_archived,
|
||||
)
|
||||
@@ -28,11 +30,13 @@ async def list_creatives(
|
||||
@creatives_router.get('/{creative_id}')
|
||||
async def get_creative(
|
||||
creative_id: uuid.UUID,
|
||||
workspace_id: uuid.UUID,
|
||||
current_user: Annotated[JWTPayload, Depends(deps.get_current_user)],
|
||||
) -> dto.CreativeOutput:
|
||||
input = dto.GetCreativeInput(
|
||||
creative_id=creative_id,
|
||||
user_id=current_user.user_id,
|
||||
workspace_id=workspace_id,
|
||||
)
|
||||
|
||||
return await deps.get_usecase().get_creative(input=input)
|
||||
@@ -41,32 +45,37 @@ async def get_creative(
|
||||
@creatives_router.post('')
|
||||
async def create_creative(
|
||||
request: dto.CreateCreativeInput,
|
||||
workspace_id: uuid.UUID,
|
||||
current_user: Annotated[JWTPayload, Depends(deps.get_current_user)],
|
||||
) -> dto.CreativeOutput:
|
||||
return await deps.get_usecase().create_creative(request, current_user.user_id)
|
||||
return await deps.get_usecase().create_creative(request, current_user.user_id, workspace_id)
|
||||
|
||||
|
||||
@creatives_router.patch('/{creative_id}')
|
||||
async def update_creative(
|
||||
creative_id: uuid.UUID,
|
||||
request: dto.UpdateCreativeInput,
|
||||
workspace_id: uuid.UUID,
|
||||
current_user: Annotated[JWTPayload, Depends(deps.get_current_user)],
|
||||
) -> dto.CreativeOutput:
|
||||
return await deps.get_usecase().update_creative(
|
||||
creative_id=creative_id,
|
||||
input=request,
|
||||
user_id=current_user.user_id,
|
||||
workspace_id=workspace_id,
|
||||
)
|
||||
|
||||
|
||||
@creatives_router.delete('/{creative_id}')
|
||||
async def delete_creative(
|
||||
creative_id: uuid.UUID,
|
||||
workspace_id: uuid.UUID,
|
||||
current_user: Annotated[JWTPayload, Depends(deps.get_current_user)],
|
||||
) -> None:
|
||||
input = dto.DeleteCreativeInput(
|
||||
creative_id=creative_id,
|
||||
user_id=current_user.user_id,
|
||||
workspace_id=workspace_id,
|
||||
)
|
||||
|
||||
await deps.get_usecase().delete_creative(input)
|
||||
|
||||
@@ -7,18 +7,20 @@ from fastapi.routing import APIRouter
|
||||
from src import deps, dto
|
||||
from src.adapter.jwt import JWTPayload
|
||||
|
||||
external_channels_router = APIRouter(prefix='/external_channels', tags=['external_channels'])
|
||||
external_channels_router = APIRouter(prefix='/workspaces/{workspace_id}/external_channels', tags=['external_channels'])
|
||||
|
||||
|
||||
@external_channels_router.get('/target/{target_channel_id}')
|
||||
async def get_external_channels(
|
||||
target_channel_id: uuid.UUID,
|
||||
workspace_id: uuid.UUID,
|
||||
current_user: Annotated[JWTPayload, Depends(deps.get_current_user)],
|
||||
) -> dto.GetExternalChannelsOutput:
|
||||
"""Get all external channels for a specific target channel."""
|
||||
input = dto.GetExternalChannelsInput(
|
||||
target_channel_id=target_channel_id,
|
||||
user_id=current_user.user_id,
|
||||
workspace_id=workspace_id,
|
||||
)
|
||||
|
||||
return await deps.get_usecase().get_external_channels(input=input)
|
||||
@@ -27,12 +29,14 @@ async def get_external_channels(
|
||||
@external_channels_router.post('')
|
||||
async def create_external_channel(
|
||||
request: dto.CreateExternalChannelInput,
|
||||
workspace_id: uuid.UUID,
|
||||
current_user: Annotated[JWTPayload, Depends(deps.get_current_user)],
|
||||
) -> dto.ExternalChannelOutput:
|
||||
"""Create a new external channel and link it to target channels."""
|
||||
return await deps.get_usecase().create_external_channel(
|
||||
input=request,
|
||||
user_id=current_user.user_id,
|
||||
workspace_id=workspace_id,
|
||||
)
|
||||
|
||||
|
||||
@@ -40,6 +44,7 @@ async def create_external_channel(
|
||||
async def update_external_channel(
|
||||
channel_id: uuid.UUID,
|
||||
request: dto.UpdateExternalChannelInput,
|
||||
workspace_id: uuid.UUID,
|
||||
current_user: Annotated[JWTPayload, Depends(deps.get_current_user)],
|
||||
) -> dto.ExternalChannelOutput:
|
||||
"""Update external channel fields (title, username, description, subscribers_count)."""
|
||||
@@ -47,6 +52,7 @@ async def update_external_channel(
|
||||
channel_id=channel_id,
|
||||
input=request,
|
||||
user_id=current_user.user_id,
|
||||
workspace_id=workspace_id,
|
||||
)
|
||||
|
||||
|
||||
@@ -54,6 +60,7 @@ async def update_external_channel(
|
||||
async def update_external_channel_links(
|
||||
channel_id: uuid.UUID,
|
||||
request: dto.UpdateExternalChannelLinksInput,
|
||||
workspace_id: uuid.UUID,
|
||||
current_user: Annotated[JWTPayload, Depends(deps.get_current_user)],
|
||||
) -> dto.ExternalChannelOutput:
|
||||
"""
|
||||
@@ -65,18 +72,21 @@ async def update_external_channel_links(
|
||||
channel_id=channel_id,
|
||||
input=request,
|
||||
user_id=current_user.user_id,
|
||||
workspace_id=workspace_id,
|
||||
)
|
||||
|
||||
|
||||
@external_channels_router.delete('/{channel_id}')
|
||||
async def delete_external_channel(
|
||||
channel_id: uuid.UUID,
|
||||
workspace_id: uuid.UUID,
|
||||
current_user: Annotated[JWTPayload, Depends(deps.get_current_user)],
|
||||
) -> None:
|
||||
"""Delete an external channel."""
|
||||
input = dto.DeleteExternalChannelInput(
|
||||
channel_id=channel_id,
|
||||
user_id=current_user.user_id,
|
||||
workspace_id=workspace_id,
|
||||
)
|
||||
|
||||
await deps.get_usecase().delete_external_channel(input=input)
|
||||
|
||||
@@ -7,11 +7,12 @@ from fastapi.routing import APIRouter
|
||||
from src import deps, dto
|
||||
from src.adapter.jwt import JWTPayload
|
||||
|
||||
placements_router = APIRouter(prefix='/placements', tags=['placements'])
|
||||
placements_router = APIRouter(prefix='/workspaces/{workspace_id}/placements', tags=['placements'])
|
||||
|
||||
|
||||
@placements_router.get('')
|
||||
async def list_placements(
|
||||
workspace_id: uuid.UUID,
|
||||
current_user: Annotated[JWTPayload, Depends(deps.get_current_user)],
|
||||
target_channel_id: uuid.UUID | None = None,
|
||||
external_channel_id: uuid.UUID | None = None,
|
||||
@@ -20,6 +21,7 @@ async def list_placements(
|
||||
) -> dto.GetPlacementsOutput:
|
||||
input = dto.GetPlacementsInput(
|
||||
user_id=current_user.user_id,
|
||||
workspace_id=workspace_id,
|
||||
target_channel_id=target_channel_id,
|
||||
external_channel_id=external_channel_id,
|
||||
creative_id=creative_id,
|
||||
@@ -32,11 +34,13 @@ async def list_placements(
|
||||
@placements_router.get('/{placement_id}')
|
||||
async def get_placement(
|
||||
placement_id: uuid.UUID,
|
||||
workspace_id: uuid.UUID,
|
||||
current_user: Annotated[JWTPayload, Depends(deps.get_current_user)],
|
||||
) -> dto.PlacementOutput:
|
||||
input = dto.GetPlacementInput(
|
||||
placement_id=placement_id,
|
||||
user_id=current_user.user_id,
|
||||
workspace_id=workspace_id,
|
||||
)
|
||||
|
||||
return await deps.get_usecase().get_placement(input=input)
|
||||
@@ -45,32 +49,37 @@ async def get_placement(
|
||||
@placements_router.post('')
|
||||
async def create_placement(
|
||||
request: dto.CreatePlacementInput,
|
||||
workspace_id: uuid.UUID,
|
||||
current_user: Annotated[JWTPayload, Depends(deps.get_current_user)],
|
||||
) -> dto.PlacementOutput:
|
||||
return await deps.get_usecase().create_placement(request, current_user.user_id)
|
||||
return await deps.get_usecase().create_placement(request, current_user.user_id, workspace_id)
|
||||
|
||||
|
||||
@placements_router.patch('/{placement_id}')
|
||||
async def update_placement(
|
||||
placement_id: uuid.UUID,
|
||||
request: dto.UpdatePlacementInput,
|
||||
workspace_id: uuid.UUID,
|
||||
current_user: Annotated[JWTPayload, Depends(deps.get_current_user)],
|
||||
) -> dto.PlacementOutput:
|
||||
return await deps.get_usecase().update_placement(
|
||||
placement_id=placement_id,
|
||||
input=request,
|
||||
user_id=current_user.user_id,
|
||||
workspace_id=workspace_id,
|
||||
)
|
||||
|
||||
|
||||
@placements_router.delete('/{placement_id}')
|
||||
async def delete_placement(
|
||||
placement_id: uuid.UUID,
|
||||
workspace_id: uuid.UUID,
|
||||
current_user: Annotated[JWTPayload, Depends(deps.get_current_user)],
|
||||
) -> None:
|
||||
input = dto.DeletePlacementInput(
|
||||
placement_id=placement_id,
|
||||
user_id=current_user.user_id,
|
||||
workspace_id=workspace_id,
|
||||
)
|
||||
|
||||
await deps.get_usecase().delete_placement(input)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import uuid
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import Depends
|
||||
@@ -6,15 +7,17 @@ from fastapi.routing import APIRouter
|
||||
from src import deps, dto
|
||||
from src.adapter.jwt import JWTPayload
|
||||
|
||||
target_channels_router = APIRouter(prefix='/target_channels', tags=['target_channels'])
|
||||
target_channels_router = APIRouter(prefix='/workspaces/{workspace_id}/target_channels', tags=['target_channels'])
|
||||
|
||||
|
||||
@target_channels_router.get('')
|
||||
async def get_user_target_chans(
|
||||
workspace_id: uuid.UUID,
|
||||
current_user: Annotated[JWTPayload, Depends(deps.get_current_user)],
|
||||
) -> dto.GetUserTargetChansOutput:
|
||||
input = dto.GetUserTargetChansInput(
|
||||
user_id=current_user.user_id,
|
||||
workspace_id=workspace_id,
|
||||
)
|
||||
|
||||
return await deps.get_usecase().get_user_target_chans(input=input)
|
||||
|
||||
@@ -8,12 +8,13 @@ from fastapi.routing import APIRouter
|
||||
from src import deps, dto
|
||||
from src.adapter.jwt import JWTPayload
|
||||
|
||||
views_router = APIRouter(prefix='/placements/{placement_id}/views', tags=['views'])
|
||||
views_router = APIRouter(prefix='/workspaces/{workspace_id}/placements/{placement_id}/views', tags=['views'])
|
||||
|
||||
|
||||
@views_router.get('/history')
|
||||
async def get_views_history(
|
||||
placement_id: uuid.UUID,
|
||||
workspace_id: uuid.UUID,
|
||||
current_user: Annotated[JWTPayload, Depends(deps.get_current_user)],
|
||||
from_date: datetime.datetime | None = None,
|
||||
to_date: datetime.datetime | None = None,
|
||||
@@ -21,23 +22,9 @@ async def get_views_history(
|
||||
input_data = dto.GetViewsHistoryInput(
|
||||
placement_id=placement_id,
|
||||
user_id=current_user.user_id,
|
||||
workspace_id=workspace_id,
|
||||
from_date=from_date,
|
||||
to_date=to_date,
|
||||
)
|
||||
|
||||
return await deps.get_usecase().get_views_history(input=input_data)
|
||||
|
||||
|
||||
@views_router.post('/manual')
|
||||
async def update_views_manually(
|
||||
placement_id: uuid.UUID,
|
||||
views_count: int,
|
||||
current_user: Annotated[JWTPayload, Depends(deps.get_current_user)],
|
||||
) -> dto.PlacementOutput:
|
||||
input_data = dto.UpdateViewsManuallyInput(
|
||||
placement_id=placement_id,
|
||||
user_id=current_user.user_id,
|
||||
views_count=views_count,
|
||||
)
|
||||
|
||||
return await deps.get_usecase().update_views_manually(input=input_data)
|
||||
|
||||
42
src/controller/http/workspaces.py
Normal file
42
src/controller/http/workspaces.py
Normal file
@@ -0,0 +1,42 @@
|
||||
import uuid
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import Depends
|
||||
from fastapi.routing import APIRouter
|
||||
|
||||
from src import deps, dto
|
||||
from src.adapter.jwt import JWTPayload
|
||||
|
||||
workspaces_router = APIRouter(prefix='/workspaces', tags=['workspaces'])
|
||||
|
||||
|
||||
@workspaces_router.get('')
|
||||
async def list_workspaces(
|
||||
current_user: Annotated[JWTPayload, Depends(deps.get_current_user)],
|
||||
) -> dto.GetWorkspacesOutput:
|
||||
return await deps.get_usecase().get_workspaces(current_user.user_id)
|
||||
|
||||
|
||||
@workspaces_router.post('')
|
||||
async def create_workspace(
|
||||
request: dto.CreateWorkspaceInput,
|
||||
current_user: Annotated[JWTPayload, Depends(deps.get_current_user)],
|
||||
) -> dto.CreateWorkspaceOutput:
|
||||
return await deps.get_usecase().create_workspace(current_user.user_id, request)
|
||||
|
||||
|
||||
@workspaces_router.patch('/{workspace_id}')
|
||||
async def update_workspace(
|
||||
workspace_id: uuid.UUID,
|
||||
request: dto.UpdateWorkspaceInput,
|
||||
current_user: Annotated[JWTPayload, Depends(deps.get_current_user)],
|
||||
) -> dto.WorkspaceMembershipOutput:
|
||||
return await deps.get_usecase().update_workspace(workspace_id, current_user.user_id, request)
|
||||
|
||||
|
||||
@workspaces_router.delete('/{workspace_id}')
|
||||
async def delete_workspace(
|
||||
workspace_id: uuid.UUID,
|
||||
current_user: Annotated[JWTPayload, Depends(deps.get_current_user)],
|
||||
) -> None:
|
||||
await deps.get_usecase().delete_workspace(workspace_id, current_user.user_id)
|
||||
Reference in New Issue
Block a user