Switch to a resource and service based API from previous app based one.
Signed-off-by: Michael Price <loneviking72@gmail.com>
This commit is contained in:
committed by
Roberto Rosario
parent
f3427c7470
commit
b4bf9bfaee
@@ -28,19 +28,19 @@ urlpatterns = [
|
||||
|
||||
api_urls = [
|
||||
url(
|
||||
r'^object/(?P<app_label>[-\w]+)/(?P<model>[-\w]+)/(?P<object_pk>\d+)/acls/$',
|
||||
r'^objects/(?P<app_label>[-\w]+)/(?P<model>[-\w]+)/(?P<object_pk>\d+)/acls/$',
|
||||
APIObjectACLListView.as_view(), name='accesscontrollist-list'
|
||||
),
|
||||
url(
|
||||
r'^object/(?P<app_label>[-\w]+)/(?P<model>[-\w]+)/(?P<object_pk>\d+)/acls/(?P<pk>\d+)/$',
|
||||
r'^objects/(?P<app_label>[-\w]+)/(?P<model>[-\w]+)/(?P<object_pk>\d+)/acls/(?P<pk>\d+)/$',
|
||||
APIObjectACLView.as_view(), name='accesscontrollist-detail'
|
||||
),
|
||||
url(
|
||||
r'^object/(?P<app_label>[-\w]+)/(?P<model>[-\w]+)/(?P<object_pk>\d+)/acls/(?P<pk>\d+)/permissions/$',
|
||||
r'^objects/(?P<app_label>[-\w]+)/(?P<model>[-\w]+)/(?P<object_pk>\d+)/acls/(?P<pk>\d+)/permissions/$',
|
||||
APIObjectACLPermissionListView.as_view(), name='accesscontrollist-permission-list'
|
||||
),
|
||||
url(
|
||||
r'^object/(?P<app_label>[-\w]+)/(?P<model>[-\w]+)/(?P<object_pk>\d+)/acls/(?P<pk>\d+)/permissions/(?P<permission_pk>\d+)/$',
|
||||
r'^objects/(?P<app_label>[-\w]+)/(?P<model>[-\w]+)/(?P<object_pk>\d+)/acls/(?P<pk>\d+)/permissions/(?P<permission_pk>\d+)/$',
|
||||
APIObjectACLPermissionView.as_view(), name='accesscontrollist-permission-detail'
|
||||
),
|
||||
]
|
||||
|
||||
@@ -92,7 +92,7 @@ class APICheckedoutDocumentView(generics.RetrieveDestroyAPIView):
|
||||
def get_queryset(self):
|
||||
if self.request.method == 'GET':
|
||||
filtered_documents = AccessControlList.objects.filter_by_access(
|
||||
(permission_document_view,), self.request.user,
|
||||
permission=permission_document_view, user=self.request.user,
|
||||
queryset=DocumentCheckout.objects.checked_out_documents()
|
||||
)
|
||||
|
||||
|
||||
@@ -26,11 +26,11 @@ urlpatterns = [
|
||||
|
||||
api_urls = [
|
||||
url(
|
||||
r'^documents/$', APICheckedoutDocumentListView.as_view(),
|
||||
r'^checkouts/$', APICheckedoutDocumentListView.as_view(),
|
||||
name='checkout-document-list'
|
||||
),
|
||||
url(
|
||||
r'^documents/(?P<pk>[0-9]+)/$', APICheckedoutDocumentView.as_view(),
|
||||
r'^documents/(?P<pk>[0-9]+)/checkout_info/$', APICheckedoutDocumentView.as_view(),
|
||||
name='checkedout-document-view'
|
||||
),
|
||||
]
|
||||
|
||||
@@ -25,11 +25,11 @@ urlpatterns = [
|
||||
|
||||
api_urls = [
|
||||
url(
|
||||
r'^document/(?P<document_pk>[0-9]+)/comments/$',
|
||||
r'^documents/(?P<document_pk>[0-9]+)/comments/$',
|
||||
APICommentListView.as_view(), name='comment-list'
|
||||
),
|
||||
url(
|
||||
r'^document/(?P<document_pk>[0-9]+)/comments/(?P<comment_pk>[0-9]+)/$',
|
||||
r'^documents/(?P<document_pk>[0-9]+)/comments/(?P<comment_pk>[0-9]+)/$',
|
||||
APICommentView.as_view(), name='comment-detail'
|
||||
),
|
||||
]
|
||||
|
||||
@@ -72,12 +72,12 @@ urlpatterns = [
|
||||
|
||||
api_urls = [
|
||||
url(
|
||||
r'^index/node/(?P<pk>[0-9]+)/documents/$',
|
||||
r'^indexes/node/(?P<pk>[0-9]+)/documents/$',
|
||||
APIIndexNodeInstanceDocumentListView.as_view(),
|
||||
name='index-node-documents'
|
||||
),
|
||||
url(
|
||||
r'^index/template/(?P<pk>[0-9]+)/$', APIIndexTemplateView.as_view(),
|
||||
r'^indexes/template/(?P<pk>[0-9]+)/$', APIIndexTemplateView.as_view(),
|
||||
name='index-template-detail'
|
||||
),
|
||||
url(
|
||||
@@ -85,12 +85,12 @@ api_urls = [
|
||||
name='index-detail'
|
||||
),
|
||||
url(
|
||||
r'^index/(?P<pk>[0-9]+)/template/$',
|
||||
r'^indexes/(?P<pk>[0-9]+)/template/$',
|
||||
APIIndexTemplateListView.as_view(), name='index-template-detail'
|
||||
),
|
||||
url(r'^indexes/$', APIIndexListView.as_view(), name='index-list'),
|
||||
url(
|
||||
r'^document/(?P<pk>[0-9]+)/indexes/$',
|
||||
r'^documents/(?P<pk>[0-9]+)/indexes/$',
|
||||
APIDocumentIndexListView.as_view(), name='document-index-list'
|
||||
),
|
||||
]
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
from django.shortcuts import get_object_or_404
|
||||
|
||||
from rest_framework import generics
|
||||
from rest_framework.response import Response
|
||||
|
||||
from documents.models import DocumentPage
|
||||
from documents.models import Document, DocumentPage
|
||||
from rest_api.permissions import MayanPermission
|
||||
|
||||
from .models import DocumentPageContent
|
||||
@@ -14,20 +16,24 @@ from .serializers import DocumentPageContentSerializer
|
||||
class APIDocumentPageContentView(generics.RetrieveAPIView):
|
||||
"""
|
||||
Returns the content of the selected document page.
|
||||
---
|
||||
GET:
|
||||
parameters:
|
||||
- name: pk
|
||||
paramType: path
|
||||
type: number
|
||||
"""
|
||||
|
||||
lookup_url_kwarg = 'page_pk'
|
||||
mayan_object_permissions = {
|
||||
'GET': (permission_content_view,),
|
||||
}
|
||||
permission_classes = (MayanPermission,)
|
||||
serializer_class = DocumentPageContentSerializer
|
||||
queryset = DocumentPage.objects.all()
|
||||
|
||||
def get_document(self):
|
||||
return get_object_or_404(Document, pk=self.kwargs['document_pk'])
|
||||
|
||||
def get_document_version(self):
|
||||
return get_object_or_404(
|
||||
self.get_document().versions.all(), pk=self.kwargs['version_pk']
|
||||
)
|
||||
|
||||
def get_queryset(self):
|
||||
return self.get_document_version().pages.all()
|
||||
|
||||
def retrieve(self, request, *args, **kwargs):
|
||||
instance = self.get_object()
|
||||
|
||||
@@ -40,7 +40,8 @@ urlpatterns = [
|
||||
|
||||
api_urls = [
|
||||
url(
|
||||
r'^page/(?P<pk>\d+)/content/$', APIDocumentPageContentView.as_view(),
|
||||
r'^documents/(?P<document_pk>\d+)/versions/(?P<version_pk>\d+)/pages/(?P<page_pk>\d+)/content/$',
|
||||
APIDocumentPageContentView.as_view(),
|
||||
name='document-page-content-view'
|
||||
),
|
||||
]
|
||||
|
||||
@@ -215,20 +215,20 @@ api_urls = [
|
||||
APIWorkflowTransitionView.as_view(), name='workflowtransition-detail'
|
||||
),
|
||||
url(
|
||||
r'^document/(?P<pk>[0-9]+)/workflows/$',
|
||||
r'^documents/(?P<pk>[0-9]+)/workflows/$',
|
||||
APIWorkflowInstanceListView.as_view(), name='workflowinstance-list'
|
||||
),
|
||||
url(
|
||||
r'^document/(?P<pk>[0-9]+)/workflows/(?P<workflow_pk>[0-9]+)/$',
|
||||
r'^documents/(?P<pk>[0-9]+)/workflows/(?P<workflow_pk>[0-9]+)/$',
|
||||
APIWorkflowInstanceView.as_view(), name='workflowinstance-detail'
|
||||
),
|
||||
url(
|
||||
r'^document/(?P<pk>[0-9]+)/workflows/(?P<workflow_pk>[0-9]+)/log_entries/$',
|
||||
r'^documents/(?P<pk>[0-9]+)/workflows/(?P<workflow_pk>[0-9]+)/log_entries/$',
|
||||
APIWorkflowInstanceLogEntryListView.as_view(),
|
||||
name='workflowinstancelogentry-list'
|
||||
),
|
||||
url(
|
||||
r'^document_type/(?P<pk>[0-9]+)/workflows/$',
|
||||
r'^document_types/(?P<pk>[0-9]+)/workflows/$',
|
||||
APIDocumentTypeWorkflowListView.as_view(),
|
||||
name='documenttype-workflow-list'
|
||||
),
|
||||
|
||||
@@ -32,7 +32,7 @@ from events.permissions import permission_events_view
|
||||
from mayan.celery import app
|
||||
from mayan_statistics.classes import StatisticNamespace, CharJSLine
|
||||
from navigation import SourceColumn
|
||||
from rest_api.classes import APIEndPoint
|
||||
from rest_api.classes import APIEndPoint, APIResource
|
||||
from rest_api.fields import DynamicSerializerField
|
||||
|
||||
from .dashboard_widgets import (
|
||||
@@ -107,7 +107,6 @@ from .widgets import (
|
||||
widget_document_version_page_number
|
||||
)
|
||||
|
||||
|
||||
class DocumentsApp(MayanAppConfig):
|
||||
has_tests = True
|
||||
name = 'documents'
|
||||
@@ -118,6 +117,9 @@ class DocumentsApp(MayanAppConfig):
|
||||
from actstream import registry
|
||||
|
||||
APIEndPoint(app=self, version_string='1')
|
||||
APIResource(label=_('Document types'), name='document_types')
|
||||
APIResource(label=_('Documents'), name='documents')
|
||||
APIResource(label=_('Trashed documents'), name='trashed_documents')
|
||||
|
||||
DeletedDocument = self.get_model('DeletedDocument')
|
||||
Document = self.get_model('Document')
|
||||
|
||||
@@ -35,7 +35,7 @@ api_urls = [
|
||||
name='search-view'
|
||||
),
|
||||
url(
|
||||
r'^advanced/(?P<search_model>[\.\w]+)/$', APIAdvancedSearchView.as_view(),
|
||||
r'^search/advanced/(?P<search_model>[\.\w]+)/$', APIAdvancedSearchView.as_view(),
|
||||
name='advanced-search-view'
|
||||
),
|
||||
]
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
from django.shortcuts import get_object_or_404
|
||||
|
||||
from rest_framework import generics, status
|
||||
from rest_framework.response import Response
|
||||
|
||||
@@ -26,10 +28,6 @@ class APIDocumentOCRView(generics.GenericAPIView):
|
||||
Submit a document for OCR.
|
||||
---
|
||||
omit_serializer: true
|
||||
parameters:
|
||||
- name: pk
|
||||
paramType: path
|
||||
type: number
|
||||
responseMessages:
|
||||
- code: 202
|
||||
message: Accepted
|
||||
@@ -40,12 +38,19 @@ class APIDocumentOCRView(generics.GenericAPIView):
|
||||
|
||||
|
||||
class APIDocumentVersionOCRView(generics.GenericAPIView):
|
||||
lookup_url_kwarg = 'version_pk'
|
||||
mayan_object_permissions = {
|
||||
'POST': (permission_ocr_document,)
|
||||
}
|
||||
permission_classes = (MayanPermission,)
|
||||
queryset = DocumentVersion.objects.all()
|
||||
|
||||
def get_document(self):
|
||||
return get_object_or_404(Document, pk=self.kwargs['document_pk'])
|
||||
|
||||
def get_queryset(self):
|
||||
return self.get_document().versions.all()
|
||||
|
||||
def get_serializer_class(self):
|
||||
return None
|
||||
|
||||
@@ -54,10 +59,6 @@ class APIDocumentVersionOCRView(generics.GenericAPIView):
|
||||
Submit a document version for OCR.
|
||||
---
|
||||
omit_serializer: true
|
||||
parameters:
|
||||
- name: pk
|
||||
paramType: path
|
||||
type: number
|
||||
responseMessages:
|
||||
- code: 202
|
||||
message: Accepted
|
||||
@@ -70,20 +71,24 @@ class APIDocumentVersionOCRView(generics.GenericAPIView):
|
||||
class APIDocumentPageOCRContentView(generics.RetrieveAPIView):
|
||||
"""
|
||||
Returns the OCR content of the selected document page.
|
||||
---
|
||||
GET:
|
||||
parameters:
|
||||
- name: pk
|
||||
paramType: path
|
||||
type: number
|
||||
"""
|
||||
|
||||
lookup_url_kwarg = 'page_pk'
|
||||
mayan_object_permissions = {
|
||||
'GET': (permission_ocr_content_view,),
|
||||
}
|
||||
permission_classes = (MayanPermission,)
|
||||
serializer_class = DocumentPageOCRContentSerializer
|
||||
queryset = DocumentPage.objects.all()
|
||||
|
||||
def get_document(self):
|
||||
return get_object_or_404(Document, pk=self.kwargs['document_pk'])
|
||||
|
||||
def get_document_version(self):
|
||||
return get_object_or_404(
|
||||
self.get_document().versions.all(), pk=self.kwargs['version_pk']
|
||||
)
|
||||
|
||||
def get_queryset(self):
|
||||
return self.get_document_version().pages.all()
|
||||
|
||||
def retrieve(self, request, *args, **kwargs):
|
||||
instance = self.get_object()
|
||||
|
||||
@@ -60,7 +60,7 @@ class OCRAPITestCase(BaseAPITestCase):
|
||||
def _request_document_version_ocr_submit_view(self):
|
||||
return self.post(
|
||||
viewname='rest_api:document-version-ocr-submit-view',
|
||||
args=(self.document.latest_version.pk,)
|
||||
args=(self.document.pk, self.document.latest_version.pk,)
|
||||
)
|
||||
|
||||
def test_submit_document_version_no_access(self):
|
||||
@@ -80,8 +80,11 @@ class OCRAPITestCase(BaseAPITestCase):
|
||||
|
||||
def _request_document_page_content_view(self):
|
||||
return self.get(
|
||||
viewname='rest_api:document-page-content-view',
|
||||
args=(self.document.latest_version.pages.first().pk,)
|
||||
viewname='rest_api:document-page-ocr-content-view',
|
||||
args=(
|
||||
self.document.pk, self.document.latest_version.pk,
|
||||
self.document.latest_version.pages.first().pk,
|
||||
)
|
||||
)
|
||||
|
||||
def test_get_document_version_page_content_no_access(self):
|
||||
|
||||
@@ -47,17 +47,17 @@ urlpatterns = [
|
||||
|
||||
api_urls = [
|
||||
url(
|
||||
r'^document/(?P<pk>\d+)/submit/$', APIDocumentOCRView.as_view(),
|
||||
r'^documents/(?P<pk>\d+)/submit/$', APIDocumentOCRView.as_view(),
|
||||
name='document-ocr-submit-view'
|
||||
),
|
||||
url(
|
||||
r'^document_version/(?P<pk>\d+)/submit/$',
|
||||
r'^documents/(?P<document_pk>\d+)/versions/(?P<version_pk>\d+)/ocr/$',
|
||||
APIDocumentVersionOCRView.as_view(),
|
||||
name='document-version-ocr-submit-view'
|
||||
),
|
||||
url(
|
||||
r'^page/(?P<pk>\d+)/content/$',
|
||||
r'^documents/(?P<document_pk>\d+)/versions/(?P<version_pk>\d+)/pages/(?P<page_pk>\d+)/ocr/$',
|
||||
APIDocumentPageOCRContentView.as_view(),
|
||||
name='document-page-content-view'
|
||||
name='document-page-ocr-content-view'
|
||||
),
|
||||
]
|
||||
|
||||
18
mayan/apps/rest_api/api_views.py
Normal file
18
mayan/apps/rest_api/api_views.py
Normal file
@@ -0,0 +1,18 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from rest_framework import generics
|
||||
|
||||
from rest_api.filters import MayanObjectPermissionsFilter
|
||||
from rest_api.permissions import MayanPermission
|
||||
|
||||
from .classes import APIResource
|
||||
from .serializers import APIResourceSerializer
|
||||
|
||||
|
||||
class APIResourceTypeListView(generics.ListAPIView):
|
||||
"""
|
||||
Returns a list of all the available API resources.
|
||||
"""
|
||||
serializer_class = APIResourceSerializer
|
||||
def get_queryset(self):
|
||||
return APIResource.all()
|
||||
@@ -5,10 +5,35 @@ from django.conf import settings
|
||||
from django.utils.encoding import force_text, python_2_unicode_compatible
|
||||
from django.utils.module_loading import import_string
|
||||
|
||||
from .exceptions import APIResourcePatternError
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class APIResource(object):
|
||||
_registry = {}
|
||||
|
||||
@classmethod
|
||||
def all(cls):
|
||||
return cls._registry.values()
|
||||
|
||||
@classmethod
|
||||
def get(cls, name):
|
||||
return cls._registry[name]
|
||||
|
||||
def __str__(self):
|
||||
return force_text(self.name)
|
||||
|
||||
def __init__(self, name, label, description=None):
|
||||
self.label = label
|
||||
self.name = name
|
||||
self.description = description
|
||||
self.__class__._registry[self.name] = self
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class APIEndPoint(object):
|
||||
_registry = {}
|
||||
_patterns = []
|
||||
|
||||
@classmethod
|
||||
def get_all(cls):
|
||||
@@ -48,6 +73,12 @@ class APIEndPoint(object):
|
||||
def register_urls(self, urlpatterns):
|
||||
from .urls import urlpatterns as app_urls
|
||||
|
||||
app_urls += [
|
||||
url(r'^%s/' % (self.name or self.app.name), include(urlpatterns)),
|
||||
]
|
||||
for url in urlpatterns:
|
||||
if url.regex.pattern not in self.__class__._patterns:
|
||||
app_urls.append(url)
|
||||
self.__class__._patterns.append(url.regex.pattern)
|
||||
else:
|
||||
raise APIResourcePatternError(
|
||||
'App "{}" tried to register API URL pattern "{}", which '
|
||||
'already exists'.format(self.app.label, url.regex.pattern)
|
||||
)
|
||||
|
||||
16
mayan/apps/rest_api/exceptions.py
Normal file
16
mayan/apps/rest_api/exceptions.py
Normal file
@@ -0,0 +1,16 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
class APIError(Exception):
|
||||
"""
|
||||
Base exception for the API app
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class APIResourcePatternError(APIError):
|
||||
"""
|
||||
Raised when an app tries to override an existing URL regular expression
|
||||
pattern
|
||||
"""
|
||||
pass
|
||||
9
mayan/apps/rest_api/serializers.py
Normal file
9
mayan/apps/rest_api/serializers.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from rest_framework import serializers
|
||||
|
||||
|
||||
class APIResourceSerializer(serializers.Serializer):
|
||||
description = serializers.CharField()
|
||||
label = serializers.CharField()
|
||||
name = serializers.CharField()
|
||||
@@ -2,15 +2,18 @@ from __future__ import unicode_literals
|
||||
|
||||
from django.conf.urls import url
|
||||
|
||||
from .views import APIBase, APIAppView, BrowseableObtainAuthToken
|
||||
from .api_views import APIResourceTypeListView
|
||||
from .views import APIBase, BrowseableObtainAuthToken
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
]
|
||||
urlpatterns = []
|
||||
|
||||
api_urls = [
|
||||
url(r'^$', APIBase.as_view(), name='api_root'),
|
||||
url(r'^api/(?P<path>.*)/?$', APIAppView.as_view(), name='api_app'),
|
||||
url(
|
||||
r'^resources/$', APIResourceTypeListView.as_view(),
|
||||
name='resource-list'
|
||||
),
|
||||
url(
|
||||
r'^auth/token/obtain/$', BrowseableObtainAuthToken.as_view(),
|
||||
name='auth_token_obtain'
|
||||
|
||||
@@ -13,13 +13,6 @@ class APIBase(SwaggerResourcesView):
|
||||
renderer_classes = (renderers.BrowsableAPIRenderer, renderers.JSONRenderer)
|
||||
|
||||
|
||||
class APIAppView(SwaggerApiView):
|
||||
"""
|
||||
Entry points of the selected app.
|
||||
"""
|
||||
renderer_classes = (renderers.BrowsableAPIRenderer, renderers.JSONRenderer)
|
||||
|
||||
|
||||
class BrowseableObtainAuthToken(ObtainAuthToken):
|
||||
"""
|
||||
Obtain an API authentication token.
|
||||
|
||||
Reference in New Issue
Block a user