Skip to content

Commit

Permalink
avoid overriding user changes via event ingest
Browse files Browse the repository at this point in the history
check event history what keys were updated
by users and ignore those when updating from
ingest.

SDCP-861
  • Loading branch information
petrjasek committed Jan 7, 2025
1 parent ee37c70 commit 9f2b0df
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 1 deletion.
31 changes: 30 additions & 1 deletion server/planning/events/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,16 @@
"eorol:venue": "Venue organiser",
}

# based on onclusive provided content fields for now
CONTENT_FIELDS = {
"name",
"definition_short",
"definition_long",
"links",
"ednote",
"subject",
}


def get_events_embedded_planning(event: Event) -> List[EmbeddedPlanning]:
def get_coverage_id(coverage: EmbeddedCoverageItem) -> str:
Expand Down Expand Up @@ -128,6 +138,20 @@ def is_event_updated(new_item: Event, old_item: Event) -> bool:
return False


def get_user_updated_keys(id: str) -> set[str]:
# Placeholder function definition
# Replace this with the actual implementation
history_service = get_resource_service("events_history")
updates = history_service.get_by_id(id)
updated_keys = set()
for update in updates:
if not update.get("user_id"):
continue
for key in update.get("update", {}):
updated_keys.add(key)
return updated_keys


class EventsService(superdesk.Service):
"""Service class for the events model."""

Expand All @@ -144,8 +168,13 @@ def post_in_mongo(self, docs, **kwargs):
self.on_created(docs)
return ids

def patch_in_mongo(self, id, document, original) -> Optional[Dict[str, Any]]:
def patch_in_mongo(self, id: str, document, original) -> Optional[Dict[str, Any]]:
"""Patch an ingested item onto an existing item locally"""
content_fields = app.config.get("EVENT_INGEST_CONTENT_FIELDS", CONTENT_FIELDS)
updated_keys = get_user_updated_keys(id)
for key in updated_keys:
if key in document and key in content_fields and original.get(key):
document[key] = original[key]

set_planning_schedule(document)
update_ingest_on_patch(document, original)
Expand Down
25 changes: 25 additions & 0 deletions server/planning/events/events_history.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

"""Superdesk Files"""

from typing import Any, TypedDict
from superdesk import Resource, get_resource_service
from planning.history import HistoryService
import logging
Expand All @@ -22,13 +23,25 @@ class EventsHistoryResource(Resource):
endpoint_name = "events_history"
resource_methods = ["GET"]
item_methods = ["GET"]

schema = {
"event_id": {"type": "string"},
"user_id": Resource.rel("users", True),
"operation": {"type": "string"},
"update": {"type": "dict", "nullable": True},
}

mongo_indexes = {
"event_id_1": ([("event_id", 1)], {"background": True}),
}


class EventHistoryRecord(TypedDict):
event_id: str
user_id: str
operation: str
update: dict[str, Any]


class EventsHistoryService(HistoryService):
def on_item_created(self, items, operation=None):
Expand Down Expand Up @@ -85,3 +98,15 @@ def on_update_repetitions(self, updates, event_id, operation):

def on_update_time(self, updates, original):
self.on_item_updated(updates, original, "update_time")

def get_by_id(self, id: str) -> list[EventHistoryRecord]:
records = self.find(where={"event_id": id})
return [
{
"event_id": record.get("event_id"),
"user_id": record.get("user_id"),
"operation": record.get("operation"),
"update": record.get("update"),
}
for record in records
]
35 changes: 35 additions & 0 deletions server/planning/events/events_ingest_tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from superdesk import get_resource_service
from datetime import datetime, timedelta
from planning.tests import TestCase
from unittest.mock import patch
from flask import g


class EventIngestTestCase(TestCase):
def test_ingest_updated_event(self):
events_service = get_resource_service("events")
events_history_service = get_resource_service("events_history")
dates = {"start": datetime.now(), "end": datetime.now() + timedelta(days=1)}
old_event = {"guid": "1", "name": "bar", "ednote": "ednote1", "dates": dates}

# event is created
events_service.post_in_mongo([old_event])

# user updates the event
updates = {"definition_short": "manual", "ednote": "manual"}
with self.app.test_request_context():
g.user = {"_id": "test"}
events_service.patch(old_event["_id"], updates)
events_history_service.on_item_updated(updates, old_event)

new_event = {"guid": "1", "name": "updated", "ednote": "updated", "dates": dates, "definition_short": "updated"}
old_event = events_service.find_one(req=None, guid="1")

# event is updated via ingest
events_service.patch_in_mongo(new_event["guid"], new_event, old_event)

updated_event = events_service.find_one(req=None, _id="1")
assert updated_event is not None
assert updated_event["name"] == "updated"
assert updated_event["ednote"] == "manual"
assert updated_event["definition_short"] == "manual"

0 comments on commit 9f2b0df

Please sign in to comment.