Files
mayan-edms/mayan/apps/document_states/api_views.py
Roberto Rosario deb363ee16 API updates
- Move all generic API classes definitions to the rest_api.generics
module.
- Update API status code on insufficient access for the apps:
indexes, parsing, documents, metadata, ocr, permission,
user management.
- Update API tests.

Signed-off-by: Roberto Rosario <roberto.rosario@mayan-edms.com>
2019-10-28 00:01:11 -04:00

561 lines
17 KiB
Python

from __future__ import absolute_import, unicode_literals
from django.http import HttpResponse
from django.shortcuts import get_object_or_404
from django.views.decorators.cache import cache_control, patch_cache_control
from mayan.apps.acls.models import AccessControlList
from mayan.apps.documents.models import Document, DocumentType
from mayan.apps.documents.permissions import permission_document_type_view
from mayan.apps.rest_api import generics
from .literals import WORKFLOW_IMAGE_TASK_TIMEOUT
from .models import Workflow
from .permissions import (
permission_workflow_create, permission_workflow_delete,
permission_workflow_edit, permission_workflow_view
)
from .serializers import (
NewWorkflowDocumentTypeSerializer, WorkflowDocumentTypeSerializer,
WorkflowInstanceSerializer, WorkflowInstanceLogEntrySerializer,
WorkflowSerializer, WorkflowStateSerializer, WorkflowTransitionSerializer,
WritableWorkflowInstanceLogEntrySerializer, WritableWorkflowSerializer,
WritableWorkflowTransitionSerializer
)
from .settings import settings_workflow_image_cache_time
from .tasks import task_generate_workflow_image
class APIDocumentTypeWorkflowRuntimeProxyListView(generics.ListAPIView):
"""
get: Returns a list of all the document type workflows.
"""
mayan_object_permissions = {
'GET': (permission_workflow_view,),
}
serializer_class = WorkflowSerializer
def get_document_type(self):
document_type = get_object_or_404(
klass=DocumentType, pk=self.kwargs['pk']
)
AccessControlList.objects.check_access(
obj=document_type, permissions=(permission_document_type_view,),
user=self.request.user
)
return document_type
def get_queryset(self):
return self.get_document_type().workflows.all()
class APIWorkflowDocumentTypeList(generics.ListCreateAPIView):
"""
get: Returns a list of all the document types attached to a workflow.
post: Attach a document type to a specified workflow.
"""
mayan_object_permissions = {
'GET': (permission_document_type_view,),
}
def get_queryset(self):
"""
This view returns a list of document types that belong to a workflow.
"""
return self.get_workflow().document_types.all()
def get_serializer(self, *args, **kwargs):
if not self.request:
return None
return super(APIWorkflowDocumentTypeList, self).get_serializer(*args, **kwargs)
def get_serializer_class(self):
if self.request.method == 'GET':
return WorkflowDocumentTypeSerializer
elif self.request.method == 'POST':
return NewWorkflowDocumentTypeSerializer
def get_serializer_context(self):
"""
Extra context provided to the serializer class.
"""
context = super(APIWorkflowDocumentTypeList, self).get_serializer_context()
if self.kwargs:
context.update(
{
'workflow': self.get_workflow(),
}
)
return context
def get_workflow(self):
"""
Retrieve the parent workflow of the workflow document type.
Perform custom permission and access check.
"""
if self.request.method == 'GET':
permission_required = permission_workflow_view
else:
permission_required = permission_workflow_edit
workflow = get_object_or_404(klass=Workflow, pk=self.kwargs['pk'])
AccessControlList.objects.check_access(
obj=workflow, permissions=(permission_required,),
user=self.request.user
)
return workflow
class APIWorkflowDocumentTypeView(generics.RetrieveDestroyAPIView):
"""
delete: Remove a document type from the selected workflow.
get: Returns the details of the selected workflow document type.
"""
lookup_url_kwarg = 'document_type_pk'
mayan_object_permissions = {
'GET': (permission_document_type_view,),
}
serializer_class = WorkflowDocumentTypeSerializer
def get_queryset(self):
return self.get_workflow().document_types.all()
def get_serializer_context(self):
"""
Extra context provided to the serializer class.
"""
context = super(APIWorkflowDocumentTypeView, self).get_serializer_context()
if self.kwargs:
context.update(
{
'workflow': self.get_workflow(),
}
)
return context
def get_workflow(self):
"""
This view returns a document types that belongs to a workflow
RESEARCH: Could the documents.api_views.APIDocumentTypeView class
be subclasses for this?
RESEARCH: Since this is a parent-child API view could this be made
into a generic API class?
RESEARCH: Reuse get_workflow method from APIWorkflowDocumentTypeList?
"""
if self.request.method == 'GET':
permission_required = permission_workflow_view
else:
permission_required = permission_workflow_edit
workflow = get_object_or_404(klass=Workflow, pk=self.kwargs['pk'])
AccessControlList.objects.check_access(
obj=workflow, permissions=(permission_required,),
user=self.request.user
)
return workflow
def perform_destroy(self, instance):
"""
RESEARCH: Move this kind of methods to the serializer instead it that
ability becomes available in Django REST framework
"""
self.get_workflow().document_types.remove(instance)
class APIWorkflowImageView(generics.RetrieveAPIView):
"""
get: Returns an image representation of the selected workflow.
"""
mayan_object_permissions = {
'GET': (permission_workflow_view,),
}
queryset = Workflow.objects.all()
def get_serializer(self, *args, **kwargs):
return None
def get_serializer_class(self):
return None
@cache_control(private=True)
def retrieve(self, request, *args, **kwargs):
task = task_generate_workflow_image.apply_async(
kwargs=dict(
document_state_id=self.get_object().pk,
)
)
cache_filename = task.get(timeout=WORKFLOW_IMAGE_TASK_TIMEOUT)
cache_file = self.get_object().cache_partition.get_file(filename=cache_filename)
with cache_file.open() as file_object:
response = HttpResponse(file_object.read(), content_type='image')
if '_hash' in request.GET:
patch_cache_control(
response,
max_age=settings_workflow_image_cache_time.value
)
return response
class APIWorkflowRuntimeProxyListView(generics.ListCreateAPIView):
"""
get: Returns a list of all the workflows.
post: Create a new workflow.
"""
mayan_object_permissions = {'GET': (permission_workflow_view,)}
mayan_view_permissions = {'POST': (permission_workflow_create,)}
queryset = Workflow.objects.all()
def get_serializer(self, *args, **kwargs):
if not self.request:
return None
return super(APIWorkflowRuntimeProxyListView, self).get_serializer(*args, **kwargs)
def get_serializer_class(self):
if self.request.method == 'GET':
return WorkflowSerializer
else:
return WritableWorkflowSerializer
class APIWorkflowView(generics.RetrieveUpdateDestroyAPIView):
"""
delete: Delete the selected workflow.
get: Return the details of the selected workflow.
patch: Edit the selected workflow.
put: Edit the selected workflow.
"""
mayan_object_permissions = {
'DELETE': (permission_workflow_delete,),
'GET': (permission_workflow_view,),
'PATCH': (permission_workflow_edit,),
'PUT': (permission_workflow_edit,)
}
queryset = Workflow.objects.all()
def get_serializer(self, *args, **kwargs):
if not self.request:
return None
return super(APIWorkflowView, self).get_serializer(*args, **kwargs)
def get_serializer_class(self):
if self.request.method == 'GET':
return WorkflowSerializer
else:
return WritableWorkflowSerializer
# Workflow state views
class APIWorkflowStateListView(generics.ListCreateAPIView):
"""
get: Returns a list of all the workflow states.
post: Create a new workflow state.
"""
serializer_class = WorkflowStateSerializer
def get_queryset(self):
return self.get_workflow().states.all()
def get_serializer_context(self):
"""
Extra context provided to the serializer class.
"""
context = super(APIWorkflowStateListView, self).get_serializer_context()
if self.kwargs:
context.update(
{
'workflow': self.get_workflow(),
}
)
return context
def get_workflow(self):
if self.request.method == 'GET':
permission_required = permission_workflow_view
else:
permission_required = permission_workflow_edit
workflow = get_object_or_404(klass=Workflow, pk=self.kwargs['pk'])
AccessControlList.objects.check_access(
obj=workflow, permissions=(permission_required,),
user=self.request.user
)
return workflow
class APIWorkflowStateView(generics.RetrieveUpdateDestroyAPIView):
"""
delete: Delete the selected workflow state.
get: Return the details of the selected workflow state.
patch: Edit the selected workflow state.
put: Edit the selected workflow state.
"""
lookup_url_kwarg = 'state_pk'
serializer_class = WorkflowStateSerializer
def get_queryset(self):
return self.get_workflow().states.all()
def get_serializer_context(self):
"""
Extra context provided to the serializer class.
"""
context = super(APIWorkflowStateView, self).get_serializer_context()
if self.kwargs:
context.update(
{
'workflow': self.get_workflow(),
}
)
return context
def get_workflow(self):
if self.request.method == 'GET':
permission_required = permission_workflow_view
else:
permission_required = permission_workflow_edit
workflow = get_object_or_404(klass=Workflow, pk=self.kwargs['pk'])
AccessControlList.objects.check_access(
obj=workflow, permissions=(permission_required,),
user=self.request.user
)
return workflow
# Workflow transition views
class APIWorkflowTransitionListView(generics.ListCreateAPIView):
"""
get: Returns a list of all the workflow transitions.
post: Create a new workflow transition.
"""
def get_queryset(self):
return self.get_workflow().transitions.all()
def get_serializer(self, *args, **kwargs):
if not self.request:
return None
return super(APIWorkflowTransitionListView, self).get_serializer(*args, **kwargs)
def get_serializer_class(self):
if self.request.method == 'GET':
return WorkflowTransitionSerializer
else:
return WritableWorkflowTransitionSerializer
def get_serializer_context(self):
"""
Extra context provided to the serializer class.
"""
context = super(APIWorkflowTransitionListView, self).get_serializer_context()
if self.kwargs:
context.update(
{
'workflow': self.get_workflow(),
}
)
return context
def get_workflow(self):
if self.request.method == 'GET':
permission_required = permission_workflow_view
else:
permission_required = permission_workflow_edit
workflow = get_object_or_404(klass=Workflow, pk=self.kwargs['pk'])
AccessControlList.objects.check_access(
obj=workflow, permissions=(permission_required,),
user=self.request.user
)
return workflow
class APIWorkflowTransitionView(generics.RetrieveUpdateDestroyAPIView):
"""
delete: Delete the selected workflow transition.
get: Return the details of the selected workflow transition.
patch: Edit the selected workflow transition.
put: Edit the selected workflow transition.
"""
lookup_url_kwarg = 'transition_pk'
def get_queryset(self):
return self.get_workflow().transitions.all()
def get_serializer(self, *args, **kwargs):
if not self.request:
return None
return super(APIWorkflowTransitionView, self).get_serializer(*args, **kwargs)
def get_serializer_class(self):
if self.request.method == 'GET':
return WorkflowTransitionSerializer
else:
return WritableWorkflowTransitionSerializer
def get_serializer_context(self):
"""
Extra context provided to the serializer class.
"""
context = super(APIWorkflowTransitionView, self).get_serializer_context()
if self.kwargs:
context.update(
{
'workflow': self.get_workflow(),
}
)
return context
def get_workflow(self):
if self.request.method == 'GET':
permission_required = permission_workflow_view
else:
permission_required = permission_workflow_edit
workflow = get_object_or_404(klass=Workflow, pk=self.kwargs['pk'])
AccessControlList.objects.check_access(
obj=workflow, permissions=(permission_required,),
user=self.request.user
)
return workflow
# Document workflow views
class APIWorkflowInstanceListView(generics.ListAPIView):
"""
get: Returns a list of all the document workflows.
"""
serializer_class = WorkflowInstanceSerializer
mayan_object_permissions = {
'GET': (permission_workflow_view,),
}
def get_document(self):
document = get_object_or_404(klass=Document, pk=self.kwargs['pk'])
AccessControlList.objects.check_access(
obj=document, permissions=(permission_workflow_view,),
user=self.request.user
)
return document
def get_queryset(self):
return self.get_document().workflows.all()
class APIWorkflowInstanceView(generics.RetrieveAPIView):
"""
get: Return the details of the selected document workflow.
"""
lookup_url_kwarg = 'workflow_pk'
mayan_object_permissions = {
'GET': (permission_workflow_view,),
}
serializer_class = WorkflowInstanceSerializer
def get_document(self):
document = get_object_or_404(klass=Document, pk=self.kwargs['pk'])
AccessControlList.objects.check_access(
obj=document, permissions=(permission_workflow_view,),
user=self.request.user
)
return document
def get_queryset(self):
return self.get_document().workflows.all()
class APIWorkflowInstanceLogEntryListView(generics.ListCreateAPIView):
"""
get: Returns a list of all the document workflows log entries.
post: Transition a document workflow by creating a new document workflow log entry.
"""
def get_document(self):
document = get_object_or_404(klass=Document, pk=self.kwargs['pk'])
if self.request.method == 'GET':
"""
Only test for permission if reading. If writing, the permission
will be checked in the serializer
IMPROVEMENT:
When writing, add check for permission or ACL for the workflow.
Failing that, check for ACLs for any of the workflow's transitions.
Failing that, then raise PermissionDenied
"""
# TODO: Improvement above
AccessControlList.objects.check_access(
obj=document, permissions=(permission_workflow_view,),
user=self.request.user
)
return document
def get_serializer(self, *args, **kwargs):
if not self.request:
return None
return super(APIWorkflowInstanceLogEntryListView, self).get_serializer(*args, **kwargs)
def get_serializer_class(self):
if self.request.method == 'GET':
return WorkflowInstanceLogEntrySerializer
else:
return WritableWorkflowInstanceLogEntrySerializer
def get_serializer_context(self):
context = super(APIWorkflowInstanceLogEntryListView, self).get_serializer_context()
if self.kwargs:
context.update(
{
'workflow_instance': self.get_workflow_instance(),
}
)
return context
def get_queryset(self):
return self.get_workflow_instance().log_entries.all()
def get_workflow_instance(self):
workflow = get_object_or_404(
klass=self.get_document().workflows, pk=self.kwargs['workflow_pk']
)
return workflow