{"action":"created","comment":{"url":"https://api.github.com/repos/scitran/api/pulls/comments/39813104","id":39813104,"diff_hunk":"@@ -0,0 +1,148 @@\n+# @author:  Renzo Frigato\n+\n+import bson.objectid\n+import copy\n+import logging\n+import re\n+\n+log = logging.getLogger('scitran.api')\n+\n+\n+class ListStorage(object):\n+    \"\"\"\n+    This class provides access to sublists of a mongodb collections elements (called containers).\n+    \"\"\"\n+\n+    def __init__(self, coll_name, list_name, use_oid = False):\n+        self.coll_name = coll_name\n+        self.list_name = list_name\n+        self.use_oid = use_oid\n+        # the collection is not loaded when the class is instantiated\n+        # this allows to instantiate the class when the db is not available\n+        # dbc is initialized using the load_collection method\n+        self.dbc = None\n+\n+\n+    def load_collection(self, db):\n+        \"\"\"\n+        Initialize the mongodb collection.\n+        \"\"\"\n+        if self.dbc is None:\n+            self.dbc = db.get_collection(self.coll_name)\n+        return self.dbc\n+\n+    def get_container(self, _id):\n+        \"\"\"\n+        Load a container from the _id.\n+\n+        This method is usually used to to check permission properties of the container.\n+        e.g. list of users that can access the container\n+\n+        For simplicity we load its full content.\n+        \"\"\"\n+        if self.dbc is None:\n+            raise RuntimeError('collection not initialized before calling get_container')\n+        if self.use_oid:\n+            _id = bson.objectid.ObjectId(_id)\n+        query = {'_id': _id}\n+        log.debug('query {}'.format(query))\n+        self.container = self.dbc.find_one(query)\n+        return self.container\n+\n+    def apply_change(self, action, _id, elem_match=None, payload=None):\n+        \"\"\"\n+        Generic method to apply an operation.\n+        The request is dispatched to the corresponding private methods.\n+        \"\"\"\n+        if self.use_oid:\n+            _id = bson.objectid.ObjectId(_id)\n+        if action == 'GET':\n+            return self._get_el(_id, elem_match)\n+        if action == 'DELETE':\n+            return self._delete_el(_id, elem_match)\n+        if action == 'PUT':\n+            return self._update_el(_id, elem_match, payload)\n+        if action == 'POST':\n+            return self._create_el(_id, payload)\n+        raise ValueError('action should be one of GET, POST, PUT, DELETE')\n+\n+    def _create_el(self, _id, payload):\n+        log.debug('payload {}'.format(payload))\n+        if isinstance(payload, dict):\n+            if payload.get('_id') is None:\n+                elem_match = copy.deepcopy(payload)\n+                payload['_id'] = bson.objectid.ObjectId()\n+            else:\n+                elem_match = payload\n+            query = {'_id': _id, self.list_name: {'$not': {'$elemMatch': elem_match} } }\n+        else:\n+            query = {'_id': _id, self.list_name: {'$ne': payload } }\n+        update = {'$push': {self.list_name: payload} }\n+        log.debug('query {}'.format(query))\n+        log.debug('update {}'.format(update))\n+        return self.dbc.update_one(query, update)\n+\n+    def _update_el(self, _id, elem_match, payload):\n+        log.debug('elem_match {}'.format(payload))\n+        log.debug('payload {}'.format(elem_match))\n+        if isinstance(payload, dict):\n+            mod_elem = {}\n+            for k,v in payload.items():\n+                mod_elem[self.list_name + '.$.' + k] = v\n+            query = {'_id': _id, self.list_name: {'$elemMatch': elem_match} }\n+            if payload.get('_id') is not None:\n+                query[self.list_name + '._id'] = {'$ne': payload.get('_id')}\n+        else:\n+            mod_elem = {\n+                self.list_name + '.$': payload\n+            }\n+            query = {\n+                '_id': _id,\n+                '$and':[\n+                    {self.list_name: elem_match},\n+                    {self.list_name: {'$ne': payload} }\n+                ]\n+            }\n+        update = {\n+            '$set': mod_elem\n+        }\n+        log.debug('query {}'.format(query))\n+        log.debug('update {}'.format(update))\n+        return self.dbc.update_one(query, update)\n+\n+    def _delete_el(self, _id, elem_match):\n+        log.debug('elem_match {}'.format(elem_match))\n+        query = {'_id': _id}\n+        update = {'$pull': {self.list_name: elem_match} }\n+        log.debug('query {}'.format(query))\n+        log.debug('update {}'.format(update))\n+        return self.dbc.update_one(query, update)\n+\n+    def _get_el(self, _id, elem_match = None):\n+        log.debug('elem_match {}'.format(elem_match))\n+        if isinstance(elem_match, str):\n+            query_params = elem_match\n+        else:\n+            query_params = {'$elemMatch': elem_match}\n+        query = {'_id': _id, self.list_name: query_params}\n+        projection = {self.list_name + '.$': 1}\n+        log.debug('query {}'.format(query))\n+        log.debug('projection {}'.format(projection))\n+        return self.dbc.find_one(query, projection)\n+\n+\n+class StringListStorage(ListStorage):\n+\n+    def __init__(self, coll_name, list_name, use_oid, key_name):","path":"api/dao/storage.py","position":136,"original_position":136,"commit_id":"70fa8ba108d75c8cc57771c8d5bc47553e675366","original_commit_id":"70fa8ba108d75c8cc57771c8d5bc47553e675366","user":{"login":"rentzso","id":1427389,"avatar_url":"https://avatars.githubusercontent.com/u/1427389?v=3","gravatar_id":"","url":"https://api.github.com/users/rentzso","html_url":"https://github.com/rentzso","followers_url":"https://api.github.com/users/rentzso/followers","following_url":"https://api.github.com/users/rentzso/following{/other_user}","gists_url":"https://api.github.com/users/rentzso/gists{/gist_id}","starred_url":"https://api.github.com/users/rentzso/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rentzso/subscriptions","organizations_url":"https://api.github.com/users/rentzso/orgs","repos_url":"https://api.github.com/users/rentzso/repos","events_url":"https://api.github.com/users/rentzso/events{/privacy}","received_events_url":"https://api.github.com/users/rentzso/received_events","type":"User","site_admin":false},"body":"key_name should be always 'value'\r\nthe parameter gets removed","created_at":"2015-09-17T23:58:43Z","updated_at":"2015-09-17T23:58:43Z","html_url":"https://github.com/scitran/api/pull/47#discussion_r39813104","pull_request_url":"https://api.github.com/repos/scitran/api/pulls/47","_links":{"self":{"href":"https://api.github.com/repos/scitran/api/pulls/comments/39813104"},"html":{"href":"https://github.com/scitran/api/pull/47#discussion_r39813104"},"pull_request":{"href":"https://api.github.com/repos/scitran/api/pulls/47"}}},"pull_request":{"url":"https://api.github.com/repos/scitran/api/pulls/47","id":45342752,"html_url":"https://github.com/scitran/api/pull/47","diff_url":"https://github.com/scitran/api/pull/47.diff","patch_url":"https://github.com/scitran/api/pull/47.patch","issue_url":"https://api.github.com/repos/scitran/api/issues/47","number":47,"state":"open","locked":false,"title":"Refactoring - WIP","user":{"login":"rentzso","id":1427389,"avatar_url":"https://avatars.githubusercontent.com/u/1427389?v=3","gravatar_id":"","url":"https://api.github.com/users/rentzso","html_url":"https://github.com/rentzso","followers_url":"https://api.github.com/users/rentzso/followers","following_url":"https://api.github.com/users/rentzso/following{/other_user}","gists_url":"https://api.github.com/users/rentzso/gists{/gist_id}","starred_url":"https://api.github.com/users/rentzso/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rentzso/subscriptions","organizations_url":"https://api.github.com/users/rentzso/orgs","repos_url":"https://api.github.com/users/rentzso/repos","events_url":"https://api.github.com/users/rentzso/events{/privacy}","received_events_url":"https://api.github.com/users/rentzso/received_events","type":"User","site_admin":false},"body":"This is still a work in progress.\r\n\r\nI am creating this pull request to have a preliminary code review.\r\n\r\nThis code  will handle updates to container sublists like tags, group roles, container permissions, etc\r\n\r\nThe idea is to follow this steps on each operation:\r\n1) get the container \r\n2) check if it is a superuser request. If it is, perform the operation\r\n3) check if it is a public request. If it is, perform only GET operation on public containers\r\n4) if it is not a superuser or a public request, check the user permissions on the container\r\n5) perform the operation if the user has the correct permissions\r\n\r\nThe permissions logic is defined in the decorator methods of this new module:\r\nhttps://github.com/scitran/api/blob/refactoring/api/permchecker.py\r\n\r\nDecorators may be complex to wrap your head around them, but they allow to extract and isolate cleanly the permission logic.","created_at":"2015-09-17T21:13:47Z","updated_at":"2015-09-17T23:58:43Z","closed_at":null,"merged_at":null,"merge_commit_sha":"5cfec298ff98465a72390bc1c8a814644b0c0565","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/scitran/api/pulls/47/commits","review_comments_url":"https://api.github.com/repos/scitran/api/pulls/47/comments","review_comment_url":"https://api.github.com/repos/scitran/api/pulls/comments{/number}","comments_url":"https://api.github.com/repos/scitran/api/issues/47/comments","statuses_url":"https://api.github.com/repos/scitran/api/statuses/70fa8ba108d75c8cc57771c8d5bc47553e675366","head":{"label":"scitran:refactoring","ref":"refactoring","sha":"70fa8ba108d75c8cc57771c8d5bc47553e675366","user":{"login":"scitran","id":7379762,"avatar_url":"https://avatars.githubusercontent.com/u/7379762?v=3","gravatar_id":"","url":"https://api.github.com/users/scitran","html_url":"https://github.com/scitran","followers_url":"https://api.github.com/users/scitran/followers","following_url":"https://api.github.com/users/scitran/following{/other_user}","gists_url":"https://api.github.com/users/scitran/gists{/gist_id}","starred_url":"https://api.github.com/users/scitran/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/scitran/subscriptions","organizations_url":"https://api.github.com/users/scitran/orgs","repos_url":"https://api.github.com/users/scitran/repos","events_url":"https://api.github.com/users/scitran/events{/privacy}","received_events_url":"https://api.github.com/users/scitran/received_events","type":"Organization","site_admin":false},"repo":{"id":16557832,"name":"api","full_name":"scitran/api","owner":{"login":"scitran","id":7379762,"avatar_url":"https://avatars.githubusercontent.com/u/7379762?v=3","gravatar_id":"","url":"https://api.github.com/users/scitran","html_url":"https://github.com/scitran","followers_url":"https://api.github.com/users/scitran/followers","following_url":"https://api.github.com/users/scitran/following{/other_user}","gists_url":"https://api.github.com/users/scitran/gists{/gist_id}","starred_url":"https://api.github.com/users/scitran/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/scitran/subscriptions","organizations_url":"https://api.github.com/users/scitran/orgs","repos_url":"https://api.github.com/users/scitran/repos","events_url":"https://api.github.com/users/scitran/events{/privacy}","received_events_url":"https://api.github.com/users/scitran/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/scitran/api","description":"RESTful API","fork":false,"url":"https://api.github.com/repos/scitran/api","forks_url":"https://api.github.com/repos/scitran/api/forks","keys_url":"https://api.github.com/repos/scitran/api/keys{/key_id}","collaborators_url":"https://api.github.com/repos/scitran/api/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/scitran/api/teams","hooks_url":"https://api.github.com/repos/scitran/api/hooks","issue_events_url":"https://api.github.com/repos/scitran/api/issues/events{/number}","events_url":"https://api.github.com/repos/scitran/api/events","assignees_url":"https://api.github.com/repos/scitran/api/assignees{/user}","branches_url":"https://api.github.com/repos/scitran/api/branches{/branch}","tags_url":"https://api.github.com/repos/scitran/api/tags","blobs_url":"https://api.github.com/repos/scitran/api/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/scitran/api/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/scitran/api/git/refs{/sha}","trees_url":"https://api.github.com/repos/scitran/api/git/trees{/sha}","statuses_url":"https://api.github.com/repos/scitran/api/statuses/{sha}","languages_url":"https://api.github.com/repos/scitran/api/languages","stargazers_url":"https://api.github.com/repos/scitran/api/stargazers","contributors_url":"https://api.github.com/repos/scitran/api/contributors","subscribers_url":"https://api.github.com/repos/scitran/api/subscribers","subscription_url":"https://api.github.com/repos/scitran/api/subscription","commits_url":"https://api.github.com/repos/scitran/api/commits{/sha}","git_commits_url":"https://api.github.com/repos/scitran/api/git/commits{/sha}","comments_url":"https://api.github.com/repos/scitran/api/comments{/number}","issue_comment_url":"https://api.github.com/repos/scitran/api/issues/comments{/number}","contents_url":"https://api.github.com/repos/scitran/api/contents/{+path}","compare_url":"https://api.github.com/repos/scitran/api/compare/{base}...{head}","merges_url":"https://api.github.com/repos/scitran/api/merges","archive_url":"https://api.github.com/repos/scitran/api/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/scitran/api/downloads","issues_url":"https://api.github.com/repos/scitran/api/issues{/number}","pulls_url":"https://api.github.com/repos/scitran/api/pulls{/number}","milestones_url":"https://api.github.com/repos/scitran/api/milestones{/number}","notifications_url":"https://api.github.com/repos/scitran/api/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/scitran/api/labels{/name}","releases_url":"https://api.github.com/repos/scitran/api/releases{/id}","created_at":"2014-02-05T20:48:50Z","updated_at":"2015-07-25T01:00:37Z","pushed_at":"2015-09-17T21:27:53Z","git_url":"git://github.com/scitran/api.git","ssh_url":"git@github.com:scitran/api.git","clone_url":"https://github.com/scitran/api.git","svn_url":"https://github.com/scitran/api","homepage":"","size":1674,"stargazers_count":3,"watchers_count":3,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":3,"mirror_url":null,"open_issues_count":4,"forks":3,"open_issues":4,"watchers":3,"default_branch":"master"}},"base":{"label":"scitran:master","ref":"master","sha":"5b02572d351fcf3db688d0be22d27d21eee6345e","user":{"login":"scitran","id":7379762,"avatar_url":"https://avatars.githubusercontent.com/u/7379762?v=3","gravatar_id":"","url":"https://api.github.com/users/scitran","html_url":"https://github.com/scitran","followers_url":"https://api.github.com/users/scitran/followers","following_url":"https://api.github.com/users/scitran/following{/other_user}","gists_url":"https://api.github.com/users/scitran/gists{/gist_id}","starred_url":"https://api.github.com/users/scitran/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/scitran/subscriptions","organizations_url":"https://api.github.com/users/scitran/orgs","repos_url":"https://api.github.com/users/scitran/repos","events_url":"https://api.github.com/users/scitran/events{/privacy}","received_events_url":"https://api.github.com/users/scitran/received_events","type":"Organization","site_admin":false},"repo":{"id":16557832,"name":"api","full_name":"scitran/api","owner":{"login":"scitran","id":7379762,"avatar_url":"https://avatars.githubusercontent.com/u/7379762?v=3","gravatar_id":"","url":"https://api.github.com/users/scitran","html_url":"https://github.com/scitran","followers_url":"https://api.github.com/users/scitran/followers","following_url":"https://api.github.com/users/scitran/following{/other_user}","gists_url":"https://api.github.com/users/scitran/gists{/gist_id}","starred_url":"https://api.github.com/users/scitran/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/scitran/subscriptions","organizations_url":"https://api.github.com/users/scitran/orgs","repos_url":"https://api.github.com/users/scitran/repos","events_url":"https://api.github.com/users/scitran/events{/privacy}","received_events_url":"https://api.github.com/users/scitran/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/scitran/api","description":"RESTful API","fork":false,"url":"https://api.github.com/repos/scitran/api","forks_url":"https://api.github.com/repos/scitran/api/forks","keys_url":"https://api.github.com/repos/scitran/api/keys{/key_id}","collaborators_url":"https://api.github.com/repos/scitran/api/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/scitran/api/teams","hooks_url":"https://api.github.com/repos/scitran/api/hooks","issue_events_url":"https://api.github.com/repos/scitran/api/issues/events{/number}","events_url":"https://api.github.com/repos/scitran/api/events","assignees_url":"https://api.github.com/repos/scitran/api/assignees{/user}","branches_url":"https://api.github.com/repos/scitran/api/branches{/branch}","tags_url":"https://api.github.com/repos/scitran/api/tags","blobs_url":"https://api.github.com/repos/scitran/api/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/scitran/api/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/scitran/api/git/refs{/sha}","trees_url":"https://api.github.com/repos/scitran/api/git/trees{/sha}","statuses_url":"https://api.github.com/repos/scitran/api/statuses/{sha}","languages_url":"https://api.github.com/repos/scitran/api/languages","stargazers_url":"https://api.github.com/repos/scitran/api/stargazers","contributors_url":"https://api.github.com/repos/scitran/api/contributors","subscribers_url":"https://api.github.com/repos/scitran/api/subscribers","subscription_url":"https://api.github.com/repos/scitran/api/subscription","commits_url":"https://api.github.com/repos/scitran/api/commits{/sha}","git_commits_url":"https://api.github.com/repos/scitran/api/git/commits{/sha}","comments_url":"https://api.github.com/repos/scitran/api/comments{/number}","issue_comment_url":"https://api.github.com/repos/scitran/api/issues/comments{/number}","contents_url":"https://api.github.com/repos/scitran/api/contents/{+path}","compare_url":"https://api.github.com/repos/scitran/api/compare/{base}...{head}","merges_url":"https://api.github.com/repos/scitran/api/merges","archive_url":"https://api.github.com/repos/scitran/api/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/scitran/api/downloads","issues_url":"https://api.github.com/repos/scitran/api/issues{/number}","pulls_url":"https://api.github.com/repos/scitran/api/pulls{/number}","milestones_url":"https://api.github.com/repos/scitran/api/milestones{/number}","notifications_url":"https://api.github.com/repos/scitran/api/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/scitran/api/labels{/name}","releases_url":"https://api.github.com/repos/scitran/api/releases{/id}","created_at":"2014-02-05T20:48:50Z","updated_at":"2015-07-25T01:00:37Z","pushed_at":"2015-09-17T21:27:53Z","git_url":"git://github.com/scitran/api.git","ssh_url":"git@github.com:scitran/api.git","clone_url":"https://github.com/scitran/api.git","svn_url":"https://github.com/scitran/api","homepage":"","size":1674,"stargazers_count":3,"watchers_count":3,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":3,"mirror_url":null,"open_issues_count":4,"forks":3,"open_issues":4,"watchers":3,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/scitran/api/pulls/47"},"html":{"href":"https://github.com/scitran/api/pull/47"},"issue":{"href":"https://api.github.com/repos/scitran/api/issues/47"},"comments":{"href":"https://api.github.com/repos/scitran/api/issues/47/comments"},"review_comments":{"href":"https://api.github.com/repos/scitran/api/pulls/47/comments"},"review_comment":{"href":"https://api.github.com/repos/scitran/api/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/scitran/api/pulls/47/commits"},"statuses":{"href":"https://api.github.com/repos/scitran/api/statuses/70fa8ba108d75c8cc57771c8d5bc47553e675366"}}}}