Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit960c5db

Browse files
committed
webpush: refactor notification test and send logic
1 parent46c2cd8 commit960c5db

File tree

3 files changed

+89
-94
lines changed

3 files changed

+89
-94
lines changed

‎coderd/notifications.go‎

Lines changed: 4 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,8 @@ package coderd
33
import (
44
"bytes"
55
"encoding/json"
6-
"io"
76
"net/http"
8-
"strconv"
97

10-
"github.com/SherClockHolmes/webpush-go"
118
"github.com/google/uuid"
129

1310
"cdr.dev/slog"
@@ -345,55 +342,21 @@ func (api *API) postUserWebpushSubscription(rw http.ResponseWriter, r *http.Requ
345342
return
346343
}
347344

348-
notificationJSON,err:=json.Marshal(codersdk.WebpushMessage{
349-
Title:"It's working!",
350-
Body:"You've subscribed to push notifications.",
351-
})
352-
iferr!=nil {
345+
iferr:=api.WebpushDispatcher.Test(ctx,req);err!=nil {
353346
httpapi.Write(ctx,rw,http.StatusInternalServerError, codersdk.Response{
354-
Message:"Failed tomarshal notification",
347+
Message:"Failed totest webpush subscription",
355348
Detail:err.Error(),
356349
})
357350
return
358351
}
359352

360-
// Before inserting the subscription into the database, we send a test notification
361-
// to ensure the subscription is valid.
362-
resp,err:=webpush.SendNotificationWithContext(r.Context(),notificationJSON,&webpush.Subscription{
363-
Endpoint:req.Endpoint,
364-
Keys: webpush.Keys{
365-
Auth:req.AuthKey,
366-
P256dh:req.P256DHKey,
367-
},
368-
},&webpush.Options{
369-
VAPIDPublicKey:api.WebpushDispatcher.PublicKey(),
370-
VAPIDPrivateKey:api.WebpushDispatcher.PrivateKey(),
371-
})
372-
iferr!=nil {
373-
httpapi.Write(ctx,rw,http.StatusInternalServerError, codersdk.Response{
374-
Message:"Failed to send notification",
375-
Detail:err.Error(),
376-
})
377-
return
378-
}
379-
deferresp.Body.Close()
380-
ifresp.StatusCode!=http.StatusCreated {
381-
body,_:=io.ReadAll(resp.Body)
382-
httpapi.Write(ctx,rw,http.StatusInternalServerError, codersdk.Response{
383-
Message:"Failed to send notification. Status code: "+strconv.Itoa(resp.StatusCode),
384-
Detail:string(body),
385-
})
386-
return
387-
}
388-
389-
_,err=api.Database.InsertWebpushSubscription(ctx, database.InsertWebpushSubscriptionParams{
353+
if_,err:=api.Database.InsertWebpushSubscription(ctx, database.InsertWebpushSubscriptionParams{
390354
CreatedAt:dbtime.Now(),
391355
UserID:user.ID,
392356
Endpoint:req.Endpoint,
393357
EndpointAuthKey:req.AuthKey,
394358
EndpointP256dhKey:req.P256DHKey,
395-
})
396-
iferr!=nil {
359+
});err!=nil {
397360
httpapi.Write(ctx,rw,http.StatusInternalServerError, codersdk.Response{
398361
Message:"Failed to insert push notification subscription.",
399362
Detail:err.Error(),

‎coderd/notifications_test.go‎

Lines changed: 25 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -385,40 +385,36 @@ const (
385385
validEndpointP256dhKey="BNNL5ZaTfK81qhXOx23+wewhigUeFb632jN6LvRWCFH1ubQr77FE/9qV1FuojuRmHP42zmf34rXgW80OvUVDgTk="
386386
)
387387

388-
funcTestPushNotificationSubscription(t*testing.T) {
388+
funcTestWebpushSubscribeUnsubscribe(t*testing.T) {
389389
t.Parallel()
390390

391-
t.Run("CRUD",func(t*testing.T) {
392-
t.Parallel()
391+
ctx:=testutil.Context(t,testutil.WaitShort)
393392

394-
ctx:=testutil.Context(t,testutil.WaitShort)
393+
client:=coderdtest.New(t,nil)
394+
owner:=coderdtest.CreateFirstUser(t,client)
395+
memberClient,_:=coderdtest.CreateAnotherUser(t,client,owner.OrganizationID)
395396

396-
client:=coderdtest.New(t,nil)
397-
owner:=coderdtest.CreateFirstUser(t,client)
398-
memberClient,_:=coderdtest.CreateAnotherUser(t,client,owner.OrganizationID)
399-
400-
handlerCalled:=make(chanbool,1)
401-
server:=httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter,_*http.Request) {
402-
w.WriteHeader(http.StatusCreated)
403-
handlerCalled<-true
404-
}))
405-
deferserver.Close()
406-
407-
err:=memberClient.PostWebpushSubscription(ctx,"me", codersdk.WebpushSubscription{
408-
Endpoint:server.URL,
409-
AuthKey:validEndpointAuthKey,
410-
P256DHKey:validEndpointP256dhKey,
411-
})
412-
require.NoError(t,err,"create webpush subscription")
413-
require.True(t,<-handlerCalled,"handler should have been called")
397+
handlerCalled:=make(chanbool,1)
398+
server:=httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter,_*http.Request) {
399+
w.WriteHeader(http.StatusCreated)
400+
handlerCalled<-true
401+
}))
402+
deferserver.Close()
414403

415-
err=memberClient.PostTestWebpushMessage(ctx)
416-
require.NoError(t,err,"test web push notification")
417-
require.True(t,<-handlerCalled,"handler should have been called again")
404+
err:=memberClient.PostWebpushSubscription(ctx,"me", codersdk.WebpushSubscription{
405+
Endpoint:server.URL,
406+
AuthKey:validEndpointAuthKey,
407+
P256DHKey:validEndpointP256dhKey,
408+
})
409+
require.NoError(t,err,"create webpush subscription")
410+
require.True(t,<-handlerCalled,"handler should have been called")
418411

419-
err=memberClient.DeleteWebpushSubscription(ctx,"me", codersdk.DeleteWebpushSubscription{
420-
Endpoint:server.URL,
421-
})
422-
require.NoError(t,err,"delete webpush subscription")
412+
err=memberClient.PostTestWebpushMessage(ctx)
413+
require.NoError(t,err,"test web push notification")
414+
require.True(t,<-handlerCalled,"handler should have been called again")
415+
416+
err=memberClient.DeleteWebpushSubscription(ctx,"me", codersdk.DeleteWebpushSubscription{
417+
Endpoint:server.URL,
423418
})
419+
require.NoError(t,err,"delete webpush subscription")
424420
}

‎coderd/webpush/webpush.go‎

Lines changed: 60 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,12 @@ import (
2424
// Dispatcher is an interface that can be used to dispatch
2525
// push notifications over Web Push.
2626
typeDispatcherinterface {
27+
// Dispatch sends a notification to all subscriptions for a user. Any
28+
// notifications that fail to send are silently dropped.
2729
Dispatch(ctx context.Context,userID uuid.UUID,notification codersdk.WebpushMessage)error
30+
// Test sends a test notification to a subscription to ensure it is valid.
31+
Test(ctx context.Context,req codersdk.WebpushSubscription)error
2832
PublicKey()string
29-
PrivateKey()string
3033
}
3134

3235
// New creates a new Dispatcher to dispatch notifications via Web Push.
@@ -93,24 +96,15 @@ func (n *Webpusher) Dispatch(ctx context.Context, userID uuid.UUID, notification
9396
for_,subscription:=rangesubscriptions {
9497
subscription:=subscription
9598
eg.Go(func()error {
96-
n.log.Debug(ctx,"dispatching via push",slog.F("subscription",subscription.Endpoint))
97-
cpy:=slices.Clone(notificationJSON)// Need to copy as webpush.SendNotificationWithContext modifies the slice.
98-
resp,err:=webpush.SendNotificationWithContext(ctx,cpy,&webpush.Subscription{
99-
Endpoint:subscription.Endpoint,
100-
Keys: webpush.Keys{
101-
Auth:subscription.EndpointAuthKey,
102-
P256dh:subscription.EndpointP256dhKey,
103-
},
104-
},&webpush.Options{
105-
VAPIDPublicKey:n.VAPIDPublicKey,
106-
VAPIDPrivateKey:n.VAPIDPrivateKey,
99+
statusCode,body,err:=n.webpushSend(ctx,notificationJSON,subscription.Endpoint, webpush.Keys{
100+
Auth:subscription.EndpointAuthKey,
101+
P256dh:subscription.EndpointP256dhKey,
107102
})
108103
iferr!=nil {
109104
returnxerrors.Errorf("send notification: %w",err)
110105
}
111-
deferresp.Body.Close()
112106

113-
ifresp.StatusCode==http.StatusGone {
107+
ifstatusCode==http.StatusGone {
114108
// The subscription is no longer valid, remove it.
115109
mu.Lock()
116110
cleanupSubscriptions=append(cleanupSubscriptions,subscription.ID)
@@ -119,10 +113,9 @@ func (n *Webpusher) Dispatch(ctx context.Context, userID uuid.UUID, notification
119113
}
120114

121115
// 200, 201, and 202 are common for successful delivery.
122-
ifresp.StatusCode>http.StatusAccepted {
116+
ifstatusCode>http.StatusAccepted {
123117
// It's likely the subscription failed to deliver for some reason.
124-
body,_:=io.ReadAll(resp.Body)
125-
returnxerrors.Errorf("web push dispatch failed with status code %d: %s",resp.StatusCode,string(body))
118+
returnxerrors.Errorf("web push dispatch failed with status code %d: %s",statusCode,string(body))
126119
}
127120

128121
returnnil
@@ -145,12 +138,55 @@ func (n *Webpusher) Dispatch(ctx context.Context, userID uuid.UUID, notification
145138
returnnil
146139
}
147140

148-
func (n*Webpusher)PublicKey()string {
149-
returnn.VAPIDPublicKey
141+
func (n*Webpusher)webpushSend(ctx context.Context,msg []byte,endpointstring,keys webpush.Keys) (int, []byte,error) {
142+
// Copy the message to avoid modifying the original.
143+
cpy:=slices.Clone(msg)
144+
resp,err:=webpush.SendNotificationWithContext(ctx,cpy,&webpush.Subscription{
145+
Endpoint:endpoint,
146+
Keys:keys,
147+
},&webpush.Options{
148+
VAPIDPublicKey:n.VAPIDPublicKey,
149+
VAPIDPrivateKey:n.VAPIDPrivateKey,
150+
})
151+
iferr!=nil {
152+
return-1,nil,xerrors.Errorf("send notification: %w",err)
153+
}
154+
deferresp.Body.Close()
155+
body,err:=io.ReadAll(resp.Body)
156+
iferr!=nil {
157+
return-1,nil,xerrors.Errorf("read response body: %w",err)
158+
}
159+
160+
returnresp.StatusCode,body,nil
161+
}
162+
163+
func (n*Webpusher)Test(ctx context.Context,req codersdk.WebpushSubscription)error {
164+
notificationJSON,err:=json.Marshal(codersdk.WebpushMessage{
165+
Title:"Test",
166+
Body:"This is a test notification",
167+
})
168+
iferr!=nil {
169+
returnxerrors.Errorf("marshal notification: %w",err)
170+
}
171+
statusCode,body,err:=n.webpushSend(ctx,notificationJSON,req.Endpoint, webpush.Keys{
172+
Auth:req.AuthKey,
173+
P256dh:req.P256DHKey,
174+
})
175+
iferr!=nil {
176+
returnxerrors.Errorf("send test notification: %w",err)
177+
}
178+
179+
// 200, 201, and 202 are common for successful delivery.
180+
ifstatusCode>http.StatusAccepted {
181+
// It's likely the subscription failed to deliver for some reason.
182+
returnxerrors.Errorf("web push dispatch failed with status code %d: %s",statusCode,string(body))
183+
}
184+
185+
returnnil
150186
}
151187

152-
func (n*Webpusher)PrivateKey()string {
153-
returnn.VAPIDPrivateKey
188+
func (n*Webpusher)PublicKey()string {
189+
returnn.VAPIDPublicKey
154190
}
155191

156192
// NoopWebpusher is a Dispatcher that does nothing except return an error.
@@ -164,11 +200,11 @@ func (n *NoopWebpusher) Dispatch(context.Context, uuid.UUID, codersdk.WebpushMes
164200
returnxerrors.New(n.Msg)
165201
}
166202

167-
func (*NoopWebpusher)PublicKey()string {
168-
return""
203+
func (n*NoopWebpusher)Test(context.Context, codersdk.WebpushSubscription)error {
204+
returnxerrors.New(n.Msg)
169205
}
170206

171-
func (*NoopWebpusher)PrivateKey()string {
207+
func (*NoopWebpusher)PublicKey()string {
172208
return""
173209
}
174210

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp