Repository: zulip/zulip

Issue:
GitHub webhook may receive a pull_request_review event with null review.body
`zerver.webhooks.github.view.get_pull_request_review_body` requires `review.body` as `string` in `pull_request_review` event:
https://github.com/zulip/zulip/blob/92c83c1df489975f8fe887f190e5d0af7c9534a2/zerver/webhooks/github/view.py#L504

But GitHub may send a `pull_request_review` event with `null` `review.body`. Here is an example event from https://github.com/groonga/groonga :

<details>

```json
{
  "action": "submitted",
  "review": {
    "id": 1483047907,
    "node_id": "PRR_kwDOAAGiws5YZYPj",
    "user": {
      "login": "kou",
      "id": 27350,
      "node_id": "MDQ6VXNlcjI3MzUw",
      "avatar_url": "https://avatars.githubusercontent.com/u/27350?v=4",
      "gravatar_id": "",
      "url": "https://api.github.com/users/kou",
      "html_url": "https://github.com/kou",
      "followers_url": "https://api.github.com/users/kou/followers",
      "following_url": "https://api.github.com/users/kou/following{/other_user}",
      "gists_url": "https://api.github.com/users/kou/gists{/gist_id}",
      "starred_url": "https://api.github.com/users/kou/starred{/owner}{/repo}",
      "subscriptions_url": "https://api.github.com/users/kou/subscriptions",
      "organizations_url": "https://api.github.com/users/kou/orgs",
      "repos_url": "https://api.github.com/users/kou/repos",
      "events_url": "https://api.github.com/users/kou/events{/privacy}",
      "received_events_url": "https://api.github.com/users/kou/received_events",
      "type": "User",
      "site_admin": false
    },
    "body": null,
    "commit_id": "db8dc428a0338f68b264eaf634a9b0c5ef6c07bb",
    "submitted_at": "2023-06-16T09:03:40Z",
    "state": "commented",
    "html_url": "https://github.com/groonga/groonga/pull/1581#pullrequestreview-1483047907",
    "pull_request_url": "https://api.github.com/repos/groonga/groonga/pulls/1581",
    "author_association": "MEMBER",
    "_links": {
      "html": {
        "href": "https://github.com/groonga/groonga/pull/1581#pullrequestreview-1483047907"
      },
      "pull_request": {
        "href": "https://api.github.com/repos/groonga/groonga/pulls/1581"
      }
    }
  },
  "pull_request": {
    "url": "https://api.github.com/repos/groonga/groonga/pulls/1581",
    "id": 1395030627,
    "node_id": "PR_kwDOAAGiws5TJnpj",
    "html_url": "https://github.com/groonga/groonga/pull/1581",
    "diff_url": "https://github.com/groonga/groonga/pull/1581.diff",
    "patch_url": "https://github.com/groonga/groonga/pull/1581.patch",
    "issue_url": "https://api.github.com/repos/groonga/groonga/issues/1581",
    "number": 1581,
    "state": "open",
    "locked": false,
    "title": "grn_db_value_lock: unlock GRN_TYPE object or GRN_EXPR object even if reference count enable",
    "user": {
      "login": "komainu8",
      "id": 19166236,
      "node_id": "MDQ6VXNlcjE5MTY2MjM2",
      "avatar_url": "https://avatars.githubusercontent.com/u/19166236?v=4",
      "gravatar_id": "",
      "url": "https://api.github.com/users/komainu8",
      "html_url": "https://github.com/komainu8",
      "followers_url": "https://api.github.com/users/komainu8/followers",
      "following_url": "https://api.github.com/users/komainu8/following{/other_user}",
      "gists_url": "https://api.github.com/users/komainu8/gists{/gist_id}",
      "starred_url": "https://api.github.com/users/komainu8/starred{/owner}{/repo}",
      "subscriptions_url": "https://api.github.com/users/komainu8/subscriptions",
      "organizations_url": "https://api.github.com/users/komainu8/orgs",
      "repos_url": "https://api.github.com/users/komainu8/repos",
      "events_url": "https://api.github.com/users/komainu8/events{/privacy}",
      "received_events_url": "https://api.github.com/users/komainu8/received_events",
      "type": "User",
      "site_admin": false
    },
    "body": "If reference count enable, also GRN_TYPE object and GRN_EXPR object maintain lock. However, if we open many object, lock count may overflow.\r\n\r\nGRN_TYPE object and GRN_EXPR don't need to maintain lock. Because There are not target of reference count.",
    "created_at": "2023-06-16T03:33:36Z",
    "updated_at": "2023-06-16T09:03:40Z",
    "closed_at": null,
    "merged_at": null,
    "merge_commit_sha": "dd38812e7f27a2bdb6c7d76bb274bab863b910dd",
    "assignee": null,
    "assignees": [

    ],
    "requested_reviewers": [

    ],
    "requested_teams": [

    ],
    "labels": [

    ],
    "milestone": null,
    "draft": false,
    "commits_url": "https://api.github.com/repos/groonga/groonga/pulls/1581/commits",
    "review_comments_url": "https://api.github.com/repos/groonga/groonga/pulls/1581/comments",
    "review_comment_url": "https://api.github.com/repos/groonga/groonga/pulls/comments{/number}",
    "comments_url": "https://api.github.com/repos/groonga/groonga/issues/1581/comments",
    "statuses_url": "https://api.github.com/repos/groonga/groonga/statuses/db8dc428a0338f68b264eaf634a9b0c5ef6c07bb",
    "head": {
      "label": "groonga:release-lock-type-and-expr",
      "ref": "release-lock-type-and-expr",
      "sha": "db8dc428a0338f68b264eaf634a9b0c5ef6c07bb",
      "user": {
        "login": "groonga",
        "id": 46466,
        "node_id": "MDEyOk9yZ2FuaXphdGlvbjQ2NDY2",
        "avatar_url": "https://avatars.githubusercontent.com/u/46466?v=4",
        "gravatar_id": "",
        "url": "https://api.github.com/users/groonga",
        "html_url": "https://github.com/groonga",
        "followers_url": "https://api.github.com/users/groonga/followers",
        "following_url": "https://api.github.com/users/groonga/following{/other_user}",
        "gists_url": "https://api.github.com/users/groonga/gists{/gist_id}",
        "starred_url": "https://api.github.com/users/groonga/starred{/owner}{/repo}",
        "subscriptions_url": "https://api.github.com/users/groonga/subscriptions",
        "organizations_url": "https://api.github.com/users/groonga/orgs",
        "repos_url": "https://api.github.com/users/groonga/repos",
        "events_url": "https://api.github.com/users/groonga/events{/privacy}",
        "received_events_url": "https://api.github.com/users/groonga/received_events",
        "type": "Organization",
        "site_admin": false
      },
      "repo": {
        "id": 107202,
        "node_id": "MDEwOlJlcG9zaXRvcnkxMDcyMDI=",
        "name": "groonga",
        "full_name": "groonga/groonga",
        "private": false,
        "owner": {
          "login": "groonga",
          "id": 46466,
          "node_id": "MDEyOk9yZ2FuaXphdGlvbjQ2NDY2",
          "avatar_url": "https://avatars.githubusercontent.com/u/46466?v=4",
          "gravatar_id": "",
          "url": "https://api.github.com/users/groonga",
          "html_url": "https://github.com/groonga",
          "followers_url": "https://api.github.com/users/groonga/followers",
          "following_url": "https://api.github.com/users/groonga/following{/other_user}",
          "gists_url": "https://api.github.com/users/groonga/gists{/gist_id}",
          "starred_url": "https://api.github.com/users/groonga/starred{/owner}{/repo}",
          "subscriptions_url": "https://api.github.com/users/groonga/subscriptions",
          "organizations_url": "https://api.github.com/users/groonga/orgs",
          "repos_url": "https://api.github.com/users/groonga/repos",
          "events_url": "https://api.github.com/users/groonga/events{/privacy}",
          "received_events_url": "https://api.github.com/users/groonga/received_events",
          "type": "Organization",
          "site_admin": false
        },
        "html_url": "https://github.com/groonga/groonga",
        "description": "An embeddable fulltext search engine. Groonga is the successor project to Senna.",
        "fork": false,
        "url": "https://api.github.com/repos/groonga/groonga",
        "forks_url": "https://api.github.com/repos/groonga/groonga/forks",
        "keys_url": "https://api.github.com/repos/groonga/groonga/keys{/key_id}",
        "collaborators_url": "https://api.github.com/repos/groonga/groonga/collaborators{/collaborator}",
        "teams_url": "https://api.github.com/repos/groonga/groonga/teams",
        "hooks_url": "https://api.github.com/repos/groonga/groonga/hooks",
        "issue_events_url": "https://api.github.com/repos/groonga/groonga/issues/events{/number}",
        "events_url": "https://api.github.com/repos/groonga/groonga/events",
        "assignees_url": "https://api.github.com/repos/groonga/groonga/assignees{/user}",
        "branches_url": "https://api.github.com/repos/groonga/groonga/branches{/branch}",
        "tags_url": "https://api.github.com/repos/groonga/groonga/tags",
        "blobs_url": "https://api.github.com/repos/groonga/groonga/git/blobs{/sha}",
        "git_tags_url": "https://api.github.com/repos/groonga/groonga/git/tags{/sha}",
        "git_refs_url": "https://api.github.com/repos/groonga/groonga/git/refs{/sha}",
        "trees_url": "https://api.github.com/repos/groonga/groonga/git/trees{/sha}",
        "statuses_url": "https://api.github.com/repos/groonga/groonga/statuses/{sha}",
        "languages_url": "https://api.github.com/repos/groonga/groonga/languages",
        "stargazers_url": "https://api.github.com/repos/groonga/groonga/stargazers",
        "contributors_url": "https://api.github.com/repos/groonga/groonga/contributors",
        "subscribers_url": "https://api.github.com/repos/groonga/groonga/subscribers",
        "subscription_url": "https://api.github.com/repos/groonga/groonga/subscription",
        "commits_url": "https://api.github.com/repos/groonga/groonga/commits{/sha}",
        "git_commits_url": "https://api.github.com/repos/groonga/groonga/git/commits{/sha}",
        "comments_url": "https://api.github.com/repos/groonga/groonga/comments{/number}",
        "issue_comment_url": "https://api.github.com/repos/groonga/groonga/issues/comments{/number}",
        "contents_url": "https://api.github.com/repos/groonga/groonga/contents/{+path}",
        "compare_url": "https://api.github.com/repos/groonga/groonga/compare/{base}...{head}",
        "merges_url": "https://api.github.com/repos/groonga/groonga/merges",
        "archive_url": "https://api.github.com/repos/groonga/groonga/{archive_format}{/ref}",
        "downloads_url": "https://api.github.com/repos/groonga/groonga/downloads",
        "issues_url": "https://api.github.com/repos/groonga/groonga/issues{/number}",
        "pulls_url": "https://api.github.com/repos/groonga/groonga/pulls{/number}",
        "milestones_url": "https://api.github.com/repos/groonga/groonga/milestones{/number}",
        "notifications_url": "https://api.github.com/repos/groonga/groonga/notifications{?since,all,participating}",
        "labels_url": "https://api.github.com/repos/groonga/groonga/labels{/name}",
        "releases_url": "https://api.github.com/repos/groonga/groonga/releases{/id}",
        "deployments_url": "https://api.github.com/repos/groonga/groonga/deployments",
        "created_at": "2009-01-14T09:30:34Z",
        "updated_at": "2023-06-12T17:35:09Z",
        "pushed_at": "2023-06-16T08:49:51Z",
        "git_url": "git://github.com/groonga/groonga.git",
        "ssh_url": "git@github.com:groonga/groonga.git",
        "clone_url": "https://github.com/groonga/groonga.git",
        "svn_url": "https://github.com/groonga/groonga",
        "homepage": "https://groonga.org/",
        "size": 97933,
        "stargazers_count": 736,
        "watchers_count": 736,
        "language": "C",
        "has_issues": true,
        "has_projects": true,
        "has_downloads": true,
        "has_wiki": true,
        "has_pages": false,
        "has_discussions": true,
        "forks_count": 116,
        "mirror_url": null,
        "archived": false,
        "disabled": false,
        "open_issues_count": 137,
        "license": {
          "key": "lgpl-2.1",
          "name": "GNU Lesser General Public License v2.1",
          "spdx_id": "LGPL-2.1",
          "url": "https://api.github.com/licenses/lgpl-2.1",
          "node_id": "MDc6TGljZW5zZTEx"
        },
        "allow_forking": true,
        "is_template": false,
        "web_commit_signoff_required": false,
        "topics": [
          "c",
          "full-text-search",
          "groonga",
          "mruby",
          "nginx"
        ],
        "visibility": "public",
        "forks": 116,
        "open_issues": 137,
        "watchers": 736,
        "default_branch": "master",
        "allow_squash_merge": true,
        "allow_merge_commit": false,
        "allow_rebase_merge": false,
        "allow_auto_merge": true,
        "delete_branch_on_merge": true,
        "allow_update_branch": false,
        "use_squash_pr_title_as_default": true,
        "squash_merge_commit_message": "PR_BODY",
        "squash_merge_commit_title": "PR_TITLE",
        "merge_commit_message": "PR_TITLE",
        "merge_commit_title": "MERGE_MESSAGE"
      }
    },
    "base": {
      "label": "groonga:master",
      "ref": "master",
      "sha": "3c2132fa2c253e7d0e81521be31c3d0905138a09",
      "user": {
        "login": "groonga",
        "id": 46466,
        "node_id": "MDEyOk9yZ2FuaXphdGlvbjQ2NDY2",
        "avatar_url": "https://avatars.githubusercontent.com/u/46466?v=4",
        "gravatar_id": "",
        "url": "https://api.github.com/users/groonga",
        "html_url": "https://github.com/groonga",
        "followers_url": "https://api.github.com/users/groonga/followers",
        "following_url": "https://api.github.com/users/groonga/following{/other_user}",
        "gists_url": "https://api.github.com/users/groonga/gists{/gist_id}",
        "starred_url": "https://api.github.com/users/groonga/starred{/owner}{/repo}",
        "subscriptions_url": "https://api.github.com/users/groonga/subscriptions",
        "organizations_url": "https://api.github.com/users/groonga/orgs",
        "repos_url": "https://api.github.com/users/groonga/repos",
        "events_url": "https://api.github.com/users/groonga/events{/privacy}",
        "received_events_url": "https://api.github.com/users/groonga/received_events",
        "type": "Organization",
        "site_admin": false
      },
      "repo": {
        "id": 107202,
        "node_id": "MDEwOlJlcG9zaXRvcnkxMDcyMDI=",
        "name": "groonga",
        "full_name": "groonga/groonga",
        "private": false,
        "owner": {
          "login": "groonga",
          "id": 46466,
          "node_id": "MDEyOk9yZ2FuaXphdGlvbjQ2NDY2",
          "avatar_url": "https://avatars.githubusercontent.com/u/46466?v=4",
          "gravatar_id": "",
          "url": "https://api.github.com/users/groonga",
          "html_url": "https://github.com/groonga",
          "followers_url": "https://api.github.com/users/groonga/followers",
          "following_url": "https://api.github.com/users/groonga/following{/other_user}",
          "gists_url": "https://api.github.com/users/groonga/gists{/gist_id}",
          "starred_url": "https://api.github.com/users/groonga/starred{/owner}{/repo}",
          "subscriptions_url": "https://api.github.com/users/groonga/subscriptions",
          "organizations_url": "https://api.github.com/users/groonga/orgs",
          "repos_url": "https://api.github.com/users/groonga/repos",
          "events_url": "https://api.github.com/users/groonga/events{/privacy}",
          "received_events_url": "https://api.github.com/users/groonga/received_events",
          "type": "Organization",
          "site_admin": false
        },
        "html_url": "https://github.com/groonga/groonga",
        "description": "An embeddable fulltext search engine. Groonga is the successor project to Senna.",
        "fork": false,
        "url": "https://api.github.com/repos/groonga/groonga",
        "forks_url": "https://api.github.com/repos/groonga/groonga/forks",
        "keys_url": "https://api.github.com/repos/groonga/groonga/keys{/key_id}",
        "collaborators_url": "https://api.github.com/repos/groonga/groonga/collaborators{/collaborator}",
        "teams_url": "https://api.github.com/repos/groonga/groonga/teams",
        "hooks_url": "https://api.github.com/repos/groonga/groonga/hooks",
        "issue_events_url": "https://api.github.com/repos/groonga/groonga/issues/events{/number}",
        "events_url": "https://api.github.com/repos/groonga/groonga/events",
        "assignees_url": "https://api.github.com/repos/groonga/groonga/assignees{/user}",
        "branches_url": "https://api.github.com/repos/groonga/groonga/branches{/branch}",
        "tags_url": "https://api.github.com/repos/groonga/groonga/tags",
        "blobs_url": "https://api.github.com/repos/groonga/groonga/git/blobs{/sha}",
        "git_tags_url": "https://api.github.com/repos/groonga/groonga/git/tags{/sha}",
        "git_refs_url": "https://api.github.com/repos/groonga/groonga/git/refs{/sha}",
        "trees_url": "https://api.github.com/repos/groonga/groonga/git/trees{/sha}",
        "statuses_url": "https://api.github.com/repos/groonga/groonga/statuses/{sha}",
        "languages_url": "https://api.github.com/repos/groonga/groonga/languages",
        "stargazers_url": "https://api.github.com/repos/groonga/groonga/stargazers",
        "contributors_url": "https://api.github.com/repos/groonga/groonga/contributors",
        "subscribers_url": "https://api.github.com/repos/groonga/groonga/subscribers",
        "subscription_url": "https://api.github.com/repos/groonga/groonga/subscription",
        "commits_url": "https://api.github.com/repos/groonga/groonga/commits{/sha}",
        "git_commits_url": "https://api.github.com/repos/groonga/groonga/git/commits{/sha}",
        "comments_url": "https://api.github.com/repos/groonga/groonga/comments{/number}",
        "issue_comment_url": "https://api.github.com/repos/groonga/groonga/issues/comments{/number}",
        "contents_url": "https://api.github.com/repos/groonga/groonga/contents/{+path}",
        "compare_url": "https://api.github.com/repos/groonga/groonga/compare/{base}...{head}",
        "merges_url": "https://api.github.com/repos/groonga/groonga/merges",
        "archive_url": "https://api.github.com/repos/groonga/groonga/{archive_format}{/ref}",
        "downloads_url": "https://api.github.com/repos/groonga/groonga/downloads",
        "issues_url": "https://api.github.com/repos/groonga/groonga/issues{/number}",
        "pulls_url": "https://api.github.com/repos/groonga/groonga/pulls{/number}",
        "milestones_url": "https://api.github.com/repos/groonga/groonga/milestones{/number}",
        "notifications_url": "https://api.github.com/repos/groonga/groonga/notifications{?since,all,participating}",
        "labels_url": "https://api.github.com/repos/groonga/groonga/labels{/name}",
        "releases_url": "https://api.github.com/repos/groonga/groonga/releases{/id}",
        "deployments_url": "https://api.github.com/repos/groonga/groonga/deployments",
        "created_at": "2009-01-14T09:30:34Z",
        "updated_at": "2023-06-12T17:35:09Z",
        "pushed_at": "2023-06-16T08:49:51Z",
        "git_url": "git://github.com/groonga/groonga.git",
        "ssh_url": "git@github.com:groonga/groonga.git",
        "clone_url": "https://github.com/groonga/groonga.git",
        "svn_url": "https://github.com/groonga/groonga",
        "homepage": "https://groonga.org/",
        "size": 97933,
        "stargazers_count": 736,
        "watchers_count": 736,
        "language": "C",
        "has_issues": true,
        "has_projects": true,
        "has_downloads": true,
        "has_wiki": true,
        "has_pages": false,
        "has_discussions": true,
        "forks_count": 116,
        "mirror_url": null,
        "archived": false,
        "disabled": false,
        "open_issues_count": 137,
        "license": {
          "key": "lgpl-2.1",
          "name": "GNU Lesser General Public License v2.1",
          "spdx_id": "LGPL-2.1",
          "url": "https://api.github.com/licenses/lgpl-2.1",
          "node_id": "MDc6TGljZW5zZTEx"
        },
        "allow_forking": true,
        "is_template": false,
        "web_commit_signoff_required": false,
        "topics": [
          "c",
          "full-text-search",
          "groonga",
          "mruby",
          "nginx"
        ],
        "visibility": "public",
        "forks": 116,
        "open_issues": 137,
        "watchers": 736,
        "default_branch": "master",
        "allow_squash_merge": true,
        "allow_merge_commit": false,
        "allow_rebase_merge": false,
        "allow_auto_merge": true,
        "delete_branch_on_merge": true,
        "allow_update_branch": false,
        "use_squash_pr_title_as_default": true,
        "squash_merge_commit_message": "PR_BODY",
        "squash_merge_commit_title": "PR_TITLE",
        "merge_commit_message": "PR_TITLE",
        "merge_commit_title": "MERGE_MESSAGE"
      }
    },
    "_links": {
      "self": {
        "href": "https://api.github.com/repos/groonga/groonga/pulls/1581"
      },
      "html": {
        "href": "https://github.com/groonga/groonga/pull/1581"
      },
      "issue": {
        "href": "https://api.github.com/repos/groonga/groonga/issues/1581"
      },
      "comments": {
        "href": "https://api.github.com/repos/groonga/groonga/issues/1581/comments"
      },
      "review_comments": {
        "href": "https://api.github.com/repos/groonga/groonga/pulls/1581/comments"
      },
      "review_comment": {
        "href": "https://api.github.com/repos/groonga/groonga/pulls/comments{/number}"
      },
      "commits": {
        "href": "https://api.github.com/repos/groonga/groonga/pulls/1581/commits"
      },
      "statuses": {
        "href": "https://api.github.com/repos/groonga/groonga/statuses/db8dc428a0338f68b264eaf634a9b0c5ef6c07bb"
      }
    },
    "author_association": "MEMBER",
    "auto_merge": null,
    "active_lock_reason": null
  },
  "repository": {
    "id": 107202,
    "node_id": "MDEwOlJlcG9zaXRvcnkxMDcyMDI=",
    "name": "groonga",
    "full_name": "groonga/groonga",
    "private": false,
    "owner": {
      "login": "groonga",
      "id": 46466,
      "node_id": "MDEyOk9yZ2FuaXphdGlvbjQ2NDY2",
      "avatar_url": "https://avatars.githubusercontent.com/u/46466?v=4",
      "gravatar_id": "",
      "url": "https://api.github.com/users/groonga",
      "html_url": "https://github.com/groonga",
      "followers_url": "https://api.github.com/users/groonga/followers",
      "following_url": "https://api.github.com/users/groonga/following{/other_user}",
      "gists_url": "https://api.github.com/users/groonga/gists{/gist_id}",
      "starred_url": "https://api.github.com/users/groonga/starred{/owner}{/repo}",
      "subscriptions_url": "https://api.github.com/users/groonga/subscriptions",
      "organizations_url": "https://api.github.com/users/groonga/orgs",
      "repos_url": "https://api.github.com/users/groonga/repos",
      "events_url": "https://api.github.com/users/groonga/events{/privacy}",
      "received_events_url": "https://api.github.com/users/groonga/received_events",
      "type": "Organization",
      "site_admin": false
    },
    "html_url": "https://github.com/groonga/groonga",
    "description": "An embeddable fulltext search engine. Groonga is the successor project to Senna.",
    "fork": false,
    "url": "https://api.github.com/repos/groonga/groonga",
    "forks_url": "https://api.github.com/repos/groonga/groonga/forks",
    "keys_url": "https://api.github.com/repos/groonga/groonga/keys{/key_id}",
    "collaborators_url": "https://api.github.com/repos/groonga/groonga/collaborators{/collaborator}",
    "teams_url": "https://api.github.com/repos/groonga/groonga/teams",
    "hooks_url": "https://api.github.com/repos/groonga/groonga/hooks",
    "issue_events_url": "https://api.github.com/repos/groonga/groonga/issues/events{/number}",
    "events_url": "https://api.github.com/repos/groonga/groonga/events",
    "assignees_url": "https://api.github.com/repos/groonga/groonga/assignees{/user}",
    "branches_url": "https://api.github.com/repos/groonga/groonga/branches{/branch}",
    "tags_url": "https://api.github.com/repos/groonga/groonga/tags",
    "blobs_url": "https://api.github.com/repos/groonga/groonga/git/blobs{/sha}",
    "git_tags_url": "https://api.github.com/repos/groonga/groonga/git/tags{/sha}",
    "git_refs_url": "https://api.github.com/repos/groonga/groonga/git/refs{/sha}",
    "trees_url": "https://api.github.com/repos/groonga/groonga/git/trees{/sha}",
    "statuses_url": "https://api.github.com/repos/groonga/groonga/statuses/{sha}",
    "languages_url": "https://api.github.com/repos/groonga/groonga/languages",
    "stargazers_url": "https://api.github.com/repos/groonga/groonga/stargazers",
    "contributors_url": "https://api.github.com/repos/groonga/groonga/contributors",
    "subscribers_url": "https://api.github.com/repos/groonga/groonga/subscribers",
    "subscription_url": "https://api.github.com/repos/groonga/groonga/subscription",
    "commits_url": "https://api.github.com/repos/groonga/groonga/commits{/sha}",
    "git_commits_url": "https://api.github.com/repos/groonga/groonga/git/commits{/sha}",
    "comments_url": "https://api.github.com/repos/groonga/groonga/comments{/number}",
    "issue_comment_url": "https://api.github.com/repos/groonga/groonga/issues/comments{/number}",
    "contents_url": "https://api.github.com/repos/groonga/groonga/contents/{+path}",
    "compare_url": "https://api.github.com/repos/groonga/groonga/compare/{base}...{head}",
    "merges_url": "https://api.github.com/repos/groonga/groonga/merges",
    "archive_url": "https://api.github.com/repos/groonga/groonga/{archive_format}{/ref}",
    "downloads_url": "https://api.github.com/repos/groonga/groonga/downloads",
    "issues_url": "https://api.github.com/repos/groonga/groonga/issues{/number}",
    "pulls_url": "https://api.github.com/repos/groonga/groonga/pulls{/number}",
    "milestones_url": "https://api.github.com/repos/groonga/groonga/milestones{/number}",
    "notifications_url": "https://api.github.com/repos/groonga/groonga/notifications{?since,all,participating}",
    "labels_url": "https://api.github.com/repos/groonga/groonga/labels{/name}",
    "releases_url": "https://api.github.com/repos/groonga/groonga/releases{/id}",
    "deployments_url": "https://api.github.com/repos/groonga/groonga/deployments",
    "created_at": "2009-01-14T09:30:34Z",
    "updated_at": "2023-06-12T17:35:09Z",
    "pushed_at": "2023-06-16T08:49:51Z",
    "git_url": "git://github.com/groonga/groonga.git",
    "ssh_url": "git@github.com:groonga/groonga.git",
    "clone_url": "https://github.com/groonga/groonga.git",
    "svn_url": "https://github.com/groonga/groonga",
    "homepage": "https://groonga.org/",
    "size": 97933,
    "stargazers_count": 736,
    "watchers_count": 736,
    "language": "C",
    "has_issues": true,
    "has_projects": true,
    "has_downloads": true,
    "has_wiki": true,
    "has_pages": false,
    "has_discussions": true,
    "forks_count": 116,
    "mirror_url": null,
    "archived": false,
    "disabled": false,
    "open_issues_count": 137,
    "license": {
      "key": "lgpl-2.1",
      "name": "GNU Lesser General Public License v2.1",
      "spdx_id": "LGPL-2.1",
      "url": "https://api.github.com/licenses/lgpl-2.1",
      "node_id": "MDc6TGljZW5zZTEx"
    },
    "allow_forking": true,
    "is_template": false,
    "web_commit_signoff_required": false,
    "topics": [
      "c",
      "full-text-search",
      "groonga",
      "mruby",
      "nginx"
    ],
    "visibility": "public",
    "forks": 116,
    "open_issues": 137,
    "watchers": 736,
    "default_branch": "master"
  },
  "organization": {
    "login": "groonga",
    "id": 46466,
    "node_id": "MDEyOk9yZ2FuaXphdGlvbjQ2NDY2",
    "url": "https://api.github.com/orgs/groonga",
    "repos_url": "https://api.github.com/orgs/groonga/repos",
    "events_url": "https://api.github.com/orgs/groonga/events",
    "hooks_url": "https://api.github.com/orgs/groonga/hooks",
    "issues_url": "https://api.github.com/orgs/groonga/issues",
    "members_url": "https://api.github.com/orgs/groonga/members{/member}",
    "public_members_url": "https://api.github.com/orgs/groonga/public_members{/member}",
    "avatar_url": "https://avatars.githubusercontent.com/u/46466?v=4",
    "description": ""
  },
  "sender": {
    "login": "kou",
    "id": 27350,
    "node_id": "MDQ6VXNlcjI3MzUw",
    "avatar_url": "https://avatars.githubusercontent.com/u/27350?v=4",
    "gravatar_id": "",
    "url": "https://api.github.com/users/kou",
    "html_url": "https://github.com/kou",
    "followers_url": "https://api.github.com/users/kou/followers",
    "following_url": "https://api.github.com/users/kou/following{/other_user}",
    "gists_url": "https://api.github.com/users/kou/gists{/gist_id}",
    "starred_url": "https://api.github.com/users/kou/starred{/owner}{/repo}",
    "subscriptions_url": "https://api.github.com/users/kou/subscriptions",
    "organizations_url": "https://api.github.com/users/kou/orgs",
    "repos_url": "https://api.github.com/users/kou/repos",
    "events_url": "https://api.github.com/users/kou/events{/privacy}",
    "received_events_url": "https://api.github.com/users/kou/received_events",
    "type": "User",
    "site_admin": false
  }
}
```

</details>

If Zulip receives a `pull_request_review` event with `null` `review.body`, the following error e-mails are sent:

```text
Subject: [Django] zulip: Traceback (most recent call last):

Logger zerver.middleware.json_error_handler, from module zerver.middleware line 432:
Error generated by GitHub Bot <github-bot@zulip.example.com> (Member) on zulip.example.com deployment

Traceback (most recent call last):
  File "/srv/zulip-venv-cache/91d4c5430487dabd51febcce463a8e3f8c67888e/zulip-py3-venv/lib/python3.10/site-packages/django/core/handlers/base.py", line 197, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/srv/zulip-venv-cache/91d4c5430487dabd51febcce463a8e3f8c67888e/zulip-py3-venv/lib/python3.10/site-packages/django/views/decorators/csrf.py", line 56, in wrapper_view
    return view_func(*args, **kwargs)
  File "/home/zulip/deployments/2023-06-01-18-44-07/./zerver/lib/request.py", line 459, in _wrapped_req_func
    return_value = req_func(request, *args, **kwargs)
  File "/home/zulip/deployments/2023-06-01-18-44-07/./zerver/decorator.py", line 370, in _wrapped_func_arguments
    raise err
  File "/home/zulip/deployments/2023-06-01-18-44-07/./zerver/decorator.py", line 355, in _wrapped_func_arguments
    return view_func(request, user_profile, *args, **kwargs)
  File "/home/zulip/deployments/2023-06-01-18-44-07/./zerver/lib/request.py", line 459, in _wrapped_req_func
    return_value = req_func(request, *args, **kwargs)
  File "/home/zulip/deployments/2023-06-01-18-44-07/./zerver/webhooks/github/view.py", line 800, in api_github_webhook
    body = body_function(helper)
  File "/home/zulip/deployments/2023-06-01-18-44-07/./zerver/webhooks/github/view.py", line 504, in get_pull_request_review_body
    message=payload["review"]["body"].tame(check_string),
  File "/home/zulip/deployments/2023-06-01-18-44-07/./zerver/lib/validator.py", line 682, in tame
    return validator(self.var_name, self.value)
  File "/home/zulip/deployments/2023-06-01-18-44-07/./zerver/lib/validator.py", line 76, in check_string
    raise ValidationError(_("{var_name} is not a string").format(var_name=var_name))
django.core.exceptions.ValidationError: ["request['review']['body'] is not a string"]


Deployed code:
- git: None
- ZULIP_VERSION: 7.0


Request info:
- path: /api/v1/external/github
- POST: {}
- REMOTE_ADDR: "..."
- QUERY_STRING: "..."
- SERVER_NAME: ""
```

```text
Subject: [Django] zulip: Internal Server Error: /api/v1/external/github

Logger django.request, from module django.utils.log line 241:
Error generated by GitHub Bot <github-bot@zulip.example.com> (Member) on zulip.example.com deployment

No stack trace available

Deployed code:
- git: None
- ZULIP_VERSION: 7.0


Request info:
- path: /api/v1/external/github
- POST: {}
- REMOTE_ADDR: "..."
- QUERY_STRING: "..."
- SERVER_NAME: ""
```

**Zulip Server and web app version:**

- [ ] Zulip Cloud (`*.zulipchat.com`)
- [x] Zulip Server 7.0+
- [ ] Zulip Server 6.0+
- [ ] Zulip Server 5.0 or older
- [ ] Other or not sure



Discussion:
Hello @zulip/server-integrations members, this issue was labeled with the "area: integrations" label, so you may want to check it out!

<!-- areaLabelAddition -->

@sbansal1999 - Mentioning you here because I recall that you've been doing some work related to the Github integrations recently.
@laurynmm Thanks for mentioning me.  You are right I have been doing some work related to the GitHub Integration.

Coming on to this issue, I think adding a check for null using `check_none_or(check_string)` should work, also I will go on and add a fixture to make sure this behavior is tested as well. I will create a PR shortly for this one.

Thanks for reporting the bug @kou.
@zulipbot claim
Hello @sbansal1999!

Thanks for your interest in Zulip! You have attempted to claim an issue without the label "help wanted". You can only claim and submit pull requests for issues with the [help wanted](https://github.com/zulip/zulip/issues?q=is%3Aopen+is%3Aissue+no%3Aassignee+label%3A%22help+wanted%22) label.

If this is your first time here, we recommend reading our [guide for new contributors](https://zulip.readthedocs.io/en/latest/overview/contributing.html) before getting started.


Resolving patch:
diff --git a/zerver/webhooks/github/view.py b/zerver/webhooks/github/view.py
--- a/zerver/webhooks/github/view.py
+++ b/zerver/webhooks/github/view.py
@@ -501,7 +501,7 @@ def get_pull_request_review_body(helper: Helper) -> str:
         url=payload["review"]["html_url"].tame(check_string),
         type="PR review",
         title=title if include_title else None,
-        message=payload["review"]["body"].tame(check_string),
+        message=payload["review"]["body"].tame(check_none_or(check_string)),
     )
 
 


Test patch:
diff --git a/zerver/webhooks/github/tests.py b/zerver/webhooks/github/tests.py
--- a/zerver/webhooks/github/tests.py
+++ b/zerver/webhooks/github/tests.py
@@ -284,6 +284,11 @@ def test_pull_request_review_msg_with_custom_topic_in_url(self) -> None:
         expected_message = "baxterthehacker submitted [PR review for #1 Update the README with new information](https://github.com/baxterthehacker/public-repo/pull/1#pullrequestreview-2626884):\n\n~~~ quote\nLooks great!\n~~~"
         self.check_webhook("pull_request_review", expected_topic, expected_message)
 
+    def test_pull_request_review_msg_with_empty_body(self) -> None:
+        expected_topic = "groonga / PR #1581 grn_db_value_lock: unlock GRN_TYPE obj..."
+        expected_message = "kou submitted [PR review](https://github.com/groonga/groonga/pull/1581#pullrequestreview-1483047907)."
+        self.check_webhook("pull_request_review__empty_body", expected_topic, expected_message)
+
     def test_pull_request_review_comment_msg(self) -> None:
         expected_message = "baxterthehacker created [PR review comment](https://github.com/baxterthehacker/public-repo/pull/1#discussion_r29724692):\n\n~~~ quote\nMaybe you should use more emojji on this line.\n~~~"
         self.check_webhook("pull_request_review_comment", TOPIC_PR, expected_message)