From 2146f948f6dab0ddb4db826770e5a7fd0b40a175 Mon Sep 17 00:00:00 2001 From: Jennifer Richards Date: Thu, 22 Jan 2026 21:40:06 -0400 Subject: [PATCH 1/6] fix: update purple publish API fields --- ietf/api/serializers_rpc.py | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/ietf/api/serializers_rpc.py b/ietf/api/serializers_rpc.py index fe7f6092515..12348c3bf62 100644 --- a/ietf/api/serializers_rpc.py +++ b/ietf/api/serializers_rpc.py @@ -137,7 +137,6 @@ class Meta: "pages", "source_format", "authors", - "shepherd", "intended_std_level", "consensus", "shepherd", @@ -263,15 +262,6 @@ class RfcPubSerializer(serializers.ModelSerializer): stream = serializers.PrimaryKeyRelatedField( queryset=StreamName.objects.filter(used=True) ) - formal_languages = serializers.PrimaryKeyRelatedField( - many=True, - required=False, - queryset=FormalLanguageName.objects.filter(used=True), - help_text=( - "formal languages used in RFC (defaults to those from draft, send empty" - "list to override)" - ) - ) std_level = serializers.PrimaryKeyRelatedField( queryset=StdLevelName.objects.filter(used=True), ) @@ -315,11 +305,8 @@ class Meta: "stream", "abstract", "pages", - "words", - "formal_languages", "std_level", "ad", - "note", "obsoletes", "updates", "subseries", From 5f1bb3ebce5fefc31d830f59e0e78b146d5d4354 Mon Sep 17 00:00:00 2001 From: Jennifer Richards Date: Thu, 22 Jan 2026 22:41:13 -0400 Subject: [PATCH 2/6] fix: handle IntegrityError more cleanly --- ietf/api/views_rpc.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/ietf/api/views_rpc.py b/ietf/api/views_rpc.py index ea9c6348cad..9e313595645 100644 --- a/ietf/api/views_rpc.py +++ b/ietf/api/views_rpc.py @@ -5,6 +5,7 @@ from tempfile import TemporaryDirectory from django.conf import settings +from django.db import IntegrityError from drf_spectacular.utils import OpenApiParameter from rest_framework import mixins, parsers, serializers, viewsets, status from rest_framework.decorators import action @@ -360,7 +361,20 @@ def post(self, request): serializer = RfcPubSerializer(data=request.data) serializer.is_valid(raise_exception=True) # Create RFC - serializer.save() + try: + serializer.save() + except IntegrityError as err: + if Document.objects.filter( + rfc_number=serializer.validated_data["rfc_number"] + ): + raise serializers.ValidationError( + f"RFC with that number already exists", + code="rfc-number-in-use", + ) + raise serializers.ValidationError( + f"Unable to publish: {err}", + code="unknown-integrity-error", + ) return Response(NotificationAckSerializer().data) From 50999715c6c0b6bc55fc6874fb0f777f1f6fff38 Mon Sep 17 00:00:00 2001 From: Jennifer Richards Date: Thu, 22 Jan 2026 22:49:39 -0400 Subject: [PATCH 3/6] fix: don't import RFC fields from draft --- ietf/api/serializers_rpc.py | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/ietf/api/serializers_rpc.py b/ietf/api/serializers_rpc.py index 12348c3bf62..b53fdcd47be 100644 --- a/ietf/api/serializers_rpc.py +++ b/ietf/api/serializers_rpc.py @@ -365,17 +365,11 @@ def create(self, validated_data): }, code="already-published-draft", ) - defaults_from_draft = { - "ad": draft.ad, - "formal_languages": draft.formal_languages.all(), - "group": draft.group, - "note": draft.note, - } # Transaction to clean up if something fails with transaction.atomic(): # create rfc, letting validated request data override draft defaults - rfc = self._create_rfc(defaults_from_draft | validated_data) + rfc = self._create_rfc(validated_data) DocEvent.objects.create( doc=rfc, rev=rfc.rev, @@ -510,14 +504,11 @@ def create(self, validated_data): def _create_rfc(self, validated_data): authors_data = validated_data.pop("authors") - formal_languages = validated_data.pop("formal_languages", []) - # todo ad field rfc = Document.objects.create( type_id="rfc", name=f"rfc{validated_data['rfc_number']}", **validated_data, ) - rfc.formal_languages.set(formal_languages) # list of PKs is ok for order, author_data in enumerate(authors_data): rfc.rfcauthor_set.create( order=order, From 5306750e8d07d569f070e9bb8d93c585b2939b06 Mon Sep 17 00:00:00 2001 From: Jennifer Richards Date: Fri, 23 Jan 2026 10:33:50 -0400 Subject: [PATCH 4/6] test: update test --- ietf/api/tests_views_rpc.py | 44 ++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/ietf/api/tests_views_rpc.py b/ietf/api/tests_views_rpc.py index ecb50ee76cd..09fb40bf6e5 100644 --- a/ietf/api/tests_views_rpc.py +++ b/ietf/api/tests_views_rpc.py @@ -80,9 +80,15 @@ def test_draftviewset_references(self): def test_notify_rfc_published(self): url = urlreverse("ietf.api.purple_api.notify_rfc_published") area = GroupFactory(type_id="area") + rfc_group = GroupFactory(type_id="wg") draft_ad = RoleFactory(group=area, name_id="ad").person - authors = PersonFactory.create_batch(2) - draft = WgDraftFactory(group__parent=area, authors=authors) + rfc_ad = PersonFactory() + draft_authors = PersonFactory.create_batch(2) + rfc_authors = PersonFactory.create_batch(3) + draft = WgDraftFactory( + group__parent=area, authors=draft_authors, ad=draft_ad, stream_id="ietf" + ) + rfc_stream_id = "ise" assert isinstance(draft, Document), "WgDraftFactory should generate a Document" unused_rfc_number = ( Document.objects.filter(rfc_number__isnull=False).aggregate( @@ -96,7 +102,7 @@ def test_notify_rfc_published(self): "draft_name": draft.name, "draft_rev": draft.rev, "rfc_number": unused_rfc_number, - "title": draft.title, + "title": "RFC " + draft.title, "authors": [ { "titlepage_name": f"titlepage {author.name}", @@ -106,17 +112,14 @@ def test_notify_rfc_published(self): "affiliation": "Some Affiliation", "country": "CA", } - for author in authors + for author in rfc_authors ], - "group": draft.group.acronym, - "stream": draft.stream_id, - "abstract": draft.abstract, - "pages": draft.pages, - "words": draft.pages * 250, - "formal_languages": [], + "group": rfc_group.acronym, + "stream": rfc_stream_id, + "abstract": "RFC version of " + draft.abstract, + "pages": draft.pages + 10, "std_level": "ps", - "ad": draft_ad.pk, - "note": "noted", + "ad": rfc_ad.pk, "obsoletes": [], "updates": [], "subseries": [], @@ -137,7 +140,7 @@ def test_notify_rfc_published(self): ).count(), 1, ) - self.assertEqual(rfc.title, draft.title) + self.assertEqual(rfc.title, "RFC " + draft.title) self.assertEqual(rfc.documentauthor_set.count(), 0) self.assertEqual( list( @@ -159,18 +162,15 @@ def test_notify_rfc_published(self): "affiliation": "Some Affiliation", "country": "CA", } - for author in authors + for author in rfc_authors ], ) - self.assertEqual(rfc.group, draft.group) - self.assertEqual(rfc.stream, draft.stream) - self.assertEqual(rfc.abstract, draft.abstract) - self.assertEqual(rfc.pages, draft.pages) - self.assertEqual(rfc.words, draft.pages * 250) - self.assertEqual(rfc.formal_languages.count(), 0) + self.assertEqual(rfc.group, rfc_group) + self.assertEqual(rfc.stream_id, rfc_stream_id) + self.assertEqual(rfc.abstract, "RFC version of " + draft.abstract) + self.assertEqual(rfc.pages, draft.pages + 10) self.assertEqual(rfc.std_level_id, "ps") - self.assertEqual(rfc.ad, draft_ad) - self.assertEqual(rfc.note, "noted") + self.assertEqual(rfc.ad, rfc_ad) self.assertEqual(rfc.related_that_doc("obs"), []) self.assertEqual(rfc.related_that_doc("updates"), []) self.assertEqual(rfc.part_of(), []) From c90eeb5a36ce59d7109992074a09e17106e5eed9 Mon Sep 17 00:00:00 2001 From: Jennifer Richards Date: Fri, 23 Jan 2026 10:34:01 -0400 Subject: [PATCH 5/6] chore: remove unused var/import --- ietf/api/serializers_rpc.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/ietf/api/serializers_rpc.py b/ietf/api/serializers_rpc.py index b53fdcd47be..440c2a73d40 100644 --- a/ietf/api/serializers_rpc.py +++ b/ietf/api/serializers_rpc.py @@ -27,7 +27,7 @@ update_rfcauthors, ) from ietf.group.models import Group -from ietf.name.models import StreamName, StdLevelName, FormalLanguageName +from ietf.name.models import StreamName, StdLevelName from ietf.person.models import Person from ietf.utils import log @@ -340,9 +340,6 @@ def create(self, validated_data): # If specified, retrieve draft and extract RFC default values from it if draft_name is None: draft = None - defaults_from_draft = { - "group": Group.objects.get(acronym="none", type_id="individ"), - } else: # validation enforces that draft_name and draft_rev are both present draft = Document.objects.filter( From 40676f844d2dc911585236652dd27dcf892c9f6f Mon Sep 17 00:00:00 2001 From: Jennifer Richards Date: Tue, 27 Jan 2026 11:00:08 -0400 Subject: [PATCH 6/6] fix: f-string -> string --- ietf/api/views_rpc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ietf/api/views_rpc.py b/ietf/api/views_rpc.py index 9e313595645..6b1799f6544 100644 --- a/ietf/api/views_rpc.py +++ b/ietf/api/views_rpc.py @@ -368,7 +368,7 @@ def post(self, request): rfc_number=serializer.validated_data["rfc_number"] ): raise serializers.ValidationError( - f"RFC with that number already exists", + "RFC with that number already exists", code="rfc-number-in-use", ) raise serializers.ValidationError(