feat(core): Add Related Ticket API v2 endpoint

ref: #248 #365
This commit is contained in:
2024-10-27 18:52:55 +09:30
parent da5d19cbcb
commit c36d36be0b
6 changed files with 246 additions and 2 deletions

View File

@ -43,6 +43,7 @@ from core.viewsets import (
ticket_category, ticket_category,
ticket_comment, ticket_comment,
ticket_linked_item, ticket_linked_item,
related_ticket,
) )
@ -119,6 +120,8 @@ router.register('config_management/group/(?P<group_id>[0-9]+)/software', config_
router.register('core/(?P<model_class>.+)/(?P<model_id>[0-9]+)/history', history_v2.ViewSet, basename='_api_v2_model_history') router.register('core/(?P<model_class>.+)/(?P<model_id>[0-9]+)/history', history_v2.ViewSet, basename='_api_v2_model_history')
router.register('core/ticket/(?P<ticket_id>[0-9]+)/linked_item', ticket_linked_item.ViewSet, basename='_api_v2_ticket_linked_item') router.register('core/ticket/(?P<ticket_id>[0-9]+)/linked_item', ticket_linked_item.ViewSet, basename='_api_v2_ticket_linked_item')
router.register('core/ticket/(?P<ticket_id>[0-9]+)/related_ticket', related_ticket.ViewSet, basename='_api_v2_ticket_related')
router.register('itam', itam_index_v2.Index, basename='_api_v2_itam_home') router.register('itam', itam_index_v2.Index, basename='_api_v2_itam_home')
router.register('itam/device', device_v2.ViewSet, basename='_api_v2_device') router.register('itam/device', device_v2.ViewSet, basename='_api_v2_device')

View File

@ -225,6 +225,19 @@ class ModelViewSet(
class ModelListRetrieveDeleteViewSet(
viewsets.mixins.ListModelMixin,
viewsets.mixins.RetrieveModelMixin,
viewsets.mixins.DestroyModelMixin,
viewsets.GenericViewSet,
ModelViewSetBase
):
""" Use for models that you wish to delete and view ONLY!"""
pass
class ModelRetrieveUpdateViewSet( class ModelRetrieveUpdateViewSet(
viewsets.mixins.RetrieveModelMixin, viewsets.mixins.RetrieveModelMixin,
viewsets.mixins.UpdateModelMixin, viewsets.mixins.UpdateModelMixin,

View File

@ -3,6 +3,8 @@ from django.db import models
from django.db.models import Q, signals, Sum from django.db.models import Q, signals, Sum
from django.forms import ValidationError from django.forms import ValidationError
from rest_framework.reverse import reverse
from .ticket_enum_values import TicketValues from .ticket_enum_values import TicketValues
from access.fields import AutoCreatedField, AutoLastModifiedField from access.fields import AutoCreatedField, AutoLastModifiedField
@ -1103,6 +1105,11 @@ class RelatedTickets(TenancyObject):
'id' 'id'
] ]
verbose_name = 'Related Ticket'
verbose_name_plural = 'Related Tickets'
class Related(models.IntegerChoices): class Related(models.IntegerChoices):
RELATED = '1', 'Related' RELATED = '1', 'Related'
@ -1159,9 +1166,32 @@ class RelatedTickets(TenancyObject):
] ]
# def __str__(self): def get_url( self, ticket_id, request = None ) -> str:
if not ticket_id:
return ''
if request:
return reverse(
"v2:_api_v2_ticket_related-detail",
request = request,
kwargs={
'ticket_id': ticket_id,
'pk': self.id
}
)
return reverse("v2:_api_v2_ticket_related-detail", kwargs={'pk': self.id})
def __str__(self):
# return '#' + str( self.id )
return '#'
# return ''
@property @property
def parent_object(self): def parent_object(self):

View File

@ -75,6 +75,7 @@ class TicketModelSerializer(TicketBaseSerializer):
), ),
'comments': reverse('v2:_api_v2_ticket_' + str(item.get_ticket_type_display()).lower() + '_comments-list', request=context['view'].request, kwargs={'ticket_id': item.pk}), 'comments': reverse('v2:_api_v2_ticket_' + str(item.get_ticket_type_display()).lower() + '_comments-list', request=context['view'].request, kwargs={'ticket_id': item.pk}),
'linked_items': reverse("v2:_api_v2_ticket_linked_item-list", request=context['view'].request, kwargs={'ticket_id': item.pk}), 'linked_items': reverse("v2:_api_v2_ticket_linked_item-list", request=context['view'].request, kwargs={'ticket_id': item.pk}),
'related_tickets': reverse("v2:_api_v2_ticket_related-list", request=context['view'].request, kwargs={'ticket_id': item.pk}),
} }

View File

@ -0,0 +1,119 @@
from rest_framework.fields import empty
from rest_framework.reverse import reverse
from rest_framework import serializers
from access.serializers.organization import OrganizationBaseSerializer
from core.serializers.ticket import TicketBaseSerializer
from core.models.ticket.ticket import RelatedTickets
class RelatedTicketBaseSerializer(serializers.ModelSerializer):
display_name = serializers.SerializerMethodField('get_display_name')
def get_display_name(self, item):
return str( item )
url = serializers.SerializerMethodField('get_url')
def get_url(self, item) -> str:
request = None
ticket_id: int = None
if 'view' in self._context:
if hasattr(self._context['view'], 'request'):
request = self._context['view'].request
if 'ticket_id' in self._kwargs['context']['view'].kwargs:
ticket_id = int(self._kwargs['context']['view'].kwargs['ticket_id'])
return item.get_url( ticket_id = ticket_id,request = request )
class Meta:
model = RelatedTickets
fields = [
'id',
'display_name',
'title',
'url',
]
read_only_fields = [
'id',
'display_name',
'title',
'url',
]
class RelatedTicketModelSerializer(RelatedTicketBaseSerializer):
_urls = serializers.SerializerMethodField('get_url')
def get_url(self, item):
request = None
ticket_id: int = None
if 'view' in self._context:
if hasattr(self._context['view'], 'request'):
request = self._context['view'].request
if 'ticket_id' in self._kwargs['context']['view'].kwargs:
ticket_id = int(self._kwargs['context']['view'].kwargs['ticket_id'])
return {
'_self': item.get_url( ticket_id = ticket_id, request = request ),
}
class Meta:
model = RelatedTickets
fields = [
'id',
'display_name',
'to_ticket_id',
'from_ticket_id',
'how_related',
'organization',
'_urls',
]
read_only_fields = [
'id',
'display_name',
'to_ticket_id',
'from_ticket_id',
'how_related',
'organization',
'_urls',
]
class RelatedTicketViewSerializer(RelatedTicketModelSerializer):
from_ticket_id = TicketBaseSerializer()
organization = OrganizationBaseSerializer(many=False, read_only=True)
to_ticket_id = TicketBaseSerializer()

View File

@ -0,0 +1,78 @@
from django.db.models import Q
from drf_spectacular.utils import extend_schema, extend_schema_view, OpenApiResponse
from access.mixin import OrganizationMixin
from api.viewsets.common import ModelListRetrieveDeleteViewSet
from core.serializers.ticket_related import (
RelatedTickets,
RelatedTicketModelSerializer,
RelatedTicketViewSerializer,
)
@extend_schema_view(
destroy = extend_schema(
summary = 'Delete a related ticket',
description = '',
responses = {
204: OpenApiResponse(description=''),
403: OpenApiResponse(description='User is missing delete permissions'),
}
),
list = extend_schema(
summary = 'Fetch all related tickets',
description='',
responses = {
200: OpenApiResponse(description='', response=RelatedTicketViewSerializer),
403: OpenApiResponse(description='User is missing view permissions'),
}
),
retrieve = extend_schema(
summary = 'Fetch a related ticket',
description='',
responses = {
200: OpenApiResponse(description='', response=RelatedTicketViewSerializer),
403: OpenApiResponse(description='User is missing view permissions'),
}
),
)
class ViewSet(ModelListRetrieveDeleteViewSet):
filterset_fields = [
'organization',
]
search_fields = [
'name',
]
model = RelatedTickets
def get_serializer_class(self):
if (
self.action == 'list'
or self.action == 'retrieve'
):
return globals()[str( self.model._meta.verbose_name).replace(' ', '') + 'ViewSerializer']
return globals()[str( self.model._meta.verbose_name).replace(' ', '') + 'ModelSerializer']
def get_queryset(self):
self.queryset = RelatedTickets.objects.filter(
Q(from_ticket_id_id=self.kwargs['ticket_id'])
|
Q(to_ticket_id_id=self.kwargs['ticket_id'])
)
return self.queryset.filter().order_by('id')