Skip to content

Commit 10d4c87

Browse files
committed
Add delete request endpoint that creates a channel event for FBA channels
1 parent eca9173 commit 10d4c87

File tree

3 files changed

+82
-1
lines changed

3 files changed

+82
-1
lines changed

Diff for: channel_event.go

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ const (
2121
EventTypeWelcomeMessage ChannelEventType = "welcome_message"
2222
EventTypeOptIn ChannelEventType = "optin"
2323
EventTypeOptOut ChannelEventType = "optout"
24+
EventDeletionRequest ChannelEventType = "delete_request"
2425
)
2526

2627
//-----------------------------------------------------------------------------

Diff for: handlers/meta/facebook_test.go

+32
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,38 @@ func TestFacebookDescribeURN(t *testing.T) {
322322
AssertChannelLogRedaction(t, clog, []string{"a123", "wac_admin_system_user_token"})
323323
}
324324

325+
func TestDeleteRequest(t *testing.T) {
326+
RunIncomingTestCases(t, facebookTestChannels, newHandler("FBA", "Facebook"), []IncomingTestCase{
327+
{
328+
Label: "Receive Delete request FBA",
329+
URL: "/c/fba/delete",
330+
Data: `{"algorithm":"HMAC-SHA256","expires":1291840400,"issued_at":1291836800,"user_id":"218471"}`,
331+
PrepRequest: addValidSignature,
332+
333+
ExpectedRespStatus: 200,
334+
ExpectedBodyContains: "Deletion Request Received",
335+
NoQueueErrorCheck: true,
336+
NoInvalidChannelCheck: true,
337+
NoLogsExpected: true,
338+
ExpectedEvents: []ExpectedEvent{
339+
{Type: courier.EventDeletionRequest, URN: "facebook:218471", Extra: map[string]string{"userID": "218471"}},
340+
},
341+
},
342+
{
343+
Label: "Receive Delete request FBA",
344+
URL: "/c/fba/delete",
345+
Data: `{"algorithm":"HMAC-SHA256","expires":1291840400,"issued_at":1291836800,"user_id":"abc1234"}`,
346+
PrepRequest: addValidSignature,
347+
348+
ExpectedRespStatus: 200,
349+
ExpectedBodyContains: "invalid facebook id",
350+
NoQueueErrorCheck: true,
351+
NoInvalidChannelCheck: true,
352+
NoLogsExpected: true,
353+
},
354+
})
355+
}
356+
325357
func TestFacebookVerify(t *testing.T) {
326358
RunIncomingTestCases(t, facebookTestChannels, newHandler("FBA", "Facebook"), []IncomingTestCase{
327359
{

Diff for: handlers/meta/handlers.go

+49-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"time"
1818

1919
"github.com/buger/jsonparser"
20+
"github.com/getsentry/sentry-go"
2021
"github.com/nyaruka/courier"
2122
"github.com/nyaruka/courier/handlers"
2223
"github.com/nyaruka/courier/handlers/meta/messenger"
@@ -86,6 +87,7 @@ func (h *handler) Initialize(s courier.Server) error {
8687
h.SetServer(s)
8788
s.AddHandlerRoute(h, http.MethodGet, "receive", courier.ChannelLogTypeWebhookVerify, h.receiveVerify)
8889
s.AddHandlerRoute(h, http.MethodPost, "receive", courier.ChannelLogTypeMultiReceive, handlers.JSONPayload(h, h.receiveEvents))
90+
s.AddHandlerRoute(h, http.MethodPost, "delete", courier.ChannelLogTypeEventReceive, handlers.JSONPayload(h, h.deleteEvents))
8991
return nil
9092
}
9193

@@ -131,7 +133,7 @@ func (h *handler) WriteRequestError(ctx context.Context, w http.ResponseWriter,
131133

132134
// GetChannel returns the channel
133135
func (h *handler) GetChannel(ctx context.Context, r *http.Request) (courier.Channel, error) {
134-
if r.Method == http.MethodGet {
136+
if r.Method == http.MethodGet || r.URL.Path == "/c/fba/delete" {
135137
return nil, nil
136138
}
137139

@@ -215,6 +217,52 @@ func (h *handler) resolveMediaURL(mediaID string, token string, clog *courier.Ch
215217
return mediaURL, err
216218
}
217219

220+
type DeletionRequestData struct {
221+
Algorithm string `json:"algorithm"`
222+
Expires int64 `json:"expires"`
223+
IssuedAt int64 `json:"issued_at"`
224+
UserID string `json:"user_id"`
225+
}
226+
227+
type DeleteConfirmationData struct {
228+
URL string `json:"url"`
229+
ConfirmationCode string `json:"confirmation_code"`
230+
}
231+
232+
// deleteEvents is our HTTP handler function for deleting data requests
233+
func (h *handler) deleteEvents(ctx context.Context, channel courier.Channel, w http.ResponseWriter, r *http.Request, payload *DeletionRequestData, clog *courier.ChannelLog) ([]courier.Event, error) {
234+
err := h.validateSignature(r)
235+
if err != nil {
236+
return nil, handlers.WriteAndLogRequestError(ctx, h, channel, w, r, err)
237+
}
238+
239+
urn, err := urns.New(urns.Facebook, payload.UserID)
240+
if err != nil {
241+
return nil, handlers.WriteAndLogRequestError(ctx, h, channel, w, r, errors.New("invalid facebook id"))
242+
}
243+
date := parseTimestamp(payload.IssuedAt)
244+
245+
events := make([]courier.Event, 0, 2)
246+
data := make([]any, 0, 2)
247+
248+
payloadJson, _ := json.Marshal(payload)
249+
sentry.CaptureMessage(fmt.Sprintf("Data Deletion Request: %s", payloadJson))
250+
251+
event := h.Backend().NewChannelEvent(channel, courier.EventDeletionRequest, urn, clog).WithOccurredOn(date).WithExtra(map[string]string{"userID": payload.UserID})
252+
253+
err = h.Backend().WriteChannelEvent(ctx, event, clog)
254+
if err != nil {
255+
return nil, err
256+
}
257+
258+
confirmationURL := fmt.Sprintf("https://%s/channels/events/read/%s/", h.Server().Config().Domain, event.UUID())
259+
260+
events = append(events, event)
261+
data = append(data, DeleteConfirmationData{URL: confirmationURL, ConfirmationCode: string(event.UUID())})
262+
263+
return events, courier.WriteDataResponse(w, http.StatusOK, "Deletion Request Received", data)
264+
}
265+
218266
// receiveEvents is our HTTP handler function for incoming messages and status updates
219267
func (h *handler) receiveEvents(ctx context.Context, channel courier.Channel, w http.ResponseWriter, r *http.Request, payload *Notifications, clog *courier.ChannelLog) ([]courier.Event, error) {
220268
err := h.validateSignature(r)

0 commit comments

Comments
 (0)