Changeset - 2feb58495f34
[Not reviewed]
beta
0 4 0
Marcin Kuzminski - 13 years ago 2013-05-06 16:34:02
marcin@python-works.com
locking API returns objects instead of string messages.

- it's now possible to easily parse the output of lock functions
4 files changed with 92 insertions and 28 deletions:
0 comments (0 inline, 0 general)
docs/api/api.rst
Show inline comments
 
@@ -119,193 +119,199 @@ API METHODS
 
+++++++++++
 

	
 

	
 
pull
 
----
 

	
 
Pulls given repo from remote location. Can be used to automatically keep
 
remote repos up to date. This command can be executed only using api_key
 
belonging to user with admin rights
 

	
 
INPUT::
 

	
 
    id : <id_for_response>
 
    api_key : "<api_key>"
 
    method :  "pull"
 
    args :    {
 
                "repoid" : "<reponame or repo_id>"
 
              }
 

	
 
OUTPUT::
 

	
 
    id : <id_given_in_input>
 
    result : "Pulled from `<reponame>`"
 
    error :  null
 

	
 

	
 
rescan_repos
 
------------
 

	
 
Dispatch rescan repositories action. If remove_obsolete is set
 
RhodeCode will delete repos that are in database but not in the filesystem.
 
This command can be executed only using api_key belonging to user with admin
 
rights.
 

	
 
INPUT::
 

	
 
    id : <id_for_response>
 
    api_key : "<api_key>"
 
    method :  "rescan_repos"
 
    args :    {
 
                "remove_obsolete" : "<boolean = Optional(False)>"
 
              }
 

	
 
OUTPUT::
 

	
 
    id : <id_given_in_input>
 
    result : "{'added': [<list of names of added repos>],
 
               'removed': [<list of names of removed repos>]}"
 
    error :  null
 

	
 

	
 
invalidate_cache
 
----------------
 

	
 
Invalidate cache for repository.
 
This command can be executed only using api_key belonging to user with admin
 
rights or regular user that have write or admin or write access to repository.
 

	
 
INPUT::
 

	
 
    id : <id_for_response>
 
    api_key : "<api_key>"
 
    method :  "invalidate_cache"
 
    args :    {
 
                "repoid" : "<reponame or repo_id>"
 
              }
 

	
 
OUTPUT::
 

	
 
    id : <id_given_in_input>
 
    result : "Caches of repository `<reponame>`"
 
    error :  null
 

	
 
lock
 
----
 

	
 
Set locking state on given repository by given user. If userid param is skipped
 
, then it is set to id of user whos calling this method. If locked param is skipped
 
then function shows current lock state of given repo.
 
This command can be executed only using api_key belonging to user with admin
 
rights or regular user that have admin or write access to repository.
 

	
 
INPUT::
 

	
 
    id : <id_for_response>
 
    api_key : "<api_key>"
 
    method :  "lock"
 
    args :    {
 
                "repoid" : "<reponame or repo_id>"
 
                "userid" : "<user_id or username = Optional(=apiuser)>",
 
                "locked" : "<bool true|false = Optional(=None)>"
 
              }
 

	
 
OUTPUT::
 

	
 
    id : <id_given_in_input>
 
    result : "User `<username>` set lock state for repo `<reponame>` to `true|false`"
 
    result : {  
 
                 "repo": "<reponame>",
 
                 "locked": "<bool true|false>",
 
                 "locked_since": "<float lock_time>",
 
                 "locked_by": "<username>",
 
                 "msg": "User `<username>` set lock state for repo `<reponame>` to `<false|true>`"
 
             }               
 
    error :  null
 

	
 

	
 
show_ip
 
-------
 

	
 
Shows IP address as seen from RhodeCode server, together with all
 
defined IP addresses for given user.
 
This command can be executed only using api_key belonging to user with admin
 
rights.
 

	
 
INPUT::
 

	
 
    id : <id_for_response>
 
    api_key : "<api_key>"
 
    method :  "show_ip"
 
    args :    {
 
                "userid" : "<user_id or username>",
 
              }
 

	
 
OUTPUT::
 

	
 
    id : <id_given_in_input>
 
    result : {
 
                 "ip_addr_server": <ip_from_clien>",
 
                 "user_ips": [
 
                                {
 
                                   "ip_addr": "<ip_with_mask>",
 
                                   "ip_range": ["<start_ip>", "<end_ip>"],
 
                                },
 
                                ...
 
                             ]
 
             }
 

	
 
    error :  null
 

	
 

	
 
get_user
 
--------
 

	
 
Get's an user by username or user_id, Returns empty result if user is not found.
 
If userid param is skipped it is set to id of user who is calling this method.
 
This command can be executed only using api_key belonging to user with admin
 
rights, or regular users that cannot specify different userid than theirs
 

	
 

	
 
INPUT::
 

	
 
    id : <id_for_response>
 
    api_key : "<api_key>"
 
    method :  "get_user"
 
    args :    {
 
                "userid" : "<username or user_id Optional(=apiuser)>"
 
              }
 

	
 
OUTPUT::
 

	
 
    id : <id_given_in_input>
 
    result: None if user does not exist or
 
            {
 
                "user_id" :     "<user_id>",
 
                "api_key" :     "<api_key>",
 
                "username" :    "<username>",
 
                "firstname":    "<firstname>",
 
                "lastname" :    "<lastname>",
 
                "email" :       "<email>",
 
                "emails":       "<list_of_all_additional_emails>",
 
                "ip_addresses": "<list_of_ip_addresses_for_user>",
 
                "active" :      "<bool>",
 
                "admin" :       "<bool>",
 
                "ldap_dn" :     "<ldap_dn>",
 
                "last_login":   "<last_login>",
 
                "permissions": {
 
                    "global": ["hg.create.repository",
 
                               "repository.read",
 
                               "hg.register.manual_activate"],
 
                    "repositories": {"repo1": "repository.none"},
 
                    "repositories_groups": {"Group1": "group.read"}
 
                 },
 
            }
 

	
 
    error:  null
 

	
 

	
 
get_users
 
---------
 

	
 
Lists all existing users. This command can be executed only using api_key
 
belonging to user with admin rights.
 

	
 

	
 
INPUT::
 

	
 
    id : <id_for_response>
 
    api_key : "<api_key>"
 
    method :  "get_users"
rhodecode/controllers/api/api.py
Show inline comments
 
# -*- coding: utf-8 -*-
 
"""
 
    rhodecode.controllers.api
 
    ~~~~~~~~~~~~~~~~~~~~~~~~~
 

	
 
    API controller for RhodeCode
 

	
 
    :created_on: Aug 20, 2011
 
    :author: marcink
 
    :copyright: (C) 2011-2012 Marcin Kuzminski <marcin@python-works.com>
 
    :license: GPLv3, see COPYING for more details.
 
"""
 
# This program is free software; you can redistribute it and/or
 
# modify it under the terms of the GNU General Public License
 
# as published by the Free Software Foundation; version 2
 
# of the License or (at your opinion) any later version of the license.
 
#
 
# This program is distributed in the hope that it will be useful,
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
# GNU General Public License for more details.
 
#
 
# You should have received a copy of the GNU General Public License
 
# along with this program; if not, write to the Free Software
 
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 
# MA  02110-1301, USA.
 

	
 
import time
 
import traceback
 
import logging
 

	
 
from rhodecode.controllers.api import JSONRPCController, JSONRPCError
 
from rhodecode.lib.auth import PasswordGenerator, AuthUser, \
 
    HasPermissionAllDecorator, HasPermissionAnyDecorator, \
 
    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.db import Repository, RhodeCodeSetting, UserIpMap,\
 
    Permission, User
 
from rhodecode.lib.compat import json
 
from rhodecode.lib.exceptions import DefaultUserException
 

	
 
log = logging.getLogger(__name__)
 

	
 

	
 
class OptionalAttr(object):
 
    """
 
    Special Optional Option that defines other attribute
 
    """
 
    def __init__(self, attr_name):
 
        self.attr_name = attr_name
 

	
 
    def __repr__(self):
 
        return '<OptionalAttr:%s>' % self.attr_name
 

	
 
    def __call__(self):
 
        return self
 
#alias
 
OAttr = OptionalAttr
 

	
 

	
 
class Optional(object):
 
    """
 
    Defines an optional parameter::
 

	
 
        param = param.getval() if isinstance(param, Optional) else param
 
        param = param() if isinstance(param, Optional) else param
 

	
 
    is equivalent of::
 

	
 
        param = Optional.extract(param)
 

	
 
    """
 
    def __init__(self, type_):
 
        self.type_ = type_
 

	
 
    def __repr__(self):
 
        return '<Optional:%s>' % self.type_.__repr__()
 

	
 
    def __call__(self):
 
        return self.getval()
 

	
 
    def getval(self):
 
        """
 
        returns value from this Optional instance
 
        """
 
        return self.type_
 

	
 
    @classmethod
 
    def extract(cls, val):
 
        if isinstance(val, cls):
 
            return val.getval()
 
        return val
 

	
 

	
 
def get_user_or_error(userid):
 
    """
 
    Get user by id or name or return JsonRPCError if not found
 

	
 
    :param userid:
 
    """
 
    user = UserModel().get_user(userid)
 
    if user is None:
 
        raise JSONRPCError("user `%s` does not exist" % userid)
 
    return user
 

	
 

	
 
def get_repo_or_error(repoid):
 
    """
 
    Get repo by id or name or return JsonRPCError if not found
 

	
 
    :param userid:
 
    """
 
    repo = RepoModel().get_repo(repoid)
 
    if repo is None:
 
        raise JSONRPCError('repository `%s` does not exist' % (repoid))
 
    return repo
 

	
 
@@ -172,213 +173,233 @@ class ApiController(JSONRPCController):
 
        """
 

	
 
        repo = get_repo_or_error(repoid)
 

	
 
        try:
 
            ScmModel().pull_changes(repo.repo_name,
 
                                    self.rhodecode_user.username)
 
            return 'Pulled from `%s`' % repo.repo_name
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            raise JSONRPCError(
 
                'Unable to pull changes from `%s`' % repo.repo_name
 
            )
 

	
 
    @HasPermissionAllDecorator('hg.admin')
 
    def rescan_repos(self, apiuser, remove_obsolete=Optional(False)):
 
        """
 
        Dispatch rescan repositories action. If remove_obsolete is set
 
        than also delete repos that are in database but not in the filesystem.
 
        aka "clean zombies"
 

	
 
        :param apiuser:
 
        :param remove_obsolete:
 
        """
 

	
 
        try:
 
            rm_obsolete = Optional.extract(remove_obsolete)
 
            added, removed = repo2db_mapper(ScmModel().repo_scan(),
 
                                            remove_obsolete=rm_obsolete)
 
            return {'added': added, 'removed': removed}
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            raise JSONRPCError(
 
                'Error occurred during rescan repositories action'
 
            )
 

	
 
    def invalidate_cache(self, apiuser, repoid):
 
        """
 
        Dispatch cache invalidation action on given repo
 

	
 
        :param apiuser:
 
        :param repoid:
 
        """
 
        repo = get_repo_or_error(repoid)
 
        if HasPermissionAnyApi('hg.admin')(user=apiuser) is False:
 
            # check if we have admin permission for this repo !
 
            if HasRepoPermissionAnyApi('repository.admin',
 
                                       'repository.write')(user=apiuser,
 
                                            repo_name=repo.repo_name) is False:
 
                raise JSONRPCError('repository `%s` does not exist' % (repoid))
 

	
 
        try:
 
            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'
 
            )
 

	
 
    def lock(self, apiuser, repoid, locked=Optional(None),
 
             userid=Optional(OAttr('apiuser'))):
 
        """
 
        Set locking state on particular repository by given user, if
 
        this command is runned by non-admin account userid is set to user
 
        who is calling this method
 

	
 
        :param apiuser:
 
        :param repoid:
 
        :param userid:
 
        :param locked:
 
        """
 
        repo = get_repo_or_error(repoid)
 
        if HasPermissionAnyApi('hg.admin')(user=apiuser):
 
            pass
 
        elif HasRepoPermissionAnyApi('repository.admin',
 
                                     'repository.write')(user=apiuser,
 
                                                         repo_name=repo.repo_name):
 
            #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:
 
                raise JSONRPCError(
 
                    'userid is not the same as your user'
 
                )
 
        else:
 
            raise JSONRPCError('repository `%s` does not exist' % (repoid))
 

	
 
        if isinstance(userid, Optional):
 
            userid = apiuser.user_id
 

	
 
        user = get_user_or_error(userid)
 

	
 
        if isinstance(locked, Optional):
 
            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(
 
                    'Error occurred locking repository `%s`' % repo.repo_name
 
                )
 

	
 
    def get_locks(self, apiuser, userid=Optional(OAttr('apiuser'))):
 
        """
 
        Get all locks for given userid, if
 
        this command is runned by non-admin account userid is set to user
 
        who is calling this method, thus returning locks for himself
 

	
 
        :param apiuser:
 
        :param userid:
 
        """
 
        if HasPermissionAnyApi('hg.admin')(user=apiuser):
 
            pass
 
        else:
 
            #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:
 
                raise JSONRPCError(
 
                    'userid is not the same as your user'
 
                )
 
        ret = []
 
        if isinstance(userid, Optional):
 
            user = None
 
        else:
 
            user = get_user_or_error(userid)
 

	
 
        #show all locks
 
        for r in Repository.getAll():
 
            userid, time_ = r.locked
 
            if time_:
 
                _api_data = r.get_api_data()
 
                # if we use userfilter just show the locks for this user
 
                if user:
 
                    if safe_int(userid) == user.user_id:
 
                        ret.append(_api_data)
 
                else:
 
                    ret.append(_api_data)
 

	
 
        return ret
 

	
 
    @HasPermissionAllDecorator('hg.admin')
 
    def show_ip(self, apiuser, userid):
 
        """
 
        Shows IP address as seen from RhodeCode server, together with all
 
        defined IP addresses for given user
 

	
 
        :param apiuser:
 
        :param userid:
 
        """
 
        user = get_user_or_error(userid)
 
        ips = UserIpMap.query().filter(UserIpMap.user == user).all()
 
        return dict(
 
            ip_addr_server=self.ip_addr,
 
            user_ips=ips
 
        )
 

	
 
    def get_user(self, apiuser, userid=Optional(OAttr('apiuser'))):
 
        """"
 
        Get a user by username, or userid, if userid is given
 

	
 
        :param apiuser:
 
        :param userid:
 
        """
 
        if HasPermissionAnyApi('hg.admin')(user=apiuser) is False:
 
            #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:
 
                raise JSONRPCError(
 
                    'userid is not the same as your user'
 
                )
 

	
 
        if isinstance(userid, Optional):
 
            userid = apiuser.user_id
 

	
 
        user = get_user_or_error(userid)
 
        data = user.get_api_data()
 
        data['permissions'] = AuthUser(user_id=user.user_id).permissions
 
        return data
 

	
 
    @HasPermissionAllDecorator('hg.admin')
 
    def get_users(self, apiuser):
 
        """"
 
        Get all users
 

	
 
        :param apiuser:
 
        """
 

	
 
        result = []
 
        users_list = User.query().order_by(User.username)\
 
                        .filter(User.username != User.DEFAULT_USER)\
 
                        .all()
 
        for user in users_list:
rhodecode/model/db.py
Show inline comments
 
@@ -914,194 +914,196 @@ class Repository(Base, BaseModel):
 
        return self.groups_with_parents, self.just_name, self.repo_name
 

	
 
    @LazyProperty
 
    def repo_path(self):
 
        """
 
        Returns base full path for that repository means where it actually
 
        exists on a filesystem
 
        """
 
        q = Session().query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
 
                                              Repository.url_sep())
 
        q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
 
        return q.one().ui_value
 

	
 
    @property
 
    def repo_full_path(self):
 
        p = [self.repo_path]
 
        # we need to split the name by / since this is how we store the
 
        # names in the database, but that eventually needs to be converted
 
        # into a valid system path
 
        p += self.repo_name.split(Repository.url_sep())
 
        return os.path.join(*map(safe_unicode, p))
 

	
 
    @property
 
    def cache_keys(self):
 
        """
 
        Returns associated cache keys for that repo
 
        """
 
        return CacheInvalidation.query()\
 
            .filter(CacheInvalidation.cache_args == self.repo_name)\
 
            .order_by(CacheInvalidation.cache_key)\
 
            .all()
 

	
 
    def get_new_name(self, repo_name):
 
        """
 
        returns new full repository name based on assigned group and new new
 

	
 
        :param group_name:
 
        """
 
        path_prefix = self.group.full_path_splitted if self.group else []
 
        return Repository.url_sep().join(path_prefix + [repo_name])
 

	
 
    @property
 
    def _ui(self):
 
        """
 
        Creates an db based ui object for this repository
 
        """
 
        from rhodecode.lib.utils import make_ui
 
        return make_ui('db', clear_session=False)
 

	
 
    @classmethod
 
    def is_valid(cls, repo_name):
 
        """
 
        returns True if given repo name is a valid filesystem repository
 

	
 
        :param cls:
 
        :param repo_name:
 
        """
 
        from rhodecode.lib.utils import is_valid_repo
 

	
 
        return is_valid_repo(repo_name, cls.base_path())
 

	
 
    def get_api_data(self):
 
        """
 
        Common function for generating repo api data
 

	
 
        """
 
        repo = self
 
        data = dict(
 
            repo_id=repo.repo_id,
 
            repo_name=repo.repo_name,
 
            repo_type=repo.repo_type,
 
            clone_uri=repo.clone_uri,
 
            private=repo.private,
 
            created_on=repo.created_on,
 
            description=repo.description,
 
            landing_rev=repo.landing_rev,
 
            owner=repo.user.username,
 
            fork_of=repo.fork.repo_name if repo.fork else None,
 
            enable_statistics=repo.enable_statistics,
 
            enable_locking=repo.enable_locking,
 
            enable_downloads=repo.enable_downloads,
 
            last_changeset=repo.changeset_cache,
 
            locked_by=User.get(self.locked[0]).get_api_data() \
 
                if self.locked[0] else None,
 
            locked_date=time_to_datetime(self.locked[1]) \
 
                if self.locked[1] else None
 
        )
 
        rc_config = RhodeCodeSetting.get_app_settings()
 
        repository_fields = str2bool(rc_config.get('rhodecode_repository_fields'))
 
        if repository_fields:
 
            for f in self.extra_fields:
 
                data[f.field_key_prefixed] = f.field_value
 

	
 
        return data
 

	
 
    @classmethod
 
    def lock(cls, repo, user_id):
 
        repo.locked = [user_id, time.time()]
 
    def lock(cls, repo, user_id, lock_time=None):
 
        if not lock_time:
 
            lock_time = time.time()
 
        repo.locked = [user_id, lock_time]
 
        Session().add(repo)
 
        Session().commit()
 

	
 
    @classmethod
 
    def unlock(cls, repo):
 
        repo.locked = None
 
        Session().add(repo)
 
        Session().commit()
 

	
 
    @classmethod
 
    def getlock(cls, repo):
 
        return repo.locked
 

	
 
    @property
 
    def last_db_change(self):
 
        return self.updated_on
 

	
 
    def clone_url(self, **override):
 
        from pylons import url
 
        from urlparse import urlparse
 
        import urllib
 
        parsed_url = urlparse(url('home', qualified=True))
 
        default_clone_uri = '%(scheme)s://%(user)s%(pass)s%(netloc)s%(prefix)s%(path)s'
 
        decoded_path = safe_unicode(urllib.unquote(parsed_url.path))
 
        args = {
 
           'user': '',
 
           'pass': '',
 
           'scheme': parsed_url.scheme,
 
           'netloc': parsed_url.netloc,
 
           'prefix': decoded_path,
 
           'path': self.repo_name
 
        }
 

	
 
        args.update(override)
 
        return default_clone_uri % args
 

	
 
    #==========================================================================
 
    # SCM PROPERTIES
 
    #==========================================================================
 

	
 
    def get_changeset(self, rev=None):
 
        return get_changeset_safe(self.scm_instance, rev)
 

	
 
    def get_landing_changeset(self):
 
        """
 
        Returns landing changeset, or if that doesn't exist returns the tip
 
        """
 
        cs = self.get_changeset(self.landing_rev) or self.get_changeset()
 
        return cs
 

	
 
    def update_changeset_cache(self, cs_cache=None):
 
        """
 
        Update cache of last changeset for repository, keys should be::
 

	
 
            short_id
 
            raw_id
 
            revision
 
            message
 
            date
 
            author
 

	
 
        :param cs_cache:
 
        """
 
        from rhodecode.lib.vcs.backends.base import BaseChangeset
 
        if cs_cache is None:
 
            cs_cache = EmptyChangeset()
 
            # use no-cache version here
 
            scm_repo = self.scm_instance_no_cache()
 
            if scm_repo:
 
                cs_cache = scm_repo.get_changeset()
 

	
 
        if isinstance(cs_cache, BaseChangeset):
 
            cs_cache = cs_cache.__json__()
 

	
 
        if (cs_cache != self.changeset_cache or not self.changeset_cache):
 
            _default = datetime.datetime.fromtimestamp(0)
 
            last_change = cs_cache.get('date') or _default
 
            log.debug('updated repo %s with new cs cache %s'
 
                      % (self.repo_name, cs_cache))
 
            self.updated_on = last_change
 
            self.changeset_cache = cs_cache
 
            Session().add(self)
 
            Session().commit()
 
        else:
 
            log.debug('Skipping repo:%s already with latest changes'
 
                      % self.repo_name)
 

	
 
    @property
 
    def tip(self):
 
        return self.get_changeset('tip')
 

	
 
    @property
 
    def author(self):
 
        return self.tip.author
 

	
 
    @property
rhodecode/tests/api/api_base.py
Show inline comments
 
from __future__ import with_statement
 
import random
 
import mock
 

	
 
from rhodecode.tests import *
 
from rhodecode.tests.fixture import Fixture
 
from rhodecode.lib.compat import json
 
from rhodecode.lib.auth import AuthUser
 
from rhodecode.model.user import UserModel
 
from rhodecode.model.users_group import UserGroupModel
 
from rhodecode.model.repo import RepoModel
 
from rhodecode.model.meta import Session
 
from rhodecode.model.scm import ScmModel
 
from rhodecode.model.db import Repository, User
 
from rhodecode.lib.utils2 import  time_to_datetime
 

	
 

	
 
API_URL = '/_admin/api'
 
TEST_USER_GROUP = 'test_users_group'
 

	
 
fixture = Fixture()
 

	
 

	
 
def _build_data(apikey, method, **kw):
 
    """
 
    Builds API data with given random ID
 

	
 
    :param random_id:
 
    :type random_id:
 
    """
 
    random_id = random.randrange(1, 9999)
 
    return random_id, json.dumps({
 
        "id": random_id,
 
        "api_key": apikey,
 
        "method": method,
 
        "args": kw
 
    })
 

	
 
jsonify = lambda obj: json.loads(json.dumps(obj))
 

	
 

	
 
def crash(*args, **kwargs):
 
    raise Exception('Total Crash !')
 

	
 

	
 
def api_call(test_obj, params):
 
    response = test_obj.app.post(API_URL, content_type='application/json',
 
                                 params=params)
 
    return response
 

	
 

	
 
## helpers
 
def make_users_group(name=TEST_USER_GROUP):
 
    gr = fixture.create_user_group(name, cur_user=TEST_USER_ADMIN_LOGIN)
 
    UserGroupModel().add_user_to_group(users_group=gr,
 
                                       user=TEST_USER_ADMIN_LOGIN)
 
    Session().commit()
 
    return gr
 

	
 

	
 
def destroy_users_group(name=TEST_USER_GROUP):
 
    UserGroupModel().delete(users_group=name, force=True)
 
    Session().commit()
 

	
 

	
 
class BaseTestApi(object):
 
    REPO = None
 
    REPO_TYPE = None
 

	
 
    @classmethod
 
    def setUpClass(self):
 
        self.usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
 
        self.apikey = self.usr.api_key
 
        self.test_user = UserModel().create_or_update(
 
            username='test-api',
 
            password='test',
 
            email='test@api.rhodecode.org',
 
            firstname='first',
 
            lastname='last'
 
        )
 
        Session().commit()
 
        self.TEST_USER_LOGIN = self.test_user.username
 
        self.apikey_regular = self.test_user.api_key
 

	
 
    @classmethod
 
    def teardownClass(self):
 
        pass
 

	
 
    def setUp(self):
 
        self.maxDiff = None
 
        make_users_group()
 

	
 
    def tearDown(self):
 
        destroy_users_group()
 

	
 
    def _compare_ok(self, id_, expected, given):
 
        expected = jsonify({
 
            'id': id_,
 
            'error': None,
 
            'result': expected
 
        })
 
        given = json.loads(given)
 
        self.assertEqual(expected, given)
 

	
 
    def _compare_error(self, id_, expected, given):
 
        expected = jsonify({
 
            'id': id_,
 
            'error': expected,
 
            'result': None
 
        })
 
        given = json.loads(given)
 
@@ -198,262 +199,296 @@ class BaseTestApi(object):
 

	
 
        usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
 
        ret = usr.get_api_data()
 
        ret['permissions'] = AuthUser(usr.user_id).permissions
 

	
 
        expected = ret
 
        self._compare_ok(id_, expected, given=response.body)
 

	
 
    def test_api_get_user_without_giving_userid_non_admin(self):
 
        id_, params = _build_data(self.apikey_regular, 'get_user')
 
        response = api_call(self, params)
 

	
 
        usr = UserModel().get_by_username(self.TEST_USER_LOGIN)
 
        ret = usr.get_api_data()
 
        ret['permissions'] = AuthUser(usr.user_id).permissions
 

	
 
        expected = ret
 
        self._compare_ok(id_, expected, given=response.body)
 

	
 
    def test_api_get_user_with_giving_userid_non_admin(self):
 
        id_, params = _build_data(self.apikey_regular, 'get_user',
 
                                  userid=self.TEST_USER_LOGIN)
 
        response = api_call(self, params)
 

	
 
        expected = 'userid is not the same as your user'
 
        self._compare_error(id_, expected, given=response.body)
 

	
 
    def test_api_pull(self):
 
        #TODO: issues with rhodecode_extras here.. not sure why !
 
        pass
 

	
 
#        repo_name = 'test_pull'
 
#        r = fixture.create_repo(repo_name, repo_type=self.REPO_TYPE)
 
#        r.clone_uri = TEST_self.REPO
 
#        Session.add(r)
 
#        Session.commit()
 
#
 
#        id_, params = _build_data(self.apikey, 'pull',
 
#                                  repoid=repo_name,)
 
#        response = self.app.post(API_URL, content_type='application/json',
 
#                                 params=params)
 
#
 
#        expected = 'Pulled from `%s`' % repo_name
 
#        self._compare_ok(id_, expected, given=response.body)
 
#
 
#        fixture.destroy_repo(repo_name)
 

	
 
    def test_api_pull_error(self):
 
        id_, params = _build_data(self.apikey, 'pull',
 
                                  repoid=self.REPO,)
 
        response = api_call(self, params)
 

	
 
        expected = 'Unable to pull changes from `%s`' % self.REPO
 
        self._compare_error(id_, expected, given=response.body)
 

	
 
    def test_api_rescan_repos(self):
 
        id_, params = _build_data(self.apikey, 'rescan_repos')
 
        response = api_call(self, params)
 

	
 
        expected = {'added': [], 'removed': []}
 
        self._compare_ok(id_, expected, given=response.body)
 

	
 
    @mock.patch.object(ScmModel, 'repo_scan', crash)
 
    def test_api_rescann_error(self):
 
        id_, params = _build_data(self.apikey, 'rescan_repos',)
 
        response = api_call(self, params)
 

	
 
        expected = 'Error occurred during rescan repositories action'
 
        self._compare_error(id_, expected, given=response.body)
 

	
 
    def test_api_invalidate_cache(self):
 
        repo = RepoModel().get_by_repo_name(self.REPO)
 
        repo.scm_instance_cached() # seed cache
 

	
 
        id_, params = _build_data(self.apikey, 'invalidate_cache',
 
                                  repoid=self.REPO)
 
        response = api_call(self, params)
 

	
 
        expected = ("Caches of repository `%s` was invalidated" % (self.REPO))
 
        self._compare_ok(id_, expected, given=response.body)
 

	
 
    @mock.patch.object(ScmModel, 'mark_for_invalidation', crash)
 
    def test_api_invalidate_cache_error(self):
 
        id_, params = _build_data(self.apikey, 'invalidate_cache',
 
                                  repoid=self.REPO)
 
        response = api_call(self, params)
 

	
 
        expected = 'Error occurred during cache invalidation action'
 
        self._compare_error(id_, expected, given=response.body)
 

	
 
    def test_api_lock_repo_lock_aquire(self):
 
        id_, params = _build_data(self.apikey, 'lock',
 
                                  userid=TEST_USER_ADMIN_LOGIN,
 
                                  repoid=self.REPO,
 
                                  locked=True)
 
        response = api_call(self, params)
 
        expected = ('User `%s` set lock state for repo `%s` to `%s`'
 
                   % (TEST_USER_ADMIN_LOGIN, self.REPO, True))
 
        expected = {
 
            'repo': self.REPO,
 
            'locked': True,
 
            'locked_since': None,
 
            'locked_by': TEST_USER_ADMIN_LOGIN,
 
            'msg': ('User `%s` set lock state for repo `%s` to `%s`'
 
                    % (TEST_USER_ADMIN_LOGIN, self.REPO, True))
 
        }
 
        expected['locked_since'] = json.loads(response.body)['result']['locked_since']
 
        self._compare_ok(id_, expected, given=response.body)
 

	
 
    def test_api_lock_repo_lock_aquire_by_non_admin(self):
 
        repo_name = 'api_delete_me'
 
        fixture.create_repo(repo_name, repo_type=self.REPO_TYPE,
 
                            cur_user=self.TEST_USER_LOGIN)
 
        try:
 
            id_, params = _build_data(self.apikey_regular, 'lock',
 
                                      repoid=repo_name,
 
                                      locked=True)
 
            response = api_call(self, params)
 
            expected = ('User `%s` set lock state for repo `%s` to `%s`'
 
                       % (self.TEST_USER_LOGIN, repo_name, True))
 
            expected = {
 
                'repo': repo_name,
 
                'locked': True,
 
                'locked_since': None,
 
                'locked_by': self.TEST_USER_LOGIN,
 
                'msg': ('User `%s` set lock state for repo `%s` to `%s`'
 
                        % (self.TEST_USER_LOGIN, repo_name, True))
 
            }
 
            expected['locked_since'] = json.loads(response.body)['result']['locked_since']
 
            self._compare_ok(id_, expected, given=response.body)
 
        finally:
 
            fixture.destroy_repo(repo_name)
 

	
 
    def test_api_lock_repo_lock_aquire_non_admin_with_userid(self):
 
        repo_name = 'api_delete_me'
 
        fixture.create_repo(repo_name, repo_type=self.REPO_TYPE,
 
                            cur_user=self.TEST_USER_LOGIN)
 
        try:
 
            id_, params = _build_data(self.apikey_regular, 'lock',
 
                                      userid=TEST_USER_ADMIN_LOGIN,
 
                                      repoid=repo_name,
 
                                      locked=True)
 
            response = api_call(self, params)
 
            expected = 'userid is not the same as your user'
 
            self._compare_error(id_, expected, given=response.body)
 
        finally:
 
            fixture.destroy_repo(repo_name)
 

	
 
    def test_api_lock_repo_lock_aquire_non_admin_not_his_repo(self):
 
        id_, params = _build_data(self.apikey_regular, 'lock',
 
                                  repoid=self.REPO,
 
                                  locked=True)
 
        response = api_call(self, params)
 
        expected = 'repository `%s` does not exist' % (self.REPO)
 
        self._compare_error(id_, expected, given=response.body)
 

	
 
    def test_api_lock_repo_lock_release(self):
 
        id_, params = _build_data(self.apikey, 'lock',
 
                                  userid=TEST_USER_ADMIN_LOGIN,
 
                                  repoid=self.REPO,
 
                                  locked=False)
 
        response = api_call(self, params)
 
        expected = ('User `%s` set lock state for repo `%s` to `%s`'
 
                   % (TEST_USER_ADMIN_LOGIN, self.REPO, False))
 
        expected = {
 
            'repo': self.REPO,
 
            'locked': False,
 
            'locked_since': None,
 
            'locked_by': TEST_USER_ADMIN_LOGIN,
 
            'msg': ('User `%s` set lock state for repo `%s` to `%s`'
 
                    % (TEST_USER_ADMIN_LOGIN, self.REPO, False))
 
        }
 
        self._compare_ok(id_, expected, given=response.body)
 

	
 
    def test_api_lock_repo_lock_aquire_optional_userid(self):
 
        id_, params = _build_data(self.apikey, 'lock',
 
                                  repoid=self.REPO,
 
                                  locked=True)
 
        response = api_call(self, params)
 
        expected = ('User `%s` set lock state for repo `%s` to `%s`'
 
                   % (TEST_USER_ADMIN_LOGIN, self.REPO, True))
 
        expected = {
 
            'repo': self.REPO,
 
            'locked': True,
 
            'locked_since': None,
 
            'locked_by': TEST_USER_ADMIN_LOGIN,
 
            'msg': ('User `%s` set lock state for repo `%s` to `%s`'
 
                    % (TEST_USER_ADMIN_LOGIN, self.REPO, True))
 
        }
 
        expected['locked_since'] = json.loads(response.body)['result']['locked_since']
 
        self._compare_ok(id_, expected, given=response.body)
 

	
 
    def test_api_lock_repo_lock_optional_locked(self):
 
        from rhodecode.lib.utils2 import  time_to_datetime
 
        _locked_since = json.dumps(time_to_datetime(Repository\
 
                                    .get_by_repo_name(self.REPO).locked[1]))
 
        id_, params = _build_data(self.apikey, 'lock',
 
                                  repoid=self.REPO)
 
        response = api_call(self, params)
 
        expected = ('Repo `%s` locked by `%s`. Locked=`True`. Locked since: `%s`'
 
                   % (self.REPO, TEST_USER_ADMIN_LOGIN, _locked_since))
 
        time_ = json.loads(response.body)['result']['locked_since']
 
        expected = {
 
            'repo': self.REPO,
 
            'locked': True,
 
            'locked_since': None,
 
            'locked_by': TEST_USER_ADMIN_LOGIN,
 
            'msg': ('Repo `%s` locked by `%s`. '
 
                            % (self.REPO,
 
                               json.dumps(time_to_datetime(time_))))
 

	
 
        }
 
        expected['locked_since'] = time_
 
        self._compare_ok(id_, expected, given=response.body)
 

	
 
    @mock.patch.object(Repository, 'lock', crash)
 
    def test_api_lock_error(self):
 
        id_, params = _build_data(self.apikey, 'lock',
 
                                  userid=TEST_USER_ADMIN_LOGIN,
 
                                  repoid=self.REPO,
 
                                  locked=True)
 
        response = api_call(self, params)
 

	
 
        expected = 'Error occurred locking repository `%s`' % self.REPO
 
        self._compare_error(id_, expected, given=response.body)
 

	
 
    def test_api_get_locks_regular_user(self):
 
        id_, params = _build_data(self.apikey_regular, 'get_locks')
 
        response = api_call(self, params)
 
        expected = []
 
        self._compare_ok(id_, expected, given=response.body)
 

	
 
    def test_api_get_locks_with_userid_regular_user(self):
 
        id_, params = _build_data(self.apikey_regular, 'get_locks',
 
                                  userid=TEST_USER_ADMIN_LOGIN)
 
        response = api_call(self, params)
 
        expected = 'userid is not the same as your user'
 
        self._compare_error(id_, expected, given=response.body)
 

	
 
    def test_api_get_locks(self):
 
        id_, params = _build_data(self.apikey, 'get_locks')
 
        response = api_call(self, params)
 
        expected = []
 
        self._compare_ok(id_, expected, given=response.body)
 

	
 
    def test_api_get_locks_with_userid(self):
 
        id_, params = _build_data(self.apikey, 'get_locks',
 
                                  userid=TEST_USER_REGULAR_LOGIN)
 
        response = api_call(self, params)
 
        expected = []
 
        self._compare_ok(id_, expected, given=response.body)
 

	
 
    def test_api_create_existing_user(self):
 
        id_, params = _build_data(self.apikey, 'create_user',
 
                                  username=TEST_USER_ADMIN_LOGIN,
 
                                  email='test@foo.com',
 
                                  password='trololo')
 
        response = api_call(self, params)
 

	
 
        expected = "user `%s` already exist" % TEST_USER_ADMIN_LOGIN
 
        self._compare_error(id_, expected, given=response.body)
 

	
 
    def test_api_create_user_with_existing_email(self):
 
        id_, params = _build_data(self.apikey, 'create_user',
 
                                  username=TEST_USER_ADMIN_LOGIN + 'new',
 
                                  email=TEST_USER_REGULAR_EMAIL,
 
                                  password='trololo')
 
        response = api_call(self, params)
 

	
 
        expected = "email `%s` already exist" % TEST_USER_REGULAR_EMAIL
 
        self._compare_error(id_, expected, given=response.body)
 

	
 
    def test_api_create_user(self):
 
        username = 'test_new_api_user'
 
        email = username + "@foo.com"
 

	
 
        id_, params = _build_data(self.apikey, 'create_user',
 
                                  username=username,
 
                                  email=email,
 
                                  password='trololo')
 
        response = api_call(self, params)
 

	
 
        usr = UserModel().get_by_username(username)
 
        ret = dict(
 
            msg='created new user `%s`' % username,
 
            user=jsonify(usr.get_api_data())
 
        )
 

	
 
        expected = ret
 
        self._compare_ok(id_, expected, given=response.body)
 

	
 
        UserModel().delete(usr.user_id)
 
        Session().commit()
 

	
 
    @mock.patch.object(UserModel, 'create_or_update', crash)
 
    def test_api_create_user_when_exception_happened(self):
 

	
 
        username = 'test_new_api_user'
 
        email = username + "@foo.com"
 

	
 
        id_, params = _build_data(self.apikey, 'create_user',
 
                                  username=username,
 
                                  email=email,
 
                                  password='trololo')
 
        response = api_call(self, params)
 
        expected = 'failed to create user `%s`' % username
 
        self._compare_error(id_, expected, given=response.body)
 

	
 
    def test_api_delete_user(self):
0 comments (0 inline, 0 general)