Repository: bradshjg/flask-githubapp

Issue:
Issue when payloads have no action
Gitapps based on this that want to do something with push payloads (for instance) will fail here:

https://github.com/bradshjg/flask-githubapp/blob/master/flask_githubapp/core.py#L170

I know because I have just suffered this! :D 
A simple change could be, I think, to do a get with a default value of None
`action = request.json.get('action', None)`

Discussion:

Patch:
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 33b65fe..4fabdfb 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -3,7 +3,9 @@
 
 name: PyPI release
 
-on: [release]
+on:
+  release:
+    types: [published]
 
 jobs:
   publish:
diff --git a/flask_githubapp/core.py b/flask_githubapp/core.py
index 34af98f..3072a87 100644
--- a/flask_githubapp/core.py
+++ b/flask_githubapp/core.py
@@ -2,9 +2,9 @@
 import hmac
 import logging
 
-from flask import abort, current_app, jsonify, request, _app_ctx_stack
+from flask import abort, current_app, jsonify, make_response, request, _app_ctx_stack
 from github3 import GitHub, GitHubEnterprise
-
+from werkzeug.exceptions import BadRequest
 
 LOG = logging.getLogger(__name__)
 
@@ -167,6 +167,23 @@ class GitHubApp(object):
         functions_to_call = []
         calls = {}
 
+        if not request.is_json:
+            error_message = 'Invalid HTTP Content-Type header for JSON body ' \
+                            '(must be application/json or application/*+json).'
+            LOG.error(error_message)
+            error_response = make_response(jsonify(status='ERROR', description=error_message),
+                                           400)
+            abort(error_response)
+
+        try:
+            request.json
+        except BadRequest:
+            error_message = 'Invalid HTTP body (must be JSON).'
+            LOG.error(error_message)
+            error_response = make_response(jsonify(status='ERROR', description=error_message),
+                                           400)
+            abort(error_response)
+
         event = request.headers['X-GitHub-Event']
         action = request.json.get('action')
 


Test patch:
diff --git a/tests/test_core.py b/tests/test_core.py
index 326cbdb..593c568 100644
--- a/tests/test_core.py
+++ b/tests/test_core.py
@@ -242,3 +242,37 @@ def test_view_returns_map_of_called_functions_and_returned_data(app, mocker):
                 'event_action_function': 'bar',
             }
         }
+
+def test_invalid_json_header_returns_error(github_app):
+    """HTTP request have a valid json content type"""
+    github_app.config['GITHUBAPP_SECRET'] = False
+    with github_app.test_client() as client:
+        resp = client.post('/',
+                           data=json.dumps({'installation': {'id': 2},
+                                            'action': 'bar'}),
+                           headers={
+                              'X-GitHub-Event': 'foo',
+                              'Content-Type': 'text/plain'
+                           })
+        assert resp.status_code == 400
+        assert resp.json == {
+            'status': 'ERROR',
+            'description': 'Invalid HTTP Content-Type header for JSON body '
+                           '(must be application/json or application/*+json).'
+        }
+
+def test_invalid_json_body_returns_error(github_app):
+    """HTTP request have a valid json body"""
+    github_app.config['GITHUBAPP_SECRET'] = False
+    with github_app.test_client() as client:
+        resp = client.post('/',
+                           data='invalid json',
+                           headers={
+                              'X-GitHub-Event': 'foo',
+                              'Content-Type': 'application/json'
+                           })
+        assert resp.status_code == 400
+        assert resp.json == {
+            'status': 'ERROR',
+            'description': 'Invalid HTTP body (must be JSON).'
+        }