Convert the API URL system from an App based one
to a resource based one. Signed-off-by: Roberto Rosario <roberto.rosario.gonzalez@gmail.com>
This commit is contained in:
@@ -71,7 +71,8 @@ Backward incompatible changes
|
|||||||
Bugs fixed or issues closed
|
Bugs fixed or issues closed
|
||||||
===========================
|
===========================
|
||||||
|
|
||||||
* `GitLab issue #378 <https://gitlab.com/mayan-edms/mayan-edms/issues/378>`_ Add metadata widget changes from @Macrobb
|
* `GitLab issue #366 <https://gitlab.com/mayan-edms/mayan-edms/issues/366>`_ Proofread documentation
|
||||||
|
* `GitLab issue #379 <https://gitlab.com/mayan-edms/mayan-edms/issues/379>`_ Add new document version list view permission.
|
||||||
* `GitLab issue #379 <https://gitlab.com/mayan-edms/mayan-edms/issues/379>`_ Add new document version list view permission.
|
* `GitLab issue #379 <https://gitlab.com/mayan-edms/mayan-edms/issues/379>`_ Add new document version list view permission.
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,8 @@ Advanced deployment
|
|||||||
===================
|
===================
|
||||||
|
|
||||||
Mayan EDMS should be deployed like any other Django_ project and
|
Mayan EDMS should be deployed like any other Django_ project and
|
||||||
preferably using virtualenv_.
|
preferably using virtualenv_. Below are some ways to deploy and use Mayan EDMS.
|
||||||
|
Do not use more than one method.
|
||||||
|
|
||||||
Being a Django_ and a Python_ project, familiarity with these technologies is
|
Being a Django_ and a Python_ project, familiarity with these technologies is
|
||||||
recommended to better understand why Mayan EDMS does some of the things it
|
recommended to better understand why Mayan EDMS does some of the things it
|
||||||
@@ -58,7 +59,7 @@ to /usr/bin/ with ...
|
|||||||
|
|
||||||
sudo ln -s /opt/local/bin/tesseract /usr/bin/tesseract
|
sudo ln -s /opt/local/bin/tesseract /usr/bin/tesseract
|
||||||
|
|
||||||
... alternatively set the paths in the ``settings/locals.py``
|
Alternatively, set the paths in the ``settings/locals.py``
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
@@ -76,9 +77,9 @@ With Homebrew installed run the command:
|
|||||||
Set the Binary paths
|
Set the Binary paths
|
||||||
********************
|
********************
|
||||||
|
|
||||||
Mayan EDMS by default will look in /usr/bin/ for the binary files it needs
|
Mayan EDMS by default will look in /usr/bin/ for the binary files it needs.
|
||||||
so either you can symlink the binaries installed via brew in /usr/local/bin/
|
You can symlink the binaries installed via brew in /usr/local/bin/
|
||||||
to /usr/bin/ with ...
|
to /usr/bin/ with:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
@@ -87,7 +88,7 @@ to /usr/bin/ with ...
|
|||||||
sudo ln -s /usr/local/bin/pdftotext /usr/bin/pdftotext && \
|
sudo ln -s /usr/local/bin/pdftotext /usr/bin/pdftotext && \
|
||||||
sudo ln -s /usr/local/bin/gs /usr/bin/gs
|
sudo ln -s /usr/local/bin/gs /usr/bin/gs
|
||||||
|
|
||||||
... alternatively set the paths in the ``settings/locals.py``
|
Alternatively, set the paths in the ``settings/locals.py``
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
@@ -265,15 +266,18 @@ Make the installation directory readable and writable by the webserver user::
|
|||||||
|
|
||||||
chown www-data:www-data /usr/share/mayan-edms -R
|
chown www-data:www-data /usr/share/mayan-edms -R
|
||||||
|
|
||||||
Restart the services::
|
Enable and restart the services [1_]::
|
||||||
|
|
||||||
systemctl enable supervisor
|
systemctl enable supervisor
|
||||||
systemctl restart supervisor
|
systemctl restart supervisor
|
||||||
systemctl restart nginx
|
systemctl restart nginx
|
||||||
|
|
||||||
|
[1]: https://bugs.launchpad.net/ubuntu/+source/supervisor/+bug/1594740
|
||||||
|
|
||||||
.. _Debian: http://www.debian.org/
|
.. _Debian: http://www.debian.org/
|
||||||
.. _Django: http://www.djangoproject.com/
|
.. _Django: http://www.djangoproject.com/
|
||||||
.. _Python: http://www.python.org/
|
.. _Python: http://www.python.org/
|
||||||
.. _SQLite: https://www.sqlite.org/
|
.. _SQLite: https://www.sqlite.org/
|
||||||
.. _Ubuntu: http://www.ubuntu.com/
|
.. _Ubuntu: http://www.ubuntu.com/
|
||||||
.. _virtualenv: http://www.virtualenv.org/en/latest/index.html
|
.. _virtualenv: http://www.virtualenv.org/en/latest/index.html
|
||||||
|
.. _1: https://bugs.launchpad.net/ubuntu/+source/supervisor/+bug/1594740
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ request on GitLab_.
|
|||||||
Project philosophies
|
Project philosophies
|
||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
How to think about Mayan EDMS when doing changes or adding new features,
|
How to think about Mayan EDMS when doing changes or adding new features;
|
||||||
why things are the way they are in Mayan EDMS.
|
why things are the way they are in Mayan EDMS:
|
||||||
|
|
||||||
- Functionality must be as market/sector independent as possible, code for the
|
- Functionality must be as market/sector independent as possible, code for the
|
||||||
95% of use cases.
|
95% of use cases.
|
||||||
@@ -36,7 +36,7 @@ why things are the way they are in Mayan EDMS.
|
|||||||
not viable/mature/efficient.
|
not viable/mature/efficient.
|
||||||
- Each app is as independent and self contained as possible. Exceptions, the
|
- Each app is as independent and self contained as possible. Exceptions, the
|
||||||
basic requirements: navigation, permissions, common, main.
|
basic requirements: navigation, permissions, common, main.
|
||||||
- If an app is meant to be used by more than one other app it should be as
|
- If an app is meant to be used by more than one other app, it should be as
|
||||||
generic as possible in regard to the project and another app will bridge the functionality.
|
generic as possible in regard to the project and another app will bridge the functionality.
|
||||||
|
|
||||||
- Example: since indexing (document_indexing) only applies to documents, the
|
- Example: since indexing (document_indexing) only applies to documents, the
|
||||||
@@ -48,7 +48,7 @@ Coding conventions
|
|||||||
|
|
||||||
Follow PEP8
|
Follow PEP8
|
||||||
~~~~~~~~~~~
|
~~~~~~~~~~~
|
||||||
Whenever possible, but don't obsess over things like line length.
|
Whenever possible, but don't obsess over things like line length:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
@@ -103,9 +103,9 @@ Example:
|
|||||||
)
|
)
|
||||||
from .models import Index, IndexInstanceNode, DocumentRenameCount
|
from .models import Index, IndexInstanceNode, DocumentRenameCount
|
||||||
|
|
||||||
All local app module imports are in relative form, local app module name is to
|
All local app module imports are in relative form. Local app module name is to
|
||||||
be referenced as little as possible, unless required by a specific feature,
|
be referenced as little as possible, unless required by a specific feature,
|
||||||
trick, restriction, ie: Runtime modification of the module's attributes.
|
trick, restriction (e.g., Runtime modification of the module's attributes).
|
||||||
|
|
||||||
Incorrect:
|
Incorrect:
|
||||||
|
|
||||||
@@ -128,7 +128,7 @@ Dependencies
|
|||||||
Mayan EDMS apps follow a hierarchical model of dependency. Apps import from
|
Mayan EDMS apps follow a hierarchical model of dependency. Apps import from
|
||||||
their parents or siblings, never from their children. Think plugins. A parent
|
their parents or siblings, never from their children. Think plugins. A parent
|
||||||
app must never assume anything about a possible existing child app. The
|
app must never assume anything about a possible existing child app. The
|
||||||
documents app and the Document model are the basic entities they must never
|
documents app and the Document model are the basic entities; they must never
|
||||||
import anything else. The common and main apps are the base apps.
|
import anything else. The common and main apps are the base apps.
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ Features
|
|||||||
* Dynamic default values for metadata.
|
* Dynamic default values for metadata.
|
||||||
|
|
||||||
* Metadata fields can have an initial value, which can be static or determined
|
* Metadata fields can have an initial value, which can be static or determined
|
||||||
by an user provided template code snippet.
|
by a template code snippet provided by the user.
|
||||||
|
|
||||||
* Documents can be uploaded from different sources.
|
* Documents can be uploaded from different sources.
|
||||||
|
|
||||||
@@ -68,7 +68,7 @@ Features
|
|||||||
|
|
||||||
* Multi page document support.
|
* Multi page document support.
|
||||||
|
|
||||||
* Multiple page PDFs and TIFFs files are supported.
|
* Multiple page PDF and TIFF files are supported.
|
||||||
|
|
||||||
* Automatic OCR processing.
|
* Automatic OCR processing.
|
||||||
|
|
||||||
|
|||||||
@@ -2,21 +2,20 @@
|
|||||||
Transformations
|
Transformations
|
||||||
===============
|
===============
|
||||||
|
|
||||||
Transformation are persistent manipulations to the previews of the stored
|
Transformations are persistent manipulations to the previews of the stored
|
||||||
documents. For example: a scanning equipment may only produce landscape PDFs.
|
documents. For example: a scanning equipment may only produce landscape PDFs.
|
||||||
In this case an useful transformation for that document source would be to
|
In this case a useful transformation for that document source would be to rotate
|
||||||
rotate all documents scanned by 270 degrees after being uploaded, this way
|
all scanned documents by 270 degrees after being uploaded. By adding this
|
||||||
whenever a document is uploaded from that scanner it will appear in portrait
|
transformation to the Mayan EDMS source that is connected to the scanner, all
|
||||||
orientation. In this case add a this transformation to the Mayan EDMS source
|
pages scanned via that source will inherit the transformation as they are
|
||||||
that is connected to that device this way all pages scanned via that source
|
created. The result is that whenever a document is uploaded from that scanner,
|
||||||
with inherit the transformation as they are created.
|
it will appear in portrait orientation, instead of landscape orientation.
|
||||||
|
|
||||||
Transformations can also be added to existing documents, by clicking on a
|
Transformations can also be added to existing documents by clicking on a
|
||||||
document's page, then clicking on "transformations". In this view the Actions
|
document's page and then clicking on "transformations". In this view the Actions
|
||||||
menu will have a new option that reads "Create new transformation". At the
|
menu will have a new option that reads "Create new transformation". Currently,
|
||||||
moment the rotation, zoom, crop, and resize transformations are available.
|
the available transformations are: rotation, zoom, crop, and resize. Once the
|
||||||
Once the document image has been corrected resubmit it for OCR for improved
|
document image has been corrected, resubmit it for OCR for improved results.
|
||||||
results.
|
|
||||||
|
|
||||||
Transformations are not destructive and do not physically modify the document
|
Transformations are not destructive and do not physically modify the document
|
||||||
file, they just modify the document's graphical representation.
|
file, they just modify the document's graphical representation.
|
||||||
|
|||||||
@@ -115,8 +115,10 @@ class AccessControlListManager(models.Manager):
|
|||||||
|
|
||||||
def filter_by_access(self, permission, user, queryset):
|
def filter_by_access(self, permission, user, queryset):
|
||||||
if user.is_superuser or user.is_staff:
|
if user.is_superuser or user.is_staff:
|
||||||
logger.debug('Unfiltered queryset returned to user "%s" as superuser or staff',
|
logger.debug(
|
||||||
user)
|
'Unfiltered queryset returned to user "%s" as superuser '
|
||||||
|
'or staff', user
|
||||||
|
)
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -28,19 +28,19 @@ urlpatterns = [
|
|||||||
|
|
||||||
api_urls = [
|
api_urls = [
|
||||||
url(
|
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'
|
APIObjectACLListView.as_view(), name='accesscontrollist-list'
|
||||||
),
|
),
|
||||||
url(
|
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'
|
APIObjectACLView.as_view(), name='accesscontrollist-detail'
|
||||||
),
|
),
|
||||||
url(
|
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'
|
APIObjectACLPermissionListView.as_view(), name='accesscontrollist-permission-list'
|
||||||
),
|
),
|
||||||
url(
|
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'
|
APIObjectACLPermissionView.as_view(), name='accesscontrollist-permission-detail'
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,20 +1,13 @@
|
|||||||
from __future__ import absolute_import, unicode_literals
|
from __future__ import absolute_import, unicode_literals
|
||||||
|
|
||||||
import pytz
|
from rest_framework import generics
|
||||||
|
|
||||||
from django.shortcuts import get_object_or_404
|
|
||||||
|
|
||||||
from rest_framework import generics, status
|
|
||||||
from rest_framework.response import Response
|
|
||||||
|
|
||||||
from acls.models import AccessControlList
|
from acls.models import AccessControlList
|
||||||
from documents.models import Document
|
|
||||||
from documents.permissions import permission_document_view
|
from documents.permissions import permission_document_view
|
||||||
|
|
||||||
from .models import DocumentCheckout
|
from .models import DocumentCheckout
|
||||||
from .permissions import (
|
from .permissions import (
|
||||||
permission_document_checkout, permission_document_checkin,
|
permission_document_checkin, permission_document_checkin_override
|
||||||
permission_document_checkin_override
|
|
||||||
)
|
)
|
||||||
from .serializers import (
|
from .serializers import (
|
||||||
DocumentCheckoutSerializer, NewDocumentCheckoutSerializer
|
DocumentCheckoutSerializer, NewDocumentCheckoutSerializer
|
||||||
@@ -47,12 +40,23 @@ class APICheckedoutDocumentListView(generics.ListCreateAPIView):
|
|||||||
APICheckedoutDocumentListView, self
|
APICheckedoutDocumentListView, self
|
||||||
).get(request, *args, **kwargs)
|
).get(request, *args, **kwargs)
|
||||||
|
|
||||||
|
def get_serializer_context(self):
|
||||||
|
"""
|
||||||
|
Extra context provided to the serializer class.
|
||||||
|
"""
|
||||||
|
return {
|
||||||
|
'format': self.format_kwarg,
|
||||||
|
'request': self.request,
|
||||||
|
'view': self
|
||||||
|
}
|
||||||
|
|
||||||
|
'''
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Checkout a document.
|
Checkout a document.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
serializer = self.get_serializer(data=request.DATA, files=request.FILES)
|
serializer = self.get_serializer(data=request.data, files=request.file)
|
||||||
|
|
||||||
if serializer.is_valid():
|
if serializer.is_valid():
|
||||||
document = get_object_or_404(
|
document = get_object_or_404(
|
||||||
@@ -83,6 +87,7 @@ class APICheckedoutDocumentListView(generics.ListCreateAPIView):
|
|||||||
return Response(status=status.HTTP_201_CREATED)
|
return Response(status=status.HTTP_201_CREATED)
|
||||||
|
|
||||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
class APICheckedoutDocumentView(generics.RetrieveDestroyAPIView):
|
class APICheckedoutDocumentView(generics.RetrieveDestroyAPIView):
|
||||||
|
|||||||
@@ -57,7 +57,10 @@ class CheckoutsApp(MayanAppConfig):
|
|||||||
|
|
||||||
Document.add_to_class(
|
Document.add_to_class(
|
||||||
'check_in',
|
'check_in',
|
||||||
lambda document, user=None: DocumentCheckout.objects.check_in_document(document, user)
|
lambda document,
|
||||||
|
user=None: DocumentCheckout.objects.check_in_document(
|
||||||
|
document, user
|
||||||
|
)
|
||||||
)
|
)
|
||||||
Document.add_to_class(
|
Document.add_to_class(
|
||||||
'checkout_info',
|
'checkout_info',
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
class DocumentCheckoutManager(models.Manager):
|
class DocumentCheckoutManager(models.Manager):
|
||||||
def checkout_document(self, document, expiration_datetime, user, block_new_version=True):
|
def checkout_document(self, document, expiration_datetime, user, block_new_version=True):
|
||||||
self.create(
|
return self.create(
|
||||||
document=document, expiration_datetime=expiration_datetime,
|
document=document, expiration_datetime=expiration_datetime,
|
||||||
user=user, block_new_version=block_new_version
|
user=user, block_new_version=block_new_version
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,15 +1,19 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
from acls.models import AccessControlList
|
||||||
|
from documents.models import Document
|
||||||
|
from documents.serializers import DocumentSerializer
|
||||||
|
|
||||||
from .models import DocumentCheckout
|
from .models import DocumentCheckout
|
||||||
|
from .permissions import permission_document_checkout
|
||||||
|
|
||||||
|
|
||||||
class DocumentCheckoutSerializer(serializers.ModelSerializer):
|
class DocumentCheckoutSerializer(serializers.ModelSerializer):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
# Hide this import otherwise strange circular import error occur
|
|
||||||
from documents.serializers import DocumentSerializer
|
|
||||||
|
|
||||||
super(DocumentCheckoutSerializer, self).__init__(*args, **kwargs)
|
super(DocumentCheckoutSerializer, self).__init__(*args, **kwargs)
|
||||||
self.fields['document'] = DocumentSerializer()
|
self.fields['document'] = DocumentSerializer()
|
||||||
|
|
||||||
@@ -17,7 +21,33 @@ class DocumentCheckoutSerializer(serializers.ModelSerializer):
|
|||||||
model = DocumentCheckout
|
model = DocumentCheckout
|
||||||
|
|
||||||
|
|
||||||
class NewDocumentCheckoutSerializer(serializers.Serializer):
|
class NewDocumentCheckoutSerializer(serializers.ModelSerializer):
|
||||||
document = serializers.IntegerField()
|
|
||||||
expiration_datetime = serializers.DateTimeField()
|
|
||||||
block_new_version = serializers.BooleanField()
|
block_new_version = serializers.BooleanField()
|
||||||
|
document_pk = serializers.IntegerField(
|
||||||
|
help_text=_('Primary key of the document to be checked out.'),
|
||||||
|
write_only=True
|
||||||
|
)
|
||||||
|
expiration_datetime = serializers.DateTimeField()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
fields = (
|
||||||
|
'block_new_version', 'document', 'document_pk',
|
||||||
|
'expiration_datetime', 'id'
|
||||||
|
)
|
||||||
|
model = DocumentCheckout
|
||||||
|
read_only_fields = ('document',)
|
||||||
|
write_only_fields = ('document_pk',)
|
||||||
|
|
||||||
|
def create(self, validated_data):
|
||||||
|
document = Document.objects.get(pk=validated_data.pop('document_pk'))
|
||||||
|
|
||||||
|
AccessControlList.objects.check_access(
|
||||||
|
permissions=permission_document_checkout,
|
||||||
|
user=self.context['request'].user, obj=document
|
||||||
|
)
|
||||||
|
|
||||||
|
validated_data['document'] = document
|
||||||
|
validated_data['user'] = self.context['request'].user
|
||||||
|
return super(NewDocumentCheckoutSerializer, self).create(
|
||||||
|
validated_data
|
||||||
|
)
|
||||||
|
|||||||
75
mayan/apps/checkouts/tests/test_api.py
Normal file
75
mayan/apps/checkouts/tests/test_api.py
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
from django.core.urlresolvers import reverse
|
||||||
|
from django.test import override_settings
|
||||||
|
from django.utils.encoding import force_text
|
||||||
|
from django.utils.timezone import now
|
||||||
|
|
||||||
|
from rest_framework.test import APITestCase
|
||||||
|
|
||||||
|
from documents.models import DocumentType
|
||||||
|
from documents.tests import TEST_DOCUMENT_TYPE, TEST_SMALL_DOCUMENT_PATH
|
||||||
|
from user_management.tests.literals import (
|
||||||
|
TEST_ADMIN_EMAIL, TEST_ADMIN_PASSWORD, TEST_ADMIN_USERNAME
|
||||||
|
)
|
||||||
|
|
||||||
|
from ..models import DocumentCheckout
|
||||||
|
|
||||||
|
|
||||||
|
@override_settings(OCR_AUTO_OCR=False)
|
||||||
|
class CheckoutAPITestCase(APITestCase):
|
||||||
|
def setUp(self):
|
||||||
|
super(CheckoutAPITestCase, self).setUp()
|
||||||
|
|
||||||
|
self.admin_user = get_user_model().objects.create_superuser(
|
||||||
|
username=TEST_ADMIN_USERNAME, email=TEST_ADMIN_EMAIL,
|
||||||
|
password=TEST_ADMIN_PASSWORD
|
||||||
|
)
|
||||||
|
|
||||||
|
self.client.login(
|
||||||
|
username=TEST_ADMIN_USERNAME, password=TEST_ADMIN_PASSWORD
|
||||||
|
)
|
||||||
|
|
||||||
|
self.document_type = DocumentType.objects.create(
|
||||||
|
label=TEST_DOCUMENT_TYPE
|
||||||
|
)
|
||||||
|
|
||||||
|
with open(TEST_SMALL_DOCUMENT_PATH) as file_object:
|
||||||
|
self.document = self.document_type.new_document(
|
||||||
|
file_object=file_object,
|
||||||
|
)
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
self.document_type.delete()
|
||||||
|
super(CheckoutAPITestCase, self).tearDown()
|
||||||
|
|
||||||
|
def test_document_checkout_get_view(self):
|
||||||
|
expiration_datetime = now() + datetime.timedelta(days=1)
|
||||||
|
|
||||||
|
DocumentCheckout.objects.checkout_document(
|
||||||
|
document=self.document, expiration_datetime=expiration_datetime,
|
||||||
|
user=self.admin_user, block_new_version=True
|
||||||
|
)
|
||||||
|
|
||||||
|
response = self.client.get(reverse('rest_api:checkout-document-list'))
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
response.data['results'][0]['document']['uuid'],
|
||||||
|
force_text(self.document.uuid)
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_document_checkout_post_view(self):
|
||||||
|
response = self.client.post(
|
||||||
|
reverse('rest_api:checkout-document-list'), data={
|
||||||
|
'document_pk': self.document.pk,
|
||||||
|
'expiration_datetime': '2099-01-01T12:00'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(response.status_code, 201)
|
||||||
|
self.assertEqual(
|
||||||
|
DocumentCheckout.objects.first().document, self.document
|
||||||
|
)
|
||||||
@@ -26,11 +26,11 @@ urlpatterns = [
|
|||||||
|
|
||||||
api_urls = [
|
api_urls = [
|
||||||
url(
|
url(
|
||||||
r'^documents/$', APICheckedoutDocumentListView.as_view(),
|
r'^checkouts/$', APICheckedoutDocumentListView.as_view(),
|
||||||
name='checkout-document-list'
|
name='checkout-document-list'
|
||||||
),
|
),
|
||||||
url(
|
url(
|
||||||
r'^documents/(?P<pk>[0-9]+)/$', APICheckedoutDocumentView.as_view(),
|
r'^checkouts/(?P<pk>[0-9]+)/$', APICheckedoutDocumentView.as_view(),
|
||||||
name='checkedout-document-view'
|
name='checkedout-document-view'
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -25,11 +25,11 @@ urlpatterns = [
|
|||||||
|
|
||||||
api_urls = [
|
api_urls = [
|
||||||
url(
|
url(
|
||||||
r'^document/(?P<document_pk>[0-9]+)/comments/$',
|
r'^documents/(?P<document_pk>[0-9]+)/comments/$',
|
||||||
APICommentListView.as_view(), name='comment-list'
|
APICommentListView.as_view(), name='comment-list'
|
||||||
),
|
),
|
||||||
url(
|
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'
|
APICommentView.as_view(), name='comment-detail'
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -72,12 +72,12 @@ urlpatterns = [
|
|||||||
|
|
||||||
api_urls = [
|
api_urls = [
|
||||||
url(
|
url(
|
||||||
r'^index/node/(?P<pk>[0-9]+)/documents/$',
|
r'^indexes/node/(?P<pk>[0-9]+)/documents/$',
|
||||||
APIIndexNodeInstanceDocumentListView.as_view(),
|
APIIndexNodeInstanceDocumentListView.as_view(),
|
||||||
name='index-node-documents'
|
name='index-node-documents'
|
||||||
),
|
),
|
||||||
url(
|
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'
|
name='index-template-detail'
|
||||||
),
|
),
|
||||||
url(
|
url(
|
||||||
@@ -85,12 +85,12 @@ api_urls = [
|
|||||||
name='index-detail'
|
name='index-detail'
|
||||||
),
|
),
|
||||||
url(
|
url(
|
||||||
r'^index/(?P<pk>[0-9]+)/template/$',
|
r'^indexes/(?P<pk>[0-9]+)/template/$',
|
||||||
APIIndexTemplateListView.as_view(), name='index-template-detail'
|
APIIndexTemplateListView.as_view(), name='index-template-detail'
|
||||||
),
|
),
|
||||||
url(r'^indexes/$', APIIndexListView.as_view(), name='index-list'),
|
url(r'^indexes/$', APIIndexListView.as_view(), name='index-list'),
|
||||||
url(
|
url(
|
||||||
r'^document/(?P<pk>[0-9]+)/indexes/$',
|
r'^documents/(?P<pk>[0-9]+)/indexes/$',
|
||||||
APIDocumentIndexListView.as_view(), name='document-index-list'
|
APIDocumentIndexListView.as_view(), name='document-index-list'
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -164,20 +164,20 @@ api_urls = [
|
|||||||
APIWorkflowTransitionView.as_view(), name='workflowtransition-detail'
|
APIWorkflowTransitionView.as_view(), name='workflowtransition-detail'
|
||||||
),
|
),
|
||||||
url(
|
url(
|
||||||
r'^document/(?P<pk>[0-9]+)/workflows/$',
|
r'^documents/(?P<pk>[0-9]+)/workflows/$',
|
||||||
APIWorkflowInstanceListView.as_view(), name='workflowinstance-list'
|
APIWorkflowInstanceListView.as_view(), name='workflowinstance-list'
|
||||||
),
|
),
|
||||||
url(
|
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'
|
APIWorkflowInstanceView.as_view(), name='workflowinstance-detail'
|
||||||
),
|
),
|
||||||
url(
|
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(),
|
APIWorkflowInstanceLogEntryListView.as_view(),
|
||||||
name='workflowinstancelogentry-list'
|
name='workflowinstancelogentry-list'
|
||||||
),
|
),
|
||||||
url(
|
url(
|
||||||
r'^document_type/(?P<pk>[0-9]+)/workflows/$',
|
r'^document_types/(?P<pk>[0-9]+)/workflows/$',
|
||||||
APIDocumentTypeWorkflowListView.as_view(),
|
APIDocumentTypeWorkflowListView.as_view(),
|
||||||
name='documenttype-workflow-list'
|
name='documenttype-workflow-list'
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ from events.links import link_events_for_object
|
|||||||
from events.permissions import permission_events_view
|
from events.permissions import permission_events_view
|
||||||
from mayan.celery import app
|
from mayan.celery import app
|
||||||
from navigation import SourceColumn
|
from navigation import SourceColumn
|
||||||
from rest_api.classes import APIEndPoint
|
from rest_api.classes import APIEndPoint, APIResource
|
||||||
from rest_api.fields import DynamicSerializerField
|
from rest_api.fields import DynamicSerializerField
|
||||||
from statistics.classes import StatisticNamespace, CharJSLine
|
from statistics.classes import StatisticNamespace, CharJSLine
|
||||||
|
|
||||||
@@ -91,6 +91,9 @@ class DocumentsApp(MayanAppConfig):
|
|||||||
from actstream import registry
|
from actstream import registry
|
||||||
|
|
||||||
APIEndPoint(app=self, version_string='1')
|
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')
|
DeletedDocument = self.get_model('DeletedDocument')
|
||||||
Document = self.get_model('Document')
|
Document = self.get_model('Document')
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ api_urls = [
|
|||||||
name='search-view'
|
name='search-view'
|
||||||
),
|
),
|
||||||
url(
|
url(
|
||||||
r'^advanced/(?P<search_model>[\.\w]+)/$', APIAdvancedSearchView.as_view(),
|
r'^search/advanced/(?P<search_model>[\.\w]+)/$', APIAdvancedSearchView.as_view(),
|
||||||
name='advanced-search-view'
|
name='advanced-search-view'
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -20,10 +20,10 @@ urlpatterns = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
api_urls = [
|
api_urls = [
|
||||||
url(r'^types/$', APIEventTypeListView.as_view(), name='event-type-list'),
|
url(r'^event_types/$', APIEventTypeListView.as_view(), name='event-type-list'),
|
||||||
url(r'^events/$', APIEventListView.as_view(), name='event-list'),
|
url(r'^events/$', APIEventListView.as_view(), name='event-list'),
|
||||||
url(
|
url(
|
||||||
r'^object/(?P<app_label>[-\w]+)/(?P<model>[-\w]+)/(?P<object_id>\d+)/events/$',
|
r'^objects/(?P<app_label>[-\w]+)/(?P<model>[-\w]+)/(?P<object_id>\d+)/events/$',
|
||||||
APIObjectEventListView.as_view(), name='object-event-list'
|
APIObjectEventListView.as_view(), name='object-event-list'
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ api_urls = [
|
|||||||
),
|
),
|
||||||
url(r'^folders/$', APIFolderListView.as_view(), name='folder-list'),
|
url(r'^folders/$', APIFolderListView.as_view(), name='folder-list'),
|
||||||
url(
|
url(
|
||||||
r'^document/(?P<pk>[0-9]+)/folders/$',
|
r'^documents/(?P<pk>[0-9]+)/folders/$',
|
||||||
APIDocumentFolderListView.as_view(), name='document-folder-list'
|
APIDocumentFolderListView.as_view(), name='document-folder-list'
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ from __future__ import absolute_import, unicode_literals
|
|||||||
from rest_framework import generics, status
|
from rest_framework import generics, status
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
|
|
||||||
|
from django.shortcuts import get_object_or_404
|
||||||
|
|
||||||
from documents.models import Document, DocumentPage, DocumentVersion
|
from documents.models import Document, DocumentPage, DocumentVersion
|
||||||
from rest_api.permissions import MayanPermission
|
from rest_api.permissions import MayanPermission
|
||||||
|
|
||||||
@@ -26,10 +28,6 @@ class APIDocumentOCRView(generics.GenericAPIView):
|
|||||||
Submit a document for OCR.
|
Submit a document for OCR.
|
||||||
---
|
---
|
||||||
omit_serializer: true
|
omit_serializer: true
|
||||||
parameters:
|
|
||||||
- name: pk
|
|
||||||
paramType: path
|
|
||||||
type: number
|
|
||||||
responseMessages:
|
responseMessages:
|
||||||
- code: 202
|
- code: 202
|
||||||
message: Accepted
|
message: Accepted
|
||||||
@@ -40,12 +38,19 @@ class APIDocumentOCRView(generics.GenericAPIView):
|
|||||||
|
|
||||||
|
|
||||||
class APIDocumentVersionOCRView(generics.GenericAPIView):
|
class APIDocumentVersionOCRView(generics.GenericAPIView):
|
||||||
|
lookup_url_kwarg = 'version_pk'
|
||||||
mayan_object_permissions = {
|
mayan_object_permissions = {
|
||||||
'POST': (permission_ocr_document,)
|
'POST': (permission_ocr_document,)
|
||||||
}
|
}
|
||||||
permission_classes = (MayanPermission,)
|
permission_classes = (MayanPermission,)
|
||||||
queryset = DocumentVersion.objects.all()
|
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):
|
def get_serializer_class(self):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@@ -54,10 +59,6 @@ class APIDocumentVersionOCRView(generics.GenericAPIView):
|
|||||||
Submit a document version for OCR.
|
Submit a document version for OCR.
|
||||||
---
|
---
|
||||||
omit_serializer: true
|
omit_serializer: true
|
||||||
parameters:
|
|
||||||
- name: pk
|
|
||||||
paramType: path
|
|
||||||
type: number
|
|
||||||
responseMessages:
|
responseMessages:
|
||||||
- code: 202
|
- code: 202
|
||||||
message: Accepted
|
message: Accepted
|
||||||
@@ -70,20 +71,25 @@ class APIDocumentVersionOCRView(generics.GenericAPIView):
|
|||||||
class APIDocumentPageContentView(generics.RetrieveAPIView):
|
class APIDocumentPageContentView(generics.RetrieveAPIView):
|
||||||
"""
|
"""
|
||||||
Returns the OCR content of the selected document page.
|
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 = {
|
mayan_object_permissions = {
|
||||||
'GET': (permission_ocr_content_view,),
|
'GET': (permission_ocr_content_view,),
|
||||||
}
|
}
|
||||||
permission_classes = (MayanPermission,)
|
permission_classes = (MayanPermission,)
|
||||||
serializer_class = DocumentPageContentSerializer
|
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):
|
def retrieve(self, request, *args, **kwargs):
|
||||||
instance = self.get_object()
|
instance = self.get_object()
|
||||||
|
|||||||
@@ -63,7 +63,9 @@ class OCRAPITestCase(BaseAPITestCase):
|
|||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse(
|
reverse(
|
||||||
'rest_api:document-version-ocr-submit-view',
|
'rest_api:document-version-ocr-submit-view',
|
||||||
args=(self.document.latest_version.pk,)
|
args=(
|
||||||
|
self.document.pk, self.document.latest_version.pk,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -77,7 +79,10 @@ class OCRAPITestCase(BaseAPITestCase):
|
|||||||
response = self.client.get(
|
response = self.client.get(
|
||||||
reverse(
|
reverse(
|
||||||
'rest_api:document-page-content-view',
|
'rest_api:document-page-content-view',
|
||||||
args=(self.document.latest_version.pages.first().pk,)
|
args=(
|
||||||
|
self.document.pk, self.document.latest_version.pk,
|
||||||
|
self.document.latest_version.pages.first().pk,
|
||||||
|
)
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -43,16 +43,16 @@ urlpatterns = [
|
|||||||
|
|
||||||
api_urls = [
|
api_urls = [
|
||||||
url(
|
url(
|
||||||
r'^document/(?P<pk>\d+)/submit/$', APIDocumentOCRView.as_view(),
|
r'^documents/(?P<pk>\d+)/ocr/$', APIDocumentOCRView.as_view(),
|
||||||
name='document-ocr-submit-view'
|
name='document-ocr-submit-view'
|
||||||
),
|
),
|
||||||
url(
|
url(
|
||||||
r'^document_version/(?P<pk>\d+)/submit/$',
|
r'^documents/(?P<document_pk>\d+)/versions/(?P<version_pk>\d+)/ocr/$',
|
||||||
APIDocumentVersionOCRView.as_view(),
|
APIDocumentVersionOCRView.as_view(),
|
||||||
name='document-version-ocr-submit-view'
|
name='document-version-ocr-submit-view'
|
||||||
),
|
),
|
||||||
url(
|
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+)/ocr/$', APIDocumentPageContentView.as_view(),
|
||||||
name='document-page-content-view'
|
name='document-page-content-view'
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -30,5 +30,4 @@ api_urls = [
|
|||||||
url(r'^permissions/$', APIPermissionList.as_view(), name='permission-list'),
|
url(r'^permissions/$', APIPermissionList.as_view(), name='permission-list'),
|
||||||
url(r'^roles/$', APIRoleListView.as_view(), name='role-list'),
|
url(r'^roles/$', APIRoleListView.as_view(), name='role-list'),
|
||||||
url(r'^roles/(?P<pk>[0-9]+)/$', APIRoleView.as_view(), name='role-detail'),
|
url(r'^roles/(?P<pk>[0-9]+)/$', APIRoleView.as_view(), name='role-detail'),
|
||||||
url(r'^$', APIPermissionList.as_view(), name='permission-list'),
|
|
||||||
]
|
]
|
||||||
|
|||||||
19
mayan/apps/rest_api/api_views.py
Normal file
19
mayan/apps/rest_api/api_views.py
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
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()
|
||||||
@@ -4,9 +4,33 @@ from django.conf.urls import include, url
|
|||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.utils.module_loading import import_string
|
from django.utils.module_loading import import_string
|
||||||
|
|
||||||
|
from .exceptions import APIResourcePatternError
|
||||||
|
|
||||||
|
|
||||||
|
class APIResource(object):
|
||||||
|
_registry = {}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def all(cls):
|
||||||
|
return cls._registry.values()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get(cls, name):
|
||||||
|
return cls._registry[name]
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return unicode(self.name)
|
||||||
|
|
||||||
|
def __init__(self, name, label, description=None):
|
||||||
|
self.label = label
|
||||||
|
self.name = name
|
||||||
|
self.description = description
|
||||||
|
self.__class__._registry[self.name] = self
|
||||||
|
|
||||||
|
|
||||||
class APIEndPoint(object):
|
class APIEndPoint(object):
|
||||||
_registry = {}
|
_registry = {}
|
||||||
|
_patterns = []
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_all(cls):
|
def get_all(cls):
|
||||||
@@ -46,6 +70,12 @@ class APIEndPoint(object):
|
|||||||
def register_urls(self, urlpatterns):
|
def register_urls(self, urlpatterns):
|
||||||
from .urls import urlpatterns as app_urls
|
from .urls import urlpatterns as app_urls
|
||||||
|
|
||||||
app_urls += [
|
for url in urlpatterns:
|
||||||
url(r'^%s/' % (self.name or self.app.name), include(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 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 = [
|
api_urls = [
|
||||||
url(r'^$', APIBase.as_view(), name='api_root'),
|
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(
|
url(
|
||||||
r'^auth/token/obtain/$', BrowseableObtainAuthToken.as_view(),
|
r'^auth/token/obtain/$', BrowseableObtainAuthToken.as_view(),
|
||||||
name='auth_token_obtain'
|
name='auth_token_obtain'
|
||||||
|
|||||||
@@ -13,13 +13,6 @@ class APIBase(SwaggerResourcesView):
|
|||||||
renderer_classes = (renderers.BrowsableAPIRenderer, renderers.JSONRenderer)
|
renderer_classes = (renderers.BrowsableAPIRenderer, renderers.JSONRenderer)
|
||||||
|
|
||||||
|
|
||||||
class APIAppView(SwaggerApiView):
|
|
||||||
"""
|
|
||||||
Entry points of the selected app.
|
|
||||||
"""
|
|
||||||
renderer_classes = (renderers.BrowsableAPIRenderer, renderers.JSONRenderer)
|
|
||||||
|
|
||||||
|
|
||||||
class BrowseableObtainAuthToken(ObtainAuthToken):
|
class BrowseableObtainAuthToken(ObtainAuthToken):
|
||||||
"""
|
"""
|
||||||
Obtain an API authentication token.
|
Obtain an API authentication token.
|
||||||
|
|||||||
Reference in New Issue
Block a user