changeset 1584:3338a0994472 beta

Improve API with user/group/repo CRUD methods
author Nicolas VINOT <aeris@imirhil.fr>
date Sat, 01 Oct 2011 16:34:07 +0200
parents 4aba7be311e8
children 56276c716599
files .hgignore rhodecode/controllers/api/api.py rhodecode/model/db.py
diffstat 3 files changed, 232 insertions(+), 59 deletions(-) [+]
line wrap: on
line diff
--- a/.hgignore	Sun Oct 02 02:06:23 2011 +0200
+++ b/.hgignore	Sat Oct 01 16:34:07 2011 +0200
@@ -1,6 +1,8 @@
 syntax: glob
 *.pyc
 *.swp
+*.ini
+Paste*
 
 syntax: regexp
 ^build
@@ -14,3 +16,4 @@
 ^test\.db$
 ^repositories\.config$
 ^RhodeCode\.egg-info$
+^env$
--- a/rhodecode/controllers/api/api.py	Sun Oct 02 02:06:23 2011 +0200
+++ b/rhodecode/controllers/api/api.py	Sat Oct 01 16:34:07 2011 +0200
@@ -2,10 +2,11 @@
 import logging
 
 from rhodecode.controllers.api import JSONRPCController, JSONRPCError
-from rhodecode.lib.auth import HasPermissionAllDecorator
+from rhodecode.lib.auth import HasPermissionAllDecorator, HasPermissionAnyDecorator
 from rhodecode.model.scm import ScmModel
 
-from rhodecode.model.db import User, UsersGroup, Repository
+from rhodecode.model.db import User, UsersGroup, UsersGroupMember, Group, Repository
+from rhodecode.model.repo import RepoModel
 
 log = logging.getLogger(__name__)
 
@@ -13,25 +14,25 @@
 class ApiController(JSONRPCController):
     """
     API Controller
-    
-    
+
+
     Each method needs to have USER as argument this is then based on given
     API_KEY propagated as instance of user object
-    
+
     Preferably this should be first argument also
-    
-    
-    Each function should also **raise** JSONRPCError for any 
+
+
+    Each function should also **raise** JSONRPCError for any
     errors that happens
-    
+
     """
 
     @HasPermissionAllDecorator('hg.admin')
     def pull(self, apiuser, repo):
         """
         Dispatch pull action on given repo
-        
-        
+
+
         :param user:
         :param repo:
         """
@@ -47,28 +48,30 @@
 
 
     @HasPermissionAllDecorator('hg.admin')
-    def create_user(self, apiuser, username, password, active, admin, name, 
-                    lastname, email):
+    def create_user(self, apiuser, username, password, name,
+                    lastname, email, active=True, admin=False, ldap_dn=None):
         """
         Creates new user
-        
+
         :param apiuser:
         :param username:
         :param password:
-        :param active:
-        :param admin:
         :param name:
         :param lastname:
         :param email:
+        :param active:
+        :param admin:
+        :param ldap_dn:
         """
-        
+
         form_data = dict(username=username,
                          password=password,
                          active=active,
                          admin=admin,
                          name=name,
                          lastname=lastname,
-                         email=email)
+                         email=email,
+                         ldap_dn=ldap_dn)
         try:
             u = User.create(form_data)
             return {'id':u.user_id,
@@ -77,12 +80,27 @@
             log.error(traceback.format_exc())
             raise JSONRPCError('failed to create user %s' % name)
 
+    @HasPermissionAllDecorator('hg.admin')
+    def list_users(self, apiuser):
+        """"
+        Lists all users
+
+        :param apiuser
+        """
+        result = []
+        for user in User.getAll():
+            result.append({ 'username':user.username,
+                            'name': user.name,
+                            'lastname': user.lastname,
+                            'email': user.email })
+        return result
+
 
     @HasPermissionAllDecorator('hg.admin')
-    def create_users_group(self, apiuser, name, active):
+    def create_users_group(self, apiuser, name, active=True):
         """
         Creates an new usergroup
-        
+
         :param name:
         :param active:
         """
@@ -95,4 +113,81 @@
         except Exception:
             log.error(traceback.format_exc())
             raise JSONRPCError('failed to create group %s' % name)
-        
\ No newline at end of file
+
+    @HasPermissionAllDecorator('hg.admin')
+    def list_users_groups(self, apiuser):
+        """"
+        Lists all users groups
+
+        :param apiuser
+        """
+        result = []
+        for users_group in UsersGroup.getAll():
+            result.append({ 'name': users_group.name })
+        return result
+
+
+    @HasPermissionAllDecorator('hg.admin')
+    def add_user_to_group(self, apiuser, user_name, group_name):
+        """"
+        Add a user to a group
+
+        :param apiuser
+        :param user_name
+        :param group_name
+        """
+
+        users_group = UsersGroup.get_by_group_name(group_name)
+        if not users_group:
+            raise JSONRPCError('unknown users group %s' % group_name)
+
+        user = User.by_username(user_name)
+        if not user:
+            raise JSONRPCError('unknown user %s' % user_name)
+
+        try:
+            UsersGroupMember.create(user, users_group)
+        except Exception:
+            log.error(traceback.format_exc())
+            raise JSONRPCError('failed to create users group member')
+
+    @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
+    def create_repo(self, apiuser, name, owner_name, description=None, repo_type='hg', \
+                    private=False, group_name=None):
+        """
+        Create a repository
+
+        :param apiuser
+        :param name
+        :param description
+        :param type
+        :param private
+        :param owner_name
+        :param group_name
+        :param clone
+        """
+
+        if group_name:
+            group = Group.get_by_group_name(group_name)
+            if group is None:
+                raise JSONRPCError('unknown group %s' % group_name)
+        else:
+            group = None
+
+        owner = User.by_username(owner_name)
+        if owner is None:
+            raise JSONRPCError('unknown user %s' % owner)
+
+        try:
+            RepoModel().create({ "repo_name" : name,
+                                 "repo_name_full" : name,
+                                 "description" : description,
+                                 "private" : private,
+                                 "repo_type" : repo_type,
+                                 "repo_group" : group,
+                                 "clone_uri" : None }, owner)
+        except Exception:
+            log.error(traceback.format_exc())
+            raise JSONRPCError('failed to create repository %s' % name)
+
+
--- a/rhodecode/model/db.py	Sun Oct 02 02:06:23 2011 +0200
+++ b/rhodecode/model/db.py	Sat Oct 01 16:34:07 2011 +0200
@@ -57,24 +57,24 @@
 class ModelSerializer(json.JSONEncoder):
     """
     Simple Serializer for JSON,
-    
+
     usage::
-        
+
         to make object customized for serialization implement a __json__
         method that will return a dict for serialization into json
-        
+
     example::
-        
+
         class Task(object):
-        
+
             def __init__(self, name, value):
                 self.name = name
                 self.value = value
-        
+
             def __json__(self):
                 return dict(name=self.name,
-                            value=self.value)     
-        
+                            value=self.value)
+
     """
 
     def default(self, obj):
@@ -125,11 +125,15 @@
 
     @classmethod
     def get(cls, id_):
-        return Session.query(cls).get(id_)
+        return cls.query().get(id_)
+
+    @classmethod
+    def getAll(cls):
+        return cls.query().all()
 
     @classmethod
     def delete(cls, id_):
-        obj = Session.query(cls).get(id_)
+        obj = cls.query().get(id_)
         Session.delete(obj)
         Session.commit()
 
@@ -152,13 +156,13 @@
 
     @classmethod
     def get_by_name(cls, ldap_key):
-        return Session.query(cls)\
+        return cls.query()\
             .filter(cls.app_settings_name == ldap_key).scalar()
 
     @classmethod
     def get_app_settings(cls, cache=False):
 
-        ret = Session.query(cls)
+        ret = cls.query()
 
         if cache:
             ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
@@ -174,7 +178,7 @@
 
     @classmethod
     def get_ldap_settings(cls, cache=False):
-        ret = Session.query(cls)\
+        ret = cls.query()\
                 .filter(cls.app_settings_name.startswith('ldap_'))\
                 .all()
         fd = {}
@@ -204,7 +208,7 @@
 
     @classmethod
     def get_by_key(cls, key):
-        return Session.query(cls).filter(cls.ui_key == key)
+        return cls.query().filter(cls.ui_key == key)
 
 
     @classmethod
@@ -282,14 +286,13 @@
     @classmethod
     def by_username(cls, username, case_insensitive=False):
         if case_insensitive:
-            return Session.query(cls).filter(cls.username.like(username)).one()
+            return cls.query().filter(cls.username.like(username)).one()
         else:
-            return Session.query(cls).filter(cls.username == username).one()
+            return cls.query().filter(cls.username == username).one()
 
     @classmethod
     def get_by_api_key(cls, api_key):
-        return Session.query(cls).filter(cls.api_key == api_key).one()
-
+        return cls.query().filter(cls.api_key == api_key).one()
 
     def update_lastlogin(self):
         """Update user lastlogin"""
@@ -302,7 +305,7 @@
     @classmethod
     def create(cls, form_data):
         from rhodecode.lib.auth import get_crypt_password
-        
+
         try:
             new_user = cls()
             for k, v in form_data.items():
@@ -354,11 +357,11 @@
     @classmethod
     def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
         if case_insensitive:
-            gr = Session.query(cls)\
-            .filter(cls.users_group_name.ilike(group_name))
+            gr = cls.query()\
+                .filter(cls.users_group_name.ilike(group_name))
         else:
-            gr = Session.query(UsersGroup)\
-                .filter(UsersGroup.users_group_name == group_name)
+            gr = cls.query()\
+                .filter(cls.users_group_name == group_name)
         if cache:
             gr = gr.options(FromCache("sql_cache_short",
                                           "get_user_%s" % group_name))
@@ -367,7 +370,7 @@
 
     @classmethod
     def get(cls, users_group_id, cache=False):
-        users_group = Session.query(cls)
+        users_group = cls.query()
         if cache:
             users_group = users_group.options(FromCache("sql_cache_short",
                                     "get_users_group_%s" % users_group_id))
@@ -451,6 +454,22 @@
         self.users_group_id = gr_id
         self.user_id = u_id
 
+    @classmethod
+    def create(cls, user, users_group):
+        try:
+            users_group_member = cls()
+            users_group_member.user = user
+            users_group_member.users_group = users_group
+
+            Session.add(users_group_member)
+            Session.commit()
+            return users_group_member
+        except:
+            log.error(traceback.format_exc())
+            Session.rollback()
+            raise
+
+
 class Repository(Base, BaseModel):
     __tablename__ = 'repositories'
     __table_args__ = (UniqueConstraint('repo_name'), {'extend_existing':True},)
@@ -485,9 +504,31 @@
         return "<%s('%s:%s')>" % (self.__class__.__name__,
                                   self.repo_id, self.repo_name)
 
+    @staticmethod
+    def create(name, description, repo_type, private, owner, group, clone):
+        try:
+            repo = Repository()
+            repo.repo_name = name
+            repo.clone_uri = clone
+            repo.repo_type = repo_type
+            repo.user = owner
+            repo.private = private
+            repo.description = description
+            repo.group = group
+
+            Session.add(repo)
+            Session.commit()
+
+            RepoToPerm.create(repo, owner, Permission.get_by_name('repository.write'))
+            return repo
+        except:
+            log.error(traceback.format_exc())
+            Session.rollback()
+            raise
+
     @classmethod
     def by_repo_name(cls, repo_name):
-        q = Session.query(cls).filter(cls.repo_name == repo_name)
+        q = cls.query().filter(cls.repo_name == repo_name)
 
         q = q.options(joinedload(Repository.fork))\
             .options(joinedload(Repository.user))\
@@ -497,7 +538,7 @@
 
     @classmethod
     def get_repo_forks(cls, repo_id):
-        return Session.query(cls).filter(Repository.fork_id == repo_id)
+        return cls.query().filter(Repository.fork_id == repo_id)
 
     @classmethod
     def base_path(cls):
@@ -541,7 +582,7 @@
         Returns base full path for that repository means where it actually
         exists on a filesystem
         """
-        q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/')
+        q = RhodeCodeUi.query().filter(RhodeCodeUi.ui_key == '/')
         q.options(FromCache("sql_cache_short", "repository_repo_path"))
         return q.one().ui_value
 
@@ -569,7 +610,7 @@
         baseui._tcfg = config.config()
 
 
-        ret = Session.query(RhodeCodeUi)\
+        ret = RhodeCodeUi.query()\
             .options(FromCache("sql_cache_short", "repository_repo_ui")).all()
 
         hg_ui = ret
@@ -624,7 +665,7 @@
         None otherwise. `cache_active = False` means that this cache
         state is not valid and needs to be invalidated
         """
-        return Session.query(CacheInvalidation)\
+        return CacheInvalidation.query()\
             .filter(CacheInvalidation.cache_key == self.repo_name)\
             .filter(CacheInvalidation.cache_active == False)\
             .scalar()
@@ -633,7 +674,7 @@
         """
         set a cache for invalidation for this instance
         """
-        inv = Session.query(CacheInvalidation)\
+        inv = CacheInvalidation.query()\
             .filter(CacheInvalidation.cache_key == self.repo_name)\
             .scalar()
 
@@ -721,6 +762,19 @@
     def url_sep(cls):
         return '/'
 
+    @classmethod
+    def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
+        if case_insensitive:
+            gr = cls.query()\
+                .filter(cls.group_name.ilike(group_name))
+        else:
+            gr = cls.query()\
+                .filter(cls.group_name == group_name)
+        if cache:
+            gr = gr.options(FromCache("sql_cache_short",
+                                          "get_group_%s" % group_name))
+        return gr.scalar()
+
     @property
     def parents(self):
         parents_recursion_limit = 5
@@ -747,7 +801,7 @@
 
     @property
     def children(self):
-        return Session.query(Group).filter(Group.parent_group == self)
+        return Group.query().filter(Group.parent_group == self)
 
     @property
     def full_path(self):
@@ -756,7 +810,7 @@
 
     @property
     def repositories(self):
-        return Session.query(Repository).filter(Repository.group == self)
+        return Repository.query().filter(Repository.group == self)
 
     @property
     def repositories_recursive_count(self):
@@ -784,7 +838,11 @@
 
     @classmethod
     def get_by_key(cls, key):
-        return Session.query(cls).filter(cls.permission_name == key).scalar()
+        return cls.query().filter(cls.permission_name == key).scalar()
+
+    @classmethod
+    def get_by_name(cls, name):
+        return cls.query().filter(cls.permission_name == name).one()
 
 class RepoToPerm(Base, BaseModel):
     __tablename__ = 'repo_to_perm'
@@ -798,6 +856,23 @@
     permission = relationship('Permission')
     repository = relationship('Repository')
 
+    @staticmethod
+    def create(repo, user, p):
+        try:
+            perm = RepoToPerm()
+            perm.repository = repo
+            perm.user = user
+            perm.permission = p
+
+            Session.add(perm)
+            Session.commit()
+
+            return perm
+        except:
+            log.error(traceback.format_exc())
+            Session.rollback()
+            raise
+
 class UserToPerm(Base, BaseModel):
     __tablename__ = 'user_to_perm'
     __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'extend_existing':True})
@@ -813,7 +888,7 @@
         if not isinstance(perm, Permission):
             raise Exception('perm needs to be an instance of Permission class')
 
-        return Session.query(cls).filter(cls.user_id == user_id)\
+        return cls.query().filter(cls.user_id == user_id)\
             .filter(cls.permission == perm).scalar() is not None
 
     @classmethod
@@ -837,7 +912,7 @@
             raise Exception('perm needs to be an instance of Permission class')
 
         try:
-            Session.query(cls).filter(cls.user_id == user_id)\
+            cls.query().filter(cls.user_id == user_id)\
                 .filter(cls.permission == perm).delete()
             Session.commit()
         except:
@@ -873,7 +948,7 @@
         if not isinstance(perm, Permission):
             raise Exception('perm needs to be an instance of Permission class')
 
-        return Session.query(cls).filter(cls.users_group_id ==
+        return cls.query().filter(cls.users_group_id ==
                                          users_group_id)\
                                          .filter(cls.permission == perm)\
                                          .scalar() is not None
@@ -899,7 +974,7 @@
             raise Exception('perm needs to be an instance of Permission class')
 
         try:
-            Session.query(cls).filter(cls.users_group_id == users_group_id)\
+            cls.query().filter(cls.users_group_id == users_group_id)\
                 .filter(cls.permission == perm).delete()
             Session.commit()
         except:
@@ -951,7 +1026,7 @@
 
     @classmethod
     def get_repo_followers(cls, repo_id):
-        return Session.query(cls).filter(cls.follows_repo_id == repo_id)
+        return cls.query().filter(cls.follows_repo_id == repo_id)
 
 class CacheInvalidation(Base, BaseModel):
     __tablename__ = 'cache_invalidation'