Merge pull request #22314 from netbox-community/21091-render-config-openapi
Fixes #21091: Correct OpenAPI schema for rendering config contexts
This commit is contained in:
commit
14c3c573e3
|
|
@ -1,4 +1,5 @@
|
|||
from django.utils.translation import gettext as _
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from drf_spectacular.utils import OpenApiResponse, OpenApiTypes, extend_schema
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.renderers import JSONRenderer
|
||||
from rest_framework.response import Response
|
||||
|
|
@ -8,7 +9,7 @@ from extras.models import ConfigTemplate
|
|||
from netbox.api.authentication import TokenWritePermission
|
||||
from netbox.api.renderers import TextRenderer
|
||||
|
||||
from .serializers import ConfigTemplateSerializer
|
||||
from .serializers import RenderConfigInputSerializer, RenderedConfigSerializer
|
||||
|
||||
__all__ = (
|
||||
'ConfigContextQuerySetMixin',
|
||||
|
|
@ -56,12 +57,11 @@ class ConfigTemplateRenderMixin:
|
|||
if request.accepted_renderer.format == 'txt':
|
||||
return Response(output)
|
||||
|
||||
template_serializer = ConfigTemplateSerializer(configtemplate, nested=True, context={'request': request})
|
||||
|
||||
return Response({
|
||||
'configtemplate': template_serializer.data,
|
||||
'content': output
|
||||
})
|
||||
serializer = RenderedConfigSerializer(
|
||||
instance={'configtemplate': configtemplate, 'content': output},
|
||||
context={'request': request},
|
||||
)
|
||||
return Response(serializer.data)
|
||||
|
||||
|
||||
class RenderConfigMixin(ConfigTemplateRenderMixin):
|
||||
|
|
@ -75,6 +75,26 @@ class RenderConfigMixin(ConfigTemplateRenderMixin):
|
|||
return [TokenWritePermission()]
|
||||
return super().get_permissions()
|
||||
|
||||
@extend_schema(
|
||||
request=RenderConfigInputSerializer,
|
||||
responses={
|
||||
200: OpenApiResponse(
|
||||
response=RenderedConfigSerializer,
|
||||
description=_(
|
||||
"The rendered config template. When the client requests `text/plain`, the raw "
|
||||
"rendered content is returned in place of the JSON object."
|
||||
),
|
||||
),
|
||||
400: OpenApiResponse(
|
||||
response=OpenApiTypes.OBJECT,
|
||||
description=_("No config template could be resolved for this object."),
|
||||
),
|
||||
500: OpenApiResponse(
|
||||
response=OpenApiTypes.OBJECT,
|
||||
description=_("An error occurred while rendering the config template."),
|
||||
),
|
||||
},
|
||||
)
|
||||
@action(detail=True, methods=['post'], url_path='render-config', renderer_classes=[JSONRenderer, TextRenderer])
|
||||
def render_config(self, request, pk):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
from django.utils.translation import gettext_lazy as _
|
||||
from drf_spectacular.extensions import OpenApiSerializerExtension
|
||||
from rest_framework import serializers
|
||||
|
||||
from core.api.serializers_.data import DataFileSerializer, DataSourceSerializer
|
||||
from extras.models import ConfigTemplate
|
||||
from netbox.api.serializers import ChangeLogMessageSerializer, ValidatedModelSerializer
|
||||
|
|
@ -6,6 +10,8 @@ from users.api.serializers_.mixins import OwnerMixin
|
|||
|
||||
__all__ = (
|
||||
'ConfigTemplateSerializer',
|
||||
'RenderConfigInputSerializer',
|
||||
'RenderedConfigSerializer',
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -32,3 +38,41 @@ class ConfigTemplateSerializer(
|
|||
'data_file', 'auto_sync_enabled', 'data_synced', 'owner', 'tags', 'created', 'last_updated',
|
||||
]
|
||||
brief_fields = ('id', 'url', 'display', 'name', 'description')
|
||||
|
||||
|
||||
class RenderConfigInputSerializer(serializers.Serializer):
|
||||
"""
|
||||
Describes the request body for the device/VM /render-config/ endpoints. Any additional keys
|
||||
supplied are passed through as context variables to the rendered template.
|
||||
"""
|
||||
config_template_id = serializers.IntegerField(
|
||||
required=False,
|
||||
help_text=_(
|
||||
"Optional ID of the ConfigTemplate to render. If omitted, the object's assigned "
|
||||
"config template is used."
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class RenderConfigInputSerializerExtension(OpenApiSerializerExtension):
|
||||
"""
|
||||
Augment the schema for `RenderConfigInputSerializer` to advertise that additional keys
|
||||
(beyond `config_template_id`) are accepted and forwarded as template context variables.
|
||||
"""
|
||||
target_class = 'extras.api.serializers_.configtemplates.RenderConfigInputSerializer'
|
||||
|
||||
def map_serializer(self, auto_schema, direction):
|
||||
schema = auto_schema._map_serializer(self.target, direction, bypass_extensions=True)
|
||||
schema['additionalProperties'] = True
|
||||
return schema
|
||||
|
||||
|
||||
class RenderedConfigSerializer(serializers.Serializer):
|
||||
"""
|
||||
Describes the JSON response returned by the /render-config/ and /render/ endpoints.
|
||||
"""
|
||||
configtemplate = ConfigTemplateSerializer(read_only=True, nested=True)
|
||||
content = serializers.CharField(
|
||||
read_only=True,
|
||||
help_text=_("The rendered template output.")
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
from django.http import Http404
|
||||
from django.shortcuts import get_object_or_404
|
||||
from drf_spectacular.utils import extend_schema, extend_schema_view
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from drf_spectacular.utils import OpenApiResponse, OpenApiTypes, extend_schema, extend_schema_view
|
||||
from rest_framework import status
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.exceptions import PermissionDenied
|
||||
|
|
@ -245,11 +246,28 @@ class ConfigTemplateViewSet(SyncedDataMixin, ConfigTemplateRenderMixin, NetBoxMo
|
|||
return [TokenWritePermission()]
|
||||
return super().get_permissions()
|
||||
|
||||
@extend_schema(
|
||||
request=OpenApiTypes.OBJECT,
|
||||
responses={
|
||||
200: OpenApiResponse(
|
||||
response=serializers.RenderedConfigSerializer,
|
||||
description=_(
|
||||
"The rendered config template. When the client requests `text/plain`, the raw "
|
||||
"rendered content is returned in place of the JSON object."
|
||||
),
|
||||
),
|
||||
500: OpenApiResponse(
|
||||
response=OpenApiTypes.OBJECT,
|
||||
description=_("An error occurred while rendering the config template."),
|
||||
),
|
||||
},
|
||||
)
|
||||
@action(detail=True, methods=['post'], renderer_classes=[JSONRenderer, TextRenderer])
|
||||
def render(self, request, pk):
|
||||
"""
|
||||
Render a ConfigTemplate using the context data provided (if any). If the client requests "text/plain" data,
|
||||
return the raw rendered content, rather than serialized JSON.
|
||||
Render a ConfigTemplate using the context data provided (if any). The request body should be a
|
||||
mapping of context variables to make available to the template. If the client requests "text/plain"
|
||||
data, return the raw rendered content, rather than serialized JSON.
|
||||
"""
|
||||
# Override restrict() on the default queryset to enforce the render & view actions
|
||||
self.queryset = self.queryset.model.objects.restrict(request.user, 'render').restrict(request.user, 'view')
|
||||
|
|
|
|||
Loading…
Reference in New Issue