Skip to content

Commit

Permalink
Merge pull request #257 from ynput/237-activity-feed-ws-message-on-ne…
Browse files Browse the repository at this point in the history
…w-activity-feed-item

Activity feed: notify clients when an activity is created, updated or deleted
  • Loading branch information
martastain authored Jun 28, 2024
2 parents 58c75f8 + 3f5aabb commit 0f1ce6c
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 5 deletions.
14 changes: 12 additions & 2 deletions api/activities/activity.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from datetime import datetime

from fastapi import BackgroundTasks
from fastapi import BackgroundTasks, Header

from ayon_server.activities import (
ActivityType,
Expand Down Expand Up @@ -42,6 +42,7 @@ async def post_project_activity(
entity_id: PathEntityID,
user: CurrentUser,
activity: ProjectActivityPostModel,
x_sender: str | None = Header(default=None),
) -> CreateActivityResponseModel:
"""Create an activity.
Expand All @@ -67,6 +68,7 @@ async def post_project_activity(
files=activity.files,
user_name=user.name,
timestamp=activity.timestamp,
sender=x_sender,
)

return CreateActivityResponseModel(id=id)
Expand All @@ -77,6 +79,7 @@ async def delete_project_activity(
project_name: ProjectName,
activity_id: str,
user: CurrentUser,
x_sender: str | None = Header(default=None),
) -> EmptyResponse:
"""Delete an activity.
Expand All @@ -89,7 +92,12 @@ async def delete_project_activity(
else:
user_name = user.name

await delete_activity(project_name, activity_id, user_name=user_name)
await delete_activity(
project_name,
activity_id,
user_name=user_name,
sender=x_sender,
)

return EmptyResponse()

Expand All @@ -106,6 +114,7 @@ async def patch_project_activity(
user: CurrentUser,
activity: ActivityPatchModel,
background_tasks: BackgroundTasks,
x_sender: str | None = Header(default=None),
) -> EmptyResponse:
"""Edit an activity.
Expand All @@ -124,6 +133,7 @@ async def patch_project_activity(
body=activity.body,
files=activity.files,
user_name=user_name,
sender=x_sender,
)

background_tasks.add_task(delete_unused_files, project_name)
Expand Down
35 changes: 34 additions & 1 deletion ayon_server/activities/create_activity.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ async def create_activity(
extra_references: list[ActivityReferenceModel] | None = None,
data: dict[str, Any] | None = None,
timestamp: datetime.datetime | None = None,
sender: str | None = None,
) -> str:
"""Create an activity.
Expand Down Expand Up @@ -175,7 +176,37 @@ async def create_activity(
ref.insertable_tuple(activity_id, timestamp) for ref in references
)

# Notify users
# Notify the front-end about the new activity

summary_references: list[dict[str, str]] = []
for ref in references:
if ref.entity_id:
summary_references.append(
{
"entity_id": ref.entity_id,
"entity_type": ref.entity_type,
"reference_type": ref.reference_type,
}
)

summary = {
"activity_id": activity_id,
"activity_type": activity_type,
"references": summary_references,
}

await EventStream.dispatch(
"activity.created",
project=project_name,
description="",
summary=summary,
store=False,
user=user_name,
sender=sender,
)

# Send inbox notifications

notify_important: list[str] = []
notify_normal: list[str] = []
for ref in references:
Expand All @@ -198,6 +229,7 @@ async def create_activity(
summary={"isImportant": True},
recipients=notify_important,
store=False,
user=user_name,
)
if notify_normal:
await EventStream.dispatch(
Expand All @@ -207,6 +239,7 @@ async def create_activity(
summary={"isImportant": False},
recipients=notify_normal,
store=False,
user=user_name,
)

return activity_id
47 changes: 45 additions & 2 deletions ayon_server/activities/delete_activity.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
"""Delete an existing activity."""

from ayon_server.events.eventstream import EventStream
from ayon_server.exceptions import ForbiddenException, NotFoundException
from ayon_server.lib.postgres import Postgres

__all__ = ["delete_activity"]


async def delete_activity(
project_name: str, activity_id: str, user_name: str | None = None
project_name: str,
activity_id: str,
user_name: str | None = None,
sender: str | None = None,
) -> None:
"""Delete an activity.
Expand All @@ -22,7 +26,11 @@ async def delete_activity(

# Load the activity first, so we can check if it really exists
# and if the user (if provided) is the author.
query = f"SELECT data FROM project_{project_name}.activities WHERE id = $1"
query = f"""
SELECT data, activity_type
FROM project_{project_name}.activities
WHERE id = $1
"""
res = await Postgres.fetch(query, activity_id)

if not res:
Expand All @@ -32,6 +40,29 @@ async def delete_activity(
data = res[0]["data"]
if "author" in data and data["author"] != user_name:
raise ForbiddenException("You are not the author of this activity")
activity_type = res[0]["activity_type"]

# create a summary of the activity before deleting it
# to notify the clients

summary_references: list[dict[str, str]] = []
async for row in Postgres.iterate(
f"""
SELECT entity_type, entity_id, reference_type
FROM project_{project_name}.activity_references
WHERE activity_id = $1 AND entity_id IS NOT NULL
""",
activity_id,
):
summary_references.append(dict(row))

summary = {
"activity_id": activity_id,
"activity_type": activity_type,
"references": summary_references,
}

# delete the activity

async with Postgres.acquire() as conn, conn.transaction():
# Unlink files from the activity
Expand All @@ -53,4 +84,16 @@ async def delete_activity(
activity_id,
)

# Notify the front-end about the deleted activity

await EventStream.dispatch(
"activity.deleted",
project=project_name,
description="",
summary=summary,
store=False,
user=user_name,
sender=sender,
)

return None
30 changes: 30 additions & 0 deletions ayon_server/activities/update_activity.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
extract_mentions,
is_body_with_checklist,
)
from ayon_server.events.eventstream import EventStream
from ayon_server.exceptions import (
BadRequestException,
NotFoundException,
Expand All @@ -25,6 +26,7 @@ async def update_activity(
user_name: str | None = None,
extra_references: list[ActivityReferenceModel] | None = None,
data: dict[str, Any] | None = None,
sender: str | None = None,
) -> None:
"""Update an activity."""

Expand Down Expand Up @@ -172,3 +174,31 @@ async def update_activity(
await st_ref.executemany(
ref.insertable_tuple(activity_id) for ref in references
)

# Notify the front-end about the update

summary_references: list[dict[str, str]] = []
for ref in references:
if ref.entity_id:
summary_references.append(
{
"entity_id": ref.entity_id,
"entity_type": ref.entity_type,
"reference_type": ref.reference_type,
}
)
summary = {
"activity_id": activity_id,
"activity_type": activity_type,
"references": summary_references,
}

await EventStream.dispatch(
"activity.updated",
project=project_name,
description="",
summary=summary,
store=False,
user=user_name,
sender=sender,
)

0 comments on commit 0f1ce6c

Please sign in to comment.