diff --git a/azuredevops/azuredevops.go b/azuredevops/azuredevops.go index 76b8fae..c031759 100644 --- a/azuredevops/azuredevops.go +++ b/azuredevops/azuredevops.go @@ -13,8 +13,9 @@ import ( // parse errors var ( - ErrInvalidHTTPMethod = errors.New("invalid HTTP Method") - ErrParsingPayload = errors.New("error parsing payload") + ErrInvalidHTTPMethod = errors.New("invalid HTTP Method") + ErrParsingPayload = errors.New("error parsing payload") + ErrBasicAuthVerificationFailed = errors.New("basic auth verification failed") ) // Event defines an Azure DevOps server hook event type @@ -30,13 +31,38 @@ const ( GitPullRequestCommentEventType Event = "ms.vss-code.git-pullrequest-comment-event" ) +// Option is a configuration option for the webhook +type Option func(*Webhook) error + +// Options is a namespace var for configuration options +var Options = WebhookOptions{} + +// WebhookOptions is a namespace for configuration option methods +type WebhookOptions struct{} + +// BasicAuth verifies payload using basic auth +func (WebhookOptions) BasicAuth(username, password string) Option { + return func(hook *Webhook) error { + hook.username = username + hook.password = password + return nil + } +} + // Webhook instance contains all methods needed to process events type Webhook struct { + username string + password string } // New creates and returns a WebHook instance -func New() (*Webhook, error) { +func New(options ...Option) (*Webhook, error) { hook := new(Webhook) + for _, opt := range options { + if err := opt(hook); err != nil { + return nil, errors.New("Error applying Option") + } + } return hook, nil } @@ -47,6 +73,10 @@ func (hook Webhook) Parse(r *http.Request, events ...Event) (interface{}, error) _ = r.Body.Close() }() + if !hook.verifyBasicAuth(r) { + return nil, ErrBasicAuthVerificationFailed + } + if r.Method != http.MethodPost { return nil, ErrInvalidHTTPMethod } @@ -83,3 +113,13 @@ func (hook Webhook) Parse(r *http.Request, events ...Event) (interface{}, error) return nil, fmt.Errorf("unknown event %s", pl.EventType) } } + +func (hook Webhook) verifyBasicAuth(r *http.Request) bool { + // skip validation if username or password was not provided + if hook.username == "" && hook.password == "" { + return true + } + username, password, ok := r.BasicAuth() + + return ok && username == hook.username && password == hook.password +} diff --git a/azuredevops/azuredevops_test.go b/azuredevops/azuredevops_test.go index 1ca7591..2ff8f11 100644 --- a/azuredevops/azuredevops_test.go +++ b/azuredevops/azuredevops_test.go @@ -1,6 +1,9 @@ package azuredevops import ( + "bytes" + "fmt" + "github.com/stretchr/testify/assert" "log" "net/http" "net/http/httptest" @@ -117,3 +120,66 @@ func TestWebhooks(t *testing.T) { }) } } + +func TestParseBasicAuth(t *testing.T) { + const validUser = "validUser" + const validPass = "pass123" + tests := []struct { + name string + webhookUser string + webhookPass string + reqUser string + reqPass string + expectedErr error + }{ + { + name: "valid basic auth", + webhookUser: validUser, + webhookPass: validPass, + reqUser: validUser, + reqPass: validPass, + expectedErr: fmt.Errorf("unknown event "), // no event passed, so this is expected + }, + { + name: "no basic auth provided", + expectedErr: fmt.Errorf("unknown event "), // no event passed, so this is expected + }, + { + name: "invalid basic auth", + webhookUser: validUser, + webhookPass: validPass, + reqUser: "fakeUser", + reqPass: "fakePass", + expectedErr: ErrBasicAuthVerificationFailed, + }, + } + + for _, tt := range tests { + h := Webhook{ + username: tt.webhookUser, + password: tt.webhookPass, + } + body := []byte(`{}`) + r, err := http.NewRequest(http.MethodPost, "", bytes.NewBuffer(body)) + assert.NoError(t, err) + r.SetBasicAuth(tt.reqUser, tt.reqPass) + + p, err := h.Parse(r) + + assert.Equal(t, err, tt.expectedErr) + assert.Nil(t, p) + } +} + +func TestBasicAuth(t *testing.T) { + const user = "user" + const pass = "pass123" + + opt := Options.BasicAuth(user, pass) + h := &Webhook{} + err := opt(h) + + assert.NoError(t, err) + assert.Equal(t, h.username, user) + assert.Equal(t, h.password, pass) +} diff --git a/github/github.go b/github/github.go index e2ee652..4a9fb14 100644 --- a/github/github.go +++ b/github/github.go @@ -74,6 +74,7 @@ const ( WorkflowJobEvent Event = "workflow_job" WorkflowRunEvent Event = "workflow_run" GitHubAppAuthorizationEvent Event = "github_app_authorization" + CodeScanningAlertEvent Event = "code_scanning_alert" ) // EventSubtype defines a GitHub Hook Event subtype @@ -353,6 +354,10 @@ func (hook Webhook) Parse(r *http.Request, events ...Event) (interface{}, error) var pl GitHubAppAuthorizationPayload err = json.Unmarshal([]byte(payload), &pl) return pl, err + case CodeScanningAlertEvent: + var pl CodeScanningAlertPayload + err = json.Unmarshal([]byte(payload), &pl) + return pl, err default: return nil, fmt.Errorf("unknown event %s", gitHubEvent) } diff --git a/github/github_test.go b/github/github_test.go index 109d694..39a6203 100644 --- a/github/github_test.go +++ b/github/github_test.go @@ -555,6 +555,15 @@ func TestWebhooks(t *testing.T) { "X-Github-Event": []string{"github_app_authorization"}, }, }, + { + name: "CodeScanningAlertEvent", + event: CodeScanningAlertEvent, + typ: CodeScanningAlertPayload{}, + filename: "../testdata/github/code_scanning_alert.json", + headers: http.Header{ + "X-Github-Event": []string{"code_scanning_alert"}, + }, + }, } for _, tt := range tests { diff --git a/github/payload.go b/github/payload.go index 2485ece..902add2 100644 --- a/github/payload.go +++ b/github/payload.go @@ -4874,16 +4874,20 @@ type PullRequestReviewPayload struct { type PullRequestReviewCommentPayload struct { Action string `json:"action"` Comment struct { - URL string `json:"url"` - ID int64 `json:"id"` - NodeID string `json:"node_id"` - DiffHunk string `json:"diff_hunk"` - Path string `json:"path"` - Position int64 `json:"position"` - OriginalPosition int64 `json:"original_position"` - CommitID string `json:"commit_id"` - OriginalCommitID string `json:"original_commit_id"` - User struct { + URL string `json:"url"` + ID int64 `json:"id"` + NodeID string `json:"node_id"` + DiffHunk string `json:"diff_hunk"` + Path string `json:"path"` + Line int64 `json:"line"` + OriginalLine int64 `json:"original_line"` + StartLine int64 `json:"start_line"` + OriginalStartLine int64 `json:"original_start_line"` + Position int64 `json:"position"` + OriginalPosition int64 `json:"original_position"` + CommitID string `json:"commit_id"` + OriginalCommitID string `json:"original_commit_id"` + User struct { Login string `json:"login"` ID int64 `json:"id"` NodeID string `json:"node_id"` @@ -6502,6 +6506,7 @@ type WorkflowJobPayload struct { HTMLURL string `json:"html_url"` Status string `json:"status"` Conclusion string `json:"conclusion"` + CreatedAt time.Time `json:"created_at"` StartedAt time.Time `json:"started_at"` CompletedAt time.Time `json:"completed_at"` Name string `json:"name"` @@ -7201,3 +7206,205 @@ type GitHubAppAuthorizationPayload struct { SiteAdmin bool `json:"site_admin"` } `json:"sender"` } + +// CodeScanningAlertPayload contains code scanning alert payload +type CodeScanningAlertPayload struct { + Action string `json:"action"` + Alert struct { + Number int `json:"number"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + Url string `json:"url"` + HtmlUrl string `json:"html_url"` + State string `json:"state"` + FixedAt interface{} `json:"fixed_at"` + DismissedBy interface{} `json:"dismissed_by"` + DismissedAt interface{} `json:"dismissed_at"` + DismissedReason interface{} `json:"dismissed_reason"` + DismissedComment interface{} `json:"dismissed_comment"` + Rule struct { + Id string `json:"id"` + Severity string `json:"severity"` + Description string `json:"description"` + Name string `json:"name"` + Tags []string `json:"tags"` + FullDescription string `json:"full_description"` + Help string `json:"help"` + HelpUri string `json:"help_uri"` + SecuritySeverityLevel string `json:"security_severity_level"` + } `json:"rule"` + Tool struct { + Name string `json:"name"` + Guid interface{} `json:"guid"` + Version string `json:"version"` + } `json:"tool"` + MostRecentInstance struct { + Ref string `json:"ref"` + AnalysisKey string `json:"analysis_key"` + Environment string `json:"environment"` + Category string `json:"category"` + State string `json:"state"` + CommitSha string `json:"commit_sha"` + Message struct { + Text string `json:"text"` + } `json:"message"` + Location struct { + Path string `json:"path"` + StartLine int `json:"start_line"` + EndLine int `json:"end_line"` + StartColumn int `json:"start_column"` + EndColumn int `json:"end_column"` + } `json:"location"` + Classifications []interface{} `json:"classifications"` + } `json:"most_recent_instance"` + InstancesUrl string `json:"instances_url"` + } `json:"alert"` + Ref string `json:"ref"` + CommitOid string `json:"commit_oid"` + Repository struct { + Id int `json:"id"` + NodeId string `json:"node_id"` + Name string `json:"name"` + FullName string `json:"full_name"` + Private bool `json:"private"` + Owner struct { + Login string `json:"login"` + Id int `json:"id"` + NodeId string `json:"node_id"` + AvatarUrl string `json:"avatar_url"` + GravatarId string `json:"gravatar_id"` + Url string `json:"url"` + HtmlUrl string `json:"html_url"` + FollowersUrl string `json:"followers_url"` + FollowingUrl string `json:"following_url"` + GistsUrl string `json:"gists_url"` + StarredUrl string `json:"starred_url"` + SubscriptionsUrl string `json:"subscriptions_url"` + OrganizationsUrl string `json:"organizations_url"` + ReposUrl string `json:"repos_url"` + EventsUrl string `json:"events_url"` + ReceivedEventsUrl string `json:"received_events_url"` + Type string `json:"type"` + SiteAdmin bool `json:"site_admin"` + } `json:"owner"` + HtmlUrl string `json:"html_url"` + Description string `json:"description"` + Fork bool `json:"fork"` + Url string `json:"url"` + ForksUrl string `json:"forks_url"` + KeysUrl string `json:"keys_url"` + CollaboratorsUrl string `json:"collaborators_url"` + TeamsUrl string `json:"teams_url"` + HooksUrl string `json:"hooks_url"` + IssueEventsUrl string `json:"issue_events_url"` + EventsUrl string `json:"events_url"` + AssigneesUrl string `json:"assignees_url"` + BranchesUrl string `json:"branches_url"` + TagsUrl string `json:"tags_url"` + BlobsUrl string `json:"blobs_url"` + GitTagsUrl string `json:"git_tags_url"` + GitRefsUrl string `json:"git_refs_url"` + TreesUrl string `json:"trees_url"` + StatusesUrl string `json:"statuses_url"` + LanguagesUrl string `json:"languages_url"` + StargazersUrl string `json:"stargazers_url"` + ContributorsUrl string `json:"contributors_url"` + SubscribersUrl string `json:"subscribers_url"` + SubscriptionUrl string `json:"subscription_url"` + CommitsUrl string `json:"commits_url"` + GitCommitsUrl string `json:"git_commits_url"` + CommentsUrl string `json:"comments_url"` + IssueCommentUrl string `json:"issue_comment_url"` + ContentsUrl string `json:"contents_url"` + CompareUrl string `json:"compare_url"` + MergesUrl string `json:"merges_url"` + ArchiveUrl string `json:"archive_url"` + DownloadsUrl string `json:"downloads_url"` + IssuesUrl string `json:"issues_url"` + PullsUrl string `json:"pulls_url"` + MilestonesUrl string `json:"milestones_url"` + NotificationsUrl string `json:"notifications_url"` + LabelsUrl string `json:"labels_url"` + ReleasesUrl string `json:"releases_url"` + DeploymentsUrl string `json:"deployments_url"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + PushedAt time.Time `json:"pushed_at"` + GitUrl string `json:"git_url"` + SshUrl string `json:"ssh_url"` + CloneUrl string `json:"clone_url"` + SvnUrl string `json:"svn_url"` + Homepage string `json:"homepage"` + Size int `json:"size"` + StargazersCount int `json:"stargazers_count"` + WatchersCount int `json:"watchers_count"` + Language string `json:"language"` + HasIssues bool `json:"has_issues"` + HasProjects bool `json:"has_projects"` + HasDownloads bool `json:"has_downloads"` + HasWiki bool `json:"has_wiki"` + HasPages bool `json:"has_pages"` + HasDiscussions bool `json:"has_discussions"` + ForksCount int `json:"forks_count"` + MirrorUrl interface{} `json:"mirror_url"` + Archived bool `json:"archived"` + Disabled bool `json:"disabled"` + OpenIssuesCount int `json:"open_issues_count"` + License interface{} `json:"license"` + AllowForking bool `json:"allow_forking"` + IsTemplate bool `json:"is_template"` + WebCommitSignoffRequired bool `json:"web_commit_signoff_required"` + Topics []string `json:"topics"` + Visibility string `json:"visibility"` + Forks int `json:"forks"` + OpenIssues int `json:"open_issues"` + Watchers int `json:"watchers"` + DefaultBranch string `json:"default_branch"` + } `json:"repository"` + Organization struct { + Login string `json:"login"` + Id int `json:"id"` + NodeId string `json:"node_id"` + Url string `json:"url"` + ReposUrl string `json:"repos_url"` + EventsUrl string `json:"events_url"` + HooksUrl string `json:"hooks_url"` + IssuesUrl string `json:"issues_url"` + MembersUrl string `json:"members_url"` + PublicMembersUrl string `json:"public_members_url"` + AvatarUrl string `json:"avatar_url"` + Description string `json:"description"` + } `json:"organization"` + Enterprise struct { + Id int `json:"id"` + Slug string `json:"slug"` + Name string `json:"name"` + NodeId string `json:"node_id"` + AvatarUrl string `json:"avatar_url"` + Description string `json:"description"` + WebsiteUrl string `json:"website_url"` + HtmlUrl string `json:"html_url"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + } `json:"enterprise"` + Sender struct { + Login string `json:"login"` + Id int `json:"id"` + NodeId string `json:"node_id"` + AvatarUrl string `json:"avatar_url"` + GravatarId string `json:"gravatar_id"` + Url string `json:"url"` + HtmlUrl string `json:"html_url"` + FollowersUrl string `json:"followers_url"` + FollowingUrl string `json:"following_url"` + GistsUrl string `json:"gists_url"` + StarredUrl string `json:"starred_url"` + SubscriptionsUrl string `json:"subscriptions_url"` + OrganizationsUrl string `json:"organizations_url"` + ReposUrl string `json:"repos_url"` + EventsUrl string `json:"events_url"` + ReceivedEventsUrl string `json:"received_events_url"` + Type string `json:"type"` + SiteAdmin bool `json:"site_admin"` + } `json:"sender"` +} diff --git a/gitlab/payload.go b/gitlab/payload.go index 7f7d690..663fefa 100644 --- a/gitlab/payload.go +++ b/gitlab/payload.go @@ -724,11 +724,14 @@ type ObjectAttributes struct { NoteableID int64 `json:"noteable_id"` // nolint: misspell System bool `json:"system"` WorkInProgress bool `json:"work_in_progress"` + Draft bool `json:"draft"` StDiffs []StDiff `json:"st_diffs"` Source Source `json:"source"` Target Target `json:"target"` LastCommit LastCommit `json:"last_commit"` Assignee Assignee `json:"assignee"` + DiscussionID string `json:"discussion_id"` // thread id + Type string `json:"type"` // "DiffNote" or "DiscussionNote" } // PipelineObjectAttributes contains pipeline specific GitLab object attributes information @@ -880,14 +883,21 @@ type Author struct { // Changes contains all changes associated with a GitLab issue or MR type Changes struct { LabelChanges LabelChanges `json:"labels"` + DraftChanges DraftChanges `json:"draft"` } -// LabelChanges contains changes in labels assocatiated with a GitLab issue or MR +// LabelChanges contains changes in labels associated with a GitLab issue or MR type LabelChanges struct { Previous []Label `json:"previous"` Current []Label `json:"current"` } +// DraftChanges contains the current and previous value of the draft property, tells us if draft was toggles +type DraftChanges struct { + Previous bool `json:"previous"` + Current bool `json:"current"` +} + // Label contains all of the GitLab label information type Label struct { ID int64 `json:"id"` diff --git a/testdata/github/code_scanning_alert.json b/testdata/github/code_scanning_alert.json new file mode 100644 index 0000000..38ff286 --- /dev/null +++ b/testdata/github/code_scanning_alert.json @@ -0,0 +1,208 @@ +{ + "action": "reopened_by_user", + "alert": { + "number": 2996, + "created_at": "2023-12-12T09:04:37Z", + "updated_at": "2023-12-17T13:40:01Z", + "url": "https://github.com/api/v3/repos/dummyrepo/non-existing/code-scanning/alerts/2996", + "html_url": "https://github.com/dummyrepo/non-existing/security/code-scanning/2996", + "state": "open", + "fixed_at": null, + "dismissed_by": null, + "dismissed_at": null, + "dismissed_reason": null, + "dismissed_comment": null, + "rule": { + "id": "CVE-2023-123456", + "severity": "note", + "description": "curl: cookie injection with none file", + "name": "OsPackageVulnerability", + "tags": [ + "LOW", + "security", + "vulnerability" + ], + "full_description": "This flaw allows an attacker to insert cookies at will into a running program.", + "help": "**Vulnerability CVE-2023-123456**\n", + "help_uri": "https://avd.aquasec.com/nvd/cve-2023-123456", + "security_severity_level": "low" + }, + "tool": { + "name": "Trivy", + "guid": null, + "version": "0.47.0" + }, + "most_recent_instance": { + "ref": "refs/heads/main", + "analysis_key": ".github/workflows/image.yml", + "environment": "{}", + "category": ".github/workflows/image.yml", + "state": "open", + "commit_sha": "285b53e372a84db195d9cdaecea544601045c9e0", + "message": { + "text": "Package: libcurl3-gnutls\nInstalled Version: 7.74.0-1.3+deb11u1\nVulnerability CVE-2023-123456\nSeverity: LOW\nFixed Version: 7.74.0-1.3+deb11u10\nLink: [CVE-2023-123456](https://avd.aquasec.com/nvd/cve-2023-123456)" + }, + "location": { + "path": "some-path", + "start_line": 1, + "end_line": 1, + "start_column": 1, + "end_column": 1 + }, + "classifications": [ + + ] + }, + "instances_url": "https://github.com/api/v3/repos/dummyrepo/non-existing/code-scanning/alerts/2996/instances" + }, + "ref": "", + "commit_oid": "", + "repository": { + "id": 89033, + "node_id": "MDEwOlJlcG9zaXRvcnk4OTAzMw==", + "name": "somerepo", + "full_name": "someorg/somerepo", + "private": false, + "owner": { + "login": "someorg", + "id": 33886, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjMzODg2", + "avatar_url": "https://avatars.github.com/u/33886?", + "gravatar_id": "", + "url": "https://github.com/api/v3/users/someorg", + "html_url": "https://github.com/someorg", + "followers_url": "https://github.com/api/v3/users/someorg/followers", + "following_url": "https://github.com/api/v3/users/someorg/following{/other_user}", + "gists_url": "https://github.com/api/v3/users/someorg/gists{/gist_id}", + "starred_url": "https://github.com/api/v3/users/someorg/starred{/owner}{/repo}", + "subscriptions_url": "https://github.com/api/v3/users/someorg/subscriptions", + "organizations_url": "https://github.com/api/v3/users/someorg/orgs", + "repos_url": "https://github.com/api/v3/users/someorg/repos", + "events_url": "https://github.com/api/v3/users/someorg/events{/privacy}", + "received_events_url": "https://github.com/api/v3/users/someorg/received_events", + "type": "Organization", + "site_admin": false + }, + "html_url": "https://github.com/someorg/somerepo", + "description": "Some description", + "fork": false, + "url": "https://github.com/api/v3/repos/someorg/somerepo", + "forks_url": "https://github.com/api/v3/repos/dummyrepo/non-existing/forks", + "keys_url": "https://github.com/api/v3/repos/dummyrepo/non-existing/keys{/key_id}", + "collaborators_url": "https://github.com/api/v3/repos/dummyrepo/non-existing/collaborators{/collaborator}", + "teams_url": "https://github.com/api/v3/repos/dummyrepo/non-existing/teams", + "hooks_url": "https://github.com/api/v3/repos/dummyrepo/non-existing/hooks", + "issue_events_url": "https://github.com/api/v3/repos/dummyrepo/non-existing/issues/events{/number}", + "events_url": "https://github.com/api/v3/repos/dummyrepo/non-existing/events", + "assignees_url": "https://github.com/api/v3/repos/dummyrepo/non-existing/assignees{/user}", + "branches_url": "https://github.com/api/v3/repos/dummyrepo/non-existing/branches{/branch}", + "tags_url": "https://github.com/api/v3/repos/dummyrepo/non-existing/tags", + "blobs_url": "https://github.com/api/v3/repos/dummyrepo/non-existing/git/blobs{/sha}", + "git_tags_url": "https://github.com/api/v3/repos/dummyrepo/non-existing/git/tags{/sha}", + "git_refs_url": "https://github.com/api/v3/repos/dummyrepo/non-existing/git/refs{/sha}", + "trees_url": "https://github.com/api/v3/repos/dummyrepo/non-existing/git/trees{/sha}", + "statuses_url": "https://github.com/api/v3/repos/dummyrepo/non-existing/statuses/{sha}", + "languages_url": "https://github.com/api/v3/repos/dummyrepo/non-existing/languages", + "stargazers_url": "https://github.com/api/v3/repos/dummyrepo/non-existing/stargazers", + "contributors_url": "https://github.com/api/v3/repos/dummyrepo/non-existing/contributors", + "subscribers_url": "https://github.com/api/v3/repos/dummyrepo/non-existing/subscribers", + "subscription_url": "https://github.com/api/v3/repos/dummyrepo/non-existing/subscription", + "commits_url": "https://github.com/api/v3/repos/dummyrepo/non-existing/commits{/sha}", + "git_commits_url": "https://github.com/api/v3/repos/dummyrepo/non-existing/git/commits{/sha}", + "comments_url": "https://github.com/api/v3/repos/dummyrepo/non-existing/comments{/number}", + "issue_comment_url": "https://github.com/api/v3/repos/dummyrepo/non-existing/issues/comments{/number}", + "contents_url": "https://github.com/api/v3/repos/dummyrepo/non-existing/contents/{+path}", + "compare_url": "https://github.com/api/v3/repos/dummyrepo/non-existing/compare/{base}...{head}", + "merges_url": "https://github.com/api/v3/repos/dummyrepo/non-existing/merges", + "archive_url": "https://github.com/api/v3/repos/dummyrepo/non-existing/{archive_format}{/ref}", + "downloads_url": "https://github.com/api/v3/repos/dummyrepo/non-existing/downloads", + "issues_url": "https://github.com/api/v3/repos/dummyrepo/non-existing/issues{/number}", + "pulls_url": "https://github.com/api/v3/repos/dummyrepo/non-existing/pulls{/number}", + "milestones_url": "https://github.com/api/v3/repos/dummyrepo/non-existing/milestones{/number}", + "notifications_url": "https://github.com/api/v3/repos/dummyrepo/non-existing/notifications{?since,all,participating}", + "labels_url": "https://github.com/api/v3/repos/dummyrepo/non-existing/labels{/name}", + "releases_url": "https://github.com/api/v3/repos/dummyrepo/non-existing/releases{/id}", + "deployments_url": "https://github.com/api/v3/repos/dummyrepo/non-existing/deployments", + "created_at": "2022-01-24T06:53:38Z", + "updated_at": "2023-06-15T13:56:45Z", + "pushed_at": "2023-12-15T09:52:10Z", + "git_url": "git://github.com/somerepo/someorg.git", + "ssh_url": "git@github.com:somerepo/someorg.git", + "clone_url": "https://github.com/somerepo/someorg.git", + "svn_url": "https://github.com/somerepo/someorg", + "homepage": "", + "size": 28828, + "stargazers_count": 0, + "watchers_count": 0, + "language": "Shell", + "has_issues": true, + "has_projects": true, + "has_downloads": true, + "has_wiki": false, + "has_pages": false, + "has_discussions": false, + "forks_count": 1, + "mirror_url": null, + "archived": false, + "disabled": false, + "open_issues_count": 10, + "license": null, + "allow_forking": true, + "is_template": false, + "web_commit_signoff_required": false, + "topics": [ + "sometopic" + ], + "visibility": "public", + "forks": 1, + "open_issues": 10, + "watchers": 0, + "default_branch": "main" + }, + "organization": { + "login": "someorg", + "id": 33886, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjMzODg2", + "url": "https://github.com/api/v3/orgs/someorg", + "repos_url": "https://github.com/api/v3/orgs/someorg/repos", + "events_url": "https://github.com/api/v3/orgs/someorg/events", + "hooks_url": "https://github.com/api/v3/orgs/someorg/hooks", + "issues_url": "https://github.com/api/v3/orgs/someorg/issues", + "members_url": "https://github.com/api/v3/orgs/someorg/members{/member}", + "public_members_url": "https://github.com/api/v3/orgs/someorg/public_members{/member}", + "avatar_url": "https://avatars.github.com/u/33886?", + "description": "Some description." + }, + "enterprise": { + "id": 1, + "slug": "some-company", + "name": "Some Company", + "node_id": "MDEwOkVudGVabcJpc2Ux", + "avatar_url": "https://avatars.github.com/b/1?", + "description": "", + "website_url": "https://github.com/", + "html_url": "https://github.com/enterprises/some-company", + "created_at": "2018-11-29T17:39:39Z", + "updated_at": "2023-06-20T14:11:12Z" + }, + "sender": { + "login": "some-user", + "id": 9773, + "node_id": "MDQ6VXabcdk3NzM=", + "avatar_url": "https://avatars.github.com/u/9773?", + "gravatar_id": "", + "url": "https://github.com/api/v3/users/some-user", + "html_url": "https://github.com/some-user", + "followers_url": "https://github.com/api/v3/users/some-user/followers", + "following_url": "https://github.com/api/v3/users/some-user/following{/other_user}", + "gists_url": "https://github.com/api/v3/users/some-user/gists{/gist_id}", + "starred_url": "https://github.com/api/v3/users/some-user/starred{/owner}{/repo}", + "subscriptions_url": "https://github.com/api/v3/users/some-user/subscriptions", + "organizations_url": "https://github.com/api/v3/users/some-user/orgs", + "repos_url": "https://github.com/api/v3/users/some-user/repos", + "events_url": "https://github.com/api/v3/users/some-user/events{/privacy}", + "received_events_url": "https://github.com/api/v3/users/some-user/received_events", + "type": "User", + "site_admin": false + } +} \ No newline at end of file diff --git a/testdata/gitlab/merge-request-event.json b/testdata/gitlab/merge-request-event.json index d873114..68f6ad4 100644 --- a/testdata/gitlab/merge-request-event.json +++ b/testdata/gitlab/merge-request-event.json @@ -52,6 +52,7 @@ "state": "opened", "blocking_discussions_resolved": true, "work_in_progress": false, + "draft": false, "first_contribution": true, "merge_status": "unchecked", "target_project_id": 14, @@ -137,6 +138,10 @@ "previous": null, "current": 1 }, + "draft": { + "previous": true, + "current": false + }, "updated_at": { "previous": "2017-09-15 16:50:55 UTC", "current":"2017-09-15 16:52:00 UTC" @@ -192,4 +197,4 @@ "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon" } ] -} \ No newline at end of file +}