From a42f1082f8082bd017f2d3e881bca86bdb362526 Mon Sep 17 00:00:00 2001 From: defelmnq Date: Wed, 2 Oct 2024 17:58:41 +0000 Subject: [PATCH 01/26] feat(notifications): add company logo url when available for email notifications --- coderd/database/models.go | 2 +- coderd/database/querier.go | 2 +- coderd/database/queries.sql.go | 2 +- coderd/notifications/dispatch/smtp/html.gotmpl | 2 +- coderd/notifications/notifier.go | 18 ++++++++++++++++++ coderd/notifications/spec.go | 2 ++ 6 files changed, 24 insertions(+), 4 deletions(-) diff --git a/coderd/database/models.go b/coderd/database/models.go index 05b4c404ea16f..7f36514e83788 100644 --- a/coderd/database/models.go +++ b/coderd/database/models.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.25.0 +// sqlc v1.26.0 package database diff --git a/coderd/database/querier.go b/coderd/database/querier.go index cb126f83af32f..2de0839b72bbe 100644 --- a/coderd/database/querier.go +++ b/coderd/database/querier.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.25.0 +// sqlc v1.26.0 package database diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go index 0b2c1d9a6822a..b4d3649e04648 100644 --- a/coderd/database/queries.sql.go +++ b/coderd/database/queries.sql.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.25.0 +// sqlc v1.26.0 package database diff --git a/coderd/notifications/dispatch/smtp/html.gotmpl b/coderd/notifications/dispatch/smtp/html.gotmpl index 78ac053cc7b4f..9632233f1c72b 100644 --- a/coderd/notifications/dispatch/smtp/html.gotmpl +++ b/coderd/notifications/dispatch/smtp/html.gotmpl @@ -8,7 +8,7 @@
- Coder Logo + Company Logo

{{ .Labels._subject }} diff --git a/coderd/notifications/notifier.go b/coderd/notifications/notifier.go index a3ca9fc931aa1..0cfbb12765ec9 100644 --- a/coderd/notifications/notifier.go +++ b/coderd/notifications/notifier.go @@ -2,7 +2,9 @@ package notifications import ( "context" + "database/sql" "encoding/json" + "errors" "sync" "text/template" @@ -22,6 +24,10 @@ import ( "github.com/coder/coder/v2/coderd/database" ) +const ( + notificationsDefaultLogoURL = "https://coder.com/coder-logo-horizontal.png" +) + // notifier is a consumer of the notifications_messages queue. It dequeues messages from that table and processes them // through a pipeline of fetch -> prepare -> render -> acquire handler -> deliver. type notifier struct { @@ -223,6 +229,18 @@ func (n *notifier) prepare(ctx context.Context, msg database.AcquireNotification return nil, xerrors.Errorf("failed to resolve handler %q", msg.Method) } + logoURL, err := n.store.GetLogoURL(ctx) + if err != nil && !errors.Is(err, sql.ErrNoRows) { + n.log.Error(ctx, "failed fetching logo url", slog.Error(err)) + } + + if logoURL == "" { + //nolint:ineffassign // define to default value if unable to fetch one from db + logoURL = notificationsDefaultLogoURL + } + + payload.Labels["_logo_url"] = logoURL + var title, body string if title, err = render.GoTemplate(msg.TitleTemplate, payload, n.helpers); err != nil { return nil, xerrors.Errorf("render title: %w", err) diff --git a/coderd/notifications/spec.go b/coderd/notifications/spec.go index b8ae063cc919e..3c4fd765392ea 100644 --- a/coderd/notifications/spec.go +++ b/coderd/notifications/spec.go @@ -22,6 +22,8 @@ type Store interface { FetchNewMessageMetadata(ctx context.Context, arg database.FetchNewMessageMetadataParams) (database.FetchNewMessageMetadataRow, error) GetNotificationMessagesByStatus(ctx context.Context, arg database.GetNotificationMessagesByStatusParams) ([]database.NotificationMessage, error) GetNotificationsSettings(ctx context.Context) (string, error) + + GetLogoURL(ctx context.Context) (string, error) } // Handler is responsible for preparing and delivering a notification by a given method. From 1fa1271a190a6e401afe7152c318a9022181f186 Mon Sep 17 00:00:00 2001 From: defelmnq Date: Thu, 3 Oct 2024 07:59:24 +0000 Subject: [PATCH 02/26] feat(notifications): adapt tests with new labels --- coderd/notifications/notifications_test.go | 5 +++-- coderd/notifications/notifier.go | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/coderd/notifications/notifications_test.go b/coderd/notifications/notifications_test.go index 61862826acca1..6ee6d3782d2f4 100644 --- a/coderd/notifications/notifications_test.go +++ b/coderd/notifications/notifications_test.go @@ -241,8 +241,9 @@ func TestWebhookDispatch(t *testing.T) { // WHEN: a notification is enqueued (including arbitrary labels) input := map[string]string{ - "a": "b", - "c": "d", + "a": "b", + "c": "d", + "_logo_url": notifications.NotificationsDefaultLogoURL, } msgID, err := enq.Enqueue(ctx, user.ID, notifications.TemplateWorkspaceDeleted, input, "test") require.NoError(t, err) diff --git a/coderd/notifications/notifier.go b/coderd/notifications/notifier.go index 0cfbb12765ec9..b487d1ec6c80b 100644 --- a/coderd/notifications/notifier.go +++ b/coderd/notifications/notifier.go @@ -25,7 +25,7 @@ import ( ) const ( - notificationsDefaultLogoURL = "https://coder.com/coder-logo-horizontal.png" + NotificationsDefaultLogoURL = "https://coder.com/coder-logo-horizontal.png" ) // notifier is a consumer of the notifications_messages queue. It dequeues messages from that table and processes them @@ -236,7 +236,7 @@ func (n *notifier) prepare(ctx context.Context, msg database.AcquireNotification if logoURL == "" { //nolint:ineffassign // define to default value if unable to fetch one from db - logoURL = notificationsDefaultLogoURL + logoURL = NotificationsDefaultLogoURL } payload.Labels["_logo_url"] = logoURL From 2def52efe347d037dce20ff801fd13ea68756a2a Mon Sep 17 00:00:00 2001 From: defelmnq Date: Thu, 3 Oct 2024 09:42:49 +0000 Subject: [PATCH 03/26] fix: revert changes on generated code version --- coderd/database/models.go | 2 +- coderd/database/querier.go | 2 +- coderd/database/queries.sql.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/coderd/database/models.go b/coderd/database/models.go index 7f36514e83788..05b4c404ea16f 100644 --- a/coderd/database/models.go +++ b/coderd/database/models.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.26.0 +// sqlc v1.25.0 package database diff --git a/coderd/database/querier.go b/coderd/database/querier.go index 2de0839b72bbe..cb126f83af32f 100644 --- a/coderd/database/querier.go +++ b/coderd/database/querier.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.26.0 +// sqlc v1.25.0 package database diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go index b4d3649e04648..0b2c1d9a6822a 100644 --- a/coderd/database/queries.sql.go +++ b/coderd/database/queries.sql.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.26.0 +// sqlc v1.25.0 package database From 1b1a4c413d4b33918e06e1f7163d49b86dd06cd2 Mon Sep 17 00:00:00 2001 From: defelmnq Date: Thu, 3 Oct 2024 13:29:29 +0000 Subject: [PATCH 04/26] feat(notification): move logo_url and app_name logic to helpers functions --- cli/server.go | 24 +++++++++++++++++++ .../notifications/dispatch/smtp/html.gotmpl | 5 +++- coderd/notifications/dispatch/smtp_test.go | 2 ++ coderd/notifications/notifications_test.go | 5 ++-- coderd/notifications/notifier.go | 15 +----------- coderd/notifications/utils_test.go | 2 ++ 6 files changed, 35 insertions(+), 18 deletions(-) diff --git a/cli/server.go b/cli/server.go index 5adb44c3c0a7d..2a702640f2c9c 100644 --- a/cli/server.go +++ b/cli/server.go @@ -1309,6 +1309,30 @@ func templateHelpers(options *coderd.Options) map[string]any { return map[string]any{ "base_url": func() string { return options.AccessURL.String() }, "current_year": func() string { return strconv.Itoa(time.Now().Year()) }, + "logo_url": func() string { + logoURL, err := options.Database.GetLogoURL(context.Background()) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + return notifications.NotificationsDefaultLogoURL + } + + return "" + } + + return logoURL + }, + "app_name": func() string { + appName, err := options.Database.GetApplicationName(context.Background()) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + return notifications.NotificationsDefaultAppName + } + + return "" + } + + return appName + }, } } diff --git a/coderd/notifications/dispatch/smtp/html.gotmpl b/coderd/notifications/dispatch/smtp/html.gotmpl index 9632233f1c72b..1d8d9d683ad32 100644 --- a/coderd/notifications/dispatch/smtp/html.gotmpl +++ b/coderd/notifications/dispatch/smtp/html.gotmpl @@ -8,7 +8,10 @@
- Company Logo + Company Logo +

+ {{ app_name }} +

{{ .Labels._subject }} diff --git a/coderd/notifications/dispatch/smtp_test.go b/coderd/notifications/dispatch/smtp_test.go index 2687e0d82bb26..117684fba5a1b 100644 --- a/coderd/notifications/dispatch/smtp_test.go +++ b/coderd/notifications/dispatch/smtp_test.go @@ -445,6 +445,8 @@ func TestSMTP(t *testing.T) { helpers := map[string]any{ "base_url": func() string { return "http://test.com" }, "current_year": func() string { return "2024" }, + "logo_url": func() string { return "https://logo.company" }, + "app_name": func() string { return "TestCompany" }, } handler := dispatch.NewSMTPHandler(tc.cfg, helpers, logger.Named("smtp")) diff --git a/coderd/notifications/notifications_test.go b/coderd/notifications/notifications_test.go index 6ee6d3782d2f4..61862826acca1 100644 --- a/coderd/notifications/notifications_test.go +++ b/coderd/notifications/notifications_test.go @@ -241,9 +241,8 @@ func TestWebhookDispatch(t *testing.T) { // WHEN: a notification is enqueued (including arbitrary labels) input := map[string]string{ - "a": "b", - "c": "d", - "_logo_url": notifications.NotificationsDefaultLogoURL, + "a": "b", + "c": "d", } msgID, err := enq.Enqueue(ctx, user.ID, notifications.TemplateWorkspaceDeleted, input, "test") require.NoError(t, err) diff --git a/coderd/notifications/notifier.go b/coderd/notifications/notifier.go index b487d1ec6c80b..48d9d7c86af6d 100644 --- a/coderd/notifications/notifier.go +++ b/coderd/notifications/notifier.go @@ -2,9 +2,7 @@ package notifications import ( "context" - "database/sql" "encoding/json" - "errors" "sync" "text/template" @@ -26,6 +24,7 @@ import ( const ( NotificationsDefaultLogoURL = "https://coder.com/coder-logo-horizontal.png" + NotificationsDefaultAppName = "Coder" ) // notifier is a consumer of the notifications_messages queue. It dequeues messages from that table and processes them @@ -229,18 +228,6 @@ func (n *notifier) prepare(ctx context.Context, msg database.AcquireNotification return nil, xerrors.Errorf("failed to resolve handler %q", msg.Method) } - logoURL, err := n.store.GetLogoURL(ctx) - if err != nil && !errors.Is(err, sql.ErrNoRows) { - n.log.Error(ctx, "failed fetching logo url", slog.Error(err)) - } - - if logoURL == "" { - //nolint:ineffassign // define to default value if unable to fetch one from db - logoURL = NotificationsDefaultLogoURL - } - - payload.Labels["_logo_url"] = logoURL - var title, body string if title, err = render.GoTemplate(msg.TitleTemplate, payload, n.helpers); err != nil { return nil, xerrors.Errorf("render title: %w", err) diff --git a/coderd/notifications/utils_test.go b/coderd/notifications/utils_test.go index 124b8554c51fb..6efeddad820b8 100644 --- a/coderd/notifications/utils_test.go +++ b/coderd/notifications/utils_test.go @@ -39,6 +39,8 @@ func defaultHelpers() map[string]any { return map[string]any{ "base_url": func() string { return "http://test.com" }, "current_year": func() string { return "2024" }, + "logo_url": func() string { return "https://logo.company" }, + "app_name": func() string { return "TestCompany" }, } } From 779260edc3dfa9a71c2c1cb6fe8cd6f596f10302 Mon Sep 17 00:00:00 2001 From: defelmnq Date: Thu, 3 Oct 2024 14:14:13 +0000 Subject: [PATCH 05/26] chore: remove unused GetLogoURL method from notifications store interface --- coderd/notifications/spec.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/coderd/notifications/spec.go b/coderd/notifications/spec.go index 3c4fd765392ea..b8ae063cc919e 100644 --- a/coderd/notifications/spec.go +++ b/coderd/notifications/spec.go @@ -22,8 +22,6 @@ type Store interface { FetchNewMessageMetadata(ctx context.Context, arg database.FetchNewMessageMetadataParams) (database.FetchNewMessageMetadataRow, error) GetNotificationMessagesByStatus(ctx context.Context, arg database.GetNotificationMessagesByStatusParams) ([]database.NotificationMessage, error) GetNotificationsSettings(ctx context.Context) (string, error) - - GetLogoURL(ctx context.Context) (string, error) } // Handler is responsible for preparing and delivering a notification by a given method. From 1e6899f669491e4a2fcb378e256be7b58839c0d2 Mon Sep 17 00:00:00 2001 From: defelmnq Date: Fri, 4 Oct 2024 09:00:25 +0000 Subject: [PATCH 06/26] fix: add better context and timeout for db related queries --- cli/server.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/cli/server.go b/cli/server.go index 2a702640f2c9c..ab11f7007a2af 100644 --- a/cli/server.go +++ b/cli/server.go @@ -1310,7 +1310,10 @@ func templateHelpers(options *coderd.Options) map[string]any { "base_url": func() string { return options.AccessURL.String() }, "current_year": func() string { return strconv.Itoa(time.Now().Year()) }, "logo_url": func() string { - logoURL, err := options.Database.GetLogoURL(context.Background()) + ctx, cancel := context.WithTimeout(context.Background(), 1 * time.Second) + defer cancel() + + logoURL, err := options.Database.GetLogoURL(ctx) if err != nil { if errors.Is(err, sql.ErrNoRows) { return notifications.NotificationsDefaultLogoURL @@ -1322,7 +1325,10 @@ func templateHelpers(options *coderd.Options) map[string]any { return logoURL }, "app_name": func() string { - appName, err := options.Database.GetApplicationName(context.Background()) + ctx, cancel := context.WithTimeout(context.Background(), 1 * time.Second) + defer cancel() + + appName, err := options.Database.GetApplicationName(ctx) if err != nil { if errors.Is(err, sql.ErrNoRows) { return notifications.NotificationsDefaultAppName From b552267520e3b7dd0472cd5ee05dabc8615a4c82 Mon Sep 17 00:00:00 2001 From: defelmnq Date: Fri, 4 Oct 2024 09:04:04 +0000 Subject: [PATCH 07/26] chore: lint --- cli/server.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cli/server.go b/cli/server.go index ab11f7007a2af..5eab05f45e3a4 100644 --- a/cli/server.go +++ b/cli/server.go @@ -1310,7 +1310,7 @@ func templateHelpers(options *coderd.Options) map[string]any { "base_url": func() string { return options.AccessURL.String() }, "current_year": func() string { return strconv.Itoa(time.Now().Year()) }, "logo_url": func() string { - ctx, cancel := context.WithTimeout(context.Background(), 1 * time.Second) + ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) defer cancel() logoURL, err := options.Database.GetLogoURL(ctx) @@ -1325,7 +1325,7 @@ func templateHelpers(options *coderd.Options) map[string]any { return logoURL }, "app_name": func() string { - ctx, cancel := context.WithTimeout(context.Background(), 1 * time.Second) + ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) defer cancel() appName, err := options.Database.GetApplicationName(ctx) From 73e07e9880a346377921c4878195baea35f4de78 Mon Sep 17 00:00:00 2001 From: defelmnq Date: Wed, 16 Oct 2024 14:29:21 +0200 Subject: [PATCH 08/26] feat(notifications): improve logo_url and app_name fetching moving it to notifications package --- cli/server.go | 30 ------------------------------ coderd/notifications/notifier.go | 30 ++++++++++++++++++++++++++++-- coderd/notifications/spec.go | 2 ++ 3 files changed, 30 insertions(+), 32 deletions(-) diff --git a/cli/server.go b/cli/server.go index 5eab05f45e3a4..5adb44c3c0a7d 100644 --- a/cli/server.go +++ b/cli/server.go @@ -1309,36 +1309,6 @@ func templateHelpers(options *coderd.Options) map[string]any { return map[string]any{ "base_url": func() string { return options.AccessURL.String() }, "current_year": func() string { return strconv.Itoa(time.Now().Year()) }, - "logo_url": func() string { - ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) - defer cancel() - - logoURL, err := options.Database.GetLogoURL(ctx) - if err != nil { - if errors.Is(err, sql.ErrNoRows) { - return notifications.NotificationsDefaultLogoURL - } - - return "" - } - - return logoURL - }, - "app_name": func() string { - ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) - defer cancel() - - appName, err := options.Database.GetApplicationName(ctx) - if err != nil { - if errors.Is(err, sql.ErrNoRows) { - return notifications.NotificationsDefaultAppName - } - - return "" - } - - return appName - }, } } diff --git a/coderd/notifications/notifier.go b/coderd/notifications/notifier.go index 48d9d7c86af6d..a5d984a56fb0b 100644 --- a/coderd/notifications/notifier.go +++ b/coderd/notifications/notifier.go @@ -2,7 +2,9 @@ package notifications import ( "context" + "database/sql" "encoding/json" + "errors" "sync" "text/template" @@ -23,8 +25,8 @@ import ( ) const ( - NotificationsDefaultLogoURL = "https://coder.com/coder-logo-horizontal.png" - NotificationsDefaultAppName = "Coder" + notificationsDefaultLogoURL = "https://coder.com/coder-logo-horizontal.png" + notificationsDefaultAppName = "Coder" ) // notifier is a consumer of the notifications_messages queue. It dequeues messages from that table and processes them @@ -228,6 +230,30 @@ func (n *notifier) prepare(ctx context.Context, msg database.AcquireNotification return nil, xerrors.Errorf("failed to resolve handler %q", msg.Method) } + appName, err := n.store.GetApplicationName(ctx) + if err != nil { + if !errors.Is(err, sql.ErrNoRows) { + return nil, xerrors.Errorf("get application name: %w", err) + } + appName = notificationsDefaultAppName + } + + logoURL, err := n.store.GetLogoURL(ctx) + if err != nil { + if !errors.Is(err, sql.ErrNoRows) { + return nil, xerrors.Errorf("get logo URL: %w", err) + } + logoURL = notificationsDefaultLogoURL + } + + helpers := make(template.FuncMap) + for k, v := range n.helpers { + helpers[k] = v + } + + helpers["app_name"] = func() string { return appName } + helpers["logo_url"] = func() string { return logoURL } + var title, body string if title, err = render.GoTemplate(msg.TitleTemplate, payload, n.helpers); err != nil { return nil, xerrors.Errorf("render title: %w", err) diff --git a/coderd/notifications/spec.go b/coderd/notifications/spec.go index b8ae063cc919e..e81d13d24d572 100644 --- a/coderd/notifications/spec.go +++ b/coderd/notifications/spec.go @@ -22,6 +22,8 @@ type Store interface { FetchNewMessageMetadata(ctx context.Context, arg database.FetchNewMessageMetadataParams) (database.FetchNewMessageMetadataRow, error) GetNotificationMessagesByStatus(ctx context.Context, arg database.GetNotificationMessagesByStatusParams) ([]database.NotificationMessage, error) GetNotificationsSettings(ctx context.Context) (string, error) + GetApplicationName(ctx context.Context) (string, error) + GetLogoURL(ctx context.Context) (string, error) } // Handler is responsible for preparing and delivering a notification by a given method. From 70e23ae393ce3575f7ad2db476e7a42380dfb6b3 Mon Sep 17 00:00:00 2001 From: defelmnq Date: Wed, 16 Oct 2024 14:39:24 +0200 Subject: [PATCH 09/26] feat(notifications): improve logo_url and app_name fetching moving it to notifications package --- coderd/notifications/dispatch/smtp_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/coderd/notifications/dispatch/smtp_test.go b/coderd/notifications/dispatch/smtp_test.go index 117684fba5a1b..87743a79ba7ae 100644 --- a/coderd/notifications/dispatch/smtp_test.go +++ b/coderd/notifications/dispatch/smtp_test.go @@ -445,8 +445,8 @@ func TestSMTP(t *testing.T) { helpers := map[string]any{ "base_url": func() string { return "http://test.com" }, "current_year": func() string { return "2024" }, - "logo_url": func() string { return "https://logo.company" }, - "app_name": func() string { return "TestCompany" }, + "logo_url": func() string { return "https://coder.com/coder-logo-horizontal.png" }, + "app_name": func() string { return "Coder" }, } handler := dispatch.NewSMTPHandler(tc.cfg, helpers, logger.Named("smtp")) From 2f620c9663becfd9fd09524fbf6d1cbce4d59468 Mon Sep 17 00:00:00 2001 From: defelmnq Date: Wed, 16 Oct 2024 14:48:55 +0200 Subject: [PATCH 10/26] feat(notifications): improve logo_url and app_name fetching moving it to notifications package --- coderd/notifications/utils_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/coderd/notifications/utils_test.go b/coderd/notifications/utils_test.go index 6efeddad820b8..9cf872ced1138 100644 --- a/coderd/notifications/utils_test.go +++ b/coderd/notifications/utils_test.go @@ -39,8 +39,8 @@ func defaultHelpers() map[string]any { return map[string]any{ "base_url": func() string { return "http://test.com" }, "current_year": func() string { return "2024" }, - "logo_url": func() string { return "https://logo.company" }, - "app_name": func() string { return "TestCompany" }, + "logo_url": func() string { return "https://coder.com/coder-logo-horizontal.png" }, + "app_name": func() string { return "Coder" }, } } From fdcdf7b14b68d6923db7498461e13931ad94195d Mon Sep 17 00:00:00 2001 From: defelmnq Date: Wed, 16 Oct 2024 15:07:31 +0200 Subject: [PATCH 11/26] feat(notifications): improve logo_url and app_name fetching moving it to notifications package --- coderd/notifications/dispatch/smtp/html.gotmpl | 2 +- .../smtp/TemplateTemplateDeleted.html.golden | 8 ++++---- .../smtp/TemplateUserAccountActivated.html.golden | 8 ++++---- .../smtp/TemplateUserAccountCreated.html.golden | 8 ++++---- .../smtp/TemplateUserAccountDeleted.html.golden | 8 ++++---- .../smtp/TemplateUserAccountSuspended.html.golden | 8 ++++---- .../smtp/TemplateUserRequestedOneTimePasscode.html.golden | 8 ++++---- .../smtp/TemplateWorkspaceAutoUpdated.html.golden | 8 ++++---- .../smtp/TemplateWorkspaceAutobuildFailed.html.golden | 8 ++++---- .../smtp/TemplateWorkspaceBuildsFailedReport.html.golden | 8 ++++---- .../smtp/TemplateWorkspaceDeleted.html.golden | 8 ++++---- .../smtp/TemplateWorkspaceDormant.html.golden | 8 ++++---- .../smtp/TemplateWorkspaceManualBuildFailed.html.golden | 8 ++++---- .../smtp/TemplateWorkspaceMarkedForDeletion.html.golden | 8 ++++---- .../smtp/TemplateYourAccountActivated.html.golden | 8 ++++---- .../smtp/TemplateYourAccountSuspended.html.golden | 8 ++++---- 16 files changed, 61 insertions(+), 61 deletions(-) diff --git a/coderd/notifications/dispatch/smtp/html.gotmpl b/coderd/notifications/dispatch/smtp/html.gotmpl index 1d8d9d683ad32..9240674205c57 100644 --- a/coderd/notifications/dispatch/smtp/html.gotmpl +++ b/coderd/notifications/dispatch/smtp/html.gotmpl @@ -8,7 +8,7 @@
- Company Logo + Logo

{{ app_name }}

diff --git a/coderd/notifications/testdata/rendered-templates/smtp/TemplateTemplateDeleted.html.golden b/coderd/notifications/testdata/rendered-templates/smtp/TemplateTemplateDeleted.html.golden index 2ae9ac8e61db5..8b0de43de63f7 100644 --- a/coderd/notifications/testdata/rendered-templates/smtp/TemplateTemplateDeleted.html.golden +++ b/coderd/notifications/testdata/rendered-templates/smtp/TemplateTemplateDeleted.html.golden @@ -1,8 +1,8 @@ From: system@coder.com To: bobby@coder.com Subject: Template "Bobby's Template" deleted -Message-Id: 02ee4935-73be-4fa1-a290-ff9999026b13@blush-whale-48 -Date: Fri, 11 Oct 2024 09:03:06 +0000 +Message-Id: 02ee4935-73be-4fa1-a290-ff9999026b13@blush-whale-48 +Date: Fri, 11 Oct 2024 09:03:06 +0000 Content-Type: multipart/alternative; boundary=bbe61b741255b6098bb6b3c1f41b885773df633cb18d2a3002b68e4bc9c4 MIME-Version: 1.0 @@ -37,8 +37,8 @@ l', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; color: #020617= r: 1px solid #e2e8f0; border-radius: 8px; background-color: #fff; text-alig= n: left; font-size: 14px; line-height: 1.5;">
- 3D"Cod= + 3D"Log=

diff --git a/coderd/notifications/testdata/rendered-templates/smtp/TemplateUserAccountActivated.html.golden b/coderd/notifications/testdata/rendered-templates/smtp/TemplateUserAccountActivated.html.golden index 81cbe7f54fd0e..2c2ebba6b7474 100644 --- a/coderd/notifications/testdata/rendered-templates/smtp/TemplateUserAccountActivated.html.golden +++ b/coderd/notifications/testdata/rendered-templates/smtp/TemplateUserAccountActivated.html.golden @@ -1,8 +1,8 @@ From: system@coder.com To: bobby@coder.com Subject: User account "bobby" activated -Message-Id: 02ee4935-73be-4fa1-a290-ff9999026b13@blush-whale-48 -Date: Fri, 11 Oct 2024 09:03:06 +0000 +Message-Id: 02ee4935-73be-4fa1-a290-ff9999026b13@blush-whale-48 +Date: Fri, 11 Oct 2024 09:03:06 +0000 Content-Type: multipart/alternative; boundary=bbe61b741255b6098bb6b3c1f41b885773df633cb18d2a3002b68e4bc9c4 MIME-Version: 1.0 @@ -40,8 +40,8 @@ l', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; color: #020617= r: 1px solid #e2e8f0; border-radius: 8px; background-color: #fff; text-alig= n: left; font-size: 14px; line-height: 1.5;">
- 3D"Cod= + 3D"Log=

diff --git a/coderd/notifications/testdata/rendered-templates/smtp/TemplateUserAccountCreated.html.golden b/coderd/notifications/testdata/rendered-templates/smtp/TemplateUserAccountCreated.html.golden index 9a6cab0989897..365093180d009 100644 --- a/coderd/notifications/testdata/rendered-templates/smtp/TemplateUserAccountCreated.html.golden +++ b/coderd/notifications/testdata/rendered-templates/smtp/TemplateUserAccountCreated.html.golden @@ -1,8 +1,8 @@ From: system@coder.com To: bobby@coder.com Subject: User account "bobby" created -Message-Id: 02ee4935-73be-4fa1-a290-ff9999026b13@blush-whale-48 -Date: Fri, 11 Oct 2024 09:03:06 +0000 +Message-Id: 02ee4935-73be-4fa1-a290-ff9999026b13@blush-whale-48 +Date: Fri, 11 Oct 2024 09:03:06 +0000 Content-Type: multipart/alternative; boundary=bbe61b741255b6098bb6b3c1f41b885773df633cb18d2a3002b68e4bc9c4 MIME-Version: 1.0 @@ -39,8 +39,8 @@ l', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; color: #020617= r: 1px solid #e2e8f0; border-radius: 8px; background-color: #fff; text-alig= n: left; font-size: 14px; line-height: 1.5;">
- 3D"Cod= + 3D"Log=

diff --git a/coderd/notifications/testdata/rendered-templates/smtp/TemplateUserAccountDeleted.html.golden b/coderd/notifications/testdata/rendered-templates/smtp/TemplateUserAccountDeleted.html.golden index c7daad54f028b..88d4802ba1d0f 100644 --- a/coderd/notifications/testdata/rendered-templates/smtp/TemplateUserAccountDeleted.html.golden +++ b/coderd/notifications/testdata/rendered-templates/smtp/TemplateUserAccountDeleted.html.golden @@ -1,8 +1,8 @@ From: system@coder.com To: bobby@coder.com Subject: User account "bobby" deleted -Message-Id: 02ee4935-73be-4fa1-a290-ff9999026b13@blush-whale-48 -Date: Fri, 11 Oct 2024 09:03:06 +0000 +Message-Id: 02ee4935-73be-4fa1-a290-ff9999026b13@blush-whale-48 +Date: Fri, 11 Oct 2024 09:03:06 +0000 Content-Type: multipart/alternative; boundary=bbe61b741255b6098bb6b3c1f41b885773df633cb18d2a3002b68e4bc9c4 MIME-Version: 1.0 @@ -39,8 +39,8 @@ l', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; color: #020617= r: 1px solid #e2e8f0; border-radius: 8px; background-color: #fff; text-alig= n: left; font-size: 14px; line-height: 1.5;">
- 3D"Cod= + 3D"Log=

diff --git a/coderd/notifications/testdata/rendered-templates/smtp/TemplateUserAccountSuspended.html.golden b/coderd/notifications/testdata/rendered-templates/smtp/TemplateUserAccountSuspended.html.golden index ccd40593ef5fb..19d4b39dc6f04 100644 --- a/coderd/notifications/testdata/rendered-templates/smtp/TemplateUserAccountSuspended.html.golden +++ b/coderd/notifications/testdata/rendered-templates/smtp/TemplateUserAccountSuspended.html.golden @@ -1,8 +1,8 @@ From: system@coder.com To: bobby@coder.com Subject: User account "bobby" suspended -Message-Id: 02ee4935-73be-4fa1-a290-ff9999026b13@blush-whale-48 -Date: Fri, 11 Oct 2024 09:03:06 +0000 +Message-Id: 02ee4935-73be-4fa1-a290-ff9999026b13@blush-whale-48 +Date: Fri, 11 Oct 2024 09:03:06 +0000 Content-Type: multipart/alternative; boundary=bbe61b741255b6098bb6b3c1f41b885773df633cb18d2a3002b68e4bc9c4 MIME-Version: 1.0 @@ -41,8 +41,8 @@ l', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; color: #020617= r: 1px solid #e2e8f0; border-radius: 8px; background-color: #fff; text-alig= n: left; font-size: 14px; line-height: 1.5;">
- 3D"Cod= + 3D"Log=

diff --git a/coderd/notifications/testdata/rendered-templates/smtp/TemplateUserRequestedOneTimePasscode.html.golden b/coderd/notifications/testdata/rendered-templates/smtp/TemplateUserRequestedOneTimePasscode.html.golden index 2b61765813bcf..33f1527044a4c 100644 --- a/coderd/notifications/testdata/rendered-templates/smtp/TemplateUserRequestedOneTimePasscode.html.golden +++ b/coderd/notifications/testdata/rendered-templates/smtp/TemplateUserRequestedOneTimePasscode.html.golden @@ -1,8 +1,8 @@ From: system@coder.com To: bobby@coder.com Subject: Your One-Time Passcode for Coder. -Message-Id: 02ee4935-73be-4fa1-a290-ff9999026b13@blush-whale-48 -Date: Fri, 11 Oct 2024 09:03:06 +0000 +Message-Id: 02ee4935-73be-4fa1-a290-ff9999026b13@blush-whale-48 +Date: Fri, 11 Oct 2024 09:03:06 +0000 Content-Type: multipart/alternative; boundary=bbe61b741255b6098bb6b3c1f41b885773df633cb18d2a3002b68e4bc9c4 MIME-Version: 1.0 @@ -40,8 +40,8 @@ l', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; color: #020617= r: 1px solid #e2e8f0; border-radius: 8px; background-color: #fff; text-alig= n: left; font-size: 14px; line-height: 1.5;">
- 3D"Cod= + 3D"Log=

diff --git a/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceAutoUpdated.html.golden b/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceAutoUpdated.html.golden index 6c68cffa8bc1b..be1d5fe22ce31 100644 --- a/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceAutoUpdated.html.golden +++ b/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceAutoUpdated.html.golden @@ -1,8 +1,8 @@ From: system@coder.com To: bobby@coder.com Subject: Workspace "bobby-workspace" updated automatically -Message-Id: 02ee4935-73be-4fa1-a290-ff9999026b13@blush-whale-48 -Date: Fri, 11 Oct 2024 09:03:06 +0000 +Message-Id: 02ee4935-73be-4fa1-a290-ff9999026b13@blush-whale-48 +Date: Fri, 11 Oct 2024 09:03:06 +0000 Content-Type: multipart/alternative; boundary=bbe61b741255b6098bb6b3c1f41b885773df633cb18d2a3002b68e4bc9c4 MIME-Version: 1.0 @@ -40,8 +40,8 @@ l', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; color: #020617= r: 1px solid #e2e8f0; border-radius: 8px; background-color: #fff; text-alig= n: left; font-size: 14px; line-height: 1.5;">
- 3D"Cod= + 3D"Log=

diff --git a/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceAutobuildFailed.html.golden b/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceAutobuildFailed.html.golden index 340e794f15c74..804851f33368c 100644 --- a/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceAutobuildFailed.html.golden +++ b/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceAutobuildFailed.html.golden @@ -1,8 +1,8 @@ From: system@coder.com To: bobby@coder.com Subject: Workspace "bobby-workspace" autobuild failed -Message-Id: 02ee4935-73be-4fa1-a290-ff9999026b13@blush-whale-48 -Date: Fri, 11 Oct 2024 09:03:06 +0000 +Message-Id: 02ee4935-73be-4fa1-a290-ff9999026b13@blush-whale-48 +Date: Fri, 11 Oct 2024 09:03:06 +0000 Content-Type: multipart/alternative; boundary=bbe61b741255b6098bb6b3c1f41b885773df633cb18d2a3002b68e4bc9c4 MIME-Version: 1.0 @@ -39,8 +39,8 @@ l', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; color: #020617= r: 1px solid #e2e8f0; border-radius: 8px; background-color: #fff; text-alig= n: left; font-size: 14px; line-height: 1.5;">
- 3D"Cod= + 3D"Log=

diff --git a/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceBuildsFailedReport.html.golden b/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceBuildsFailedReport.html.golden index 7cc16f00f3796..036e1f9f6aefa 100644 --- a/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceBuildsFailedReport.html.golden +++ b/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceBuildsFailedReport.html.golden @@ -1,8 +1,8 @@ From: system@coder.com To: bobby@coder.com Subject: Workspace builds failed for template "Bobby First Template" -Message-Id: 02ee4935-73be-4fa1-a290-ff9999026b13@blush-whale-48 -Date: Fri, 11 Oct 2024 09:03:06 +0000 +Message-Id: 02ee4935-73be-4fa1-a290-ff9999026b13@blush-whale-48 +Date: Fri, 11 Oct 2024 09:03:06 +0000 Content-Type: multipart/alternative; boundary=bbe61b741255b6098bb6b3c1f41b885773df633cb18d2a3002b68e4bc9c4 MIME-Version: 1.0 @@ -57,8 +57,8 @@ l', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; color: #020617= r: 1px solid #e2e8f0; border-radius: 8px; background-color: #fff; text-alig= n: left; font-size: 14px; line-height: 1.5;">
- 3D"Cod= + 3D"Log=

diff --git a/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceDeleted.html.golden b/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceDeleted.html.golden index 0d821bdc4dacd..9278ae7fd0bd8 100644 --- a/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceDeleted.html.golden +++ b/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceDeleted.html.golden @@ -1,8 +1,8 @@ From: system@coder.com To: bobby@coder.com Subject: Workspace "bobby-workspace" deleted -Message-Id: 02ee4935-73be-4fa1-a290-ff9999026b13@blush-whale-48 -Date: Fri, 11 Oct 2024 09:03:06 +0000 +Message-Id: 02ee4935-73be-4fa1-a290-ff9999026b13@blush-whale-48 +Date: Fri, 11 Oct 2024 09:03:06 +0000 Content-Type: multipart/alternative; boundary=bbe61b741255b6098bb6b3c1f41b885773df633cb18d2a3002b68e4bc9c4 MIME-Version: 1.0 @@ -41,8 +41,8 @@ l', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; color: #020617= r: 1px solid #e2e8f0; border-radius: 8px; background-color: #fff; text-alig= n: left; font-size: 14px; line-height: 1.5;">
- 3D"Cod= + 3D"Log=

diff --git a/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceDormant.html.golden b/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceDormant.html.golden index 0c6cbf5a2dd85..59ffae029cba0 100644 --- a/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceDormant.html.golden +++ b/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceDormant.html.golden @@ -1,8 +1,8 @@ From: system@coder.com To: bobby@coder.com Subject: Workspace "bobby-workspace" marked as dormant -Message-Id: 02ee4935-73be-4fa1-a290-ff9999026b13@blush-whale-48 -Date: Fri, 11 Oct 2024 09:03:06 +0000 +Message-Id: 02ee4935-73be-4fa1-a290-ff9999026b13@blush-whale-48 +Date: Fri, 11 Oct 2024 09:03:06 +0000 Content-Type: multipart/alternative; boundary=bbe61b741255b6098bb6b3c1f41b885773df633cb18d2a3002b68e4bc9c4 MIME-Version: 1.0 @@ -43,8 +43,8 @@ l', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; color: #020617= r: 1px solid #e2e8f0; border-radius: 8px; background-color: #fff; text-alig= n: left; font-size: 14px; line-height: 1.5;">
- 3D"Cod= + 3D"Log=

diff --git a/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceManualBuildFailed.html.golden b/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceManualBuildFailed.html.golden index 1f456a72f4df4..b38aacabe1248 100644 --- a/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceManualBuildFailed.html.golden +++ b/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceManualBuildFailed.html.golden @@ -1,8 +1,8 @@ From: system@coder.com To: bobby@coder.com Subject: Workspace "bobby-workspace" manual build failed -Message-Id: 02ee4935-73be-4fa1-a290-ff9999026b13@blush-whale-48 -Date: Fri, 11 Oct 2024 09:03:06 +0000 +Message-Id: 02ee4935-73be-4fa1-a290-ff9999026b13@blush-whale-48 +Date: Fri, 11 Oct 2024 09:03:06 +0000 Content-Type: multipart/alternative; boundary=bbe61b741255b6098bb6b3c1f41b885773df633cb18d2a3002b68e4bc9c4 MIME-Version: 1.0 @@ -40,8 +40,8 @@ l', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; color: #020617= r: 1px solid #e2e8f0; border-radius: 8px; background-color: #fff; text-alig= n: left; font-size: 14px; line-height: 1.5;">
- 3D"Cod= + 3D"Log=

diff --git a/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceMarkedForDeletion.html.golden b/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceMarkedForDeletion.html.golden index 6d91458f2cbcc..18c8d81df3c70 100644 --- a/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceMarkedForDeletion.html.golden +++ b/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceMarkedForDeletion.html.golden @@ -1,8 +1,8 @@ From: system@coder.com To: bobby@coder.com Subject: Workspace "bobby-workspace" marked for deletion -Message-Id: 02ee4935-73be-4fa1-a290-ff9999026b13@blush-whale-48 -Date: Fri, 11 Oct 2024 09:03:06 +0000 +Message-Id: 02ee4935-73be-4fa1-a290-ff9999026b13@blush-whale-48 +Date: Fri, 11 Oct 2024 09:03:06 +0000 Content-Type: multipart/alternative; boundary=bbe61b741255b6098bb6b3c1f41b885773df633cb18d2a3002b68e4bc9c4 MIME-Version: 1.0 @@ -40,8 +40,8 @@ l', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; color: #020617= r: 1px solid #e2e8f0; border-radius: 8px; background-color: #fff; text-alig= n: left; font-size: 14px; line-height: 1.5;">
- 3D"Cod= + 3D"Log=

diff --git a/coderd/notifications/testdata/rendered-templates/smtp/TemplateYourAccountActivated.html.golden b/coderd/notifications/testdata/rendered-templates/smtp/TemplateYourAccountActivated.html.golden index aef12ab957feb..2e12c2d310dc5 100644 --- a/coderd/notifications/testdata/rendered-templates/smtp/TemplateYourAccountActivated.html.golden +++ b/coderd/notifications/testdata/rendered-templates/smtp/TemplateYourAccountActivated.html.golden @@ -1,8 +1,8 @@ From: system@coder.com To: bobby@coder.com Subject: Your account "bobby" has been activated -Message-Id: 02ee4935-73be-4fa1-a290-ff9999026b13@blush-whale-48 -Date: Fri, 11 Oct 2024 09:03:06 +0000 +Message-Id: 02ee4935-73be-4fa1-a290-ff9999026b13@blush-whale-48 +Date: Fri, 11 Oct 2024 09:03:06 +0000 Content-Type: multipart/alternative; boundary=bbe61b741255b6098bb6b3c1f41b885773df633cb18d2a3002b68e4bc9c4 MIME-Version: 1.0 @@ -37,8 +37,8 @@ l', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; color: #020617= r: 1px solid #e2e8f0; border-radius: 8px; background-color: #fff; text-alig= n: left; font-size: 14px; line-height: 1.5;">
- 3D"Cod= + 3D"Log=

diff --git a/coderd/notifications/testdata/rendered-templates/smtp/TemplateYourAccountSuspended.html.golden b/coderd/notifications/testdata/rendered-templates/smtp/TemplateYourAccountSuspended.html.golden index d9406e2c1f344..332d6201ec5dd 100644 --- a/coderd/notifications/testdata/rendered-templates/smtp/TemplateYourAccountSuspended.html.golden +++ b/coderd/notifications/testdata/rendered-templates/smtp/TemplateYourAccountSuspended.html.golden @@ -1,8 +1,8 @@ From: system@coder.com To: bobby@coder.com Subject: Your account "bobby" has been suspended -Message-Id: 02ee4935-73be-4fa1-a290-ff9999026b13@blush-whale-48 -Date: Fri, 11 Oct 2024 09:03:06 +0000 +Message-Id: 02ee4935-73be-4fa1-a290-ff9999026b13@blush-whale-48 +Date: Fri, 11 Oct 2024 09:03:06 +0000 Content-Type: multipart/alternative; boundary=bbe61b741255b6098bb6b3c1f41b885773df633cb18d2a3002b68e4bc9c4 MIME-Version: 1.0 @@ -35,8 +35,8 @@ l', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; color: #020617= r: 1px solid #e2e8f0; border-radius: 8px; background-color: #fff; text-alig= n: left; font-size: 14px; line-height: 1.5;">
- 3D"Cod= + 3D"Log=

From 9c6c105ea5ac42c2dfb1105f076bf1fe60d1b302 Mon Sep 17 00:00:00 2001 From: defelmnq Date: Wed, 16 Oct 2024 13:49:01 +0000 Subject: [PATCH 12/26] feat(coderd): regenerate golden files --- .../smtp/TemplateTemplateDeleted.html.golden | 8 ++++++-- .../smtp/TemplateUserAccountActivated.html.golden | 8 ++++++-- .../smtp/TemplateUserAccountCreated.html.golden | 8 ++++++-- .../smtp/TemplateUserAccountDeleted.html.golden | 8 ++++++-- .../smtp/TemplateUserAccountSuspended.html.golden | 8 ++++++-- .../smtp/TemplateUserRequestedOneTimePasscode.html.golden | 8 ++++++-- .../smtp/TemplateWorkspaceAutoUpdated.html.golden | 8 ++++++-- .../smtp/TemplateWorkspaceAutobuildFailed.html.golden | 8 ++++++-- .../smtp/TemplateWorkspaceBuildsFailedReport.html.golden | 8 ++++++-- .../smtp/TemplateWorkspaceDeleted.html.golden | 8 ++++++-- .../smtp/TemplateWorkspaceDormant.html.golden | 8 ++++++-- .../smtp/TemplateWorkspaceManualBuildFailed.html.golden | 8 ++++++-- .../smtp/TemplateWorkspaceMarkedForDeletion.html.golden | 8 ++++++-- .../smtp/TemplateYourAccountActivated.html.golden | 8 ++++++-- .../smtp/TemplateYourAccountSuspended.html.golden | 8 ++++++-- 15 files changed, 90 insertions(+), 30 deletions(-) diff --git a/coderd/notifications/testdata/rendered-templates/smtp/TemplateTemplateDeleted.html.golden b/coderd/notifications/testdata/rendered-templates/smtp/TemplateTemplateDeleted.html.golden index 8b0de43de63f7..26b1cf35e9b9c 100644 --- a/coderd/notifications/testdata/rendered-templates/smtp/TemplateTemplateDeleted.html.golden +++ b/coderd/notifications/testdata/rendered-templates/smtp/TemplateTemplateDeleted.html.golden @@ -1,8 +1,8 @@ From: system@coder.com To: bobby@coder.com Subject: Template "Bobby's Template" deleted -Message-Id: 02ee4935-73be-4fa1-a290-ff9999026b13@blush-whale-48 -Date: Fri, 11 Oct 2024 09:03:06 +0000 +Message-Id: 02ee4935-73be-4fa1-a290-ff9999026b13@blush-whale-48 +Date: Fri, 11 Oct 2024 09:03:06 +0000 Content-Type: multipart/alternative; boundary=bbe61b741255b6098bb6b3c1f41b885773df633cb18d2a3002b68e4bc9c4 MIME-Version: 1.0 @@ -39,6 +39,10 @@ n: left; font-size: 14px; line-height: 1.5;">
3D"Log= +

+ Coder +

diff --git a/coderd/notifications/testdata/rendered-templates/smtp/TemplateUserAccountActivated.html.golden b/coderd/notifications/testdata/rendered-templates/smtp/TemplateUserAccountActivated.html.golden index 2c2ebba6b7474..382a74dcca0cd 100644 --- a/coderd/notifications/testdata/rendered-templates/smtp/TemplateUserAccountActivated.html.golden +++ b/coderd/notifications/testdata/rendered-templates/smtp/TemplateUserAccountActivated.html.golden @@ -1,8 +1,8 @@ From: system@coder.com To: bobby@coder.com Subject: User account "bobby" activated -Message-Id: 02ee4935-73be-4fa1-a290-ff9999026b13@blush-whale-48 -Date: Fri, 11 Oct 2024 09:03:06 +0000 +Message-Id: 02ee4935-73be-4fa1-a290-ff9999026b13@blush-whale-48 +Date: Fri, 11 Oct 2024 09:03:06 +0000 Content-Type: multipart/alternative; boundary=bbe61b741255b6098bb6b3c1f41b885773df633cb18d2a3002b68e4bc9c4 MIME-Version: 1.0 @@ -42,6 +42,10 @@ n: left; font-size: 14px; line-height: 1.5;">
3D"Log= +

+ Coder +

diff --git a/coderd/notifications/testdata/rendered-templates/smtp/TemplateUserAccountCreated.html.golden b/coderd/notifications/testdata/rendered-templates/smtp/TemplateUserAccountCreated.html.golden index 365093180d009..8ec582093d00b 100644 --- a/coderd/notifications/testdata/rendered-templates/smtp/TemplateUserAccountCreated.html.golden +++ b/coderd/notifications/testdata/rendered-templates/smtp/TemplateUserAccountCreated.html.golden @@ -1,8 +1,8 @@ From: system@coder.com To: bobby@coder.com Subject: User account "bobby" created -Message-Id: 02ee4935-73be-4fa1-a290-ff9999026b13@blush-whale-48 -Date: Fri, 11 Oct 2024 09:03:06 +0000 +Message-Id: 02ee4935-73be-4fa1-a290-ff9999026b13@blush-whale-48 +Date: Fri, 11 Oct 2024 09:03:06 +0000 Content-Type: multipart/alternative; boundary=bbe61b741255b6098bb6b3c1f41b885773df633cb18d2a3002b68e4bc9c4 MIME-Version: 1.0 @@ -41,6 +41,10 @@ n: left; font-size: 14px; line-height: 1.5;">
3D"Log= +

+ Coder +

diff --git a/coderd/notifications/testdata/rendered-templates/smtp/TemplateUserAccountDeleted.html.golden b/coderd/notifications/testdata/rendered-templates/smtp/TemplateUserAccountDeleted.html.golden index 88d4802ba1d0f..464a9b3fb69c3 100644 --- a/coderd/notifications/testdata/rendered-templates/smtp/TemplateUserAccountDeleted.html.golden +++ b/coderd/notifications/testdata/rendered-templates/smtp/TemplateUserAccountDeleted.html.golden @@ -1,8 +1,8 @@ From: system@coder.com To: bobby@coder.com Subject: User account "bobby" deleted -Message-Id: 02ee4935-73be-4fa1-a290-ff9999026b13@blush-whale-48 -Date: Fri, 11 Oct 2024 09:03:06 +0000 +Message-Id: 02ee4935-73be-4fa1-a290-ff9999026b13@blush-whale-48 +Date: Fri, 11 Oct 2024 09:03:06 +0000 Content-Type: multipart/alternative; boundary=bbe61b741255b6098bb6b3c1f41b885773df633cb18d2a3002b68e4bc9c4 MIME-Version: 1.0 @@ -41,6 +41,10 @@ n: left; font-size: 14px; line-height: 1.5;">
3D"Log= +

+ Coder +

diff --git a/coderd/notifications/testdata/rendered-templates/smtp/TemplateUserAccountSuspended.html.golden b/coderd/notifications/testdata/rendered-templates/smtp/TemplateUserAccountSuspended.html.golden index 19d4b39dc6f04..0045e7d7f10f2 100644 --- a/coderd/notifications/testdata/rendered-templates/smtp/TemplateUserAccountSuspended.html.golden +++ b/coderd/notifications/testdata/rendered-templates/smtp/TemplateUserAccountSuspended.html.golden @@ -1,8 +1,8 @@ From: system@coder.com To: bobby@coder.com Subject: User account "bobby" suspended -Message-Id: 02ee4935-73be-4fa1-a290-ff9999026b13@blush-whale-48 -Date: Fri, 11 Oct 2024 09:03:06 +0000 +Message-Id: 02ee4935-73be-4fa1-a290-ff9999026b13@blush-whale-48 +Date: Fri, 11 Oct 2024 09:03:06 +0000 Content-Type: multipart/alternative; boundary=bbe61b741255b6098bb6b3c1f41b885773df633cb18d2a3002b68e4bc9c4 MIME-Version: 1.0 @@ -43,6 +43,10 @@ n: left; font-size: 14px; line-height: 1.5;">
3D"Log= +

+ Coder +

diff --git a/coderd/notifications/testdata/rendered-templates/smtp/TemplateUserRequestedOneTimePasscode.html.golden b/coderd/notifications/testdata/rendered-templates/smtp/TemplateUserRequestedOneTimePasscode.html.golden index 33f1527044a4c..d9d7936e996a7 100644 --- a/coderd/notifications/testdata/rendered-templates/smtp/TemplateUserRequestedOneTimePasscode.html.golden +++ b/coderd/notifications/testdata/rendered-templates/smtp/TemplateUserRequestedOneTimePasscode.html.golden @@ -1,8 +1,8 @@ From: system@coder.com To: bobby@coder.com Subject: Your One-Time Passcode for Coder. -Message-Id: 02ee4935-73be-4fa1-a290-ff9999026b13@blush-whale-48 -Date: Fri, 11 Oct 2024 09:03:06 +0000 +Message-Id: 02ee4935-73be-4fa1-a290-ff9999026b13@blush-whale-48 +Date: Fri, 11 Oct 2024 09:03:06 +0000 Content-Type: multipart/alternative; boundary=bbe61b741255b6098bb6b3c1f41b885773df633cb18d2a3002b68e4bc9c4 MIME-Version: 1.0 @@ -42,6 +42,10 @@ n: left; font-size: 14px; line-height: 1.5;">
3D"Log= +

+ Coder +

diff --git a/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceAutoUpdated.html.golden b/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceAutoUpdated.html.golden index be1d5fe22ce31..f6b908814d878 100644 --- a/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceAutoUpdated.html.golden +++ b/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceAutoUpdated.html.golden @@ -1,8 +1,8 @@ From: system@coder.com To: bobby@coder.com Subject: Workspace "bobby-workspace" updated automatically -Message-Id: 02ee4935-73be-4fa1-a290-ff9999026b13@blush-whale-48 -Date: Fri, 11 Oct 2024 09:03:06 +0000 +Message-Id: 02ee4935-73be-4fa1-a290-ff9999026b13@blush-whale-48 +Date: Fri, 11 Oct 2024 09:03:06 +0000 Content-Type: multipart/alternative; boundary=bbe61b741255b6098bb6b3c1f41b885773df633cb18d2a3002b68e4bc9c4 MIME-Version: 1.0 @@ -42,6 +42,10 @@ n: left; font-size: 14px; line-height: 1.5;">
3D"Log= +

+ Coder +

diff --git a/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceAutobuildFailed.html.golden b/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceAutobuildFailed.html.golden index 804851f33368c..14fa316cbc71b 100644 --- a/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceAutobuildFailed.html.golden +++ b/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceAutobuildFailed.html.golden @@ -1,8 +1,8 @@ From: system@coder.com To: bobby@coder.com Subject: Workspace "bobby-workspace" autobuild failed -Message-Id: 02ee4935-73be-4fa1-a290-ff9999026b13@blush-whale-48 -Date: Fri, 11 Oct 2024 09:03:06 +0000 +Message-Id: 02ee4935-73be-4fa1-a290-ff9999026b13@blush-whale-48 +Date: Fri, 11 Oct 2024 09:03:06 +0000 Content-Type: multipart/alternative; boundary=bbe61b741255b6098bb6b3c1f41b885773df633cb18d2a3002b68e4bc9c4 MIME-Version: 1.0 @@ -41,6 +41,10 @@ n: left; font-size: 14px; line-height: 1.5;">
3D"Log= +

+ Coder +

diff --git a/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceBuildsFailedReport.html.golden b/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceBuildsFailedReport.html.golden index 036e1f9f6aefa..e83ee9bddac51 100644 --- a/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceBuildsFailedReport.html.golden +++ b/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceBuildsFailedReport.html.golden @@ -1,8 +1,8 @@ From: system@coder.com To: bobby@coder.com Subject: Workspace builds failed for template "Bobby First Template" -Message-Id: 02ee4935-73be-4fa1-a290-ff9999026b13@blush-whale-48 -Date: Fri, 11 Oct 2024 09:03:06 +0000 +Message-Id: 02ee4935-73be-4fa1-a290-ff9999026b13@blush-whale-48 +Date: Fri, 11 Oct 2024 09:03:06 +0000 Content-Type: multipart/alternative; boundary=bbe61b741255b6098bb6b3c1f41b885773df633cb18d2a3002b68e4bc9c4 MIME-Version: 1.0 @@ -59,6 +59,10 @@ n: left; font-size: 14px; line-height: 1.5;">
3D"Log= +

+ Coder +

diff --git a/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceDeleted.html.golden b/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceDeleted.html.golden index 9278ae7fd0bd8..6b715a6e485e1 100644 --- a/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceDeleted.html.golden +++ b/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceDeleted.html.golden @@ -1,8 +1,8 @@ From: system@coder.com To: bobby@coder.com Subject: Workspace "bobby-workspace" deleted -Message-Id: 02ee4935-73be-4fa1-a290-ff9999026b13@blush-whale-48 -Date: Fri, 11 Oct 2024 09:03:06 +0000 +Message-Id: 02ee4935-73be-4fa1-a290-ff9999026b13@blush-whale-48 +Date: Fri, 11 Oct 2024 09:03:06 +0000 Content-Type: multipart/alternative; boundary=bbe61b741255b6098bb6b3c1f41b885773df633cb18d2a3002b68e4bc9c4 MIME-Version: 1.0 @@ -43,6 +43,10 @@ n: left; font-size: 14px; line-height: 1.5;">
3D"Log= +

+ Coder +

diff --git a/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceDormant.html.golden b/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceDormant.html.golden index 59ffae029cba0..10b39588d19bf 100644 --- a/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceDormant.html.golden +++ b/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceDormant.html.golden @@ -1,8 +1,8 @@ From: system@coder.com To: bobby@coder.com Subject: Workspace "bobby-workspace" marked as dormant -Message-Id: 02ee4935-73be-4fa1-a290-ff9999026b13@blush-whale-48 -Date: Fri, 11 Oct 2024 09:03:06 +0000 +Message-Id: 02ee4935-73be-4fa1-a290-ff9999026b13@blush-whale-48 +Date: Fri, 11 Oct 2024 09:03:06 +0000 Content-Type: multipart/alternative; boundary=bbe61b741255b6098bb6b3c1f41b885773df633cb18d2a3002b68e4bc9c4 MIME-Version: 1.0 @@ -45,6 +45,10 @@ n: left; font-size: 14px; line-height: 1.5;">
3D"Log= +

+ Coder +

diff --git a/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceManualBuildFailed.html.golden b/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceManualBuildFailed.html.golden index b38aacabe1248..5457fefd88d63 100644 --- a/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceManualBuildFailed.html.golden +++ b/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceManualBuildFailed.html.golden @@ -1,8 +1,8 @@ From: system@coder.com To: bobby@coder.com Subject: Workspace "bobby-workspace" manual build failed -Message-Id: 02ee4935-73be-4fa1-a290-ff9999026b13@blush-whale-48 -Date: Fri, 11 Oct 2024 09:03:06 +0000 +Message-Id: 02ee4935-73be-4fa1-a290-ff9999026b13@blush-whale-48 +Date: Fri, 11 Oct 2024 09:03:06 +0000 Content-Type: multipart/alternative; boundary=bbe61b741255b6098bb6b3c1f41b885773df633cb18d2a3002b68e4bc9c4 MIME-Version: 1.0 @@ -42,6 +42,10 @@ n: left; font-size: 14px; line-height: 1.5;">
3D"Log= +

+ Coder +

diff --git a/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceMarkedForDeletion.html.golden b/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceMarkedForDeletion.html.golden index 18c8d81df3c70..152faca3d29b0 100644 --- a/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceMarkedForDeletion.html.golden +++ b/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceMarkedForDeletion.html.golden @@ -1,8 +1,8 @@ From: system@coder.com To: bobby@coder.com Subject: Workspace "bobby-workspace" marked for deletion -Message-Id: 02ee4935-73be-4fa1-a290-ff9999026b13@blush-whale-48 -Date: Fri, 11 Oct 2024 09:03:06 +0000 +Message-Id: 02ee4935-73be-4fa1-a290-ff9999026b13@blush-whale-48 +Date: Fri, 11 Oct 2024 09:03:06 +0000 Content-Type: multipart/alternative; boundary=bbe61b741255b6098bb6b3c1f41b885773df633cb18d2a3002b68e4bc9c4 MIME-Version: 1.0 @@ -42,6 +42,10 @@ n: left; font-size: 14px; line-height: 1.5;">
3D"Log= +

+ Coder +

diff --git a/coderd/notifications/testdata/rendered-templates/smtp/TemplateYourAccountActivated.html.golden b/coderd/notifications/testdata/rendered-templates/smtp/TemplateYourAccountActivated.html.golden index 2e12c2d310dc5..6168e369bfd50 100644 --- a/coderd/notifications/testdata/rendered-templates/smtp/TemplateYourAccountActivated.html.golden +++ b/coderd/notifications/testdata/rendered-templates/smtp/TemplateYourAccountActivated.html.golden @@ -1,8 +1,8 @@ From: system@coder.com To: bobby@coder.com Subject: Your account "bobby" has been activated -Message-Id: 02ee4935-73be-4fa1-a290-ff9999026b13@blush-whale-48 -Date: Fri, 11 Oct 2024 09:03:06 +0000 +Message-Id: 02ee4935-73be-4fa1-a290-ff9999026b13@blush-whale-48 +Date: Fri, 11 Oct 2024 09:03:06 +0000 Content-Type: multipart/alternative; boundary=bbe61b741255b6098bb6b3c1f41b885773df633cb18d2a3002b68e4bc9c4 MIME-Version: 1.0 @@ -39,6 +39,10 @@ n: left; font-size: 14px; line-height: 1.5;">
3D"Log= +

+ Coder +

diff --git a/coderd/notifications/testdata/rendered-templates/smtp/TemplateYourAccountSuspended.html.golden b/coderd/notifications/testdata/rendered-templates/smtp/TemplateYourAccountSuspended.html.golden index 332d6201ec5dd..a15ec36ea2060 100644 --- a/coderd/notifications/testdata/rendered-templates/smtp/TemplateYourAccountSuspended.html.golden +++ b/coderd/notifications/testdata/rendered-templates/smtp/TemplateYourAccountSuspended.html.golden @@ -1,8 +1,8 @@ From: system@coder.com To: bobby@coder.com Subject: Your account "bobby" has been suspended -Message-Id: 02ee4935-73be-4fa1-a290-ff9999026b13@blush-whale-48 -Date: Fri, 11 Oct 2024 09:03:06 +0000 +Message-Id: 02ee4935-73be-4fa1-a290-ff9999026b13@blush-whale-48 +Date: Fri, 11 Oct 2024 09:03:06 +0000 Content-Type: multipart/alternative; boundary=bbe61b741255b6098bb6b3c1f41b885773df633cb18d2a3002b68e4bc9c4 MIME-Version: 1.0 @@ -37,6 +37,10 @@ n: left; font-size: 14px; line-height: 1.5;">
3D"Log= +

+ Coder +

From 0c131a5acd537cd5ff69ec962621c38a88d72cd7 Mon Sep 17 00:00:00 2001 From: defelmnq Date: Thu, 17 Oct 2024 03:06:50 +0200 Subject: [PATCH 13/26] feat(notifications): working on tests for logo and app_name --- .../notifications/dispatch/smtp/html.gotmpl | 5 +-- coderd/notifications/fetcher.go | 31 +++++++++++++++++++ coderd/notifications/notifier.go | 25 +++++---------- coderd/notifications/utils_test.go | 2 -- 4 files changed, 40 insertions(+), 23 deletions(-) create mode 100644 coderd/notifications/fetcher.go diff --git a/coderd/notifications/dispatch/smtp/html.gotmpl b/coderd/notifications/dispatch/smtp/html.gotmpl index 9240674205c57..23a549288fa15 100644 --- a/coderd/notifications/dispatch/smtp/html.gotmpl +++ b/coderd/notifications/dispatch/smtp/html.gotmpl @@ -8,10 +8,7 @@
- Logo -

- {{ app_name }} -

+ {{ app_name }} Logo

{{ .Labels._subject }} diff --git a/coderd/notifications/fetcher.go b/coderd/notifications/fetcher.go new file mode 100644 index 0000000000000..10f31be7dba66 --- /dev/null +++ b/coderd/notifications/fetcher.go @@ -0,0 +1,31 @@ +package notifications + +import ( + "context" + "database/sql" + "errors" + + "golang.org/x/xerrors" +) + +func (n *notifier) FetchAppName(ctx context.Context) (string, error) { + appName, err := n.store.GetApplicationName(ctx) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + return notificationsDefaultAppName, nil + } + return "", xerrors.Errorf("get organization: %w", err) + } + return appName, nil +} + +func (n *notifier) FetchLogoURL(ctx context.Context) (string, error) { + logoURL, err := n.store.GetLogoURL(ctx) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + return notificationsDefaultLogoURL, nil + } + return "", xerrors.Errorf("get logo URL: %w", err) + } + return logoURL, nil +} diff --git a/coderd/notifications/notifier.go b/coderd/notifications/notifier.go index a5d984a56fb0b..abf229ab08090 100644 --- a/coderd/notifications/notifier.go +++ b/coderd/notifications/notifier.go @@ -2,9 +2,7 @@ package notifications import ( "context" - "database/sql" "encoding/json" - "errors" "sync" "text/template" @@ -230,25 +228,18 @@ func (n *notifier) prepare(ctx context.Context, msg database.AcquireNotification return nil, xerrors.Errorf("failed to resolve handler %q", msg.Method) } - appName, err := n.store.GetApplicationName(ctx) - if err != nil { - if !errors.Is(err, sql.ErrNoRows) { - return nil, xerrors.Errorf("get application name: %w", err) - } - appName = notificationsDefaultAppName + helpers := make(template.FuncMap) + for k, v := range n.helpers { + helpers[k] = v } - logoURL, err := n.store.GetLogoURL(ctx) + appName, err := n.FetchAppName(ctx) if err != nil { - if !errors.Is(err, sql.ErrNoRows) { - return nil, xerrors.Errorf("get logo URL: %w", err) - } - logoURL = notificationsDefaultLogoURL + return nil, xerrors.Errorf("fetch app name: %w", err) } - - helpers := make(template.FuncMap) - for k, v := range n.helpers { - helpers[k] = v + logoURL, err := n.FetchLogoURL(ctx) + if err != nil { + return nil, xerrors.Errorf("fetch logo URL: %w", err) } helpers["app_name"] = func() string { return appName } diff --git a/coderd/notifications/utils_test.go b/coderd/notifications/utils_test.go index 9cf872ced1138..124b8554c51fb 100644 --- a/coderd/notifications/utils_test.go +++ b/coderd/notifications/utils_test.go @@ -39,8 +39,6 @@ func defaultHelpers() map[string]any { return map[string]any{ "base_url": func() string { return "http://test.com" }, "current_year": func() string { return "2024" }, - "logo_url": func() string { return "https://coder.com/coder-logo-horizontal.png" }, - "app_name": func() string { return "Coder" }, } } From 34d661187959b1cc13aef5d92cda88206b047da4 Mon Sep 17 00:00:00 2001 From: defelmnq Date: Thu, 17 Oct 2024 14:29:06 +0000 Subject: [PATCH 14/26] feat(notifications): change helpers logic to be defined in the Dispatcher function --- coderd/notifications/dispatch/smtp.go | 12 +- coderd/notifications/dispatch/smtp_test.go | 10 +- coderd/notifications/dispatch/utils_test.go | 10 + coderd/notifications/dispatch/webhook.go | 3 +- coderd/notifications/dispatch/webhook_test.go | 2 +- coderd/notifications/fetcher.go | 4 +- coderd/notifications/manager.go | 6 +- coderd/notifications/manager_test.go | 3 +- coderd/notifications/metrics_test.go | 5 +- coderd/notifications/notifications_test.go | 225 +++++++++++++++++- coderd/notifications/notifier.go | 10 +- coderd/notifications/spec.go | 3 +- .../smtp/TemplateTemplateDeleted.html.golden | 8 +- .../TemplateUserAccountActivated.html.golden | 8 +- .../TemplateUserAccountCreated.html.golden | 8 +- .../TemplateUserAccountDeleted.html.golden | 8 +- .../TemplateUserAccountSuspended.html.golden | 8 +- ...teUserRequestedOneTimePasscode.html.golden | 8 +- .../TemplateWorkspaceAutoUpdated.html.golden | 8 +- ...mplateWorkspaceAutobuildFailed.html.golden | 8 +- ...ateWorkspaceBuildsFailedReport.html.golden | 8 +- .../smtp/TemplateWorkspaceDeleted.html.golden | 8 +- .../smtp/TemplateWorkspaceDormant.html.golden | 8 +- ...lateWorkspaceManualBuildFailed.html.golden | 8 +- ...lateWorkspaceMarkedForDeletion.html.golden | 8 +- .../TemplateYourAccountActivated.html.golden | 8 +- .../TemplateYourAccountSuspended.html.golden | 8 +- coderd/notifications/utils_test.go | 7 +- 28 files changed, 295 insertions(+), 125 deletions(-) create mode 100644 coderd/notifications/dispatch/utils_test.go diff --git a/coderd/notifications/dispatch/smtp.go b/coderd/notifications/dispatch/smtp.go index b03108e95cc72..14bba5a5746a1 100644 --- a/coderd/notifications/dispatch/smtp.go +++ b/coderd/notifications/dispatch/smtp.go @@ -55,15 +55,13 @@ type SMTPHandler struct { noAuthWarnOnce sync.Once loginWarnOnce sync.Once - - helpers template.FuncMap } -func NewSMTPHandler(cfg codersdk.NotificationsEmailConfig, helpers template.FuncMap, log slog.Logger) *SMTPHandler { - return &SMTPHandler{cfg: cfg, helpers: helpers, log: log} +func NewSMTPHandler(cfg codersdk.NotificationsEmailConfig, log slog.Logger) *SMTPHandler { + return &SMTPHandler{cfg: cfg, log: log} } -func (s *SMTPHandler) Dispatcher(payload types.MessagePayload, titleTmpl, bodyTmpl string) (DeliveryFunc, error) { +func (s *SMTPHandler) Dispatcher(helpers template.FuncMap, payload types.MessagePayload, titleTmpl, bodyTmpl string) (DeliveryFunc, error) { // First render the subject & body into their own discrete strings. subject, err := markdown.PlaintextFromMarkdown(titleTmpl) if err != nil { @@ -79,12 +77,12 @@ func (s *SMTPHandler) Dispatcher(payload types.MessagePayload, titleTmpl, bodyTm // Then, reuse these strings in the HTML & plain body templates. payload.Labels["_subject"] = subject payload.Labels["_body"] = htmlBody - htmlBody, err = render.GoTemplate(htmlTemplate, payload, s.helpers) + htmlBody, err = render.GoTemplate(htmlTemplate, payload, helpers) if err != nil { return nil, xerrors.Errorf("render full html template: %w", err) } payload.Labels["_body"] = plainBody - plainBody, err = render.GoTemplate(plainTemplate, payload, s.helpers) + plainBody, err = render.GoTemplate(plainTemplate, payload, helpers) if err != nil { return nil, xerrors.Errorf("render full plaintext template: %w", err) } diff --git a/coderd/notifications/dispatch/smtp_test.go b/coderd/notifications/dispatch/smtp_test.go index 87743a79ba7ae..f3fc2b67bbd1f 100644 --- a/coderd/notifications/dispatch/smtp_test.go +++ b/coderd/notifications/dispatch/smtp_test.go @@ -442,13 +442,7 @@ func TestSMTP(t *testing.T) { require.NoError(t, hp.Set(listen.Addr().String())) tc.cfg.Smarthost = hp - helpers := map[string]any{ - "base_url": func() string { return "http://test.com" }, - "current_year": func() string { return "2024" }, - "logo_url": func() string { return "https://coder.com/coder-logo-horizontal.png" }, - "app_name": func() string { return "Coder" }, - } - handler := dispatch.NewSMTPHandler(tc.cfg, helpers, logger.Named("smtp")) + handler := dispatch.NewSMTPHandler(tc.cfg, logger.Named("smtp")) // Start mock SMTP server in the background. var wg sync.WaitGroup @@ -486,7 +480,7 @@ func TestSMTP(t *testing.T) { Labels: make(map[string]string), } - dispatchFn, err := handler.Dispatcher(payload, subject, body) + dispatchFn, err := handler.Dispatcher(helpers, payload, subject, body) require.NoError(t, err) msgID := uuid.New() diff --git a/coderd/notifications/dispatch/utils_test.go b/coderd/notifications/dispatch/utils_test.go new file mode 100644 index 0000000000000..483402ecf196f --- /dev/null +++ b/coderd/notifications/dispatch/utils_test.go @@ -0,0 +1,10 @@ +package dispatch_test + +var ( + helpers = map[string]any{ + "base_url": func() string { return "http://test.com" }, + "current_year": func() string { return "2024" }, + "logo_url": func() string { return "https://coder.com/coder-logo-horizontal.png" }, + "app_name": func() string { return "Coder" }, + } +) diff --git a/coderd/notifications/dispatch/webhook.go b/coderd/notifications/dispatch/webhook.go index fcad3a7b0eae2..119a7119d0447 100644 --- a/coderd/notifications/dispatch/webhook.go +++ b/coderd/notifications/dispatch/webhook.go @@ -7,6 +7,7 @@ import ( "errors" "io" "net/http" + "text/template" "github.com/google/uuid" "golang.org/x/xerrors" @@ -41,7 +42,7 @@ func NewWebhookHandler(cfg codersdk.NotificationsWebhookConfig, log slog.Logger) return &WebhookHandler{cfg: cfg, log: log, cl: &http.Client{}} } -func (w *WebhookHandler) Dispatcher(payload types.MessagePayload, titleMarkdown, bodyMarkdown string) (DeliveryFunc, error) { +func (w *WebhookHandler) Dispatcher(helpers template.FuncMap, payload types.MessagePayload, titleMarkdown, bodyMarkdown string) (DeliveryFunc, error) { if w.cfg.Endpoint.String() == "" { return nil, xerrors.New("webhook endpoint not defined") } diff --git a/coderd/notifications/dispatch/webhook_test.go b/coderd/notifications/dispatch/webhook_test.go index 26a78752cfd45..37966f418025c 100644 --- a/coderd/notifications/dispatch/webhook_test.go +++ b/coderd/notifications/dispatch/webhook_test.go @@ -141,7 +141,7 @@ func TestWebhook(t *testing.T) { Endpoint: *serpent.URLOf(endpoint), } handler := dispatch.NewWebhookHandler(cfg, logger.With(slog.F("test", tc.name))) - deliveryFn, err := handler.Dispatcher(msgPayload, titleMarkdown, bodyMarkdown) + deliveryFn, err := handler.Dispatcher(helpers, msgPayload, titleMarkdown, bodyMarkdown) require.NoError(t, err) retryable, err := deliveryFn(ctx, msgID) diff --git a/coderd/notifications/fetcher.go b/coderd/notifications/fetcher.go index 10f31be7dba66..73d5cfcc607bd 100644 --- a/coderd/notifications/fetcher.go +++ b/coderd/notifications/fetcher.go @@ -8,7 +8,7 @@ import ( "golang.org/x/xerrors" ) -func (n *notifier) FetchAppName(ctx context.Context) (string, error) { +func (n *notifier) fetchAppName(ctx context.Context) (string, error) { appName, err := n.store.GetApplicationName(ctx) if err != nil { if errors.Is(err, sql.ErrNoRows) { @@ -19,7 +19,7 @@ func (n *notifier) FetchAppName(ctx context.Context) (string, error) { return appName, nil } -func (n *notifier) FetchLogoURL(ctx context.Context) (string, error) { +func (n *notifier) fetchLogoURL(ctx context.Context) (string, error) { logoURL, err := n.store.GetLogoURL(ctx) if err != nil { if errors.Is(err, sql.ErrNoRows) { diff --git a/coderd/notifications/manager.go b/coderd/notifications/manager.go index 8b765bbe88c33..97c56f2accb6c 100644 --- a/coderd/notifications/manager.go +++ b/coderd/notifications/manager.go @@ -109,7 +109,7 @@ func NewManager(cfg codersdk.NotificationsConfig, store Store, helpers template. stop: make(chan any), done: make(chan any), - handlers: defaultHandlers(cfg, helpers, log), + handlers: defaultHandlers(cfg, log), helpers: helpers, clock: quartz.NewReal(), @@ -121,9 +121,9 @@ func NewManager(cfg codersdk.NotificationsConfig, store Store, helpers template. } // defaultHandlers builds a set of known handlers; panics if any error occurs as these handlers should be valid at compile time. -func defaultHandlers(cfg codersdk.NotificationsConfig, helpers template.FuncMap, log slog.Logger) map[database.NotificationMethod]Handler { +func defaultHandlers(cfg codersdk.NotificationsConfig, log slog.Logger) map[database.NotificationMethod]Handler { return map[database.NotificationMethod]Handler{ - database.NotificationMethodSmtp: dispatch.NewSMTPHandler(cfg.SMTP, helpers, log.Named("dispatcher.smtp")), + database.NotificationMethodSmtp: dispatch.NewSMTPHandler(cfg.SMTP, log.Named("dispatcher.smtp")), database.NotificationMethodWebhook: dispatch.NewWebhookHandler(cfg.Webhook, log.Named("dispatcher.webhook")), } } diff --git a/coderd/notifications/manager_test.go b/coderd/notifications/manager_test.go index ddbdb0b518d90..60b8dee9a6cc8 100644 --- a/coderd/notifications/manager_test.go +++ b/coderd/notifications/manager_test.go @@ -5,6 +5,7 @@ import ( "encoding/json" "sync/atomic" "testing" + "text/template" "time" "github.com/google/uuid" @@ -205,7 +206,7 @@ type santaHandler struct { nice atomic.Int32 } -func (s *santaHandler) Dispatcher(payload types.MessagePayload, _, _ string) (dispatch.DeliveryFunc, error) { +func (s *santaHandler) Dispatcher(helpers template.FuncMap, payload types.MessagePayload, _, _ string) (dispatch.DeliveryFunc, error) { return func(ctx context.Context, msgID uuid.UUID) (retryable bool, err error) { if payload.Labels["nice"] != "true" { s.naughty.Add(1) diff --git a/coderd/notifications/metrics_test.go b/coderd/notifications/metrics_test.go index 294eccc31c891..7b6c0ca1d8a11 100644 --- a/coderd/notifications/metrics_test.go +++ b/coderd/notifications/metrics_test.go @@ -5,6 +5,7 @@ import ( "strconv" "sync" "testing" + "text/template" "time" "github.com/google/uuid" @@ -515,8 +516,8 @@ func newBarrierHandler(total int, handler notifications.Handler) *barrierHandler } } -func (bh *barrierHandler) Dispatcher(payload types.MessagePayload, title, body string) (dispatch.DeliveryFunc, error) { - deliverFn, err := bh.h.Dispatcher(payload, title, body) +func (bh *barrierHandler) Dispatcher(helpers template.FuncMap, payload types.MessagePayload, title, body string) (dispatch.DeliveryFunc, error) { + deliverFn, err := bh.h.Dispatcher(helpers, payload, title, body) if err != nil { return nil, err } diff --git a/coderd/notifications/notifications_test.go b/coderd/notifications/notifications_test.go index 61862826acca1..1387c77ba18cf 100644 --- a/coderd/notifications/notifications_test.go +++ b/coderd/notifications/notifications_test.go @@ -23,6 +23,7 @@ import ( "sync" "sync/atomic" "testing" + "text/template" "time" "github.com/emersion/go-sasl" @@ -155,7 +156,7 @@ func TestSMTPDispatch(t *testing.T) { Smarthost: serpent.HostPort{Host: "localhost", Port: fmt.Sprintf("%d", mockSMTPSrv.PortNumber())}, Hello: "localhost", } - handler := newDispatchInterceptor(dispatch.NewSMTPHandler(cfg.SMTP, defaultHelpers(), api.Logger.Named("smtp"))) + handler := newDispatchInterceptor(dispatch.NewSMTPHandler(cfg.SMTP, api.Logger.Named("smtp"))) mgr, err := notifications.NewManager(cfg, api.Database, defaultHelpers(), createMetrics(), api.Logger.Named("manager")) require.NoError(t, err) mgr.WithHandlers(map[database.NotificationMethod]notifications.Handler{method: handler}) @@ -1561,7 +1562,7 @@ type fakeHandler struct { succeeded, failed []string } -func (f *fakeHandler) Dispatcher(payload types.MessagePayload, _, _ string) (dispatch.DeliveryFunc, error) { +func (f *fakeHandler) Dispatcher(helpers template.FuncMap, payload types.MessagePayload, _, _ string) (dispatch.DeliveryFunc, error) { return func(_ context.Context, msgID uuid.UUID) (retryable bool, err error) { f.mu.Lock() defer f.mu.Unlock() @@ -1610,3 +1611,223 @@ func (n *acquireSignalingInterceptor) AcquireNotificationMessages(ctx context.Co n.acquiredChan <- struct{}{} return messages, err } + +func TestNotificationTemplates_GoldenWithCustomLogoURL(t *testing.T) { + t.Parallel() + + if !dbtestutil.WillUsePostgres() { + t.Skip("This test requires postgres; it relies on the notification templates added by migrations in the database") + } + + const ( + username = "bob" + password = "🤫" + + hello = "localhost" + + from = "system@coder.com" + hint = "run \"DB=ci make update-golden-files\" and commit the changes" + ) + + tests := []struct { + name string + id uuid.UUID + payload types.MessagePayload + }{ + { + name: "TemplateWorkspaceDeleted", + id: notifications.TemplateWorkspaceDeleted, + payload: types.MessagePayload{ + UserName: "Bobby", + UserEmail: "bobby@coder.com", + UserUsername: "bobby", + Labels: map[string]string{ + "name": "bobby-workspace", + "reason": "autodeleted due to dormancy", + "initiator": "autobuild", + }, + }, + }, + } + + // We must have a test case for every notification_template. This is enforced below: + allTemplates, err := enumerateAllTemplates(t) + require.NoError(t, err) + for _, name := range allTemplates { + var found bool + for _, tc := range tests { + if tc.name == name { + found = true + } + } + + require.Truef(t, found, "could not find test case for %q", name) + } + + for _, tc := range tests { + tc := tc + + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + t.Run("smtp", func(t *testing.T) { + t.Parallel() + + // Spin up the DB + db, logger, user := func() (*database.Store, *slog.Logger, *codersdk.User) { + adminClient, _, api := coderdtest.NewWithAPI(t, nil) + db := api.Database + db.UpsertApplicationName(context.Background(), "myNewValue") + firstUser := coderdtest.CreateFirstUser(t, adminClient) + + _, user := coderdtest.CreateAnotherUserMutators( + t, + adminClient, + firstUser.OrganizationID, + []rbac.RoleIdentifier{rbac.RoleUserAdmin()}, + func(r *codersdk.CreateUserRequestWithOrgs) { + r.Username = tc.payload.UserUsername + r.Email = tc.payload.UserEmail + r.Name = tc.payload.UserName + }, + ) + return &db, &api.Logger, &user + }() + + // nolint:gocritic // Unit test. + ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong)) + + ddb := *db + err := ddb.UpsertLogoURL(ctx, "newURL") + require.NoError(t, err) + + // smtp config shared between client and server + smtpConfig := codersdk.NotificationsEmailConfig{ + Hello: hello, + From: from, + + Auth: codersdk.NotificationsEmailAuthConfig{ + Username: username, + Password: password, + }, + } + + // Spin up the mock SMTP server + backend := smtptest.NewBackend(smtptest.Config{ + AuthMechanisms: []string{sasl.Login}, + + AcceptedIdentity: smtpConfig.Auth.Identity.String(), + AcceptedUsername: username, + AcceptedPassword: password, + }) + + // Create a mock SMTP server which conditionally listens for plain or TLS connections. + srv, listen, err := smtptest.CreateMockSMTPServer(backend, false) + require.NoError(t, err) + t.Cleanup(func() { + err := srv.Shutdown(ctx) + require.NoError(t, err) + }) + + var hp serpent.HostPort + require.NoError(t, hp.Set(listen.Addr().String())) + smtpConfig.Smarthost = hp + + // Start mock SMTP server in the background. + var wg sync.WaitGroup + wg.Add(1) + go func() { + defer wg.Done() + assert.NoError(t, srv.Serve(listen)) + }() + + // Wait for the server to become pingable. + require.Eventually(t, func() bool { + cl, err := smtptest.PingClient(listen, false, smtpConfig.TLS.StartTLS.Value()) + if err != nil { + t.Logf("smtp not yet dialable: %s", err) + return false + } + + if err = cl.Noop(); err != nil { + t.Logf("smtp not yet noopable: %s", err) + return false + } + + if err = cl.Close(); err != nil { + t.Logf("smtp didn't close properly: %s", err) + return false + } + + return true + }, testutil.WaitShort, testutil.IntervalFast) + + smtpCfg := defaultNotificationsConfig(database.NotificationMethodSmtp) + smtpCfg.SMTP = smtpConfig + + smtpManager, err := notifications.NewManager( + smtpCfg, + *db, + defaultHelpers(), + createMetrics(), + logger.Named("manager"), + ) + require.NoError(t, err) + + smtpManager.Run(ctx) + + notificationCfg := defaultNotificationsConfig(database.NotificationMethodSmtp) + + smtpEnqueuer, err := notifications.NewStoreEnqueuer( + notificationCfg, + *db, + defaultHelpers(), + logger.Named("enqueuer"), + quartz.NewReal(), + ) + require.NoError(t, err) + + _, err = smtpEnqueuer.EnqueueWithData( + ctx, + user.ID, + tc.id, + tc.payload.Labels, + tc.payload.Data, + user.Username, + user.ID, + ) + require.NoError(t, err) + + // Wait for the message to be fetched + var msg *smtptest.Message + require.Eventually(t, func() bool { + msg = backend.LastMessage() + return msg != nil && len(msg.Contents) > 0 + }, testutil.WaitShort, testutil.IntervalFast) + + body := normalizeGoldenEmail([]byte(msg.Contents)) + + err = smtpManager.Stop(ctx) + require.NoError(t, err) + + partialName := strings.Split(t.Name(), "/")[1] + goldenFile := filepath.Join("testdata", "rendered-templates", "smtp", partialName+".html.golden") + if *updateGoldenFiles { + err = os.MkdirAll(filepath.Dir(goldenFile), 0o755) + require.NoError(t, err, "want no error creating golden file directory") + err = os.WriteFile(goldenFile, body, 0o600) + require.NoError(t, err, "want no error writing body golden file") + return + } + + wantBody, err := os.ReadFile(goldenFile) + require.NoError(t, err, fmt.Sprintf("missing golden notification body file. %s", hint)) + require.Empty( + t, + cmp.Diff(wantBody, body), + fmt.Sprintf("golden file mismatch: %s. If this is expected, %s. (-want +got). ", goldenFile, hint), + ) + }) + }) + } +} diff --git a/coderd/notifications/notifier.go b/coderd/notifications/notifier.go index abf229ab08090..f2f34c75efdf6 100644 --- a/coderd/notifications/notifier.go +++ b/coderd/notifications/notifier.go @@ -233,11 +233,11 @@ func (n *notifier) prepare(ctx context.Context, msg database.AcquireNotification helpers[k] = v } - appName, err := n.FetchAppName(ctx) + appName, err := n.fetchAppName(ctx) if err != nil { return nil, xerrors.Errorf("fetch app name: %w", err) } - logoURL, err := n.FetchLogoURL(ctx) + logoURL, err := n.fetchLogoURL(ctx) if err != nil { return nil, xerrors.Errorf("fetch logo URL: %w", err) } @@ -246,14 +246,14 @@ func (n *notifier) prepare(ctx context.Context, msg database.AcquireNotification helpers["logo_url"] = func() string { return logoURL } var title, body string - if title, err = render.GoTemplate(msg.TitleTemplate, payload, n.helpers); err != nil { + if title, err = render.GoTemplate(msg.TitleTemplate, payload, helpers); err != nil { return nil, xerrors.Errorf("render title: %w", err) } - if body, err = render.GoTemplate(msg.BodyTemplate, payload, n.helpers); err != nil { + if body, err = render.GoTemplate(msg.BodyTemplate, payload, helpers); err != nil { return nil, xerrors.Errorf("render body: %w", err) } - return handler.Dispatcher(payload, title, body) + return handler.Dispatcher(helpers, payload, title, body) } // deliver sends a given notification message via its defined method. diff --git a/coderd/notifications/spec.go b/coderd/notifications/spec.go index e81d13d24d572..f3f7d1aa5b64f 100644 --- a/coderd/notifications/spec.go +++ b/coderd/notifications/spec.go @@ -2,6 +2,7 @@ package notifications import ( "context" + "text/template" "github.com/google/uuid" @@ -29,7 +30,7 @@ type Store interface { // Handler is responsible for preparing and delivering a notification by a given method. type Handler interface { // Dispatcher constructs a DeliveryFunc to be used for delivering a notification via the chosen method. - Dispatcher(payload types.MessagePayload, title, body string) (dispatch.DeliveryFunc, error) + Dispatcher(helpers template.FuncMap, payload types.MessagePayload, title, body string) (dispatch.DeliveryFunc, error) } // Enqueuer enqueues a new notification message in the store and returns its ID, should it enqueue without failure. diff --git a/coderd/notifications/testdata/rendered-templates/smtp/TemplateTemplateDeleted.html.golden b/coderd/notifications/testdata/rendered-templates/smtp/TemplateTemplateDeleted.html.golden index 26b1cf35e9b9c..2ae9ac8e61db5 100644 --- a/coderd/notifications/testdata/rendered-templates/smtp/TemplateTemplateDeleted.html.golden +++ b/coderd/notifications/testdata/rendered-templates/smtp/TemplateTemplateDeleted.html.golden @@ -37,12 +37,8 @@ l', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; color: #020617= r: 1px solid #e2e8f0; border-radius: 8px; background-color: #fff; text-alig= n: left; font-size: 14px; line-height: 1.5;">
- 3D"Log= -

- Coder -

+ 3D"Cod=

diff --git a/coderd/notifications/testdata/rendered-templates/smtp/TemplateUserAccountActivated.html.golden b/coderd/notifications/testdata/rendered-templates/smtp/TemplateUserAccountActivated.html.golden index 382a74dcca0cd..81cbe7f54fd0e 100644 --- a/coderd/notifications/testdata/rendered-templates/smtp/TemplateUserAccountActivated.html.golden +++ b/coderd/notifications/testdata/rendered-templates/smtp/TemplateUserAccountActivated.html.golden @@ -40,12 +40,8 @@ l', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; color: #020617= r: 1px solid #e2e8f0; border-radius: 8px; background-color: #fff; text-alig= n: left; font-size: 14px; line-height: 1.5;">
- 3D"Log= -

- Coder -

+ 3D"Cod=

diff --git a/coderd/notifications/testdata/rendered-templates/smtp/TemplateUserAccountCreated.html.golden b/coderd/notifications/testdata/rendered-templates/smtp/TemplateUserAccountCreated.html.golden index 8ec582093d00b..9a6cab0989897 100644 --- a/coderd/notifications/testdata/rendered-templates/smtp/TemplateUserAccountCreated.html.golden +++ b/coderd/notifications/testdata/rendered-templates/smtp/TemplateUserAccountCreated.html.golden @@ -39,12 +39,8 @@ l', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; color: #020617= r: 1px solid #e2e8f0; border-radius: 8px; background-color: #fff; text-alig= n: left; font-size: 14px; line-height: 1.5;">
- 3D"Log= -

- Coder -

+ 3D"Cod=

diff --git a/coderd/notifications/testdata/rendered-templates/smtp/TemplateUserAccountDeleted.html.golden b/coderd/notifications/testdata/rendered-templates/smtp/TemplateUserAccountDeleted.html.golden index 464a9b3fb69c3..c7daad54f028b 100644 --- a/coderd/notifications/testdata/rendered-templates/smtp/TemplateUserAccountDeleted.html.golden +++ b/coderd/notifications/testdata/rendered-templates/smtp/TemplateUserAccountDeleted.html.golden @@ -39,12 +39,8 @@ l', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; color: #020617= r: 1px solid #e2e8f0; border-radius: 8px; background-color: #fff; text-alig= n: left; font-size: 14px; line-height: 1.5;">
- 3D"Log= -

- Coder -

+ 3D"Cod=

diff --git a/coderd/notifications/testdata/rendered-templates/smtp/TemplateUserAccountSuspended.html.golden b/coderd/notifications/testdata/rendered-templates/smtp/TemplateUserAccountSuspended.html.golden index 0045e7d7f10f2..ccd40593ef5fb 100644 --- a/coderd/notifications/testdata/rendered-templates/smtp/TemplateUserAccountSuspended.html.golden +++ b/coderd/notifications/testdata/rendered-templates/smtp/TemplateUserAccountSuspended.html.golden @@ -41,12 +41,8 @@ l', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; color: #020617= r: 1px solid #e2e8f0; border-radius: 8px; background-color: #fff; text-alig= n: left; font-size: 14px; line-height: 1.5;">
- 3D"Log= -

- Coder -

+ 3D"Cod=

diff --git a/coderd/notifications/testdata/rendered-templates/smtp/TemplateUserRequestedOneTimePasscode.html.golden b/coderd/notifications/testdata/rendered-templates/smtp/TemplateUserRequestedOneTimePasscode.html.golden index d9d7936e996a7..2b61765813bcf 100644 --- a/coderd/notifications/testdata/rendered-templates/smtp/TemplateUserRequestedOneTimePasscode.html.golden +++ b/coderd/notifications/testdata/rendered-templates/smtp/TemplateUserRequestedOneTimePasscode.html.golden @@ -40,12 +40,8 @@ l', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; color: #020617= r: 1px solid #e2e8f0; border-radius: 8px; background-color: #fff; text-alig= n: left; font-size: 14px; line-height: 1.5;">
- 3D"Log= -

- Coder -

+ 3D"Cod=

diff --git a/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceAutoUpdated.html.golden b/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceAutoUpdated.html.golden index f6b908814d878..6c68cffa8bc1b 100644 --- a/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceAutoUpdated.html.golden +++ b/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceAutoUpdated.html.golden @@ -40,12 +40,8 @@ l', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; color: #020617= r: 1px solid #e2e8f0; border-radius: 8px; background-color: #fff; text-alig= n: left; font-size: 14px; line-height: 1.5;">
- 3D"Log= -

- Coder -

+ 3D"Cod=

diff --git a/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceAutobuildFailed.html.golden b/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceAutobuildFailed.html.golden index 14fa316cbc71b..340e794f15c74 100644 --- a/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceAutobuildFailed.html.golden +++ b/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceAutobuildFailed.html.golden @@ -39,12 +39,8 @@ l', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; color: #020617= r: 1px solid #e2e8f0; border-radius: 8px; background-color: #fff; text-alig= n: left; font-size: 14px; line-height: 1.5;">
- 3D"Log= -

- Coder -

+ 3D"Cod=

diff --git a/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceBuildsFailedReport.html.golden b/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceBuildsFailedReport.html.golden index e83ee9bddac51..7cc16f00f3796 100644 --- a/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceBuildsFailedReport.html.golden +++ b/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceBuildsFailedReport.html.golden @@ -57,12 +57,8 @@ l', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; color: #020617= r: 1px solid #e2e8f0; border-radius: 8px; background-color: #fff; text-alig= n: left; font-size: 14px; line-height: 1.5;">
- 3D"Log= -

- Coder -

+ 3D"Cod=

diff --git a/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceDeleted.html.golden b/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceDeleted.html.golden index 6b715a6e485e1..0d821bdc4dacd 100644 --- a/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceDeleted.html.golden +++ b/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceDeleted.html.golden @@ -41,12 +41,8 @@ l', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; color: #020617= r: 1px solid #e2e8f0; border-radius: 8px; background-color: #fff; text-alig= n: left; font-size: 14px; line-height: 1.5;">
- 3D"Log= -

- Coder -

+ 3D"Cod=

diff --git a/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceDormant.html.golden b/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceDormant.html.golden index 10b39588d19bf..0c6cbf5a2dd85 100644 --- a/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceDormant.html.golden +++ b/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceDormant.html.golden @@ -43,12 +43,8 @@ l', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; color: #020617= r: 1px solid #e2e8f0; border-radius: 8px; background-color: #fff; text-alig= n: left; font-size: 14px; line-height: 1.5;">
- 3D"Log= -

- Coder -

+ 3D"Cod=

diff --git a/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceManualBuildFailed.html.golden b/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceManualBuildFailed.html.golden index 5457fefd88d63..1f456a72f4df4 100644 --- a/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceManualBuildFailed.html.golden +++ b/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceManualBuildFailed.html.golden @@ -40,12 +40,8 @@ l', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; color: #020617= r: 1px solid #e2e8f0; border-radius: 8px; background-color: #fff; text-alig= n: left; font-size: 14px; line-height: 1.5;">
- 3D"Log= -

- Coder -

+ 3D"Cod=

diff --git a/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceMarkedForDeletion.html.golden b/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceMarkedForDeletion.html.golden index 152faca3d29b0..6d91458f2cbcc 100644 --- a/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceMarkedForDeletion.html.golden +++ b/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceMarkedForDeletion.html.golden @@ -40,12 +40,8 @@ l', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; color: #020617= r: 1px solid #e2e8f0; border-radius: 8px; background-color: #fff; text-alig= n: left; font-size: 14px; line-height: 1.5;">
- 3D"Log= -

- Coder -

+ 3D"Cod=

diff --git a/coderd/notifications/testdata/rendered-templates/smtp/TemplateYourAccountActivated.html.golden b/coderd/notifications/testdata/rendered-templates/smtp/TemplateYourAccountActivated.html.golden index 6168e369bfd50..aef12ab957feb 100644 --- a/coderd/notifications/testdata/rendered-templates/smtp/TemplateYourAccountActivated.html.golden +++ b/coderd/notifications/testdata/rendered-templates/smtp/TemplateYourAccountActivated.html.golden @@ -37,12 +37,8 @@ l', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; color: #020617= r: 1px solid #e2e8f0; border-radius: 8px; background-color: #fff; text-alig= n: left; font-size: 14px; line-height: 1.5;">
- 3D"Log= -

- Coder -

+ 3D"Cod=

diff --git a/coderd/notifications/testdata/rendered-templates/smtp/TemplateYourAccountSuspended.html.golden b/coderd/notifications/testdata/rendered-templates/smtp/TemplateYourAccountSuspended.html.golden index a15ec36ea2060..d9406e2c1f344 100644 --- a/coderd/notifications/testdata/rendered-templates/smtp/TemplateYourAccountSuspended.html.golden +++ b/coderd/notifications/testdata/rendered-templates/smtp/TemplateYourAccountSuspended.html.golden @@ -35,12 +35,8 @@ l', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; color: #020617= r: 1px solid #e2e8f0; border-radius: 8px; background-color: #fff; text-alig= n: left; font-size: 14px; line-height: 1.5;">
- 3D"Log= -

- Coder -

+ 3D"Cod=

diff --git a/coderd/notifications/utils_test.go b/coderd/notifications/utils_test.go index 124b8554c51fb..30934bf3d6343 100644 --- a/coderd/notifications/utils_test.go +++ b/coderd/notifications/utils_test.go @@ -4,6 +4,7 @@ import ( "context" "sync/atomic" "testing" + "text/template" "time" "github.com/google/uuid" @@ -39,6 +40,8 @@ func defaultHelpers() map[string]any { return map[string]any{ "base_url": func() string { return "http://test.com" }, "current_year": func() string { return "2024" }, + "logo_url": func() string { return "https://coder.com/coder-logo-horizontal.png" }, + "app_name": func() string { return "Coder" }, } } @@ -67,9 +70,9 @@ func newDispatchInterceptor(h notifications.Handler) *dispatchInterceptor { return &dispatchInterceptor{handler: h} } -func (i *dispatchInterceptor) Dispatcher(payload types.MessagePayload, title, body string) (dispatch.DeliveryFunc, error) { +func (i *dispatchInterceptor) Dispatcher(helpers template.FuncMap, payload types.MessagePayload, title, body string) (dispatch.DeliveryFunc, error) { return func(ctx context.Context, msgID uuid.UUID) (retryable bool, err error) { - deliveryFn, err := i.handler.Dispatcher(payload, title, body) + deliveryFn, err := i.handler.Dispatcher(defaultHelpers(), payload, title, body) if err != nil { return false, err } From 0a9a66a23e7db2201bdc220d1f4edc4b844cd8bd Mon Sep 17 00:00:00 2001 From: defelmnq Date: Thu, 17 Oct 2024 23:16:44 +0000 Subject: [PATCH 15/26] feat(notifications): add golden file for custom appearance --- coderd/notifications/notifications_test.go | 308 ++++++++---------- ...ceDeleted_WithCustomAppearance.html.golden | 90 +++++ 2 files changed, 225 insertions(+), 173 deletions(-) create mode 100644 coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceDeleted_WithCustomAppearance.html.golden diff --git a/coderd/notifications/notifications_test.go b/coderd/notifications/notifications_test.go index 1387c77ba18cf..6d5068f0e0613 100644 --- a/coderd/notifications/notifications_test.go +++ b/coderd/notifications/notifications_test.go @@ -1612,7 +1612,7 @@ func (n *acquireSignalingInterceptor) AcquireNotificationMessages(ctx context.Co return messages, err } -func TestNotificationTemplates_GoldenWithCustomLogoURL(t *testing.T) { +func TestNotificationTemplates_GoldenWithCustomAppearance(t *testing.T) { t.Parallel() if !dbtestutil.WillUsePostgres() { @@ -1629,205 +1629,167 @@ func TestNotificationTemplates_GoldenWithCustomLogoURL(t *testing.T) { hint = "run \"DB=ci make update-golden-files\" and commit the changes" ) - tests := []struct { - name string - id uuid.UUID - payload types.MessagePayload - }{ - { - name: "TemplateWorkspaceDeleted", - id: notifications.TemplateWorkspaceDeleted, - payload: types.MessagePayload{ - UserName: "Bobby", - UserEmail: "bobby@coder.com", - UserUsername: "bobby", - Labels: map[string]string{ - "name": "bobby-workspace", - "reason": "autodeleted due to dormancy", - "initiator": "autobuild", - }, + var ( + payload = types.MessagePayload{ + Labels: map[string]string{ + "name": "bobby-workspace", + "reason": "autodeleted due to dormancy", + "initiator": "autobuild", }, - }, - } - - // We must have a test case for every notification_template. This is enforced below: - allTemplates, err := enumerateAllTemplates(t) - require.NoError(t, err) - for _, name := range allTemplates { - var found bool - for _, tc := range tests { - if tc.name == name { - found = true - } } + ) - require.Truef(t, found, "could not find test case for %q", name) - } - - for _, tc := range tests { - tc := tc - - t.Run(tc.name, func(t *testing.T) { - t.Parallel() - - t.Run("smtp", func(t *testing.T) { - t.Parallel() - - // Spin up the DB - db, logger, user := func() (*database.Store, *slog.Logger, *codersdk.User) { - adminClient, _, api := coderdtest.NewWithAPI(t, nil) - db := api.Database - db.UpsertApplicationName(context.Background(), "myNewValue") - firstUser := coderdtest.CreateFirstUser(t, adminClient) + // Spin up the DB + db, logger, user := func() (database.Store, *slog.Logger, *codersdk.User) { + adminClient, _, api := coderdtest.NewWithAPI(t, nil) + firstUser := coderdtest.CreateFirstUser(t, adminClient) + + _, user := coderdtest.CreateAnotherUserMutators( + t, + adminClient, + firstUser.OrganizationID, + []rbac.RoleIdentifier{rbac.RoleUserAdmin()}, + func(r *codersdk.CreateUserRequestWithOrgs) { + r.Username = "bobby" + r.Email = "bobby@coder.com" + r.Name = "Bobby" + }, + ) + return api.Database, &api.Logger, &user + }() - _, user := coderdtest.CreateAnotherUserMutators( - t, - adminClient, - firstUser.OrganizationID, - []rbac.RoleIdentifier{rbac.RoleUserAdmin()}, - func(r *codersdk.CreateUserRequestWithOrgs) { - r.Username = tc.payload.UserUsername - r.Email = tc.payload.UserEmail - r.Name = tc.payload.UserName - }, - ) - return &db, &api.Logger, &user - }() + // nolint:gocritic // Unit test. + ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong)) - // nolint:gocritic // Unit test. - ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong)) + err := db.UpsertApplicationName(ctx, "CustomApplication") + require.NoError(t, err) - ddb := *db - err := ddb.UpsertLogoURL(ctx, "newURL") - require.NoError(t, err) + err = db.UpsertLogoURL(ctx, "https://custom.application") + require.NoError(t, err) - // smtp config shared between client and server - smtpConfig := codersdk.NotificationsEmailConfig{ - Hello: hello, - From: from, + // smtp config shared between client and server + smtpConfig := codersdk.NotificationsEmailConfig{ + Hello: hello, + From: from, - Auth: codersdk.NotificationsEmailAuthConfig{ - Username: username, - Password: password, - }, - } + Auth: codersdk.NotificationsEmailAuthConfig{ + Username: username, + Password: password, + }, + } - // Spin up the mock SMTP server - backend := smtptest.NewBackend(smtptest.Config{ - AuthMechanisms: []string{sasl.Login}, + // Spin up the mock SMTP server + backend := smtptest.NewBackend(smtptest.Config{ + AuthMechanisms: []string{sasl.Login}, - AcceptedIdentity: smtpConfig.Auth.Identity.String(), - AcceptedUsername: username, - AcceptedPassword: password, - }) + AcceptedIdentity: smtpConfig.Auth.Identity.String(), + AcceptedUsername: username, + AcceptedPassword: password, + }) - // Create a mock SMTP server which conditionally listens for plain or TLS connections. - srv, listen, err := smtptest.CreateMockSMTPServer(backend, false) - require.NoError(t, err) - t.Cleanup(func() { - err := srv.Shutdown(ctx) - require.NoError(t, err) - }) + // Create a mock SMTP server which conditionally listens for plain or TLS connections. + srv, listen, err := smtptest.CreateMockSMTPServer(backend, false) + require.NoError(t, err) + t.Cleanup(func() { + err := srv.Shutdown(ctx) + require.NoError(t, err) + }) - var hp serpent.HostPort - require.NoError(t, hp.Set(listen.Addr().String())) - smtpConfig.Smarthost = hp + var hp serpent.HostPort + require.NoError(t, hp.Set(listen.Addr().String())) + smtpConfig.Smarthost = hp - // Start mock SMTP server in the background. - var wg sync.WaitGroup - wg.Add(1) - go func() { - defer wg.Done() - assert.NoError(t, srv.Serve(listen)) - }() + // Start mock SMTP server in the background. + var wg sync.WaitGroup + wg.Add(1) + go func() { + defer wg.Done() + assert.NoError(t, srv.Serve(listen)) + }() - // Wait for the server to become pingable. - require.Eventually(t, func() bool { - cl, err := smtptest.PingClient(listen, false, smtpConfig.TLS.StartTLS.Value()) - if err != nil { - t.Logf("smtp not yet dialable: %s", err) - return false - } + // Wait for the server to become pingable. + require.Eventually(t, func() bool { + cl, err := smtptest.PingClient(listen, false, smtpConfig.TLS.StartTLS.Value()) + if err != nil { + t.Logf("smtp not yet dialable: %s", err) + return false + } - if err = cl.Noop(); err != nil { - t.Logf("smtp not yet noopable: %s", err) - return false - } + if err = cl.Noop(); err != nil { + t.Logf("smtp not yet noopable: %s", err) + return false + } - if err = cl.Close(); err != nil { - t.Logf("smtp didn't close properly: %s", err) - return false - } + if err = cl.Close(); err != nil { + t.Logf("smtp didn't close properly: %s", err) + return false + } - return true - }, testutil.WaitShort, testutil.IntervalFast) + return true + }, testutil.WaitShort, testutil.IntervalFast) - smtpCfg := defaultNotificationsConfig(database.NotificationMethodSmtp) - smtpCfg.SMTP = smtpConfig + smtpCfg := defaultNotificationsConfig(database.NotificationMethodSmtp) + smtpCfg.SMTP = smtpConfig - smtpManager, err := notifications.NewManager( - smtpCfg, - *db, - defaultHelpers(), - createMetrics(), - logger.Named("manager"), - ) - require.NoError(t, err) + smtpManager, err := notifications.NewManager( + smtpCfg, + db, + defaultHelpers(), + createMetrics(), + logger.Named("manager"), + ) + require.NoError(t, err) - smtpManager.Run(ctx) + smtpManager.Run(ctx) - notificationCfg := defaultNotificationsConfig(database.NotificationMethodSmtp) + notificationCfg := defaultNotificationsConfig(database.NotificationMethodSmtp) - smtpEnqueuer, err := notifications.NewStoreEnqueuer( - notificationCfg, - *db, - defaultHelpers(), - logger.Named("enqueuer"), - quartz.NewReal(), - ) - require.NoError(t, err) + smtpEnqueuer, err := notifications.NewStoreEnqueuer( + notificationCfg, + db, + defaultHelpers(), + logger.Named("enqueuer"), + quartz.NewReal(), + ) + require.NoError(t, err) - _, err = smtpEnqueuer.EnqueueWithData( - ctx, - user.ID, - tc.id, - tc.payload.Labels, - tc.payload.Data, - user.Username, - user.ID, - ) - require.NoError(t, err) + _, err = smtpEnqueuer.EnqueueWithData( + ctx, + user.ID, + notifications.TemplateWorkspaceDeleted, + payload.Labels, + payload.Data, + user.Username, + user.ID, + ) + require.NoError(t, err) - // Wait for the message to be fetched - var msg *smtptest.Message - require.Eventually(t, func() bool { - msg = backend.LastMessage() - return msg != nil && len(msg.Contents) > 0 - }, testutil.WaitShort, testutil.IntervalFast) + // Wait for the message to be fetched + var msg *smtptest.Message + require.Eventually(t, func() bool { + msg = backend.LastMessage() + return msg != nil && len(msg.Contents) > 0 + }, testutil.WaitShort, testutil.IntervalFast) - body := normalizeGoldenEmail([]byte(msg.Contents)) + body := normalizeGoldenEmail([]byte(msg.Contents)) - err = smtpManager.Stop(ctx) - require.NoError(t, err) + err = smtpManager.Stop(ctx) + require.NoError(t, err) - partialName := strings.Split(t.Name(), "/")[1] - goldenFile := filepath.Join("testdata", "rendered-templates", "smtp", partialName+".html.golden") - if *updateGoldenFiles { - err = os.MkdirAll(filepath.Dir(goldenFile), 0o755) - require.NoError(t, err, "want no error creating golden file directory") - err = os.WriteFile(goldenFile, body, 0o600) - require.NoError(t, err, "want no error writing body golden file") - return - } + goldenFile := filepath.Join("testdata", "rendered-templates", "smtp", "TemplateWorkspaceDeleted_WithCustomAppearance.html.golden") - wantBody, err := os.ReadFile(goldenFile) - require.NoError(t, err, fmt.Sprintf("missing golden notification body file. %s", hint)) - require.Empty( - t, - cmp.Diff(wantBody, body), - fmt.Sprintf("golden file mismatch: %s. If this is expected, %s. (-want +got). ", goldenFile, hint), - ) - }) - }) + if *updateGoldenFiles { + err = os.MkdirAll(filepath.Dir(goldenFile), 0o755) + require.NoError(t, err, "want no error creating golden file directory") + err = os.WriteFile(goldenFile, body, 0o600) + require.NoError(t, err, "want no error writing body golden file") } + + wantBody, err := os.ReadFile(goldenFile) + require.NoError(t, err, fmt.Sprintf("missing golden notification body file. %s", hint)) + require.Empty( + t, + cmp.Diff(wantBody, body), + fmt.Sprintf("golden file mismatch: %s. If this is expected, %s. (-want +got). ", goldenFile, hint), + ) } diff --git a/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceDeleted_WithCustomAppearance.html.golden b/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceDeleted_WithCustomAppearance.html.golden new file mode 100644 index 0000000000000..f0c0eed2f37b9 --- /dev/null +++ b/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceDeleted_WithCustomAppearance.html.golden @@ -0,0 +1,90 @@ +From: system@coder.com +To: bobby@coder.com +Subject: Workspace "bobby-workspace" deleted +Message-Id: 02ee4935-73be-4fa1-a290-ff9999026b13@blush-whale-48 +Date: Fri, 11 Oct 2024 09:03:06 +0000 +Content-Type: multipart/alternative; boundary=bbe61b741255b6098bb6b3c1f41b885773df633cb18d2a3002b68e4bc9c4 +MIME-Version: 1.0 + +--bbe61b741255b6098bb6b3c1f41b885773df633cb18d2a3002b68e4bc9c4 +Content-Transfer-Encoding: quoted-printable +Content-Type: text/plain; charset=UTF-8 + +Hi Bobby, + +Your workspace bobby-workspace was deleted. + +The specified reason was "autodeleted due to dormancy (autobuild)". + + +View workspaces: http://test.com/workspaces + +View templates: http://test.com/templates + +--bbe61b741255b6098bb6b3c1f41b885773df633cb18d2a3002b68e4bc9c4 +Content-Transfer-Encoding: quoted-printable +Content-Type: text/html; charset=UTF-8 + + + + + + + Workspace "bobby-workspace" deleted + + +
+
+ 3D"CustomApplication +
+

+ Workspace "bobby-workspace" deleted +

+
+

Hi Bobby,

+ +

Your workspace bobby-workspace was deleted.

+ +

The specified reason was “autodeleted due to dormancy (aut= +obuild)”.

+
+ + +
+ + + +--bbe61b741255b6098bb6b3c1f41b885773df633cb18d2a3002b68e4bc9c4-- From bf558cbeb492b97fc467df924bc9007188d9afc4 Mon Sep 17 00:00:00 2001 From: defelmnq Date: Thu, 17 Oct 2024 23:38:39 +0000 Subject: [PATCH 16/26] feat(notifications): add golden file for custom appearance --- coderd/notifications/dispatch/utils_test.go | 14 +++++----- coderd/notifications/dispatch/webhook.go | 2 +- coderd/notifications/manager_test.go | 4 +-- coderd/notifications/metrics_test.go | 30 ++++++++++----------- coderd/notifications/notifications_test.go | 24 ++++++++--------- coderd/notifications/utils_test.go | 2 +- 6 files changed, 36 insertions(+), 40 deletions(-) diff --git a/coderd/notifications/dispatch/utils_test.go b/coderd/notifications/dispatch/utils_test.go index 483402ecf196f..1defa0be3fe93 100644 --- a/coderd/notifications/dispatch/utils_test.go +++ b/coderd/notifications/dispatch/utils_test.go @@ -1,10 +1,8 @@ package dispatch_test -var ( - helpers = map[string]any{ - "base_url": func() string { return "http://test.com" }, - "current_year": func() string { return "2024" }, - "logo_url": func() string { return "https://coder.com/coder-logo-horizontal.png" }, - "app_name": func() string { return "Coder" }, - } -) +var helpers = map[string]any{ + "base_url": func() string { return "http://test.com" }, + "current_year": func() string { return "2024" }, + "logo_url": func() string { return "https://coder.com/coder-logo-horizontal.png" }, + "app_name": func() string { return "Coder" }, +} diff --git a/coderd/notifications/dispatch/webhook.go b/coderd/notifications/dispatch/webhook.go index 119a7119d0447..f404de98e3e03 100644 --- a/coderd/notifications/dispatch/webhook.go +++ b/coderd/notifications/dispatch/webhook.go @@ -42,7 +42,7 @@ func NewWebhookHandler(cfg codersdk.NotificationsWebhookConfig, log slog.Logger) return &WebhookHandler{cfg: cfg, log: log, cl: &http.Client{}} } -func (w *WebhookHandler) Dispatcher(helpers template.FuncMap, payload types.MessagePayload, titleMarkdown, bodyMarkdown string) (DeliveryFunc, error) { +func (w *WebhookHandler) Dispatcher(_ template.FuncMap, payload types.MessagePayload, titleMarkdown, bodyMarkdown string) (DeliveryFunc, error) { if w.cfg.Endpoint.String() == "" { return nil, xerrors.New("webhook endpoint not defined") } diff --git a/coderd/notifications/manager_test.go b/coderd/notifications/manager_test.go index 60b8dee9a6cc8..5f13f47f1cbb1 100644 --- a/coderd/notifications/manager_test.go +++ b/coderd/notifications/manager_test.go @@ -206,8 +206,8 @@ type santaHandler struct { nice atomic.Int32 } -func (s *santaHandler) Dispatcher(helpers template.FuncMap, payload types.MessagePayload, _, _ string) (dispatch.DeliveryFunc, error) { - return func(ctx context.Context, msgID uuid.UUID) (retryable bool, err error) { +func (s *santaHandler) Dispatcher(_ template.FuncMap, payload types.MessagePayload, _, _ string) (dispatch.DeliveryFunc, error) { + return func(_ context.Context, _ uuid.UUID) (retryable bool, err error) { if payload.Labels["nice"] != "true" { s.naughty.Add(1) return false, xerrors.New("be nice") diff --git a/coderd/notifications/metrics_test.go b/coderd/notifications/metrics_test.go index 7b6c0ca1d8a11..566e719fb8601 100644 --- a/coderd/notifications/metrics_test.go +++ b/coderd/notifications/metrics_test.go @@ -44,7 +44,7 @@ func TestMetrics(t *testing.T) { reg := prometheus.NewRegistry() metrics := notifications.NewMetrics(reg) - template := notifications.TemplateWorkspaceDeleted + tmplate := notifications.TemplateWorkspaceDeleted const ( method = database.NotificationMethodSmtp @@ -76,7 +76,7 @@ func TestMetrics(t *testing.T) { user := createSampleUser(t, api.Database) // Build fingerprints for the two different series we expect. - methodTemplateFP := fingerprintLabels(notifications.LabelMethod, string(method), notifications.LabelTemplateID, template.String()) + methodTemplateFP := fingerprintLabels(notifications.LabelMethod, string(method), notifications.LabelTemplateID, tmplate.String()) methodFP := fingerprintLabels(notifications.LabelMethod, string(method)) expected := map[string]func(metric *dto.Metric, series string) bool{ @@ -90,7 +90,7 @@ func TestMetrics(t *testing.T) { var match string for result, val := range results { - seriesFP := fingerprintLabels(notifications.LabelMethod, string(method), notifications.LabelTemplateID, template.String(), notifications.LabelResult, result) + seriesFP := fingerprintLabels(notifications.LabelMethod, string(method), notifications.LabelTemplateID, tmplate.String(), notifications.LabelResult, result) if !hasMatchingFingerprint(metric, seriesFP) { continue } @@ -165,9 +165,9 @@ func TestMetrics(t *testing.T) { } // WHEN: 2 notifications are enqueued, 1 of which will fail until its retries are exhausted, and another which will succeed - _, err = enq.Enqueue(ctx, user.ID, template, map[string]string{"type": "success"}, "test") // this will succeed + _, err = enq.Enqueue(ctx, user.ID, tmplate, map[string]string{"type": "success"}, "test") // this will succeed require.NoError(t, err) - _, err = enq.Enqueue(ctx, user.ID, template, map[string]string{"type": "failure"}, "test2") // this will fail and retry (maxAttempts - 1) times + _, err = enq.Enqueue(ctx, user.ID, tmplate, map[string]string{"type": "failure"}, "test2") // this will fail and retry (maxAttempts - 1) times require.NoError(t, err) mgr.Run(ctx) @@ -217,7 +217,7 @@ func TestPendingUpdatesMetric(t *testing.T) { reg := prometheus.NewRegistry() metrics := notifications.NewMetrics(reg) - template := notifications.TemplateWorkspaceDeleted + tmplate := notifications.TemplateWorkspaceDeleted const method = database.NotificationMethodSmtp @@ -248,9 +248,9 @@ func TestPendingUpdatesMetric(t *testing.T) { user := createSampleUser(t, api.Database) // WHEN: 2 notifications are enqueued, one of which will fail and one which will succeed - _, err = enq.Enqueue(ctx, user.ID, template, map[string]string{"type": "success"}, "test") // this will succeed + _, err = enq.Enqueue(ctx, user.ID, tmplate, map[string]string{"type": "success"}, "test") // this will succeed require.NoError(t, err) - _, err = enq.Enqueue(ctx, user.ID, template, map[string]string{"type": "failure"}, "test2") // this will fail and retry (maxAttempts - 1) times + _, err = enq.Enqueue(ctx, user.ID, tmplate, map[string]string{"type": "failure"}, "test2") // this will fail and retry (maxAttempts - 1) times require.NoError(t, err) mgr.Run(ctx) @@ -301,7 +301,7 @@ func TestInflightDispatchesMetric(t *testing.T) { reg := prometheus.NewRegistry() metrics := notifications.NewMetrics(reg) - template := notifications.TemplateWorkspaceDeleted + tmplate := notifications.TemplateWorkspaceDeleted const method = database.NotificationMethodSmtp @@ -334,7 +334,7 @@ func TestInflightDispatchesMetric(t *testing.T) { // WHEN: notifications are enqueued which will succeed (and be delayed during dispatch) for i := 0; i < msgCount; i++ { - _, err = enq.Enqueue(ctx, user.ID, template, map[string]string{"type": "success", "i": strconv.Itoa(i)}, "test") + _, err = enq.Enqueue(ctx, user.ID, tmplate, map[string]string{"type": "success", "i": strconv.Itoa(i)}, "test") require.NoError(t, err) } @@ -343,7 +343,7 @@ func TestInflightDispatchesMetric(t *testing.T) { // THEN: // Ensure we see the dispatches of the messages inflight. require.Eventually(t, func() bool { - return promtest.ToFloat64(metrics.InflightDispatches.WithLabelValues(string(method), template.String())) == msgCount + return promtest.ToFloat64(metrics.InflightDispatches.WithLabelValues(string(method), tmplate.String())) == msgCount }, testutil.WaitShort, testutil.IntervalFast) for i := 0; i < msgCount; i++ { @@ -380,7 +380,7 @@ func TestCustomMethodMetricCollection(t *testing.T) { var ( reg = prometheus.NewRegistry() metrics = notifications.NewMetrics(reg) - template = notifications.TemplateWorkspaceDeleted + tmplate = notifications.TemplateWorkspaceDeleted anotherTemplate = notifications.TemplateWorkspaceDormant ) @@ -391,7 +391,7 @@ func TestCustomMethodMetricCollection(t *testing.T) { // GIVEN: a template whose notification method differs from the default. out, err := api.Database.UpdateNotificationTemplateMethodByID(ctx, database.UpdateNotificationTemplateMethodByIDParams{ - ID: template, + ID: tmplate, Method: database.NullNotificationMethod{NotificationMethod: customMethod, Valid: true}, }) require.NoError(t, err) @@ -417,7 +417,7 @@ func TestCustomMethodMetricCollection(t *testing.T) { user := createSampleUser(t, api.Database) - _, err = enq.Enqueue(ctx, user.ID, template, map[string]string{"type": "success"}, "test") + _, err = enq.Enqueue(ctx, user.ID, tmplate, map[string]string{"type": "success"}, "test") require.NoError(t, err) _, err = enq.Enqueue(ctx, user.ID, anotherTemplate, map[string]string{"type": "success"}, "test") require.NoError(t, err) @@ -438,7 +438,7 @@ func TestCustomMethodMetricCollection(t *testing.T) { // THEN: we should have metric series for both the default and custom notification methods. require.Eventually(t, func() bool { return promtest.ToFloat64(metrics.DispatchAttempts.WithLabelValues(string(defaultMethod), anotherTemplate.String(), notifications.ResultSuccess)) > 0 && - promtest.ToFloat64(metrics.DispatchAttempts.WithLabelValues(string(customMethod), template.String(), notifications.ResultSuccess)) > 0 + promtest.ToFloat64(metrics.DispatchAttempts.WithLabelValues(string(customMethod), tmplate.String(), notifications.ResultSuccess)) > 0 }, testutil.WaitShort, testutil.IntervalFast) } diff --git a/coderd/notifications/notifications_test.go b/coderd/notifications/notifications_test.go index 6d5068f0e0613..d928d8ac3ce2e 100644 --- a/coderd/notifications/notifications_test.go +++ b/coderd/notifications/notifications_test.go @@ -1414,12 +1414,12 @@ func TestCustomNotificationMethod(t *testing.T) { // GIVEN: a notification template which has a method explicitly set var ( - template = notifications.TemplateWorkspaceDormant + tmplate = notifications.TemplateWorkspaceDormant defaultMethod = database.NotificationMethodSmtp customMethod = database.NotificationMethodWebhook ) out, err := api.Database.UpdateNotificationTemplateMethodByID(ctx, database.UpdateNotificationTemplateMethodByIDParams{ - ID: template, + ID: tmplate, Method: database.NullNotificationMethod{NotificationMethod: customMethod, Valid: true}, }) require.NoError(t, err) @@ -1447,7 +1447,7 @@ func TestCustomNotificationMethod(t *testing.T) { // WHEN: a notification of that template is enqueued, it should be delivered with the configured method - not the default. user := createSampleUser(t, api.Database) - msgID, err := enq.Enqueue(ctx, user.ID, template, map[string]string{}, "test") + msgID, err := enq.Enqueue(ctx, user.ID, tmplate, map[string]string{}, "test") require.NoError(t, err) // THEN: the notification should be received by the custom dispatch method @@ -1562,7 +1562,7 @@ type fakeHandler struct { succeeded, failed []string } -func (f *fakeHandler) Dispatcher(helpers template.FuncMap, payload types.MessagePayload, _, _ string) (dispatch.DeliveryFunc, error) { +func (f *fakeHandler) Dispatcher(_ template.FuncMap, payload types.MessagePayload, _, _ string) (dispatch.DeliveryFunc, error) { return func(_ context.Context, msgID uuid.UUID) (retryable bool, err error) { f.mu.Lock() defer f.mu.Unlock() @@ -1629,15 +1629,13 @@ func TestNotificationTemplates_GoldenWithCustomAppearance(t *testing.T) { hint = "run \"DB=ci make update-golden-files\" and commit the changes" ) - var ( - payload = types.MessagePayload{ - Labels: map[string]string{ - "name": "bobby-workspace", - "reason": "autodeleted due to dormancy", - "initiator": "autobuild", - }, - } - ) + payload := types.MessagePayload{ + Labels: map[string]string{ + "name": "bobby-workspace", + "reason": "autodeleted due to dormancy", + "initiator": "autobuild", + }, + } // Spin up the DB db, logger, user := func() (database.Store, *slog.Logger, *codersdk.User) { diff --git a/coderd/notifications/utils_test.go b/coderd/notifications/utils_test.go index 30934bf3d6343..7143d2b9326f1 100644 --- a/coderd/notifications/utils_test.go +++ b/coderd/notifications/utils_test.go @@ -70,7 +70,7 @@ func newDispatchInterceptor(h notifications.Handler) *dispatchInterceptor { return &dispatchInterceptor{handler: h} } -func (i *dispatchInterceptor) Dispatcher(helpers template.FuncMap, payload types.MessagePayload, title, body string) (dispatch.DeliveryFunc, error) { +func (i *dispatchInterceptor) Dispatcher(_ template.FuncMap, payload types.MessagePayload, title, body string) (dispatch.DeliveryFunc, error) { return func(ctx context.Context, msgID uuid.UUID) (retryable bool, err error) { deliveryFn, err := i.handler.Dispatcher(defaultHelpers(), payload, title, body) if err != nil { From a420f3786048b6a80dc368c0ab2b5c94226fa945 Mon Sep 17 00:00:00 2001 From: defelmnq Date: Fri, 18 Oct 2024 13:15:27 +0200 Subject: [PATCH 17/26] feat(notifications): work on tests --- coderd/notifications/metrics_test.go | 30 +++++++++---------- coderd/notifications/notifications_test.go | 35 ++++++++++++++++++++-- 2 files changed, 47 insertions(+), 18 deletions(-) diff --git a/coderd/notifications/metrics_test.go b/coderd/notifications/metrics_test.go index 566e719fb8601..32240a54fed05 100644 --- a/coderd/notifications/metrics_test.go +++ b/coderd/notifications/metrics_test.go @@ -44,7 +44,7 @@ func TestMetrics(t *testing.T) { reg := prometheus.NewRegistry() metrics := notifications.NewMetrics(reg) - tmplate := notifications.TemplateWorkspaceDeleted + tmpl := notifications.TemplateWorkspaceDeleted const ( method = database.NotificationMethodSmtp @@ -76,7 +76,7 @@ func TestMetrics(t *testing.T) { user := createSampleUser(t, api.Database) // Build fingerprints for the two different series we expect. - methodTemplateFP := fingerprintLabels(notifications.LabelMethod, string(method), notifications.LabelTemplateID, tmplate.String()) + methodTemplateFP := fingerprintLabels(notifications.LabelMethod, string(method), notifications.LabelTemplateID, tmpl.String()) methodFP := fingerprintLabels(notifications.LabelMethod, string(method)) expected := map[string]func(metric *dto.Metric, series string) bool{ @@ -90,7 +90,7 @@ func TestMetrics(t *testing.T) { var match string for result, val := range results { - seriesFP := fingerprintLabels(notifications.LabelMethod, string(method), notifications.LabelTemplateID, tmplate.String(), notifications.LabelResult, result) + seriesFP := fingerprintLabels(notifications.LabelMethod, string(method), notifications.LabelTemplateID, tmpl.String(), notifications.LabelResult, result) if !hasMatchingFingerprint(metric, seriesFP) { continue } @@ -165,9 +165,9 @@ func TestMetrics(t *testing.T) { } // WHEN: 2 notifications are enqueued, 1 of which will fail until its retries are exhausted, and another which will succeed - _, err = enq.Enqueue(ctx, user.ID, tmplate, map[string]string{"type": "success"}, "test") // this will succeed + _, err = enq.Enqueue(ctx, user.ID, tmpl, map[string]string{"type": "success"}, "test") // this will succeed require.NoError(t, err) - _, err = enq.Enqueue(ctx, user.ID, tmplate, map[string]string{"type": "failure"}, "test2") // this will fail and retry (maxAttempts - 1) times + _, err = enq.Enqueue(ctx, user.ID, tmpl, map[string]string{"type": "failure"}, "test2") // this will fail and retry (maxAttempts - 1) times require.NoError(t, err) mgr.Run(ctx) @@ -217,7 +217,7 @@ func TestPendingUpdatesMetric(t *testing.T) { reg := prometheus.NewRegistry() metrics := notifications.NewMetrics(reg) - tmplate := notifications.TemplateWorkspaceDeleted + tmpl := notifications.TemplateWorkspaceDeleted const method = database.NotificationMethodSmtp @@ -248,9 +248,9 @@ func TestPendingUpdatesMetric(t *testing.T) { user := createSampleUser(t, api.Database) // WHEN: 2 notifications are enqueued, one of which will fail and one which will succeed - _, err = enq.Enqueue(ctx, user.ID, tmplate, map[string]string{"type": "success"}, "test") // this will succeed + _, err = enq.Enqueue(ctx, user.ID, tmpl, map[string]string{"type": "success"}, "test") // this will succeed require.NoError(t, err) - _, err = enq.Enqueue(ctx, user.ID, tmplate, map[string]string{"type": "failure"}, "test2") // this will fail and retry (maxAttempts - 1) times + _, err = enq.Enqueue(ctx, user.ID, tmpl, map[string]string{"type": "failure"}, "test2") // this will fail and retry (maxAttempts - 1) times require.NoError(t, err) mgr.Run(ctx) @@ -301,7 +301,7 @@ func TestInflightDispatchesMetric(t *testing.T) { reg := prometheus.NewRegistry() metrics := notifications.NewMetrics(reg) - tmplate := notifications.TemplateWorkspaceDeleted + tmpl := notifications.TemplateWorkspaceDeleted const method = database.NotificationMethodSmtp @@ -334,7 +334,7 @@ func TestInflightDispatchesMetric(t *testing.T) { // WHEN: notifications are enqueued which will succeed (and be delayed during dispatch) for i := 0; i < msgCount; i++ { - _, err = enq.Enqueue(ctx, user.ID, tmplate, map[string]string{"type": "success", "i": strconv.Itoa(i)}, "test") + _, err = enq.Enqueue(ctx, user.ID, tmpl, map[string]string{"type": "success", "i": strconv.Itoa(i)}, "test") require.NoError(t, err) } @@ -343,7 +343,7 @@ func TestInflightDispatchesMetric(t *testing.T) { // THEN: // Ensure we see the dispatches of the messages inflight. require.Eventually(t, func() bool { - return promtest.ToFloat64(metrics.InflightDispatches.WithLabelValues(string(method), tmplate.String())) == msgCount + return promtest.ToFloat64(metrics.InflightDispatches.WithLabelValues(string(method), tmpl.String())) == msgCount }, testutil.WaitShort, testutil.IntervalFast) for i := 0; i < msgCount; i++ { @@ -380,7 +380,7 @@ func TestCustomMethodMetricCollection(t *testing.T) { var ( reg = prometheus.NewRegistry() metrics = notifications.NewMetrics(reg) - tmplate = notifications.TemplateWorkspaceDeleted + tmpl = notifications.TemplateWorkspaceDeleted anotherTemplate = notifications.TemplateWorkspaceDormant ) @@ -391,7 +391,7 @@ func TestCustomMethodMetricCollection(t *testing.T) { // GIVEN: a template whose notification method differs from the default. out, err := api.Database.UpdateNotificationTemplateMethodByID(ctx, database.UpdateNotificationTemplateMethodByIDParams{ - ID: tmplate, + ID: tmpl, Method: database.NullNotificationMethod{NotificationMethod: customMethod, Valid: true}, }) require.NoError(t, err) @@ -417,7 +417,7 @@ func TestCustomMethodMetricCollection(t *testing.T) { user := createSampleUser(t, api.Database) - _, err = enq.Enqueue(ctx, user.ID, tmplate, map[string]string{"type": "success"}, "test") + _, err = enq.Enqueue(ctx, user.ID, tmpl, map[string]string{"type": "success"}, "test") require.NoError(t, err) _, err = enq.Enqueue(ctx, user.ID, anotherTemplate, map[string]string{"type": "success"}, "test") require.NoError(t, err) @@ -438,7 +438,7 @@ func TestCustomMethodMetricCollection(t *testing.T) { // THEN: we should have metric series for both the default and custom notification methods. require.Eventually(t, func() bool { return promtest.ToFloat64(metrics.DispatchAttempts.WithLabelValues(string(defaultMethod), anotherTemplate.String(), notifications.ResultSuccess)) > 0 && - promtest.ToFloat64(metrics.DispatchAttempts.WithLabelValues(string(customMethod), tmplate.String(), notifications.ResultSuccess)) > 0 + promtest.ToFloat64(metrics.DispatchAttempts.WithLabelValues(string(customMethod), tmpl.String(), notifications.ResultSuccess)) > 0 }, testutil.WaitShort, testutil.IntervalFast) } diff --git a/coderd/notifications/notifications_test.go b/coderd/notifications/notifications_test.go index d928d8ac3ce2e..473f9638b7be9 100644 --- a/coderd/notifications/notifications_test.go +++ b/coderd/notifications/notifications_test.go @@ -708,6 +708,9 @@ func TestNotificationTemplates_Golden(t *testing.T) { name string id uuid.UUID payload types.MessagePayload + + appName string + logoURL string }{ { name: "TemplateWorkspaceDeleted", @@ -958,6 +961,22 @@ func TestNotificationTemplates_Golden(t *testing.T) { }, }, }, + { + name: "TemplateWorkspaceDeleted_CustomAppearance", + id: notifications.TemplateWorkspaceDeleted, + payload: types.MessagePayload{ + UserName: "Bobby", + UserEmail: "bobby@coder.com", + UserUsername: "bobby", + Labels: map[string]string{ + "name": "bobby-workspace", + "reason": "autodeleted due to dormancy", + "initiator": "autobuild", + }, + }, + appName: "Custom Application Name", + logoURL: "https://custom.application/logo.png", + }, } // We must have a test case for every notification_template. This is enforced below: @@ -1079,6 +1098,16 @@ func TestNotificationTemplates_Golden(t *testing.T) { ) require.NoError(t, err) + if tc.appName != "" { + err = (*db).UpsertApplicationName(ctx, "Custom Application") + require.NoError(t, err) + } + + if tc.logoURL != "" { + err = (*db).UpsertLogoURL(ctx, "https://custom.application/logo.png") + require.NoError(t, err) + } + smtpManager.Run(ctx) notificationCfg := defaultNotificationsConfig(database.NotificationMethodSmtp) @@ -1414,12 +1443,12 @@ func TestCustomNotificationMethod(t *testing.T) { // GIVEN: a notification template which has a method explicitly set var ( - tmplate = notifications.TemplateWorkspaceDormant + tmpl = notifications.TemplateWorkspaceDormant defaultMethod = database.NotificationMethodSmtp customMethod = database.NotificationMethodWebhook ) out, err := api.Database.UpdateNotificationTemplateMethodByID(ctx, database.UpdateNotificationTemplateMethodByIDParams{ - ID: tmplate, + ID: tmpl, Method: database.NullNotificationMethod{NotificationMethod: customMethod, Valid: true}, }) require.NoError(t, err) @@ -1447,7 +1476,7 @@ func TestCustomNotificationMethod(t *testing.T) { // WHEN: a notification of that template is enqueued, it should be delivered with the configured method - not the default. user := createSampleUser(t, api.Database) - msgID, err := enq.Enqueue(ctx, user.ID, tmplate, map[string]string{}, "test") + msgID, err := enq.Enqueue(ctx, user.ID, tmpl, map[string]string{}, "test") require.NoError(t, err) // THEN: the notification should be received by the custom dispatch method From 51d8d3385330d34404732e3f8b256b83dbc95872 Mon Sep 17 00:00:00 2001 From: defelmnq Date: Fri, 18 Oct 2024 12:36:41 +0000 Subject: [PATCH 18/26] feat(notifications): add golden file for custom appearance --- ...spaceDeleted_CustomAppearance.html.golden} | 4 +-- ...kspaceDeleted_CustomAppearance.json.golden | 33 +++++++++++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) rename coderd/notifications/testdata/rendered-templates/smtp/{TemplateWorkspaceDeleted_WithCustomAppearance.html.golden => TemplateWorkspaceDeleted_CustomAppearance.html.golden} (94%) create mode 100644 coderd/notifications/testdata/rendered-templates/webhook/TemplateWorkspaceDeleted_CustomAppearance.json.golden diff --git a/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceDeleted_WithCustomAppearance.html.golden b/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceDeleted_CustomAppearance.html.golden similarity index 94% rename from coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceDeleted_WithCustomAppearance.html.golden rename to coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceDeleted_CustomAppearance.html.golden index f0c0eed2f37b9..a6aa1f62d9ab9 100644 --- a/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceDeleted_WithCustomAppearance.html.golden +++ b/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceDeleted_CustomAppearance.html.golden @@ -41,8 +41,8 @@ l', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; color: #020617= r: 1px solid #e2e8f0; border-radius: 8px; background-color: #fff; text-alig= n: left; font-size: 14px; line-height: 1.5;">
- 3D"CustomApplication + 3D"Custom

diff --git a/coderd/notifications/testdata/rendered-templates/webhook/TemplateWorkspaceDeleted_CustomAppearance.json.golden b/coderd/notifications/testdata/rendered-templates/webhook/TemplateWorkspaceDeleted_CustomAppearance.json.golden new file mode 100644 index 0000000000000..171e893dd943f --- /dev/null +++ b/coderd/notifications/testdata/rendered-templates/webhook/TemplateWorkspaceDeleted_CustomAppearance.json.golden @@ -0,0 +1,33 @@ +{ + "_version": "1.1", + "msg_id": "00000000-0000-0000-0000-000000000000", + "payload": { + "_version": "1.1", + "notification_name": "Workspace Deleted", + "notification_template_id": "00000000-0000-0000-0000-000000000000", + "user_id": "00000000-0000-0000-0000-000000000000", + "user_email": "bobby@coder.com", + "user_name": "Bobby", + "user_username": "bobby", + "actions": [ + { + "label": "View workspaces", + "url": "http://test.com/workspaces" + }, + { + "label": "View templates", + "url": "http://test.com/templates" + } + ], + "labels": { + "initiator": "autobuild", + "name": "bobby-workspace", + "reason": "autodeleted due to dormancy" + }, + "data": null + }, + "title": "Workspace \"bobby-workspace\" deleted", + "title_markdown": "Workspace \"bobby-workspace\" deleted", + "body": "Hi Bobby,\n\nYour workspace bobby-workspace was deleted.\n\nThe specified reason was \"autodeleted due to dormancy (autobuild)\".", + "body_markdown": "Hi Bobby,\n\nYour workspace **bobby-workspace** was deleted.\n\nThe specified reason was \"**autodeleted due to dormancy (autobuild)**\"." +} \ No newline at end of file From d492d098df555d89d6956af494350a810e2c7d80 Mon Sep 17 00:00:00 2001 From: defelmnq Date: Fri, 18 Oct 2024 12:57:40 +0000 Subject: [PATCH 19/26] feat(notifications): add golden file for custom appearance --- coderd/notifications/notifications_test.go | 180 --------------------- 1 file changed, 180 deletions(-) diff --git a/coderd/notifications/notifications_test.go b/coderd/notifications/notifications_test.go index 473f9638b7be9..ff33638ef16c9 100644 --- a/coderd/notifications/notifications_test.go +++ b/coderd/notifications/notifications_test.go @@ -1640,183 +1640,3 @@ func (n *acquireSignalingInterceptor) AcquireNotificationMessages(ctx context.Co n.acquiredChan <- struct{}{} return messages, err } - -func TestNotificationTemplates_GoldenWithCustomAppearance(t *testing.T) { - t.Parallel() - - if !dbtestutil.WillUsePostgres() { - t.Skip("This test requires postgres; it relies on the notification templates added by migrations in the database") - } - - const ( - username = "bob" - password = "🤫" - - hello = "localhost" - - from = "system@coder.com" - hint = "run \"DB=ci make update-golden-files\" and commit the changes" - ) - - payload := types.MessagePayload{ - Labels: map[string]string{ - "name": "bobby-workspace", - "reason": "autodeleted due to dormancy", - "initiator": "autobuild", - }, - } - - // Spin up the DB - db, logger, user := func() (database.Store, *slog.Logger, *codersdk.User) { - adminClient, _, api := coderdtest.NewWithAPI(t, nil) - firstUser := coderdtest.CreateFirstUser(t, adminClient) - - _, user := coderdtest.CreateAnotherUserMutators( - t, - adminClient, - firstUser.OrganizationID, - []rbac.RoleIdentifier{rbac.RoleUserAdmin()}, - func(r *codersdk.CreateUserRequestWithOrgs) { - r.Username = "bobby" - r.Email = "bobby@coder.com" - r.Name = "Bobby" - }, - ) - return api.Database, &api.Logger, &user - }() - - // nolint:gocritic // Unit test. - ctx := dbauthz.AsSystemRestricted(testutil.Context(t, testutil.WaitSuperLong)) - - err := db.UpsertApplicationName(ctx, "CustomApplication") - require.NoError(t, err) - - err = db.UpsertLogoURL(ctx, "https://custom.application") - require.NoError(t, err) - - // smtp config shared between client and server - smtpConfig := codersdk.NotificationsEmailConfig{ - Hello: hello, - From: from, - - Auth: codersdk.NotificationsEmailAuthConfig{ - Username: username, - Password: password, - }, - } - - // Spin up the mock SMTP server - backend := smtptest.NewBackend(smtptest.Config{ - AuthMechanisms: []string{sasl.Login}, - - AcceptedIdentity: smtpConfig.Auth.Identity.String(), - AcceptedUsername: username, - AcceptedPassword: password, - }) - - // Create a mock SMTP server which conditionally listens for plain or TLS connections. - srv, listen, err := smtptest.CreateMockSMTPServer(backend, false) - require.NoError(t, err) - t.Cleanup(func() { - err := srv.Shutdown(ctx) - require.NoError(t, err) - }) - - var hp serpent.HostPort - require.NoError(t, hp.Set(listen.Addr().String())) - smtpConfig.Smarthost = hp - - // Start mock SMTP server in the background. - var wg sync.WaitGroup - wg.Add(1) - go func() { - defer wg.Done() - assert.NoError(t, srv.Serve(listen)) - }() - - // Wait for the server to become pingable. - require.Eventually(t, func() bool { - cl, err := smtptest.PingClient(listen, false, smtpConfig.TLS.StartTLS.Value()) - if err != nil { - t.Logf("smtp not yet dialable: %s", err) - return false - } - - if err = cl.Noop(); err != nil { - t.Logf("smtp not yet noopable: %s", err) - return false - } - - if err = cl.Close(); err != nil { - t.Logf("smtp didn't close properly: %s", err) - return false - } - - return true - }, testutil.WaitShort, testutil.IntervalFast) - - smtpCfg := defaultNotificationsConfig(database.NotificationMethodSmtp) - smtpCfg.SMTP = smtpConfig - - smtpManager, err := notifications.NewManager( - smtpCfg, - db, - defaultHelpers(), - createMetrics(), - logger.Named("manager"), - ) - require.NoError(t, err) - - smtpManager.Run(ctx) - - notificationCfg := defaultNotificationsConfig(database.NotificationMethodSmtp) - - smtpEnqueuer, err := notifications.NewStoreEnqueuer( - notificationCfg, - db, - defaultHelpers(), - logger.Named("enqueuer"), - quartz.NewReal(), - ) - require.NoError(t, err) - - _, err = smtpEnqueuer.EnqueueWithData( - ctx, - user.ID, - notifications.TemplateWorkspaceDeleted, - payload.Labels, - payload.Data, - user.Username, - user.ID, - ) - require.NoError(t, err) - - // Wait for the message to be fetched - var msg *smtptest.Message - require.Eventually(t, func() bool { - msg = backend.LastMessage() - return msg != nil && len(msg.Contents) > 0 - }, testutil.WaitShort, testutil.IntervalFast) - - body := normalizeGoldenEmail([]byte(msg.Contents)) - - err = smtpManager.Stop(ctx) - require.NoError(t, err) - - goldenFile := filepath.Join("testdata", "rendered-templates", "smtp", "TemplateWorkspaceDeleted_WithCustomAppearance.html.golden") - - if *updateGoldenFiles { - err = os.MkdirAll(filepath.Dir(goldenFile), 0o755) - require.NoError(t, err, "want no error creating golden file directory") - err = os.WriteFile(goldenFile, body, 0o600) - require.NoError(t, err, "want no error writing body golden file") - } - - wantBody, err := os.ReadFile(goldenFile) - require.NoError(t, err, fmt.Sprintf("missing golden notification body file. %s", hint)) - require.Empty( - t, - cmp.Diff(wantBody, body), - fmt.Sprintf("golden file mismatch: %s. If this is expected, %s. (-want +got). ", goldenFile, hint), - ) -} From 0c9c48535e4bfa0b676e7ee23fa653ad03e5fb44 Mon Sep 17 00:00:00 2001 From: defelmnq Date: Fri, 18 Oct 2024 13:54:39 +0000 Subject: [PATCH 20/26] feat(notifications): add comment ent in tests for enterprise feature --- coderd/notifications/notifications_test.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/coderd/notifications/notifications_test.go b/coderd/notifications/notifications_test.go index ff33638ef16c9..4e34c70b91cc9 100644 --- a/coderd/notifications/notifications_test.go +++ b/coderd/notifications/notifications_test.go @@ -1008,6 +1008,14 @@ func TestNotificationTemplates_Golden(t *testing.T) { db := api.Database firstUser := coderdtest.CreateFirstUser(t, adminClient) + if tc.appName != "" || tc.logoURL != "" { + err = adminClient.UpdateAppearance(context.Background(), codersdk.UpdateAppearanceConfig{ + ApplicationName: tc.appName, + LogoURL: tc.logoURL, + }) + require.NoError(t, err) + } + _, user := coderdtest.CreateAnotherUserMutators( t, adminClient, @@ -1098,6 +1106,9 @@ func TestNotificationTemplates_Golden(t *testing.T) { ) require.NoError(t, err) + // we apply ApplicationName and LogoURL changes directly in the db + // as appearance changes are enterprise features and we do not want to mix those + // can't use the api if tc.appName != "" { err = (*db).UpsertApplicationName(ctx, "Custom Application") require.NoError(t, err) From a6d4a0c8a107a402531332f22afb98f02c275920 Mon Sep 17 00:00:00 2001 From: defelmnq Date: Fri, 18 Oct 2024 14:03:29 +0000 Subject: [PATCH 21/26] feat(coderd): remove unused call --- coderd/notifications/notifications_test.go | 8 -------- 1 file changed, 8 deletions(-) diff --git a/coderd/notifications/notifications_test.go b/coderd/notifications/notifications_test.go index 4e34c70b91cc9..c4aae768667ae 100644 --- a/coderd/notifications/notifications_test.go +++ b/coderd/notifications/notifications_test.go @@ -1008,14 +1008,6 @@ func TestNotificationTemplates_Golden(t *testing.T) { db := api.Database firstUser := coderdtest.CreateFirstUser(t, adminClient) - if tc.appName != "" || tc.logoURL != "" { - err = adminClient.UpdateAppearance(context.Background(), codersdk.UpdateAppearanceConfig{ - ApplicationName: tc.appName, - LogoURL: tc.logoURL, - }) - require.NoError(t, err) - } - _, user := coderdtest.CreateAnotherUserMutators( t, adminClient, From 4c5cb3d94d6e68eb42d7515e4e1c1c38fad4653f Mon Sep 17 00:00:00 2001 From: defelmnq Date: Fri, 18 Oct 2024 16:36:04 +0200 Subject: [PATCH 22/26] fix(notifications): improve tests and some nit fixes --- coderd/notifications/dispatch/smtp.go | 2 +- coderd/notifications/dispatch/smtp_test.go | 2 +- coderd/notifications/dispatch/utils_test.go | 12 ++++++---- coderd/notifications/dispatch/webhook.go | 2 +- coderd/notifications/dispatch/webhook_test.go | 2 +- coderd/notifications/fetcher.go | 24 ++++++++++++++++++- coderd/notifications/manager_test.go | 2 +- coderd/notifications/metrics_test.go | 4 ++-- coderd/notifications/notifications_test.go | 2 +- coderd/notifications/notifier.go | 18 +++----------- coderd/notifications/spec.go | 2 +- coderd/notifications/utils_test.go | 4 ++-- 12 files changed, 44 insertions(+), 32 deletions(-) diff --git a/coderd/notifications/dispatch/smtp.go b/coderd/notifications/dispatch/smtp.go index 14bba5a5746a1..e18aeaef88b81 100644 --- a/coderd/notifications/dispatch/smtp.go +++ b/coderd/notifications/dispatch/smtp.go @@ -61,7 +61,7 @@ func NewSMTPHandler(cfg codersdk.NotificationsEmailConfig, log slog.Logger) *SMT return &SMTPHandler{cfg: cfg, log: log} } -func (s *SMTPHandler) Dispatcher(helpers template.FuncMap, payload types.MessagePayload, titleTmpl, bodyTmpl string) (DeliveryFunc, error) { +func (s *SMTPHandler) Dispatcher(payload types.MessagePayload, titleTmpl, bodyTmpl string, helpers template.FuncMap) (DeliveryFunc, error) { // First render the subject & body into their own discrete strings. subject, err := markdown.PlaintextFromMarkdown(titleTmpl) if err != nil { diff --git a/coderd/notifications/dispatch/smtp_test.go b/coderd/notifications/dispatch/smtp_test.go index f3fc2b67bbd1f..c9a60b426ae70 100644 --- a/coderd/notifications/dispatch/smtp_test.go +++ b/coderd/notifications/dispatch/smtp_test.go @@ -480,7 +480,7 @@ func TestSMTP(t *testing.T) { Labels: make(map[string]string), } - dispatchFn, err := handler.Dispatcher(helpers, payload, subject, body) + dispatchFn, err := handler.Dispatcher(payload, subject, body, helpers()) require.NoError(t, err) msgID := uuid.New() diff --git a/coderd/notifications/dispatch/utils_test.go b/coderd/notifications/dispatch/utils_test.go index 1defa0be3fe93..3ed4e09cffc11 100644 --- a/coderd/notifications/dispatch/utils_test.go +++ b/coderd/notifications/dispatch/utils_test.go @@ -1,8 +1,10 @@ package dispatch_test -var helpers = map[string]any{ - "base_url": func() string { return "http://test.com" }, - "current_year": func() string { return "2024" }, - "logo_url": func() string { return "https://coder.com/coder-logo-horizontal.png" }, - "app_name": func() string { return "Coder" }, +func helpers() map[string]any { + return map[string]any{ + "base_url": func() string { return "http://test.com" }, + "current_year": func() string { return "2024" }, + "logo_url": func() string { return "https://coder.com/coder-logo-horizontal.png" }, + "app_name": func() string { return "Coder" }, + } } diff --git a/coderd/notifications/dispatch/webhook.go b/coderd/notifications/dispatch/webhook.go index f404de98e3e03..1322996db10e1 100644 --- a/coderd/notifications/dispatch/webhook.go +++ b/coderd/notifications/dispatch/webhook.go @@ -42,7 +42,7 @@ func NewWebhookHandler(cfg codersdk.NotificationsWebhookConfig, log slog.Logger) return &WebhookHandler{cfg: cfg, log: log, cl: &http.Client{}} } -func (w *WebhookHandler) Dispatcher(_ template.FuncMap, payload types.MessagePayload, titleMarkdown, bodyMarkdown string) (DeliveryFunc, error) { +func (w *WebhookHandler) Dispatcher(payload types.MessagePayload, titleMarkdown, bodyMarkdown string, _ template.FuncMap) (DeliveryFunc, error) { if w.cfg.Endpoint.String() == "" { return nil, xerrors.New("webhook endpoint not defined") } diff --git a/coderd/notifications/dispatch/webhook_test.go b/coderd/notifications/dispatch/webhook_test.go index 37966f418025c..9f898a6fd6efd 100644 --- a/coderd/notifications/dispatch/webhook_test.go +++ b/coderd/notifications/dispatch/webhook_test.go @@ -141,7 +141,7 @@ func TestWebhook(t *testing.T) { Endpoint: *serpent.URLOf(endpoint), } handler := dispatch.NewWebhookHandler(cfg, logger.With(slog.F("test", tc.name))) - deliveryFn, err := handler.Dispatcher(helpers, msgPayload, titleMarkdown, bodyMarkdown) + deliveryFn, err := handler.Dispatcher(msgPayload, titleMarkdown, bodyMarkdown, helpers()) require.NoError(t, err) retryable, err := deliveryFn(ctx, msgID) diff --git a/coderd/notifications/fetcher.go b/coderd/notifications/fetcher.go index 73d5cfcc607bd..a579275d127bf 100644 --- a/coderd/notifications/fetcher.go +++ b/coderd/notifications/fetcher.go @@ -4,17 +4,39 @@ import ( "context" "database/sql" "errors" + "text/template" "golang.org/x/xerrors" ) +func (n *notifier) fetchHelpers(ctx context.Context) (map[string]any, error) { + appName, err := n.fetchAppName(ctx) + if err != nil { + return nil, xerrors.Errorf("fetch app name: %w", err) + } + logoURL, err := n.fetchLogoURL(ctx) + if err != nil { + return nil, xerrors.Errorf("fetch logo URL: %w", err) + } + + helpers := make(template.FuncMap) + for k, v := range n.helpers { + helpers[k] = v + } + + helpers["app_name"] = func() string { return appName } + helpers["logo_url"] = func() string { return logoURL } + + return helpers, nil +} + func (n *notifier) fetchAppName(ctx context.Context) (string, error) { appName, err := n.store.GetApplicationName(ctx) if err != nil { if errors.Is(err, sql.ErrNoRows) { return notificationsDefaultAppName, nil } - return "", xerrors.Errorf("get organization: %w", err) + return "", xerrors.Errorf("get application name: %w", err) } return appName, nil } diff --git a/coderd/notifications/manager_test.go b/coderd/notifications/manager_test.go index 5f13f47f1cbb1..c6df79722c7ce 100644 --- a/coderd/notifications/manager_test.go +++ b/coderd/notifications/manager_test.go @@ -206,7 +206,7 @@ type santaHandler struct { nice atomic.Int32 } -func (s *santaHandler) Dispatcher(_ template.FuncMap, payload types.MessagePayload, _, _ string) (dispatch.DeliveryFunc, error) { +func (s *santaHandler) Dispatcher(payload types.MessagePayload, _, _ string, _ template.FuncMap) (dispatch.DeliveryFunc, error) { return func(_ context.Context, _ uuid.UUID) (retryable bool, err error) { if payload.Labels["nice"] != "true" { s.naughty.Add(1) diff --git a/coderd/notifications/metrics_test.go b/coderd/notifications/metrics_test.go index 32240a54fed05..34eb8a266cf24 100644 --- a/coderd/notifications/metrics_test.go +++ b/coderd/notifications/metrics_test.go @@ -516,8 +516,8 @@ func newBarrierHandler(total int, handler notifications.Handler) *barrierHandler } } -func (bh *barrierHandler) Dispatcher(helpers template.FuncMap, payload types.MessagePayload, title, body string) (dispatch.DeliveryFunc, error) { - deliverFn, err := bh.h.Dispatcher(helpers, payload, title, body) +func (bh *barrierHandler) Dispatcher(payload types.MessagePayload, title, body string, helpers template.FuncMap) (dispatch.DeliveryFunc, error) { + deliverFn, err := bh.h.Dispatcher(payload, title, body, helpers) if err != nil { return nil, err } diff --git a/coderd/notifications/notifications_test.go b/coderd/notifications/notifications_test.go index c4aae768667ae..07c413430c17c 100644 --- a/coderd/notifications/notifications_test.go +++ b/coderd/notifications/notifications_test.go @@ -1594,7 +1594,7 @@ type fakeHandler struct { succeeded, failed []string } -func (f *fakeHandler) Dispatcher(_ template.FuncMap, payload types.MessagePayload, _, _ string) (dispatch.DeliveryFunc, error) { +func (f *fakeHandler) Dispatcher(payload types.MessagePayload, _, _ string, _ template.FuncMap) (dispatch.DeliveryFunc, error) { return func(_ context.Context, msgID uuid.UUID) (retryable bool, err error) { f.mu.Lock() defer f.mu.Unlock() diff --git a/coderd/notifications/notifier.go b/coderd/notifications/notifier.go index f2f34c75efdf6..a22a63c77346d 100644 --- a/coderd/notifications/notifier.go +++ b/coderd/notifications/notifier.go @@ -228,22 +228,10 @@ func (n *notifier) prepare(ctx context.Context, msg database.AcquireNotification return nil, xerrors.Errorf("failed to resolve handler %q", msg.Method) } - helpers := make(template.FuncMap) - for k, v := range n.helpers { - helpers[k] = v - } - - appName, err := n.fetchAppName(ctx) + helpers, err := n.fetchHelpers(ctx) if err != nil { - return nil, xerrors.Errorf("fetch app name: %w", err) + return nil, xerrors.Errorf("fetch helpers: %w", err) } - logoURL, err := n.fetchLogoURL(ctx) - if err != nil { - return nil, xerrors.Errorf("fetch logo URL: %w", err) - } - - helpers["app_name"] = func() string { return appName } - helpers["logo_url"] = func() string { return logoURL } var title, body string if title, err = render.GoTemplate(msg.TitleTemplate, payload, helpers); err != nil { @@ -253,7 +241,7 @@ func (n *notifier) prepare(ctx context.Context, msg database.AcquireNotification return nil, xerrors.Errorf("render body: %w", err) } - return handler.Dispatcher(helpers, payload, title, body) + return handler.Dispatcher(payload, title, body, helpers) } // deliver sends a given notification message via its defined method. diff --git a/coderd/notifications/spec.go b/coderd/notifications/spec.go index f3f7d1aa5b64f..7ac40b6cae8b8 100644 --- a/coderd/notifications/spec.go +++ b/coderd/notifications/spec.go @@ -30,7 +30,7 @@ type Store interface { // Handler is responsible for preparing and delivering a notification by a given method. type Handler interface { // Dispatcher constructs a DeliveryFunc to be used for delivering a notification via the chosen method. - Dispatcher(helpers template.FuncMap, payload types.MessagePayload, title, body string) (dispatch.DeliveryFunc, error) + Dispatcher(payload types.MessagePayload, title, body string, helpers template.FuncMap) (dispatch.DeliveryFunc, error) } // Enqueuer enqueues a new notification message in the store and returns its ID, should it enqueue without failure. diff --git a/coderd/notifications/utils_test.go b/coderd/notifications/utils_test.go index 7143d2b9326f1..a835b15bf1bd1 100644 --- a/coderd/notifications/utils_test.go +++ b/coderd/notifications/utils_test.go @@ -70,9 +70,9 @@ func newDispatchInterceptor(h notifications.Handler) *dispatchInterceptor { return &dispatchInterceptor{handler: h} } -func (i *dispatchInterceptor) Dispatcher(_ template.FuncMap, payload types.MessagePayload, title, body string) (dispatch.DeliveryFunc, error) { +func (i *dispatchInterceptor) Dispatcher(payload types.MessagePayload, title, body string, _ template.FuncMap) (dispatch.DeliveryFunc, error) { return func(ctx context.Context, msgID uuid.UUID) (retryable bool, err error) { - deliveryFn, err := i.handler.Dispatcher(defaultHelpers(), payload, title, body) + deliveryFn, err := i.handler.Dispatcher(payload, title, body, defaultHelpers()) if err != nil { return false, err } From e4194319dc56c63e45b10078f0f40aefaea2f41d Mon Sep 17 00:00:00 2001 From: defelmnq Date: Fri, 18 Oct 2024 17:30:25 +0200 Subject: [PATCH 23/26] chore(retry): improve retry policy on fetcher --- coderd/notifications/fetcher.go | 4 ++++ coderd/notifications/notifier.go | 12 ++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/coderd/notifications/fetcher.go b/coderd/notifications/fetcher.go index a579275d127bf..82405049f933a 100644 --- a/coderd/notifications/fetcher.go +++ b/coderd/notifications/fetcher.go @@ -7,15 +7,19 @@ import ( "text/template" "golang.org/x/xerrors" + + "cdr.dev/slog" ) func (n *notifier) fetchHelpers(ctx context.Context) (map[string]any, error) { appName, err := n.fetchAppName(ctx) if err != nil { + n.log.Error(ctx, "failed to fetch app name", slog.Error(err)) return nil, xerrors.Errorf("fetch app name: %w", err) } logoURL, err := n.fetchLogoURL(ctx) if err != nil { + n.log.Error(ctx, "failed to fetch logo URL", slog.Error(err)) return nil, xerrors.Errorf("fetch logo URL: %w", err) } diff --git a/coderd/notifications/notifier.go b/coderd/notifications/notifier.go index a22a63c77346d..a3509426a8333 100644 --- a/coderd/notifications/notifier.go +++ b/coderd/notifications/notifier.go @@ -27,6 +27,10 @@ const ( notificationsDefaultAppName = "Coder" ) +var ( + errFetchfailed = xerrors.New("failed to fetch helpers") +) + // notifier is a consumer of the notifications_messages queue. It dequeues messages from that table and processes them // through a pipeline of fetch -> prepare -> render -> acquire handler -> deliver. type notifier struct { @@ -168,7 +172,11 @@ func (n *notifier) process(ctx context.Context, success chan<- dispatchResult, f deliverFn, err := n.prepare(ctx, msg) if err != nil { n.log.Warn(ctx, "dispatcher construction failed", slog.F("msg_id", msg.ID), slog.Error(err)) - failure <- n.newFailedDispatch(msg, err, false) + if xerrors.Is(err, errFetchfailed) { + failure <- n.newFailedDispatch(msg, err, true) + } else { + failure <- n.newFailedDispatch(msg, err, false) + } n.metrics.PendingUpdates.Set(float64(len(success) + len(failure))) continue @@ -230,7 +238,7 @@ func (n *notifier) prepare(ctx context.Context, msg database.AcquireNotification helpers, err := n.fetchHelpers(ctx) if err != nil { - return nil, xerrors.Errorf("fetch helpers: %w", err) + return nil, errFetchfailed } var title, body string From 8b766f622540238a601cbceb2899b7a066bc8540 Mon Sep 17 00:00:00 2001 From: defelmnq Date: Tue, 22 Oct 2024 03:07:43 +0200 Subject: [PATCH 24/26] improve errors handling --- coderd/notifications/notifier.go | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/coderd/notifications/notifier.go b/coderd/notifications/notifier.go index f0ca4359bbda9..30b583abd88ac 100644 --- a/coderd/notifications/notifier.go +++ b/coderd/notifications/notifier.go @@ -28,7 +28,7 @@ const ( ) var ( - errFetchfailed = xerrors.New("failed to fetch helpers") + errDecorateHelpersFailed = xerrors.New("failed to decorate helpers") ) // notifier is a consumer of the notifications_messages queue. It dequeues messages from that table and processes them @@ -167,12 +167,7 @@ func (n *notifier) process(ctx context.Context, success chan<- dispatchResult, f deliverFn, err := n.prepare(ctx, msg) if err != nil { n.log.Warn(ctx, "dispatcher construction failed", slog.F("msg_id", msg.ID), slog.Error(err)) - if xerrors.Is(err, errFetchfailed) { - failure <- n.newFailedDispatch(msg, err, true) - } else { - failure <- n.newFailedDispatch(msg, err, false) - } - + failure <- n.newFailedDispatch(msg, err, xerrors.Is(err, errDecorateHelpersFailed)) n.metrics.PendingUpdates.Set(float64(len(success) + len(failure))) continue } @@ -233,7 +228,7 @@ func (n *notifier) prepare(ctx context.Context, msg database.AcquireNotification helpers, err := n.fetchHelpers(ctx) if err != nil { - return nil, errFetchfailed + return nil, errDecorateHelpersFailed } var title, body string From 157e08694f8a690eecb6905f97268207e5c579aa Mon Sep 17 00:00:00 2001 From: defelmnq Date: Tue, 22 Oct 2024 03:12:56 +0200 Subject: [PATCH 25/26] improve errors handling --- coderd/notifications/notifications_test.go | 2 +- coderd/notifications/utils_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/coderd/notifications/notifications_test.go b/coderd/notifications/notifications_test.go index e0cfa113bdf44..4a6978b5024fe 100644 --- a/coderd/notifications/notifications_test.go +++ b/coderd/notifications/notifications_test.go @@ -158,7 +158,7 @@ func TestSMTPDispatch(t *testing.T) { Smarthost: serpent.HostPort{Host: "localhost", Port: fmt.Sprintf("%d", mockSMTPSrv.PortNumber())}, Hello: "localhost", } - handler := newDispatchInterceptor(dispatch.NewSMTPHandler(cfg.SMTP, defaultHelpers(), logger.Named("smtp"))) + handler := newDispatchInterceptor(dispatch.NewSMTPHandler(cfg.SMTP, logger.Named("smtp"))) mgr, err := notifications.NewManager(cfg, store, defaultHelpers(), createMetrics(), logger.Named("manager")) require.NoError(t, err) mgr.WithHandlers(map[database.NotificationMethod]notifications.Handler{method: handler}) diff --git a/coderd/notifications/utils_test.go b/coderd/notifications/utils_test.go index 57f21bf91bf77..95155ea39c347 100644 --- a/coderd/notifications/utils_test.go +++ b/coderd/notifications/utils_test.go @@ -111,7 +111,7 @@ type chanHandler struct { calls chan dispatchCall } -func (c chanHandler) Dispatcher(payload types.MessagePayload, title, body string) (dispatch.DeliveryFunc, error) { +func (c chanHandler) Dispatcher(payload types.MessagePayload, title, body string, _ template.FuncMap) (dispatch.DeliveryFunc, error) { result := make(chan dispatchResult) call := dispatchCall{ payload: payload, From 790ff333d98e23ca46de1aaa0f5fa24f68052f54 Mon Sep 17 00:00:00 2001 From: defelmnq Date: Tue, 22 Oct 2024 03:17:36 +0200 Subject: [PATCH 26/26] improve errors handling --- coderd/notifications/notifier.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/coderd/notifications/notifier.go b/coderd/notifications/notifier.go index 30b583abd88ac..5fa71d80ce175 100644 --- a/coderd/notifications/notifier.go +++ b/coderd/notifications/notifier.go @@ -27,9 +27,7 @@ const ( notificationsDefaultAppName = "Coder" ) -var ( - errDecorateHelpersFailed = xerrors.New("failed to decorate helpers") -) +var errDecorateHelpersFailed = xerrors.New("failed to decorate helpers") // notifier is a consumer of the notifications_messages queue. It dequeues messages from that table and processes them // through a pipeline of fetch -> prepare -> render -> acquire handler -> deliver.