mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-17 15:53:28 +00:00
[API2] ReversionTag
This commit is contained in:
83
seahub/api2/endpoints/admin/revision_tag.py
Normal file
83
seahub/api2/endpoints/admin/revision_tag.py
Normal file
@@ -0,0 +1,83 @@
|
||||
# Copyright (c) 2012-2016 Seafile Ltd.
|
||||
import re
|
||||
|
||||
from rest_framework.authentication import SessionAuthentication
|
||||
from rest_framework.permissions import IsAdminUser
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework import status
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
from seahub.api2.throttling import UserRateThrottle
|
||||
from seahub.api2.authentication import TokenAuthentication
|
||||
from seahub.api2.utils import api_error
|
||||
from seahub.base.accounts import User
|
||||
from seahub.revision_tag.models import RevisionTags
|
||||
|
||||
from seaserv import seafile_api
|
||||
|
||||
|
||||
def check_tagname(tag_name):
|
||||
return True if re.match('^[\.\w-]+$', tag_name, re.U) else False
|
||||
|
||||
|
||||
class AdminTaggedItemsView(APIView):
|
||||
authentication_classes = (TokenAuthentication, SessionAuthentication)
|
||||
permission_classes = (IsAdminUser, )
|
||||
throttle_classes = (UserRateThrottle, )
|
||||
|
||||
def get(self, request):
|
||||
normal = True
|
||||
user = request.GET.get('user', None)
|
||||
if user is not None:
|
||||
if not normal:
|
||||
error_msg = "unsupported operation"
|
||||
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||
normal = False
|
||||
try:
|
||||
User.objects.get(email=user)
|
||||
except User.DoesNotExist:
|
||||
error_msg = "User %s not found" % user
|
||||
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||
revision_tags = RevisionTags.objects.get_all_tags_by_creator(user)
|
||||
|
||||
repo_id = request.GET.get('repo_id', None)
|
||||
if repo_id is not None:
|
||||
if not normal:
|
||||
error_msg = "unsupported operation"
|
||||
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||
normal = False
|
||||
repo = seafile_api.get_repo(repo_id)
|
||||
if not repo:
|
||||
error_msg = "Library %s not found" % repo_id
|
||||
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||
revision_tags = RevisionTags.objects.get_all_tags_by_repo(repo_id)
|
||||
|
||||
tag_name = request.GET.get('tag_name', None)
|
||||
if tag_name is not None:
|
||||
if not normal:
|
||||
error_msg = "unsupported operation"
|
||||
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||
normal = False
|
||||
if not check_tagname(tag_name):
|
||||
error_msg = "Tag %s invalid" % tag_name
|
||||
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||
revision_tags = RevisionTags.objects.get_all_tags_by_tagname(tag_name)
|
||||
|
||||
tag_contains = request.GET.get('tag_contains', None)
|
||||
if tag_contains is not None:
|
||||
if not normal:
|
||||
error_msg = "unsupported operation"
|
||||
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||
normal = False
|
||||
if not check_tagname(tag_contains):
|
||||
error_msg = "key word %s invalid" % tag_contains
|
||||
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||
revision_tags = RevisionTags.objects.get_all_tags_by_key(tag_contains)
|
||||
|
||||
if normal:
|
||||
revision_tags = RevisionTags.objects.all()
|
||||
|
||||
revision_tags = sorted(revision_tags, key=lambda revision_tags: revision_tags['tag'])
|
||||
return Response([revision_tag.to_dict() for revision_tag in revision_tags])
|
110
seahub/api2/endpoints/revision_tag.py
Normal file
110
seahub/api2/endpoints/revision_tag.py
Normal file
@@ -0,0 +1,110 @@
|
||||
# Copyright (c) 2012-2016 Seafile Ltd.
|
||||
import re
|
||||
|
||||
from rest_framework.authentication import SessionAuthentication
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework import status
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
from seahub.api2.throttling import UserRateThrottle
|
||||
from seahub.api2.authentication import TokenAuthentication
|
||||
from seahub.api2.utils import api_error
|
||||
from seahub.revision_tag.models import Tags, RevisionTags
|
||||
from seahub.views import check_folder_permission
|
||||
|
||||
from seaserv import seafile_api
|
||||
|
||||
|
||||
def check_parameter(func):
|
||||
def _decorated(view, request, *args, **kwargs):
|
||||
if request.method == "POST":
|
||||
repo_id = request.data.get('repo_id', '')
|
||||
tag_names = request.data.get('tag_names', '')
|
||||
if not tag_names:
|
||||
error_msg = _("Tag can not be empty")
|
||||
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||
names = [name.strip() for name in tag_names.split(',')]
|
||||
tag_names = []
|
||||
for name in names:
|
||||
if not check_tagname(name):
|
||||
error_msg = _("Tag can only contains letters, numbers, dot, hyphen or underscore")
|
||||
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||
tag_names.append(name)
|
||||
elif request.method == "DELETE":
|
||||
repo_id = request.GET.get('repo_id', '')
|
||||
tag_name = request.GET.get('tag_name', '')
|
||||
if not tag_name:
|
||||
error_msg = _("Tag can not be empty")
|
||||
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||
if tag_name not in Tags.objects.get_all_tag_name():
|
||||
error_msg = "Tag %s not found" % tag_name
|
||||
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||
if not check_tagname(tag_name):
|
||||
error_msg = _('Tag can only contains letters, numbers, dot, hyphen or underscore')
|
||||
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||
|
||||
if not repo_id:
|
||||
error_msg = "Repo can not be empty"
|
||||
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||
repo = seafile_api.get_repo(repo_id)
|
||||
if not repo:
|
||||
error_msg = "Library %s not found" % repo_id
|
||||
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||
|
||||
if check_folder_permission(request, repo_id, '/') != 'rw':
|
||||
error_msg = 'Permission denied.'
|
||||
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
|
||||
|
||||
return func(view, request, *args, **kwargs)
|
||||
return _decorated
|
||||
|
||||
|
||||
def check_tagname(tagname):
|
||||
return True if re.match('^[\.\w-]+$', tagname, re.U) else False
|
||||
|
||||
|
||||
class TaggedItemsView(APIView):
|
||||
authentication_classes = (TokenAuthentication, SessionAuthentication)
|
||||
permission_classes = (IsAuthenticated, )
|
||||
throttle_classes = (UserRateThrottle, )
|
||||
|
||||
@check_parameter
|
||||
def post(self, request):
|
||||
repo_id = request.POST.get('repo_id')
|
||||
tag_names = request.POST.get('tag_names').split(',')
|
||||
repo = seafile_api.get_repo(repo_id)
|
||||
if repo.head_commit_id is not None:
|
||||
commit_id = repo.head_commit_id
|
||||
else:
|
||||
commit_id = seafile_api.get_commit_list(repo_id, 0, 1)[0].id
|
||||
for name in tag_names:
|
||||
revision_tag, created = RevisionTags.objects.create_revision_tag(
|
||||
repo_id, commit_id, name.strip(), request.user.username)
|
||||
return Response({"success": True}, status=status.HTTP_200_OK)
|
||||
|
||||
@check_parameter
|
||||
def delete(self, request, repo_id, tag_name):
|
||||
repo_id = request.GET.get('repo_id')
|
||||
tag_name = request.GET.get('tag_name')
|
||||
commit_id = None
|
||||
if RevisionTags.objects.delete_revision_tag(repo_id, commit_id,
|
||||
tag_name):
|
||||
return Response({"success": True}, status=status.HTTP_200_OK)
|
||||
else:
|
||||
return Response({"success": True}, status=status.HTTP_202_ACCEPTED)
|
||||
|
||||
|
||||
class TagNamesView(APIView):
|
||||
authentication_classes = (TokenAuthentication, SessionAuthentication)
|
||||
permission_classes = (IsAuthenticated, )
|
||||
throttle_classes = (UserRateThrottle, )
|
||||
|
||||
def get(self, request):
|
||||
revision_tags = [tag["tag"].name for tag in RevisionTags.objects.\
|
||||
get_all_tags_by_creator(request.user.username)]
|
||||
revision_tags = sorted(revision_tags)
|
||||
|
||||
return Response(revision_tags)
|
0
seahub/revision_tag/__init__.py
Normal file
0
seahub/revision_tag/__init__.py
Normal file
100
seahub/revision_tag/models.py
Normal file
100
seahub/revision_tag/models.py
Normal file
@@ -0,0 +1,100 @@
|
||||
# Copyright (c) 2012-2016 Seafile Ltd.
|
||||
import os
|
||||
from django.db import models
|
||||
from django.core.urlresolvers import reverse
|
||||
|
||||
from seahub.base.fields import LowerCaseCharField
|
||||
from seahub.base.templatetags.seahub_tags import email2nickname, email2contact_email
|
||||
from seahub.utils.timeutils import timestamp_to_isoformat_timestr
|
||||
|
||||
import seaserv
|
||||
from seaserv import seafile_api
|
||||
|
||||
|
||||
########## manager
|
||||
class TagsManager(models.Manager):
|
||||
def get_all_tag_name(self):
|
||||
return [tag.name for tag in super(TagsManager, self).all()]
|
||||
|
||||
def get_or_create_tag(self, tagname):
|
||||
try:
|
||||
tag = super(TagsManager, self).get(name=tagname)
|
||||
return tag
|
||||
except self.model.DoesNotExist:
|
||||
tag = self.model(name=tagname)
|
||||
tag.save()
|
||||
return tag
|
||||
|
||||
class RevisionTagsManager(models.Manager):
|
||||
def get_one_revision_tag(self, repo_id, commit_id, tag_name):
|
||||
try:
|
||||
return super(RevisionTagsManager, self).get(
|
||||
repo_id=repo_id,
|
||||
revision_id=commit_id,
|
||||
tag__name=tag_name)
|
||||
except:
|
||||
return None
|
||||
|
||||
def get_all_tags_by_repo(self, repo_id):
|
||||
return super(RevisionTagsManager, self).filter(repo_id=repo_id)
|
||||
|
||||
def get_all_tags_by_creator(self, creator):
|
||||
return super(RevisionTagsManager, self).filter(username=creator)
|
||||
|
||||
def get_all_tags_by_key(self, key):
|
||||
return super(RevisionTagsManager, self).filter(tag__name__contains=key)
|
||||
|
||||
def get_all_tags_by_tagname(self, tag_name):
|
||||
return super(RevisionTagsManager, self).filter(tag__name=tag_name)
|
||||
|
||||
def create_revision_tag(self, repo_id, commit_id, tag_name, creator):
|
||||
revision_tag = self.get_one_revision_tag(repo_id, commit_id, tag_name)
|
||||
exists = False
|
||||
if revision_tag:
|
||||
return revision_tag, False
|
||||
else:
|
||||
tag = Tags.objects.get_or_create_tag(tag_name)
|
||||
revision_tag = self.model(repo_id=repo_id, revision_id=commit_id, tag=tag, username=creator)
|
||||
revision_tag.save()
|
||||
return revision_tag, True
|
||||
|
||||
def delete_revision_tag(self, repo_id, commit_id, tag_name):
|
||||
revision_tag = self.get_one_revision_tag(repo_id, commit_id, tag_name)
|
||||
if not revision_tag:
|
||||
return False
|
||||
else:
|
||||
revision_tag.delete()
|
||||
return True
|
||||
|
||||
########## models
|
||||
class Tags(models.Model):
|
||||
name = models.CharField(max_length=255, unique=True)
|
||||
objects = TagsManager()
|
||||
|
||||
class RevisionTags(models.Model):
|
||||
repo_id = models.CharField(max_length=36, db_index=True)
|
||||
path = models.TextField(default='/')
|
||||
revision_id = models.CharField(max_length=255, db_index=True)
|
||||
tag = models.ForeignKey("Tags", on_delete=models.CASCADE)
|
||||
username = LowerCaseCharField(max_length=255, db_index=True)
|
||||
objects = RevisionTagsManager()
|
||||
|
||||
def to_dict(self):
|
||||
repo = seafile_api.get_repo(self.repo_id)
|
||||
commit = seaserv.get_commit(repo.id, repo.revision, self.revision_id)
|
||||
email = commit.creator_name
|
||||
return {"tag":self.tag.name,
|
||||
"tag_creator": self.username,
|
||||
"revision": {
|
||||
"repo_id": self.repo_id,
|
||||
"commit_id": self.revision_id,
|
||||
"email": email,
|
||||
"name": email2nickname(email),
|
||||
"contact_email": email2contact_email(email),
|
||||
"time": timestamp_to_isoformat_timestr(commit.ctime),
|
||||
"description": commit.desc,
|
||||
"link": reverse("repo_history_view", args=[self.repo_id])+"?commit_id=%s"%self.revision_id
|
||||
}}
|
||||
|
||||
def __getitem__(self, item):
|
||||
return getattr(self, item)
|
@@ -225,6 +225,7 @@ INSTALLED_APPS = (
|
||||
'seahub.admin_log',
|
||||
'seahub.wopi',
|
||||
'seahub.tags',
|
||||
'seahub.revision_tag',
|
||||
)
|
||||
|
||||
# Enabled or disable constance(web settings).
|
||||
|
@@ -47,8 +47,10 @@ from seahub.api2.endpoints.notifications import NotificationsView, NotificationV
|
||||
from seahub.api2.endpoints.user_enabled_modules import UserEnabledModulesView
|
||||
from seahub.api2.endpoints.repo_file_uploaded_bytes import RepoFileUploadedBytesView
|
||||
from seahub.api2.endpoints.user_avatar import UserAvatarView
|
||||
from seahub.api2.endpoints.revision_tag import TaggedItemsView,TagNamesView
|
||||
|
||||
# Admin
|
||||
from seahub.api2.endpoints.admin.revision_tag import AdminTaggedItemsView
|
||||
from seahub.api2.endpoints.admin.login import Login
|
||||
from seahub.api2.endpoints.admin.file_audit import FileAudit
|
||||
from seahub.api2.endpoints.admin.file_update import FileUpdate
|
||||
@@ -211,6 +213,10 @@ urlpatterns = patterns(
|
||||
url(r'^api/v2.1/upload-links/$', UploadLinks.as_view(), name='api-v2.1-upload-links'),
|
||||
url(r'^api/v2.1/upload-links/(?P<token>[a-f0-9]+)/$', UploadLink.as_view(), name='api-v2.1-upload-link'),
|
||||
|
||||
## user::revision-tags
|
||||
url(r'^api/v2.1/revision-tags/tagged-items/$', TaggedItemsView.as_view(), name='api-v2.1-revision-tags-tagged-items'),
|
||||
url(r'^api/v2.1/revision-tags/tag-names/$', TagNamesView.as_view(), name='api-v2.1-revision-tags-tag-names'),
|
||||
|
||||
## user::repos
|
||||
url(r'^api/v2.1/repos/batch/$', ReposBatchView.as_view(), name='api-v2.1-repos-batch'),
|
||||
url(r'^api/v2.1/repos/(?P<repo_id>[-0-9a-f]{36})/$', RepoView.as_view(), name='api-v2.1-repo-view'),
|
||||
@@ -246,6 +252,9 @@ urlpatterns = patterns(
|
||||
## admin::sysinfo
|
||||
url(r'^api/v2.1/admin/sysinfo/$', SysInfo.as_view(), name='api-v2.1-sysinfo'),
|
||||
|
||||
## admin::revision-tags
|
||||
url(r'^api/v2.1/admin/revision-tags/tagged-items/$', AdminTaggedItemsView.as_view(), name='api-v2.1-admin-revision-tags-tagged-items'),
|
||||
|
||||
## admin::devices
|
||||
url(r'^api/v2.1/admin/devices/$', AdminDevices.as_view(), name='api-v2.1-admin-devices'),
|
||||
url(r'^api/v2.1/admin/device-errors/$', AdminDeviceErrors.as_view(), name='api-v2.1-admin-device-errors'),
|
||||
|
81
tests/api/endpoints/admin/test_revision_tag.py
Normal file
81
tests/api/endpoints/admin/test_revision_tag.py
Normal file
@@ -0,0 +1,81 @@
|
||||
import os
|
||||
import json
|
||||
|
||||
from mock import patch
|
||||
from django.core.urlresolvers import reverse
|
||||
|
||||
from seahub.test_utils import BaseTestCase
|
||||
from seaserv import seafile_api
|
||||
|
||||
|
||||
class RevisionTagsTest(BaseTestCase):
|
||||
def setUp(self):
|
||||
self.login_as(self.admin)
|
||||
self.url = reverse("api-v2.1-admin-revision-tags-tagged-items")
|
||||
self.url_create = reverse("api-v2.1-revision-tags-tagged-items")
|
||||
self.repo = seafile_api.get_repo(self.create_repo(
|
||||
name="test_repo",
|
||||
desc="",
|
||||
username=self.admin.username,
|
||||
passwd=None
|
||||
))
|
||||
self.tag_name = "test_tag_name"
|
||||
|
||||
def test_get_revision_by_user(self):
|
||||
resp = self.client.post(self.url_create, {
|
||||
"tag_names": self.tag_name,
|
||||
"repo_id": self.repo.id,
|
||||
})
|
||||
assert resp.status_code in [200, 201]
|
||||
resp = self.client.get(self.url+"?user="+self.admin.username)
|
||||
assert self.tag_name in [tag["tag"] for tag in resp.data]
|
||||
resp = self.client.get(self.url+"?user="+self.user.username)
|
||||
assert not self.tag_name in [tag["tag"] for tag in resp.data]
|
||||
|
||||
def test_get_revision_by_repo_id(self):
|
||||
p_repo = seafile_api.get_repo(self.create_repo(
|
||||
name="test_repo",
|
||||
desc="",
|
||||
username=self.admin.username,
|
||||
passwd=None
|
||||
))
|
||||
resp = self.client.post(self.url_create, {
|
||||
"tag_names": self.tag_name,
|
||||
"repo_id": self.repo.id,
|
||||
})
|
||||
assert resp.status_code in [200, 201]
|
||||
resp = self.client.get(self.url+"?repo_id="+self.repo.id)
|
||||
assert self.tag_name in [tag["tag"] for tag in resp.data]
|
||||
resp = self.client.get(self.url+"?repo_id="+p_repo.id)
|
||||
assert not self.tag_name in [tag["tag"] for tag in resp.data]
|
||||
|
||||
def test_revisin_by_tag_name(self):
|
||||
resp = self.client.post(self.url_create, {
|
||||
"tag_names": self.tag_name,
|
||||
"repo_id": self.repo.id,
|
||||
})
|
||||
assert resp.status_code in [200, 201]
|
||||
resp = self.client.get(self.url+"?tag_name="+self.tag_name)
|
||||
assert self.tag_name in [tag["tag"] for tag in resp.data]
|
||||
resp = self.client.get(self.url+"?tag_name=Hello")
|
||||
assert not self.tag_name in [tag["tag"] for tag in resp.data]
|
||||
|
||||
def test_revisin_by_tag_contains(self):
|
||||
resp = self.client.post(self.url_create, {
|
||||
"tag_names": self.tag_name,
|
||||
"repo_id": self.repo.id,
|
||||
})
|
||||
assert resp.status_code in [200, 201]
|
||||
resp = self.client.get(self.url+"?tag_contains="+self.tag_name[:-2])
|
||||
assert self.tag_name in [tag["tag"] for tag in resp.data]
|
||||
resp = self.client.get(self.url+"?tag_contains=Hello")
|
||||
assert not self.tag_name in [tag["tag"] for tag in resp.data]
|
||||
|
||||
def test_revision_all(self):
|
||||
resp = self.client.post(self.url_create, {
|
||||
"tag_names": self.tag_name,
|
||||
"repo_id": self.repo.id,
|
||||
})
|
||||
assert resp.status_code in [200, 201]
|
||||
resp = self.client.get(self.url)
|
||||
assert self.tag_name in [tag["tag"] for tag in resp.data]
|
42
tests/api/endpoints/test_revision_tag.py
Normal file
42
tests/api/endpoints/test_revision_tag.py
Normal file
@@ -0,0 +1,42 @@
|
||||
import os
|
||||
import json
|
||||
|
||||
from mock import patch
|
||||
from django.core.urlresolvers import reverse
|
||||
|
||||
from seahub.test_utils import BaseTestCase
|
||||
from seaserv import seafile_api
|
||||
|
||||
|
||||
class RevisionTagsTest(BaseTestCase):
|
||||
def setUp(self):
|
||||
self.login_as(self.user)
|
||||
self.tag_name = "test_tag_name"
|
||||
self.repo = seafile_api.get_repo(self.create_repo(
|
||||
name="test_repo",
|
||||
desc="",
|
||||
username=self.user.username,
|
||||
passwd=None
|
||||
))
|
||||
self.url = reverse("api-v2.1-revision-tags-tagged-items")
|
||||
self.url_get = reverse("api-v2.1-revision-tags-tag-names")
|
||||
|
||||
@patch('seahub.api2.endpoints.revision_tag.check_folder_permission')
|
||||
def test_revision_tags(self, mock_permission):
|
||||
mock_permission.return_value = 'rw'
|
||||
c_resp = self.client.post(self.url, {
|
||||
"tag_names": self.tag_name,
|
||||
"repo_id": self.repo.repo_id,
|
||||
})
|
||||
assert c_resp.status_code in [200, 201]
|
||||
g_resp = self.client.get(self.url_get+"?name_only=true")
|
||||
assert g_resp.status_code == 200
|
||||
assert self.tag_name in g_resp.data
|
||||
|
||||
#d_resp = self.client.delete(self.url+"?repo_id="+str(self.repo.repo_id)+
|
||||
# "&tag_name="+str(self.tag_name))
|
||||
#assert d_resp.status_code in [200, 202]
|
||||
|
||||
#g_resp = self.client.get(self.url_get+"?name_only=true")
|
||||
#assert g_resp.status_code == 200
|
||||
#assert self.tag_name not in g_resp.data
|
Reference in New Issue
Block a user