Skip to content

Commit b5e7b9f

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 4e9068d commit b5e7b9f

File tree

10 files changed

+118
-17
lines changed

10 files changed

+118
-17
lines changed

backend/pkg/httpserver/create_notification_channel.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ 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.Webhook {
3737
if err := httputils.ValidateSlackWebhookURL(cfg.Url); err != nil {
3838
fieldErrors.addFieldError("config.url", err)
3939
}

backend/pkg/httpserver/create_notification_channel_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ func TestCreateNotificationChannel(t *testing.T) {
5454
expectedRequest: backend.CreateNotificationChannelRequest{
5555
Name: "My Webhook",
5656
Config: newTestCreateNotificationChannelConfig(t, backend.WebhookConfig{
57-
Type: backend.WebhookConfigTypeWebhook,
57+
Type: backend.Webhook,
5858
Url: "https://hooks.slack.com/services/123",
5959
}),
6060
},
@@ -63,7 +63,7 @@ func TestCreateNotificationChannel(t *testing.T) {
6363
Name: "My Webhook",
6464
Type: backend.NotificationChannelResponseTypeWebhook,
6565
Config: newTestNotificationChannelConfig(t, backend.WebhookConfig{
66-
Type: backend.WebhookConfigTypeWebhook,
66+
Type: backend.Webhook,
6767
Url: "https://hooks.slack.com/services/123",
6868
}),
6969
Status: backend.NotificationChannelStatusEnabled,
@@ -125,7 +125,7 @@ func TestCreateNotificationChannel(t *testing.T) {
125125
expectedRequest: backend.CreateNotificationChannelRequest{
126126
Name: "Another Webhook",
127127
Config: newTestCreateNotificationChannelConfig(t, backend.WebhookConfig{
128-
Type: backend.WebhookConfigTypeWebhook,
128+
Type: backend.Webhook,
129129
Url: "https://hooks.slack.com/services/456",
130130
}),
131131
},
@@ -154,7 +154,7 @@ func TestCreateNotificationChannel(t *testing.T) {
154154
expectedRequest: backend.CreateNotificationChannelRequest{
155155
Name: "Generic Webhook",
156156
Config: newTestCreateNotificationChannelConfig(t, backend.WebhookConfig{
157-
Type: backend.WebhookConfigTypeWebhook,
157+
Type: backend.Webhook,
158158
Url: "https://hooks.slack.com/services/789",
159159
}),
160160
},
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: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ 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.Webhook {
4949
if err := httputils.ValidateSlackWebhookURL(cfg.Url); err != nil {
5050
fieldErrors.addFieldError("config.url", err)
5151
}

backend/pkg/httpserver/update_notification_channel_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ func TestUpdateNotificationChannel_Restrictions(t *testing.T) {
6969
Name: "Old Webhook",
7070
Type: backend.NotificationChannelResponseTypeWebhook,
7171
Config: newTestNotificationChannelConfig(t, backend.WebhookConfig{
72-
Type: backend.WebhookConfigTypeWebhook,
72+
Type: backend.Webhook,
7373
Url: "https://hooks.slack.com/services/old",
7474
}),
7575
Status: backend.NotificationChannelStatusEnabled,
@@ -91,7 +91,7 @@ func TestUpdateNotificationChannel_Restrictions(t *testing.T) {
9191
Name: "Updated Webhook",
9292
Type: backend.NotificationChannelResponseTypeWebhook,
9393
Config: newTestNotificationChannelConfig(t, backend.WebhookConfig{
94-
Type: backend.WebhookConfigTypeWebhook,
94+
Type: backend.Webhook,
9595
Url: "https://hooks.slack.com/services/old",
9696
}),
9797
Status: backend.NotificationChannelStatusEnabled,

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: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -561,7 +561,7 @@ 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.Webhook {
565565
channelType = gcpspanner.NotificationChannelTypeWebhook
566566
spannerWebhookConfig = s.toSpannerWebhookConfig(&cfg)
567567
} else {
@@ -635,7 +635,8 @@ func (s *Backend) UpdateNotificationChannel(
635635
updateReq.Name.Value = *req.Name
636636
case backend.UpdateNotificationChannelRequestMaskConfig:
637637
// 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 {
638+
if cfg, err := req.Config.AsWebhookConfig(); err == nil &&
639+
cfg.Type == backend.Webhook {
639640
updateReq.Type.IsSet = true
640641
updateReq.Type.Value = gcpspanner.NotificationChannelTypeWebhook
641642
updateReq.WebhookConfig.IsSet = true
@@ -701,7 +702,7 @@ func toBackendNotificationChannel(channel *gcpspanner.NotificationChannel) *back
701702
case gcpspanner.NotificationChannelTypeWebhook:
702703
if channel.WebhookConfig != nil {
703704
bytes, _ := json.Marshal(backend.WebhookConfig{
704-
Type: backend.WebhookConfigTypeWebhook,
705+
Type: backend.Webhook,
705706
Url: channel.WebhookConfig.URL,
706707
})
707708
// UnmarshalJSON() is confusingly named - it just makes a copy of 'bytes' to store in config.

lib/gcpspanner/spanneradapters/backend_test.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2278,7 +2278,7 @@ func TestCreateNotificationChannel(t *testing.T) {
22782278
req: backend.CreateNotificationChannelRequest{
22792279
Name: "My Webhook",
22802280
Config: newTestCreateNotificationChannelConfig(t, backend.WebhookConfig{
2281-
Type: backend.WebhookConfigTypeWebhook,
2281+
Type: backend.Webhook,
22822282
Url: "https://hooks.slack.com/services/123",
22832283
}),
22842284
},
@@ -2312,7 +2312,7 @@ func TestCreateNotificationChannel(t *testing.T) {
23122312
Name: "My Webhook",
23132313
Type: backend.NotificationChannelResponseTypeWebhook,
23142314
Config: newTestNotificationChannelConfig(t, backend.WebhookConfig{
2315-
Type: backend.WebhookConfigTypeWebhook,
2315+
Type: backend.Webhook,
23162316
Url: "https://hooks.slack.com/services/123",
23172317
}),
23182318
Status: backend.NotificationChannelStatusEnabled,
@@ -2402,7 +2402,7 @@ func TestUpdateNotificationChannel(t *testing.T) {
24022402
Name: "New Name",
24032403
Type: backend.NotificationChannelResponseTypeWebhook,
24042404
Config: newTestNotificationChannelConfig(t, backend.WebhookConfig{
2405-
Type: backend.WebhookConfigTypeWebhook,
2405+
Type: backend.Webhook,
24062406
Url: "https://hooks.slack.com/services/123",
24072407
}),
24082408
Status: backend.NotificationChannelStatusEnabled,
@@ -2447,7 +2447,7 @@ func TestUpdateNotificationChannel(t *testing.T) {
24472447
},
24482448
Name: nil,
24492449
Config: newTestUpdateNotificationChannelRequestConfig(t, backend.WebhookConfig{
2450-
Type: backend.WebhookConfigTypeWebhook,
2450+
Type: backend.Webhook,
24512451
Url: "https://hooks.slack.com/services/456",
24522452
}),
24532453
},
@@ -2483,7 +2483,7 @@ 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,
2486+
Type: backend.Webhook,
24872487
Url: "https://hooks.slack.com/services/456",
24882488
}),
24892489
Status: backend.NotificationChannelStatusEnabled,

openapi/backend/openapi.yaml

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1372,6 +1372,48 @@ 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+
parameters:
1388+
- $ref: '#/components/parameters/paginationTokenParam'
1389+
- $ref: '#/components/parameters/paginationSizeParam'
1390+
security:
1391+
- noAuth: []
1392+
responses:
1393+
'200':
1394+
description: OK
1395+
content:
1396+
application/rss+xml:
1397+
schema:
1398+
type: string
1399+
'404':
1400+
description: Not Found
1401+
content:
1402+
application/json:
1403+
schema:
1404+
$ref: '#/components/schemas/BasicErrorModel'
1405+
'429':
1406+
description: Rate Limit
1407+
content:
1408+
application/json:
1409+
schema:
1410+
$ref: '#/components/schemas/BasicErrorModel'
1411+
'500':
1412+
description: Internal Service Error
1413+
content:
1414+
application/json:
1415+
schema:
1416+
$ref: '#/components/schemas/BasicErrorModel'
13751417
components:
13761418
parameters:
13771419
browserPathParam:
@@ -1867,6 +1909,15 @@ components:
18671909
required:
18681910
- type
18691911
- address
1912+
RSSConfig:
1913+
type: object
1914+
properties:
1915+
type:
1916+
type: string
1917+
enum:
1918+
- rss
1919+
required:
1920+
- type
18701921
CreateNotificationChannelRequest:
18711922
type: object
18721923
required:
@@ -1880,10 +1931,12 @@ components:
18801931
description: Configuration specific to the channel type.
18811932
oneOf:
18821933
- $ref: '#/components/schemas/WebhookConfig'
1934+
- $ref: '#/components/schemas/RSSConfig'
18831935
discriminator:
18841936
propertyName: type
18851937
mapping:
18861938
webhook: '#/components/schemas/WebhookConfig'
1939+
rss: '#/components/schemas/RSSConfig'
18871940
NotificationChannel:
18881941
type: object
18891942
properties:
@@ -1892,6 +1945,7 @@ components:
18921945
enum:
18931946
- email
18941947
- webhook
1948+
- rss
18951949
name:
18961950
type: string
18971951
description: The name of the channel.
@@ -1900,11 +1954,13 @@ components:
19001954
oneOf:
19011955
- $ref: '#/components/schemas/EmailConfig'
19021956
- $ref: '#/components/schemas/WebhookConfig'
1957+
- $ref: '#/components/schemas/RSSConfig'
19031958
discriminator:
19041959
propertyName: type
19051960
mapping:
19061961
email: '#/components/schemas/EmailConfig'
19071962
webhook: '#/components/schemas/WebhookConfig'
1963+
rss: '#/components/schemas/RSSConfig'
19081964
required:
19091965
- type
19101966
- name
@@ -1943,10 +1999,12 @@ components:
19431999
description: Configuration specific to the channel type.
19442000
oneOf:
19452001
- $ref: '#/components/schemas/WebhookConfig'
2002+
- $ref: '#/components/schemas/RSSConfig'
19462003
discriminator:
19472004
propertyName: type
19482005
mapping:
19492006
webhook: '#/components/schemas/WebhookConfig'
2007+
rss: '#/components/schemas/RSSConfig'
19502008
update_mask:
19512009
type: array
19522010
description: >

0 commit comments

Comments
 (0)