Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit cd19e79

Browse files
chore: enable coder inbox by default (#17077)
Add a flag to enable Coder Inbox by default, as well as supporting disabling the feature.
1 parent 5f3a53f commit cd19e79

File tree

15 files changed

+258
-40
lines changed

15 files changed

+258
-40
lines changed

cli/server.go

+20-24
Original file line numberDiff line numberDiff line change
@@ -920,34 +920,30 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd.
920920
notificationsManager *notifications.Manager
921921
)
922922

923-
if notificationsCfg.Enabled() {
924-
metrics := notifications.NewMetrics(options.PrometheusRegistry)
925-
helpers := templateHelpers(options)
923+
metrics := notifications.NewMetrics(options.PrometheusRegistry)
924+
helpers := templateHelpers(options)
926925

927-
// The enqueuer is responsible for enqueueing notifications to the given store.
928-
enqueuer, err := notifications.NewStoreEnqueuer(notificationsCfg, options.Database, helpers, logger.Named("notifications.enqueuer"), quartz.NewReal())
929-
if err != nil {
930-
return xerrors.Errorf("failed to instantiate notification store enqueuer: %w", err)
931-
}
932-
options.NotificationsEnqueuer = enqueuer
926+
// The enqueuer is responsible for enqueueing notifications to the given store.
927+
enqueuer, err := notifications.NewStoreEnqueuer(notificationsCfg, options.Database, helpers, logger.Named("notifications.enqueuer"), quartz.NewReal())
928+
if err != nil {
929+
return xerrors.Errorf("failed to instantiate notification store enqueuer: %w", err)
930+
}
931+
options.NotificationsEnqueuer = enqueuer
933932

934-
// The notification manager is responsible for:
935-
// - creating notifiers and managing their lifecycles (notifiers are responsible for dequeueing/sending notifications)
936-
// - keeping the store updated with status updates
937-
notificationsManager, err = notifications.NewManager(notificationsCfg, options.Database, options.Pubsub, helpers, metrics, logger.Named("notifications.manager"))
938-
if err != nil {
939-
return xerrors.Errorf("failed to instantiate notification manager: %w", err)
940-
}
933+
// The notification manager is responsible for:
934+
// - creating notifiers and managing their lifecycles (notifiers are responsible for dequeueing/sending notifications)
935+
// - keeping the store updated with status updates
936+
notificationsManager, err = notifications.NewManager(notificationsCfg, options.Database, options.Pubsub, helpers, metrics, logger.Named("notifications.manager"))
937+
if err != nil {
938+
return xerrors.Errorf("failed to instantiate notification manager: %w", err)
939+
}
941940

942-
// nolint:gocritic // We need to run the manager in a notifier context.
943-
notificationsManager.Run(dbauthz.AsNotifier(ctx))
941+
// nolint:gocritic // We need to run the manager in a notifier context.
942+
notificationsManager.Run(dbauthz.AsNotifier(ctx))
944943

945-
// Run report generator to distribute periodic reports.
946-
notificationReportGenerator := reports.NewReportGenerator(ctx, logger.Named("notifications.report_generator"), options.Database, options.NotificationsEnqueuer, quartz.NewReal())
947-
defer notificationReportGenerator.Close()
948-
} else {
949-
logger.Debug(ctx, "notifications are currently disabled as there are no configured delivery methods. See https://coder.com/docs/admin/monitoring/notifications#delivery-methods for more details")
950-
}
944+
// Run report generator to distribute periodic reports.
945+
notificationReportGenerator := reports.NewReportGenerator(ctx, logger.Named("notifications.report_generator"), options.Database, options.NotificationsEnqueuer, quartz.NewReal())
946+
defer notificationReportGenerator.Close()
951947

952948
// Since errCh only has one buffered slot, all routines
953949
// sending on it must be wrapped in a select/default to

cli/server_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,7 @@ func TestServer(t *testing.T) {
298298
out := pty.ReadAll()
299299
numLines := countLines(string(out))
300300
t.Logf("numLines: %d", numLines)
301-
require.Less(t, numLines, 12, "expected less than 12 lines of output (terminal width 80), got %d", numLines)
301+
require.Less(t, numLines, 20, "expected less than 20 lines of output (terminal width 80), got %d", numLines)
302302
})
303303

304304
t.Run("OAuth2GitHubDefaultProvider", func(t *testing.T) {

cli/testdata/coder_server_--help.golden

+4
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,10 @@ Configure TLS for your SMTP server target.
473473
Enable STARTTLS to upgrade insecure SMTP connections using TLS.
474474
DEPRECATED: Use --email-tls-starttls instead.
475475

476+
NOTIFICATIONS / INBOX OPTIONS:
477+
--notifications-inbox-enabled bool, $CODER_NOTIFICATIONS_INBOX_ENABLED (default: true)
478+
Enable Coder Inbox.
479+
476480
NOTIFICATIONS / WEBHOOK OPTIONS:
477481
--notifications-webhook-endpoint url, $CODER_NOTIFICATIONS_WEBHOOK_ENDPOINT
478482
The endpoint to which to send webhooks.

cli/testdata/server-config.yaml.golden

+4
Original file line numberDiff line numberDiff line change
@@ -643,6 +643,10 @@ notifications:
643643
# The endpoint to which to send webhooks.
644644
# (default: <unset>, type: url)
645645
endpoint:
646+
inbox:
647+
# Enable Coder Inbox.
648+
# (default: true, type: bool)
649+
enabled: true
646650
# The upper limit of attempts to send a notification.
647651
# (default: 5, type: int)
648652
maxSendAttempts: 5

coderd/apidoc/docs.go

+16
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/apidoc/swagger.json

+16
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/notifications/enqueuer.go

+25-13
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package notifications
33
import (
44
"context"
55
"encoding/json"
6+
"slices"
67
"strings"
78
"text/template"
89

@@ -28,7 +29,10 @@ type StoreEnqueuer struct {
2829
store Store
2930
log slog.Logger
3031

31-
defaultMethod database.NotificationMethod
32+
defaultMethod database.NotificationMethod
33+
defaultEnabled bool
34+
inboxEnabled bool
35+
3236
// helpers holds a map of template funcs which are used when rendering templates. These need to be passed in because
3337
// the template funcs will return values which are inappropriately encapsulated in this struct.
3438
helpers template.FuncMap
@@ -44,11 +48,13 @@ func NewStoreEnqueuer(cfg codersdk.NotificationsConfig, store Store, helpers tem
4448
}
4549

4650
return &StoreEnqueuer{
47-
store: store,
48-
log: log,
49-
defaultMethod: method,
50-
helpers: helpers,
51-
clock: clock,
51+
store: store,
52+
log: log,
53+
defaultMethod: method,
54+
defaultEnabled: cfg.Enabled(),
55+
inboxEnabled: cfg.Inbox.Enabled.Value(),
56+
helpers: helpers,
57+
clock: clock,
5258
}, nil
5359
}
5460

@@ -69,11 +75,6 @@ func (s *StoreEnqueuer) EnqueueWithData(ctx context.Context, userID, templateID
6975
return nil, xerrors.Errorf("new message metadata: %w", err)
7076
}
7177

72-
dispatchMethod := s.defaultMethod
73-
if metadata.CustomMethod.Valid {
74-
dispatchMethod = metadata.CustomMethod.NotificationMethod
75-
}
76-
7778
payload, err := s.buildPayload(metadata, labels, data, targets)
7879
if err != nil {
7980
s.log.Warn(ctx, "failed to build payload", slog.F("template_id", templateID), slog.F("user_id", userID), slog.Error(err))
@@ -85,11 +86,22 @@ func (s *StoreEnqueuer) EnqueueWithData(ctx context.Context, userID, templateID
8586
return nil, xerrors.Errorf("failed encoding input labels: %w", err)
8687
}
8788

88-
uuids := make([]uuid.UUID, 0, 2)
89+
methods := []database.NotificationMethod{}
90+
if metadata.CustomMethod.Valid {
91+
methods = append(methods, metadata.CustomMethod.NotificationMethod)
92+
} else if s.defaultEnabled {
93+
methods = append(methods, s.defaultMethod)
94+
}
95+
8996
// All the enqueued messages are enqueued both on the dispatch method set by the user (or default one) and the inbox.
9097
// As the inbox is not configurable per the user and is always enabled, we always enqueue the message on the inbox.
9198
// The logic is done here in order to have two completely separated processing and retries are handled separately.
92-
for _, method := range []database.NotificationMethod{dispatchMethod, database.NotificationMethodInbox} {
99+
if !slices.Contains(methods, database.NotificationMethodInbox) && s.inboxEnabled {
100+
methods = append(methods, database.NotificationMethodInbox)
101+
}
102+
103+
uuids := make([]uuid.UUID, 0, 2)
104+
for _, method := range methods {
93105
id := uuid.New()
94106
err = s.store.EnqueueNotificationMessage(ctx, database.EnqueueNotificationMessageParams{
95107
ID: id,

coderd/notifications/notifications_test.go

+84
Original file line numberDiff line numberDiff line change
@@ -1856,6 +1856,90 @@ func TestNotificationDuplicates(t *testing.T) {
18561856
require.NoError(t, err)
18571857
}
18581858

1859+
func TestNotificationTargetMatrix(t *testing.T) {
1860+
t.Parallel()
1861+
1862+
tests := []struct {
1863+
name string
1864+
defaultMethod database.NotificationMethod
1865+
defaultEnabled bool
1866+
inboxEnabled bool
1867+
expectedEnqueued int
1868+
}{
1869+
{
1870+
name: "NoDefaultAndNoInbox",
1871+
defaultMethod: database.NotificationMethodSmtp,
1872+
defaultEnabled: false,
1873+
inboxEnabled: false,
1874+
expectedEnqueued: 0,
1875+
},
1876+
{
1877+
name: "DefaultAndNoInbox",
1878+
defaultMethod: database.NotificationMethodSmtp,
1879+
defaultEnabled: true,
1880+
inboxEnabled: false,
1881+
expectedEnqueued: 1,
1882+
},
1883+
{
1884+
name: "NoDefaultAndInbox",
1885+
defaultMethod: database.NotificationMethodSmtp,
1886+
defaultEnabled: false,
1887+
inboxEnabled: true,
1888+
expectedEnqueued: 1,
1889+
},
1890+
{
1891+
name: "DefaultAndInbox",
1892+
defaultMethod: database.NotificationMethodSmtp,
1893+
defaultEnabled: true,
1894+
inboxEnabled: true,
1895+
expectedEnqueued: 2,
1896+
},
1897+
}
1898+
1899+
for _, tt := range tests {
1900+
tt := tt
1901+
1902+
t.Run(tt.name, func(t *testing.T) {
1903+
t.Parallel()
1904+
1905+
// nolint:gocritic // Unit test.
1906+
ctx := dbauthz.AsNotifier(testutil.Context(t, testutil.WaitSuperLong))
1907+
store, pubsub := dbtestutil.NewDB(t)
1908+
logger := testutil.Logger(t)
1909+
1910+
cfg := defaultNotificationsConfig(tt.defaultMethod)
1911+
cfg.Inbox.Enabled = serpent.Bool(tt.inboxEnabled)
1912+
1913+
// If the default method is not enabled, we want to ensure the config
1914+
// is wiped out.
1915+
if !tt.defaultEnabled {
1916+
cfg.SMTP = codersdk.NotificationsEmailConfig{}
1917+
cfg.Webhook = codersdk.NotificationsWebhookConfig{}
1918+
}
1919+
1920+
mgr, err := notifications.NewManager(cfg, store, pubsub, defaultHelpers(), createMetrics(), logger.Named("manager"))
1921+
require.NoError(t, err)
1922+
t.Cleanup(func() {
1923+
assert.NoError(t, mgr.Stop(ctx))
1924+
})
1925+
1926+
// Set the time to a known value.
1927+
mClock := quartz.NewMock(t)
1928+
mClock.Set(time.Date(2024, 1, 15, 9, 0, 0, 0, time.UTC))
1929+
1930+
enq, err := notifications.NewStoreEnqueuer(cfg, store, defaultHelpers(), logger.Named("enqueuer"), mClock)
1931+
require.NoError(t, err)
1932+
user := createSampleUser(t, store)
1933+
1934+
// When: A notification is enqueued, it enqueues the correct amount of notifications.
1935+
enqueued, err := enq.Enqueue(ctx, user.ID, notifications.TemplateWorkspaceDeleted,
1936+
map[string]string{"initiator": "danny"}, "test", user.ID)
1937+
require.NoError(t, err)
1938+
require.Len(t, enqueued, tt.expectedEnqueued)
1939+
})
1940+
}
1941+
}
1942+
18591943
type fakeHandler struct {
18601944
mu sync.RWMutex
18611945
succeeded, failed []string

coderd/notifications/utils_test.go

+18-2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package notifications_test
22

33
import (
44
"context"
5+
"net/url"
56
"sync/atomic"
67
"testing"
78
"text/template"
@@ -21,6 +22,18 @@ import (
2122
)
2223

2324
func defaultNotificationsConfig(method database.NotificationMethod) codersdk.NotificationsConfig {
25+
var (
26+
smtp codersdk.NotificationsEmailConfig
27+
webhook codersdk.NotificationsWebhookConfig
28+
)
29+
30+
switch method {
31+
case database.NotificationMethodSmtp:
32+
smtp.Smarthost = serpent.String("localhost:1337")
33+
case database.NotificationMethodWebhook:
34+
webhook.Endpoint = serpent.URL(url.URL{Host: "localhost"})
35+
}
36+
2437
return codersdk.NotificationsConfig{
2538
Method: serpent.String(method),
2639
MaxSendAttempts: 5,
@@ -31,8 +44,11 @@ func defaultNotificationsConfig(method database.NotificationMethod) codersdk.Not
3144
RetryInterval: serpent.Duration(time.Millisecond * 50),
3245
LeaseCount: 10,
3346
StoreSyncBufferSize: 50,
34-
SMTP: codersdk.NotificationsEmailConfig{},
35-
Webhook: codersdk.NotificationsWebhookConfig{},
47+
SMTP: smtp,
48+
Webhook: webhook,
49+
Inbox: codersdk.NotificationsInboxConfig{
50+
Enabled: serpent.Bool(true),
51+
},
3652
}
3753
}
3854

0 commit comments

Comments
 (0)