Repository: nicklaw5/helix
Pull request #133 (2021-12-28 11:59:48)

Add Get Extension Live Channels
- Implement Extension Live Channels Helix endpoint
- Unit test for Extension Live Channels
- Updated Supported Endpoints

coveralls: ## Pull Request Test Coverage Report for [Build 1630376567](https://coveralls.io/builds/45261526)

* **12** of **14**   **(85.71%)**  changed or added relevant lines in **1** file are covered.
* No unchanged relevant lines lost coverage.
* Overall coverage increased (+**0.9%**) to **92.451%**

---

|  Changes Missing Coverage | Covered Lines | Changed/Added Lines | % |
| :-----|--------------|--------|---: |
| [extensions.go](https://coveralls.io/builds/45261526/source?filename=extensions.go#L133) | 12 | 14 | 85.71%
<!-- | **Total:** | **12** | **14** | **85.71%** | -->


|  Totals | [![Coverage Status](https://coveralls.io/builds/45261526/badge)](https://coveralls.io/builds/45261526) |
| :-- | --: |
| Change from base [Build 1564710043](https://coveralls.io/builds/44922728): |  0.9% |
| Covered Lines: | 1237 |
| Relevant Lines: | 1338 |

---
##### 💛  - [Coveralls](https://coveralls.io)


Patch:
diff --git a/SUPPORTED_ENDPOINTS.md b/SUPPORTED_ENDPOINTS.md
index 749d408..63fd0e0 100644
--- a/SUPPORTED_ENDPOINTS.md
+++ b/SUPPORTED_ENDPOINTS.md
@@ -90,7 +90,7 @@
 - [x] Create Extension Secret
 - [x] Get Extension Secret
 - [ ] Revoke Extension Secrets
-- [ ] Get Live Channels with Extension Activated
+- [x] Get Live Channels with Extension Activated
 - [x] Set Extension Required Configuration
 - [x] Set Extension Configuration Segment
 - [ ] Get Extension Channel Configuration
diff --git a/extensions.go b/extensions.go
index af61cf1..293e6a0 100644
--- a/extensions.go
+++ b/extensions.go
@@ -54,6 +54,30 @@ type ExtensionSendChatMessageResponse struct {
 	ResponseCommon
 }
 
+type ExtensionLiveChannel struct {
+	BroadcasterID   string `json:"broadcaster_id"`
+	BroadcasterName string `json:"broadcaster_name"`
+	GameName        string `json:"game_name"`
+	GameID          string `json:"game_id"`
+	Title           string `json:"title"`
+}
+
+type ManyExtensionLiveChannels struct {
+	LiveChannels []ExtensionLiveChannel `json:"data"`
+	Pagination   string                 `json:"pagination"`
+}
+
+type ExtensionLiveChannelsParams struct {
+	ExtensionID string `query:"extension_id"` // Required
+	After       string `query:"after"`        // Optional
+	First       int    `query:"first,20"`     // Optional, Limit 100
+}
+
+type ExtensionLiveChannelsResponse struct {
+	ResponseCommon
+	Data ManyExtensionLiveChannels
+}
+
 // GetExtensionTransactions allows extension back end servers to fetch a list of transactions that
 // have occurred for their extension across all of Twitch. A transaction is a record of a user
 // exchanging Bits for an in-Extension digital good.
@@ -97,3 +121,21 @@ func (c *Client) SendExtensionChatMessage(params *ExtensionSendChatMessageParams
 
 	return sndExtMsgResp, nil
 }
+
+func (c *Client) GetExtensionLiveChannels(params *ExtensionLiveChannelsParams) (*ExtensionLiveChannelsResponse, error) {
+
+	if params.ExtensionID == "" {
+		return nil, fmt.Errorf("error: extension ID must be specified")
+	}
+
+	resp, err := c.get("/extensions/live", &ManyExtensionLiveChannels{}, params)
+	if err != nil {
+		return nil, err
+	}
+
+	liveChannels := &ExtensionLiveChannelsResponse{}
+	resp.HydrateResponseCommon(&liveChannels.ResponseCommon)
+	liveChannels.Data.LiveChannels = resp.Data.(*ManyExtensionLiveChannels).LiveChannels
+	liveChannels.Data.Pagination = resp.Data.(*ManyExtensionLiveChannels).Pagination
+	return liveChannels, nil
+}


Test patch:
diff --git a/extensions_test.go b/extensions_test.go
index 212671f..2e963e4 100644
--- a/extensions_test.go
+++ b/extensions_test.go
@@ -100,6 +100,69 @@ func TestGetExtensionTransactions(t *testing.T) {
 	}
 }
 
+func TestGetExtensionLiveChannels(t *testing.T) {
+	t.Parallel()
+
+	testCases := []struct {
+		statusCode     int
+		options        *Options
+		params         *ExtensionLiveChannelsParams
+		respBody       string
+		expectedErrMsg string
+	}{
+		{
+			http.StatusOK,
+			&Options{ClientID: "my-client-id"},
+			&ExtensionLiveChannelsParams{ExtensionID: "some-extension-id"},
+			`{ "data": [{ "broadcaster_id": "121086094", "broadcaster_name": "khaizer93", "game_name": "Art", "game_id": "509660", "title": "random artstream sketching Kiryu COCO" }, { "broadcaster_id": "165951395", "broadcaster_name": "MelloTodd", "game_name": "Jackbox Party Packs", "game_id": "493174", "title": "[OPEN] WInner Choses Next Game (1-8) | !jackbox !uptime #envtuber" }, { "broadcaster_id": "21724294", "broadcaster_name": "Mahoog47", "game_name": "Escape from Tarkov", "game_id": "491931", "title": "LVL 39| The holy relic Ash-12 has been acquired" }, { "broadcaster_id": "253663808", "broadcaster_name": "MrHatcher_", "game_name": "Dota 2", "game_id": "29595", "title": "Road to 53/55 followers! Dota 2 Ranked 1k mmr (british/filipino)" }, { "broadcaster_id": "245641098", "broadcaster_name": "ChoKoii", "game_name": "Escape from Tarkov", "game_id": "491931", "title": "First Drops Enabled Stream? | Labs Main | 1 Follower=5 push-ups" }, { "broadcaster_id": "268444856", "broadcaster_name": "D4RK_5KY", "game_name": "Always On", "game_id": "499973", "title": "24/7 FULLSEND HOST RAFFLE - Need THAT #SUPPORT!? #Affiliate PUSH!? Try Your LUCK \u0026 WIN The Raffle!" }, { "broadcaster_id": "42871388", "broadcaster_name": "mieudiary", "game_name": "Stardew Valley", "game_id": "490744", "title": "I'm very sleepy but let's farm | !melaomi" }, { "broadcaster_id": "429972112", "broadcaster_name": "andy_gra", "game_name": "twitch", "game_id": "", "title": "wbijaj smialo :)" }, { "broadcaster_id": "486154226", "broadcaster_name": "mrboone521", "game_name": "Escape from Tarkov", "game_id": "491931", "title": "TARK TARK offline and scav runs" }, { "broadcaster_id": "503028811", "broadcaster_name": "Uwlsy2k", "game_name": "Fortnite", "game_id": "33214", "title": "Bot Zonewars?! Join up and chat " }, { "broadcaster_id": "520878515", "broadcaster_name": "me_fon", "game_name": "Teamfight Tactics", "game_id": "513143", "title": "Ranking up in TFT Mob" }, { "broadcaster_id": "521301629", "broadcaster_name": "acrolic_", "game_name": "Apex Legends", "game_id": "511224", "title": "Come say hi! |road to 50 followers | song choices" }, { "broadcaster_id": "54270050", "broadcaster_name": "ELIASS_1", "game_name": "SCUM", "game_id": "495811", "title": "walking simulator 2022 | !setup | !sleep  | 386/400 followers | " }, { "broadcaster_id": "611701485", "broadcaster_name": "Semmy_22", "game_name": "Overwatch", "game_id": "488552", "title": "Support slave at your service " }, { "broadcaster_id": "63501619", "broadcaster_name": "KittySinisterr", "game_name": "Fortnite", "game_id": "33214", "title": "Winterfest challenges" }, { "broadcaster_id": "625059457", "broadcaster_name": "unisclan", "game_name": "Battlefield 2042", "game_id": "514974", "title": "crazy gameplay tanks   will not live " }, { "broadcaster_id": "604281079", "broadcaster_name": "viperarishyt", "game_name": "FIFA 22", "game_id": "1869092879", "title": "Grab Your Breakfast and Join me. Lets chat :-)" }, { "broadcaster_id": "666411722", "broadcaster_name": "SarahBree", "game_name": "Apex Legends", "game_id": "511224", "title": "Winter express only before it goes away :'(" }, { "broadcaster_id": "647613771", "broadcaster_name": "batbat0508", "game_name": "Battlefield 4", "game_id": "66402", "title": "( GOVS ) ~Fr-En ~ rules (LOCKER)" }, { "broadcaster_id": "653487605", "broadcaster_name": "honka2019", "game_name": "Identity V", "game_id": "508662", "title": "JPN♡本日も23:30頃までまったりプレイ⚠️mobile play" }], "pagination": "YVc1emRHRnNiQ00yTXpVd01UWXhPVHBsT1ROalpqZzNNekJ1WkRFeGVqZG5aWEJyYkhreVozSjVOV3QyT0dzNjoz" }`,		"",
+		},
+		{
+			http.StatusOK,
+			&Options{ClientID: "my-client-id"},
+			&ExtensionLiveChannelsParams{ExtensionID: "", First: 2},
+			`{"error":"Bad Request","status":400,"message":"missing extension id"}`,
+			"error: extension ID must be specified",
+		},
+	}
+
+	for _, testCase := range testCases {
+		c := newMockClient(testCase.options, newMockHandler(testCase.statusCode, testCase.respBody, nil))
+
+		resp, err := c.GetExtensionLiveChannels(testCase.params)
+		if err != nil {
+			if err.Error() == testCase.expectedErrMsg {
+				continue
+			}
+
+			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.StatusForbidden {
+			if resp.Error != "Forbidden" {
+				t.Errorf("expected error to be \"%s\", got \"%s\"", "Bad Request", resp.Error)
+			}
+
+			if resp.ErrorStatus != http.StatusForbidden {
+				t.Errorf("expected error status to be \"%d\", got \"%d\"", http.StatusForbidden, resp.ErrorStatus)
+			}
+
+			if resp.ErrorMessage != testCase.expectedErrMsg {
+				t.Errorf("expected error message to be \"%s\", got \"%s\"", testCase.expectedErrMsg, resp.ErrorMessage)
+			}
+
+			continue
+		}
+
+		if testCase.params.First != 0 && testCase.params.First != len(resp.Data.LiveChannels) {
+			t.Errorf("expected %d transactions, got %d", testCase.params.First, len(resp.Data.LiveChannels))
+		}
+	}
+}
+
 func TestExtensionSendChatMessage(t *testing.T) {
 	t.Parallel()