{"action":"created","comment":{"url":"https://api.github.com/repos/rackerlabs/otter/pulls/comments/23119329","id":23119329,"diff_hunk":"@@ -128,8 +146,138 @@ def test_defaults(self):\n                     data=None,\n                     log=None,\n                     reauth_codes=(401, 403),\n-                    success_codes=(200,),\n+                    success_pred=has_code(200),\n                     json_response=True,\n                 )\n             )\n         )\n+\n+\n+class PerformServiceRequestTests(SynchronousTestCase):\n+    \"\"\"Tests for :func:`concretize_service_request`.\"\"\"\n+    def setUp(self):\n+        \"\"\"Save some common parameters.\"\"\"\n+        self.log = object()\n+        self.authenticator = object()\n+        self.service_mapping = {\n+            ServiceType.CLOUD_SERVERS: 'cloudServersOpenStack'}\n+        eff = service_request(ServiceType.CLOUD_SERVERS, 'GET', 'servers')\n+        self.svcreq = eff.intent\n+\n+    def _concrete(self, svcreq):\n+        \"\"\"\n+        Call :func:`concretize_service_request` with premade test objects.\n+        \"\"\"\n+        return concretize_service_request(\n+            self.authenticator, self.log, self.service_mapping, 'DFW',\n+            1,\n+            svcreq\n+        )\n+\n+    def test_authenticates(self):\n+        \"\"\"Auth is done before making the request.\"\"\"\n+        eff = self._concrete(self.svcreq)\n+        expected_intent = Authenticate(self.authenticator, 1, self.log)\n+        self.assertEqual(eff.intent, expected_intent)\n+        next_eff = resolve_authenticate(eff)\n+        # The next effect in the chain is the requested HTTP request,\n+        # with appropriate auth headers\n+        self.assertEqual(\n+            next_eff.intent,\n+            Request(method='GET', url='http://dfw.openstack/servers',\n+                    headers=headers('token'), log=self.log))\n+\n+    def test_invalidate_on_auth_error_code(self):\n+        \"\"\"\n+        Upon authentication error, the auth cache is invalidated.\n+        \"\"\"\n+        eff = self._concrete(self.svcreq)\n+        next_eff = resolve_authenticate(eff)\n+        # When the HTTP response is an auth error, the auth cache is\n+        # invalidated, by way of the next effect:\n+        invalidate_eff = resolve_effect(next_eff, stub_pure_response(\"\", 401))\n+        expected_intent = InvalidateToken(self.authenticator, 1)\n+        self.assertEqual(invalidate_eff.intent, expected_intent)\n+        self.assertRaises(APIError, resolve_effect, invalidate_eff, None)\n+\n+    def test_json(self):\n+        \"\"\"\n+        JSON-serializable requests are dumped before being sent, and\n+        JSON-serialized responses are parsed.\n+        \"\"\"\n+        input_json = {\"a\": 1}\n+        output_json = {\"b\": 2}\n+        svcreq = service_request(ServiceType.CLOUD_SERVERS, \"GET\", \"servers\",\n+                                 data=input_json).intent\n+        eff = self._concrete(svcreq)\n+        next_eff = resolve_authenticate(eff)\n+        result = resolve_effect(next_eff,\n+                                stub_pure_response(json.dumps(output_json)))\n+        self.assertEqual(next_eff.intent.data, json.dumps(input_json))","path":"otter/test/test_http.py","position":121,"original_position":121,"commit_id":"0c3e496f8b54a3530d546fa6136dc5d8f2cc23a7","original_commit_id":"3b836fdacc68e751f2c64dbb08bd8afed4ea7524","user":{"login":"manishtomar","id":758529,"avatar_url":"https://avatars.githubusercontent.com/u/758529?v=3","gravatar_id":"","url":"https://api.github.com/users/manishtomar","html_url":"https://github.com/manishtomar","followers_url":"https://api.github.com/users/manishtomar/followers","following_url":"https://api.github.com/users/manishtomar/following{/other_user}","gists_url":"https://api.github.com/users/manishtomar/gists{/gist_id}","starred_url":"https://api.github.com/users/manishtomar/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/manishtomar/subscriptions","organizations_url":"https://api.github.com/users/manishtomar/orgs","repos_url":"https://api.github.com/users/manishtomar/repos","events_url":"https://api.github.com/users/manishtomar/events{/privacy}","received_events_url":"https://api.github.com/users/manishtomar/received_events","type":"User","site_admin":false},"body":"Minor nitpick: It might be easier for reader to move this at line 214 after getting `next_effect`. And a space after the check would be nice. Something like:\r\n```python\r\n# input json is as expected\r\nnext_eff = resolve_authenticate(eff)\r\nself.assertEqual(next_eff.intent.data, json.dumps(input_json))\r\n\r\n# output json is as expected\r\nresult = resolve_effect(next_eff, stub_pure_response(json.dumps(output_json)))\r\nself.assertEqual(result, output_json)\r\n```","created_at":"2015-01-17T00:31:06Z","updated_at":"2015-01-17T00:31:06Z","html_url":"https://github.com/rackerlabs/otter/pull/851#discussion_r23119329","pull_request_url":"https://api.github.com/repos/rackerlabs/otter/pulls/851","_links":{"self":{"href":"https://api.github.com/repos/rackerlabs/otter/pulls/comments/23119329"},"html":{"href":"https://github.com/rackerlabs/otter/pull/851#discussion_r23119329"},"pull_request":{"href":"https://api.github.com/repos/rackerlabs/otter/pulls/851"}}},"pull_request":{"url":"https://api.github.com/repos/rackerlabs/otter/pulls/851","id":26407030,"html_url":"https://github.com/rackerlabs/otter/pull/851","diff_url":"https://github.com/rackerlabs/otter/pull/851.diff","patch_url":"https://github.com/rackerlabs/otter/pull/851.patch","issue_url":"https://api.github.com/repos/rackerlabs/otter/issues/851","number":851,"state":"open","locked":false,"title":"Add concretize_service_request function","user":{"login":"radix","id":227068,"avatar_url":"https://avatars.githubusercontent.com/u/227068?v=3","gravatar_id":"","url":"https://api.github.com/users/radix","html_url":"https://github.com/radix","followers_url":"https://api.github.com/users/radix/followers","following_url":"https://api.github.com/users/radix/following{/other_user}","gists_url":"https://api.github.com/users/radix/gists{/gist_id}","starred_url":"https://api.github.com/users/radix/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/radix/subscriptions","organizations_url":"https://api.github.com/users/radix/orgs","repos_url":"https://api.github.com/users/radix/repos","events_url":"https://api.github.com/users/radix/events{/privacy}","received_events_url":"https://api.github.com/users/radix/received_events","type":"User","site_admin":false},"body":"This branch introduces a new intent, `TenantScope`, which allows specifying a tenant for any effect which might perform `ServiceRequest`s, and a performer for `TenantScope` which itself provides a performer for `ServiceRequest`. This is a pretty clever (not to be mistaken for hacky, IMO ;) use of the new Effect performer/dispatcher mechanics introduces in Effect 0.1a12. Please feel free to grill me for a better understanding of how it works, if necessary.\r\n\r\nSome points for reviewers:\r\n\r\n- `concretize_service_request` and `perform_tenant_scope` should bear the brunt of your reviewing efforts.\r\n- The tests for `concretize_service_request` are hack-'n'-pasted from the get_request_func tests. Once we get rid of get_request_func, we can delete the old tests.\r\n- This branch also changes ServiceRequest from taking `success_codes` to the newer `success_pred` system.\r\n- This change draws our attention to #896. This isn't exactly a new problem, as request_func had the same issue. I've added a comment to the place in metrics.py where we're creating the metrics-specific request_func and dispatchers. So: things should work with this branch, but we need to resolve #896 before I'm really happy with ServiceRequest.","created_at":"2014-12-19T23:50:38Z","updated_at":"2015-01-17T00:31:06Z","closed_at":null,"merged_at":null,"merge_commit_sha":"510f4f807b1151dd91665af7bde501c0647a1e4f","assignee":{"login":"radix","id":227068,"avatar_url":"https://avatars.githubusercontent.com/u/227068?v=3","gravatar_id":"","url":"https://api.github.com/users/radix","html_url":"https://github.com/radix","followers_url":"https://api.github.com/users/radix/followers","following_url":"https://api.github.com/users/radix/following{/other_user}","gists_url":"https://api.github.com/users/radix/gists{/gist_id}","starred_url":"https://api.github.com/users/radix/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/radix/subscriptions","organizations_url":"https://api.github.com/users/radix/orgs","repos_url":"https://api.github.com/users/radix/repos","events_url":"https://api.github.com/users/radix/events{/privacy}","received_events_url":"https://api.github.com/users/radix/received_events","type":"User","site_admin":false},"milestone":null,"commits_url":"https://api.github.com/repos/rackerlabs/otter/pulls/851/commits","review_comments_url":"https://api.github.com/repos/rackerlabs/otter/pulls/851/comments","review_comment_url":"https://api.github.com/repos/rackerlabs/otter/pulls/comments/{number}","comments_url":"https://api.github.com/repos/rackerlabs/otter/issues/851/comments","statuses_url":"https://api.github.com/repos/rackerlabs/otter/statuses/0c3e496f8b54a3530d546fa6136dc5d8f2cc23a7","head":{"label":"rackerlabs:concretize-service-request","ref":"concretize-service-request","sha":"0c3e496f8b54a3530d546fa6136dc5d8f2cc23a7","user":{"login":"rackerlabs","id":84293,"avatar_url":"https://avatars.githubusercontent.com/u/84293?v=3","gravatar_id":"","url":"https://api.github.com/users/rackerlabs","html_url":"https://github.com/rackerlabs","followers_url":"https://api.github.com/users/rackerlabs/followers","following_url":"https://api.github.com/users/rackerlabs/following{/other_user}","gists_url":"https://api.github.com/users/rackerlabs/gists{/gist_id}","starred_url":"https://api.github.com/users/rackerlabs/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rackerlabs/subscriptions","organizations_url":"https://api.github.com/users/rackerlabs/orgs","repos_url":"https://api.github.com/users/rackerlabs/repos","events_url":"https://api.github.com/users/rackerlabs/events{/privacy}","received_events_url":"https://api.github.com/users/rackerlabs/received_events","type":"Organization","site_admin":false},"repo":{"id":6097139,"name":"otter","full_name":"rackerlabs/otter","owner":{"login":"rackerlabs","id":84293,"avatar_url":"https://avatars.githubusercontent.com/u/84293?v=3","gravatar_id":"","url":"https://api.github.com/users/rackerlabs","html_url":"https://github.com/rackerlabs","followers_url":"https://api.github.com/users/rackerlabs/followers","following_url":"https://api.github.com/users/rackerlabs/following{/other_user}","gists_url":"https://api.github.com/users/rackerlabs/gists{/gist_id}","starred_url":"https://api.github.com/users/rackerlabs/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rackerlabs/subscriptions","organizations_url":"https://api.github.com/users/rackerlabs/orgs","repos_url":"https://api.github.com/users/rackerlabs/repos","events_url":"https://api.github.com/users/rackerlabs/events{/privacy}","received_events_url":"https://api.github.com/users/rackerlabs/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/rackerlabs/otter","description":"Rackspace Auto Scale","fork":false,"url":"https://api.github.com/repos/rackerlabs/otter","forks_url":"https://api.github.com/repos/rackerlabs/otter/forks","keys_url":"https://api.github.com/repos/rackerlabs/otter/keys{/key_id}","collaborators_url":"https://api.github.com/repos/rackerlabs/otter/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/rackerlabs/otter/teams","hooks_url":"https://api.github.com/repos/rackerlabs/otter/hooks","issue_events_url":"https://api.github.com/repos/rackerlabs/otter/issues/events{/number}","events_url":"https://api.github.com/repos/rackerlabs/otter/events","assignees_url":"https://api.github.com/repos/rackerlabs/otter/assignees{/user}","branches_url":"https://api.github.com/repos/rackerlabs/otter/branches{/branch}","tags_url":"https://api.github.com/repos/rackerlabs/otter/tags","blobs_url":"https://api.github.com/repos/rackerlabs/otter/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/rackerlabs/otter/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/rackerlabs/otter/git/refs{/sha}","trees_url":"https://api.github.com/repos/rackerlabs/otter/git/trees{/sha}","statuses_url":"https://api.github.com/repos/rackerlabs/otter/statuses/{sha}","languages_url":"https://api.github.com/repos/rackerlabs/otter/languages","stargazers_url":"https://api.github.com/repos/rackerlabs/otter/stargazers","contributors_url":"https://api.github.com/repos/rackerlabs/otter/contributors","subscribers_url":"https://api.github.com/repos/rackerlabs/otter/subscribers","subscription_url":"https://api.github.com/repos/rackerlabs/otter/subscription","commits_url":"https://api.github.com/repos/rackerlabs/otter/commits{/sha}","git_commits_url":"https://api.github.com/repos/rackerlabs/otter/git/commits{/sha}","comments_url":"https://api.github.com/repos/rackerlabs/otter/comments{/number}","issue_comment_url":"https://api.github.com/repos/rackerlabs/otter/issues/comments/{number}","contents_url":"https://api.github.com/repos/rackerlabs/otter/contents/{+path}","compare_url":"https://api.github.com/repos/rackerlabs/otter/compare/{base}...{head}","merges_url":"https://api.github.com/repos/rackerlabs/otter/merges","archive_url":"https://api.github.com/repos/rackerlabs/otter/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/rackerlabs/otter/downloads","issues_url":"https://api.github.com/repos/rackerlabs/otter/issues{/number}","pulls_url":"https://api.github.com/repos/rackerlabs/otter/pulls{/number}","milestones_url":"https://api.github.com/repos/rackerlabs/otter/milestones{/number}","notifications_url":"https://api.github.com/repos/rackerlabs/otter/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/rackerlabs/otter/labels{/name}","releases_url":"https://api.github.com/repos/rackerlabs/otter/releases{/id}","created_at":"2012-10-05T21:35:59Z","updated_at":"2015-01-16T19:38:46Z","pushed_at":"2015-01-17T00:15:00Z","git_url":"git://github.com/rackerlabs/otter.git","ssh_url":"git@github.com:rackerlabs/otter.git","clone_url":"https://github.com/rackerlabs/otter.git","svn_url":"https://github.com/rackerlabs/otter","homepage":"http://www.rackspace.com/cloud/auto-scale/","size":37712,"stargazers_count":40,"watchers_count":40,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":15,"mirror_url":null,"open_issues_count":60,"forks":15,"open_issues":60,"watchers":40,"default_branch":"master"}},"base":{"label":"rackerlabs:master","ref":"master","sha":"617c4ec4da0aeadbbfb9afc80a88f790ef2ab42e","user":{"login":"rackerlabs","id":84293,"avatar_url":"https://avatars.githubusercontent.com/u/84293?v=3","gravatar_id":"","url":"https://api.github.com/users/rackerlabs","html_url":"https://github.com/rackerlabs","followers_url":"https://api.github.com/users/rackerlabs/followers","following_url":"https://api.github.com/users/rackerlabs/following{/other_user}","gists_url":"https://api.github.com/users/rackerlabs/gists{/gist_id}","starred_url":"https://api.github.com/users/rackerlabs/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rackerlabs/subscriptions","organizations_url":"https://api.github.com/users/rackerlabs/orgs","repos_url":"https://api.github.com/users/rackerlabs/repos","events_url":"https://api.github.com/users/rackerlabs/events{/privacy}","received_events_url":"https://api.github.com/users/rackerlabs/received_events","type":"Organization","site_admin":false},"repo":{"id":6097139,"name":"otter","full_name":"rackerlabs/otter","owner":{"login":"rackerlabs","id":84293,"avatar_url":"https://avatars.githubusercontent.com/u/84293?v=3","gravatar_id":"","url":"https://api.github.com/users/rackerlabs","html_url":"https://github.com/rackerlabs","followers_url":"https://api.github.com/users/rackerlabs/followers","following_url":"https://api.github.com/users/rackerlabs/following{/other_user}","gists_url":"https://api.github.com/users/rackerlabs/gists{/gist_id}","starred_url":"https://api.github.com/users/rackerlabs/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rackerlabs/subscriptions","organizations_url":"https://api.github.com/users/rackerlabs/orgs","repos_url":"https://api.github.com/users/rackerlabs/repos","events_url":"https://api.github.com/users/rackerlabs/events{/privacy}","received_events_url":"https://api.github.com/users/rackerlabs/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/rackerlabs/otter","description":"Rackspace Auto Scale","fork":false,"url":"https://api.github.com/repos/rackerlabs/otter","forks_url":"https://api.github.com/repos/rackerlabs/otter/forks","keys_url":"https://api.github.com/repos/rackerlabs/otter/keys{/key_id}","collaborators_url":"https://api.github.com/repos/rackerlabs/otter/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/rackerlabs/otter/teams","hooks_url":"https://api.github.com/repos/rackerlabs/otter/hooks","issue_events_url":"https://api.github.com/repos/rackerlabs/otter/issues/events{/number}","events_url":"https://api.github.com/repos/rackerlabs/otter/events","assignees_url":"https://api.github.com/repos/rackerlabs/otter/assignees{/user}","branches_url":"https://api.github.com/repos/rackerlabs/otter/branches{/branch}","tags_url":"https://api.github.com/repos/rackerlabs/otter/tags","blobs_url":"https://api.github.com/repos/rackerlabs/otter/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/rackerlabs/otter/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/rackerlabs/otter/git/refs{/sha}","trees_url":"https://api.github.com/repos/rackerlabs/otter/git/trees{/sha}","statuses_url":"https://api.github.com/repos/rackerlabs/otter/statuses/{sha}","languages_url":"https://api.github.com/repos/rackerlabs/otter/languages","stargazers_url":"https://api.github.com/repos/rackerlabs/otter/stargazers","contributors_url":"https://api.github.com/repos/rackerlabs/otter/contributors","subscribers_url":"https://api.github.com/repos/rackerlabs/otter/subscribers","subscription_url":"https://api.github.com/repos/rackerlabs/otter/subscription","commits_url":"https://api.github.com/repos/rackerlabs/otter/commits{/sha}","git_commits_url":"https://api.github.com/repos/rackerlabs/otter/git/commits{/sha}","comments_url":"https://api.github.com/repos/rackerlabs/otter/comments{/number}","issue_comment_url":"https://api.github.com/repos/rackerlabs/otter/issues/comments/{number}","contents_url":"https://api.github.com/repos/rackerlabs/otter/contents/{+path}","compare_url":"https://api.github.com/repos/rackerlabs/otter/compare/{base}...{head}","merges_url":"https://api.github.com/repos/rackerlabs/otter/merges","archive_url":"https://api.github.com/repos/rackerlabs/otter/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/rackerlabs/otter/downloads","issues_url":"https://api.github.com/repos/rackerlabs/otter/issues{/number}","pulls_url":"https://api.github.com/repos/rackerlabs/otter/pulls{/number}","milestones_url":"https://api.github.com/repos/rackerlabs/otter/milestones{/number}","notifications_url":"https://api.github.com/repos/rackerlabs/otter/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/rackerlabs/otter/labels{/name}","releases_url":"https://api.github.com/repos/rackerlabs/otter/releases{/id}","created_at":"2012-10-05T21:35:59Z","updated_at":"2015-01-16T19:38:46Z","pushed_at":"2015-01-17T00:15:00Z","git_url":"git://github.com/rackerlabs/otter.git","ssh_url":"git@github.com:rackerlabs/otter.git","clone_url":"https://github.com/rackerlabs/otter.git","svn_url":"https://github.com/rackerlabs/otter","homepage":"http://www.rackspace.com/cloud/auto-scale/","size":37712,"stargazers_count":40,"watchers_count":40,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":15,"mirror_url":null,"open_issues_count":60,"forks":15,"open_issues":60,"watchers":40,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/rackerlabs/otter/pulls/851"},"html":{"href":"https://github.com/rackerlabs/otter/pull/851"},"issue":{"href":"https://api.github.com/repos/rackerlabs/otter/issues/851"},"comments":{"href":"https://api.github.com/repos/rackerlabs/otter/issues/851/comments"},"review_comments":{"href":"https://api.github.com/repos/rackerlabs/otter/pulls/851/comments"},"review_comment":{"href":"https://api.github.com/repos/rackerlabs/otter/pulls/comments/{number}"},"commits":{"href":"https://api.github.com/repos/rackerlabs/otter/pulls/851/commits"},"statuses":{"href":"https://api.github.com/repos/rackerlabs/otter/statuses/0c3e496f8b54a3530d546fa6136dc5d8f2cc23a7"}}}}