fix(city-service): align incidents API with actual DB schema
- Use correct column names: created_by_dais_id, target_scope_type, assigned_to_dais_id - Add UUID parsing for incident IDs - Fix incident_history to use actor_dais_id and new_value columns
This commit is contained in:
@@ -615,32 +615,34 @@ async def get_incidents(
|
|||||||
|
|
||||||
if status:
|
if status:
|
||||||
params.append(status)
|
params.append(status)
|
||||||
where_clauses.append(f"status = ${len(params)}")
|
where_clauses.append(f"i.status = ${len(params)}")
|
||||||
|
|
||||||
if priority:
|
if priority:
|
||||||
params.append(priority)
|
params.append(priority)
|
||||||
where_clauses.append(f"priority = ${len(params)}")
|
where_clauses.append(f"i.priority = ${len(params)}")
|
||||||
|
|
||||||
if scope_type:
|
if scope_type:
|
||||||
params.append(scope_type)
|
params.append(scope_type)
|
||||||
where_clauses.append(f"scope_type = ${len(params)}")
|
where_clauses.append(f"i.target_scope_type = ${len(params)}")
|
||||||
|
|
||||||
if scope_id:
|
if scope_id:
|
||||||
params.append(scope_id)
|
params.append(scope_id)
|
||||||
where_clauses.append(f"scope_id = ${len(params)}")
|
where_clauses.append(f"i.target_scope_id = ${len(params)}")
|
||||||
|
|
||||||
where_sql = " AND ".join(where_clauses) if where_clauses else "1=1"
|
where_sql = " AND ".join(where_clauses) if where_clauses else "1=1"
|
||||||
|
|
||||||
query = f"""
|
query = f"""
|
||||||
SELECT
|
SELECT
|
||||||
i.id, i.title, i.description, i.status, i.priority,
|
i.id::text as id, i.title, i.description, i.status::text, i.priority::text,
|
||||||
i.scope_type, i.scope_id, i.escalation_level,
|
i.target_scope_type::text as scope_type, i.target_scope_id as scope_id,
|
||||||
i.reporter_id, i.assigned_to, i.created_at, i.updated_at,
|
i.escalation_level::text,
|
||||||
|
i.created_by_dais_id as reporter_id, i.assigned_to_dais_id as assigned_to,
|
||||||
|
i.created_at, i.updated_at,
|
||||||
r.display_name as reporter_name,
|
r.display_name as reporter_name,
|
||||||
a.display_name as assignee_name
|
a.display_name as assignee_name
|
||||||
FROM incidents i
|
FROM incidents i
|
||||||
LEFT JOIN agents r ON i.reporter_id = r.id
|
LEFT JOIN agents r ON i.created_by_dais_id = r.id
|
||||||
LEFT JOIN agents a ON i.assigned_to = a.id
|
LEFT JOIN agents a ON i.assigned_to_dais_id = a.id
|
||||||
WHERE {where_sql}
|
WHERE {where_sql}
|
||||||
ORDER BY
|
ORDER BY
|
||||||
CASE i.priority
|
CASE i.priority
|
||||||
@@ -663,20 +665,28 @@ async def get_incident_by_id(incident_id: str) -> Optional[dict]:
|
|||||||
"""Отримати інцидент за ID"""
|
"""Отримати інцидент за ID"""
|
||||||
pool = await get_pool()
|
pool = await get_pool()
|
||||||
|
|
||||||
|
import uuid
|
||||||
|
try:
|
||||||
|
incident_uuid = uuid.UUID(incident_id)
|
||||||
|
except ValueError:
|
||||||
|
return None
|
||||||
|
|
||||||
query = """
|
query = """
|
||||||
SELECT
|
SELECT
|
||||||
i.id, i.title, i.description, i.status, i.priority,
|
i.id::text as id, i.title, i.description, i.status::text, i.priority::text,
|
||||||
i.scope_type, i.scope_id, i.escalation_level,
|
i.target_scope_type::text as scope_type, i.target_scope_id as scope_id,
|
||||||
i.reporter_id, i.assigned_to, i.created_at, i.updated_at,
|
i.escalation_level::text,
|
||||||
|
i.created_by_dais_id as reporter_id, i.assigned_to_dais_id as assigned_to,
|
||||||
|
i.created_at, i.updated_at,
|
||||||
r.display_name as reporter_name,
|
r.display_name as reporter_name,
|
||||||
a.display_name as assignee_name
|
a.display_name as assignee_name
|
||||||
FROM incidents i
|
FROM incidents i
|
||||||
LEFT JOIN agents r ON i.reporter_id = r.id
|
LEFT JOIN agents r ON i.created_by_dais_id = r.id
|
||||||
LEFT JOIN agents a ON i.assigned_to = a.id
|
LEFT JOIN agents a ON i.assigned_to_dais_id = a.id
|
||||||
WHERE i.id = $1
|
WHERE i.id = $1
|
||||||
"""
|
"""
|
||||||
|
|
||||||
row = await pool.fetchrow(query, incident_id)
|
row = await pool.fetchrow(query, incident_uuid)
|
||||||
return dict(row) if row else None
|
return dict(row) if row else None
|
||||||
|
|
||||||
|
|
||||||
@@ -691,28 +701,33 @@ async def create_incident(
|
|||||||
"""Створити новий інцидент"""
|
"""Створити новий інцидент"""
|
||||||
pool = await get_pool()
|
pool = await get_pool()
|
||||||
|
|
||||||
incident_id = generate_id("inc")
|
# Default scope if not provided
|
||||||
|
if not scope_type:
|
||||||
|
scope_type = "city"
|
||||||
|
if not scope_id:
|
||||||
|
scope_id = "daarion"
|
||||||
|
|
||||||
query = """
|
query = """
|
||||||
INSERT INTO incidents (
|
INSERT INTO incidents (
|
||||||
id, title, description, status, priority,
|
title, description, status, priority,
|
||||||
scope_type, scope_id, escalation_level,
|
target_scope_type, target_scope_id, escalation_level,
|
||||||
reporter_id, created_at, updated_at
|
created_by_dais_id, created_at, updated_at
|
||||||
)
|
)
|
||||||
VALUES ($1, $2, $3, 'open', $4, $5, $6, 'microdao', $7, NOW(), NOW())
|
VALUES ($1, $2, 'open', $3::incident_priority, $4::target_scope_type, $5, 'microdao', $6, NOW(), NOW())
|
||||||
RETURNING id, title, status, priority, created_at
|
RETURNING id::text, title, status::text, priority::text, created_at
|
||||||
"""
|
"""
|
||||||
|
|
||||||
row = await pool.fetchrow(
|
row = await pool.fetchrow(
|
||||||
query,
|
query,
|
||||||
incident_id, title, description, priority,
|
title, description, priority,
|
||||||
scope_type, scope_id, reporter_id
|
scope_type, scope_id, reporter_id
|
||||||
)
|
)
|
||||||
|
|
||||||
# Log to history
|
if row:
|
||||||
await add_incident_history(incident_id, reporter_id, "created", {"title": title})
|
# Log to history
|
||||||
|
await add_incident_history(row["id"], reporter_id, "created", {"title": title})
|
||||||
|
|
||||||
return dict(row)
|
return dict(row) if row else {}
|
||||||
|
|
||||||
|
|
||||||
async def update_incident_status(
|
async def update_incident_status(
|
||||||
@@ -723,14 +738,20 @@ async def update_incident_status(
|
|||||||
"""Оновити статус інциденту"""
|
"""Оновити статус інциденту"""
|
||||||
pool = await get_pool()
|
pool = await get_pool()
|
||||||
|
|
||||||
|
import uuid
|
||||||
|
try:
|
||||||
|
incident_uuid = uuid.UUID(incident_id)
|
||||||
|
except ValueError:
|
||||||
|
return None
|
||||||
|
|
||||||
query = """
|
query = """
|
||||||
UPDATE incidents
|
UPDATE incidents
|
||||||
SET status = $2, updated_at = NOW()
|
SET status = $2::incident_status, updated_at = NOW()
|
||||||
WHERE id = $1
|
WHERE id = $1
|
||||||
RETURNING id, title, status, priority, updated_at
|
RETURNING id::text, title, status::text, priority::text, updated_at
|
||||||
"""
|
"""
|
||||||
|
|
||||||
row = await pool.fetchrow(query, incident_id, new_status)
|
row = await pool.fetchrow(query, incident_uuid, new_status)
|
||||||
|
|
||||||
if row:
|
if row:
|
||||||
await add_incident_history(incident_id, actor_id, "status_changed", {"new_status": new_status})
|
await add_incident_history(incident_id, actor_id, "status_changed", {"new_status": new_status})
|
||||||
@@ -746,14 +767,20 @@ async def assign_incident(
|
|||||||
"""Призначити інцидент на агента"""
|
"""Призначити інцидент на агента"""
|
||||||
pool = await get_pool()
|
pool = await get_pool()
|
||||||
|
|
||||||
|
import uuid
|
||||||
|
try:
|
||||||
|
incident_uuid = uuid.UUID(incident_id)
|
||||||
|
except ValueError:
|
||||||
|
return None
|
||||||
|
|
||||||
query = """
|
query = """
|
||||||
UPDATE incidents
|
UPDATE incidents
|
||||||
SET assigned_to = $2, status = 'in_progress', updated_at = NOW()
|
SET assigned_to_dais_id = $2, status = 'in_progress', updated_at = NOW()
|
||||||
WHERE id = $1
|
WHERE id = $1
|
||||||
RETURNING id, title, status, assigned_to, updated_at
|
RETURNING id::text, title, status::text, assigned_to_dais_id as assigned_to, updated_at
|
||||||
"""
|
"""
|
||||||
|
|
||||||
row = await pool.fetchrow(query, incident_id, assignee_id)
|
row = await pool.fetchrow(query, incident_uuid, assignee_id)
|
||||||
|
|
||||||
if row:
|
if row:
|
||||||
await add_incident_history(incident_id, actor_id, "assigned", {"assignee_id": assignee_id})
|
await add_incident_history(incident_id, actor_id, "assigned", {"assignee_id": assignee_id})
|
||||||
@@ -768,10 +795,16 @@ async def escalate_incident(
|
|||||||
"""Ескалювати інцидент на вищий рівень"""
|
"""Ескалювати інцидент на вищий рівень"""
|
||||||
pool = await get_pool()
|
pool = await get_pool()
|
||||||
|
|
||||||
|
import uuid
|
||||||
|
try:
|
||||||
|
incident_uuid = uuid.UUID(incident_id)
|
||||||
|
except ValueError:
|
||||||
|
return None
|
||||||
|
|
||||||
# Get current level
|
# Get current level
|
||||||
current = await pool.fetchrow(
|
current = await pool.fetchrow(
|
||||||
"SELECT escalation_level FROM incidents WHERE id = $1",
|
"SELECT escalation_level::text FROM incidents WHERE id = $1",
|
||||||
incident_id
|
incident_uuid
|
||||||
)
|
)
|
||||||
|
|
||||||
if not current:
|
if not current:
|
||||||
@@ -788,12 +821,12 @@ async def escalate_incident(
|
|||||||
|
|
||||||
query = """
|
query = """
|
||||||
UPDATE incidents
|
UPDATE incidents
|
||||||
SET escalation_level = $2, updated_at = NOW()
|
SET escalation_level = $2::escalation_level, updated_at = NOW()
|
||||||
WHERE id = $1
|
WHERE id = $1
|
||||||
RETURNING id, title, status, escalation_level, updated_at
|
RETURNING id::text, title, status::text, escalation_level::text, updated_at
|
||||||
"""
|
"""
|
||||||
|
|
||||||
row = await pool.fetchrow(query, incident_id, new_level)
|
row = await pool.fetchrow(query, incident_uuid, new_level)
|
||||||
|
|
||||||
if row:
|
if row:
|
||||||
await add_incident_history(incident_id, actor_id, "escalated", {"new_level": new_level})
|
await add_incident_history(incident_id, actor_id, "escalated", {"new_level": new_level})
|
||||||
@@ -809,14 +842,20 @@ async def resolve_incident(
|
|||||||
"""Вирішити інцидент"""
|
"""Вирішити інцидент"""
|
||||||
pool = await get_pool()
|
pool = await get_pool()
|
||||||
|
|
||||||
|
import uuid
|
||||||
|
try:
|
||||||
|
incident_uuid = uuid.UUID(incident_id)
|
||||||
|
except ValueError:
|
||||||
|
return None
|
||||||
|
|
||||||
query = """
|
query = """
|
||||||
UPDATE incidents
|
UPDATE incidents
|
||||||
SET status = 'resolved', updated_at = NOW()
|
SET status = 'resolved', resolution = $2, resolved_at = NOW(), updated_at = NOW()
|
||||||
WHERE id = $1
|
WHERE id = $1
|
||||||
RETURNING id, title, status, updated_at
|
RETURNING id::text, title, status::text, updated_at
|
||||||
"""
|
"""
|
||||||
|
|
||||||
row = await pool.fetchrow(query, incident_id)
|
row = await pool.fetchrow(query, incident_uuid, resolution)
|
||||||
|
|
||||||
if row:
|
if row:
|
||||||
await add_incident_history(incident_id, actor_id, "resolved", {"resolution": resolution})
|
await add_incident_history(incident_id, actor_id, "resolved", {"resolution": resolution})
|
||||||
@@ -831,14 +870,20 @@ async def close_incident(
|
|||||||
"""Закрити інцидент"""
|
"""Закрити інцидент"""
|
||||||
pool = await get_pool()
|
pool = await get_pool()
|
||||||
|
|
||||||
|
import uuid
|
||||||
|
try:
|
||||||
|
incident_uuid = uuid.UUID(incident_id)
|
||||||
|
except ValueError:
|
||||||
|
return None
|
||||||
|
|
||||||
query = """
|
query = """
|
||||||
UPDATE incidents
|
UPDATE incidents
|
||||||
SET status = 'closed', updated_at = NOW()
|
SET status = 'closed', closed_at = NOW(), updated_at = NOW()
|
||||||
WHERE id = $1
|
WHERE id = $1
|
||||||
RETURNING id, title, status, updated_at
|
RETURNING id::text, title, status::text, updated_at
|
||||||
"""
|
"""
|
||||||
|
|
||||||
row = await pool.fetchrow(query, incident_id)
|
row = await pool.fetchrow(query, incident_uuid)
|
||||||
|
|
||||||
if row:
|
if row:
|
||||||
await add_incident_history(incident_id, actor_id, "closed", {})
|
await add_incident_history(incident_id, actor_id, "closed", {})
|
||||||
@@ -852,7 +897,24 @@ async def add_incident_comment(
|
|||||||
comment: str
|
comment: str
|
||||||
) -> dict:
|
) -> dict:
|
||||||
"""Додати коментар до інциденту"""
|
"""Додати коментар до інциденту"""
|
||||||
return await add_incident_history(incident_id, actor_id, "comment", {"text": comment})
|
pool = await get_pool()
|
||||||
|
|
||||||
|
import uuid
|
||||||
|
try:
|
||||||
|
incident_uuid = uuid.UUID(incident_id)
|
||||||
|
except ValueError:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
query = """
|
||||||
|
INSERT INTO incident_history (
|
||||||
|
incident_id, actor_dais_id, action, comment, created_at
|
||||||
|
)
|
||||||
|
VALUES ($1, $2, 'comment', $3, NOW())
|
||||||
|
RETURNING id::text, action, created_at
|
||||||
|
"""
|
||||||
|
|
||||||
|
row = await pool.fetchrow(query, incident_uuid, actor_id, comment)
|
||||||
|
return dict(row) if row else {}
|
||||||
|
|
||||||
|
|
||||||
async def add_incident_history(
|
async def add_incident_history(
|
||||||
@@ -864,38 +926,48 @@ async def add_incident_history(
|
|||||||
"""Додати запис в історію інциденту"""
|
"""Додати запис в історію інциденту"""
|
||||||
pool = await get_pool()
|
pool = await get_pool()
|
||||||
|
|
||||||
history_id = generate_id("ih")
|
import uuid
|
||||||
|
try:
|
||||||
|
incident_uuid = uuid.UUID(incident_id)
|
||||||
|
except ValueError:
|
||||||
|
return {}
|
||||||
|
|
||||||
query = """
|
query = """
|
||||||
INSERT INTO incident_history (
|
INSERT INTO incident_history (
|
||||||
id, incident_id, actor_id, action, details, created_at
|
incident_id, actor_dais_id, action, new_value, created_at
|
||||||
)
|
)
|
||||||
VALUES ($1, $2, $3, $4, $5::jsonb, NOW())
|
VALUES ($1, $2, $3, $4::jsonb, NOW())
|
||||||
RETURNING id, action, created_at
|
RETURNING id::text, action, created_at
|
||||||
"""
|
"""
|
||||||
|
|
||||||
row = await pool.fetchrow(
|
row = await pool.fetchrow(
|
||||||
query,
|
query,
|
||||||
history_id, incident_id, actor_id, action, json.dumps(details)
|
incident_uuid, actor_id, action, json.dumps(details)
|
||||||
)
|
)
|
||||||
|
|
||||||
return dict(row)
|
return dict(row) if row else {}
|
||||||
|
|
||||||
|
|
||||||
async def get_incident_history(incident_id: str) -> List[dict]:
|
async def get_incident_history(incident_id: str) -> List[dict]:
|
||||||
"""Отримати історію інциденту"""
|
"""Отримати історію інциденту"""
|
||||||
pool = await get_pool()
|
pool = await get_pool()
|
||||||
|
|
||||||
|
import uuid
|
||||||
|
try:
|
||||||
|
incident_uuid = uuid.UUID(incident_id)
|
||||||
|
except ValueError:
|
||||||
|
return []
|
||||||
|
|
||||||
query = """
|
query = """
|
||||||
SELECT
|
SELECT
|
||||||
ih.id, ih.action, ih.details, ih.created_at,
|
ih.id::text, ih.action, ih.new_value as details, ih.comment, ih.created_at,
|
||||||
a.display_name as actor_name
|
a.display_name as actor_name
|
||||||
FROM incident_history ih
|
FROM incident_history ih
|
||||||
LEFT JOIN agents a ON ih.actor_id = a.id
|
LEFT JOIN agents a ON ih.actor_dais_id = a.id
|
||||||
WHERE ih.incident_id = $1
|
WHERE ih.incident_id = $1
|
||||||
ORDER BY ih.created_at DESC
|
ORDER BY ih.created_at DESC
|
||||||
"""
|
"""
|
||||||
|
|
||||||
rows = await pool.fetch(query, incident_id)
|
rows = await pool.fetch(query, incident_uuid)
|
||||||
return [dict(row) for row in rows]
|
return [dict(row) for row in rows]
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user