Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
104 commits
Select commit Hold shift + click to select a range
0e4a032
feat: Add rpc_person API call
jennifer-richards Sep 26, 2023
e92136c
chore: Add OpenAPI yaml for RPC API
jennifer-richards Sep 26, 2023
0f90c4d
Merge pull request #6388 from ietf-tools/main
jennifer-richards Sep 26, 2023
0e309d0
feat: api endpoint for docs submitted to the rpc
rjsparks Sep 26, 2023
3998f34
chore: remove some debug
rjsparks Sep 26, 2023
14471ec
Merge pull request #6390 from rjsparks/submitted_to_rpc
jennifer-richards Sep 26, 2023
76f043a
feat: api for created demo people
rjsparks Sep 26, 2023
ec324e0
feat: optimize fetching persons
rjsparks Sep 26, 2023
75d01b2
Merge pull request #6391 from rjsparks/persons_optimization
jennifer-richards Sep 26, 2023
f27ff98
feat: api for creating demo drafts (#6402)
rjsparks Sep 28, 2023
9e9a787
fix: Typo in rpcapi.yaml
jennifer-richards Sep 28, 2023
fd90d8b
refactor: Allow existing Person in create_demo_person (#6398)
jennifer-richards Sep 28, 2023
fbc1dde
fix: include pubreq docevents in demo drafts (#6404)
rjsparks Sep 28, 2023
57f5c9a
fix: Minor API fixes (#6405)
jennifer-richards Sep 28, 2023
603ba8c
chore: Document 404 from rpcapi get_person_by_id
jennifer-richards Oct 1, 2023
084e863
feat: adding rev to demo doc creation (#6425)
rjsparks Oct 3, 2023
af0ea91
refactor: Replace api token checks with decorator (#6434)
jennifer-richards Oct 5, 2023
8c29605
refactor: Improve usability of @requires_api_token
jennifer-richards Oct 5, 2023
272db6d
feat: get_draft_by_id api call (#6446)
jennifer-richards Oct 10, 2023
4bc5ec6
fix: construct the cdn photo url correctly (#6491)
rjsparks Oct 17, 2023
5c0cdec
chore: restructure the rpc api (#6505)
rjsparks Oct 20, 2023
f2b3138
fix: authenticate person api correctly
rjsparks Oct 20, 2023
6b75cff
chore: Fix plain_name lookup
jennifer-richards Oct 25, 2023
524f714
Merge pull request #6551 from ietf-tools/main
jennifer-richards Oct 27, 2023
3f04ca7
feat: subject_id -> Person api call (#6566)
jennifer-richards Nov 1, 2023
4a129ac
Merge pull request #6631 from ietf-tools/main
jennifer-richards Nov 10, 2023
adf4f5a
Merge pull request #6717 from ietf-tools/main
jennifer-richards Nov 30, 2023
02bdf4f
Merge remote-tracking branch 'upstream/main' into feat/rpc-api
jennifer-richards Dec 12, 2023
5d5459f
Merge pull request #6788 from ietf-tools/main
jennifer-richards Dec 16, 2023
633e000
Merge branch 'main' into feat/rpc-api
jennifer-richards Dec 19, 2023
a8087b0
Merge pull request #6815 from ietf-tools/main
jennifer-richards Dec 19, 2023
e8fe731
feat: rpc api drafts by names (#6853)
rjsparks Jan 2, 2024
1c0a47f
Merge branch 'main' into feat/rpc-api
rjsparks Feb 16, 2024
5863e83
ci: Merge branch 'main' into feat/rpc-api
rjsparks Jul 16, 2024
34a0987
feat: get stream rfcs were first published into (#7814)
rjsparks Aug 9, 2024
93f95be
Merge branch 'main' into feat/rpc-api
rjsparks Aug 23, 2024
6ea1e82
fix: return "none" from drafts_by_names if a draft has a stream of No…
rjsparks Aug 28, 2024
2ed6a2e
feat: API changes needed for "real" draft imports (#7877)
jennifer-richards Sep 6, 2024
1d3eba7
fix: typo in drafts_by_names() (#7912)
jennifer-richards Sep 9, 2024
ac66c85
Merge branch 'main' into feat/rpc-api
jennifer-richards Sep 20, 2024
a7a7caf
Merge branch 'main' into feat/rpc-api
rjsparks Oct 11, 2024
bc8d3f8
Merge pull request #8071 from ietf-tools/main
jennifer-richards Oct 23, 2024
9cf8f10
feat: support for building rfc authors (#7983)
rjsparks Oct 28, 2024
14a76f9
Merge pull request #8124 from ietf-tools/main
jennifer-richards Oct 30, 2024
1ab36cd
feat: api to gather draft authors (#8126)
rjsparks Oct 30, 2024
732c502
Merge pull request #8257 from ietf-tools/main
jennifer-richards Nov 21, 2024
ebc2710
ci: tag feature branch release images
jennifer-richards Nov 21, 2024
eba8175
chore: fix git nonsense
jennifer-richards Nov 21, 2024
0545a26
Merge branch 'main' into feat/rpc-api
jennifer-richards Nov 21, 2024
093fc2b
Merge pull request #8281 from ietf-tools/main
jennifer-richards Nov 27, 2024
a90cbad
feat: API for RFC metadata fetch/filtering (#8291)
jennifer-richards Dec 9, 2024
ba3bad5
feat: add 'abstract' to rpc api
holloway Dec 12, 2024
05e6cb6
Merge pull request #8326 from holloway/rpc-api-abstract
jennifer-richards Dec 12, 2024
501d92a
Merge pull request #8327 from ietf-tools/main
jennifer-richards Dec 12, 2024
8eae978
chore: fix unused/duplicate imports
jennifer-richards Dec 13, 2024
e896928
chore: fix mypy lint
jennifer-richards Dec 13, 2024
d245312
chore: unused import
jennifer-richards Dec 13, 2024
d60dba1
feat: retrieve single rfc, including text (#8346)
jennifer-richards Dec 19, 2024
c48e6e7
fix: many=True for identifiers (#8425)
jennifer-richards Jan 15, 2025
7da3965
Merge branch 'main' into feat/rpc-api
jennifer-richards Jan 31, 2025
151c936
feat: add more rfc api fields (many stubs)
jennifer-richards Jan 31, 2025
359af60
Merge pull request #8492 from jennifer-richards/more-fields
jennifer-richards Feb 19, 2025
f88e9f9
chore: adding postscript (ps) to rfc meta serializer (#8560)
holloway Feb 21, 2025
114dfa8
Merge remote-tracking branch 'refs/remotes/upstream/main' into feat/r…
jennifer-richards Apr 2, 2025
968f9cb
Merge pull request #8772 from ietf-tools/main
jennifer-richards Apr 2, 2025
389e97a
chore: merge main into feat/rpc-api
jennifer-richards May 6, 2025
13a17b4
ci: merge branch 'main' into feat/rpc-api
rjsparks May 13, 2025
c2efcfa
chore: merge main into feat/rpc-api
jennifer-richards May 22, 2025
76ebc10
fix: acknowledge not-issued in RfcStatusSlugT
jennifer-richards May 22, 2025
cc4ae6e
Merge pull request #8914 from jennifer-richards/not-issued-lint
jennifer-richards May 22, 2025
1f810ef
feat: Add API call to get references
kesara May 23, 2025
d37a982
fix: Filter drafts
kesara May 23, 2025
69ef75d
chore: Optimize the data query
kesara May 28, 2025
b568be9
test: Test for norminative references API call
kesara May 28, 2025
28c85ff
chore: Fix typos in tests
kesara May 28, 2025
95aeccc
chore: Fix typos
kesara May 28, 2025
6574331
Merge pull request #8918 from kesara/feat/refs
jennifer-richards May 28, 2025
9f718a3
refactor: Separate demo logic (#8937)
kesara May 29, 2025
01aa99d
fix: trailing slashes for all rpc api endpoints (#8940)
rjsparks May 29, 2025
f3b8600
chore: Add RPC references API call to OpenAPI spec (#8941)
kesara May 30, 2025
e266247
fix: Fix OpenAPI spec errors (#8943)
kesara May 30, 2025
daf7b62
feat: include picture URL in rpc_person API (#9009)
jennifer-richards Jun 17, 2025
3d1728c
Merge branch 'main' into feat/rpc-api
jennifer-richards Jun 24, 2025
38fda63
feat: person search endpoint (#9062)
jennifer-richards Jun 27, 2025
987a460
Merge pull request #9090 from ietf-tools/main
jennifer-richards Jul 2, 2025
fc7b1c5
Merge pull request #9108 from ietf-tools/main
jennifer-richards Jul 7, 2025
70f093d
refactor: reimplement purple API in django-rest-framework (#9097)
jennifer-richards Jul 8, 2025
84da631
ci: only migrate blobdb if it is configured
jennifer-richards Jul 9, 2025
8bca0e1
feat: add email/url to purple person API (#9127)
jennifer-richards Jul 12, 2025
24d9815
Merge pull request #9316 from ietf-tools/main
jennifer-richards Aug 6, 2025
5eae568
Merge branch 'main' into feat/rpc-api
jennifer-richards Sep 4, 2025
b34bb04
feat: expose consensus in submission api
rudimatz Sep 11, 2025
9bb80a5
Merge pull request #9504 from rudimatz/feat/add-consensus-submission
jennifer-richards Sep 12, 2025
7bc1d63
feat: subseries api for red (#9556)
jennifer-richards Sep 19, 2025
cb34712
feat: add subseries+stub titlepage_name to rfc serializer (#9569)
jennifer-richards Sep 22, 2025
6c6b8d7
chore: merge main to feat/rpc-api
jennifer-richards Sep 22, 2025
983c794
fix: renumber migrations
jennifer-richards Sep 22, 2025
4f5da10
feat: add consensus on FullDraftSerializer
rudimatz Sep 24, 2025
3daf659
Merge pull request #9605 from rudimatz/feat/add-consensus-submission
jennifer-richards Oct 1, 2025
18cc41a
feat: add type field in serializer
rudimatz Oct 23, 2025
8d0bbb2
Merge pull request #9776 from rudimatz/feat/add-type-for-group
rudimatz Oct 23, 2025
23960e9
feat: tag subseries API endpoints for purple (#9763)
rudimatz Oct 23, 2025
d1aa690
feat: change slugs/names (#9778)
rudimatz Oct 23, 2025
c020f0f
fix: remove double tag (#9787)
jennifer-richards Oct 24, 2025
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
8 changes: 6 additions & 2 deletions dev/build/migration-start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@
echo "Running Datatracker migrations..."
./ietf/manage.py migrate --settings=settings_local

echo "Running Blobdb migrations ..."
./ietf/manage.py migrate --settings=settings_local --database=blobdb
# Check whether the blobdb database exists - inspectdb will return a false
# status if not.
if ./ietf/manage.py inspectdb --database blobdb > /dev/null 2>&1; then
echo "Running Blobdb migrations ..."
./ietf/manage.py migrate --settings=settings_local --database=blobdb
fi

echo "Done!"
5 changes: 2 additions & 3 deletions dev/deploy-to-container/settings_local.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,10 @@

DE_GFM_BINARY = '/usr/local/bin/de-gfm'

# No real secrets here, these are public testing values _only_
APP_API_TOKENS = {
"ietf.api.views.ingest_email_test": ["ingestion-test-token"]
"ietf.api.views.ingest_email_test": ["ingestion-test-token"], # Not a real secret
"ietf.api.views_rpc" : ["devtoken"], # For RPC dev work only, remove before merging to main!!!
Copy link
Member

Choose a reason for hiding this comment

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

Replace this with "not a real secret"?

}


# OIDC configuration
SITE_URL = 'https://__HOSTNAME__'
4 changes: 4 additions & 0 deletions docker/configs/settings_local.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,7 @@
bucket_name=f"{storagename}",
),
}

APP_API_TOKENS = {
"ietf.api.views_rpc" : ["devtoken"],
Copy link
Member

Choose a reason for hiding this comment

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

not a real secret?

}
194 changes: 194 additions & 0 deletions ietf/api/serializers_rpc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
# Copyright The IETF Trust 2025, All Rights Reserved
import datetime
from typing import Literal, Optional

from django.urls import reverse as urlreverse
from drf_spectacular.types import OpenApiTypes
from drf_spectacular.utils import extend_schema_field
from rest_framework import serializers

from ietf.doc.models import DocumentAuthor, Document
from ietf.doc.utils import default_consensus
from ietf.person.models import Person


class PersonSerializer(serializers.ModelSerializer):
email = serializers.EmailField(read_only=True)
picture = serializers.URLField(source="cdn_photo_url", read_only=True)
url = serializers.SerializerMethodField(
help_text="relative URL for datatracker person page"
)

class Meta:
model = Person
fields = ["id", "plain_name", "email", "picture", "url"]
read_only_fields = ["id", "plain_name", "email", "picture", "url"]

@extend_schema_field(OpenApiTypes.URI)
def get_url(self, object: Person):
return urlreverse(
"ietf.person.views.profile",
kwargs={"email_or_name": object.email_address() or object.name},
)


class EmailPersonSerializer(serializers.Serializer):
email = serializers.EmailField(source="address")
person_pk = serializers.IntegerField(source="person.pk")
name = serializers.CharField(source="person.name")
last_name = serializers.CharField(source="person.last_name")
initials = serializers.CharField(source="person.initials")


class LowerCaseEmailField(serializers.EmailField):
def to_representation(self, value):
return super().to_representation(value).lower()


class AuthorPersonSerializer(serializers.ModelSerializer):
person_pk = serializers.IntegerField(source="pk", read_only=True)
last_name = serializers.CharField()
initials = serializers.CharField()
email_addresses = serializers.ListField(
source="email_set.all", child=LowerCaseEmailField()
)

class Meta:
model = Person
fields = ["person_pk", "name", "last_name", "initials", "email_addresses"]


class RfcWithAuthorsSerializer(serializers.ModelSerializer):
authors = AuthorPersonSerializer(many=True)

class Meta:
model = Document
fields = ["rfc_number", "authors"]


class DraftWithAuthorsSerializer(serializers.ModelSerializer):
draft_name = serializers.CharField(source="name")
authors = AuthorPersonSerializer(many=True)

class Meta:
model = Document
fields = ["draft_name", "authors"]


class DocumentAuthorSerializer(serializers.ModelSerializer):
"""Serializer for a Person in a response"""

plain_name = serializers.SerializerMethodField()

class Meta:
model = DocumentAuthor
fields = ["person", "plain_name"]

def get_plain_name(self, document_author: DocumentAuthor) -> str:
return document_author.person.plain_name()


class FullDraftSerializer(serializers.ModelSerializer):
# Redefine these fields so they don't pick up the regex validator patterns.
# There seem to be some non-compliant drafts in the system! If this serializer
# is used for a writeable view, the validation will need to be added back.
name = serializers.CharField(max_length=255)
title = serializers.CharField(max_length=255)

# Other fields we need to add / adjust
source_format = serializers.SerializerMethodField()
authors = DocumentAuthorSerializer(many=True, source="documentauthor_set")
shepherd = serializers.SerializerMethodField()
consensus = serializers.SerializerMethodField()

class Meta:
model = Document
fields = [
"id",
"name",
"rev",
"stream",
"title",
"pages",
"source_format",
"authors",
"shepherd",
"intended_std_level",
"consensus",
]

def get_consensus(self, doc: Document) -> Optional[bool]:
return default_consensus(doc)

def get_source_format(
self, doc: Document
) -> Literal["unknown", "xml-v2", "xml-v3", "txt"]:
submission = doc.submission()
if submission is None:
return "unknown"
if ".xml" in submission.file_types:
if submission.xml_version == "3":
return "xml-v3"
else:
return "xml-v2"
elif ".txt" in submission.file_types:
return "txt"
return "unknown"

@extend_schema_field(OpenApiTypes.EMAIL)
def get_shepherd(self, doc: Document) -> str:
if doc.shepherd:
return doc.shepherd.formatted_ascii_email()
return ""


class DraftSerializer(FullDraftSerializer):
class Meta:
model = Document
fields = [
"id",
"name",
"rev",
"stream",
"title",
"pages",
"source_format",
"authors",
]


class SubmittedToQueueSerializer(FullDraftSerializer):
submitted = serializers.SerializerMethodField()
consensus = serializers.SerializerMethodField()

class Meta:
model = Document
fields = [
"id",
"name",
"stream",
"submitted",
"consensus",
]

def get_submitted(self, doc) -> Optional[datetime.datetime]:
event = doc.sent_to_rfc_editor_event()
return None if event is None else event.time

def get_consensus(self, doc) -> Optional[bool]:
return default_consensus(doc)


class OriginalStreamSerializer(serializers.ModelSerializer):
stream = serializers.CharField(read_only=True, source="orig_stream_id")

class Meta:
model = Document
fields = ["rfc_number", "stream"]


class ReferenceSerializer(serializers.ModelSerializer):
class Meta:
model = Document
fields = ["id", "name"]
read_only_fields = ["id", "name"]
69 changes: 69 additions & 0 deletions ietf/api/tests_views_rpc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Copyright The IETF Trust 2025, All Rights Reserved
# -*- coding: utf-8 -*-

from django.test.utils import override_settings
from django.urls import reverse as urlreverse

from ietf.doc.factories import IndividualDraftFactory
from ietf.doc.models import RelatedDocument
from ietf.utils.test_utils import TestCase, reload_db_objects


class RpcApiTests(TestCase):
@override_settings(APP_API_TOKENS={"ietf.api.views_rpc": ["valid-token"]})
def test_api_refs(self):
# non-existent draft
url = urlreverse("ietf.api.views_rpc.rpc_draft_refs", kwargs={"doc_id": 999999})
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
r = self.client.get(url, headers={"X-Api-Key": "valid-token"})
jsondata = r.json()
refs = jsondata["references"]
self.assertEqual(refs, [])

# draft without any normative references
draft = IndividualDraftFactory()
draft = reload_db_objects(draft)
url = urlreverse(
"ietf.api.views_rpc.rpc_draft_refs", kwargs={"doc_id": draft.id}
)
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
r = self.client.get(url, headers={"X-Api-Key": "valid-token"})
jsondata = r.json()
refs = jsondata["references"]
self.assertEqual(refs, [])

# draft without any normative references but with an informative reference
draft_foo = IndividualDraftFactory()
draft_foo = reload_db_objects(draft_foo)
RelatedDocument.objects.create(
source=draft, target=draft_foo, relationship_id="refinfo"
)
url = urlreverse(
"ietf.api.views_rpc.rpc_draft_refs", kwargs={"doc_id": draft.id}
)
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
r = self.client.get(url, headers={"X-Api-Key": "valid-token"})
jsondata = r.json()
refs = jsondata["references"]
self.assertEqual(refs, [])

# draft with a normative reference
draft_bar = IndividualDraftFactory()
draft_bar = reload_db_objects(draft_bar)
RelatedDocument.objects.create(
source=draft, target=draft_bar, relationship_id="refnorm"
)
url = urlreverse(
"ietf.api.views_rpc.rpc_draft_refs", kwargs={"doc_id": draft.id}
)
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
r = self.client.get(url, headers={"X-Api-Key": "valid-token"})
jsondata = r.json()
refs = jsondata["references"]
self.assertEqual(len(refs), 1)
self.assertEqual(refs[0]["id"], draft_bar.id)
self.assertEqual(refs[0]["name"], draft_bar.name)
19 changes: 13 additions & 6 deletions ietf/api/urls.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,31 @@
# Copyright The IETF Trust 2017-2024, All Rights Reserved

from drf_spectacular.views import SpectacularAPIView

from django.conf import settings
from django.urls import include
from django.urls import include, path
from django.views.generic import TemplateView

from ietf import api
from ietf.doc import views_ballot
from ietf.doc import views_ballot, api as doc_api
from ietf.meeting import views as meeting_views
from ietf.submit import views as submit_views
from ietf.utils.urls import url

from . import views as api_views
from .routers import PrefixedSimpleRouter

# DRF API routing - disabled until we plan to use it
# from drf_spectacular.views import SpectacularAPIView
# from django.urls import path
# from ietf.person import api as person_api
# from .routers import PrefixedSimpleRouter
# core_router = PrefixedSimpleRouter(name_prefix="ietf.api.core_api") # core api router
# core_router.register("email", person_api.EmailViewSet)
# core_router.register("person", person_api.PersonViewSet)

# todo more general name for this API?
red_router = PrefixedSimpleRouter(name_prefix="ietf.api.red_api") # red api router
red_router.register("doc", doc_api.RfcViewSet)
red_router.register("subseries", doc_api.SubseriesViewSet, basename="subseries")

api.autodiscover()

urlpatterns = [
Expand All @@ -32,7 +37,9 @@
url(r'^v2/person/person', api_views.ApiV2PersonExportView.as_view()),
# --- DRF API ---
# path("core/", include(core_router.urls)),
# path("schema/", SpectacularAPIView.as_view()),
path("purple/", include("ietf.api.urls_rpc")),
path("red/", include(red_router.urls)),
path("schema/", SpectacularAPIView.as_view()),
#
# --- Custom API endpoints, sorted alphabetically ---
# Email alias information for drafts
Expand Down
33 changes: 33 additions & 0 deletions ietf/api/urls_rpc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Copyright The IETF Trust 2023-2025, All Rights Reserved

from rest_framework import routers

from django.conf import settings
from django.urls import include, path

from ietf.api import views_rpc, views_rpc_demo
from ietf.utils.urls import url

router = routers.DefaultRouter()
router.register(r"draft", views_rpc.DraftViewSet, basename="draft")
router.register(r"person", views_rpc.PersonViewSet)
router.register(r"rfc", views_rpc.RfcViewSet, basename="rfc")

if settings.SERVER_MODE not in {"production", "test"}:
# for non production demos
router.register(r"demo", views_rpc_demo.DemoViewSet, basename="demo")


urlpatterns = [
url(r"^doc/drafts_by_names/", views_rpc.DraftsByNamesView.as_view()),
url(r"^persons/search/", views_rpc.RpcPersonSearch.as_view()),
path(r"subject/<str:subject_id>/person/", views_rpc.SubjectPersonView.as_view()),
]

# add routers at the end so individual routes can steal parts of their address
# space (specifically, ^person/ routes so far)
Copy link
Member

Choose a reason for hiding this comment

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

verify that this is current?

urlpatterns.extend(
[
path("", include(router.urls)),
]
)
Loading
Loading