diff rhodecode/controllers/api/api.py @ 3960:5293d4bbb1ea

Merged dev into stable/default/master branch
author Marcin Kuzminski <marcin@python-works.com>
date Fri, 07 Jun 2013 00:31:11 +0200
parents e3857cbb6d10 e1a0fdaecf63
children ffd45b185016
line wrap: on
line diff
--- a/rhodecode/controllers/api/api.py	Mon May 20 12:26:09 2013 +0200
+++ b/rhodecode/controllers/api/api.py	Fri Jun 07 00:31:11 2013 +0200
@@ -25,6 +25,7 @@
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 # MA  02110-1301, USA.
 
+import time
 import traceback
 import logging
 
@@ -34,19 +35,30 @@
     HasPermissionAnyApi, HasRepoPermissionAnyApi
 from rhodecode.lib.utils import map_groups, repo2db_mapper
 from rhodecode.lib.utils2 import str2bool, time_to_datetime, safe_int
-from rhodecode.lib import helpers as h
 from rhodecode.model.meta import Session
 from rhodecode.model.scm import ScmModel
 from rhodecode.model.repo import RepoModel
 from rhodecode.model.user import UserModel
 from rhodecode.model.users_group import UserGroupModel
+from rhodecode.model.repos_group import ReposGroupModel
 from rhodecode.model.db import Repository, RhodeCodeSetting, UserIpMap,\
-    Permission
+    Permission, User, Gist
 from rhodecode.lib.compat import json
+from rhodecode.lib.exceptions import DefaultUserException
+from rhodecode.model.gist import GistModel
 
 log = logging.getLogger(__name__)
 
 
+def store_update(updates, attr, name):
+    """
+    Stores param in updates dict if it's not instance of Optional
+    allows easy updates of passed in params
+    """
+    if not isinstance(attr, Optional):
+        updates[name] = attr
+
+
 class OptionalAttr(object):
     """
     Special Optional Option that defines other attribute
@@ -113,7 +125,7 @@
     """
     Get repo by id or name or return JsonRPCError if not found
 
-    :param userid:
+    :param repoid:
     """
     repo = RepoModel().get_repo(repoid)
     if repo is None:
@@ -121,6 +133,19 @@
     return repo
 
 
+def get_repo_group_or_error(repogroupid):
+    """
+    Get repo group by id or name or return JsonRPCError if not found
+
+    :param repogroupid:
+    """
+    repo_group = ReposGroupModel()._get_repo_group(repogroupid)
+    if repo_group is None:
+        raise JSONRPCError(
+            'repository group `%s` does not exist' % (repogroupid,))
+    return repo_group
+
+
 def get_users_group_or_error(usersgroupid):
     """
     Get user group by id or name or return JsonRPCError if not found
@@ -212,7 +237,7 @@
         :param repoid:
         """
         repo = get_repo_or_error(repoid)
-        if HasPermissionAnyApi('hg.admin')(user=apiuser) is False:
+        if not HasPermissionAnyApi('hg.admin')(user=apiuser):
             # check if we have admin permission for this repo !
             if HasRepoPermissionAnyApi('repository.admin',
                                        'repository.write')(user=apiuser,
@@ -220,16 +245,15 @@
                 raise JSONRPCError('repository `%s` does not exist' % (repoid))
 
         try:
-            invalidated_keys = ScmModel().mark_for_invalidation(repo.repo_name)
-            Session().commit()
-            return ('Cache for repository `%s` was invalidated: '
-                    'invalidated cache keys: %s' % (repoid, invalidated_keys))
+            ScmModel().mark_for_invalidation(repo.repo_name)
+            return ('Caches of repository `%s` was invalidated' % repoid)
         except Exception:
             log.error(traceback.format_exc())
             raise JSONRPCError(
                 'Error occurred during cache invalidation action'
             )
 
+    # permission check inside
     def lock(self, apiuser, repoid, locked=Optional(None),
              userid=Optional(OAttr('apiuser'))):
         """
@@ -266,27 +290,47 @@
             lockobj = Repository.getlock(repo)
 
             if lockobj[0] is None:
-                return ('Repo `%s` not locked. Locked=`False`.'
-                        % (repo.repo_name))
+                _d = {
+                    'repo': repo.repo_name,
+                    'locked': False,
+                    'locked_since': None,
+                    'locked_by': None,
+                    'msg': 'Repo `%s` not locked.' % repo.repo_name
+                }
+                return _d
             else:
                 userid, time_ = lockobj
-                user = get_user_or_error(userid)
+                lock_user = get_user_or_error(userid)
+                _d = {
+                    'repo': repo.repo_name,
+                    'locked': True,
+                    'locked_since': time_,
+                    'locked_by': lock_user.username,
+                    'msg': ('Repo `%s` locked by `%s`. '
+                            % (repo.repo_name,
+                               json.dumps(time_to_datetime(time_))))
+                }
+                return _d
 
-                return ('Repo `%s` locked by `%s`. Locked=`True`. '
-                        'Locked since: `%s`'
-                    % (repo.repo_name, user.username,
-                       json.dumps(time_to_datetime(time_))))
-
+        # force locked state through a flag
         else:
             locked = str2bool(locked)
             try:
                 if locked:
-                    Repository.lock(repo, user.user_id)
+                    lock_time = time.time()
+                    Repository.lock(repo, user.user_id, lock_time)
                 else:
+                    lock_time = None
                     Repository.unlock(repo)
-
-                return ('User `%s` set lock state for repo `%s` to `%s`'
-                        % (user.username, repo.repo_name, locked))
+                _d = {
+                    'repo': repo.repo_name,
+                    'locked': locked,
+                    'locked_since': lock_time,
+                    'locked_by': user.username,
+                    'msg': ('User `%s` set lock state for repo `%s` to `%s`'
+                            % (user.username, repo.repo_name, locked))
+                }
+                return _d
             except Exception:
                 log.error(traceback.format_exc())
                 raise JSONRPCError(
@@ -302,9 +346,8 @@
         :param apiuser:
         :param userid:
         """
-        if HasPermissionAnyApi('hg.admin')(user=apiuser):
-            pass
-        else:
+
+        if not HasPermissionAnyApi('hg.admin')(user=apiuser):
             #make sure normal user does not pass someone else userid,
             #he is not allowed to do that
             if not isinstance(userid, Optional) and userid != apiuser.user_id:
@@ -354,7 +397,7 @@
         :param apiuser:
         :param userid:
         """
-        if HasPermissionAnyApi('hg.admin')(user=apiuser) is False:
+        if not HasPermissionAnyApi('hg.admin')(user=apiuser):
             #make sure normal user does not pass someone else userid,
             #he is not allowed to do that
             if not isinstance(userid, Optional) and userid != apiuser.user_id:
@@ -379,12 +422,15 @@
         """
 
         result = []
-        for user in UserModel().get_all():
+        users_list = User.query().order_by(User.username)\
+                        .filter(User.username != User.DEFAULT_USER)\
+                        .all()
+        for user in users_list:
             result.append(user.get_api_data())
         return result
 
     @HasPermissionAllDecorator('hg.admin')
-    def create_user(self, apiuser, username, email, password,
+    def create_user(self, apiuser, username, email, password=Optional(None),
                     firstname=Optional(None), lastname=Optional(None),
                     active=Optional(True), admin=Optional(False),
                     ldap_dn=Optional(None)):
@@ -479,6 +525,9 @@
                 msg='updated user ID:%s %s' % (user.user_id, user.username),
                 user=user.get_api_data()
             )
+        except DefaultUserException:
+            log.error(traceback.format_exc())
+            raise JSONRPCError('editing default user is forbidden')
         except Exception:
             log.error(traceback.format_exc())
             raise JSONRPCError('failed to update user `%s`' % userid)
@@ -538,12 +587,15 @@
         return result
 
     @HasPermissionAllDecorator('hg.admin')
-    def create_users_group(self, apiuser, group_name, active=Optional(True)):
+    def create_users_group(self, apiuser, group_name,
+                           owner=Optional(OAttr('apiuser')),
+                           active=Optional(True)):
         """
         Creates an new usergroup
 
         :param apiuser:
         :param group_name:
+        :param owner:
         :param active:
         """
 
@@ -551,8 +603,14 @@
             raise JSONRPCError("user group `%s` already exist" % group_name)
 
         try:
+            if isinstance(owner, Optional):
+                owner = apiuser.user_id
+
+            owner = get_user_or_error(owner)
             active = Optional.extract(active)
-            ug = UserGroupModel().create(name=group_name, active=active)
+            ug = UserGroupModel().create(name=group_name,
+                                         owner=owner,
+                                         active=active)
             Session().commit()
             return dict(
                 msg='created new user group `%s`' % group_name,
@@ -633,10 +691,10 @@
         """
         repo = get_repo_or_error(repoid)
 
-        if HasPermissionAnyApi('hg.admin')(user=apiuser) is False:
+        if not HasPermissionAnyApi('hg.admin')(user=apiuser):
             # check if we have admin permission for this repo !
-            if HasRepoPermissionAnyApi('repository.admin')(user=apiuser,
-                                            repo_name=repo.repo_name) is False:
+            if not HasRepoPermissionAnyApi('repository.admin')(user=apiuser,
+                                            repo_name=repo.repo_name):
                 raise JSONRPCError('repository `%s` does not exist' % (repoid))
 
         members = []
@@ -665,6 +723,7 @@
         data['followers'] = followers
         return data
 
+    # permission check inside
     def get_repos(self, apiuser):
         """"
         Get all repositories
@@ -792,6 +851,71 @@
             log.error(traceback.format_exc())
             raise JSONRPCError('failed to create repository `%s`' % repo_name)
 
+    # permission check inside
+    def update_repo(self, apiuser, repoid, name=Optional(None),
+                    owner=Optional(OAttr('apiuser')),
+                    group=Optional(None),
+                    description=Optional(''), private=Optional(False),
+                    clone_uri=Optional(None), landing_rev=Optional('tip'),
+                    enable_statistics=Optional(False),
+                    enable_locking=Optional(False),
+                    enable_downloads=Optional(False)):
+
+        """
+        Updates repo
+
+        :param apiuser: filled automatically from apikey
+        :type apiuser: AuthUser
+        :param repoid: repository name or repository id
+        :type repoid: str or int
+        :param name:
+        :param owner:
+        :param group:
+        :param description:
+        :param private:
+        :param clone_uri:
+        :param landing_rev:
+        :param enable_statistics:
+        :param enable_locking:
+        :param enable_downloads:
+        """
+        repo = get_repo_or_error(repoid)
+        if not HasPermissionAnyApi('hg.admin')(user=apiuser):
+            # check if we have admin permission for this repo !
+            if not HasRepoPermissionAnyApi('repository.admin')(user=apiuser,
+                                                               repo_name=repo.repo_name):
+                raise JSONRPCError('repository `%s` does not exist' % (repoid,))
+
+        updates = {
+            # update function requires this.
+            'repo_name': repo.repo_name
+        }
+        repo_group = group
+        if not isinstance(repo_group, Optional):
+            repo_group = get_repo_group_or_error(repo_group)
+            repo_group = repo_group.group_id
+        try:
+            store_update(updates, name, 'repo_name')
+            store_update(updates, repo_group, 'repo_group')
+            store_update(updates, owner, 'user')
+            store_update(updates, description, 'repo_description')
+            store_update(updates, private, 'repo_private')
+            store_update(updates, clone_uri, 'clone_uri')
+            store_update(updates, landing_rev, 'repo_landing_rev')
+            store_update(updates, enable_statistics, 'repo_enable_statistics')
+            store_update(updates, enable_locking, 'repo_enable_locking')
+            store_update(updates, enable_downloads, 'repo_enable_downloads')
+
+            RepoModel().update(repo, **updates)
+            Session().commit()
+            return dict(
+                msg='updated repo ID:%s %s' % (repo.repo_id, repo.repo_name),
+                repository=repo.get_api_data()
+            )
+        except Exception:
+            log.error(traceback.format_exc())
+            raise JSONRPCError('failed to update repo `%s`' % repoid)
+
     @HasPermissionAnyDecorator('hg.admin', 'hg.fork.repository')
     def fork_repo(self, apiuser, repoid, fork_name, owner=Optional(OAttr('apiuser')),
                   description=Optional(''), copy_permissions=Optional(False),
@@ -853,6 +977,7 @@
                                                             fork_name)
             )
 
+    # perms handled inside
     def delete_repo(self, apiuser, repoid, forks=Optional(None)):
         """
         Deletes a given repository
@@ -874,9 +999,9 @@
             _forks_msg = ''
             _forks = [f for f in repo.forks]
             if handle_forks == 'detach':
-                _forks_msg = ' ' + _('Detached %s forks') % len(_forks)
+                _forks_msg = ' ' + 'Detached %s forks' % len(_forks)
             elif handle_forks == 'delete':
-                _forks_msg = ' ' + _('Deleted %s forks') % len(_forks)
+                _forks_msg = ' ' + 'Deleted %s forks' % len(_forks)
             elif _forks:
                 raise JSONRPCError(
                     'Cannot delete `%s` it still contains attached forks'
@@ -1029,3 +1154,34 @@
                     users_group.users_group_name, repo.repo_name
                 )
             )
+
+    def create_gist(self, apiuser, files, owner=Optional(OAttr('apiuser')),
+                    gist_type=Optional(Gist.GIST_PUBLIC), lifetime=Optional(-1),
+                    description=Optional('')):
+
+        try:
+            if isinstance(owner, Optional):
+                owner = apiuser.user_id
+
+            owner = get_user_or_error(owner)
+            description = Optional.extract(description)
+            gist_type = Optional.extract(gist_type)
+            lifetime = Optional.extract(lifetime)
+
+            # files: {
+            #    'filename': {'content':'...', 'lexer': null},
+            #    'filename2': {'content':'...', 'lexer': null}
+            #}
+            gist = GistModel().create(description=description,
+                                      owner=owner,
+                                      gist_mapping=files,
+                                      gist_type=gist_type,
+                                      lifetime=lifetime)
+            Session().commit()
+            return dict(
+                msg='created new gist',
+                gist=gist.get_api_data()
+            )
+        except Exception:
+            log.error(traceback.format_exc())
+            raise JSONRPCError('failed to create gist')