feat(itim): Add Project Task API v2 endpoint

ref: #248 #377
This commit is contained in:
2024-11-02 16:12:09 +09:30
parent 7fc5138fcd
commit 74d55fb81e
7 changed files with 389 additions and 26 deletions

View File

@ -78,6 +78,7 @@ from project_management.viewsets import (
project as project_v2, project as project_v2,
project_milestone as project_milestone_v2, project_milestone as project_milestone_v2,
project_state as project_state_v2, project_state as project_state_v2,
project_task,
project_type as project_type_v2, project_type as project_type_v2,
) )
@ -155,6 +156,7 @@ router.register('project_management', project_management_v2.Index, basename='_ap
router.register('project_management/project', project_v2.ViewSet, basename='_api_v2_project') router.register('project_management/project', project_v2.ViewSet, basename='_api_v2_project')
router.register('project_management/project/(?P<project_id>[0-9]+)/milestone', project_milestone_v2.ViewSet, basename='_api_v2_project_milestone') router.register('project_management/project/(?P<project_id>[0-9]+)/milestone', project_milestone_v2.ViewSet, basename='_api_v2_project_milestone')
router.register('project_management/project/(?P<project_id>[0-9]+)/notes', notes_v2.ViewSet, basename='_api_v2_project_notes') router.register('project_management/project/(?P<project_id>[0-9]+)/notes', notes_v2.ViewSet, basename='_api_v2_project_notes')
router.register('project_management/project/(?P<project_id>[0-9]+)/project_task', project_task.ViewSet, basename='_api_v2_ticket_project_task')
router.register('settings', settings_index_v2.Index, basename='_api_v2_settings_home') router.register('settings', settings_index_v2.Index, basename='_api_v2_settings_home')

View File

@ -6,6 +6,7 @@ from api.views.core.tickets import View
@extend_schema(deprecated=True)
class View(View): class View(View):
_ticket_type:str = 'project_task' _ticket_type:str = 'project_task'

View File

@ -29,12 +29,26 @@ class TicketBaseSerializer(serializers.ModelSerializer):
context = self.context.copy() context = self.context.copy()
return reverse( ticket_type = str(item.get_ticket_type_display()).lower().replace(' ', '_')
"v2:_api_v2_ticket_" + str(item.get_ticket_type_display()).lower() + "-detail",
request=context['view'].request, if ticket_type == 'project_task':
kwargs={
kwargs: dict = {
'project_id': item.project.id,
'pk': item.pk 'pk': item.pk
} }
else:
kwargs: dict = {
'pk': item.pk
}
return reverse(
"v2:_api_v2_ticket_" + ticket_type + "-detail",
request=context['view'].request,
kwargs = kwargs
) )
@ -65,13 +79,29 @@ class TicketModelSerializer(TicketBaseSerializer):
context = self.context.copy() context = self.context.copy()
context = self.context.copy()
ticket_type = str(item.get_ticket_type_display()).lower().replace(' ', '_')
if ticket_type == 'project_task':
kwargs: dict = {
'project_id': item.project.id,
'pk': item.pk
}
else:
kwargs: dict = {
'pk': item.pk
}
return { return {
'_self': reverse( '_self': reverse(
"v2:_api_v2_ticket_" + str(item.get_ticket_type_display()).lower() + "-detail", "v2:_api_v2_ticket_" + ticket_type + "-detail",
request=context['view'].request, request=context['view'].request,
kwargs={ kwargs = kwargs
'pk': item.pk
}
), ),
'comments': reverse('v2:_api_v2_ticket_comments-list', request=context['view'].request, kwargs={'ticket_id': item.pk}), 'comments': reverse('v2:_api_v2_ticket_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}),

View File

@ -43,6 +43,14 @@ from itim.serializers.problem import (
ProblemTicketViewSerializer, ProblemTicketViewSerializer,
) )
from project_management.serializers.project_task import (
ProjectTaskAddTicketModelSerializer,
ProjectTaskChangeTicketModelSerializer,
ProjectTaskImportTicketModelSerializer,
ProjectTaskTriageTicketModelSerializer,
ProjectTaskTicketModelSerializer,
ProjectTaskTicketViewSerializer,
)
from settings.models.user_settings import UserSettings from settings.models.user_settings import UserSettings
@ -181,9 +189,18 @@ class TicketViewSet(ModelViewSet):
self.get_ticket_type() self.get_ticket_type()
queryset = super().get_queryset().filter( if str(self._ticket_type).lower().replace(' ', '_') == 'project_task':
ticket_type = self._ticket_type_id
) queryset = super().get_queryset().filter(
project_id = int(self.kwargs['project_id'])
)
else:
queryset = super().get_queryset().filter(
ticket_type = self._ticket_type_id
)
self.queryset = queryset self.queryset = queryset
@ -198,9 +215,9 @@ class TicketViewSet(ModelViewSet):
ticket_types = [e for e in Ticket.TicketType] ticket_types = [e for e in Ticket.TicketType]
for i in range( 1, len(ticket_types) ): for i in range( 0, len(ticket_types) ):
if self._ticket_type.lower() == str(ticket_types[i - 1].label).lower(): if self._ticket_type.lower() == str(ticket_types[i].label).lower():
ticket_type_id = i ticket_type_id = i
@ -220,7 +237,7 @@ class TicketViewSet(ModelViewSet):
self.get_ticket_type() self.get_ticket_type()
serializer_prefix = self._ticket_type serializer_prefix = str(self._ticket_type).replace(' ', '')
if ( if (
@ -262,48 +279,47 @@ class TicketViewSet(ModelViewSet):
if self.has_organization_permission( if self.has_organization_permission(
organization = organization, organization = organization,
permissions_required = [ permissions_required = [
'core.import_ticket_request' 'core.import_ticket_' + str(self._ticket_type).lower().replace(' ', '_')
] ]
): ):
serializer_prefix = self._ticket_type + 'Import' serializer_prefix = serializer_prefix + 'Import'
elif self.has_organization_permission( elif self.has_organization_permission(
organization = organization, organization = organization,
permissions_required = [ permissions_required = [
'core.triage_ticket_request' 'core.triage_ticket_' + str(self._ticket_type).lower().replace(' ', '_')
] ]
): ):
serializer_prefix = self._ticket_type + 'Triage' serializer_prefix = serializer_prefix + 'Triage'
elif self.has_organization_permission( elif self.has_organization_permission(
organization = organization, organization = organization,
permissions_required = [ permissions_required = [
'core.change_ticket_request' 'core.change_ticket_' + str(self._ticket_type).lower().replace(' ', '_')
] ]
): ):
serializer_prefix = self._ticket_type + 'Change' serializer_prefix = serializer_prefix + 'Change'
elif self.has_organization_permission( elif self.has_organization_permission(
organization = organization, organization = organization,
permissions_required = [ permissions_required = [
'core.add_ticket_request' 'core.add_ticket_' + str(self._ticket_type).lower().replace(' ', '_')
] ]
): ):
serializer_prefix = self._ticket_type + 'Add' serializer_prefix = serializer_prefix + 'Add'
elif self.has_organization_permission( elif self.has_organization_permission(
organization = organization, organization = organization,
permissions_required = [ permissions_required = [
'core.view_ticket_request' 'core.view_ticket_' + str(self._ticket_type).lower().replace(' ', '_')
] ]
): ):
serializer_prefix = self._ticket_type + 'View' serializer_prefix = serializer_prefix + 'View'
if ( if (

View File

@ -66,7 +66,13 @@ class ProjectModelSerializer(ProjectBaseSerializer):
), ),
'milestone': reverse("v2:_api_v2_project_milestone-list", request=self._context['view'].request, kwargs={'project_id': item.pk}), 'milestone': reverse("v2:_api_v2_project_milestone-list", request=self._context['view'].request, kwargs={'project_id': item.pk}),
'notes': reverse("v2:_api_v2_project_notes-list", request=self._context['view'].request, kwargs={'project_id': item.pk}), 'notes': reverse("v2:_api_v2_project_notes-list", request=self._context['view'].request, kwargs={'project_id': item.pk}),
'tickets': 'ToDo' 'tickets': reverse(
"v2:_api_v2_ticket_project_task-list",
request=self._context['view'].request,
kwargs={
'project_id': item.pk
}
),
} }

View File

@ -0,0 +1,225 @@
from rest_framework import serializers
from app.serializers.user import UserBaseSerializer
from core.serializers.ticket import (
Ticket,
TicketBaseSerializer,
TicketModelSerializer,
TicketViewSerializer
)
class ProjectTaskTicketBaseSerializer(
TicketBaseSerializer
):
class Meta( TicketBaseSerializer.Meta ):
pass
class ProjectTaskTicketModelSerializer(
ProjectTaskTicketBaseSerializer,
TicketModelSerializer,
):
status = serializers.ChoiceField([(e.value, e.label) for e in Ticket.TicketStatus.ProjectTask])
class Meta( TicketModelSerializer.Meta ):
fields = [
'id',
'assigned_teams',
'assigned_users',
'category',
'created',
'modified',
'status',
'status_badge',
'title',
'description',
'estimate',
'duration',
'urgency',
'impact',
'priority',
'external_ref',
'external_system',
'ticket_type',
'is_deleted',
'date_closed',
'opened_by',
'organization',
'project',
'milestone',
'subscribed_teams',
'subscribed_users',
'_urls',
]
read_only_fields = [
'id',
'display_name',
'external_ref',
'external_system',
'status_badge',
'ticket_type',
'_urls',
]
class ProjectTaskAddTicketModelSerializer(
ProjectTaskTicketModelSerializer,
):
"""Serializer for `Add` user
Args:
ProjectTaskTicketModelSerializer (class): Model Serializer
"""
class Meta(ProjectTaskTicketModelSerializer.Meta):
read_only_fields = [
'id',
'assigned_teams',
'assigned_users',
'category',
'created',
'modified',
'status',
'status_badge',
'estimate',
'duration',
'impact',
'priority',
'external_ref',
'external_system',
'ticket_type',
'is_deleted',
'date_closed',
'planned_start_date',
'planned_finish_date',
'real_start_date',
'real_finish_date',
'opened_by',
'organization',
'project',
'milestone',
'subscribed_teams',
'subscribed_users',
'_urls',
]
class ProjectTaskChangeTicketModelSerializer(
ProjectTaskTicketModelSerializer,
):
"""Serializer for `ProjectTask` user
Args:
ProjectTaskTicketModelSerializer (class): ProjectTask Model Serializer
"""
class Meta(ProjectTaskTicketModelSerializer.Meta):
read_only_fields = [
'id',
'assigned_teams',
'assigned_users',
'category',
'created',
'modified',
'status',
'status_badge',
'estimate',
'duration',
'impact',
'priority',
'external_ref',
'external_system',
'ticket_type',
'is_deleted',
'date_closed',
'planned_start_date',
'planned_finish_date',
'real_start_date',
'real_finish_date',
'opened_by',
'organization',
'project',
'milestone',
'subscribed_teams',
'subscribed_users',
'_urls',
]
class ProjectTaskTriageTicketModelSerializer(
ProjectTaskTicketModelSerializer,
):
"""Serializer for `Triage` user
Args:
ProjectTaskTicketModelSerializer (class): ProjectTask Model Serializer
"""
class Meta(ProjectTaskTicketModelSerializer.Meta):
read_only_fields = [
'id',
'created',
'modified',
'status_badge',
'estimate',
'duration',
'external_ref',
'external_system',
'ticket_type',
'is_deleted',
'date_closed',
'planned_start_date',
'planned_finish_date',
'real_start_date',
'real_finish_date',
'opened_by',
'organization',
'_urls',
]
class ProjectTaskImportTicketModelSerializer(
ProjectTaskTicketModelSerializer,
):
"""Serializer for `Import` user
Args:
ProjectTaskTicketModelSerializer (class): ProjectTask Model Serializer
"""
class Meta(ProjectTaskTicketModelSerializer.Meta):
read_only_fields = [
'id',
'display_name',
'status_badge',
'ticket_type',
'_urls',
]
class ProjectTaskTicketViewSerializer(
ProjectTaskTicketModelSerializer,
TicketViewSerializer,
):
pass

View File

@ -0,0 +1,83 @@
from drf_spectacular.utils import (
extend_schema,
extend_schema_view,
OpenApiResponse,
PolymorphicProxySerializer,
)
from project_management.serializers.project_task import (
ProjectTaskAddTicketModelSerializer,
ProjectTaskChangeTicketModelSerializer,
ProjectTaskImportTicketModelSerializer,
ProjectTaskTriageTicketModelSerializer,
ProjectTaskTicketViewSerializer,
)
from core.viewsets.ticket import TicketViewSet
@extend_schema_view(
create=extend_schema(
summary = 'Create a Project Task',
description='',
request = PolymorphicProxySerializer(
component_name = 'ProjectTask',
serializers=[
ProjectTaskImportTicketModelSerializer,
ProjectTaskAddTicketModelSerializer,
ProjectTaskChangeTicketModelSerializer,
ProjectTaskTriageTicketModelSerializer,
],
resource_type_field_name=None,
many = False
),
responses = {
201: OpenApiResponse(description='Created', response=ProjectTaskTicketViewSerializer),
403: OpenApiResponse(description='User is missing add permissions'),
}
),
destroy = extend_schema(
summary = 'Delete a Project Task',
description = '',
responses = {
204: OpenApiResponse(description=''),
403: OpenApiResponse(description='User is missing delete permissions'),
}
),
list = extend_schema(
summary = 'Fetch all Project Task',
description='',
responses = {
200: OpenApiResponse(description='', response=ProjectTaskTicketViewSerializer),
403: OpenApiResponse(description='User is missing view permissions'),
}
),
retrieve = extend_schema(
summary = 'Fetch a Project Task',
description='',
responses = {
200: OpenApiResponse(description='', response=ProjectTaskTicketViewSerializer),
403: OpenApiResponse(description='User is missing view permissions'),
}
),
update = extend_schema(exclude = True),
partial_update = extend_schema(
summary = 'Update a Project Task',
description = '',
responses = {
200: OpenApiResponse(description='', response=ProjectTaskTicketViewSerializer),
403: OpenApiResponse(description='User is missing change permissions'),
}
),
)
class ViewSet(TicketViewSet):
"""Change Ticket
This class exists only for the purpose of swagger for documentation.
Args:
TicketViewSet (class): Base Ticket ViewSet.
"""
_ticket_type: str = 'Project Task'