Skip to content

Commit 5459ac2

Browse files
committed
PROPOSAL: helix extension endpoints
- JWT signing - extension configuration segments endpoints - extension secrets endpoints - UPDATE extension docs Update Docs - remove role parameter when creating claim Updates to extension endpoints PR - update go mod to use https://github.com/golang-jwt/jwt - gofmt on project - validation check error on extension options owner id HOTFIX: json unmarshalling - adapt secrets structs to properly handle api response - add missing json tag for many extension segment configurations HOTFIX: broadcaster ID in extension configuration requests The Twitch docs are not really clear on some parts of the requests getExtensionConfiguration: - broadcasterID is an optional query parameter setExtensionConfiguration: - broadcasterId is an optional body parameter - validate the segment type if broadcasterID is provided MISC: - update 'GetExtensionSecret' -> 'GetExtensionSecrets' HOTFIX: pubsub requests - fix pubsub requests to be an array Extension Configuration - refactor setExtension params - refactor Extension Configuration Segment consts - add validation to get extension configuration if broadcaster Id provided - add unit test for getExtensionConfigurationSegment - add unit test for. setExtensionConfigurationSegment Remove unnecessary options - remove options from the extension options config, as these can just be provided in parameters Extension Tests Add unit tests - JWT tests - pubsub - extension required - extension send chat message Verify JWT Params test - add test to verify params
1 parent 343474f commit 5459ac2

15 files changed

+1197
-8
lines changed

README.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -105,16 +105,16 @@ If you are looking for the Twitch API docs, see the [Twitch Developer website](h
105105
- [x] Get Videos
106106
- [x] Delete Videos
107107
- [x] Get Webhook Subscriptions
108-
- [ ] Create Extension Secret
109-
- [ ] Get Extension Secret
108+
- [x] Create Extension Secret
109+
- [x] Get Extension Secret
110110
- [ ] Revoke Extension Secrets
111111
- [ ] Get Live Channels with Extension Activated
112-
- [ ] Set Extension Required Configuration
113-
- [ ] Set Extension Configuration Segment
112+
- [x] Set Extension Required Configuration
113+
- [x] Set Extension Configuration Segment
114114
- [ ] Get Extension Channel Configuration
115-
- [ ] Get Extension Configuration Segment
116-
- [ ] Send Extension PubSub Message
117-
- [ ] Send Extension Chat Message
115+
- [x] Get Extension Configuration Segment
116+
- [x] Send Extension PubSub Message
117+
- [x] Send Extension Chat Message
118118

119119
## Quick Usage Example
120120

docs/extensions_docs.md

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,98 @@
11
# Extensions Documentation
22

3+
## Extension Helix Requests
4+
5+
### Generate PUBSUB JWT Permissions
6+
> relevant PUBSUB permission must be passed to the 'ExtensionCreateClaims()' func, in order to correctly publish a pubsub message of a particular type
7+
8+
Broadcast pubsub type
9+
```go
10+
client.FormBroadcastSendPubSubPermissions()
11+
```
12+
13+
Global pubsub type
14+
```go
15+
perms := client.FormGlobalSendPubSubPermissions()
16+
```
17+
18+
Whisper User type
19+
```go
20+
client.FormWhisperSendPubSubPermissions(userId)
21+
```
22+
23+
### JWT ROLES
24+
> Note:- Currently only the 'external' role is supported by helix endpoints
25+
26+
27+
### EBS JWT
28+
this is used to set the correct header for any Extension helix requests
29+
30+
```go
31+
client, err := helix.NewClient(&helix.Options{
32+
ClientID: "your-client-id",
33+
UserAccessToken: "your-user-access-token",
34+
ExtensionOpts: helix.ExtensionOptions{
35+
OwnerUserID: os.Getenv(""),
36+
Secret: os.Getenv(""),
37+
ConfigurationVersion: os.Getenv(""),
38+
Version: os.Getenv(""),
39+
},
40+
})
41+
42+
43+
// see docs below to see what pub-sub permissions you can pass
44+
claims, err := client.ExtensionCreateClaims(broadcasterID, client.FormBroadcastSendPubSubPermissions(), 0)
45+
if err != nil {
46+
// handle err
47+
}
48+
49+
jwt,err := client.ExtensionJWTSign(claims)
50+
if err != nil {
51+
// handle err
52+
}
53+
54+
// set this before doing extension endpoint requests
55+
client.SetExtensionSignedJWTToken(jwt)
56+
```
57+
## Get Extension Configuration Segments
58+
59+
```go
60+
61+
client, err := helix.NewClient(&helix.Options{
62+
ClientID: "your-client-id",
63+
UserAccessToken: "your-user-access-token",
64+
ExtensionOpts: helix.ExtensionOptions{
65+
OwnerUserID: os.Getenv("EXT_OWNER_ID"),
66+
Secret: os.Getenv("EXT_SECRET"),
67+
ConfigurationVersion: os.Getenv("EXT_CFG_VERSION"),
68+
Version: os.Getenv("EXT_VERSION"),
69+
},
70+
})
71+
if err != nil {
72+
// handle error
73+
}
74+
75+
claims, err := client.ExtensionCreateClaims(broadcasterID, ExternalRole, FormBroadcastSendPubSubPermissions(), 0)
76+
if err != nil {
77+
// handle error
78+
}
79+
80+
// set the JWT token to be used as in the Auth bearer header
81+
jwt := client.ExtensionJWTSign(claims)
82+
client.SetExtensionSignedJWTToken(jwt)
83+
84+
params := helix.ExtensionGetConfigurationParams{
85+
ExtensionID: "some-extension-id", // Required
86+
Segments: []helix.ExtensionSegmentType{helix.GlobalSegment}, // Optional
87+
}
88+
resp, err := client.GetExtensionConfigurationSegment
89+
if err != nil {
90+
// handle error
91+
}
92+
93+
fmt.Printf("%+v\n", resp)
94+
```
95+
396
## Get Extension Transactions
497

598
```go

extension_configuration.go

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
package helix
2+
3+
import "fmt"
4+
5+
// SegmentType A segment configuration type
6+
type ExtensionSegmentType string
7+
8+
// Types of segments datastores for the configuration service
9+
const (
10+
ExtensionConfigrationBroadcasterSegment ExtensionSegmentType = "broadcaster"
11+
ExtensionConfigurationDeveloperSegment ExtensionSegmentType = "developer"
12+
ExtensionConfigurationGlobalSegment ExtensionSegmentType = "global"
13+
)
14+
15+
func (s ExtensionSegmentType) String() string {
16+
return string(s)
17+
}
18+
19+
type ExtensionSetConfigurationParams struct {
20+
Segment ExtensionSegmentType `json:"segment"`
21+
ExtensionID string `json:"extension-id"`
22+
BroadcasterID string `json:"broadcaster_id,omitempty"` // populated if segment is of type 'developer' || 'broadcaster'
23+
Version string `json:"version"`
24+
Content string `json:"content"`
25+
}
26+
27+
type ExtensionConfigurationSegment struct {
28+
Segment ExtensionSegmentType `json:"segment"`
29+
Version string `json:"version"`
30+
Content string `json:"content"`
31+
}
32+
33+
type ExtensionGetConfigurationParams struct {
34+
ExtensionID string `query:"extension_id"`
35+
BroadcasterID string `query:"broadcaster_id"`
36+
Segments []ExtensionSegmentType `query:"segment"`
37+
}
38+
39+
type ExtensionSetRequiredConfigurationParams struct {
40+
BroadcasterID string `query:"broadcaster_id" json:"-"`
41+
ExtensionID string `json:"extension_id"`
42+
RequiredConfiguration string `json:"required_version"`
43+
ExtensionVersion string `json:"extension_version"`
44+
ConfigurationVersion string `json:"configuration_version"`
45+
}
46+
47+
type ExtensionSetRequiredConfigurationResponse struct {
48+
ResponseCommon
49+
}
50+
51+
type ExtensionGetConfigurationSegmentResponse struct {
52+
ResponseCommon
53+
Data ManyExtensionConfigurationSegments
54+
}
55+
56+
type ManyExtensionConfigurationSegments struct {
57+
Segments []ExtensionConfigurationSegment `json:"data"`
58+
}
59+
60+
type ExtensionSetConfigurationResponse struct {
61+
ResponseCommon
62+
}
63+
64+
// https://dev.twitch.tv/docs/extensions/reference/#set-extension-configuration-segment
65+
func (c *Client) SetExtensionSegmentConfig(params *ExtensionSetConfigurationParams) (*ExtensionSetConfigurationResponse, error) {
66+
if params.BroadcasterID != "" {
67+
switch params.Segment {
68+
case ExtensionConfigurationDeveloperSegment, ExtensionConfigrationBroadcasterSegment:
69+
default:
70+
return nil, fmt.Errorf("error: developer or broadcaster extension configuration segment type must be provided for broadcasters")
71+
}
72+
}
73+
74+
resp, err := c.putAsJSON("/extensions/configurations", &ManyPolls{}, params)
75+
if err != nil {
76+
return nil, err
77+
}
78+
79+
setExtCnfgResp := &ExtensionSetConfigurationResponse{}
80+
resp.HydrateResponseCommon(&setExtCnfgResp.ResponseCommon)
81+
82+
return setExtCnfgResp, nil
83+
}
84+
85+
func (c *Client) GetExtensionConfigurationSegment(params *ExtensionGetConfigurationParams) (*ExtensionGetConfigurationSegmentResponse, error) {
86+
87+
if params.BroadcasterID != "" {
88+
for _, segment := range params.Segments {
89+
switch segment {
90+
case ExtensionConfigurationDeveloperSegment, ExtensionConfigrationBroadcasterSegment:
91+
default:
92+
return nil, fmt.Errorf("error: only developer or broadcaster extension configuration segment type must be provided for broadcasters")
93+
}
94+
}
95+
}
96+
97+
resp, err := c.get("/extensions/configurations", &ManyExtensionConfigurationSegments{}, params)
98+
if err != nil {
99+
return nil, err
100+
}
101+
102+
extCfgSegResp := &ExtensionGetConfigurationSegmentResponse{}
103+
resp.HydrateResponseCommon(&extCfgSegResp.ResponseCommon)
104+
extCfgSegResp.Data.Segments = resp.Data.(*ManyExtensionConfigurationSegments).Segments
105+
106+
return extCfgSegResp, nil
107+
}
108+
109+
func (c *Client) SetExtensionRequiredConfiguration(params *ExtensionSetRequiredConfigurationParams) (*ExtensionSetRequiredConfigurationResponse, error) {
110+
111+
resp, err := c.putAsJSON("/extensions/configurations/required_configuration", &ExtensionSetRequiredConfigurationResponse{}, params)
112+
if err != nil {
113+
return nil, err
114+
}
115+
116+
extReqCfgResp := &ExtensionSetRequiredConfigurationResponse{}
117+
resp.HydrateResponseCommon(&extReqCfgResp.ResponseCommon)
118+
119+
return extReqCfgResp, nil
120+
}

0 commit comments

Comments
 (0)