1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-17 15:53:28 +00:00

[API2] ReversionTag

This commit is contained in:
zming
2017-07-03 11:51:52 +08:00
parent 8a853b3061
commit d28e597921
8 changed files with 426 additions and 0 deletions

View 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])

View 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)

View File

View 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)

View File

@@ -225,6 +225,7 @@ INSTALLED_APPS = (
'seahub.admin_log',
'seahub.wopi',
'seahub.tags',
'seahub.revision_tag',
)
# Enabled or disable constance(web settings).

View File

@@ -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'),

View 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]

View 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