Repository: nicklaw5/helix
Pull request #82 (2021-04-18 22:41:06)

Add Delete Video and Get Channel Editors features
Reorder api entrypoint as Twitch documentation
Part of #74 issue


Patch:
diff --git a/README.md b/README.md
index 09fc3e6..b8d862b 100644
--- a/README.md
+++ b/README.md
@@ -30,10 +30,19 @@ looking for the Twitch API docs, see the [Twitch Developer website](https://dev.
 
 - [x] Start Commercial
 - [x] Get Extension Analytics
-- [x] Get Cheermotes
 - [x] Get Game Analytics
 - [x] Get Bits Leaderboard
+- [x] Get Cheermotes
 - [ ] Get Extension Transactions
+- [x] Get Channel Information
+- [x] Modify Channel Information
+- [x] Get Channel Editors
+- [ ] Create Custom Rewards
+- [ ] Delete Custom Reward
+- [ ] Get Custom Reward
+- [ ] Get Custom Reward Redemption
+- [ ] Update Custom Reward
+- [ ] Update Redemption Status
 - [x] Create Clip
 - [x] Get Clip
 - [x] Get Clips
@@ -41,12 +50,13 @@ looking for the Twitch API docs, see the [Twitch Developer website](https://dev.
 - [x] Get Code Status
 - [x] Get Drops Entitlements
 - [x] Redeem Code
+- [x] Create / Remove / List EventSub Subscriptions
 - [x] Get Top Games
 - [x] Get Games
 - [ ] Get Hype Train Events
 - [ ] Check AutoMod Status
-- [x] Get Banned Users
 - [ ] Get Banned Events
+- [x] Get Banned Users
 - [ ] Get Moderators
 - [ ] Get Moderator Events
 - [ ] Search Categories
@@ -55,24 +65,27 @@ looking for the Twitch API docs, see the [Twitch Developer website](https://dev.
 - [x] Get Streams
 - [x] Create Stream Marker
 - [x] Get Stream Markers
-- [x] Get Channel Information
-- [x] Modify Channel Information
 - [x] Get Broadcaster Subscriptions
 - [x] Check User Subscription
 - [ ] Get All Stream Tags
 - [ ] Get Stream Tags
 - [ ] Replace Stream Tags
-- [ ] Create User Follows
-- [ ] Delete User Follows
+- [ ] Get Channel Teams
+- [ ] Get Teams
 - [x] Get Users
+- [x] Update User
 - [x] Get Users Follows
+- [ ] Create User Follows
+- [ ] Delete User Follows
+- [ ] Get User Block List
+- [ ] Block User
+- [ ] UnBlock User
 - [ ] Get User Extensions
 - [ ] Get User Active Extensions
-- [x] Update User
 - [ ] Update User Extensions
 - [x] Get Videos
+- [x] Delete Videos
 - [x] Get Webhook Subscriptions
-- [x] Create / Remove / List EventSub Subscriptions
 - [ ] Create Extension Secret
 - [ ] Get Extension Secret
 - [ ] Revoke Extension Secrets
diff --git a/channels_editors.go b/channels_editors.go
new file mode 100644
index 0000000..be6485c
--- /dev/null
+++ b/channels_editors.go
@@ -0,0 +1,39 @@
+package helix
+
+// ChannelEditorParams ...
+type ChannelEditorsParams struct {
+	BroadcasterID  string `query:"broadcaster_id"`
+}
+
+// ManyChannelEditors ...
+type ManyChannelEditors struct {
+	ChannelEditors   [] ChannelEditor `json:"data"`
+}
+
+// ChannelEditor
+type ChannelEditor struct {
+	UserID    string `json:"user_id"`
+	UserName  string `json:"user_name"`
+	CreatedAt Time   `json:"created_at"`
+}
+
+// ChannelEditorsResponse ...
+type ChannelEditorsResponse struct {
+	ResponseCommon
+	Data ManyChannelEditors
+}
+
+// GetChannelEditors Get a list of users who have editor permissions for a specific channel
+// Required scope: channel:read:editors
+func (c *Client) GetChannelEditors(params *ChannelEditorsParams) (*ChannelEditorsResponse, error) {
+	resp, err := c.get("/channels/editors", &ManyChannelEditors{}, params)
+	if err != nil {
+		return nil, err
+	}
+
+	editors := &ChannelEditorsResponse{}
+	resp.HydrateResponseCommon(&editors.ResponseCommon)
+	editors.Data.ChannelEditors = resp.Data.(*ManyChannelEditors).ChannelEditors
+
+	return editors, nil
+}
diff --git a/docs/videos_docs.md b/docs/videos_docs.md
index 8a090ac..598ae9d 100644
--- a/docs/videos_docs.md
+++ b/docs/videos_docs.md
@@ -25,3 +25,27 @@ if err != nil {
 
 fmt.Printf("%+v\n", resp)
 ```
+
+## Delete Videos
+
+This is an example of how to delete videos.
+
+```go
+client, err := helix.NewClient(&helix.Options{
+    ClientID: "your-client-id",
+})
+if err != nil {
+    // handle error
+}
+
+resp, err := client.DeleteVideos(&helix.DeleteVideosParams{
+    IDs: []string{"992599293"},
+})
+if err != nil {
+    // handle error
+}
+
+fmt.Printf("%+v\n", resp)
+```
+
+
diff --git a/videos.go b/videos.go
index 2d642c9..0919b10 100644
--- a/videos.go
+++ b/videos.go
@@ -49,12 +49,22 @@ type VideosParams struct {
 	Type     string `query:"type,all"`   // "all" (default), "upload", "archive", and "highlight"
 }
 
+// DeleteVideosParams ...
+type DeleteVideosParams struct {
+	IDs    []string `query:"id"` // Limit 5
+}
+
 // VideosResponse ...
 type VideosResponse struct {
 	ResponseCommon
 	Data ManyVideos
 }
 
+// DeleteVideosResponse ...
+type DeleteVideosResponse struct {
+	ResponseCommon
+}
+
 // GetVideos gets video information by video ID (one or more), user ID (one only),
 // or game ID (one only).
 func (c *Client) GetVideos(params *VideosParams) (*VideosResponse, error) {
@@ -70,3 +80,17 @@ func (c *Client) GetVideos(params *VideosParams) (*VideosResponse, error) {
 
 	return videos, nil
 }
+
+// DeleteVideos delete one or more videos (max 5)
+// Required scope: channel:manage:videos
+func (c *Client) DeleteVideos(params *DeleteVideosParams) (*DeleteVideosResponse, error) {
+	resp, err := c.delete("/videos", &DeleteVideosResponse{}, params)
+	if err != nil {
+		return nil, err
+	}
+
+	videos := &DeleteVideosResponse{}
+	resp.HydrateResponseCommon(&videos.ResponseCommon)
+
+	return videos, nil
+}


Test patch:
diff --git a/channels_editors_test.go b/channels_editors_test.go
new file mode 100644
index 0000000..68ee872
--- /dev/null
+++ b/channels_editors_test.go
@@ -0,0 +1,54 @@
+package helix
+
+import (
+	"net/http"
+	"testing"
+)
+
+func TestGetChannelEditors(t *testing.T) {
+	t.Parallel()
+
+	testCases := []struct {
+		statusCode    int
+		options       *Options
+		BroadcasterID string
+		respBody      string
+	}{
+		{
+			http.StatusOK,
+			&Options{ClientID: "my-client-id"},
+			"145328278",
+			`{"data":[{"user_id":"123","user_name":"user_name","created_at":"2018-03-06T15:07:45Z"}]}`,
+		},
+		{
+			http.StatusBadRequest,
+			&Options{ClientID: "my-client-id"},
+			"",
+			`{"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.GetChannelEditors(&ChannelEditorsParams{
+			BroadcasterID: testCase.BroadcasterID,
+		})
+		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)
+		}
+	}
+}
diff --git a/videos_test.go b/videos_test.go
index 919eb2a..424ee92 100644
--- a/videos_test.go
+++ b/videos_test.go
@@ -70,3 +70,57 @@ func TestGetVideos(t *testing.T) {
 		}
 	}
 }
+
+func TestDeleteVideos(t *testing.T) {
+	t.Parallel()
+
+	testCases := []struct {
+		statusCode         int
+		options            *Options
+		DeleteVideosParams *DeleteVideosParams
+		respBody           string
+	}{
+		{
+			http.StatusBadRequest,
+			&Options{ClientID: "my-client-id"},
+			&DeleteVideosParams{IDs: []string{}},
+			`{"error":"Bad Request","status":400,"message":"The parameter \"id\" was malformed: the value must be greater than or equal to 1"}`,
+		},
+		{
+			http.StatusOK,
+			&Options{ClientID: "my-client-id"},
+			&DeleteVideosParams{IDs: []string{"456741"}},
+			"",
+		},
+	}
+
+	for _, testCase := range testCases {
+		c := newMockClient(testCase.options, newMockHandler(testCase.statusCode, testCase.respBody, nil))
+
+		resp, err := c.DeleteVideos(testCase.DeleteVideosParams)
+		if err != nil {
+			t.Error(err)
+		}
+
+		if resp.StatusCode != testCase.statusCode {
+			t.Errorf("expected status code to be %d, got %d", testCase.statusCode, resp.StatusCode)
+		}
+
+		if resp.StatusCode == http.StatusBadRequest {
+			if resp.Error != "Bad Request" {
+				t.Errorf("expected error to be %s, got %s", "Bad Request", resp.Error)
+			}
+
+			if resp.ErrorStatus != http.StatusBadRequest {
+				t.Errorf("expected error status to be %d, got %d", http.StatusBadRequest, resp.ErrorStatus)
+			}
+
+			expectedErrMsg := "The parameter \"id\" was malformed: the value must be greater than or equal to 1"
+			if resp.ErrorMessage != expectedErrMsg {
+				t.Errorf("expected error message to be %s, got %s", expectedErrMsg, resp.ErrorMessage)
+			}
+
+			continue
+		}
+	}
+}