From 0678b1df25a7f11a33ad46b99fdd357f8ae61ffc Mon Sep 17 00:00:00 2001
From: Ryan Barrett <git@ryanb.org>
Date: Sun, 27 May 2018 20:55:35 -0700
Subject: [PATCH] github: better error handling for creating comments, surface
 messages

fixes snarfed/bridgy#824
---
 granary/github.py           | 23 +++++++++++++++--------
 granary/test/test_github.py | 34 ++++++++++++++++++++++++++++++++++
 2 files changed, 49 insertions(+), 8 deletions(-)

diff --git a/granary/github.py b/granary/github.py
index 2e06bff3..a6d2a7a6 100644
--- a/granary/github.py
+++ b/granary/github.py
@@ -235,7 +235,12 @@ def graphql(self, graphql, kwargs):
       })
     resp.raise_for_status()
     result = resp.json()
-    assert 'errors' not in result, result
+
+    errs = result.get('errors')
+    if errs:
+      logging.warning(result)
+      raise ValueError('\n'.join(e.get('message') for e in errs))
+
     return result['data']
 
   def rest(self, url, data=None, **kwargs):
@@ -418,7 +423,7 @@ def render_markdown(self, markdown, owner, repo):
 
     Returns: unicode string, rendered HTML
     """
-    return self.rest (REST_API_MARKDOWN, {
+    return self.rest(REST_API_MARKDOWN, {
       'text': markdown,
       'mode': 'gfm',
       'context': '%s/%s' % (owner, repo),
@@ -480,7 +485,6 @@ def _create(self, obj, preview=None, include_link=source.OMIT_LINK,
       If preview is True, the contents will be a unicode string HTML
       snippet. If False, it will be a dict with 'id' and 'url' keys
       for the newly created GitHub object.
-
     """
     assert preview in (False, True)
 
@@ -609,11 +613,14 @@ def _create(self, obj, preview=None, include_link=source.OMIT_LINK,
           })
 
         else:
-          resp = self.graphql(GRAPHQL_ADD_COMMENT, {
-            'subject_id': issue['id'],
-            'body': content,
-          })
-          return source.creation_result(resp['addComment']['commentEdge']['node'])
+          try:
+            resp = self.graphql(GRAPHQL_ADD_COMMENT, {
+              'subject_id': issue['id'],
+              'body': content,
+            })
+            return source.creation_result(resp['addComment']['commentEdge']['node'])
+          except ValueError as e:
+            return source.creation_result(abort=True, error_plain=unicode(e))
 
     return source.creation_result(
       abort=False,
diff --git a/granary/test/test_github.py b/granary/test/test_github.py
index ec2f480d..2855b635 100644
--- a/granary/test/test_github.py
+++ b/granary/test/test_github.py
@@ -795,6 +795,40 @@ def test_preview_issue_tags_to_labels(self):
       preview.description, preview)
 
   def test_create_comment_without_in_reply_to(self):
+    msg = 'Although you appear to have the correct authorization credentials,\nthe `whatwg` organization has enabled OAuth App access restrictions, meaning that data\naccess to third-parties is limited. For more information on these restrictions, including\nhow to whitelist this app, visit\nhttps://help.github.com/articles/restricting-access-to-your-organization-s-data/\n'
+
+    self.expect_graphql_issue()
+    self.expect_requests_post(
+      GRAPHQL_BASE,
+      headers={'Authorization': 'bearer a-towkin'},
+      json={
+        'query': github.GRAPHQL_ADD_COMMENT % {
+          'subject_id': ISSUE_GRAPHQL['id'],
+          'body': COMMENT_OBJ['content'],
+        },
+      },
+      # status_code=403,
+      response={
+        'errors': [
+          {
+            'path': ['addComment'],
+            'message': msg,
+            'type': 'FORBIDDEN',
+            'locations': [{'column': 3, 'line': 3}],
+          },
+        ],
+        'data': {
+          'addComment': None,
+        },
+      })
+    self.mox.ReplayAll()
+
+    result = self.gh.create(COMMENT_OBJ)
+    self.assertTrue(result.abort)
+    self.assertEquals(msg, result.error_plain)
+
+  def test_create_comment_org_access_forbidden(self):
+    """https://github.com/snarfed/bridgy/issues/824"""
     obj = copy.deepcopy(COMMENT_OBJ)
     obj['inReplyTo'] = [{'url': 'http://foo.com/bar'}]