Repository: google/go-github
Pull request #2639 (2023-01-24 20:28:07)

Add method to update codescanning alert
Adds method to update codescanning alert.
https://docs.github.com/en/rest/code-scanning?apiVersion=2022-11-28#update-a-code-scanning-alert

codecov[bot]: # [Codecov](https://codecov.io/gh/google/go-github/pull/2639?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google) Report
> Merging [#2639](https://codecov.io/gh/google/go-github/pull/2639?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google) (9a78bc0) into [master](https://codecov.io/gh/google/go-github/commit/2561c07393f1a665976919fe051d7f2e0cc660b4?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google) (2561c07) will **decrease** coverage by `0.12%`.
> The diff coverage is `0.00%`.

```diff
@@            Coverage Diff             @@
##           master    #2639      +/-   ##
==========================================
- Coverage   98.05%   97.93%   -0.12%     
==========================================
  Files         130      130              
  Lines       11242    11255      +13     
==========================================
  Hits        11023    11023              
- Misses        150      163      +13     
  Partials       69       69              
```


| [Impacted Files](https://codecov.io/gh/google/go-github/pull/2639?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google) | Coverage Δ | |
|---|---|---|
| [github/code-scanning.go](https://codecov.io/gh/google/go-github/pull/2639?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google#diff-Z2l0aHViL2NvZGUtc2Nhbm5pbmcuZ28=) | `88.69% <0.00%> (-11.31%)` | :arrow_down: |

Help us with your feedback. Take ten seconds to tell us [how you rate us](https://about.codecov.io/nps?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google). Have a feature suggestion? [Share it here.](https://app.codecov.io/gh/feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google)

ganeshkumarsv: Thanks for the commit suggestion @gmlewis I fixed the  test cases and generators.
ganeshkumarsv: @gmlewis we got 2 approvals on the PR. can you please merge the PR and create a new release?
gmlewis: > @gmlewis we got 2 approvals on the PR. can you please merge the PR and create a new release?

Yes, but as for the new release, we already have breaking API changes and I would like to get https://github.com/google/go-github/pull/2646 approved and committed too before the major version change, so if you feel like reviewing that one, @ganeshkumarsv , that would speed the process along.  Thanks.
gmlewis: Thank you, @AbbanMustafa !
Merging.

Patch:
diff --git a/github/code-scanning.go b/github/code-scanning.go
index 53200fa3..6717348e 100644
--- a/github/code-scanning.go
+++ b/github/code-scanning.go
@@ -173,6 +173,22 @@ type SarifAnalysis struct {
 	ToolName    *string    `json:"tool_name,omitempty"`
 }
 
+// CodeScanningAlertState specifies the state of a code scanning alert.
+//
+// GitHub API docs: https://docs.github.com/en/rest/code-scanning
+type CodeScanningAlertState struct {
+	// State sets the state of the code scanning alert and is a required field.
+	// You must also provide DismissedReason when you set the state to "dismissed".
+	// State can be one of: "open", "dismissed".
+	State string `json:"state"`
+	// DismissedReason represents the reason for dismissing or closing the alert.
+	// It is required when the state is "dismissed".
+	// It can be one of: "false positive", "won't fix", "used in tests".
+	DismissedReason *string `json:"dismissed_reason,omitempty"`
+	// DismissedComment is associated with the dismissal of the alert.
+	DismissedComment *string `json:"dismissed_comment,omitempty"`
+}
+
 // SarifID identifies a sarif analysis upload.
 //
 // GitHub API docs: https://docs.github.com/en/rest/code-scanning
@@ -261,6 +277,31 @@ func (s *CodeScanningService) GetAlert(ctx context.Context, owner, repo string,
 	return a, resp, nil
 }
 
+// UpdateAlert updates the state of a single code scanning alert for a repository.
+//
+// You must use an access token with the security_events scope to use this endpoint.
+// GitHub Apps must have the security_events read permission to use this endpoint.
+//
+// The security alert_id is the number at the end of the security alert's URL.
+//
+// GitHub API docs: https://docs.github.com/en/rest/code-scanning?apiVersion=2022-11-28#update-a-code-scanning-alert
+func (s *CodeScanningService) UpdateAlert(ctx context.Context, owner, repo string, id int64, stateInfo *CodeScanningAlertState) (*Alert, *Response, error) {
+	u := fmt.Sprintf("repos/%v/%v/code-scanning/alerts/%v", owner, repo, id)
+
+	req, err := s.client.NewRequest("PATCH", u, stateInfo)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	a := new(Alert)
+	resp, err := s.client.Do(ctx, req, a)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return a, resp, nil
+}
+
 // UploadSarif uploads the result of code scanning job to GitHub.
 //
 // For the parameter sarif, you must first compress your SARIF file using gzip and then translate the contents of the file into a Base64 encoding string.
diff --git a/github/github-accessors.go b/github/github-accessors.go
index 400bde8c..1290696d 100644
--- a/github/github-accessors.go
+++ b/github/github-accessors.go
@@ -2582,6 +2582,22 @@ func (c *CodeScanningAlertEvent) GetSender() *User {
 	return c.Sender
 }
 
+// GetDismissedComment returns the DismissedComment field if it's non-nil, zero value otherwise.
+func (c *CodeScanningAlertState) GetDismissedComment() string {
+	if c == nil || c.DismissedComment == nil {
+		return ""
+	}
+	return *c.DismissedComment
+}
+
+// GetDismissedReason returns the DismissedReason field if it's non-nil, zero value otherwise.
+func (c *CodeScanningAlertState) GetDismissedReason() string {
+	if c == nil || c.DismissedReason == nil {
+		return ""
+	}
+	return *c.DismissedReason
+}
+
 // GetIncompleteResults returns the IncompleteResults field if it's non-nil, zero value otherwise.
 func (c *CodeSearchResult) GetIncompleteResults() bool {
 	if c == nil || c.IncompleteResults == nil {


Test patch:
diff --git a/github/code-scanning_test.go b/github/code-scanning_test.go
index c8cf35cf..1ae05096 100644
--- a/github/code-scanning_test.go
+++ b/github/code-scanning_test.go
@@ -613,6 +613,123 @@ func TestCodeScanningService_ListAlertsForRepo(t *testing.T) {
 	})
 }
 
+func TestCodeScanningService_UpdateAlert(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+	mux.HandleFunc("/repos/o/r/code-scanning/alerts/88", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "PATCH")
+		fmt.Fprint(w, `{"rule_id":"js/useless-expression",
+				"rule_severity":"warning",
+				"rule_description":"Expression has no effect",
+				"tool": {
+					"name": "CodeQL",
+					"guid": null,
+					"version": "1.4.0"
+				},
+				"rule": {
+					"id": "useless expression",
+					"severity": "warning",
+					"description": "Expression has no effect",
+					"name": "useless expression",
+					"full_description": "Expression has no effect",
+					"help": "Expression has no effect"
+				},
+				"most_recent_instance": {
+					"ref": "refs/heads/main",
+					"state": "dismissed",
+					"commit_sha": "abcdefg12345",
+					"message": {
+						"text": "This path depends on a user-provided value."
+					},
+					"location": {
+						"path": "spec-main/api-session-spec.ts",
+						"start_line": 917,
+						"end_line": 917,
+						"start_column": 7,
+						"end_column": 18
+					},
+					"classifications": [
+						"test"
+					]
+				},
+				"created_at":"2019-01-02T15:04:05Z",
+				"state":"dismissed",
+				"dismissed_reason": "false positive",
+				"dismissed_comment": "This alert is not actually correct as sanitizer is used",
+				"closed_by":null,
+				"closed_at":null,
+				"url":"https://api.github.com/repos/o/r/code-scanning/alerts/88",
+				"html_url":"https://github.com/o/r/security/code-scanning/88"}`)
+	})
+
+	ctx := context.Background()
+	dismissedComment := String("This alert is not actually correct as sanitizer is used")
+	dismissedReason := String("false positive")
+	state := String("dismissed")
+	stateInfo := &CodeScanningAlertState{State: *state, DismissedReason: dismissedReason, DismissedComment: dismissedComment}
+	alert, _, err := client.CodeScanning.UpdateAlert(ctx, "o", "r", 88, stateInfo)
+	if err != nil {
+		t.Errorf("CodeScanning.UpdateAlert returned error: %v", err)
+	}
+
+	date := Timestamp{time.Date(2019, time.January, 02, 15, 04, 05, 0, time.UTC)}
+	want := &Alert{
+		RuleID:          String("js/useless-expression"),
+		RuleSeverity:    String("warning"),
+		RuleDescription: String("Expression has no effect"),
+		Tool:            &Tool{Name: String("CodeQL"), GUID: nil, Version: String("1.4.0")},
+		Rule: &Rule{
+			ID:              String("useless expression"),
+			Severity:        String("warning"),
+			Description:     String("Expression has no effect"),
+			Name:            String("useless expression"),
+			FullDescription: String("Expression has no effect"),
+			Help:            String("Expression has no effect"),
+		},
+		CreatedAt:        &date,
+		State:            state,
+		DismissedReason:  dismissedReason,
+		DismissedComment: dismissedComment,
+		ClosedBy:         nil,
+		ClosedAt:         nil,
+		URL:              String("https://api.github.com/repos/o/r/code-scanning/alerts/88"),
+		HTMLURL:          String("https://github.com/o/r/security/code-scanning/88"),
+		MostRecentInstance: &MostRecentInstance{
+			Ref:       String("refs/heads/main"),
+			State:     String("dismissed"),
+			CommitSHA: String("abcdefg12345"),
+			Message: &Message{
+				Text: String("This path depends on a user-provided value."),
+			},
+			Location: &Location{
+				Path:        String("spec-main/api-session-spec.ts"),
+				StartLine:   Int(917),
+				EndLine:     Int(917),
+				StartColumn: Int(7),
+				EndColumn:   Int(18),
+			},
+			Classifications: []string{"test"},
+		},
+	}
+	if !cmp.Equal(alert, want) {
+		t.Errorf("CodeScanning.UpdateAlert returned %+v, want %+v", alert, want)
+	}
+
+	const methodName = "UpdateAlert"
+	testBadOptions(t, methodName, func() (err error) {
+		_, _, err = client.CodeScanning.UpdateAlert(ctx, "\n", "\n", -88, stateInfo)
+		return err
+	})
+
+	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
+		got, resp, err := client.CodeScanning.UpdateAlert(ctx, "o", "r", 88, stateInfo)
+		if got != nil {
+			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
+		}
+		return resp, err
+	})
+}
+
 func TestCodeScanningService_GetAlert(t *testing.T) {
 	client, mux, _, teardown := setup()
 	defer teardown()
diff --git a/github/github-accessors_test.go b/github/github-accessors_test.go
index 3e4b4694..6babd83e 100644
--- a/github/github-accessors_test.go
+++ b/github/github-accessors_test.go
@@ -3048,6 +3048,26 @@ func TestCodeScanningAlertEvent_GetSender(tt *testing.T) {
 	c.GetSender()
 }
 
+func TestCodeScanningAlertState_GetDismissedComment(tt *testing.T) {
+	var zeroValue string
+	c := &CodeScanningAlertState{DismissedComment: &zeroValue}
+	c.GetDismissedComment()
+	c = &CodeScanningAlertState{}
+	c.GetDismissedComment()
+	c = nil
+	c.GetDismissedComment()
+}
+
+func TestCodeScanningAlertState_GetDismissedReason(tt *testing.T) {
+	var zeroValue string
+	c := &CodeScanningAlertState{DismissedReason: &zeroValue}
+	c.GetDismissedReason()
+	c = &CodeScanningAlertState{}
+	c.GetDismissedReason()
+	c = nil
+	c.GetDismissedReason()
+}
+
 func TestCodeSearchResult_GetIncompleteResults(tt *testing.T) {
 	var zeroValue bool
 	c := &CodeSearchResult{IncompleteResults: &zeroValue}