{"action":"created","issue":{"url":"https://api.github.com/repos/RallyTools/RallyRestToolkitForPython/issues/48","labels_url":"https://api.github.com/repos/RallyTools/RallyRestToolkitForPython/issues/48/labels{/name}","comments_url":"https://api.github.com/repos/RallyTools/RallyRestToolkitForPython/issues/48/comments","events_url":"https://api.github.com/repos/RallyTools/RallyRestToolkitForPython/issues/48/events","html_url":"https://github.com/RallyTools/RallyRestToolkitForPython/issues/48","id":55440504,"number":48,"title":"Problems hydrating previously unhydrated PortfolioItem objects","user":{"login":"patknight","id":7013980,"avatar_url":"https://avatars.githubusercontent.com/u/7013980?v=3","gravatar_id":"","url":"https://api.github.com/users/patknight","html_url":"https://github.com/patknight","followers_url":"https://api.github.com/users/patknight/followers","following_url":"https://api.github.com/users/patknight/following{/other_user}","gists_url":"https://api.github.com/users/patknight/gists{/gist_id}","starred_url":"https://api.github.com/users/patknight/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/patknight/subscriptions","organizations_url":"https://api.github.com/users/patknight/orgs","repos_url":"https://api.github.com/users/patknight/repos","events_url":"https://api.github.com/users/patknight/events{/privacy}","received_events_url":"https://api.github.com/users/patknight/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":2,"created_at":"2015-01-26T02:13:43Z","updated_at":"2015-03-02T23:31:39Z","closed_at":null,"body":"BIG REWORDING AS I THINK MY ORIGINAL REPORT WAS CONFUSINGLY WORDED.\r\n\r\nThese problems arose when WSAPI 2.0 started requiring the full hierarchical entity type name of portfolio items in the REST API instead of the sub type name (i.e. requiring PortfolioItem/Feature instead of Feature.) This is handled fine in the initial request to get an object, but doesn't work when pyral needs to hydrate a previously unhydrated object. This is because the code to hydrate the object that builds a get request  doesn't handle the \"/\" in the middle of portfolio entity type names.\r\n\r\nThere are two issues:\r\n1) In entity.py `Persistable.__getattr__` incorrectly extracts portolio item entity names when splitting the self._ref. To construct the entity name to send to the Rally API server it needs to cope with a '/' within entity types ('PortfolioItem/Feature' vs 'HierarchicalRequirement')\r\n\r\nThis is addressed by first splitting on `WS_API_VERSION + '/'` to get a string containing just the entity name and oid before further splitting that to extract those fields.\r\n\r\n2) Although the server wants the entity type to be e.g. 'PortfolioItem/Feature' in the request, it uses 'Feature' as the key in the response content. However, the Pyral class for the unhydrated feature object (in this case) is 'PortfolioItem' so we get a KeyError when we try to extract the response content.\r\n\r\nThis is addressed by catching the KeyError on 'PortfolioItem', then looping through PORTFOLIO_ITEM_SUB_TYPES, re-raising the original exception if we don't find the contents.\r\n\r\nHere's a file that shows the problem (if you have access to my workspace and projects - otherwise just put in your own)\r\n```python\r\nimport pyral\r\nimport metadata\r\n\r\n\r\nclass RallyError(Exception):\r\n    pass\r\n\r\n\r\n_rally_instance = None\r\n\r\n\r\ndef get_rally_instance():\r\n    global _rally_instance\r\n\r\n    if _rally_instance is None:\r\n        _rally_instance = pyral.Rally(\r\n            server=metadata.rally_auth[u\"server\"],\r\n            user=metadata.rally_auth[u\"user\"],\r\n            password=metadata.rally_auth[u\"password\"],\r\n            workspace=metadata.rally_auth[u\"workspace\"],\r\n            project=metadata.rally_auth[u\"project\"])\r\n\r\n    return _rally_instance\r\n\r\nrally = get_rally_instance()\r\n\r\nunhydrated = list(rally.get(\"PortfolioItem/Feature\", fetch=False,\r\n                            query='FormattedID = \"F226\"'))[0]\r\nhydrated = list(rally.get(\"PortfolioItem/Feature\", fetch=True,\r\n                         query='FormattedID = \"F226\"'))[0]\r\nprint hydrated.FormattedID # this works\r\nprint unhydrated.FormattedID # this throws an unknown OID 422 exception\r\n```"},"comment":{"url":"https://api.github.com/repos/RallyTools/RallyRestToolkitForPython/issues/comments/76850819","html_url":"https://github.com/RallyTools/RallyRestToolkitForPython/issues/48#issuecomment-76850819","issue_url":"https://api.github.com/repos/RallyTools/RallyRestToolkitForPython/issues/48","id":76850819,"user":{"login":"patknight","id":7013980,"avatar_url":"https://avatars.githubusercontent.com/u/7013980?v=3","gravatar_id":"","url":"https://api.github.com/users/patknight","html_url":"https://github.com/patknight","followers_url":"https://api.github.com/users/patknight/followers","following_url":"https://api.github.com/users/patknight/following{/other_user}","gists_url":"https://api.github.com/users/patknight/gists{/gist_id}","starred_url":"https://api.github.com/users/patknight/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/patknight/subscriptions","organizations_url":"https://api.github.com/users/patknight/orgs","repos_url":"https://api.github.com/users/patknight/repos","events_url":"https://api.github.com/users/patknight/events{/privacy}","received_events_url":"https://api.github.com/users/patknight/received_events","type":"User","site_admin":false},"created_at":"2015-03-02T23:31:39Z","updated_at":"2015-03-02T23:31:39Z","body":"I've added pull request #53 with my proposed fix. Pull request #41 from ksato9700 has an alternative approach which I found after I'd coded mine. Please choose whichever you think is more in keeping with the existing code base."}}