improvement: add operationId override to @extend_schema; fix tags

This commit is contained in:
Thorsten Franzel 2019-12-13 23:04:24 +01:00
parent d24a15f166
commit 1535ee2a3c
3 changed files with 33 additions and 29 deletions

View File

@ -141,7 +141,7 @@ class AutoSchema(ViewInspector):
def get_operation(self, path, method): def get_operation(self, path, method):
operation = {} operation = {}
operation['operationId'] = self._get_operation_id(path, method) operation['operationId'] = self.get_operation_id(path, method)
operation['description'] = self.get_description(path, method) operation['description'] = self.get_description(path, method)
operation['parameters'] = sorted( operation['parameters'] = sorted(
[ [
@ -207,38 +207,35 @@ class AutoSchema(ViewInspector):
def get_tags(self, path, method): def get_tags(self, path, method):
""" override this for custom behaviour """ """ override this for custom behaviour """
path = re.sub( tokenized_path = self._tokenize_path(path)
pattern=api_settings.SCHEMA_PATH_PREFIX, # use first non-parameter path part as tag
repl='', return tokenized_path[:1]
string=path,
flags=re.IGNORECASE
).split('/')
return [path[0]]
def _get_operation_id(self, path, method): def get_operation_id(self, path, method):
""" """ override this for custom behaviour """
Compute an operation ID from the model, serializer or view name. tokenized_path = self._tokenize_path(path)
"""
# remove path prefix
sub_path = re.sub(
pattern=api_settings.SCHEMA_PATH_PREFIX,
repl='',
string=path,
flags=re.IGNORECASE
)
# cleanup, normalize and tokenize remaining parts.
# replace dashes as they can be problematic later in code generation # replace dashes as they can be problematic later in code generation
sub_path = sub_path.replace('-', '_').rstrip('/').lstrip('/') tokenized_path = [t.replace('-', '_') for t in tokenized_path]
sub_path = sub_path.split('/') if sub_path else []
# remove path variables
sub_path = [p for p in sub_path if not p.startswith('{')]
if is_list_view(path, method, self.view): if is_list_view(path, method, self.view):
action = 'list' action = 'list'
else: else:
action = self.method_mapping[method.lower()] action = self.method_mapping[method.lower()]
return '_'.join(sub_path + [action]) return '_'.join(tokenized_path + [action])
def _tokenize_path(self, path):
# remove path prefix
path = re.sub(
pattern=api_settings.SCHEMA_PATH_PREFIX,
repl='',
string=path,
flags=re.IGNORECASE
)
# cleanup and tokenize remaining parts.
path = path.rstrip('/').lstrip('/').split('/')
# remove path variables and empty tokens
return [t for t in path if t and not t.startswith('{')]
def _get_path_parameters(self, path, method): def _get_path_parameters(self, path, method):
""" """

View File

@ -92,6 +92,7 @@ class QueryParameter(OpenApiSchemaBase):
def extend_schema( def extend_schema(
operation=None, operation=None,
operation_id=None,
extra_parameters=None, extra_parameters=None,
responses=None, responses=None,
request=None, request=None,
@ -117,10 +118,16 @@ def extend_schema(
return operation return operation
return super().get_operation(path, method) return super().get_operation(path, method)
def get_operation_id(self, path, method):
if operation_id:
return operation_id
return super().get_operation_id(path, method)
def get_extra_parameters(self, path, method): def get_extra_parameters(self, path, method):
if extra_parameters: if extra_parameters:
return [ return [
p.to_schema() if isinstance(p, OpenApiSchemaBase) else p for p in extra_parameters p.to_schema() if isinstance(p, OpenApiSchemaBase) else p
for p in extra_parameters
] ]
return super().get_extra_parameters(path, method) return super().get_extra_parameters(path, method)

View File

@ -90,7 +90,7 @@ class TestOperationIntrospection(TestCase):
'operationId': 'example_list', 'operationId': 'example_list',
'description': 'get: A description of my GET operation.\npost: A description of my POST operation.', 'description': 'get: A description of my GET operation.\npost: A description of my POST operation.',
'parameters': [], 'parameters': [],
'tags': [''], 'tags': ['example'],
'security': [{'cookieAuth': []}, {'basicAuth': []}, {}], 'security': [{'cookieAuth': []}, {'basicAuth': []}, {}],
'responses': { 'responses': {
'200': { '200': {
@ -138,7 +138,7 @@ class TestOperationIntrospection(TestCase):
} }
} }
], ],
'tags': [''], 'tags': ['example'],
'security': [{'cookieAuth': []}, {'basicAuth': []}, {}], 'security': [{'cookieAuth': []}, {'basicAuth': []}, {}],
'responses': { 'responses': {
'200': { '200': {
@ -555,7 +555,7 @@ class TestOperationIntrospection(TestCase):
inspector.view = view inspector.view = view
inspector.init(ComponentRegistry()) inspector.init(ComponentRegistry())
operationId = inspector._get_operation_id(path, method) operationId = inspector.get_operation_id(path, method)
assert operationId == 'list' assert operationId == 'list'
def test_repeat_operation_ids(self): def test_repeat_operation_ids(self):