Repository: nicklaw5/helix
Pull request #104 (2021-07-01 21:51:07)

Add Get/Delete custom rewards


Patch:
diff --git a/README.md b/README.md
index f27bdb8..cb61264 100644
--- a/README.md
+++ b/README.md
@@ -39,8 +39,8 @@ If you are looking for the Twitch API docs, see the [Twitch Developer website](h
 - [x] Modify Channel Information
 - [x] Get Channel Editors
 - [x] Create Custom Rewards
-- [ ] Delete Custom Reward
-- [ ] Get Custom Reward
+- [x] Delete Custom Reward
+- [x] Get Custom Reward
 - [ ] Get Custom Reward Redemption
 - [ ] Update Custom Reward
 - [ ] Update Redemption Status
@@ -55,6 +55,7 @@ If you are looking for the Twitch API docs, see the [Twitch Developer website](h
 - [x] Create Entitlement Grants Upload URL
 - [x] Get Code Status
 - [x] Get Drops Entitlements
+- [ ] Update Drops Entitlements
 - [x] Redeem Code
 - [x] Create / Remove / List EventSub Subscriptions
 - [x] Get Top Games
@@ -72,6 +73,12 @@ If you are looking for the Twitch API docs, see the [Twitch Developer website](h
 - [x] Get Predictions
 - [x] Create Prediction
 - [x] End Prediction
+- [ ] Get Channel Stream Schedule
+- [ ] Get Channel iCalendar
+- [ ] Update Channel Stream Schedule
+- [ ] Create Channel Stream Schedule Segment
+- [ ] Update Channel Stream Schedule Segment
+- [ ] Delete Channel Stream Schedule Segment
 - [ ] Search Categories
 - [ ] Search Channels
 - [ ] Get Stream Key
@@ -89,8 +96,6 @@ If you are looking for the Twitch API docs, see the [Twitch Developer website](h
 - [x] Get Users
 - [x] Update User
 - [x] Get Users Follows
-- [ ] Create User Follows
-- [ ] Delete User Follows
 - [x] Get User Block List
 - [x] Block User
 - [x] UnBlock User
diff --git a/channels_points.go b/channels_points.go
index 2f8dc24..b4831a3 100644
--- a/channels_points.go
+++ b/channels_points.go
@@ -17,6 +17,17 @@ type ChannelCustomRewardsParams struct {
 	ShouldRedemptionsSkipRequestQueue bool   `json:"should_redemptions_skip_request_queue"`
 }
 
+type DeleteCustomRewardsParams struct {
+	BroadcasterID string `query:"broadcaster_id"`
+	ID            string `query:"id"`
+}
+
+type GetCustomRewardsParams struct {
+	BroadcasterID         string `query:"broadcaster_id"`
+	ID                    string `query:"id"`
+	OnlyManageableRewards bool   `query:"only_manageable_rewards"`
+}
+
 type ManyChannelCustomRewards struct {
 	ChannelCustomRewards []ChannelCustomReward `json:"data"`
 }
@@ -69,6 +80,11 @@ type ChannelCustomRewardResponse struct {
 	Data ManyChannelCustomRewards
 }
 
+// Response for removing a custom reward
+type DeleteCustomRewardsResponse struct {
+	ResponseCommon
+}
+
 // CreateCustomReward : Creates a Custom Reward on a channel.
 // Required scope: channel:manage:redemptions
 func (c *Client) CreateCustomReward(params *ChannelCustomRewardsParams) (*ChannelCustomRewardResponse, error) {
@@ -83,3 +99,32 @@ func (c *Client) CreateCustomReward(params *ChannelCustomRewardsParams) (*Channe
 
 	return reward, nil
 }
+
+// DeleteCustomRewards : Deletes a Custom Rewards on a channel
+// Required scope: channel:manage:redemptions
+func (c *Client) DeleteCustomRewards(params *DeleteCustomRewardsParams) (*DeleteCustomRewardsResponse, error) {
+	resp, err := c.delete("/channel_points/custom_rewards", nil, params)
+	if err != nil {
+		return nil, err
+	}
+
+	reward := &DeleteCustomRewardsResponse{}
+	resp.HydrateResponseCommon(&reward.ResponseCommon)
+
+	return reward, nil
+}
+
+// GetCustomRewards : Get Custom Rewards on a channel
+// Required scope: channel:read:redemptions
+func (c *Client) GetCustomRewards(params *GetCustomRewardsParams) (*ChannelCustomRewardResponse, error) {
+	resp, err := c.get("/channel_points/custom_rewards", &ManyChannelCustomRewards{}, params)
+	if err != nil {
+		return nil, err
+	}
+
+	rewards := &ChannelCustomRewardResponse{}
+	resp.HydrateResponseCommon(&rewards.ResponseCommon)
+	rewards.Data.ChannelCustomRewards = resp.Data.(*ManyChannelCustomRewards).ChannelCustomRewards
+
+	return rewards, nil
+}
diff --git a/docs/channels_points_docs.md b/docs/channels_points_docs.md
index cf35709..bf2311d 100644
--- a/docs/channels_points_docs.md
+++ b/docs/channels_points_docs.md
@@ -23,3 +23,48 @@ if err != nil {
 
 fmt.Printf("%+v\n", resp)
 ```
+
+## Delete Custom Rewards
+
+This is an example of how to delete a custom rewards.
+
+```go
+client, err := helix.NewClient(&helix.Options{
+    ClientID: "your-client-id",
+})
+if err != nil {
+    // handle error
+}
+
+resp, err := client.DeleteCustomRewards(&helix.DeleteCustomRewardsParams{
+    BroadcasterID : "145328278",
+    ID            : "84da6b13-efe1-4a82-91d0-25260aeb6a9b",
+})
+if err != nil {
+    // handle error
+}
+
+fmt.Printf("%+v\n", resp)
+```
+
+## Get Custom Rewards
+
+This is an example of how to get a custom rewards.
+
+```go
+client, err := helix.NewClient(&helix.Options{
+    ClientID: "your-client-id",
+})
+if err != nil {
+    // handle error
+}
+
+resp, err := client.GetCustomRewards(&helix.GetCustomRewardsParams{
+    BroadcasterID : "145328278",
+})
+if err != nil {
+    // handle error
+}
+
+fmt.Printf("%+v\n", resp)
+```
diff --git a/docs/eventsub_docs.md b/docs/eventsub_docs.md
index fa38853..44986e6 100644
--- a/docs/eventsub_docs.md
+++ b/docs/eventsub_docs.md
@@ -44,10 +44,10 @@ if err != nil {
 resp, err := client.CreateEventSubSubscription(&helix.EventSubSubscription{
     Type: helix.EventSubTypeChannelFollow,
     Version: "1",
-    Condition: helix.EventSubCondition{
+    Condition &helix.EventSubCondition{
         BroadcasterUserID: "1337",
     },
-    Transport: helix.EventSubTransport{
+    Transport &helix.EventSubTransport{
         Method: "webhook",
         Callback: "https://example.com/follow",
         Secret: "s3cre7w0rd",
@@ -81,6 +81,7 @@ if err != nil {
 fmt.Printf("%+v\n", resp)
 ```
 
+
 ## Example for handling a notification
 
 ```go
@@ -117,7 +118,8 @@ func eventsubFollow(w http.ResponseWriter, r *http.Request) {
         return
     }
     var followEvent helix.EventSubChannelFollowEvent
-    err = json.NewDecoder(bytes.NewReader(vals.Event)).Decode(&followEvent)
+    s, _ := vals.Event.MarshalJSON()
+    err = json.NewDecoder(bytes.NewReader(s)).Decode(&followEvent)
 
     log.Printf("got follow webhook: %s follows %s\n", followEvent.UserName, followEvent.BroadcasterUserName)
     w.WriteHeader(200)
diff --git a/eventsub.go b/eventsub.go
index c692218..301e332 100644
--- a/eventsub.go
+++ b/eventsub.go
@@ -83,8 +83,8 @@ const (
 	EventSubTypeChannelFollow                             = "channel.follow"
 	EventSubTypeChannelSubscription                       = "channel.subscribe"
 	EventSubTypeChannelSubscriptionEnd                    = "channel.subscription.end"
-	EventSubTypeChannelSubscriptionGift                   = "channel.subscription.gift"    /* beta */
-	EventSubTypeChannelSubscriptionMessage                = "channel.subscription.message" /* beta */
+	EventSubTypeChannelSubscriptionGift                   = "channel.subscription.gift"
+	EventSubTypeChannelSubscriptionMessage                = "channel.subscription.message"
 	EventSubTypeChannelCheer                              = "channel.cheer"
 	EventSubTypeChannelRaid                               = "channel.raid"
 	EventSubTypeChannelBan                                = "channel.ban"


Test patch:
diff --git a/channels_points_test.go b/channels_points_test.go
index 589935f..8a66f3e 100644
--- a/channels_points_test.go
+++ b/channels_points_test.go
@@ -74,3 +74,140 @@ func TestCreateCustomReward(t *testing.T) {
 		t.Error("expected error does match return error")
 	}
 }
+
+func TestDeleteCustomRewards(t *testing.T) {
+	t.Parallel()
+
+	testCases := []struct {
+		statusCode int
+		options    *Options
+		params     *DeleteCustomRewardsParams
+		respBody   string
+	}{
+		{
+			http.StatusOK,
+			&Options{ClientID: "my-client-id"},
+			&DeleteCustomRewardsParams{
+				BroadcasterID: "145328278",
+				ID:            "84da6b13-efe1-4a82-91d0-25260aeb6a9b",
+			},
+			``,
+		},
+		{
+			http.StatusBadRequest,
+			&Options{ClientID: "my-client-id"},
+			&DeleteCustomRewardsParams{},
+			`{"error":"Bad Request","status":400,"message":"Missing required parameter \"broadcaster_id\""}`,
+		},
+	}
+
+	for _, testCase := range testCases {
+		c := newMockClient(testCase.options, newMockHandler(testCase.statusCode, testCase.respBody, nil))
+
+		resp, err := c.DeleteCustomRewards(testCase.params)
+		if err != nil {
+			t.Error(err)
+		}
+
+		// Test Bad Request Responses
+		if resp.StatusCode == http.StatusBadRequest {
+			firstErrStr := "Missing required parameter \"broadcaster_id\""
+			if resp.ErrorMessage != firstErrStr {
+				t.Errorf("expected error message to be \"%s\", got \"%s\"", firstErrStr, resp.ErrorMessage)
+			}
+			continue
+		}
+
+		if resp.StatusCode != testCase.statusCode {
+			t.Errorf("expected status code to be \"%d\", got \"%d\"", testCase.statusCode, resp.StatusCode)
+		}
+	}
+
+	// Test with HTTP Failure
+	options := &Options{
+		ClientID: "my-client-id",
+		HTTPClient: &badMockHTTPClient{
+			newMockHandler(0, "", nil),
+		},
+	}
+	c := &Client{
+		opts: options,
+	}
+
+	_, err := c.DeleteCustomRewards(&DeleteCustomRewardsParams{})
+	if err == nil {
+		t.Error("expected error but got nil")
+	}
+
+	if err.Error() != "Failed to execute API request: Oops, that's bad :(" {
+		t.Error("expected error does match return error")
+	}
+}
+
+func TestGetCustomRewards(t *testing.T) {
+	t.Parallel()
+
+	testCases := []struct {
+		statusCode int
+		options    *Options
+		params     *GetCustomRewardsParams
+		respBody   string
+	}{
+		{
+			http.StatusOK,
+			&Options{ClientID: "my-client-id"},
+			&GetCustomRewardsParams{
+				BroadcasterID: "145328278",
+			},
+			`{"data":[{"broadcaster_name":"Scorfly","broadcaster_login":"scorfly","broadcaster_id":"145328278","id":"7e86e512-f423-45b2-9e4b-2ad99a814ffc","image":null,"background_color":"#421561","is_enabled":false,"cost":50000,"title":"game analysis 1v1","prompt":"","is_user_input_required":false,"max_per_stream_setting":{"is_enabled":false,"max_per_stream":0},"max_per_user_per_stream_setting":{"is_enabled":false,"max_per_user_per_stream":0},"global_cooldown_setting":{"is_enabled":false,"global_cooldown_seconds":0},"is_paused":false,"is_in_stock":true,"default_image":{"url_1x":"https://static-cdn.jtvnw.net/custom-reward-images/default-1.png","url_2x":"https://static-cdn.jtvnw.net/custom-reward-images/default-2.png","url_4x":"https://static-cdn.jtvnw.net/custom-reward-images/default-4.png"},"should_redemptions_skip_request_queue":false,"redemptions_redeemed_current_stream":null,"cooldown_expires_at":null},{"broadcaster_name":"Scorfly","broadcaster_login":"scorfly","broadcaster_id":"145328278","id":"c0d6687c-c543-4a3a-8494-502451e7fa45","image":null,"background_color":"#FFBF00","is_enabled":false,"cost":50000,"title":"game analysis 1v2","prompt":"","is_user_input_required":false,"max_per_stream_setting":{"is_enabled":false,"max_per_stream":0},"max_per_user_per_stream_setting":{"is_enabled":false,"max_per_user_per_stream":0},"global_cooldown_setting":{"is_enabled":false,"global_cooldown_seconds":0},"is_paused":false,"is_in_stock":true,"default_image":{"url_1x":"https://static-cdn.jtvnw.net/custom-reward-images/default-1.png","url_2x":"https://static-cdn.jtvnw.net/custom-reward-images/default-2.png","url_4x":"https://static-cdn.jtvnw.net/custom-reward-images/default-4.png"},"should_redemptions_skip_request_queue":false,"redemptions_redeemed_current_stream":null,"cooldown_expires_at":null}]}`,
+		},
+		{
+			http.StatusBadRequest,
+			&Options{ClientID: "my-client-id"},
+			&GetCustomRewardsParams{},
+			`{"error":"Bad Request","status":400,"message":"Missing required parameter \"broadcaster_id\""}`,
+		},
+	}
+
+	for _, testCase := range testCases {
+		c := newMockClient(testCase.options, newMockHandler(testCase.statusCode, testCase.respBody, nil))
+
+		resp, err := c.GetCustomRewards(testCase.params)
+		if err != nil {
+			t.Error(err)
+		}
+
+		// Test Bad Request Responses
+		if resp.StatusCode == http.StatusBadRequest {
+			firstErrStr := "Missing required parameter \"broadcaster_id\""
+			if resp.ErrorMessage != firstErrStr {
+				t.Errorf("expected error message to be \"%s\", got \"%s\"", firstErrStr, resp.ErrorMessage)
+			}
+			continue
+		}
+
+		if resp.StatusCode != testCase.statusCode {
+			t.Errorf("expected status code to be \"%d\", got \"%d\"", testCase.statusCode, resp.StatusCode)
+		}
+	}
+
+	// Test with HTTP Failure
+	options := &Options{
+		ClientID: "my-client-id",
+		HTTPClient: &badMockHTTPClient{
+			newMockHandler(0, "", nil),
+		},
+	}
+	c := &Client{
+		opts: options,
+	}
+
+	_, err := c.GetCustomRewards(&GetCustomRewardsParams{})
+	if err == nil {
+		t.Error("expected error but got nil")
+	}
+
+	if err.Error() != "Failed to execute API request: Oops, that's bad :(" {
+		t.Error("expected error does match return error")
+	}
+}