Skip to content

Commit cb79f40

Browse files
committed
feat: API changes to support RSS feeds
After this change, openapi stopped generating the WebhookConfigTypeWebhook constant. Hence the updates in the go files. The API changes are: 1. Add /v1/subscriptions/{subscription_id}/rss endpoint for public RSS feeds This endpoint will accept a subscription ID and fetch the 'n' most recent diffs/updates 2. Update CreateNotificationChannelRequest and UpdateNotificationChannelRequest to support RSSConfig RSS config is an empty object and is pretty much just a placeholder to make it a first class citizen in the codebase like Webhook and email
1 parent 9c0020c commit cb79f40

File tree

10 files changed

+131
-29
lines changed

10 files changed

+131
-29
lines changed

backend/pkg/httpserver/create_notification_channel.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ func validateNotificationChannel(input *backend.CreateNotificationChannelRequest
3333
fieldErrors.addFieldError("name", errNotificationChannelInvalidNameLength)
3434
}
3535

36-
if cfg, err := input.Config.AsWebhookConfig(); err == nil && cfg.Type == backend.WebhookConfigTypeWebhook {
36+
if cfg, err := input.Config.AsWebhookConfig(); err == nil && cfg.Type == backend.WebhookConfigType("webhook") {
37+
3738
if err := httputils.ValidateSlackWebhookURL(cfg.Url); err != nil {
3839
fieldErrors.addFieldError("config.url", err)
3940
}

backend/pkg/httpserver/create_notification_channel_test.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -54,17 +54,17 @@ func TestCreateNotificationChannel(t *testing.T) {
5454
expectedRequest: backend.CreateNotificationChannelRequest{
5555
Name: "My Webhook",
5656
Config: newTestCreateNotificationChannelConfig(t, backend.WebhookConfig{
57-
Type: backend.WebhookConfigTypeWebhook,
58-
Url: "https://hooks.slack.com/services/123",
57+
Type: backend.WebhookConfigType("webhook"),
58+
Url: "https://hooks.slack.com/services/123",
5959
}),
6060
},
6161
output: &backend.NotificationChannelResponse{
6262
Id: "channel123",
6363
Name: "My Webhook",
6464
Type: backend.NotificationChannelResponseTypeWebhook,
6565
Config: newTestNotificationChannelConfig(t, backend.WebhookConfig{
66-
Type: backend.WebhookConfigTypeWebhook,
67-
Url: "https://hooks.slack.com/services/123",
66+
Type: backend.WebhookConfigType("webhook"),
67+
Url: "https://hooks.slack.com/services/123",
6868
}),
6969
Status: backend.NotificationChannelStatusEnabled,
7070
CreatedAt: time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC),
@@ -125,8 +125,8 @@ func TestCreateNotificationChannel(t *testing.T) {
125125
expectedRequest: backend.CreateNotificationChannelRequest{
126126
Name: "Another Webhook",
127127
Config: newTestCreateNotificationChannelConfig(t, backend.WebhookConfig{
128-
Type: backend.WebhookConfigTypeWebhook,
129-
Url: "https://hooks.slack.com/services/456",
128+
Type: backend.WebhookConfigType("webhook"),
129+
Url: "https://hooks.slack.com/services/456",
130130
}),
131131
},
132132
output: nil,
@@ -154,8 +154,8 @@ func TestCreateNotificationChannel(t *testing.T) {
154154
expectedRequest: backend.CreateNotificationChannelRequest{
155155
Name: "Generic Webhook",
156156
Config: newTestCreateNotificationChannelConfig(t, backend.WebhookConfig{
157-
Type: backend.WebhookConfigTypeWebhook,
158-
Url: "https://hooks.slack.com/services/789",
157+
Type: backend.WebhookConfigType("webhook"),
158+
Url: "https://hooks.slack.com/services/789",
159159
}),
160160
},
161161
output: nil,
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Copyright 2026 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package httpserver
16+
17+
import (
18+
"context"
19+
20+
"github.com/GoogleChrome/webstatus.dev/lib/gen/openapi/backend"
21+
)
22+
23+
// GetSubscriptionRSS returns a "not supported" error for now.
24+
// nolint: ireturn // Signature generated from OpenAPI.
25+
func (s *Server) GetSubscriptionRSS(
26+
_ context.Context,
27+
_ backend.GetSubscriptionRSSRequestObject,
28+
) (backend.GetSubscriptionRSSResponseObject, error) {
29+
return backend.GetSubscriptionRSS500JSONResponse{
30+
Code: 500,
31+
Message: "Not supported",
32+
}, nil
33+
}

backend/pkg/httpserver/server_test.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1472,6 +1472,15 @@ func (m *mockServerInterface) DeleteSubscription(ctx context.Context,
14721472
panic("unimplemented")
14731473
}
14741474

1475+
// GetSubscriptionRSS implements backend.StrictServerInterface.
1476+
// nolint: ireturn // WONTFIX - generated method signature
1477+
func (m *mockServerInterface) GetSubscriptionRSS(ctx context.Context,
1478+
_ backend.GetSubscriptionRSSRequestObject) (
1479+
backend.GetSubscriptionRSSResponseObject, error) {
1480+
assertUserInCtx(ctx, m.t, m.expectedUserInCtx)
1481+
m.callCount++
1482+
panic("unimplemented")
1483+
}
14751484
func (m *mockServerInterface) assertCallCount(expectedCallCount int) {
14761485
if m.callCount != expectedCallCount {
14771486
m.t.Errorf("expected mock server to be used %d times. only used %d times", expectedCallCount, m.callCount)

backend/pkg/httpserver/update_notification_channel.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ func validateUpdateNotificationChannel(request *backend.UpdateNotificationChanne
4545
}
4646

4747
if cfg, err := request.Config.AsWebhookConfig(); err == nil &&
48-
cfg.Type == backend.WebhookConfigTypeWebhook {
48+
cfg.Type == backend.WebhookConfigType("webhook") {
49+
4950
if err := httputils.ValidateSlackWebhookURL(cfg.Url); err != nil {
5051
fieldErrors.addFieldError("config.url", err)
5152
}

backend/pkg/httpserver/update_notification_channel_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,8 @@ func TestUpdateNotificationChannel_Restrictions(t *testing.T) {
6969
Name: "Old Webhook",
7070
Type: backend.NotificationChannelResponseTypeWebhook,
7171
Config: newTestNotificationChannelConfig(t, backend.WebhookConfig{
72-
Type: backend.WebhookConfigTypeWebhook,
73-
Url: "https://hooks.slack.com/services/old",
72+
Type: backend.WebhookConfigType("webhook"),
73+
Url: "https://hooks.slack.com/services/old",
7474
}),
7575
Status: backend.NotificationChannelStatusEnabled,
7676
CreatedAt: time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC),
@@ -91,8 +91,8 @@ func TestUpdateNotificationChannel_Restrictions(t *testing.T) {
9191
Name: "Updated Webhook",
9292
Type: backend.NotificationChannelResponseTypeWebhook,
9393
Config: newTestNotificationChannelConfig(t, backend.WebhookConfig{
94-
Type: backend.WebhookConfigTypeWebhook,
95-
Url: "https://hooks.slack.com/services/old",
94+
Type: backend.WebhookConfigType("webhook"),
95+
Url: "https://hooks.slack.com/services/old",
9696
}),
9797
Status: backend.NotificationChannelStatusEnabled,
9898
CreatedAt: time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC),

frontend/src/static/js/components/webstatus-app.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ export class WebstatusApp extends LitElement {
4040

4141
@provide({context: routerContext})
4242
// TODO(https://github.com/GoogleChrome/webstatus.dev/issues/2020): Remove after moving away from vaadin/router.
43-
// @ts-expect-error: Router type instantiation is excessively deep in TS 6.0
43+
// @ts-ignore: Router type instantiation is excessively deep in TS 6.0
4444
router?: Router;
4545

4646
@property({type: Object})

lib/gcpspanner/spanneradapters/backend.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -561,7 +561,8 @@ func (s *Backend) CreateNotificationChannel(ctx context.Context,
561561
var channelType gcpspanner.NotificationChannelType
562562
var spannerWebhookConfig *gcpspanner.WebhookConfig
563563

564-
if cfg, err := req.Config.AsWebhookConfig(); err == nil && cfg.Type == backend.WebhookConfigTypeWebhook {
564+
if cfg, err := req.Config.AsWebhookConfig(); err == nil && cfg.Type == backend.WebhookConfigType("webhook") {
565+
565566
channelType = gcpspanner.NotificationChannelTypeWebhook
566567
spannerWebhookConfig = s.toSpannerWebhookConfig(&cfg)
567568
} else {
@@ -635,7 +636,9 @@ func (s *Backend) UpdateNotificationChannel(
635636
updateReq.Name.Value = *req.Name
636637
case backend.UpdateNotificationChannelRequestMaskConfig:
637638
// We need to know the type to know which config to set.
638-
if cfg, err := req.Config.AsWebhookConfig(); err == nil && cfg.Type == backend.WebhookConfigTypeWebhook {
639+
if cfg, err := req.Config.AsWebhookConfig(); err == nil &&
640+
cfg.Type == backend.WebhookConfigType("webhook") {
641+
639642
updateReq.Type.IsSet = true
640643
updateReq.Type.Value = gcpspanner.NotificationChannelTypeWebhook
641644
updateReq.WebhookConfig.IsSet = true
@@ -701,8 +704,8 @@ func toBackendNotificationChannel(channel *gcpspanner.NotificationChannel) *back
701704
case gcpspanner.NotificationChannelTypeWebhook:
702705
if channel.WebhookConfig != nil {
703706
bytes, _ := json.Marshal(backend.WebhookConfig{
704-
Type: backend.WebhookConfigTypeWebhook,
705-
Url: channel.WebhookConfig.URL,
707+
Type: backend.WebhookConfigType("webhook"),
708+
Url: channel.WebhookConfig.URL,
706709
})
707710
// UnmarshalJSON() is confusingly named - it just makes a copy of 'bytes' to store in config.
708711
_ = config.UnmarshalJSON(bytes)

lib/gcpspanner/spanneradapters/backend_test.go

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2278,8 +2278,8 @@ func TestCreateNotificationChannel(t *testing.T) {
22782278
req: backend.CreateNotificationChannelRequest{
22792279
Name: "My Webhook",
22802280
Config: newTestCreateNotificationChannelConfig(t, backend.WebhookConfig{
2281-
Type: backend.WebhookConfigTypeWebhook,
2282-
Url: "https://hooks.slack.com/services/123",
2281+
Type: backend.WebhookConfigType("webhook"),
2282+
Url: "https://hooks.slack.com/services/123",
22832283
}),
22842284
},
22852285

@@ -2312,8 +2312,8 @@ func TestCreateNotificationChannel(t *testing.T) {
23122312
Name: "My Webhook",
23132313
Type: backend.NotificationChannelResponseTypeWebhook,
23142314
Config: newTestNotificationChannelConfig(t, backend.WebhookConfig{
2315-
Type: backend.WebhookConfigTypeWebhook,
2316-
Url: "https://hooks.slack.com/services/123",
2315+
Type: backend.WebhookConfigType("webhook"),
2316+
Url: "https://hooks.slack.com/services/123",
23172317
}),
23182318
Status: backend.NotificationChannelStatusEnabled,
23192319
CreatedAt: now,
@@ -2402,8 +2402,8 @@ func TestUpdateNotificationChannel(t *testing.T) {
24022402
Name: "New Name",
24032403
Type: backend.NotificationChannelResponseTypeWebhook,
24042404
Config: newTestNotificationChannelConfig(t, backend.WebhookConfig{
2405-
Type: backend.WebhookConfigTypeWebhook,
2406-
Url: "https://hooks.slack.com/services/123",
2405+
Type: backend.WebhookConfigType("webhook"),
2406+
Url: "https://hooks.slack.com/services/123",
24072407
}),
24082408
Status: backend.NotificationChannelStatusEnabled,
24092409
CreatedAt: now,
@@ -2447,8 +2447,8 @@ func TestUpdateNotificationChannel(t *testing.T) {
24472447
},
24482448
Name: nil,
24492449
Config: newTestUpdateNotificationChannelRequestConfig(t, backend.WebhookConfig{
2450-
Type: backend.WebhookConfigTypeWebhook,
2451-
Url: "https://hooks.slack.com/services/456",
2450+
Type: backend.WebhookConfigType("webhook"),
2451+
Url: "https://hooks.slack.com/services/456",
24522452
}),
24532453
},
24542454
cfg: &mockUpdateNotificationChannelConfig{
@@ -2483,8 +2483,8 @@ func TestUpdateNotificationChannel(t *testing.T) {
24832483
Name: "My Email", // Name didn't change
24842484
Type: backend.NotificationChannelResponseTypeWebhook,
24852485
Config: newTestNotificationChannelConfig(t, backend.WebhookConfig{
2486-
Type: backend.WebhookConfigTypeWebhook,
2487-
Url: "https://hooks.slack.com/services/456",
2486+
Type: backend.WebhookConfigType("webhook"),
2487+
Url: "https://hooks.slack.com/services/456",
24882488
}),
24892489
Status: backend.NotificationChannelStatusEnabled,
24902490
CreatedAt: now,

openapi/backend/openapi.yaml

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1372,6 +1372,45 @@ paths:
13721372
application/json:
13731373
schema:
13741374
$ref: '#/components/schemas/BasicErrorModel'
1375+
/v1/subscriptions/{subscription_id}/rss:
1376+
description: Public RSS feed for a specific subscription.
1377+
parameters:
1378+
- name: subscription_id
1379+
in: path
1380+
description: Subscription ID
1381+
required: true
1382+
schema:
1383+
type: string
1384+
get:
1385+
summary: Get RSS feed for a subscription
1386+
operationId: getSubscriptionRSS
1387+
security:
1388+
- noAuth: []
1389+
responses:
1390+
'200':
1391+
description: OK
1392+
content:
1393+
application/rss+xml:
1394+
schema:
1395+
type: string
1396+
'404':
1397+
description: Not Found
1398+
content:
1399+
application/json:
1400+
schema:
1401+
$ref: '#/components/schemas/BasicErrorModel'
1402+
'429':
1403+
description: Rate Limit
1404+
content:
1405+
application/json:
1406+
schema:
1407+
$ref: '#/components/schemas/BasicErrorModel'
1408+
'500':
1409+
description: Internal Service Error
1410+
content:
1411+
application/json:
1412+
schema:
1413+
$ref: '#/components/schemas/BasicErrorModel'
13751414
components:
13761415
parameters:
13771416
browserPathParam:
@@ -1867,6 +1906,15 @@ components:
18671906
required:
18681907
- type
18691908
- address
1909+
RSSConfig:
1910+
type: object
1911+
properties:
1912+
type:
1913+
type: string
1914+
enum:
1915+
- rss
1916+
required:
1917+
- type
18701918
CreateNotificationChannelRequest:
18711919
type: object
18721920
required:
@@ -1880,10 +1928,12 @@ components:
18801928
description: Configuration specific to the channel type.
18811929
oneOf:
18821930
- $ref: '#/components/schemas/WebhookConfig'
1931+
- $ref: '#/components/schemas/RSSConfig'
18831932
discriminator:
18841933
propertyName: type
18851934
mapping:
18861935
webhook: '#/components/schemas/WebhookConfig'
1936+
rss: '#/components/schemas/RSSConfig'
18871937
NotificationChannel:
18881938
type: object
18891939
properties:
@@ -1892,6 +1942,7 @@ components:
18921942
enum:
18931943
- email
18941944
- webhook
1945+
- rss
18951946
name:
18961947
type: string
18971948
description: The name of the channel.
@@ -1900,11 +1951,13 @@ components:
19001951
oneOf:
19011952
- $ref: '#/components/schemas/EmailConfig'
19021953
- $ref: '#/components/schemas/WebhookConfig'
1954+
- $ref: '#/components/schemas/RSSConfig'
19031955
discriminator:
19041956
propertyName: type
19051957
mapping:
19061958
email: '#/components/schemas/EmailConfig'
19071959
webhook: '#/components/schemas/WebhookConfig'
1960+
rss: '#/components/schemas/RSSConfig'
19081961
required:
19091962
- type
19101963
- name
@@ -1943,10 +1996,12 @@ components:
19431996
description: Configuration specific to the channel type.
19441997
oneOf:
19451998
- $ref: '#/components/schemas/WebhookConfig'
1999+
- $ref: '#/components/schemas/RSSConfig'
19462000
discriminator:
19472001
propertyName: type
19482002
mapping:
19492003
webhook: '#/components/schemas/WebhookConfig'
2004+
rss: '#/components/schemas/RSSConfig'
19502005
update_mask:
19512006
type: array
19522007
description: >

0 commit comments

Comments
 (0)