)]}'
{"/COMMIT_MSG":[{"author":{"_account_id":6598,"name":"Berezovsky Irena","email":"irenab.dev@gmail.com","username":"irenab"},"change_message_id":"8d08f62566ccb76fea7940e470152f349d409b1e","unresolved":false,"context_lines":[{"line_number":5,"context_line":"CommitDate: 2016-12-06 17:42:41 +0200"},{"line_number":6,"context_line":""},{"line_number":7,"context_line":"[WIP] Add CRUD class to API NB"},{"line_number":8,"context_line":""},{"line_number":9,"context_line":"Change-Id: I1ff25f66724f9ad69645570fb20704ecd99cebbe"},{"line_number":10,"context_line":"Signed-off-by: Dima Kuznetsov \u003cdima.kuznetsov@toganetworks.com\u003e"}],"source_content_type":"text/x-gerrit-commit-message","patch_set":5,"id":"3a71b18c_e88dad31","line":8,"updated":"2016-12-07 06:01:36.000000000","message":"please link to launchpad bp/bug","commit_id":"95a8eeb16b3ec1b12469877e73b57d3682d0c4e7"},{"author":{"_account_id":23766,"name":"Dima Kuznetsov","email":"dima.kuznetsov@toganetworks.com","username":"dimak"},"change_message_id":"ed03654b0f85c5b22ab7b2fe5c1f0cf4fa92218d","unresolved":false,"context_lines":[{"line_number":5,"context_line":"CommitDate: 2016-12-06 17:42:41 +0200"},{"line_number":6,"context_line":""},{"line_number":7,"context_line":"[WIP] Add CRUD class to API NB"},{"line_number":8,"context_line":""},{"line_number":9,"context_line":"Change-Id: I1ff25f66724f9ad69645570fb20704ecd99cebbe"},{"line_number":10,"context_line":"Signed-off-by: Dima Kuznetsov \u003cdima.kuznetsov@toganetworks.com\u003e"}],"source_content_type":"text/x-gerrit-commit-message","patch_set":5,"id":"3a71b18c_04eff7b0","line":8,"in_reply_to":"3a71b18c_e88dad31","updated":"2016-12-08 09:24:18.000000000","message":"I created a BP, will add it to all commits in the next PS","commit_id":"95a8eeb16b3ec1b12469877e73b57d3682d0c4e7"}],"dragonflow/db/api_nb.py":[{"author":{"_account_id":20229,"name":"Omer Anson","email":"omer.anson@toganetworks.com","username":"oanson"},"change_message_id":"fa25544182c5b9c717da7d039388ccebea6a2f46","unresolved":false,"context_lines":[{"line_number":873,"context_line":"            obj.update(columns)"},{"line_number":874,"context_line":"            return jsonutils.dumps(obj)"},{"line_number":875,"context_line":""},{"line_number":876,"context_line":"        def create(self, id, topic, notify\u003dTrue, **columns):"},{"line_number":877,"context_line":"            # Allocate unique key if our class needs one"},{"line_number":878,"context_line":"            if issubclass(self.model, db_models.NbDbObjectWithUniqueKey):"},{"line_number":879,"context_line":"                unique_key \u003d self.api_nb.driver.allocate_unique_key("}],"source_content_type":"text/x-python","patch_set":4,"id":"3a71b18c_adcf6354","line":876,"range":{"start_line":876,"start_character":36,"end_line":876,"end_character":47},"updated":"2016-12-06 15:52:32.000000000","message":"It\u0027s expected that notify will be on a per-call basis, rather than a per-object basis?","commit_id":"5e5b0c9b40a4dc06a1e713e22e22f6cbc5d2a019"},{"author":{"_account_id":23766,"name":"Dima Kuznetsov","email":"dima.kuznetsov@toganetworks.com","username":"dimak"},"change_message_id":"40c7dd205c678e26603d7cc38535aed791f0d4e5","unresolved":false,"context_lines":[{"line_number":873,"context_line":"            obj.update(columns)"},{"line_number":874,"context_line":"            return jsonutils.dumps(obj)"},{"line_number":875,"context_line":""},{"line_number":876,"context_line":"        def create(self, id, topic, notify\u003dTrue, **columns):"},{"line_number":877,"context_line":"            # Allocate unique key if our class needs one"},{"line_number":878,"context_line":"            if issubclass(self.model, db_models.NbDbObjectWithUniqueKey):"},{"line_number":879,"context_line":"                unique_key \u003d self.api_nb.driver.allocate_unique_key("}],"source_content_type":"text/x-python","patch_set":4,"id":"3a71b18c_2ae4bd0b","line":876,"range":{"start_line":876,"start_character":36,"end_line":876,"end_character":47},"in_reply_to":"3a71b18c_adcf6354","updated":"2016-12-08 09:40:02.000000000","message":"I needed this only for floatingip which notifies selectively, I think having it per call does not have any negative implications at the moment.","commit_id":"5e5b0c9b40a4dc06a1e713e22e22f6cbc5d2a019"},{"author":{"_account_id":20229,"name":"Omer Anson","email":"omer.anson@toganetworks.com","username":"oanson"},"change_message_id":"fa25544182c5b9c717da7d039388ccebea6a2f46","unresolved":false,"context_lines":[{"line_number":875,"context_line":""},{"line_number":876,"context_line":"        def create(self, id, topic, notify\u003dTrue, **columns):"},{"line_number":877,"context_line":"            # Allocate unique key if our class needs one"},{"line_number":878,"context_line":"            if issubclass(self.model, db_models.NbDbObjectWithUniqueKey):"},{"line_number":879,"context_line":"                unique_key \u003d self.api_nb.driver.allocate_unique_key("},{"line_number":880,"context_line":"                    self.table_name)"},{"line_number":881,"context_line":"                columns[db_models.UNIQUE_KEY] \u003d unique_key"}],"source_content_type":"text/x-python","patch_set":4,"id":"3a71b18c_0d8bf71f","line":878,"range":{"start_line":878,"start_character":0,"end_line":878,"end_character":73},"updated":"2016-12-06 15:52:32.000000000","message":"Consider making this a subclass? e.g. _UIDCRUDHelper?","commit_id":"5e5b0c9b40a4dc06a1e713e22e22f6cbc5d2a019"},{"author":{"_account_id":23766,"name":"Dima Kuznetsov","email":"dima.kuznetsov@toganetworks.com","username":"dimak"},"change_message_id":"40c7dd205c678e26603d7cc38535aed791f0d4e5","unresolved":false,"context_lines":[{"line_number":875,"context_line":""},{"line_number":876,"context_line":"        def create(self, id, topic, notify\u003dTrue, **columns):"},{"line_number":877,"context_line":"            # Allocate unique key if our class needs one"},{"line_number":878,"context_line":"            if issubclass(self.model, db_models.NbDbObjectWithUniqueKey):"},{"line_number":879,"context_line":"                unique_key \u003d self.api_nb.driver.allocate_unique_key("},{"line_number":880,"context_line":"                    self.table_name)"},{"line_number":881,"context_line":"                columns[db_models.UNIQUE_KEY] \u003d unique_key"}],"source_content_type":"text/x-python","patch_set":4,"id":"3a71b18c_aa1c4d78","line":878,"range":{"start_line":878,"start_character":0,"end_line":878,"end_character":73},"in_reply_to":"3a71b18c_0d8bf71f","updated":"2016-12-08 09:40:02.000000000","message":"Its issubclass check vs another class in the hierarchy. Not sure which I dislike more.","commit_id":"5e5b0c9b40a4dc06a1e713e22e22f6cbc5d2a019"},{"author":{"_account_id":20229,"name":"Omer Anson","email":"omer.anson@toganetworks.com","username":"oanson"},"change_message_id":"fa25544182c5b9c717da7d039388ccebea6a2f46","unresolved":false,"context_lines":[{"line_number":908,"context_line":"                LOG.warning("},{"line_number":909,"context_line":"                    _LW(\u0027Could not find object %(id)s to delete in %(table)s\u0027),"},{"line_number":910,"context_line":"                    id\u003did, table\u003dself.table_name)"},{"line_number":911,"context_line":"                raise"},{"line_number":912,"context_line":"            finally:"},{"line_number":913,"context_line":"                self.api_nb._send_db_change_event(self.table_name, id,"},{"line_number":914,"context_line":"                                                  \u0027delete\u0027, id, topic)"}],"source_content_type":"text/x-python","patch_set":4,"id":"3a71b18c_6d370b4d","line":911,"range":{"start_line":911,"start_character":0,"end_line":911,"end_character":21},"updated":"2016-12-06 15:52:32.000000000","message":"I think this is unnecessary in case of delete.","commit_id":"5e5b0c9b40a4dc06a1e713e22e22f6cbc5d2a019"},{"author":{"_account_id":23766,"name":"Dima Kuznetsov","email":"dima.kuznetsov@toganetworks.com","username":"dimak"},"change_message_id":"40c7dd205c678e26603d7cc38535aed791f0d4e5","unresolved":false,"context_lines":[{"line_number":908,"context_line":"                LOG.warning("},{"line_number":909,"context_line":"                    _LW(\u0027Could not find object %(id)s to delete in %(table)s\u0027),"},{"line_number":910,"context_line":"                    id\u003did, table\u003dself.table_name)"},{"line_number":911,"context_line":"                raise"},{"line_number":912,"context_line":"            finally:"},{"line_number":913,"context_line":"                self.api_nb._send_db_change_event(self.table_name, id,"},{"line_number":914,"context_line":"                                                  \u0027delete\u0027, id, topic)"}],"source_content_type":"text/x-python","patch_set":4,"id":"3a71b18c_0a6d4132","line":911,"range":{"start_line":911,"start_character":0,"end_line":911,"end_character":21},"in_reply_to":"3a71b18c_6d370b4d","updated":"2016-12-08 09:40:02.000000000","message":"All the delete_* methods did not handle key not found exceptions that rise from driver.delete_key, so I preferred to stick to the same behavior.","commit_id":"5e5b0c9b40a4dc06a1e713e22e22f6cbc5d2a019"},{"author":{"_account_id":20229,"name":"Omer Anson","email":"omer.anson@toganetworks.com","username":"oanson"},"change_message_id":"fa25544182c5b9c717da7d039388ccebea6a2f46","unresolved":false,"context_lines":[{"line_number":918,"context_line":"                return self.api_nb.driver.get_key(self.table_name, id, topic)"},{"line_number":919,"context_line":"            except Exception:"},{"line_number":920,"context_line":"                LOG.exception("},{"line_number":921,"context_line":"                    _LE(\u0027Could not get object {1} from table {0}\u0027).format("},{"line_number":922,"context_line":"                        self.table_name, id))"},{"line_number":923,"context_line":"                return None"},{"line_number":924,"context_line":""},{"line_number":925,"context_line":"        def get(self, id, topic\u003dNone):"}],"source_content_type":"text/x-python","patch_set":4,"id":"3a71b18c_0d7cd7ea","line":922,"range":{"start_line":921,"start_character":0,"end_line":922,"end_character":45},"updated":"2016-12-06 15:52:32.000000000","message":"In logs, I think %s and a comma is better, to let the logging framework take care of it.","commit_id":"5e5b0c9b40a4dc06a1e713e22e22f6cbc5d2a019"},{"author":{"_account_id":23766,"name":"Dima Kuznetsov","email":"dima.kuznetsov@toganetworks.com","username":"dimak"},"change_message_id":"40c7dd205c678e26603d7cc38535aed791f0d4e5","unresolved":false,"context_lines":[{"line_number":918,"context_line":"                return self.api_nb.driver.get_key(self.table_name, id, topic)"},{"line_number":919,"context_line":"            except Exception:"},{"line_number":920,"context_line":"                LOG.exception("},{"line_number":921,"context_line":"                    _LE(\u0027Could not get object {1} from table {0}\u0027).format("},{"line_number":922,"context_line":"                        self.table_name, id))"},{"line_number":923,"context_line":"                return None"},{"line_number":924,"context_line":""},{"line_number":925,"context_line":"        def get(self, id, topic\u003dNone):"}],"source_content_type":"text/x-python","patch_set":4,"id":"3a71b18c_8a5f9174","line":922,"range":{"start_line":921,"start_character":0,"end_line":922,"end_character":45},"in_reply_to":"3a71b18c_0d7cd7ea","updated":"2016-12-08 09:40:02.000000000","message":"Agree, will change.","commit_id":"5e5b0c9b40a4dc06a1e713e22e22f6cbc5d2a019"},{"author":{"_account_id":20229,"name":"Omer Anson","email":"omer.anson@toganetworks.com","username":"oanson"},"change_message_id":"fa25544182c5b9c717da7d039388ccebea6a2f46","unresolved":false,"context_lines":[{"line_number":930,"context_line":"            res \u003d self.api_nb.driver.get_all_entries(self.table_name, topic)"},{"line_number":931,"context_line":"            return [self.model(v) for v in res]"},{"line_number":932,"context_line":""},{"line_number":933,"context_line":"        def _add_element(self, id, topic, new_version, elem_name, nested_obj):"},{"line_number":934,"context_line":"            obj \u003d self._get_raw(id, topic)"},{"line_number":935,"context_line":"            obj[\u0027version\u0027] \u003d new_version"},{"line_number":936,"context_line":"            obj.setdefault(elem_name, []).append(nested_obj)"}],"source_content_type":"text/x-python","patch_set":4,"id":"3a71b18c_2d66f350","line":933,"updated":"2016-12-06 15:52:32.000000000","message":"Unused private methods. If it is for subclassing, maybe make it public?","commit_id":"5e5b0c9b40a4dc06a1e713e22e22f6cbc5d2a019"},{"author":{"_account_id":23766,"name":"Dima Kuznetsov","email":"dima.kuznetsov@toganetworks.com","username":"dimak"},"change_message_id":"40c7dd205c678e26603d7cc38535aed791f0d4e5","unresolved":false,"context_lines":[{"line_number":930,"context_line":"            res \u003d self.api_nb.driver.get_all_entries(self.table_name, topic)"},{"line_number":931,"context_line":"            return [self.model(v) for v in res]"},{"line_number":932,"context_line":""},{"line_number":933,"context_line":"        def _add_element(self, id, topic, new_version, elem_name, nested_obj):"},{"line_number":934,"context_line":"            obj \u003d self._get_raw(id, topic)"},{"line_number":935,"context_line":"            obj[\u0027version\u0027] \u003d new_version"},{"line_number":936,"context_line":"            obj.setdefault(elem_name, []).append(nested_obj)"}],"source_content_type":"text/x-python","patch_set":4,"id":"3a71b18c_aaf28d6d","line":933,"in_reply_to":"3a71b18c_2d66f350","updated":"2016-12-08 09:40:02.000000000","message":"They are used by deriving classes in subsequent patches, I think they should stay _internal rather than public.","commit_id":"5e5b0c9b40a4dc06a1e713e22e22f6cbc5d2a019"},{"author":{"_account_id":20229,"name":"Omer Anson","email":"omer.anson@toganetworks.com","username":"oanson"},"change_message_id":"fa25544182c5b9c717da7d039388ccebea6a2f46","unresolved":false,"context_lines":[{"line_number":932,"context_line":""},{"line_number":933,"context_line":"        def _add_element(self, id, topic, new_version, elem_name, nested_obj):"},{"line_number":934,"context_line":"            obj \u003d self._get_raw(id, topic)"},{"line_number":935,"context_line":"            obj[\u0027version\u0027] \u003d new_version"},{"line_number":936,"context_line":"            obj.setdefault(elem_name, []).append(nested_obj)"},{"line_number":937,"context_line":"            self.update(id, topic, **obj)"},{"line_number":938,"context_line":""}],"source_content_type":"text/x-python","patch_set":4,"id":"3a71b18c_0df1376b","line":935,"updated":"2016-12-06 15:52:32.000000000","message":"_get_raw returns a json string. maybe you want get? or pass it through jsonutils.loads? (Appears in other methods too)","commit_id":"5e5b0c9b40a4dc06a1e713e22e22f6cbc5d2a019"},{"author":{"_account_id":23766,"name":"Dima Kuznetsov","email":"dima.kuznetsov@toganetworks.com","username":"dimak"},"change_message_id":"40c7dd205c678e26603d7cc38535aed791f0d4e5","unresolved":false,"context_lines":[{"line_number":932,"context_line":""},{"line_number":933,"context_line":"        def _add_element(self, id, topic, new_version, elem_name, nested_obj):"},{"line_number":934,"context_line":"            obj \u003d self._get_raw(id, topic)"},{"line_number":935,"context_line":"            obj[\u0027version\u0027] \u003d new_version"},{"line_number":936,"context_line":"            obj.setdefault(elem_name, []).append(nested_obj)"},{"line_number":937,"context_line":"            self.update(id, topic, **obj)"},{"line_number":938,"context_line":""}],"source_content_type":"text/x-python","patch_set":4,"id":"3a71b18c_4a4e59c2","line":935,"in_reply_to":"3a71b18c_0df1376b","updated":"2016-12-08 09:40:02.000000000","message":"Fixed it :)","commit_id":"5e5b0c9b40a4dc06a1e713e22e22f6cbc5d2a019"},{"author":{"_account_id":11159,"name":"Hong Hui Xiao","email":"honghui_xiao@yeah.net","username":"HongHuiXiao"},"change_message_id":"282df028c00705e16037f917e110aaa3d78191d8","unresolved":false,"context_lines":[{"line_number":75,"context_line":"    def initialize(self, db_ip\u003d\u0027127.0.0.1\u0027, db_port\u003d4001):"},{"line_number":76,"context_line":"        self.driver.initialize(db_ip, db_port, config\u003dcfg.CONF.df)"},{"line_number":77,"context_line":""},{"line_number":78,"context_line":"        self.chassis \u003d self._CRUDHelper(self, db_models.Chassis)"},{"line_number":79,"context_line":"        self.lport \u003d self._CRUDHelper(self, db_models.LogicalPort)"},{"line_number":80,"context_line":"        self.lswitch \u003d self._CRUDHelper(self, db_models.LogicalSwitch)"},{"line_number":81,"context_line":"        self.lrouter \u003d self._CRUDHelper(self, db_models.LogicalRouter)"},{"line_number":82,"context_line":"        self.security_group \u003d self._CRUDHelper(self, db_models.SecurityGroup)"},{"line_number":83,"context_line":"        self.floatingip \u003d self._CRUDHelper(self, db_models.Floatingip)"},{"line_number":84,"context_line":"        self.publisher \u003d self._CRUDHelper(self, db_models.Publisher)"},{"line_number":85,"context_line":"        self.qos_policy \u003d self._CRUDHelper(self, db_models.QosPolicy)"},{"line_number":86,"context_line":""},{"line_number":87,"context_line":"        if self.use_pubsub:"},{"line_number":88,"context_line":"            self.pubsub \u003d PubSub("}],"source_content_type":"text/x-python","patch_set":5,"id":"3a71b18c_586d2831","line":85,"range":{"start_line":78,"start_character":0,"end_line":85,"end_character":69},"updated":"2016-12-09 02:27:43.000000000","message":"This is actually key-value pairs in api_nb. And we are expecting more resources to be added. So I think, can we change this part to a register way? So we don\u0027t need to maintain this part.","commit_id":"95a8eeb16b3ec1b12469877e73b57d3682d0c4e7"},{"author":{"_account_id":11159,"name":"Hong Hui Xiao","email":"honghui_xiao@yeah.net","username":"HongHuiXiao"},"change_message_id":"282df028c00705e16037f917e110aaa3d78191d8","unresolved":false,"context_lines":[{"line_number":927,"context_line":"                return self.model(value)"},{"line_number":928,"context_line":""},{"line_number":929,"context_line":"        def _get_dict(self, id, topic\u003dNone):"},{"line_number":930,"context_line":"            return jsonutils.loads(self._get_raw(id, topic))"},{"line_number":931,"context_line":""},{"line_number":932,"context_line":"        def get_all(self, topic\u003dNone):"},{"line_number":933,"context_line":"            res \u003d self.api_nb.driver.get_all_entries(self.table_name, topic)"}],"source_content_type":"text/x-python","patch_set":5,"id":"3a71b18c_f82ef440","line":930,"range":{"start_line":930,"start_character":0,"end_line":930,"end_character":60},"updated":"2016-12-09 02:27:43.000000000","message":"I would suggest we raise exception here when self._get_raw returns None. We often get such exception said \"exception with json load None\". It would be better to tell which resource with which id can\u0027t be found in nb db.","commit_id":"95a8eeb16b3ec1b12469877e73b57d3682d0c4e7"},{"author":{"_account_id":23766,"name":"Dima Kuznetsov","email":"dima.kuznetsov@toganetworks.com","username":"dimak"},"change_message_id":"8aa917554c18d8e1a643ba20c14534c88827d3f0","unresolved":false,"context_lines":[{"line_number":927,"context_line":"                return self.model(value)"},{"line_number":928,"context_line":""},{"line_number":929,"context_line":"        def _get_dict(self, id, topic\u003dNone):"},{"line_number":930,"context_line":"            return jsonutils.loads(self._get_raw(id, topic))"},{"line_number":931,"context_line":""},{"line_number":932,"context_line":"        def get_all(self, topic\u003dNone):"},{"line_number":933,"context_line":"            res \u003d self.api_nb.driver.get_all_entries(self.table_name, topic)"}],"source_content_type":"text/x-python","patch_set":5,"id":"3a71b18c_1df7e28b","line":930,"range":{"start_line":930,"start_character":0,"end_line":930,"end_character":60},"in_reply_to":"3a71b18c_f82ef440","updated":"2016-12-11 11:18:33.000000000","message":"I agree","commit_id":"95a8eeb16b3ec1b12469877e73b57d3682d0c4e7"},{"author":{"_account_id":11159,"name":"Hong Hui Xiao","email":"honghui_xiao@yeah.net","username":"HongHuiXiao"},"change_message_id":"282df028c00705e16037f917e110aaa3d78191d8","unresolved":false,"context_lines":[{"line_number":943,"context_line":"                            elem_id, update_dict):"},{"line_number":944,"context_line":"            obj \u003d self._get_dict(id, topic)"},{"line_number":945,"context_line":"            obj[\u0027version\u0027] \u003d new_version"},{"line_number":946,"context_line":"            for elem in obj.setdefault(elem_name, []):"},{"line_number":947,"context_line":"                if elem[\u0027id\u0027] \u003d\u003d elem_id:"},{"line_number":948,"context_line":"                    elem.update(update_dict)"},{"line_number":949,"context_line":"                    break"}],"source_content_type":"text/x-python","patch_set":5,"id":"3a71b18c_389cec5c","line":946,"range":{"start_line":946,"start_character":0,"end_line":946,"end_character":54},"updated":"2016-12-09 02:27:43.000000000","message":"If the object don\u0027t have elem_name before, this will create an empty list in the object with key elem_name. Is that an intentional behavior? If not, why not just return if the object doesn\u0027t have elem_name?","commit_id":"95a8eeb16b3ec1b12469877e73b57d3682d0c4e7"},{"author":{"_account_id":23766,"name":"Dima Kuznetsov","email":"dima.kuznetsov@toganetworks.com","username":"dimak"},"change_message_id":"8aa917554c18d8e1a643ba20c14534c88827d3f0","unresolved":false,"context_lines":[{"line_number":943,"context_line":"                            elem_id, update_dict):"},{"line_number":944,"context_line":"            obj \u003d self._get_dict(id, topic)"},{"line_number":945,"context_line":"            obj[\u0027version\u0027] \u003d new_version"},{"line_number":946,"context_line":"            for elem in obj.setdefault(elem_name, []):"},{"line_number":947,"context_line":"                if elem[\u0027id\u0027] \u003d\u003d elem_id:"},{"line_number":948,"context_line":"                    elem.update(update_dict)"},{"line_number":949,"context_line":"                    break"}],"source_content_type":"text/x-python","patch_set":5,"id":"3a71b18c_9d1852d5","line":946,"range":{"start_line":946,"start_character":0,"end_line":946,"end_character":54},"in_reply_to":"3a71b18c_389cec5c","updated":"2016-12-11 11:18:33.000000000","message":"I agree.\n\nI am not sure though how to handle it correctly. If the nested object is missing, I wouldn\u0027t expect the call to succeed, but what about updating a version? It would get out of sync with neutron revision.\n\nIt really doesn\u0027t make much sense to update the db at all if we\u0027re not doing any change.","commit_id":"95a8eeb16b3ec1b12469877e73b57d3682d0c4e7"},{"author":{"_account_id":11159,"name":"Hong Hui Xiao","email":"honghui_xiao@yeah.net","username":"HongHuiXiao"},"change_message_id":"282df028c00705e16037f917e110aaa3d78191d8","unresolved":false,"context_lines":[{"line_number":953,"context_line":"                            elem_name, nested_id):"},{"line_number":954,"context_line":"            obj \u003d self._get_dict(id, topic)"},{"line_number":955,"context_line":"            obj[\u0027version\u0027] \u003d new_version"},{"line_number":956,"context_line":"            obj[elem_name] \u003d ["},{"line_number":957,"context_line":"                e for e in obj.setdefault(elem_name, [])"},{"line_number":958,"context_line":"                if e[\u0027id\u0027] !\u003d nested_id"},{"line_number":959,"context_line":"            ]"},{"line_number":960,"context_line":"            self.update(**obj)"}],"source_content_type":"text/x-python","patch_set":5,"id":"3a71b18c_98ad008b","line":959,"range":{"start_line":956,"start_character":0,"end_line":959,"end_character":13},"updated":"2016-12-09 02:27:43.000000000","message":"Same comment above.","commit_id":"95a8eeb16b3ec1b12469877e73b57d3682d0c4e7"},{"author":{"_account_id":11159,"name":"Hong Hui Xiao","email":"honghui_xiao@yeah.net","username":"HongHuiXiao"},"change_message_id":"128e182d537f6233f35eb8fee65ed7995bdbff6b","unresolved":false,"context_lines":[{"line_number":75,"context_line":"    def initialize(self, db_ip\u003d\u0027127.0.0.1\u0027, db_port\u003d4001):"},{"line_number":76,"context_line":"        self.driver.initialize(db_ip, db_port, config\u003dcfg.CONF.df)"},{"line_number":77,"context_line":""},{"line_number":78,"context_line":"        self.chassis \u003d self._CRUDHelper(self, db_models.Chassis)"},{"line_number":79,"context_line":"        self.lport \u003d self._CRUDHelper(self, db_models.LogicalPort)"},{"line_number":80,"context_line":"        self.lswitch \u003d self._CRUDHelper(self, db_models.LogicalSwitch)"},{"line_number":81,"context_line":"        self.lrouter \u003d self._CRUDHelper(self, db_models.LogicalRouter)"},{"line_number":82,"context_line":"        self.security_group \u003d self._CRUDHelper(self, db_models.SecurityGroup)"},{"line_number":83,"context_line":"        self.floatingip \u003d self._CRUDHelper(self, db_models.Floatingip)"},{"line_number":84,"context_line":"        self.publisher \u003d self._CRUDHelper(self, db_models.Publisher)"},{"line_number":85,"context_line":"        self.qos_policy \u003d self._CRUDHelper(self, db_models.QosPolicy)"},{"line_number":86,"context_line":""},{"line_number":87,"context_line":"        if self.use_pubsub:"},{"line_number":88,"context_line":"            self.pubsub \u003d PubSub("}],"source_content_type":"text/x-python","patch_set":10,"id":"3a71b18c_8bf20c99","line":85,"range":{"start_line":78,"start_character":0,"end_line":85,"end_character":69},"updated":"2016-12-09 02:58:30.000000000","message":"after checking other patches, it will be hard to maitain the code in register way, as each resource has its own crud helper. So, my previous comment may not be applied here.","commit_id":"ea6e13a0e2138f2d4f0d46afeb99f9f5bedcdd8d"},{"author":{"_account_id":11159,"name":"Hong Hui Xiao","email":"honghui_xiao@yeah.net","username":"HongHuiXiao"},"change_message_id":"d872af4ce2dd517cea0fb16c136e5afbfb73d964","unresolved":false,"context_lines":[{"line_number":75,"context_line":"    def initialize(self, db_ip\u003d\u0027127.0.0.1\u0027, db_port\u003d4001):"},{"line_number":76,"context_line":"        self.driver.initialize(db_ip, db_port, config\u003dcfg.CONF.df)"},{"line_number":77,"context_line":""},{"line_number":78,"context_line":"        self.chassis \u003d self._CRUDHelper(self, db_models.Chassis)"},{"line_number":79,"context_line":"        self.lport \u003d self._CRUDHelper(self, db_models.LogicalPort)"},{"line_number":80,"context_line":"        self.lswitch \u003d self._CRUDHelper(self, db_models.LogicalSwitch)"},{"line_number":81,"context_line":"        self.lrouter \u003d self._CRUDHelper(self, db_models.LogicalRouter)"},{"line_number":82,"context_line":"        self.security_group \u003d self._CRUDHelper(self, db_models.SecurityGroup)"},{"line_number":83,"context_line":"        self.floatingip \u003d self._CRUDHelper(self, db_models.Floatingip)"},{"line_number":84,"context_line":"        self.publisher \u003d self._CRUDHelper(self, db_models.Publisher)"},{"line_number":85,"context_line":"        self.qos_policy \u003d self._CRUDHelper(self, db_models.QosPolicy)"},{"line_number":86,"context_line":""},{"line_number":87,"context_line":"        if self.use_pubsub:"},{"line_number":88,"context_line":"            self.pubsub \u003d PubSub("}],"source_content_type":"text/x-python","patch_set":12,"id":"1a6eadb0_b123f904","line":85,"range":{"start_line":78,"start_character":0,"end_line":85,"end_character":69},"updated":"2016-12-13 02:24:39.000000000","message":"After checking other patches, I want to discuss this part again. First, I have to say, I don\u0027t like the way to organize code here.\n\n1) This is actually some hardcode in api_nb. hardcode is not something good, since we are doing the refactor work to uniform code.\n2) This will conflict with existing code and maybe some future code. The way you organize the code here is the reason why you need [1], right? I don\u0027t think it is good to change existing code if you don\u0027t have to.\n3) In future, we might want to move all the _CRUDHelper out of api_nb, so that the api_nb will be less decoupled with nb resources. The code here will be the pain if we want go with the decouple work. \n\n\nI would suggest using a \"resource_handlers\" here. It is a dictionary, and key is the table_name, value is the _CRUDHelper. So the problems above will also go.\n\n[1] https://review.openstack.org/#/c/407548/","commit_id":"9f6b5399d0631f30e723b5c873fd0cf18d1cbdaa"},{"author":{"_account_id":20229,"name":"Omer Anson","email":"omer.anson@toganetworks.com","username":"oanson"},"change_message_id":"3a9c901034b2ebe665c5246e9eb5a18aead2a739","unresolved":false,"context_lines":[{"line_number":75,"context_line":"    def initialize(self, db_ip\u003d\u0027127.0.0.1\u0027, db_port\u003d4001):"},{"line_number":76,"context_line":"        self.driver.initialize(db_ip, db_port, config\u003dcfg.CONF.df)"},{"line_number":77,"context_line":""},{"line_number":78,"context_line":"        self.chassis \u003d self._CRUDHelper(self, db_models.Chassis)"},{"line_number":79,"context_line":"        self.lport \u003d self._CRUDHelper(self, db_models.LogicalPort)"},{"line_number":80,"context_line":"        self.lswitch \u003d self._CRUDHelper(self, db_models.LogicalSwitch)"},{"line_number":81,"context_line":"        self.lrouter \u003d self._CRUDHelper(self, db_models.LogicalRouter)"},{"line_number":82,"context_line":"        self.security_group \u003d self._CRUDHelper(self, db_models.SecurityGroup)"},{"line_number":83,"context_line":"        self.floatingip \u003d self._CRUDHelper(self, db_models.Floatingip)"},{"line_number":84,"context_line":"        self.publisher \u003d self._CRUDHelper(self, db_models.Publisher)"},{"line_number":85,"context_line":"        self.qos_policy \u003d self._CRUDHelper(self, db_models.QosPolicy)"},{"line_number":86,"context_line":""},{"line_number":87,"context_line":"        if self.use_pubsub:"},{"line_number":88,"context_line":"            self.pubsub \u003d PubSub("}],"source_content_type":"text/x-python","patch_set":12,"id":"1a6eadb0_cca36f9d","line":85,"range":{"start_line":78,"start_character":0,"end_line":85,"end_character":69},"in_reply_to":"1a6eadb0_064c686f","updated":"2016-12-13 09:39:58.000000000","message":"I would like to add my 2 cents.\n\nWe change the code such that self.\u003ctable\u003e is a CRUD actions container. We only have to state that in the general case, we expecte create/read/update/delete to be implemented. _CRUDHelper can then be moved out, replaced. That doesn\u0027t sound like hard-coding or strong coupling. (Provided the CRUDHelper is mostly limited to these methods)\n\nIn the future, we may want to replace the call to _CRUDHelper with something more dynamic. Maybe have each model provide its CRUDHelper, or have each model register itself. But this will be a localised change here (or in the models code).\n\nIn general, I think this direction is all right. I am worried about the patch [1], which makes pubsub less elegant. But I am beginning to suspect that:\n1. It is the lesser of evils\n2. It stays in line with the rest of the API, i.e. nb_api.\u003ctopic/table\u003e.\u003caction\u003e.\n\nSeeing item 2., maybe the correct change in [1] is not to have publisher and subscriber as attributes on pubsub, but have methods in pubsub call the publisher or subscriber where relevant. e.g.\nself.nb_api.pubsub.publishers.end_event(...) -\u003e self.nb_api.pubsub.send_event(...)\n\n[1] https://review.openstack.org/#/c/407548/","commit_id":"9f6b5399d0631f30e723b5c873fd0cf18d1cbdaa"},{"author":{"_account_id":23766,"name":"Dima Kuznetsov","email":"dima.kuznetsov@toganetworks.com","username":"dimak"},"change_message_id":"8edefb02c8d6077ebcac38f6e695a53cbe2a8912","unresolved":false,"context_lines":[{"line_number":75,"context_line":"    def initialize(self, db_ip\u003d\u0027127.0.0.1\u0027, db_port\u003d4001):"},{"line_number":76,"context_line":"        self.driver.initialize(db_ip, db_port, config\u003dcfg.CONF.df)"},{"line_number":77,"context_line":""},{"line_number":78,"context_line":"        self.chassis \u003d self._CRUDHelper(self, db_models.Chassis)"},{"line_number":79,"context_line":"        self.lport \u003d self._CRUDHelper(self, db_models.LogicalPort)"},{"line_number":80,"context_line":"        self.lswitch \u003d self._CRUDHelper(self, db_models.LogicalSwitch)"},{"line_number":81,"context_line":"        self.lrouter \u003d self._CRUDHelper(self, db_models.LogicalRouter)"},{"line_number":82,"context_line":"        self.security_group \u003d self._CRUDHelper(self, db_models.SecurityGroup)"},{"line_number":83,"context_line":"        self.floatingip \u003d self._CRUDHelper(self, db_models.Floatingip)"},{"line_number":84,"context_line":"        self.publisher \u003d self._CRUDHelper(self, db_models.Publisher)"},{"line_number":85,"context_line":"        self.qos_policy \u003d self._CRUDHelper(self, db_models.QosPolicy)"},{"line_number":86,"context_line":""},{"line_number":87,"context_line":"        if self.use_pubsub:"},{"line_number":88,"context_line":"            self.pubsub \u003d PubSub("}],"source_content_type":"text/x-python","patch_set":12,"id":"1a6eadb0_064c686f","line":85,"range":{"start_line":78,"start_character":0,"end_line":85,"end_character":69},"in_reply_to":"1a6eadb0_b123f904","updated":"2016-12-13 09:06:17.000000000","message":"I think I\u0027ve posted it somewhere already but in case I didn\u0027t, I think having member for each model is convenient.\n\nMost of the access to nb models happens with a specific model in mind, e.g mech_driver wants to add an lport, it calls nb_api.create_lport or nb_api.lport.create.\n\nIf we add a lookup I think it would make the code less nice:\n\nnb_api.handler(models.Lport).create\n\nI suggest we add the latter in case we want to support access when we only have the model (e.g. a list of models we want to .get_all on every model).","commit_id":"9f6b5399d0631f30e723b5c873fd0cf18d1cbdaa"},{"author":{"_account_id":11159,"name":"Hong Hui Xiao","email":"honghui_xiao@yeah.net","username":"HongHuiXiao"},"change_message_id":"d872af4ce2dd517cea0fb16c136e5afbfb73d964","unresolved":false,"context_lines":[{"line_number":890,"context_line":"                                                  \u0027create\u0027, obj_json, topic)"},{"line_number":891,"context_line":""},{"line_number":892,"context_line":"        def update(self, id, topic, notify\u003dTrue, **columns):"},{"line_number":893,"context_line":"            original \u003d self._get_dict(id, topic)"},{"line_number":894,"context_line":""},{"line_number":895,"context_line":"            for key, value in six.iteritems(columns):"},{"line_number":896,"context_line":"                if value !\u003d const.ATTR_NOT_SPECIFIED:"}],"source_content_type":"text/x-python","patch_set":12,"id":"1a6eadb0_f131f1ed","line":893,"range":{"start_line":893,"start_character":12,"end_line":893,"end_character":48},"updated":"2016-12-13 02:24:39.000000000","message":"The exception I mentioned in patch 5(json loads None) is usually observed here, but it is not handled in this version. I think you can just throw exception at _get_raw if get_key returns None.","commit_id":"9f6b5399d0631f30e723b5c873fd0cf18d1cbdaa"},{"author":{"_account_id":23766,"name":"Dima Kuznetsov","email":"dima.kuznetsov@toganetworks.com","username":"dimak"},"change_message_id":"8edefb02c8d6077ebcac38f6e695a53cbe2a8912","unresolved":false,"context_lines":[{"line_number":890,"context_line":"                                                  \u0027create\u0027, obj_json, topic)"},{"line_number":891,"context_line":""},{"line_number":892,"context_line":"        def update(self, id, topic, notify\u003dTrue, **columns):"},{"line_number":893,"context_line":"            original \u003d self._get_dict(id, topic)"},{"line_number":894,"context_line":""},{"line_number":895,"context_line":"            for key, value in six.iteritems(columns):"},{"line_number":896,"context_line":"                if value !\u003d const.ATTR_NOT_SPECIFIED:"}],"source_content_type":"text/x-python","patch_set":12,"id":"1a6eadb0_c668b0bd","line":893,"range":{"start_line":893,"start_character":12,"end_line":893,"end_character":48},"in_reply_to":"1a6eadb0_f131f1ed","updated":"2016-12-13 09:06:17.000000000","message":"Will address","commit_id":"9f6b5399d0631f30e723b5c873fd0cf18d1cbdaa"},{"author":{"_account_id":11159,"name":"Hong Hui Xiao","email":"honghui_xiao@yeah.net","username":"HongHuiXiao"},"change_message_id":"d872af4ce2dd517cea0fb16c136e5afbfb73d964","unresolved":false,"context_lines":[{"line_number":927,"context_line":"                LOG.exception("},{"line_number":928,"context_line":"                    _LE(\u0027Could not get object %(id)s from table %(table)s\u0027),"},{"line_number":929,"context_line":"                    extra\u003d{\u0027id\u0027: id, \u0027table\u0027: self.table_name})"},{"line_number":930,"context_line":"                return None"},{"line_number":931,"context_line":""},{"line_number":932,"context_line":"        def _get_dict(self, id, topic\u003dNone):"},{"line_number":933,"context_line":"            return jsonutils.loads(self._get_raw(id, topic))"}],"source_content_type":"text/x-python","patch_set":12,"id":"1a6eadb0_516e25ca","line":930,"range":{"start_line":930,"start_character":0,"end_line":930,"end_character":27},"updated":"2016-12-13 02:24:39.000000000","message":"nit: this is redundant.","commit_id":"9f6b5399d0631f30e723b5c873fd0cf18d1cbdaa"},{"author":{"_account_id":23766,"name":"Dima Kuznetsov","email":"dima.kuznetsov@toganetworks.com","username":"dimak"},"change_message_id":"8edefb02c8d6077ebcac38f6e695a53cbe2a8912","unresolved":false,"context_lines":[{"line_number":927,"context_line":"                LOG.exception("},{"line_number":928,"context_line":"                    _LE(\u0027Could not get object %(id)s from table %(table)s\u0027),"},{"line_number":929,"context_line":"                    extra\u003d{\u0027id\u0027: id, \u0027table\u0027: self.table_name})"},{"line_number":930,"context_line":"                return None"},{"line_number":931,"context_line":""},{"line_number":932,"context_line":"        def _get_dict(self, id, topic\u003dNone):"},{"line_number":933,"context_line":"            return jsonutils.loads(self._get_raw(id, topic))"}],"source_content_type":"text/x-python","patch_set":12,"id":"1a6eadb0_4082c853","line":930,"range":{"start_line":930,"start_character":0,"end_line":930,"end_character":27},"in_reply_to":"1a6eadb0_516e25ca","updated":"2016-12-13 09:06:17.000000000","message":"I agree it is not needed but IMO this extra bit verbosity makes it much quicker to read and understand.","commit_id":"9f6b5399d0631f30e723b5c873fd0cf18d1cbdaa"},{"author":{"_account_id":11159,"name":"Hong Hui Xiao","email":"honghui_xiao@yeah.net","username":"HongHuiXiao"},"change_message_id":"d872af4ce2dd517cea0fb16c136e5afbfb73d964","unresolved":false,"context_lines":[{"line_number":946,"context_line":"                            elem_id, update_dict):"},{"line_number":947,"context_line":"            obj \u003d self._get_dict(id, topic)"},{"line_number":948,"context_line":"            obj[\u0027version\u0027] \u003d new_version"},{"line_number":949,"context_line":"            for elem in obj.setdefault(elem_name, []):"},{"line_number":950,"context_line":"                if elem[\u0027id\u0027] \u003d\u003d elem_id:"},{"line_number":951,"context_line":"                    elem.update(update_dict)"},{"line_number":952,"context_line":"                    break"}],"source_content_type":"text/x-python","patch_set":12,"id":"1a6eadb0_91dd1d73","line":949,"range":{"start_line":949,"start_character":24,"end_line":949,"end_character":53},"updated":"2016-12-13 02:24:39.000000000","message":"The concern I mentioned in PS5 still exists.","commit_id":"9f6b5399d0631f30e723b5c873fd0cf18d1cbdaa"},{"author":{"_account_id":23766,"name":"Dima Kuznetsov","email":"dima.kuznetsov@toganetworks.com","username":"dimak"},"change_message_id":"8edefb02c8d6077ebcac38f6e695a53cbe2a8912","unresolved":false,"context_lines":[{"line_number":946,"context_line":"                            elem_id, update_dict):"},{"line_number":947,"context_line":"            obj \u003d self._get_dict(id, topic)"},{"line_number":948,"context_line":"            obj[\u0027version\u0027] \u003d new_version"},{"line_number":949,"context_line":"            for elem in obj.setdefault(elem_name, []):"},{"line_number":950,"context_line":"                if elem[\u0027id\u0027] \u003d\u003d elem_id:"},{"line_number":951,"context_line":"                    elem.update(update_dict)"},{"line_number":952,"context_line":"                    break"}],"source_content_type":"text/x-python","patch_set":12,"id":"1a6eadb0_e6acccef","line":949,"range":{"start_line":949,"start_character":24,"end_line":949,"end_character":53},"in_reply_to":"1a6eadb0_91dd1d73","updated":"2016-12-13 09:06:17.000000000","message":"Will address","commit_id":"9f6b5399d0631f30e723b5c873fd0cf18d1cbdaa"},{"author":{"_account_id":11159,"name":"Hong Hui Xiao","email":"honghui_xiao@yeah.net","username":"HongHuiXiao"},"change_message_id":"d872af4ce2dd517cea0fb16c136e5afbfb73d964","unresolved":false,"context_lines":[{"line_number":962,"context_line":"            ]"},{"line_number":963,"context_line":"            self._set_object(id, topic, obj)"},{"line_number":964,"context_line":""},{"line_number":965,"context_line":"    class _UniqueKeyCRUDHelper(_CRUDHelper):"},{"line_number":966,"context_line":"        def create(self, id, topic, notify\u003dTrue, **columns):"},{"line_number":967,"context_line":"            unique_key \u003d self.api_nb.driver.allocate_unique_key("},{"line_number":968,"context_line":"                self.table_name)"},{"line_number":969,"context_line":"            columns[db_models.UNIQUE_KEY] \u003d unique_key"},{"line_number":970,"context_line":"            return super(NbApi._UniqueKeyCRUDHelper, self).create("},{"line_number":971,"context_line":"                id, topic, notify, **columns)"}],"source_content_type":"text/x-python","patch_set":12,"id":"1a6eadb0_2e4f8893","line":971,"range":{"start_line":965,"start_character":0,"end_line":971,"end_character":45},"updated":"2016-12-13 02:24:39.000000000","message":"I kind of like the old way of handling unique key. When you created a tool class, you probably don\u0027t want the user of it to know much of it(or else why user will need a tool class).\n\nIn the way in this patch, if I want to add some resource with unique key(I am still adding the unique key to security group), I will have to change its db_model to inherit from unique key db_models. And then I will have to change its _CRUDHelper class here. The required steps are the same before this refactor.\n\nIn the old way(in version 10), I will just need to change its db model.","commit_id":"9f6b5399d0631f30e723b5c873fd0cf18d1cbdaa"},{"author":{"_account_id":23766,"name":"Dima Kuznetsov","email":"dima.kuznetsov@toganetworks.com","username":"dimak"},"change_message_id":"8edefb02c8d6077ebcac38f6e695a53cbe2a8912","unresolved":false,"context_lines":[{"line_number":962,"context_line":"            ]"},{"line_number":963,"context_line":"            self._set_object(id, topic, obj)"},{"line_number":964,"context_line":""},{"line_number":965,"context_line":"    class _UniqueKeyCRUDHelper(_CRUDHelper):"},{"line_number":966,"context_line":"        def create(self, id, topic, notify\u003dTrue, **columns):"},{"line_number":967,"context_line":"            unique_key \u003d self.api_nb.driver.allocate_unique_key("},{"line_number":968,"context_line":"                self.table_name)"},{"line_number":969,"context_line":"            columns[db_models.UNIQUE_KEY] \u003d unique_key"},{"line_number":970,"context_line":"            return super(NbApi._UniqueKeyCRUDHelper, self).create("},{"line_number":971,"context_line":"                id, topic, notify, **columns)"}],"source_content_type":"text/x-python","patch_set":12,"id":"1a6eadb0_e6adac32","line":971,"range":{"start_line":965,"start_character":0,"end_line":971,"end_character":45},"in_reply_to":"1a6eadb0_2e4f8893","updated":"2016-12-13 09:06:17.000000000","message":"I followed Omer\u0027s suggestion here because it makes moves the model semantics (like having a unique key and making sure it is assigned) from the _CRUDHelper class that has to be aware of all of it, to the person defining or changing the model itself.","commit_id":"9f6b5399d0631f30e723b5c873fd0cf18d1cbdaa"}],"dragonflow/tests/unit/test_api_nb.py":[{"author":{"_account_id":11159,"name":"Hong Hui Xiao","email":"honghui_xiao@yeah.net","username":"HongHuiXiao"},"change_message_id":"b6d6c7d8df15a14f67c7fadb582d6594b4454049","unresolved":false,"context_lines":[{"line_number":234,"context_line":"        submitted_obj \u003d jsonutils.loads("},{"line_number":235,"context_line":"            self.nb_api.driver.set_key.call_args[0][2])"},{"line_number":236,"context_line":""},{"line_number":237,"context_line":"        self.assertEqual(submitted_obj[\u0027collection\u0027], [{\u0027id\u0027: \u0027subobject2\u0027}])"},{"line_number":238,"context_line":""},{"line_number":239,"context_line":"    def test_update_element_fetches_only_once(self):"},{"line_number":240,"context_line":"        self.dummy_crud._update_element(self.fakedummy1[\u0027id\u0027],"}],"source_content_type":"text/x-python","patch_set":10,"id":"3a71b18c_5877e8d3","line":237,"range":{"start_line":237,"start_character":0,"end_line":237,"end_character":77},"updated":"2016-12-09 02:28:53.000000000","message":"It has the convention that the assertEqual will be used as assertEqual(expected, result)","commit_id":"ea6e13a0e2138f2d4f0d46afeb99f9f5bedcdd8d"},{"author":{"_account_id":23766,"name":"Dima Kuznetsov","email":"dima.kuznetsov@toganetworks.com","username":"dimak"},"change_message_id":"4902968d37d64e575c73506973125d0cc4ed66df","unresolved":false,"context_lines":[{"line_number":234,"context_line":"        submitted_obj \u003d jsonutils.loads("},{"line_number":235,"context_line":"            self.nb_api.driver.set_key.call_args[0][2])"},{"line_number":236,"context_line":""},{"line_number":237,"context_line":"        self.assertEqual(submitted_obj[\u0027collection\u0027], [{\u0027id\u0027: \u0027subobject2\u0027}])"},{"line_number":238,"context_line":""},{"line_number":239,"context_line":"    def test_update_element_fetches_only_once(self):"},{"line_number":240,"context_line":"        self.dummy_crud._update_element(self.fakedummy1[\u0027id\u0027],"}],"source_content_type":"text/x-python","patch_set":10,"id":"3a71b18c_3dc2de34","line":237,"range":{"start_line":237,"start_character":0,"end_line":237,"end_character":77},"in_reply_to":"3a71b18c_5877e8d3","updated":"2016-12-11 11:03:59.000000000","message":"Will change","commit_id":"ea6e13a0e2138f2d4f0d46afeb99f9f5bedcdd8d"},{"author":{"_account_id":11159,"name":"Hong Hui Xiao","email":"honghui_xiao@yeah.net","username":"HongHuiXiao"},"change_message_id":"b6d6c7d8df15a14f67c7fadb582d6594b4454049","unresolved":false,"context_lines":[{"line_number":268,"context_line":"        submitted_obj \u003d jsonutils.loads("},{"line_number":269,"context_line":"            self.nb_api.driver.set_key.call_args[0][2])"},{"line_number":270,"context_line":""},{"line_number":271,"context_line":"        self.assertEqual(len(submitted_obj[\u0027collection\u0027]), 2)"},{"line_number":272,"context_line":"        self.assertIn({\u0027id\u0027: \u0027subobject1\u0027, \u0027a\u0027: \u0027b\u0027},"},{"line_number":273,"context_line":"                      submitted_obj[\u0027collection\u0027])"},{"line_number":274,"context_line":"        self.assertIn({\u0027id\u0027: \u0027subobject2\u0027}, submitted_obj[\u0027collection\u0027])"}],"source_content_type":"text/x-python","patch_set":10,"id":"3a71b18c_f84c349f","line":271,"range":{"start_line":271,"start_character":0,"end_line":271,"end_character":61},"updated":"2016-12-09 02:28:53.000000000","message":"ditto","commit_id":"ea6e13a0e2138f2d4f0d46afeb99f9f5bedcdd8d"},{"author":{"_account_id":23766,"name":"Dima Kuznetsov","email":"dima.kuznetsov@toganetworks.com","username":"dimak"},"change_message_id":"4902968d37d64e575c73506973125d0cc4ed66df","unresolved":false,"context_lines":[{"line_number":268,"context_line":"        submitted_obj \u003d jsonutils.loads("},{"line_number":269,"context_line":"            self.nb_api.driver.set_key.call_args[0][2])"},{"line_number":270,"context_line":""},{"line_number":271,"context_line":"        self.assertEqual(len(submitted_obj[\u0027collection\u0027]), 2)"},{"line_number":272,"context_line":"        self.assertIn({\u0027id\u0027: \u0027subobject1\u0027, \u0027a\u0027: \u0027b\u0027},"},{"line_number":273,"context_line":"                      submitted_obj[\u0027collection\u0027])"},{"line_number":274,"context_line":"        self.assertIn({\u0027id\u0027: \u0027subobject2\u0027}, submitted_obj[\u0027collection\u0027])"}],"source_content_type":"text/x-python","patch_set":10,"id":"3a71b18c_7dde1612","line":271,"range":{"start_line":271,"start_character":0,"end_line":271,"end_character":61},"in_reply_to":"3a71b18c_f84c349f","updated":"2016-12-11 11:03:59.000000000","message":"Will change","commit_id":"ea6e13a0e2138f2d4f0d46afeb99f9f5bedcdd8d"}]}
