Commit 7940b1d8 authored by Michal Čihař's avatar Michal Čihař

Add read only repository status API

Signed-off-by: default avatarMichal Čihař <michal@cihar.com>
parent ce91f9e6
...@@ -47,6 +47,10 @@ Projects ...@@ -47,6 +47,10 @@ Projects
Returns information about project. Returns information about project.
.. http:get:: /api/projects/(string:project)/repository/
Returns information about VCS repository status.
Components Components
++++++++++ ++++++++++
...@@ -64,6 +68,10 @@ Components ...@@ -64,6 +68,10 @@ Components
:query lock: Boolean whether to lock or not. :query lock: Boolean whether to lock or not.
.. http:get:: /api/components/(string:project)/(string:component)/repository/
Returns information about VCS repository status.
Translations Translations
++++++++++++ ++++++++++++
...@@ -90,6 +98,10 @@ Translations ...@@ -90,6 +98,10 @@ Translations
-H "Authorization: Token TOKEN" \ -H "Authorization: Token TOKEN" \
http://example.com/api/translations/hello/android/cs/file/ http://example.com/api/translations/hello/android/cs/file/
.. http:get:: /api/translations/(string:project)/(string:component)/(string:language)/repository/
Returns information about VCS repository status.
.. _hooks: .. _hooks:
Notification hooks Notification hooks
......
...@@ -54,6 +54,15 @@ class APIBaseTest(APITestCase, RepoTestMixin): ...@@ -54,6 +54,15 @@ class APIBaseTest(APITestCase, RepoTestMixin):
HTTP_AUTHORIZATION='Token ' + user.auth_token.key HTTP_AUTHORIZATION='Token ' + user.auth_token.key
) )
def do_request(self, name, kwargs, data=None, code=200, superuser=False):
self.authenticate(superuser)
response = self.client.get(
reverse(name, kwargs=kwargs)
)
self.assertEqual(response.status_code, code)
if data is not None:
self.assertEqual(response.data, data)
class ProjectAPITest(APIBaseTest): class ProjectAPITest(APIBaseTest):
def test_list_projects(self): def test_list_projects(self):
...@@ -69,6 +78,25 @@ class ProjectAPITest(APIBaseTest): ...@@ -69,6 +78,25 @@ class ProjectAPITest(APIBaseTest):
) )
self.assertEqual(response.data['slug'], 'test') self.assertEqual(response.data['slug'], 'test')
def test_repo_status_denied(self):
self.do_request(
'api:project-repository',
self.project_kwargs,
code=403
)
def test_repo_status(self):
self.do_request(
'api:project-repository',
self.project_kwargs,
superuser=True,
data={
'needs_push': False,
'needs_merge': False,
'needs_commit': False
}
)
class ComponentAPITest(APIBaseTest): class ComponentAPITest(APIBaseTest):
def test_list_components(self): def test_list_components(self):
...@@ -122,6 +150,25 @@ class ComponentAPITest(APIBaseTest): ...@@ -122,6 +150,25 @@ class ComponentAPITest(APIBaseTest):
response = self.client.post(url, {'lock': False}) response = self.client.post(url, {'lock': False})
self.assertEqual(response.data, {'locked': False}) self.assertEqual(response.data, {'locked': False})
def test_repo_status_denied(self):
self.do_request(
'api:component-repository',
self.component_kwargs,
code=403
)
def test_repo_status(self):
self.do_request(
'api:component-repository',
self.component_kwargs,
superuser=True,
data={
'needs_push': False,
'needs_merge': False,
'needs_commit': False
}
)
class LanguageAPITest(APIBaseTest): class LanguageAPITest(APIBaseTest):
def test_list_languages(self): def test_list_languages(self):
...@@ -203,3 +250,22 @@ class TranslationAPITest(APIBaseTest): ...@@ -203,3 +250,22 @@ class TranslationAPITest(APIBaseTest):
response.data, response.data,
{'count': 5, 'result': True} {'count': 5, 'result': True}
) )
def test_repo_status_denied(self):
self.do_request(
'api:translation-repository',
self.translation_kwargs,
code=403
)
def test_repo_status(self):
self.do_request(
'api:translation-repository',
self.translation_kwargs,
superuser=True,
data={
'needs_push': False,
'needs_merge': False,
'needs_commit': False
}
)
...@@ -33,7 +33,7 @@ from weblate.api.serializers import ( ...@@ -33,7 +33,7 @@ from weblate.api.serializers import (
from weblate.trans.exporters import EXPORTERS from weblate.trans.exporters import EXPORTERS
from weblate.trans.models import Project, SubProject, Translation from weblate.trans.models import Project, SubProject, Translation
from weblate.trans.permissions import ( from weblate.trans.permissions import (
can_upload_translation, can_lock_subproject, can_upload_translation, can_lock_subproject, can_see_repository_status,
) )
from weblate.lang.models import Language from weblate.lang.models import Language
from weblate.trans.views.helper import download_translation_file from weblate.trans.views.helper import download_translation_file
...@@ -57,7 +57,7 @@ class MultipleFieldMixin(object): ...@@ -57,7 +57,7 @@ class MultipleFieldMixin(object):
return get_object_or_404(queryset, **lookup) return get_object_or_404(queryset, **lookup)
class FileExportViewSet(viewsets.ReadOnlyModelViewSet): class WeblateViewSet(viewsets.ReadOnlyModelViewSet):
""" """
Allows to skip content negotiation for certain requests. Allows to skip content negotiation for certain requests.
""" """
...@@ -71,12 +71,34 @@ class FileExportViewSet(viewsets.ReadOnlyModelViewSet): ...@@ -71,12 +71,34 @@ class FileExportViewSet(viewsets.ReadOnlyModelViewSet):
renderers = self.get_renderers() renderers = self.get_renderers()
return (renderers[0], renderers[0].media_type) return (renderers[0], renderers[0].media_type)
raise Http404('Not supported exporter') raise Http404('Not supported exporter')
return super(FileExportViewSet, self).perform_content_negotiation( return super(WeblateViewSet, self).perform_content_negotiation(
request, force request, force
) )
@detail_route(methods=['get', 'post'])
def repository(self, request, **kwargs):
obj = self.get_object()
if hasattr(obj, 'subproject'):
project = obj.subproject.project
elif hasattr(obj, 'project'):
project = obj.project
else:
project = obj
if not can_see_repository_status(request.user, project):
raise PermissionDenied()
return Response(
data={
'needs_commit': obj.repo_needs_commit(),
'needs_merge': obj.repo_needs_merge(),
'needs_push': obj.repo_needs_push(),
}
)
class ProjectViewSet(viewsets.ReadOnlyModelViewSet): class ProjectViewSet(WeblateViewSet):
"""Translation projects API. """Translation projects API.
""" """
queryset = Project.objects.none() queryset = Project.objects.none()
...@@ -89,7 +111,7 @@ class ProjectViewSet(viewsets.ReadOnlyModelViewSet): ...@@ -89,7 +111,7 @@ class ProjectViewSet(viewsets.ReadOnlyModelViewSet):
) )
class ComponentViewSet(MultipleFieldMixin, viewsets.ReadOnlyModelViewSet): class ComponentViewSet(MultipleFieldMixin, WeblateViewSet):
"""Translation components API. """Translation components API.
""" """
queryset = SubProject.objects.none() queryset = SubProject.objects.none()
...@@ -128,7 +150,7 @@ class ComponentViewSet(MultipleFieldMixin, viewsets.ReadOnlyModelViewSet): ...@@ -128,7 +150,7 @@ class ComponentViewSet(MultipleFieldMixin, viewsets.ReadOnlyModelViewSet):
return Response(data=LockSerializer(obj).data) return Response(data=LockSerializer(obj).data)
class TranslationViewSet(MultipleFieldMixin, FileExportViewSet): class TranslationViewSet(MultipleFieldMixin, WeblateViewSet):
"""Translation components API. """Translation components API.
""" """
queryset = Translation.objects.none() queryset = Translation.objects.none()
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment