Repository: google/go-github
Pull request #3258 (2024-09-08 17:15:14)

Add type SponsorshipEvent
### Description

This pull request adds support for the `SponsorshipEvent` to the **go-github** library, addressing the issue described in [#2982](https://github.com/google/go-github/issues/2982).

#### Details

According to GitHub documentation ([SponsorshipEvent](https://docs.github.com/en/rest/overview/github-event-types?apiVersion=2022-11-28#sponsorshipevent)), the `SponsorshipEvent` is triggered when sponsorship changes occur, such as creation, cancellation, or tier changes.

This PR introduces the following changes:
- **New `SponsorshipEvent` struct**: Captures the key properties of a sponsorship event, including:
  - `Action`: The type of action performed (e.g., `created`, `tier_changed`, `cancelled`, etc.).
  - `EffectiveDate`: The date when the event or change becomes effective, applicable for events like `pending_cancellation` and `pending_tier_change`.
  - `Changes`: An optional field that captures details about changes, such as adjustments in sponsorship tier or privacy. This is represented by an embedded struct called `SponsorshipChanges`.
  - Standard fields like `Repository`, `Organization`, `Sender`, and `Installation` are also included to maintain consistency with other GitHub event types.
  
- **New `SponsorshipChanges` struct**: This struct provides additional details when the `tier` or `privacy_level` of a sponsorship is modified.
  - The `Tier` field captures tier changes using an embedded `SponsorshipTier` struct, which contains a `From` field representing the previous tier.
  - The `PrivacyLevel` field tracks any changes to the sponsorship’s privacy settings.

This new event type will allow **go-github** users to programmatically handle sponsorship-related events triggered on GitHub, improving support for GitHub Sponsors workflows.

#### Testing
- Unit tests have been added to verify the correct deserialization of the `SponsorshipEvent` struct from JSON payloads.
- The tests cover various event scenarios, including tier changes, privacy level changes, and sponsorship creation.

#### Issue Link
Resolves [#2982](https://github.com/google/go-github/issues/2982)


codecov[bot]: ## [Codecov](https://app.codecov.io/gh/google/go-github/pull/3258?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 92.94%. Comparing base [(`2b8c7fa`)](https://app.codecov.io/gh/google/go-github/commit/2b8c7fa86fcd1cb4a89a44c8e7b5a49fe4be8a45?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google) to head [(`01d7e08`)](https://app.codecov.io/gh/google/go-github/commit/01d7e08d889f158d2596ade9fdb8cd7eae9111d7?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google).
> Report is 106 commits behind head on master.

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##           master    #3258      +/-   ##
==========================================
- Coverage   97.72%   92.94%   -4.78%     
==========================================
  Files         153      171      +18     
  Lines       13390    11663    -1727     
==========================================
- Hits        13085    10840    -2245     
- Misses        215      729     +514     
- Partials       90       94       +4     
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/google/go-github/pull/3258?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=google).

CodeDiego15: Force the push? Oh sorry I didn't notice.
gmlewis: > Force the push? Oh sorry I didn't notice.

No. Please push the results.  (Not a big deal, but please do _NOT_ use force push in this repo, as explained in CONTRIBUTING.md, as we always use squash and merge in this repo.)
CodeDiego15: I did a normal git push, I don't know what happened, I'm sorry

gmlewis: > Please read step 4 of CONTRIBUTING.md

Once again, please read step 4 of CONTRIBUTING.md.

Specifically,

> 4. Run `script/fmt.sh`, `script/test.sh` and `script/lint.sh` to format your code and
   check that it passes all tests and linters. `script/lint.sh` may also tell you
   that generated files need to be updated. If so, run `script/generate.sh` to
   update them.

CodeDiego15: That's what I'm trying right now, but when I do I get this: ./script/fmt.sh                                                                                                                
usage: dirname path 
gmlewis: OK, then just try `./script/generate.sh`
gmlewis: And if that fails, then please try `go generate ./...`
CodeDiego15: That's it, some dependencies were downloaded, After that and excuse me. First time I contribute to the
CodeDiego15: Excuse me for bothering you so much, is there something missing to solve it?
gmlewis: Once you run `go generate ./...` then some auto-generated files should be updated.

Then, you can commit the changes, push to this PR, and the tests should all pass.

gmlewis: For example:

```bash
go generate ./...
git commit -sam 'run step 4'
git push origin add-sponsorship-event-type
```
CodeDiego15: I already did👍🏻 
CodeDiego15: Was it done again as Git Push --forze? Because I did a normal git push?
CodeDiego15: Hello good evening, an apology I had to leave. I'm already working on it


Patch:
diff --git a/github/event_types.go b/github/event_types.go
index df8d9e03..41d71f8d 100644
--- a/github/event_types.go
+++ b/github/event_types.go
@@ -1821,3 +1821,27 @@ type CodeScanningAlertEvent struct {
 
 	Installation *Installation `json:"installation,omitempty"`
 }
+
+// SponsorshipEvent represents a sponsorship event in GitHub.
+//
+// GitHub API docs: https://docs.github.com/en/rest/overview/github-event-types?apiVersion=2022-11-28#sponsorshipevent
+type SponsorshipEvent struct {
+	Action        *string             `json:"action,omitempty"`
+	EffectiveDate *string             `json:"effective_date,omitempty"`
+	Changes       *SponsorshipChanges `json:"changes,omitempty"`
+	Repository    *Repository         `json:"repository,omitempty"`
+	Organization  *Organization       `json:"organization,omitempty"`
+	Sender        *User               `json:"sender,omitempty"`
+	Installation  *Installation       `json:"installation,omitempty"`
+}
+
+// SponsorshipChanges represents changes made to the sponsorship.
+type SponsorshipChanges struct {
+	Tier         *SponsorshipTier `json:"tier,omitempty"`
+	PrivacyLevel *string          `json:"privacy_level,omitempty"`
+}
+
+// SponsorshipTier represents the tier information of a sponsorship.
+type SponsorshipTier struct {
+	From *string `json:"from,omitempty"`
+}
diff --git a/github/github-accessors.go b/github/github-accessors.go
index 10096e44..1abbabd0 100644
--- a/github/github-accessors.go
+++ b/github/github-accessors.go
@@ -22398,6 +22398,86 @@ func (s *SourceImportAuthor) GetURL() string {
 	return *s.URL
 }
 
+// GetPrivacyLevel returns the PrivacyLevel field if it's non-nil, zero value otherwise.
+func (s *SponsorshipChanges) GetPrivacyLevel() string {
+	if s == nil || s.PrivacyLevel == nil {
+		return ""
+	}
+	return *s.PrivacyLevel
+}
+
+// GetTier returns the Tier field.
+func (s *SponsorshipChanges) GetTier() *SponsorshipTier {
+	if s == nil {
+		return nil
+	}
+	return s.Tier
+}
+
+// GetAction returns the Action field if it's non-nil, zero value otherwise.
+func (s *SponsorshipEvent) GetAction() string {
+	if s == nil || s.Action == nil {
+		return ""
+	}
+	return *s.Action
+}
+
+// GetChanges returns the Changes field.
+func (s *SponsorshipEvent) GetChanges() *SponsorshipChanges {
+	if s == nil {
+		return nil
+	}
+	return s.Changes
+}
+
+// GetEffectiveDate returns the EffectiveDate field if it's non-nil, zero value otherwise.
+func (s *SponsorshipEvent) GetEffectiveDate() string {
+	if s == nil || s.EffectiveDate == nil {
+		return ""
+	}
+	return *s.EffectiveDate
+}
+
+// GetInstallation returns the Installation field.
+func (s *SponsorshipEvent) GetInstallation() *Installation {
+	if s == nil {
+		return nil
+	}
+	return s.Installation
+}
+
+// GetOrganization returns the Organization field.
+func (s *SponsorshipEvent) GetOrganization() *Organization {
+	if s == nil {
+		return nil
+	}
+	return s.Organization
+}
+
+// GetRepository returns the Repository field.
+func (s *SponsorshipEvent) GetRepository() *Repository {
+	if s == nil {
+		return nil
+	}
+	return s.Repository
+}
+
+// GetSender returns the Sender field.
+func (s *SponsorshipEvent) GetSender() *User {
+	if s == nil {
+		return nil
+	}
+	return s.Sender
+}
+
+// GetFrom returns the From field if it's non-nil, zero value otherwise.
+func (s *SponsorshipTier) GetFrom() string {
+	if s == nil || s.From == nil {
+		return ""
+	}
+	return *s.From
+}
+
 // GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise.
 func (s *SSHSigningKey) GetCreatedAt() Timestamp {
 	if s == nil || s.CreatedAt == nil {
diff --git a/github/messages.go b/github/messages.go
index 72edbd9f..0385d398 100644
--- a/github/messages.go
+++ b/github/messages.go
@@ -102,6 +102,7 @@ var (
 		"secret_scanning_alert":          &SecretScanningAlertEvent{},
 		"security_advisory":              &SecurityAdvisoryEvent{},
 		"security_and_analysis":          &SecurityAndAnalysisEvent{},
+		"sponsorship":                    &SponsorshipEvent{},
 		"star":                           &StarEvent{},
 		"status":                         &StatusEvent{},
 		"team":                           &TeamEvent{},
diff --git a/scrape/go.mod b/scrape/go.mod
index 4b58ef21..04eff1de 100644
--- a/scrape/go.mod
+++ b/scrape/go.mod
@@ -9,7 +9,7 @@ require (
 	github.com/google/go-cmp v0.6.0
 	github.com/google/go-github/v64 v64.0.0
 	github.com/xlzd/gotp v0.1.0
-	golang.org/x/net v0.28.0
+	golang.org/x/net v0.29.0
 )
 
 require (
diff --git a/scrape/go.sum b/scrape/go.sum
index 9bdd6719..f3014057 100644
--- a/scrape/go.sum
+++ b/scrape/go.sum
@@ -21,8 +21,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
 golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
 golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
 golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
-golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
-golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
+golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
+golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=


Test patch:
diff --git a/github/event_types_test.go b/github/event_types_test.go
index 79532d49..e2a8a39f 100644
--- a/github/event_types_test.go
+++ b/github/event_types_test.go
@@ -17885,3 +17885,97 @@ func TestCodeScanningAlertEvent_Marshal(t *testing.T) {
 
 	testJSONMarshal(t, u, want)
 }
+
+func TestSponsorshipEvent_Marshal(t *testing.T) {
+	testJSONMarshal(t, &SponsorshipEvent{}, "{}")
+
+	u := &SponsorshipEvent{
+		Action:        String("created"),
+		EffectiveDate: String("2023-01-01T00:00:00Z"),
+		Changes: &SponsorshipChanges{
+			Tier: &SponsorshipTier{
+				From: String("basic"),
+			},
+			PrivacyLevel: String("public"),
+		},
+		Repository: &Repository{
+			ID:     Int64(12345),
+			NodeID: String("MDEwOlJlcG9zaXRvcnkxMjM0NQ=="),
+			Name:   String("example-repo"),
+		},
+		Organization: &Organization{
+			Login: String("example-org"),
+			ID:    Int64(67890),
+		},
+		Sender: &User{
+			Login: String("example-user"),
+			ID:    Int64(1111),
+		},
+		Installation: &Installation{
+			ID: Int64(2222),
+		},
+	}
+
+	want := `{
+		"action": "created",
+		"effective_date": "2023-01-01T00:00:00Z",
+		"changes": {
+			"tier": {
+				"from": "basic"
+			},
+			"privacy_level": "public"
+		},
+		"repository": {
+			"id": 12345,
+			"node_id": "MDEwOlJlcG9zaXRvcnkxMjM0NQ==",
+			"name": "example-repo"
+		},
+		"organization": {
+			"login": "example-org",
+			"id": 67890
+		},
+		"sender": {
+			"login": "example-user",
+			"id": 1111
+		},
+		"installation": {
+			"id": 2222
+		}
+	}`
+
+	testJSONMarshal(t, u, want)
+}
+
+func TestSponsorshipChanges_Marshal(t *testing.T) {
+	testJSONMarshal(t, &SponsorshipChanges{}, "{}")
+
+	u := &SponsorshipChanges{
+		Tier: &SponsorshipTier{
+			From: String("premium"),
+		},
+		PrivacyLevel: String("private"),
+	}
+
+	want := `{
+		"tier": {
+			"from": "premium"
+		},
+		"privacy_level": "private"
+	}`
+
+	testJSONMarshal(t, u, want)
+}
+
+func TestSponsorshipTier_Marshal(t *testing.T) {
+	testJSONMarshal(t, &SponsorshipTier{}, "{}")
+
+	u := &SponsorshipTier{
+		From: String("gold"),
+	}
+
+	want := `{
+		"from": "gold"
+	}`
+
+	testJSONMarshal(t, u, want)
+}
diff --git a/github/github-accessors_test.go b/github/github-accessors_test.go
index aa694fd5..6d1419c7 100644
--- a/github/github-accessors_test.go
+++ b/github/github-accessors_test.go
@@ -26018,6 +26018,88 @@ func TestSourceImportAuthor_GetURL(tt *testing.T) {
 	s.GetURL()
 }
 
+func TestSponsorshipChanges_GetPrivacyLevel(tt *testing.T) {
+	var zeroValue string
+	s := &SponsorshipChanges{PrivacyLevel: &zeroValue}
+	s.GetPrivacyLevel()
+	s = &SponsorshipChanges{}
+	s.GetPrivacyLevel()
+	s = nil
+	s.GetPrivacyLevel()
+}
+
+func TestSponsorshipChanges_GetTier(tt *testing.T) {
+	s := &SponsorshipChanges{}
+	s.GetTier()
+	s = nil
+	s.GetTier()
+}
+
+func TestSponsorshipEvent_GetAction(tt *testing.T) {
+	var zeroValue string
+	s := &SponsorshipEvent{Action: &zeroValue}
+	s.GetAction()
+	s = &SponsorshipEvent{}
+	s.GetAction()
+	s = nil
+	s.GetAction()
+}
+
+func TestSponsorshipEvent_GetChanges(tt *testing.T) {
+	s := &SponsorshipEvent{}
+	s.GetChanges()
+	s = nil
+	s.GetChanges()
+}
+
+func TestSponsorshipEvent_GetEffectiveDate(tt *testing.T) {
+	var zeroValue string
+	s := &SponsorshipEvent{EffectiveDate: &zeroValue}
+	s.GetEffectiveDate()
+	s = &SponsorshipEvent{}
+	s.GetEffectiveDate()
+	s = nil
+	s.GetEffectiveDate()
+}
+
+func TestSponsorshipEvent_GetInstallation(tt *testing.T) {
+	s := &SponsorshipEvent{}
+	s.GetInstallation()
+	s = nil
+	s.GetInstallation()
+}
+
+func TestSponsorshipEvent_GetOrganization(tt *testing.T) {
+	s := &SponsorshipEvent{}
+	s.GetOrganization()
+	s = nil
+	s.GetOrganization()
+}
+
+func TestSponsorshipEvent_GetRepository(tt *testing.T) {
+	s := &SponsorshipEvent{}
+	s.GetRepository()
+	s = nil
+	s.GetRepository()
+}
+
+func TestSponsorshipEvent_GetSender(tt *testing.T) {
+	s := &SponsorshipEvent{}
+	s.GetSender()
+	s = nil
+	s.GetSender()
+}
+
+func TestSponsorshipTier_GetFrom(tt *testing.T) {
+	var zeroValue string
+	s := &SponsorshipTier{From: &zeroValue}
+	s.GetFrom()
+	s = &SponsorshipTier{}
+	s.GetFrom()
+	s = nil
+	s.GetFrom()
+}
+
 func TestSSHSigningKey_GetCreatedAt(tt *testing.T) {
 	var zeroValue Timestamp
 	s := &SSHSigningKey{CreatedAt: &zeroValue}
diff --git a/github/messages_test.go b/github/messages_test.go
index 1243d755..4783ec68 100644
--- a/github/messages_test.go
+++ b/github/messages_test.go
@@ -468,6 +468,10 @@ func TestParseWebHook(t *testing.T) {
 			payload:     &SecurityAndAnalysisEvent{},
 			messageType: "security_and_analysis",
 		},
+		{
+			payload:     &SponsorshipEvent{},
+			messageType: "sponsorship",
+		},
 		{
 			payload:     &StarEvent{},
 			messageType: "star",