Skip to content

Commit fa40bfa

Browse files
Lucidiotcarltongibson
authored andcommitted
Add operationId on OpenAPI operations (#6549)
1 parent be3e128 commit fa40bfa

File tree

2 files changed

+49
-0
lines changed

2 files changed

+49
-0
lines changed

rest_framework/schemas/inspectors.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -511,10 +511,19 @@ def __get__(self, instance, owner):
511511
class OpenAPIAutoSchema(ViewInspector):
512512

513513
content_types = ['application/json']
514+
method_mapping = {
515+
'get': 'Retrieve',
516+
'post': 'Create',
517+
'put': 'Update',
518+
'patch': 'PartialUpdate',
519+
'delete': 'Destroy',
520+
}
514521

515522
def get_operation(self, path, method):
516523
operation = {}
517524

525+
operation['operationId'] = self._get_operation_id(path, method)
526+
518527
parameters = []
519528
parameters += self._get_path_parameters(path, method)
520529
parameters += self._get_pagination_parameters(path, method)
@@ -528,6 +537,45 @@ def get_operation(self, path, method):
528537

529538
return operation
530539

540+
def _get_operation_id(self, path, method):
541+
"""
542+
Compute an operation ID from the model, serializer or view name.
543+
"""
544+
# TODO: Allow an attribute/method on the view to change that ID?
545+
# Avoid cyclic imports
546+
from rest_framework.generics import GenericAPIView
547+
548+
if is_list_view(path, method, self.view):
549+
action = 'List'
550+
else:
551+
action = self.method_mapping[method.lower()]
552+
553+
# Try to deduce the ID from the view's model
554+
model = getattr(getattr(self.view, 'queryset', None), 'model', None)
555+
if model is not None:
556+
name = model.__name__
557+
558+
# Try with the serializer class name
559+
elif isinstance(self.view, GenericAPIView):
560+
name = self.view.get_serializer_class().__name__
561+
if name.endswith('Serializer'):
562+
name = name[:-10]
563+
564+
# Fallback to the view name
565+
else:
566+
name = self.view.__class__.__name__
567+
if name.endswith('APIView'):
568+
name = name[:-7]
569+
elif name.endswith('View'):
570+
name = name[:-4]
571+
if name.endswith(action): # ListView, UpdateAPIView, ThingDelete ...
572+
name = name[:-len(action)]
573+
574+
if action == 'List' and not name.endswith('s'): # ListThings instead of ListThing
575+
name += 's'
576+
577+
return action + name
578+
531579
def _get_path_parameters(self, path, method):
532580
"""
533581
Return a list of parameters from templated path variables.

tests/schemas/test_openapi.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ def test_path_without_parameters(self):
5757

5858
operation = inspector.get_operation(path, method)
5959
assert operation == {
60+
'operationId': 'ListExamples',
6061
'parameters': [],
6162
'responses': {'200': {'content': {'application/json': {}}}},
6263
}

0 commit comments

Comments
 (0)