-
-
Notifications
You must be signed in to change notification settings - Fork 12
Add webhooks for OURA #901
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
toddkazakov
merged 24 commits into
BACK-3960-oura-connection
from
tk-oura-webhooks-shopify
Jan 14, 2026
Merged
Changes from 3 commits
Commits
Show all changes
24 commits
Select commit
Hold shift + click to select a range
61a0934
Add jotform webhooks for Oura
toddkazakov 599118c
Use the configured jotform API base url
toddkazakov 48c809f
Ensure user exists before creating consent
toddkazakov 01c769f
Parse date time fields correctly
toddkazakov aea76a4
Add tests for jotform webhooks
toddkazakov e29bcbf
Process oura sizing kit fulfillment webhooks from shopify
toddkazakov da418c4
Add tests for fulfillment event webhooks
toddkazakov 9a99e16
Add shopify webhook routes
toddkazakov 80d6b51
Use fulfillment created/updated instead of fulfillment_events to allo…
toddkazakov 0242687
Use test store product id
toddkazakov 20a5de8
Use product GID when creating discount code
toddkazakov a4b8333
Configure discount codes correctly
toddkazakov a951e35
Fix default customer.io track api base url
toddkazakov 27ec01d
Use id instead of cid when sending events to customer io
toddkazakov d0d7ec6
Remove some of the unused fields of the fulfillment event
toddkazakov a3aa538
Add webhook for orders create events to notify customer.io of placed …
toddkazakov 9995641
Add tests for shopify orders
toddkazakov a9e257a
Generate account linking code when ring is delivered
toddkazakov 6b33383
Add oura account linking token expiration time
toddkazakov 8af6266
Address code review feedback
toddkazakov d0e99a7
Reorganize the oura releated code
toddkazakov 7b057b0
Use the client module to implement the customerio client
toddkazakov 002e3c0
Remove unused variable
toddkazakov e52c935
Remove import alias
toddkazakov File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,96 @@ | ||
| package customerio | ||
|
|
||
| import ( | ||
| "context" | ||
| "encoding/base64" | ||
| "encoding/json" | ||
| "fmt" | ||
| "net/http" | ||
|
|
||
| "github.com/tidepool-org/platform/client" | ||
| "github.com/tidepool-org/platform/errors" | ||
| "github.com/tidepool-org/platform/log" | ||
| "github.com/tidepool-org/platform/request" | ||
| ) | ||
|
|
||
| type Client struct { | ||
| appClient *client.Client | ||
| trackClient *client.Client | ||
| config Config | ||
| logger log.Logger | ||
| httpClient *http.Client | ||
| } | ||
|
|
||
| type Config struct { | ||
| AppAPIBaseURL string `envconfig:"TIDEPOOL_CUSTOMERIO_APP_API_BASE_URL" default:"https://api.customer.io"` | ||
| AppAPIKey string `envconfig:"TIDEPOOL_CUSTOMERIO_APP_API_KEY"` | ||
| SiteID string `envconfig:"TIDEPOOL_CUSTOMERIO_SITE_ID"` | ||
| TrackAPIBaseURL string `envconfig:"TIDEPOOL_CUSTOMERIO_TRACK_API_BASE_URL" default:"https://track.customer.io"` | ||
| TrackAPIKey string `envconfig:"TIDEPOOL_CUSTOMERIO_TRACK_API_KEY"` | ||
| } | ||
|
|
||
| func NewClient(config Config, logger log.Logger) (*Client, error) { | ||
| errorParser := newErrorResponseParser() | ||
|
|
||
| appClient, err := client.NewWithErrorParser(&client.Config{ | ||
| Address: config.AppAPIBaseURL, | ||
| }, errorParser) | ||
| if err != nil { | ||
| return nil, errors.Wrap(err, "failed to create app API client") | ||
| } | ||
|
|
||
| trackClient, err := client.NewWithErrorParser(&client.Config{ | ||
| Address: config.TrackAPIBaseURL, | ||
| }, errorParser) | ||
| if err != nil { | ||
| return nil, errors.Wrap(err, "failed to create track API client") | ||
| } | ||
|
|
||
| return &Client{ | ||
| appClient: appClient, | ||
| trackClient: trackClient, | ||
| config: config, | ||
| logger: logger, | ||
| httpClient: http.DefaultClient, | ||
| }, nil | ||
| } | ||
|
|
||
| // appAPIAuthMutator returns a request mutator for App API authentication (Bearer token) | ||
| func (c *Client) appAPIAuthMutator() *request.HeaderMutator { | ||
| return request.NewHeaderMutator("Authorization", fmt.Sprintf("Bearer %s", c.config.AppAPIKey)) | ||
| } | ||
|
|
||
| // trackAPIAuthMutator returns a request mutator for Track API authentication (Basic auth) | ||
| func (c *Client) trackAPIAuthMutator() *request.HeaderMutator { | ||
| auth := c.config.SiteID + ":" + c.config.TrackAPIKey | ||
| basicAuth := "Basic " + base64.StdEncoding.EncodeToString([]byte(auth)) | ||
| return request.NewHeaderMutator("Authorization", basicAuth) | ||
| } | ||
|
|
||
| // errorResponseParser implements client.ErrorResponseParser for Customer.io API errors | ||
| type errorResponseParser struct{} | ||
|
|
||
| func newErrorResponseParser() *errorResponseParser { | ||
| return &errorResponseParser{} | ||
| } | ||
|
|
||
| func (p *errorResponseParser) ParseErrorResponse(ctx context.Context, res *http.Response, req *http.Request) error { | ||
| var errResp errorResponse | ||
| if err := json.NewDecoder(res.Body).Decode(&errResp); err != nil { | ||
| return nil | ||
| } | ||
|
|
||
| if len(errResp.Errors) > 0 { | ||
| return errors.Newf("API error (status %d): %s", res.StatusCode, errResp.Errors[0].Message) | ||
| } | ||
|
|
||
| return nil | ||
| } | ||
|
|
||
| type errorResponse struct { | ||
| Errors []struct { | ||
| Reason string `json:"reason,omitempty"` | ||
| Field string `json:"field,omitempty"` | ||
| Message string `json:"message,omitempty"` | ||
| } `json:"errors,omitempty"` | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Has there ever been more than one error here? If so, it might be useful to in some way at least log the other errors—they might provide useful hints as to what went wrong.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've only seen a single error during testing