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
13 changes: 13 additions & 0 deletions ietf/api/serializers_rpc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Copyright The IETF Trust 2025, All Rights Reserved
from rest_framework import serializers

from ietf.person.models import Person


class PersonSerializer(serializers.ModelSerializer):
picture = serializers.URLField(source="cdn_photo_url", read_only=True)

class Meta:
model = Person
fields = ["id", "plain_name", "picture"]
read_only_fields = ["id", "plain_name", "picture"]
1 change: 1 addition & 0 deletions ietf/api/urls_rpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
url(r"^person/persons_by_email/$", views_rpc.persons_by_email),
url(r"^person/(?P<person_id>[0-9]+)/$", views_rpc.rpc_person),
url(r"^persons/$", views_rpc.rpc_persons),
url(r"^persons/search/", views_rpc.RpcPersonSearch.as_view()),
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.

General question: Isn't Rpc and rpc_ are redundent because these already in views_rpc?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I think so, but I didn't want to violate the convention we've been using so far in this PR. My inclination is to do a follow-up PR that refactors a bunch of this, including getting rid of the hand-crufted rpcapi.yaml.

These names aren't exposed through the API schema so the impacts both of this being a bad name and of changing the names in a later PR shouldn't impact the purple API client. We should be able to match the existing schema with a generated one, at least pretty closely.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

(Having the prefix in the method/class names is nice if you want to do a from views_rpc import rpc_whatever but I don't think we're likely to do it that way.)

Copy link
Copy Markdown
Member

@kesara kesara Jun 26, 2025

Choose a reason for hiding this comment

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

(not relevant to this PR), should persons_by_email should be rpc_persons_by_email?
What I struggle with is difference between rpc prefixed person and non-rpc prefixed person in views_rpc.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I guess we weren't as consistent as I thought at a glance. On the datatracker side, there's no such thing as an RpcPerson except where the Rpc is a prefix.

Purple has an RpcPerson model that represents someone who has a role with the RPC - editor, manager, etc.

url(r"^subject/(?P<subject_id>[0-9]+)/person/$", views_rpc.rpc_subject_person),
]

Expand Down
47 changes: 46 additions & 1 deletion ietf/api/views_rpc.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright The IETF Trust 2023-2024, All Rights Reserved
# Copyright The IETF Trust 2023-2025, All Rights Reserved

import json

Expand All @@ -12,8 +12,14 @@
from django.shortcuts import get_object_or_404
from django.views.decorators.csrf import csrf_exempt
from django.contrib.auth.models import User
from drf_spectacular.utils import extend_schema_view, extend_schema
from rest_framework import generics
from rest_framework.fields import CharField
from rest_framework.filters import SearchFilter
from rest_framework.pagination import LimitOffsetPagination

from ietf.api.ietf_utils import requires_api_token
from ietf.api.serializers_rpc import PersonSerializer
from ietf.doc.models import Document, DocHistory, RelatedDocument
from ietf.person.models import Email, Person

Expand Down Expand Up @@ -63,6 +69,45 @@ def rpc_persons(request):
return JsonResponse(response)


class RpcLimitOffsetPagination(LimitOffsetPagination):
default_limit = 10
max_limit = 100


class SingleTermSearchFilter(SearchFilter):
"""SearchFilter backend that does not split terms

The default SearchFilter treats comma or whitespace-separated terms as individual
search terms. This backend instead searches for the exact term.
"""

def get_search_terms(self, request):
value = request.query_params.get(self.search_param, '')
field = CharField(trim_whitespace=False, allow_blank=True)
cleaned_value = field.run_validation(value)
return [cleaned_value]


@extend_schema_view(
get=extend_schema(
operation_id="search_person",
description="Get a list of persons, matching by partial name or email",
),
)
class RpcPersonSearch(generics.ListAPIView):
# n.b. the OpenAPI schema for this can be generated by running
# ietf/manage.py spectacular --file spectacular.yaml
# and extracting / touching up the rpc_person_search_list operation
api_key_endpoint = "ietf.api.views_rpc"
queryset = Person.objects.all()
serializer_class = PersonSerializer
pagination_class = RpcLimitOffsetPagination

# Searchable on all name-like fields or email addresses
filter_backends = [SingleTermSearchFilter]
search_fields = ["name", "plain", "email__address"]


def _document_source_format(doc):
submission = doc.submission()
if submission is None:
Expand Down
2 changes: 1 addition & 1 deletion ietf/person/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ def short(self):
else:
prefix, first, middle, last, suffix = self.ascii_parts()
return (first and first[0]+"." or "")+(middle or "")+" "+last+(suffix and " "+suffix or "")
def plain_name(self):
def plain_name(self) -> str:
Comment thread
kesara marked this conversation as resolved.
if not hasattr(self, '_cached_plain_name'):
if self.plain:
self._cached_plain_name = self.plain
Expand Down
55 changes: 55 additions & 0 deletions rpcapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,37 @@ paths:
type: object
additionalProperties:
type: string

/persons/search/:
get:
operationId: search_person
description: Get a list of persons, matching by partial name or email
parameters:
- name: limit
required: false
in: query
description: Number of results to return per page.
schema:
type: integer
- name: offset
required: false
in: query
description: The initial index from which to return the results.
schema:
type: integer
- name: search
required: false
in: query
description: A search term.
schema:
type: string
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/PaginatedPersonList'

/person/create_demo_person/:
post:
Expand Down Expand Up @@ -457,6 +488,30 @@ components:
stream:
type: string

PaginatedPersonList:
type: object
required:
- count
- results
properties:
count:
type: integer
example: 123
next:
type: string
nullable: true
format: uri
example: https://api.example.org/accounts/?offset=400&limit=100
previous:
type: string
nullable: true
format: uri
example: https://api.example.org/accounts/?offset=200&limit=100
results:
type: array
items:
$ref: '#/components/schemas/Person'

securitySchemes:
ApiKeyAuth:
type: apiKey
Expand Down