[eventsub] Fix: Properly handle 409 error

by fetching the existing subscription instead of failing to access the
expected response which is not there in case of a collision

Signed-off-by: Knut Ahlers <knut@ahlers.me>
This commit is contained in:
Knut Ahlers 2024-03-28 00:09:28 +01:00
parent 0e4a963bc7
commit 1d4cbd9a66
Signed by: luzifer
SSH Key Fingerprint: SHA256:/xtE5lCgiRDQr8SLxHMS92ZBlACmATUmF1crK16Ks4E

View File

@ -6,10 +6,12 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"net/http" "net/http"
"net/url"
"time" "time"
"github.com/mitchellh/hashstructure/v2" "github.com/mitchellh/hashstructure/v2"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/sirupsen/logrus"
) )
// Collection of known EventSub event-types // Collection of known EventSub event-types
@ -277,8 +279,10 @@ func (c *Client) createEventSubSubscriptionWebsocket(ctx context.Context, sub ev
func (c *Client) createEventSubSubscription(ctx context.Context, auth AuthType, sub eventSubSubscription) (*eventSubSubscription, error) { func (c *Client) createEventSubSubscription(ctx context.Context, auth AuthType, sub eventSubSubscription) (*eventSubSubscription, error) {
var ( var (
buf = new(bytes.Buffer) buf = new(bytes.Buffer)
resp struct { err error
mustFetchSubsctiption bool
resp struct {
Total int64 `json:"total"` Total int64 `json:"total"`
Data []eventSubSubscription `json:"data"` Data []eventSubSubscription `json:"data"`
Pagination struct { Pagination struct {
@ -287,11 +291,16 @@ func (c *Client) createEventSubSubscription(ctx context.Context, auth AuthType,
} }
) )
if err := json.NewEncoder(buf).Encode(sub); err != nil { conHash, err := sub.Condition.Hash()
if err != nil {
return nil, fmt.Errorf("hashing input condition: %w", err)
}
if err = json.NewEncoder(buf).Encode(sub); err != nil {
return nil, errors.Wrap(err, "assemble subscribe payload") return nil, errors.Wrap(err, "assemble subscribe payload")
} }
if err := c.Request(ctx, ClientRequestOpts{ if err = c.Request(ctx, ClientRequestOpts{
AuthType: auth, AuthType: auth,
Body: buf, Body: buf,
Method: http.MethodPost, Method: http.MethodPost,
@ -301,6 +310,7 @@ func (c *Client) createEventSubSubscription(ctx context.Context, auth AuthType,
ValidateFunc: func(opts ClientRequestOpts, resp *http.Response) error { ValidateFunc: func(opts ClientRequestOpts, resp *http.Response) error {
if resp.StatusCode == http.StatusConflict { if resp.StatusCode == http.StatusConflict {
// This is fine: We needed that subscription, it exists // This is fine: We needed that subscription, it exists
mustFetchSubsctiption = true
return nil return nil
} }
@ -308,8 +318,46 @@ func (c *Client) createEventSubSubscription(ctx context.Context, auth AuthType,
return ValidateStatus(opts, resp) return ValidateStatus(opts, resp)
}, },
}); err != nil { }); err != nil {
return nil, errors.Wrap(err, "executing request") return nil, fmt.Errorf("creating subscription: %w", err)
} }
return &resp.Data[0], nil if mustFetchSubsctiption {
params := make(url.Values)
params.Set("status", "enabled")
params.Set("type", sub.Type)
if err = c.Request(ctx, ClientRequestOpts{
AuthType: auth,
Method: http.MethodGet,
OKStatus: http.StatusOK,
Out: &resp,
URL: fmt.Sprintf("https://api.twitch.tv/helix/eventsub/subscriptions?%s", params.Encode()),
}); err != nil {
return nil, fmt.Errorf("fetching subscription: %w", err)
}
}
for i := range resp.Data {
s := resp.Data[i]
if s.Type != sub.Type || s.Version != sub.Version {
// Not the subscription we're searching for
continue
}
sConHash, err := s.Condition.Hash()
if err != nil {
logrus.WithError(err).Error("hashing eventsub subscription condition")
continue
}
if sConHash != conHash {
// Different conditions
continue
}
return &s, nil
}
return nil, fmt.Errorf("no subscription matching input found")
} }