Skip to content
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

feat: add POC for custom SMS sender Hook #1246

Closed
wants to merge 37 commits into from
Closed
Show file tree
Hide file tree
Changes from 36 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
9d6947b
feat: initial migration schema
Sep 13, 2023
7bc3cfb
chore: rename hooks table add trigger stubs
Sep 13, 2023
e850b02
feat: add trigger mechanism
Sep 13, 2023
0c138ae
feat: add corresponding errors
Sep 13, 2023
496a073
feat: add logic for fetching hook configuration
Sep 13, 2023
759b5e4
feat: link sms sending to trigger
Sep 13, 2023
8c87861
fix: update schemas to include request response
Sep 13, 2023
e5da60a
fix: update the name
Sep 13, 2023
5c50c78
refactor: condense function
Sep 19, 2023
493f655
feat: add separate auth hook logic
Sep 19, 2023
50e1fa8
refactor: change types
Sep 19, 2023
b6bbbff
deps: add jsonschema
Sep 25, 2023
abd73c8
refactor: remove stray statement
Sep 26, 2023
33f1deb
refactor: uncomment more sectoins
Sep 27, 2023
7dabdcf
refactor: add jsonschema and some validation
Sep 27, 2023
0077a2e
refactor: add validation
Sep 27, 2023
480b830
refactor: add response validation
Sep 27, 2023
52a6eb6
refactor: move inputs and outputs to separate file
Sep 27, 2023
64a3af3
refactor: patch some of the TODOs
Oct 4, 2023
9a95a57
refactor: patch some typing issues
Oct 4, 2023
737a71c
fix: cast response
Oct 4, 2023
c129103
refactor: add validation for encode
Oct 4, 2023
9e07026
fix: continue to exclude structs
Oct 6, 2023
4152025
refactor: move transforms to separate file
Oct 9, 2023
e4a4b13
refacotr: remove dated request.json
Oct 9, 2023
f02fd39
refactor: use generated inputs instead
Oct 9, 2023
f7fb237
refactor: generate instead of manually typing
Oct 9, 2023
b5b5277
refactor: add webhookResponseError
Oct 10, 2023
23d0d75
merge in master
Oct 10, 2023
089c509
refactor: update transforms
Oct 10, 2023
1556ff7
fix: update proposed readme
Oct 10, 2023
a52299d
chore: run gofmt
Oct 10, 2023
099f87a
fix: update README
Oct 11, 2023
fb3a37c
refactor: drop unused logged fields
Oct 11, 2023
eacf689
feat: set headers
Oct 11, 2023
cb3cba2
feat: add character set checks on migration
Oct 11, 2023
b8f903c
reafactor: convert claims to custom claims
Oct 30, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ require (
require (
github.com/go-jose/go-jose/v3 v3.0.0 // indirect
github.com/gobuffalo/nulls v0.4.2 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
)

require (
Expand Down
6 changes: 6 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,12 @@ github.com/supabase/mailme v0.0.0-20230628061017-01f68480c747 h1:FIUdLV4o5JLsJno
github.com/supabase/mailme v0.0.0-20230628061017-01f68480c747/go.mod h1:kWsnmPfUBZTavlXYkfJrE9unzmmRAIi/kqsxXfEWEY8=
github.com/tinylib/msgp v1.1.0 h1:9fQd+ICuRIu/ue4vxJZu6/LzxN0HwMds2nq/0cFvxHU=
github.com/tinylib/msgp v1.1.0/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
Expand Down
5 changes: 3 additions & 2 deletions internal/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -229,8 +229,9 @@ func NewAPIWithVersion(ctx context.Context, globalConfig *conf.GlobalConfigurati
})

corsHandler := cors.New(cors.Options{
AllowedMethods: []string{http.MethodGet, http.MethodPost, http.MethodPut, http.MethodDelete},
AllowedHeaders: globalConfig.CORS.AllAllowedHeaders([]string{"Accept", "Authorization", "Content-Type", "X-Client-IP", "X-Client-Info", audHeaderName, useCookieHeader}),
AllowedMethods: []string{http.MethodGet, http.MethodPost, http.MethodPut, http.MethodDelete},
AllowedHeaders: globalConfig.CORS.AllAllowedHeaders([]string{"Accept", "Authorization", "Content-Type", "X-Client-IP", "X-Client-Info", audHeaderName, useCookieHeader, webhookSignatureHeader, webhookTimestampHeader, webhookIDHeader}),
// TODO: Add check if headers need to be exposed as well
ExposedHeaders: []string{"X-Total-Count", "Link"},
AllowCredentials: true,
})
Expand Down
3 changes: 3 additions & 0 deletions internal/api/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,9 @@ func tooManyRequestsError(fmtString string, args ...interface{}) *HTTPError {
func conflictError(fmtString string, args ...interface{}) *HTTPError {
return httpError(http.StatusConflict, fmtString, args...)
}
func webhookResponseError(fmtString string, args ...interface{}) *HTTPError {
return internalServerError(fmt.Sprintf("Webhook returned malformed JSON: %v", fmtString), args)
}

// HTTPError is an error with a message and an HTTP status code.
type HTTPError struct {
Expand Down
198 changes: 198 additions & 0 deletions internal/api/hook_inputs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
package api

import (
"bytes"
"encoding/json"
"errors"
"fmt"
)

// AppMetadata
type AppMetadata struct {
}

// CustomSMSRequest
type CustomSMSRequest struct {

// version of the api
ApiVersion string `json:"api_version"`
UserData *UserData `json:"user"`
}

// UserData
type UserData struct {
AppMetadata *AppMetadata `json:"app_metadata"`

// Stores user attributes which do not impact core functionality
ConfirmedAt string `json:"confirmed_at"`

// Store when a phone has been confirmed. Null if unconfirmed.
Phone string `json:"phone"`
}

func (strct *CustomSMSRequest) MarshalJSON() ([]byte, error) {
buf := bytes.NewBuffer(make([]byte, 0))
buf.WriteString("{")
comma := false
// "ApiVersion" field is required
// only required object types supported for marshal checking (for now)
// Marshal the "api_version" field
if comma {
buf.WriteString(",")
}
buf.WriteString("\"api_version\": ")
if tmp, err := json.Marshal(strct.ApiVersion); err != nil {
return nil, err
} else {
buf.Write(tmp)
}
comma = true
// "UserData" field is required
if strct.UserData == nil {
return nil, errors.New("user is a required field")
}
// Marshal the "user" field
if comma {
buf.WriteString(",")
}
buf.WriteString("\"user\": ")
if tmp, err := json.Marshal(strct.UserData); err != nil {
return nil, err
} else {
buf.Write(tmp)
}
comma = true

buf.WriteString("}")
rv := buf.Bytes()
return rv, nil
}

func (strct *CustomSMSRequest) UnmarshalJSON(b []byte) error {
api_versionReceived := false
userReceived := false
var jsonMap map[string]json.RawMessage
if err := json.Unmarshal(b, &jsonMap); err != nil {
return err
}
// parse all the defined properties
for k, v := range jsonMap {
switch k {
case "api_version":
if err := json.Unmarshal([]byte(v), &strct.ApiVersion); err != nil {
return err
}
api_versionReceived = true
case "user":
if err := json.Unmarshal([]byte(v), &strct.UserData); err != nil {
return err
}
userReceived = true
default:
return fmt.Errorf("additional property not allowed: \"" + k + "\"")
}
}
// check if api_version (a required property) was received
if !api_versionReceived {
return errors.New("\"api_version\" is required but was not present")
}
// check if user (a required property) was received
if !userReceived {
return errors.New("\"user\" is required but was not present")
}
return nil
}

func (strct *UserData) MarshalJSON() ([]byte, error) {
buf := bytes.NewBuffer(make([]byte, 0))
buf.WriteString("{")
comma := false
// "AppMetadata" field is required
if strct.AppMetadata == nil {
return nil, errors.New("app_metadata is a required field")
}
// Marshal the "app_metadata" field
if comma {
buf.WriteString(",")
}
buf.WriteString("\"app_metadata\": ")
if tmp, err := json.Marshal(strct.AppMetadata); err != nil {
return nil, err
} else {
buf.Write(tmp)
}
comma = true
// "ConfirmedAt" field is required
// only required object types supported for marshal checking (for now)
// Marshal the "confirmed_at" field
if comma {
buf.WriteString(",")
}
buf.WriteString("\"confirmed_at\": ")
if tmp, err := json.Marshal(strct.ConfirmedAt); err != nil {
return nil, err
} else {
buf.Write(tmp)
}
comma = true
// "Phone" field is required
// only required object types supported for marshal checking (for now)
// Marshal the "phone" field
if comma {
buf.WriteString(",")
}
buf.WriteString("\"phone\": ")
if tmp, err := json.Marshal(strct.Phone); err != nil {
return nil, err
} else {
buf.Write(tmp)
}
comma = true

buf.WriteString("}")
rv := buf.Bytes()
return rv, nil
}

func (strct *UserData) UnmarshalJSON(b []byte) error {
app_metadataReceived := false
confirmed_atReceived := false
phoneReceived := false
var jsonMap map[string]json.RawMessage
if err := json.Unmarshal(b, &jsonMap); err != nil {
return err
}
// parse all the defined properties
for k, v := range jsonMap {
switch k {
case "app_metadata":
if err := json.Unmarshal([]byte(v), &strct.AppMetadata); err != nil {
return err
}
app_metadataReceived = true
case "confirmed_at":
if err := json.Unmarshal([]byte(v), &strct.ConfirmedAt); err != nil {
return err
}
confirmed_atReceived = true
case "phone":
if err := json.Unmarshal([]byte(v), &strct.Phone); err != nil {
return err
}
phoneReceived = true
}
}
// check if app_metadata (a required property) was received
if !app_metadataReceived {
return errors.New("\"app_metadata\" is required but was not present")
}
// check if confirmed_at (a required property) was received
if !confirmed_atReceived {
return errors.New("\"confirmed_at\" is required but was not present")
}
// check if phone (a required property) was received
if !phoneReceived {
return errors.New("\"phone\" is required but was not present")
}
return nil
}
Loading
Loading