{"action":"created","comment":{"url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/pulls/comments/109310678","pull_request_review_id":30405753,"id":109310678,"diff_hunk":"@@ -40,62 +37,17 @@ def raw_request(self, base_url, method_name, params, response_format):\n         Make a request to the Mixpanel API and return a raw urllib2/url.request file-like\n         response object.\n         \"\"\"\n-        params['api_key'] = self.client.api_key\n-        params['expire'] = int(time.time()) + 600   # Grant this request 10 minutes.\n         params['format'] = response_format\n         # Getting rid of the None params\n         params = self.check_params(params)\n-\n-        # Creating signature\n-        if 'sig' in params:\n-            del params['sig']\n-        params['sig'] = self.hash_args(params, self.client.api_secret)\n-\n-        request_url = '{base_url}/{version}/{method_name}/?{encoded_params}'.format(\n+        url_without_params = '{base_url}/{version}/{method_name}/'.format(\n             base_url=base_url,\n             version=self.VERSION,\n             method_name=method_name,\n-            encoded_params=self.unicode_urlencode(params)\n-        )\n-        return url_request.urlopen(request_url, timeout=self.DEFAULT_TIMEOUT if self.client.timeout is None else self.client.timeout)\n-\n-    def unicode_urlencode(self, params):\n-        \"\"\"\n-        Convert lists to JSON encoded strings, and correctly handle any\n-        unicode URL parameters.\n-        \"\"\"\n-        if isinstance(params, dict):\n-            params = list(six.iteritems(params))\n-        for i, param in enumerate(params):\n-            if isinstance(param[1], list):\n-                params[i] = (param[0], json.dumps(param[1]),)\n-\n-        return urlencode(\n-            [(_tobytes(k), _tobytes(v)) for k, v in params]\n         )\n-\n-    def hash_args(self, args, secret=None):\n-        \"\"\"\n-        Hashes arguments by joining key=value pairs, appending the api_secret, and\n-        then taking the MD5 hex digest.\n-        \"\"\"\n-        for a in args:\n-            if isinstance(args[a], list):\n-                args[a] = json.dumps(args[a])\n-\n-        args_joined = six.b('')\n-        for a in sorted(args.keys()):\n-            args_joined += _tobytes(a)\n-            args_joined += six.b('=')\n-            args_joined += _tobytes(args[a])\n-\n-        hash = hashlib.md5(args_joined)\n-\n-        if secret:\n-            hash.update(_tobytes(secret))\n-        elif self.client.api_secret:\n-            hash.update(_tobytes(self.client.api_secret))\n-        return hash.hexdigest()\n+        request_obj = self.client.auth.authenticate(url_without_params, params)","path":"mixpanel_query/connection.py","position":87,"original_position":87,"commit_id":"f0a4ee75835a8d69349f29c7829cd7bc54f30b23","original_commit_id":"f0a4ee75835a8d69349f29c7829cd7bc54f30b23","user":{"login":"cooncesean","id":47877,"avatar_url":"https://avatars3.githubusercontent.com/u/47877?v=3","gravatar_id":"","url":"https://api.github.com/users/cooncesean","html_url":"https://github.com/cooncesean","followers_url":"https://api.github.com/users/cooncesean/followers","following_url":"https://api.github.com/users/cooncesean/following{/other_user}","gists_url":"https://api.github.com/users/cooncesean/gists{/gist_id}","starred_url":"https://api.github.com/users/cooncesean/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cooncesean/subscriptions","organizations_url":"https://api.github.com/users/cooncesean/orgs","repos_url":"https://api.github.com/users/cooncesean/repos","events_url":"https://api.github.com/users/cooncesean/events{/privacy}","received_events_url":"https://api.github.com/users/cooncesean/received_events","type":"User","site_admin":false},"body":"👍 much cleaner.","created_at":"2017-04-02T14:48:33Z","updated_at":"2017-04-02T14:48:33Z","html_url":"https://github.com/cooncesean/mixpanel-query-py/pull/30#discussion_r109310678","pull_request_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/pulls/30","_links":{"self":{"href":"https://api.github.com/repos/cooncesean/mixpanel-query-py/pulls/comments/109310678"},"html":{"href":"https://github.com/cooncesean/mixpanel-query-py/pull/30#discussion_r109310678"},"pull_request":{"href":"https://api.github.com/repos/cooncesean/mixpanel-query-py/pulls/30"}}},"pull_request":{"url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/pulls/30","id":113799052,"html_url":"https://github.com/cooncesean/mixpanel-query-py/pull/30","diff_url":"https://github.com/cooncesean/mixpanel-query-py/pull/30.diff","patch_url":"https://github.com/cooncesean/mixpanel-query-py/pull/30.patch","issue_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/issues/30","number":30,"state":"open","locked":false,"title":"Improve authentication method support","user":{"login":"thesmallestduck","id":7416654,"avatar_url":"https://avatars0.githubusercontent.com/u/7416654?v=3","gravatar_id":"","url":"https://api.github.com/users/thesmallestduck","html_url":"https://github.com/thesmallestduck","followers_url":"https://api.github.com/users/thesmallestduck/followers","following_url":"https://api.github.com/users/thesmallestduck/following{/other_user}","gists_url":"https://api.github.com/users/thesmallestduck/gists{/gist_id}","starred_url":"https://api.github.com/users/thesmallestduck/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/thesmallestduck/subscriptions","organizations_url":"https://api.github.com/users/thesmallestduck/orgs","repos_url":"https://api.github.com/users/thesmallestduck/repos","events_url":"https://api.github.com/users/thesmallestduck/events{/privacy}","received_events_url":"https://api.github.com/users/thesmallestduck/received_events","type":"User","site_admin":false},"body":"why\r\n---\r\nMixpanel documentation clearly states signature-based authentication is considered deprecated. However, on several occasions we have been informed there are no plans to discontinue support of signature-based authentication. There has been some interest in supporting the new \"secret in header\" means of authentication. Two PRs have been submitted to that effect:\r\n\r\nhttps://github.com/cooncesean/mixpanel-query-py/pull/27\r\nhttps://github.com/cooncesean/mixpanel-query-py/pull/29\r\n\r\nIn the interest of keeping this repo up to date, we should strive to conform to the latest documentation from Mixpanel.\r\n\r\nwhat\r\n---\r\nthis PR:\r\n\r\n* creates a new `mixpanel_query.auth` module with two classes, `SignatureAuth` and `SecretAuth`\r\n* adjusts `mixpanel_query.client.MixpanelQueryClient` to accept an auth_class upon initialization. This auth class is used to create an instance upon initialization for future use in authorizing requests\r\n* adjusts `mixpanel_query.connection.Connection` to use the client's auth instance to authenticate requests\r\n* moves the `unicode_urlencode` method from the connection class to `mixpanel_query.utils` so both authentication classes could use it\r\n\r\nnew usage\r\n---\r\nIf we merge this PR and update the pip version, you should not have to make any changes to your code in a production environment what-so-ever. The same signature method of authentication will be used, any unit tests you have setup to introspect url parameters should see everything behaving as usual.\r\n\r\nHowever, if you wish to switch production code to use the secret-based means of authentication, you should be able to do so like this:\r\n\r\nBEFORE:\r\n```\r\nfrom mixpanel_query.client import MixpanelQueryClient\r\n\r\nsignature_client = MixpanelQueryClient(API_KEY, API_SECRET)\r\n```\r\n\r\nAFTER:\r\n```\r\nrom mixpanel_query.client import MixpanelQueryClient\r\nfrom mixpanel_query.auth import SecretAuth\r\n\r\nsecret_client = MixpanelQueryClient(API_KEY, API_SECRET, auth_class=SecretAuth)\r\n```\r\n\r\nnotes\r\n---\r\n* in the future, we can consider changing the default `auth_class` used by the client to be `SecretAuth`. We should most certainly do so if Mixpanel ever communicates their intent to discontinue support of the signature-based authentication method.\r\n* thank you @drathm and @robin900 for your prior PRs on this (I leaned heavily on your PRs for the `SecretAuth` class.\r\n* I have tested both authentication methods using the `get_segmentation` and `get_export` methods, and both are functioning well. I'd appreciate any additional tire-kicking folks can bring to the PR. ","created_at":"2017-04-01T23:42:01Z","updated_at":"2017-04-02T14:48:33Z","closed_at":null,"merged_at":null,"merge_commit_sha":"8cceb099acda51c438c79e184d8d6fcde114699a","assignee":null,"assignees":[],"milestone":null,"commits_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/pulls/30/commits","review_comments_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/pulls/30/comments","review_comment_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/pulls/comments{/number}","comments_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/issues/30/comments","statuses_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/statuses/f0a4ee75835a8d69349f29c7829cd7bc54f30b23","head":{"label":"cooncesean:multi_auth_support","ref":"multi_auth_support","sha":"f0a4ee75835a8d69349f29c7829cd7bc54f30b23","user":{"login":"cooncesean","id":47877,"avatar_url":"https://avatars3.githubusercontent.com/u/47877?v=3","gravatar_id":"","url":"https://api.github.com/users/cooncesean","html_url":"https://github.com/cooncesean","followers_url":"https://api.github.com/users/cooncesean/followers","following_url":"https://api.github.com/users/cooncesean/following{/other_user}","gists_url":"https://api.github.com/users/cooncesean/gists{/gist_id}","starred_url":"https://api.github.com/users/cooncesean/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cooncesean/subscriptions","organizations_url":"https://api.github.com/users/cooncesean/orgs","repos_url":"https://api.github.com/users/cooncesean/repos","events_url":"https://api.github.com/users/cooncesean/events{/privacy}","received_events_url":"https://api.github.com/users/cooncesean/received_events","type":"User","site_admin":false},"repo":{"id":22551642,"name":"mixpanel-query-py","full_name":"cooncesean/mixpanel-query-py","owner":{"login":"cooncesean","id":47877,"avatar_url":"https://avatars3.githubusercontent.com/u/47877?v=3","gravatar_id":"","url":"https://api.github.com/users/cooncesean","html_url":"https://github.com/cooncesean","followers_url":"https://api.github.com/users/cooncesean/followers","following_url":"https://api.github.com/users/cooncesean/following{/other_user}","gists_url":"https://api.github.com/users/cooncesean/gists{/gist_id}","starred_url":"https://api.github.com/users/cooncesean/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cooncesean/subscriptions","organizations_url":"https://api.github.com/users/cooncesean/orgs","repos_url":"https://api.github.com/users/cooncesean/repos","events_url":"https://api.github.com/users/cooncesean/events{/privacy}","received_events_url":"https://api.github.com/users/cooncesean/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/cooncesean/mixpanel-query-py","description":"The Python interface to fetch data from Mixpanel.","fork":false,"url":"https://api.github.com/repos/cooncesean/mixpanel-query-py","forks_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/forks","keys_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/keys{/key_id}","collaborators_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/teams","hooks_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/hooks","issue_events_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/issues/events{/number}","events_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/events","assignees_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/assignees{/user}","branches_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/branches{/branch}","tags_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/tags","blobs_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/git/refs{/sha}","trees_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/git/trees{/sha}","statuses_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/statuses/{sha}","languages_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/languages","stargazers_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/stargazers","contributors_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/contributors","subscribers_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/subscribers","subscription_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/subscription","commits_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/commits{/sha}","git_commits_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/git/commits{/sha}","comments_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/comments{/number}","issue_comment_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/issues/comments{/number}","contents_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/contents/{+path}","compare_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/compare/{base}...{head}","merges_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/merges","archive_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/downloads","issues_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/issues{/number}","pulls_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/pulls{/number}","milestones_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/milestones{/number}","notifications_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/labels{/name}","releases_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/releases{/id}","deployments_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/deployments","created_at":"2014-08-02T17:17:32Z","updated_at":"2017-03-27T16:36:03Z","pushed_at":"2017-04-02T00:37:01Z","git_url":"git://github.com/cooncesean/mixpanel-query-py.git","ssh_url":"git@github.com:cooncesean/mixpanel-query-py.git","clone_url":"https://github.com/cooncesean/mixpanel-query-py.git","svn_url":"https://github.com/cooncesean/mixpanel-query-py","homepage":null,"size":56,"stargazers_count":17,"watchers_count":17,"language":"Python","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":17,"mirror_url":null,"open_issues_count":7,"forks":17,"open_issues":7,"watchers":17,"default_branch":"master"}},"base":{"label":"cooncesean:master","ref":"master","sha":"3efda7e05d6c14d3ea9e967df0e9b13c1ea0918d","user":{"login":"cooncesean","id":47877,"avatar_url":"https://avatars3.githubusercontent.com/u/47877?v=3","gravatar_id":"","url":"https://api.github.com/users/cooncesean","html_url":"https://github.com/cooncesean","followers_url":"https://api.github.com/users/cooncesean/followers","following_url":"https://api.github.com/users/cooncesean/following{/other_user}","gists_url":"https://api.github.com/users/cooncesean/gists{/gist_id}","starred_url":"https://api.github.com/users/cooncesean/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cooncesean/subscriptions","organizations_url":"https://api.github.com/users/cooncesean/orgs","repos_url":"https://api.github.com/users/cooncesean/repos","events_url":"https://api.github.com/users/cooncesean/events{/privacy}","received_events_url":"https://api.github.com/users/cooncesean/received_events","type":"User","site_admin":false},"repo":{"id":22551642,"name":"mixpanel-query-py","full_name":"cooncesean/mixpanel-query-py","owner":{"login":"cooncesean","id":47877,"avatar_url":"https://avatars3.githubusercontent.com/u/47877?v=3","gravatar_id":"","url":"https://api.github.com/users/cooncesean","html_url":"https://github.com/cooncesean","followers_url":"https://api.github.com/users/cooncesean/followers","following_url":"https://api.github.com/users/cooncesean/following{/other_user}","gists_url":"https://api.github.com/users/cooncesean/gists{/gist_id}","starred_url":"https://api.github.com/users/cooncesean/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cooncesean/subscriptions","organizations_url":"https://api.github.com/users/cooncesean/orgs","repos_url":"https://api.github.com/users/cooncesean/repos","events_url":"https://api.github.com/users/cooncesean/events{/privacy}","received_events_url":"https://api.github.com/users/cooncesean/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/cooncesean/mixpanel-query-py","description":"The Python interface to fetch data from Mixpanel.","fork":false,"url":"https://api.github.com/repos/cooncesean/mixpanel-query-py","forks_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/forks","keys_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/keys{/key_id}","collaborators_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/teams","hooks_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/hooks","issue_events_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/issues/events{/number}","events_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/events","assignees_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/assignees{/user}","branches_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/branches{/branch}","tags_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/tags","blobs_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/git/refs{/sha}","trees_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/git/trees{/sha}","statuses_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/statuses/{sha}","languages_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/languages","stargazers_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/stargazers","contributors_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/contributors","subscribers_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/subscribers","subscription_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/subscription","commits_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/commits{/sha}","git_commits_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/git/commits{/sha}","comments_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/comments{/number}","issue_comment_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/issues/comments{/number}","contents_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/contents/{+path}","compare_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/compare/{base}...{head}","merges_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/merges","archive_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/downloads","issues_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/issues{/number}","pulls_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/pulls{/number}","milestones_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/milestones{/number}","notifications_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/labels{/name}","releases_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/releases{/id}","deployments_url":"https://api.github.com/repos/cooncesean/mixpanel-query-py/deployments","created_at":"2014-08-02T17:17:32Z","updated_at":"2017-03-27T16:36:03Z","pushed_at":"2017-04-02T00:37:01Z","git_url":"git://github.com/cooncesean/mixpanel-query-py.git","ssh_url":"git@github.com:cooncesean/mixpanel-query-py.git","clone_url":"https://github.com/cooncesean/mixpanel-query-py.git","svn_url":"https://github.com/cooncesean/mixpanel-query-py","homepage":null,"size":56,"stargazers_count":17,"watchers_count":17,"language":"Python","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":17,"mirror_url":null,"open_issues_count":7,"forks":17,"open_issues":7,"watchers":17,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/cooncesean/mixpanel-query-py/pulls/30"},"html":{"href":"https://github.com/cooncesean/mixpanel-query-py/pull/30"},"issue":{"href":"https://api.github.com/repos/cooncesean/mixpanel-query-py/issues/30"},"comments":{"href":"https://api.github.com/repos/cooncesean/mixpanel-query-py/issues/30/comments"},"review_comments":{"href":"https://api.github.com/repos/cooncesean/mixpanel-query-py/pulls/30/comments"},"review_comment":{"href":"https://api.github.com/repos/cooncesean/mixpanel-query-py/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/cooncesean/mixpanel-query-py/pulls/30/commits"},"statuses":{"href":"https://api.github.com/repos/cooncesean/mixpanel-query-py/statuses/f0a4ee75835a8d69349f29c7829cd7bc54f30b23"}}}}
{"actor":{"display_login":"cooncesean"}}