diff --git a/src/Octokit.Webhooks/Converter/WebhookConverter.cs b/src/Octokit.Webhooks/Converter/WebhookConverter.cs index c1684b66..29cdfa94 100644 --- a/src/Octokit.Webhooks/Converter/WebhookConverter.cs +++ b/src/Octokit.Webhooks/Converter/WebhookConverter.cs @@ -1,21 +1,22 @@ namespace Octokit.Webhooks.Converter; using System.Linq; +using System.Reflection; [PublicAPI] public class WebhookConverter : JsonConverter where T : WebhookEvent { - private readonly IDictionary types; + private readonly Dictionary types; public WebhookConverter() { var type = typeof(T); this.types = this.GetType().Assembly.GetTypes() .Where(x => type.IsAssignableFrom(x) && x is { IsClass: true, IsAbstract: false } && - Attribute.GetCustomAttribute(x, typeof(WebhookActionTypeAttribute)) is not null) + x.GetCustomAttribute() is not null) .ToDictionary( - y => ((WebhookActionTypeAttribute)Attribute.GetCustomAttribute(y, typeof(WebhookActionTypeAttribute))!).ActionType, + y => y.GetCustomAttribute()!.ActionType, y => y); } @@ -27,12 +28,30 @@ public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerial } using var jsonDocument = JsonDocument.ParseValue(ref reader); - if (!jsonDocument.RootElement.TryGetProperty(nameof(WebhookEvent.Action).ToLower(CultureInfo.InvariantCulture), out var action)) + if (!jsonDocument.RootElement.TryGetProperty("action", out var action)) { throw new JsonException(); } - var type = this.types.FirstOrDefault(x => x.Key == action.GetString()).Value ?? throw new JsonException(); + Type? type = null; + + var actionValue = action.GetString(); + + if (actionValue is not null && this.types.TryGetValue(actionValue, out var payloadType)) + { + type = payloadType; + } + + // repository_dispatch events can have custom user-defined actions values + if (type == null && typeToConvert == typeof(Events.RepositoryDispatchEvent)) + { + type = typeof(Events.RepositoryDispatch.RepositoryDispatchCustomEvent); + } + + if (type is null) + { + throw new JsonException(); + } var jsonObject = jsonDocument.RootElement.GetRawText(); return (T)JsonSerializer.Deserialize(jsonObject, type, options)!; diff --git a/src/Octokit.Webhooks/Events/RepositoryDispatch/RepositoryDispatchCustomEvent.cs b/src/Octokit.Webhooks/Events/RepositoryDispatch/RepositoryDispatchCustomEvent.cs new file mode 100644 index 00000000..9253fec8 --- /dev/null +++ b/src/Octokit.Webhooks/Events/RepositoryDispatch/RepositoryDispatchCustomEvent.cs @@ -0,0 +1,4 @@ +namespace Octokit.Webhooks.Events.RepositoryDispatch; + +[PublicAPI] +public sealed record RepositoryDispatchCustomEvent : RepositoryDispatchEvent; diff --git a/test/Octokit.Webhooks.Test/Resources/repository_dispatch/custom.payload.json b/test/Octokit.Webhooks.Test/Resources/repository_dispatch/custom.payload.json new file mode 100644 index 00000000..3425e738 --- /dev/null +++ b/test/Octokit.Webhooks.Test/Resources/repository_dispatch/custom.payload.json @@ -0,0 +1,140 @@ +{ + "action": "foo", + "branch": "main", + "client_payload": { "foo": 42, "bar": "baz" }, + "repository": { + "id": 17273051, + "node_id": "MDEwOlJlcG9zaXRvcnkxNzI3MzA1MQ==", + "name": "octo-repo", + "full_name": "octo-org/octo-repo", + "private": true, + "owner": { + "login": "octo-org", + "id": 6811672, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjY4MTE2NzI=", + "avatar_url": "https://avatars3.githubusercontent.com/u/6811672?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/octo-org", + "html_url": "https://github.com/octo-org", + "followers_url": "https://api.github.com/users/octo-org/followers", + "following_url": "https://api.github.com/users/octo-org/following{/other_user}", + "gists_url": "https://api.github.com/users/octo-org/gists{/gist_id}", + "starred_url": "https://api.github.com/users/octo-org/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/octo-org/subscriptions", + "organizations_url": "https://api.github.com/users/octo-org/orgs", + "repos_url": "https://api.github.com/users/octo-org/repos", + "events_url": "https://api.github.com/users/octo-org/events{/privacy}", + "received_events_url": "https://api.github.com/users/octo-org/received_events", + "type": "Organization", + "site_admin": false + }, + "html_url": "https://github.com/octo-org/octo-repo", + "description": "My first repo on GitHub!", + "fork": false, + "url": "https://api.github.com/repos/octo-org/octo-repo", + "forks_url": "https://api.github.com/repos/octo-org/octo-repo/forks", + "keys_url": "https://api.github.com/repos/octo-org/octo-repo/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/octo-org/octo-repo/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/octo-org/octo-repo/teams", + "hooks_url": "https://api.github.com/repos/octo-org/octo-repo/hooks", + "issue_events_url": "https://api.github.com/repos/octo-org/octo-repo/issues/events{/number}", + "events_url": "https://api.github.com/repos/octo-org/octo-repo/events", + "assignees_url": "https://api.github.com/repos/octo-org/octo-repo/assignees{/user}", + "branches_url": "https://api.github.com/repos/octo-org/octo-repo/branches{/branch}", + "tags_url": "https://api.github.com/repos/octo-org/octo-repo/tags", + "blobs_url": "https://api.github.com/repos/octo-org/octo-repo/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/octo-org/octo-repo/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/octo-org/octo-repo/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/octo-org/octo-repo/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/octo-org/octo-repo/statuses/{sha}", + "languages_url": "https://api.github.com/repos/octo-org/octo-repo/languages", + "stargazers_url": "https://api.github.com/repos/octo-org/octo-repo/stargazers", + "contributors_url": "https://api.github.com/repos/octo-org/octo-repo/contributors", + "subscribers_url": "https://api.github.com/repos/octo-org/octo-repo/subscribers", + "subscription_url": "https://api.github.com/repos/octo-org/octo-repo/subscription", + "commits_url": "https://api.github.com/repos/octo-org/octo-repo/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/octo-org/octo-repo/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/octo-org/octo-repo/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/octo-org/octo-repo/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/octo-org/octo-repo/contents/{+path}", + "compare_url": "https://api.github.com/repos/octo-org/octo-repo/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/octo-org/octo-repo/merges", + "archive_url": "https://api.github.com/repos/octo-org/octo-repo/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/octo-org/octo-repo/downloads", + "issues_url": "https://api.github.com/repos/octo-org/octo-repo/issues{/number}", + "pulls_url": "https://api.github.com/repos/octo-org/octo-repo/pulls{/number}", + "milestones_url": "https://api.github.com/repos/octo-org/octo-repo/milestones{/number}", + "notifications_url": "https://api.github.com/repos/octo-org/octo-repo/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/octo-org/octo-repo/labels{/name}", + "releases_url": "https://api.github.com/repos/octo-org/octo-repo/releases{/id}", + "deployments_url": "https://api.github.com/repos/octo-org/octo-repo/deployments", + "created_at": "2014-02-28T02:42:51Z", + "updated_at": "2018-10-10T15:58:51Z", + "pushed_at": "2018-10-10T15:58:47Z", + "git_url": "git://github.com/octo-org/octo-repo.git", + "ssh_url": "git@github.com:octo-org/octo-repo.git", + "clone_url": "https://github.com/octo-org/octo-repo.git", + "svn_url": "https://github.com/octo-org/octo-repo", + "homepage": "https://example.com", + "size": 59, + "stargazers_count": 0, + "watchers_count": 0, + "language": "JavaScript", + "has_issues": true, + "has_projects": true, + "has_downloads": true, + "has_wiki": true, + "has_pages": false, + "forks_count": 1, + "mirror_url": null, + "archived": false, + "open_issues_count": 23, + "license": null, + "forks": 1, + "open_issues": 23, + "watchers": 0, + "default_branch": "master", + "is_template": false, + "topics": [], + "visibility": "public", + "web_commit_signoff_required": false + }, + "organization": { + "login": "octo-org", + "id": 6811672, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjY4MTE2NzI=", + "url": "https://api.github.com/orgs/octo-org", + "repos_url": "https://api.github.com/orgs/octo-org/repos", + "events_url": "https://api.github.com/orgs/octo-org/events", + "hooks_url": "https://api.github.com/orgs/octo-org/hooks", + "issues_url": "https://api.github.com/orgs/octo-org/issues", + "members_url": "https://api.github.com/orgs/octo-org/members{/member}", + "public_members_url": "https://api.github.com/orgs/octo-org/public_members{/member}", + "avatar_url": "https://avatars3.githubusercontent.com/u/6811672?v=4", + "description": "Working better together!" + }, + "sender": { + "login": "Codertocat", + "id": 21031067, + "node_id": "MDQ6VXNlcjIxMDMxMDY3", + "avatar_url": "https://avatars1.githubusercontent.com/u/21031067?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/Codertocat", + "html_url": "https://github.com/Codertocat", + "followers_url": "https://api.github.com/users/Codertocat/followers", + "following_url": "https://api.github.com/users/Codertocat/following{/other_user}", + "gists_url": "https://api.github.com/users/Codertocat/gists{/gist_id}", + "starred_url": "https://api.github.com/users/Codertocat/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/Codertocat/subscriptions", + "organizations_url": "https://api.github.com/users/Codertocat/orgs", + "repos_url": "https://api.github.com/users/Codertocat/repos", + "events_url": "https://api.github.com/users/Codertocat/events{/privacy}", + "received_events_url": "https://api.github.com/users/Codertocat/received_events", + "type": "User", + "site_admin": false + }, + "installation": { + "id": 375706, + "node_id": "MDIzOkludGVncmF0aW9uSW5zdGFsbGF0aW9uMzc1NzA2" + } +}