Skip to content

Commit 3ec5782

Browse files
committed
🐛 bug hunting
1 parent 6b865d6 commit 3ec5782

File tree

3 files changed

+98
-86
lines changed

3 files changed

+98
-86
lines changed

app/api/cms.py

Lines changed: 80 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@
1111
from app.api.dependencies.security import (
1212
get_current_active_superuser_or_backend_service_account,
1313
get_current_active_user,
14+
get_current_active_user_or_service_account,
1415
)
15-
from app.models import ContentType
16+
from app.models import ContentType, User
1617
from app.schemas.cms import (
1718
BulkContentRequest,
1819
BulkContentResponse,
@@ -105,6 +106,60 @@ async def list_content(
105106
)
106107

107108

109+
@router.get("/content/{content_type}", response_model=ContentResponse, deprecated=True)
110+
async def get_cms_content_by_type(
111+
session: DBSessionDep,
112+
content_type: ContentType = Path(description="What type of content to return"),
113+
query: str | None = Query(
114+
None, description="A query string to match against content"
115+
),
116+
jsonpath_match: str = Query(
117+
None,
118+
description="Filter using a JSONPath over the content. The resulting value must be a boolean expression.",
119+
),
120+
pagination: PaginatedQueryParams = Depends(),
121+
):
122+
"""
123+
DEPRECATED: Get a filtered and paginated list of content by content type.
124+
125+
Use GET /content with content_type query parameter instead.
126+
This endpoint will be removed in a future version.
127+
"""
128+
logger.warning(
129+
"DEPRECATED endpoint accessed",
130+
endpoint="GET /content/{content_type}",
131+
replacement="GET /content?content_type=...",
132+
content_type=content_type,
133+
)
134+
135+
try:
136+
data = await crud.content.aget_all_with_optional_filters(
137+
session,
138+
content_type=content_type,
139+
search=query,
140+
jsonpath_match=jsonpath_match,
141+
skip=pagination.skip,
142+
limit=pagination.limit,
143+
)
144+
logger.info(
145+
"Retrieved digital content",
146+
content_type=content_type,
147+
query=query,
148+
data=data,
149+
jsonpath_match=jsonpath_match,
150+
skip=pagination.skip,
151+
limit=pagination.limit,
152+
)
153+
except ValueError as e:
154+
raise HTTPException(
155+
status_code=status.HTTP_400_BAD_REQUEST, detail=str(e)
156+
) from e
157+
158+
return ContentResponse(
159+
pagination=Pagination(**pagination.to_dict(), total=None), data=data
160+
)
161+
162+
108163
@router.get("/content/{content_id}", response_model=ContentDetail)
109164
async def get_content(
110165
session: DBSessionDep,
@@ -125,11 +180,20 @@ async def get_content(
125180
async def create_content(
126181
session: DBSessionDep,
127182
content_data: ContentCreate,
128-
current_user=Security(get_current_active_user),
183+
current_user_or_service_account=Security(
184+
get_current_active_user_or_service_account
185+
),
129186
):
130187
"""Create new content."""
188+
# For service accounts, set the creator to null.
189+
created_by = (
190+
current_user_or_service_account.id
191+
if isinstance(current_user_or_service_account, User)
192+
else None
193+
)
194+
131195
content = await crud.content.acreate(
132-
session, obj_in=content_data, created_by=current_user.id
196+
session, obj_in=content_data, created_by=created_by
133197
)
134198
logger.info("Created content", content_id=content.id, type=content.type)
135199
return content
@@ -176,7 +240,7 @@ async def update_content_status(
176240
session: DBSessionDep,
177241
content_id: UUID = Path(description="Content ID"),
178242
status_update: ContentStatusUpdate = Body(...),
179-
current_user=Security(get_current_active_user),
243+
current_user=Security(get_current_active_user_or_service_account),
180244
):
181245
"""Update content workflow status."""
182246
content = await crud.content.aget(session, content_id)
@@ -210,7 +274,7 @@ async def update_content_status(
210274
async def bulk_content_operations(
211275
session: DBSessionDep,
212276
bulk_request: BulkContentRequest,
213-
current_user=Security(get_current_active_user),
277+
current_user=Security(get_current_active_user_or_service_account),
214278
):
215279
"""Perform bulk operations on content."""
216280
# Implementation would handle bulk create/update/delete
@@ -363,12 +427,18 @@ async def get_flow(
363427
async def create_flow(
364428
session: DBSessionDep,
365429
flow_data: FlowCreate,
366-
current_user=Security(get_current_active_user),
430+
current_user_or_service_account=Security(
431+
get_current_active_user_or_service_account
432+
),
367433
):
368434
"""Create new flow."""
369-
flow = await crud.flow.acreate(
370-
session, obj_in=flow_data, created_by=current_user.id
435+
436+
created_by = (
437+
current_user_or_service_account.id
438+
if isinstance(current_user_or_service_account, User)
439+
else None
371440
)
441+
flow = await crud.flow.acreate(session, obj_in=flow_data, created_by=created_by)
372442
logger.info("Created flow", flow_id=flow.id, name=flow.name)
373443
return flow
374444

@@ -396,7 +466,7 @@ async def publish_flow(
396466
session: DBSessionDep,
397467
flow_id: UUID = Path(description="Flow ID"),
398468
publish_request: FlowPublishRequest = Body(...),
399-
current_user=Security(get_current_active_user),
469+
current_user=Security(get_current_active_user_or_service_account),
400470
):
401471
"""Publish or unpublish a flow."""
402472
flow = await crud.flow.aget(session, flow_id)
@@ -426,7 +496,7 @@ async def clone_flow(
426496
session: DBSessionDep,
427497
flow_id: UUID = Path(description="Flow ID"),
428498
clone_request: FlowCloneRequest = Body(...),
429-
current_user=Security(get_current_active_user),
499+
current_user=Security(get_current_active_user_or_service_account),
430500
):
431501
"""Clone an existing flow."""
432502
source_flow = await crud.flow.aget(session, flow_id)
@@ -662,58 +732,3 @@ async def delete_flow_connection(
662732

663733
await crud.flow_connection.aremove(session, id=connection_id)
664734
logger.info("Deleted flow connection", connection_id=connection_id, flow_id=flow_id)
665-
666-
667-
# Legacy endpoint - DEPRECATED - Use GET /content with content_type query param instead
668-
@router.get("/content/{content_type}", response_model=ContentResponse, deprecated=True)
669-
async def get_cms_content_by_type(
670-
session: DBSessionDep,
671-
content_type: ContentType = Path(description="What type of content to return"),
672-
query: str | None = Query(
673-
None, description="A query string to match against content"
674-
),
675-
jsonpath_match: str = Query(
676-
None,
677-
description="Filter using a JSONPath over the content. The resulting value must be a boolean expression.",
678-
),
679-
pagination: PaginatedQueryParams = Depends(),
680-
):
681-
"""
682-
DEPRECATED: Get a filtered and paginated list of content by content type.
683-
684-
Use GET /content with content_type query parameter instead.
685-
This endpoint will be removed in a future version.
686-
"""
687-
logger.warning(
688-
"DEPRECATED endpoint accessed",
689-
endpoint="GET /content/{content_type}",
690-
replacement="GET /content?content_type=...",
691-
content_type=content_type,
692-
)
693-
694-
try:
695-
data = await crud.content.aget_all_with_optional_filters(
696-
session,
697-
content_type=content_type,
698-
search=query,
699-
jsonpath_match=jsonpath_match,
700-
skip=pagination.skip,
701-
limit=pagination.limit,
702-
)
703-
logger.info(
704-
"Retrieved digital content",
705-
content_type=content_type,
706-
query=query,
707-
data=data,
708-
jsonpath_match=jsonpath_match,
709-
skip=pagination.skip,
710-
limit=pagination.limit,
711-
)
712-
except ValueError as e:
713-
raise HTTPException(
714-
status_code=status.HTTP_400_BAD_REQUEST, detail=str(e)
715-
) from e
716-
717-
return ContentResponse(
718-
pagination=Pagination(**pagination.to_dict(), total=None), data=data
719-
)

app/schemas/cms.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
class ContentCreate(BaseModel):
2020
type: ContentType
2121
content: Dict[str, Any]
22-
meta_data: Optional[Dict[str, Any]] = {}
22+
info: Optional[Dict[str, Any]] = {}
2323
tags: Optional[List[str]] = []
2424
is_active: Optional[bool] = True
2525
status: Optional[ContentStatus] = ContentStatus.DRAFT
@@ -28,7 +28,7 @@ class ContentCreate(BaseModel):
2828
class ContentUpdate(BaseModel):
2929
type: Optional[ContentType] = None
3030
content: Optional[Dict[str, Any]] = None
31-
meta_data: Optional[Dict[str, Any]] = None
31+
info: Optional[Dict[str, Any]] = None
3232
tags: Optional[List[str]] = None
3333
is_active: Optional[bool] = None
3434
status: Optional[ContentStatus] = None
@@ -49,7 +49,7 @@ class ContentBrief(BaseModel):
4949

5050
class ContentDetail(ContentBrief):
5151
content: Dict[str, Any]
52-
meta_data: Dict[str, Any]
52+
info: Dict[str, Any]
5353
created_by: Optional[UUID4] = None
5454

5555

@@ -104,7 +104,7 @@ class FlowCreate(BaseModel):
104104
version: str = Field(..., max_length=50)
105105
flow_data: Dict[str, Any]
106106
entry_node_id: str = Field(..., max_length=255)
107-
meta_data: Optional[Dict[str, Any]] = {}
107+
info: Optional[Dict[str, Any]] = {}
108108

109109

110110
class FlowUpdate(BaseModel):
@@ -113,7 +113,7 @@ class FlowUpdate(BaseModel):
113113
version: Optional[str] = Field(None, max_length=50)
114114
flow_data: Optional[Dict[str, Any]] = None
115115
entry_node_id: Optional[str] = Field(None, max_length=255)
116-
meta_data: Optional[Dict[str, Any]] = None
116+
info: Optional[Dict[str, Any]] = None
117117
is_active: Optional[bool] = None
118118

119119

@@ -134,7 +134,7 @@ class FlowDetail(FlowBrief):
134134
description: Optional[str] = None
135135
flow_data: Dict[str, Any]
136136
entry_node_id: str
137-
meta_data: Dict[str, Any]
137+
info: Dict[str, Any]
138138
created_by: Optional[UUID4] = None
139139
published_by: Optional[UUID4] = None
140140

@@ -159,15 +159,15 @@ class NodeCreate(BaseModel):
159159
template: Optional[str] = Field(None, max_length=100)
160160
content: Dict[str, Any]
161161
position: Optional[Dict[str, Any]] = {"x": 0, "y": 0}
162-
meta_data: Optional[Dict[str, Any]] = {}
162+
info: Optional[Dict[str, Any]] = {}
163163

164164

165165
class NodeUpdate(BaseModel):
166166
node_type: Optional[NodeType] = None
167167
template: Optional[str] = Field(None, max_length=100)
168168
content: Optional[Dict[str, Any]] = None
169169
position: Optional[Dict[str, Any]] = None
170-
meta_data: Optional[Dict[str, Any]] = None
170+
info: Optional[Dict[str, Any]] = None
171171

172172

173173
class NodeDetail(BaseModel):
@@ -178,7 +178,7 @@ class NodeDetail(BaseModel):
178178
template: Optional[str] = None
179179
content: Dict[str, Any]
180180
position: Dict[str, Any]
181-
meta_data: Dict[str, Any]
181+
info: Dict[str, Any]
182182
created_at: datetime
183183
updated_at: datetime
184184

@@ -199,7 +199,7 @@ class ConnectionCreate(BaseModel):
199199
target_node_id: str = Field(..., max_length=255)
200200
connection_type: ConnectionType
201201
conditions: Optional[Dict[str, Any]] = {}
202-
meta_data: Optional[Dict[str, Any]] = {}
202+
info: Optional[Dict[str, Any]] = {}
203203

204204

205205
class ConnectionDetail(BaseModel):
@@ -209,7 +209,7 @@ class ConnectionDetail(BaseModel):
209209
target_node_id: str
210210
connection_type: ConnectionType
211211
conditions: Dict[str, Any]
212-
meta_data: Dict[str, Any]
212+
info: Dict[str, Any]
213213
created_at: datetime
214214

215215
model_config = ConfigDict(from_attributes=True)
@@ -233,7 +233,7 @@ class SessionDetail(BaseModel):
233233
session_token: str
234234
current_node_id: Optional[str] = None
235235
state: Dict[str, Any]
236-
meta_data: Dict[str, Any]
236+
info: Dict[str, Any]
237237
started_at: datetime
238238
last_activity_at: datetime
239239
ended_at: Optional[datetime] = None

app/services/event_listener.py

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -55,15 +55,12 @@ async def connect(self) -> None:
5555
"""Establish connection to PostgreSQL for listening to notifications."""
5656
try:
5757
# Parse the database URL for asyncpg connection
58-
db_url = str(self.settings.SQLALCHEMY_DATABASE_URI)
59-
if db_url.startswith("postgresql://"):
60-
db_url = db_url.replace("postgresql://", "postgresql+asyncpg://", 1)
61-
elif not db_url.startswith("postgresql+asyncpg://"):
62-
# Fallback to direct connection params
63-
db_url = "postgresql://postgres:password@localhost/postgres"
64-
65-
# Remove the +asyncpg part for asyncpg.connect
66-
connection_url = db_url.replace("postgresql+asyncpg://", "postgresql://")
58+
db_url = self.settings.SQLALCHEMY_ASYNC_URI
59+
60+
# Remove the +asyncpg part for asyncpg.connect and unhide the password
61+
connection_url = db_url.render_as_string(False).replace(
62+
"postgresql+asyncpg://", "postgresql://"
63+
)
6764

6865
self.connection = await asyncpg.connect(connection_url)
6966
logger.info("Connected to PostgreSQL for event listening")

0 commit comments

Comments
 (0)