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. pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy