Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions docker/configs/settings_local.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,3 +105,7 @@
"ietf.api.red_api" : ["devtoken", "redtoken"], # Not a real secret
"ietf.api.views_rpc" : ["devtoken"], # Not a real secret
}

# Errata system api configuration
ERRATA_METADATA_NOTIFICATION_URL = "http://host.docker.internal:8808/api/rfc_metadata_update/"
ERRATA_METADATA_NOTIFICATION_API_KEY = "not a real secret"
28 changes: 22 additions & 6 deletions ietf/api/tests_views_rpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@
from django.db.models.functions import Coalesce
from django.test.utils import override_settings
from django.urls import reverse as urlreverse
import mock

from ietf.blobdb.models import Blob
from ietf.doc.factories import IndividualDraftFactory, WgDraftFactory, WgRfcFactory
from ietf.doc.factories import IndividualDraftFactory, RfcFactory, WgDraftFactory, WgRfcFactory
from ietf.doc.models import RelatedDocument, Document
from ietf.group.factories import RoleFactory, GroupFactory
from ietf.person.factories import PersonFactory
Expand Down Expand Up @@ -77,7 +78,8 @@ def test_draftviewset_references(self):
self.assertEqual(refs[0]["name"], draft_bar.name)

@override_settings(APP_API_TOKENS={"ietf.api.views_rpc": ["valid-token"]})
def test_notify_rfc_published(self):
@mock.patch("ietf.doc.tasks.signal_update_rfc_metadata_task.delay")
def test_notify_rfc_published(self, mock_task_delay):
url = urlreverse("ietf.api.purple_api.notify_rfc_published")
area = GroupFactory(type_id="area")
rfc_group = GroupFactory(type_id="wg")
Expand All @@ -90,6 +92,8 @@ def test_notify_rfc_published(self):
)
rfc_stream_id = "ise"
assert isinstance(draft, Document), "WgDraftFactory should generate a Document"
updates = RfcFactory.create_batch(2)
obsoletes = RfcFactory.create_batch(2)
unused_rfc_number = (
Document.objects.filter(rfc_number__isnull=False).aggregate(
unused_rfc_number=Max("rfc_number") + 1
Expand Down Expand Up @@ -120,8 +124,8 @@ def test_notify_rfc_published(self):
"pages": draft.pages + 10,
"std_level": "ps",
"ad": rfc_ad.pk,
"obsoletes": [],
"updates": [],
"obsoletes": [o.rfc_number for o in obsoletes],
"updates": [o.rfc_number for o in updates],
"subseries": [],
}
r = self.client.post(url, data=post_data, format="json")
Expand Down Expand Up @@ -172,13 +176,25 @@ def test_notify_rfc_published(self):
self.assertEqual(rfc.pages, draft.pages + 10)
self.assertEqual(rfc.std_level_id, "ps")
self.assertEqual(rfc.ad, rfc_ad)
self.assertEqual(rfc.related_that_doc("obs"), [])
self.assertEqual(rfc.related_that_doc("updates"), [])
self.assertEqual(set(rfc.related_that_doc("obs")), set([o for o in obsoletes]))
self.assertEqual(
set(rfc.related_that_doc("updates")), set([o for o in updates])
)
self.assertEqual(rfc.part_of(), [])
self.assertEqual(draft.get_state().slug, "rfc")
# todo test non-empty relationships
# todo test references (when updating that is part of the handling)

self.assertTrue(mock_task_delay.called)
mock_args, mock_kwargs = mock_task_delay.call_args
self.assertIn("rfc_number_list", mock_kwargs)
expected_rfc_number_list = [rfc.rfc_number]
expected_rfc_number_list.extend(
[d.rfc_number for d in updates + obsoletes]
)
expected_rfc_number_list = sorted(set(expected_rfc_number_list))
self.assertEqual(mock_kwargs["rfc_number_list"], expected_rfc_number_list)

@override_settings(APP_API_TOKENS={"ietf.api.views_rpc": ["valid-token"]})
def test_upload_rfc_files(self):
def _valid_post_data():
Expand Down
9 changes: 8 additions & 1 deletion ietf/api/views_rpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
from ietf.doc.models import Document, DocHistory, RfcAuthor
from ietf.doc.serializers import RfcAuthorSerializer
from ietf.doc.storage_utils import remove_from_storage, store_file, exists_in_storage
from ietf.doc.tasks import signal_update_rfc_metadata_task
from ietf.person.models import Email, Person


Expand Down Expand Up @@ -362,7 +363,7 @@ def post(self, request):
serializer.is_valid(raise_exception=True)
# Create RFC
try:
serializer.save()
rfc = serializer.save()
except IntegrityError as err:
if Document.objects.filter(
rfc_number=serializer.validated_data["rfc_number"]
Expand All @@ -375,6 +376,12 @@ def post(self, request):
f"Unable to publish: {err}",
code="unknown-integrity-error",
)
rfc_number_list = [rfc.rfc_number]
rfc_number_list.extend(
[d.rfc_number for d in rfc.related_that_doc(("updates", "obs"))]
)
rfc_number_list = sorted(set(rfc_number_list))
signal_update_rfc_metadata_task.delay(rfc_number_list=rfc_number_list)
return Response(NotificationAckSerializer().data)


Expand Down
5 changes: 5 additions & 0 deletions ietf/doc/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
investigate_fragment,
)
from .utils_bofreq import fixup_bofreq_timestamps
from .utils_errata import signal_update_rfc_metadata


@shared_task
Expand Down Expand Up @@ -155,3 +156,7 @@ def rebuild_reference_relations_task(doc_names: list[str]):
@shared_task
def fixup_bofreq_timestamps_task(): # pragma: nocover
fixup_bofreq_timestamps()

@shared_task
def signal_update_rfc_metadata_task(rfc_number_list=()):
signal_update_rfc_metadata(rfc_number_list)
35 changes: 35 additions & 0 deletions ietf/doc/utils_errata.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Copyright The IETF Trust 2026, All Rights Reserved
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Run ruff on this module? It has some quote / indentation deviations from the preferred style.


import requests

from django.conf import settings

from ietf.utils.log import log


def signal_update_rfc_metadata(rfc_number_list=()):
key = getattr(settings, "ERRATA_METADATA_NOTIFICATION_API_KEY", None)
if key is not None:
headers = {"X-Api-Key": settings.ERRATA_METADATA_NOTIFICATION_API_KEY}
post_dict = {
"rfc_number_list": list(rfc_number_list),
}
try:
response = requests.post(
settings.ERRATA_METADATA_NOTIFICATION_URL,
headers=headers,
json=post_dict,
timeout=settings.DEFAULT_REQUESTS_TIMEOUT,
)
except requests.Timeout as e:
log(
f"POST request timed out for {settings.ERRATA_METADATA_NOTIFICATION_URL} ]: {e}"
)
# raise RuntimeError(f'POST request timed out for {settings.ERRATA_METADATA_NOTIFICATION_URL}') from e
return
if response.status_code != 200:
log(
f"POST request failed for {settings.ERRATA_METADATA_NOTIFICATION_URL} ]: {response.status_code} {response.text}"
)
else:
log("No API key configured for errata metadata notification, skipping")
5 changes: 5 additions & 0 deletions ietf/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -1368,6 +1368,11 @@ def skip_unreadable_post(record):
MEETECHO_AUDIO_STREAM_URL = "https://mp3.conf.meetecho.com/ietf{session.meeting.number}/{session.pk}.m3u"
MEETECHO_SESSION_RECORDING_URL = "https://meetecho-player.ietf.org/playout/?session={session_label}"

# Errata system api configuration
# settings should provide
# ERRATA_METADATA_NOTIFICATION_URL
# ERRATA_METADATA_NOTIFICATION_API_KEY

# Put the production SECRET_KEY in settings_local.py, and also any other
# sensitive or site-specific changes. DO NOT commit settings_local.py to svn.
from ietf.settings_local import * # pyflakes:ignore pylint: disable=wildcard-import
Expand Down
Loading