Skip to content

Commit c1aef88

Browse files
committed
event configure command
1 parent c35214b commit c1aef88

File tree

6 files changed

+181
-11
lines changed

6 files changed

+181
-11
lines changed

cmd/events.go

+60-8
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010

1111
"github.com/spf13/cobra"
1212
"github.com/twitchdev/twitch-cli/internal/events"
13+
configure_event "github.com/twitchdev/twitch-cli/internal/events/configure"
1314
"github.com/twitchdev/twitch-cli/internal/events/trigger"
1415
"github.com/twitchdev/twitch-cli/internal/events/types"
1516
"github.com/twitchdev/twitch-cli/internal/events/verify"
@@ -25,6 +26,7 @@ var (
2526
forwardAddress string
2627
event string
2728
transport string
29+
noConfig bool
2830
fromUser string
2931
toUser string
3032
giftUser string
@@ -127,16 +129,25 @@ var startWebsocketServerCmd = &cobra.Command{
127129
Deprecated: `use "twitch event websocket start-server" instead.`,
128130
}
129131

132+
var configureEventCmd = &cobra.Command{
133+
Use: "configure",
134+
Short: "Allows users to configure defaults for the twitch event subcommands.",
135+
RunE: configureEventRun,
136+
Example: `twitch event configure`,
137+
}
138+
130139
func init() {
131140
rootCmd.AddCommand(eventCmd)
132141

133-
eventCmd.AddCommand(triggerCmd, retriggerCmd, verifyCmd, websocketCmd, startWebsocketServerCmd)
142+
eventCmd.AddCommand(triggerCmd, retriggerCmd, verifyCmd, websocketCmd, startWebsocketServerCmd, configureEventCmd)
143+
eventCmd.Flags().BoolVarP(&noConfig, "no-config", "D", false, "Disables the use of the configuration, if it exists.")
134144

135145
// trigger flags
136146
//// flags for forwarding functionality/changing payloads
137147
triggerCmd.Flags().StringVarP(&forwardAddress, "forward-address", "F", "", "Forward address for mock event (webhook only).")
138148
triggerCmd.Flags().StringVarP(&transport, "transport", "T", "webhook", fmt.Sprintf("Preferred transport method for event. Defaults to /EventSub.\nSupported values: %s", events.ValidTransports()))
139149
triggerCmd.Flags().StringVarP(&secret, "secret", "s", "", "Webhook secret. If defined, signs all forwarded events with the SHA256 HMAC and must be 10-100 characters in length.")
150+
triggerCmd.Flags().BoolVarP(&noConfig, "no-config", "D", false, "Disables the use of the configuration, if it exists.")
140151

141152
// trigger flags
142153
//// per-topic flags
@@ -167,6 +178,7 @@ func init() {
167178
retriggerCmd.Flags().StringVarP(&forwardAddress, "forward-address", "F", "", "Forward address for mock event (webhook only).")
168179
retriggerCmd.Flags().StringVarP(&eventID, "id", "i", "", "ID of the event to be refired.")
169180
retriggerCmd.Flags().StringVarP(&secret, "secret", "s", "", "Webhook secret. If defined, signs all forwarded events with the SHA256 HMAC and must be 10-100 characters in length.")
181+
retriggerCmd.Flags().BoolVarP(&noConfig, "no-config", "D", false, "Disables the use of the configuration, if it exists.")
170182
retriggerCmd.MarkFlagRequired("id")
171183

172184
// verify-subscription flags
@@ -176,7 +188,7 @@ func init() {
176188
verifyCmd.Flags().StringVar(&timestamp, "timestamp", "", "Sets the timestamp to be used in payloads and headers. Must be in RFC3339Nano format.")
177189
verifyCmd.Flags().StringVarP(&eventID, "subscription-id", "u", "", "Manually set the subscription/event ID of the event itself.") // TODO: This description will need to change with https://github.com/twitchdev/twitch-cli/issues/184
178190
verifyCmd.Flags().StringVarP(&version, "version", "v", "", "Chooses the EventSub version used for a specific event. Not required for most events.")
179-
verifyCmd.MarkFlagRequired("forward-address")
191+
verifyCmd.Flags().BoolVarP(&noConfig, "no-config", "D", false, "Disables the use of the configuration, if it exists.")
180192

181193
// websocket flags
182194
/// flags for start-server
@@ -192,6 +204,10 @@ func init() {
192204
websocketCmd.Flags().StringVar(&wsSubscription, "subscription", "", `Subscription to target with your server command. Used with "websocket subscription".`)
193205
websocketCmd.Flags().StringVar(&wsStatus, "status", "", `Changes the status of an existing subscription. Used with "websocket subscription".`)
194206
websocketCmd.Flags().StringVar(&wsReason, "reason", "", `Sets the close reason when sending a Close message to the client. Used with "websocket close".`)
207+
208+
// configure flags
209+
configureEventCmd.Flags().StringVarP(&forwardAddress, "forward-address", "F", "", "Forward address for mock event (webhook only).")
210+
configureEventCmd.Flags().StringVarP(&secret, "secret", "s", "", "Webhook secret. If defined, signs all forwarded events with the SHA256 HMAC and must be 10-100 characters in length.")
195211
}
196212

197213
func triggerCmdRun(cmd *cobra.Command, args []string) error {
@@ -204,8 +220,14 @@ func triggerCmdRun(cmd *cobra.Command, args []string) error {
204220
return fmt.Errorf(websubDeprecationNotice)
205221
}
206222

207-
if secret != "" && (len(secret) < 10 || len(secret) > 100) {
208-
return fmt.Errorf("Invalid secret provided. Secrets must be between 10-100 characters")
223+
defaults := configure_event.GetEventConfiguration(noConfig)
224+
225+
if secret != "" {
226+
if len(secret) < 10 || len(secret) > 100 {
227+
return fmt.Errorf("Invalid secret provided. Secrets must be between 10-100 characters")
228+
}
229+
} else {
230+
secret = defaults.Secret
209231
}
210232

211233
// Validate that the forward address is actually a URL
@@ -214,6 +236,8 @@ func triggerCmdRun(cmd *cobra.Command, args []string) error {
214236
if err != nil {
215237
return err
216238
}
239+
} else {
240+
forwardAddress = defaults.ForwardAddress
217241
}
218242

219243
for i := 0; i < count; i++ {
@@ -260,8 +284,21 @@ func retriggerCmdRun(cmd *cobra.Command, args []string) error {
260284
return fmt.Errorf(websubDeprecationNotice)
261285
}
262286

263-
if secret != "" && (len(secret) < 10 || len(secret) > 100) {
264-
return fmt.Errorf("Invalid secret provided. Secrets must be between 10-100 characters")
287+
defaults := configure_event.GetEventConfiguration(noConfig)
288+
289+
if secret != "" {
290+
if len(secret) < 10 || len(secret) > 100 {
291+
return fmt.Errorf("Invalid secret provided. Secrets must be between 10-100 characters")
292+
}
293+
} else {
294+
secret = defaults.Secret
295+
}
296+
297+
if forwardAddress == "" {
298+
if defaults.ForwardAddress == "" {
299+
return fmt.Errorf("if a default configuration is not set, forward-address must be provided")
300+
}
301+
forwardAddress = defaults.ForwardAddress
265302
}
266303

267304
res, err := trigger.RefireEvent(eventID, trigger.TriggerParameters{
@@ -287,8 +324,14 @@ func verifyCmdRun(cmd *cobra.Command, args []string) error {
287324
return fmt.Errorf(websubDeprecationNotice)
288325
}
289326

290-
if secret != "" && (len(secret) < 10 || len(secret) > 100) {
291-
return fmt.Errorf("Invalid secret provided. Secrets must be between 10-100 characters")
327+
defaults := configure_event.GetEventConfiguration(noConfig)
328+
329+
if secret != "" {
330+
if len(secret) < 10 || len(secret) > 100 {
331+
return fmt.Errorf("Invalid secret provided. Secrets must be between 10-100 characters")
332+
}
333+
} else {
334+
secret = defaults.Secret
292335
}
293336

294337
// Validate that the forward address is actually a URL
@@ -297,6 +340,8 @@ func verifyCmdRun(cmd *cobra.Command, args []string) error {
297340
if err != nil {
298341
return err
299342
}
343+
} else {
344+
forwardAddress = defaults.ForwardAddress
300345
}
301346

302347
if timestamp == "" {
@@ -352,3 +397,10 @@ func websocketCmdRun(cmd *cobra.Command, args []string) error {
352397

353398
return nil
354399
}
400+
401+
func configureEventRun(cmd *cobra.Command, args []string) error {
402+
return configure_event.ConfigureEvents(configure_event.EventConfigurationParams{
403+
ForwardAddress: forwardAddress,
404+
Secret: secret,
405+
})
406+
}

docs/event.md

+21-3
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,31 @@
22

33
- [Events](#events)
44
- [Description](#description)
5+
- [Configure](#configure)
56
- [Trigger](#trigger)
67
- [Retrigger](#retrigger)
78
- [Verify-Subscription](#verify-subscription)
8-
- [Websocket](#websocket)
9+
- [WebSocket](#websocket)
910

1011
## Description
1112

12-
The `event` product contains commands to trigger mock events for local webhook testing or migration.
13+
The `event` command contains subcommands to trigger mock events for local webhook testing or migration.
1314

1415
All commands exit the program with a non-zero exit code when the command fails, including when an event does not exist, or when the mock EventSub WebSocket server does not start correctly.
1516

17+
18+
## Configure
19+
20+
Used to configure the forwarding address and/or the secret used with the `trigger`, `verify-subscription`, and `retrigger` subcommands.
21+
22+
**Flags**
23+
24+
| Flag | Shorthand | Description | Example | Required? (Y/N) |
25+
|---------------------------|-----------|---------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------|-----------------|
26+
| `--forward-address` | `-F` | Web server address for where to send mock events. | `-F https://localhost:8080` | N |
27+
| `--secret` | `-s` | Webhook secret. If defined, signs all forwarded events with the SHA256 HMAC and must be 10-100 characters in length. | `-s testsecret` | N |
28+
29+
1630
## Trigger
1731

1832
Used to either create or send mock events for use with local webhooks testing.
@@ -92,6 +106,7 @@ This command can take either the Event or Alias listed as an argument. It is pre
92106
| `--gift-user` | `-g` | Used only for subcription-based events, denotes the gifting user ID. | `-g 44635596` | N |
93107
| `--item-id` | `-i` | Manually set the ID of the event payload item (for example the reward ID in redemption events or game in stream events). | `-i 032e4a6c-4aef-11eb-a9f5-1f703d1f0b92` | N |
94108
| `--item-name` | `-n` | Manually set the name of the event payload item (for example the reward ID in redemption events or game name in stream events). | `-n "Science & Technology"` | N |
109+
| `--no-config` | `-D` | Disables the use of the configuration values should they exist. | `-D` | N |
95110
| `--secret` | `-s` | Webhook secret. If defined, signs all forwarded events with the SHA256 HMAC and must be 10-100 characters in length. | `-s testsecret` | N |
96111
| `--session` | | WebSocket session to target. Only used when forwarding to WebSocket servers with --transport=websocket | `--session e411cc1e_a2613d4e` | N |
97112
| `--subscription-id` | `-u` | Manually set the subscription/event ID of the event itself. | `-u 5d3aed06-d019-11ed-afa1-0242ac120002` | N |
@@ -134,8 +149,10 @@ None
134149
|---------------------|-----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------|-----------------|
135150
| `--forward-address` | `-F` | Web server address for where to send mock events. | `-F https://localhost:8080` | N |
136151
| `--id` | `-i` | The ID of the event to refire. | `-i <id>` | Y |
152+
| `--no-config` | `-D` | Disables the use of the configuration values should they exist. | `-D` | N |
137153
| `--secret` | `-s` | Webhook secret. If defined, signs all forwarded events with the SHA256 HMAC and must be 10-100 characters in length. | `-s testsecret` | N |
138154

155+
139156
**Examples**
140157

141158
```sh
@@ -144,7 +161,7 @@ twitch event retrigger -i "713f3254-0178-9757-7439-d779400c0999" -F https://loca
144161

145162
## Verify-Subscription
146163

147-
Allows you to test if your webserver responds to subscription requests properly.
164+
Allows you to test if your webserver responds to subscription requests properly. The `forward-address` flag is required *unless* you have configured a default forwarding address via `twitch event configure -F <address>`.
148165

149166
**Args**
150167

@@ -155,6 +172,7 @@ This command takes the same arguments as [Trigger](#trigger).
155172
| Flag | Shorthand | Description | Example | Required? (Y/N) |
156173
|---------------------|-----------|----------------------------------------------------------------------------------------------------------------------|-----------------------------|-----------------|
157174
| `--forward-address` | `-F` | Web server address for where to send mock subscription. | `-F https://localhost:8080` | Y |
175+
| `--no-config` | `-D` | Disables the use of the configuration values should they exist. | `-D` | N |
158176
| `--secret` | `-s` | Webhook secret. If defined, signs all forwarded events with the SHA256 HMAC and must be 10-100 characters in length. | `-s testsecret` | N |
159177
| `--transport` | `-T` | The method used to send events. Default is `eventsub`. | `-T eventsub` | N |
160178

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package configure_event
2+
3+
import (
4+
"fmt"
5+
"net/url"
6+
7+
"github.com/spf13/viper"
8+
"github.com/twitchdev/twitch-cli/internal/util"
9+
)
10+
11+
type EventConfigurationParams struct {
12+
Secret string
13+
ForwardAddress string
14+
}
15+
16+
func ConfigureEvents(p EventConfigurationParams) error {
17+
var err error
18+
if p.ForwardAddress == "" && p.Secret == "" {
19+
return fmt.Errorf("you must provide at least one of --secret or --forward-address")
20+
}
21+
22+
// Validate that the forward address is actually a URL
23+
if len(p.ForwardAddress) > 0 {
24+
_, err := url.ParseRequestURI(p.ForwardAddress)
25+
if err != nil {
26+
return err
27+
}
28+
viper.Set("forwardAddress", p.ForwardAddress)
29+
}
30+
if p.Secret != "" {
31+
if len(p.Secret) < 10 || len(p.Secret) > 100 {
32+
return fmt.Errorf("invalid secret provided. Secrets must be between 10-100 characters")
33+
}
34+
viper.Set("eventSecret", p.Secret)
35+
}
36+
37+
configPath, err := util.GetConfigPath()
38+
if err != nil {
39+
return err
40+
}
41+
42+
if err := viper.WriteConfigAs(configPath); err != nil {
43+
return fmt.Errorf("failed to write configuration: %v", err.Error())
44+
}
45+
46+
fmt.Println("Updated configuration.")
47+
return nil
48+
}
49+
50+
func GetEventConfiguration(noConfig bool) EventConfigurationParams {
51+
if noConfig {
52+
return EventConfigurationParams{}
53+
}
54+
return EventConfigurationParams{
55+
ForwardAddress: viper.GetString("forwardAddress"),
56+
Secret: viper.GetString("eventSecret"),
57+
}
58+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package configure_event_test
2+
3+
import (
4+
"testing"
5+
6+
"github.com/spf13/viper"
7+
configure_event "github.com/twitchdev/twitch-cli/internal/events/configure"
8+
"github.com/twitchdev/twitch-cli/test_setup"
9+
)
10+
11+
func TestWriteEventConfig(t *testing.T) {
12+
a := test_setup.SetupTestEnv(t)
13+
defaultForwardAddress := "http://localhost:3000/"
14+
defaultSecret := "12345678910"
15+
test_config := configure_event.EventConfigurationParams{
16+
ForwardAddress: defaultForwardAddress,
17+
Secret: defaultSecret,
18+
}
19+
20+
// test a good config writes correctly
21+
a.NoError(configure_event.ConfigureEvents(test_config))
22+
23+
a.Equal(defaultForwardAddress, viper.Get("forwardAddress"))
24+
a.Equal(defaultSecret, viper.Get("eventSecret"))
25+
26+
// test for secret length validation
27+
test_config.Secret = "1"
28+
a.Error(configure_event.ConfigureEvents(test_config))
29+
a.NotEqual("1", viper.Get("eventSecret"))
30+
test_config.Secret = defaultSecret
31+
32+
// test for forward address validation
33+
test_config.ForwardAddress = "not a url"
34+
a.Error(configure_event.ConfigureEvents(test_config))
35+
a.NotEqual("not a url", viper.Get("forwardAddress"))
36+
}

internal/util/path.go

+5
Original file line numberDiff line numberDiff line change
@@ -58,5 +58,10 @@ func GetConfigPath() (string, error) {
5858

5959
configPath := filepath.Join(home, ".twitch-cli.env")
6060

61+
// purely for testing purposes- this allows us to run tests without overwriting the user's config
62+
if os.Getenv("GOLANG_TESTING") == "true" {
63+
configPath = filepath.Join(home, ".twitch-cli-test.env")
64+
}
65+
6166
return configPath, nil
6267
}

test_setup/test_setup.go

+1
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,6 @@ func SetupTestEnv(t *testing.T) *assert.Assertions {
2121
viper.SetConfigType("env")
2222

2323
viper.Set("DB_FILENAME", "test-eventCache.db")
24+
t.Setenv("GOLANG_TESTING", "true")
2425
return assert
2526
}

0 commit comments

Comments
 (0)