From 541b852686f7d5c5a84be891412fb92d26ab3413 Mon Sep 17 00:00:00 2001
From: Danielle Maywood
Date: Mon, 30 Dec 2024 14:36:44 +0000
Subject: [PATCH 01/10] add notification on workspace update
---
...280_workspace_update_notification.down.sql | 1 +
...00280_workspace_update_notification.up.sql | 20 +++++
coderd/notifications/events.go | 1 +
coderd/notifications/notifications_test.go | 15 ++++
...mplateWorkspaceManuallyUpdated.html.golden | 89 +++++++++++++++++++
...mplateWorkspaceManuallyUpdated.json.golden | 34 +++++++
coderd/workspacebuilds.go | 89 +++++++++++++++++++
7 files changed, 249 insertions(+)
create mode 100644 coderd/database/migrations/000280_workspace_update_notification.down.sql
create mode 100644 coderd/database/migrations/000280_workspace_update_notification.up.sql
create mode 100644 coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceManuallyUpdated.html.golden
create mode 100644 coderd/notifications/testdata/rendered-templates/webhook/TemplateWorkspaceManuallyUpdated.json.golden
diff --git a/coderd/database/migrations/000280_workspace_update_notification.down.sql b/coderd/database/migrations/000280_workspace_update_notification.down.sql
new file mode 100644
index 0000000000000..5097c0248fe9b
--- /dev/null
+++ b/coderd/database/migrations/000280_workspace_update_notification.down.sql
@@ -0,0 +1 @@
+DELETE FROM notification_templates WHERE id = 'd089fe7b-d5c5-4c0c-aaf5-689859f7d392';
diff --git a/coderd/database/migrations/000280_workspace_update_notification.up.sql b/coderd/database/migrations/000280_workspace_update_notification.up.sql
new file mode 100644
index 0000000000000..d1d53c7dada52
--- /dev/null
+++ b/coderd/database/migrations/000280_workspace_update_notification.up.sql
@@ -0,0 +1,20 @@
+INSERT INTO notification_templates
+ (id, name, title_template, body_template, "group", actions)
+VALUES (
+ 'd089fe7b-d5c5-4c0c-aaf5-689859f7d392',
+ 'Workspace Manually Updated',
+ E'Workspace ''{{.Labels.workspace}}'' has been manually updated',
+ E'Hello {{.UserName}},\n\n'||
+ E'Your workspace **{{.Labels.workspace}}** has been manually updated to template version **{{.Labels.version}}**.',
+ 'Workspace Events',
+ '[
+ {
+ "label": "See workspace",
+ "url": "{{base_url}}/@{{.UserUsername}}/{{.Labels.workspace}}"
+ },
+ {
+ "label": "See template version",
+ "url": "{{base_url}}/templates/{{.Labels.organization}}/{{.Labels.template}}/versions/{{.Labels.version}}"
+ }
+ ]'::jsonb
+);
diff --git a/coderd/notifications/events.go b/coderd/notifications/events.go
index 12aecbaac74ae..754d2e5c7f745 100644
--- a/coderd/notifications/events.go
+++ b/coderd/notifications/events.go
@@ -8,6 +8,7 @@ import "github.com/google/uuid"
// Workspace-related events.
var (
TemplateWorkspaceCreated = uuid.MustParse("281fdf73-c6d6-4cbb-8ff5-888baf8a2fff")
+ TemplateWorkspaceManuallyUpdated = uuid.MustParse("d089fe7b-d5c5-4c0c-aaf5-689859f7d392")
TemplateWorkspaceDeleted = uuid.MustParse("f517da0b-cdc9-410f-ab89-a86107c420ed")
TemplateWorkspaceAutobuildFailed = uuid.MustParse("381df2a9-c0c0-4749-420f-80a9280c66f9")
TemplateWorkspaceDormant = uuid.MustParse("0ea69165-ec14-4314-91f1-69566ac3c5a0")
diff --git a/coderd/notifications/notifications_test.go b/coderd/notifications/notifications_test.go
index 90cf1a46be28a..616ed364f3809 100644
--- a/coderd/notifications/notifications_test.go
+++ b/coderd/notifications/notifications_test.go
@@ -1048,6 +1048,21 @@ func TestNotificationTemplates_Golden(t *testing.T) {
},
},
},
+ {
+ name: "TemplateWorkspaceManuallyUpdated",
+ id: notifications.TemplateWorkspaceManuallyUpdated,
+ payload: types.MessagePayload{
+ UserName: "Bobby",
+ UserEmail: "bobby@coder.com",
+ UserUsername: "bobby",
+ Labels: map[string]string{
+ "organization": "bobby-organization",
+ "workspace": "bobby-workspace",
+ "template": "bobby-template",
+ "version": "alpha",
+ },
+ },
+ },
}
// We must have a test case for every notification_template. This is enforced below:
diff --git a/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceManuallyUpdated.html.golden b/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceManuallyUpdated.html.golden
new file mode 100644
index 0000000000000..b8630e64c9b50
--- /dev/null
+++ b/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceManuallyUpdated.html.golden
@@ -0,0 +1,89 @@
+From: system@coder.com
+To: bobby@coder.com
+Subject: Workspace 'bobby-workspace' has been manually updated
+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
+
+Hello Bobby,
+
+Your workspace bobby-workspace has been manually updated to template versio=
+n alpha.
+
+
+See workspace: http://test.com/@bobby/bobby-workspace
+
+See template version: http://test.com/templates/bobby-organization/bobby-te=
+mplate/versions/alpha
+
+--bbe61b741255b6098bb6b3c1f41b885773df633cb18d2a3002b68e4bc9c4
+Content-Transfer-Encoding: quoted-printable
+Content-Type: text/html; charset=UTF-8
+
+
+
+
+
+
+ Workspace 'bobby-workspace' has been manually updated
+
+
+
+
+

+
+
+ Workspace 'bobby-workspace' has been manually updated
+
+
+
Hello Bobby,
+
+
Your workspace bobby-workspace has been manually update=
+d to template version alpha.
+
+
+
+
+
+
+
+--bbe61b741255b6098bb6b3c1f41b885773df633cb18d2a3002b68e4bc9c4--
diff --git a/coderd/notifications/testdata/rendered-templates/webhook/TemplateWorkspaceManuallyUpdated.json.golden b/coderd/notifications/testdata/rendered-templates/webhook/TemplateWorkspaceManuallyUpdated.json.golden
new file mode 100644
index 0000000000000..da9defe42bb75
--- /dev/null
+++ b/coderd/notifications/testdata/rendered-templates/webhook/TemplateWorkspaceManuallyUpdated.json.golden
@@ -0,0 +1,34 @@
+{
+ "_version": "1.1",
+ "msg_id": "00000000-0000-0000-0000-000000000000",
+ "payload": {
+ "_version": "1.1",
+ "notification_name": "Workspace Manually Updated",
+ "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": "See workspace",
+ "url": "http://test.com/@bobby/bobby-workspace"
+ },
+ {
+ "label": "See template version",
+ "url": "http://test.com/templates/bobby-organization/bobby-template/versions/alpha"
+ }
+ ],
+ "labels": {
+ "organization": "bobby-organization",
+ "template": "bobby-template",
+ "version": "alpha",
+ "workspace": "bobby-workspace"
+ },
+ "data": null
+ },
+ "title": "Workspace 'bobby-workspace' has been manually updated",
+ "title_markdown": "Workspace 'bobby-workspace' has been manually updated",
+ "body": "Hello Bobby,\n\nYour workspace bobby-workspace has been manually updated to template version alpha.",
+ "body_markdown": "Hello Bobby,\n\nYour workspace **bobby-workspace** has been manually updated to template version **alpha**."
+}
\ No newline at end of file
diff --git a/coderd/workspacebuilds.go b/coderd/workspacebuilds.go
index 7eb598a7d4564..4427bf0a117c7 100644
--- a/coderd/workspacebuilds.go
+++ b/coderd/workspacebuilds.go
@@ -27,6 +27,7 @@ import (
"github.com/coder/coder/v2/coderd/database/provisionerjobs"
"github.com/coder/coder/v2/coderd/httpapi"
"github.com/coder/coder/v2/coderd/httpmw"
+ "github.com/coder/coder/v2/coderd/notifications"
"github.com/coder/coder/v2/coderd/provisionerdserver"
"github.com/coder/coder/v2/coderd/rbac"
"github.com/coder/coder/v2/coderd/rbac/policy"
@@ -327,6 +328,15 @@ func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) {
return
}
+ previousWorkspaceBuild, err := api.Database.GetLatestWorkspaceBuildByWorkspaceID(ctx, workspace.ID)
+ if err != nil && !xerrors.Is(err, sql.ErrNoRows) {
+ httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
+ Message: "Internal error fetching latest workspace build",
+ Detail: err.Error(),
+ })
+ return
+ }
+
builder := wsbuilder.New(workspace, database.WorkspaceTransition(createBuild.Transition)).
Initiator(apiKey.UserID).
RichParameterValues(createBuild.RichParameterValues).
@@ -420,6 +430,12 @@ func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) {
return
}
+ // If this workspace build has a different template version ID to the previous build
+ // we can assume it has just been updated.
+ if createBuild.TemplateVersionID != uuid.Nil && createBuild.TemplateVersionID != previousWorkspaceBuild.TemplateVersionID {
+ api.notifyWorkspaceUpdated(ctx, workspace.ID)
+ }
+
api.publishWorkspaceUpdate(ctx, workspace.OwnerID, wspubsub.WorkspaceEvent{
Kind: wspubsub.WorkspaceEventKindStateChange,
WorkspaceID: workspace.ID,
@@ -428,6 +444,79 @@ func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) {
httpapi.Write(ctx, rw, http.StatusCreated, apiBuild)
}
+func (api *API) notifyWorkspaceUpdated(ctx context.Context, workspaceID uuid.UUID) {
+ log := api.Logger.With(slog.F("workspace_id", workspaceID))
+
+ workspace, err := api.Database.GetWorkspaceByID(ctx, workspaceID)
+ if err != nil {
+ log.Warn(ctx, "failed to fetch workspace for workspace update notification", slog.Error(err))
+ return
+ }
+
+ template, err := api.Database.GetTemplateByID(ctx, workspace.TemplateID)
+ if err != nil {
+ log.Warn(ctx, "failed to fetch template for workspace creation notification", slog.F("template_id", workspace.TemplateID), slog.Error(err))
+ return
+ }
+
+ owner, err := api.Database.GetUserByID(ctx, workspace.OwnerID)
+ if err != nil {
+ log.Warn(ctx, "failed to fetch user for workspace update notification", slog.F("owner_id", workspace.OwnerID), slog.Error(err))
+ return
+ }
+
+ version, err := api.Database.GetTemplateVersionByID(ctx, template.ActiveVersionID)
+ if err != nil {
+ log.Warn(ctx, "failed to fetch template version for workspace update notification", slog.F("template_version_id", template.ActiveVersionID), slog.Error(err))
+ return
+ }
+
+ build, err := api.Database.GetLatestWorkspaceBuildByWorkspaceID(ctx, workspace.ID)
+ if err != nil {
+ log.Warn(ctx, "failed to fetch latest workspace build for workspace update notification", slog.Error(err))
+ return
+ }
+
+ parameters, err := api.Database.GetWorkspaceBuildParameters(ctx, build.ID)
+ if err != nil {
+ log.Warn(ctx, "failed to fetch workspace build parameters for workspace update notification", slog.Error(err))
+ return
+ }
+
+ buildParameters := make([]map[string]any, len(parameters))
+ for idx, parameter := range parameters {
+ buildParameters[idx] = map[string]any{
+ "name": parameter.Name,
+ "value": parameter.Value,
+ }
+ }
+
+ if _, err := api.NotificationsEnqueuer.EnqueueWithData(
+ // nolint:gocritic // Need notifier actor to enqueue notifications
+ dbauthz.AsNotifier(ctx),
+ workspace.OwnerID,
+ notifications.TemplateWorkspaceManuallyUpdated,
+ map[string]string{
+ "organization": template.OrganizationName,
+ "workspace": workspace.Name,
+ "template": template.Name,
+ "version": version.Name,
+ },
+ map[string]any{
+ "workspace": map[string]any{"id": workspace.ID, "name": workspace.Name},
+ "template": map[string]any{"id": template.ID, "name": template.Name},
+ "template_version": map[string]any{"id": version.ID, "name": version.Name},
+ "owner": map[string]any{"id": owner.ID, "name": owner.Name},
+ "parameters": buildParameters,
+ },
+ "api-workspaces-updated",
+ // Associate this notification with all the related entities
+ workspace.ID, workspace.OwnerID, workspace.TemplateID, workspace.OrganizationID,
+ ); err != nil {
+ log.Warn(ctx, "failed to notify of workspace update", slog.Error(err))
+ }
+}
+
// @Summary Cancel workspace build
// @ID cancel-workspace-build
// @Security CoderSessionToken
From 835e49cdfa8db64f9804a27cf4ff5d6175e2925c Mon Sep 17 00:00:00 2001
From: Danielle Maywood
Date: Mon, 30 Dec 2024 18:42:28 +0000
Subject: [PATCH 02/10] tests: test expectations
---
coderd/workspacebuilds_test.go | 100 +++++++++++++++++++++++++++++++++
1 file changed, 100 insertions(+)
diff --git a/coderd/workspacebuilds_test.go b/coderd/workspacebuilds_test.go
index feb748ad29250..43674b308583c 100644
--- a/coderd/workspacebuilds_test.go
+++ b/coderd/workspacebuilds_test.go
@@ -27,6 +27,8 @@ import (
"github.com/coder/coder/v2/coderd/database/dbtestutil"
"github.com/coder/coder/v2/coderd/database/dbtime"
"github.com/coder/coder/v2/coderd/externalauth"
+ "github.com/coder/coder/v2/coderd/notifications"
+ "github.com/coder/coder/v2/coderd/notifications/notificationstest"
"github.com/coder/coder/v2/coderd/rbac"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/provisioner/echo"
@@ -560,6 +562,104 @@ func TestWorkspaceBuildResources(t *testing.T) {
})
}
+func TestWorkspaceBuildWithUpdatedTemplateVersionSendsNotification(t *testing.T) {
+ t.Parallel()
+
+ t.Run("OnlyOneNotification", func(t *testing.T) {
+ t.Parallel()
+
+ notify := ¬ificationstest.FakeEnqueuer{}
+
+ client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true, NotificationsEnqueuer: notify})
+ first := coderdtest.CreateFirstUser(t, client)
+ userClient, user := coderdtest.CreateAnotherUser(t, client, first.OrganizationID)
+
+ // Create a template with an initial version
+ version := coderdtest.CreateTemplateVersion(t, client, first.OrganizationID, nil)
+ coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID)
+ template := coderdtest.CreateTemplate(t, client, first.OrganizationID, version.ID)
+
+ // Create a workspace using this template
+ workspace := coderdtest.CreateWorkspace(t, userClient, template.ID)
+ coderdtest.AwaitWorkspaceBuildJobCompleted(t, userClient, workspace.LatestBuild.ID)
+ coderdtest.MustTransitionWorkspace(t, userClient, workspace.ID, database.WorkspaceTransitionStart, database.WorkspaceTransitionStop)
+
+ // Create a new version of the template
+ newVersion := coderdtest.CreateTemplateVersion(t, client, first.OrganizationID, nil, func(ctvr *codersdk.CreateTemplateVersionRequest) {
+ ctvr.TemplateID = template.ID
+ })
+ coderdtest.AwaitTemplateVersionJobCompleted(t, client, newVersion.ID)
+
+ // Create a workspace build using this new template version
+ build := coderdtest.CreateWorkspaceBuild(t, userClient, workspace, database.WorkspaceTransitionStart, func(cwbr *codersdk.CreateWorkspaceBuildRequest) {
+ cwbr.TemplateVersionID = newVersion.ID
+ })
+ coderdtest.AwaitWorkspaceBuildJobCompleted(t, userClient, build.ID)
+ coderdtest.MustTransitionWorkspace(t, userClient, workspace.ID, database.WorkspaceTransitionStart, database.WorkspaceTransitionStop)
+
+ // Create the workspace build _again_. We are doing this to ensure we only create 1 notification.
+ build = coderdtest.CreateWorkspaceBuild(t, userClient, workspace, database.WorkspaceTransitionStart, func(cwbr *codersdk.CreateWorkspaceBuildRequest) {
+ cwbr.TemplateVersionID = newVersion.ID
+ })
+ coderdtest.AwaitWorkspaceBuildJobCompleted(t, userClient, build.ID)
+ coderdtest.MustTransitionWorkspace(t, userClient, workspace.ID, database.WorkspaceTransitionStart, database.WorkspaceTransitionStop)
+
+ // Ensure we receive only 1 workspace manually updated notification
+ sent := notify.Sent(notificationstest.WithTemplateID(notifications.TemplateWorkspaceManuallyUpdated))
+ require.Len(t, sent, 1)
+ require.Equal(t, user.ID, sent[0].UserID)
+ require.Contains(t, sent[0].Targets, template.ID)
+ require.Contains(t, sent[0].Targets, workspace.ID)
+ require.Contains(t, sent[0].Targets, workspace.OrganizationID)
+ require.Contains(t, sent[0].Targets, workspace.OwnerID)
+ })
+
+ t.Run("ToCorrectUser", func(t *testing.T) {
+ t.Parallel()
+
+ notify := ¬ificationstest.FakeEnqueuer{}
+
+ client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true, NotificationsEnqueuer: notify})
+ first := coderdtest.CreateFirstUser(t, client)
+ userClient, user := coderdtest.CreateAnotherUser(t, client, first.OrganizationID)
+
+ // Create a template with an initial version
+ version := coderdtest.CreateTemplateVersion(t, client, first.OrganizationID, nil)
+ coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID)
+ template := coderdtest.CreateTemplate(t, client, first.OrganizationID, version.ID)
+
+ // Create a workspace using this template
+ workspace := coderdtest.CreateWorkspace(t, userClient, template.ID)
+ coderdtest.AwaitWorkspaceBuildJobCompleted(t, userClient, workspace.LatestBuild.ID)
+ coderdtest.MustTransitionWorkspace(t, userClient, workspace.ID, database.WorkspaceTransitionStart, database.WorkspaceTransitionStop)
+
+ // Create a new version of the template
+ newVersion := coderdtest.CreateTemplateVersion(t, client, first.OrganizationID, nil, func(ctvr *codersdk.CreateTemplateVersionRequest) {
+ ctvr.TemplateID = template.ID
+ })
+ coderdtest.AwaitTemplateVersionJobCompleted(t, client, newVersion.ID)
+
+ // Create a workspace build using this new template version from a different user
+ ctx := testutil.Context(t, testutil.WaitShort)
+ build, err := client.CreateWorkspaceBuild(ctx, workspace.ID, codersdk.CreateWorkspaceBuildRequest{
+ Transition: codersdk.WorkspaceTransitionStart,
+ TemplateVersionID: newVersion.ID,
+ })
+ require.NoError(t, err)
+ coderdtest.AwaitWorkspaceBuildJobCompleted(t, userClient, build.ID)
+ coderdtest.MustTransitionWorkspace(t, userClient, workspace.ID, database.WorkspaceTransitionStart, database.WorkspaceTransitionStop)
+
+ // Ensure we receive only 1 workspace manually updated notification and to the right user
+ sent := notify.Sent(notificationstest.WithTemplateID(notifications.TemplateWorkspaceManuallyUpdated))
+ require.Len(t, sent, 1)
+ require.Equal(t, user.ID, sent[0].UserID)
+ require.Contains(t, sent[0].Targets, template.ID)
+ require.Contains(t, sent[0].Targets, workspace.ID)
+ require.Contains(t, sent[0].Targets, workspace.OrganizationID)
+ require.Contains(t, sent[0].Targets, workspace.OwnerID)
+ })
+}
+
func assertWorkspaceResource(t *testing.T, actual codersdk.WorkspaceResource, name, aType string, numAgents int) {
assert.Equal(t, name, actual.Name)
assert.Equal(t, aType, actual.Type)
From b43a7e6a25efb085c4897d74798799113067b5ab Mon Sep 17 00:00:00 2001
From: Danielle Maywood
Date: Tue, 31 Dec 2024 09:55:41 +0000
Subject: [PATCH 03/10] chore: change error message, add error log
---
coderd/workspacebuilds.go | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/coderd/workspacebuilds.go b/coderd/workspacebuilds.go
index 4427bf0a117c7..ab055534978d5 100644
--- a/coderd/workspacebuilds.go
+++ b/coderd/workspacebuilds.go
@@ -330,8 +330,9 @@ func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) {
previousWorkspaceBuild, err := api.Database.GetLatestWorkspaceBuildByWorkspaceID(ctx, workspace.ID)
if err != nil && !xerrors.Is(err, sql.ErrNoRows) {
+ api.Logger.Error(ctx, "failed fetching previous workspace build", slog.F("workspace_id", workspace.ID), slog.Error(err))
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
- Message: "Internal error fetching latest workspace build",
+ Message: "Internal error fetching previous workspace build",
Detail: err.Error(),
})
return
From c683985c8837fe85bbaf5c79cb6c82ef30fc55fa Mon Sep 17 00:00:00 2001
From: Danielle Maywood
Date: Tue, 31 Dec 2024 14:40:23 +0000
Subject: [PATCH 04/10] chore: apply feedback
---
...00280_workspace_update_notification.up.sql | 16 +++++++--
coderd/notifications/notifications_test.go | 1 +
.../smtp/TemplateWorkspaceCreated.html.golden | 4 +--
...mplateWorkspaceManuallyUpdated.html.golden | 19 +++++-----
.../TemplateWorkspaceCreated.json.golden | 2 +-
...mplateWorkspaceManuallyUpdated.json.golden | 9 ++---
coderd/workspacebuilds.go | 36 ++++++++-----------
7 files changed, 47 insertions(+), 40 deletions(-)
diff --git a/coderd/database/migrations/000280_workspace_update_notification.up.sql b/coderd/database/migrations/000280_workspace_update_notification.up.sql
index d1d53c7dada52..23d2331a323f6 100644
--- a/coderd/database/migrations/000280_workspace_update_notification.up.sql
+++ b/coderd/database/migrations/000280_workspace_update_notification.up.sql
@@ -5,16 +5,26 @@ VALUES (
'Workspace Manually Updated',
E'Workspace ''{{.Labels.workspace}}'' has been manually updated',
E'Hello {{.UserName}},\n\n'||
- E'Your workspace **{{.Labels.workspace}}** has been manually updated to template version **{{.Labels.version}}**.',
+ E'A new workspace build has been manually created for your workspace **{{.Labels.workspace}}** by **{{.Labels.initiator}}** to update it to version **{{.Labels.version}}** of template **{{.Labels.template}}**.',
'Workspace Events',
'[
{
- "label": "See workspace",
+ "label": "View workspace",
"url": "{{base_url}}/@{{.UserUsername}}/{{.Labels.workspace}}"
},
{
- "label": "See template version",
+ "label": "View template version",
"url": "{{base_url}}/templates/{{.Labels.organization}}/{{.Labels.template}}/versions/{{.Labels.version}}"
}
]'::jsonb
);
+
+UPDATE notification_templates
+SET
+ actions = '[
+ {
+ "label": "View workspace",
+ "url": "{{base_url}}/@{{.UserUsername}}/{{.Labels.workspace}}"
+ }
+ ]'::jsonb
+WHERE id = '281fdf73-c6d6-4cbb-8ff5-888baf8a2fff';
diff --git a/coderd/notifications/notifications_test.go b/coderd/notifications/notifications_test.go
index 616ed364f3809..1c4be51974b05 100644
--- a/coderd/notifications/notifications_test.go
+++ b/coderd/notifications/notifications_test.go
@@ -1057,6 +1057,7 @@ func TestNotificationTemplates_Golden(t *testing.T) {
UserUsername: "bobby",
Labels: map[string]string{
"organization": "bobby-organization",
+ "initiator": "bobby",
"workspace": "bobby-workspace",
"template": "bobby-template",
"version": "alpha",
diff --git a/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceCreated.html.golden b/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceCreated.html.golden
index 000b2a71ac77b..9d039ea7f77e9 100644
--- a/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceCreated.html.golden
+++ b/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceCreated.html.golden
@@ -16,7 +16,7 @@ The workspace bobby-workspace has been created from the template bobby-temp=
late using version alpha.
-See workspace: http://test.com/@bobby/bobby-workspace
+View workspace: http://test.com/@bobby/bobby-workspace
--bbe61b741255b6098bb6b3c1f41b885773df633cb18d2a3002b68e4bc9c4
Content-Transfer-Encoding: quoted-printable
@@ -57,7 +57,7 @@ ng>.
- See workspace
+ View workspace
=20
diff --git a/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceManuallyUpdated.html.golden b/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceManuallyUpdated.html.golden
index b8630e64c9b50..57a9a0d51b7b7 100644
--- a/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceManuallyUpdated.html.golden
+++ b/coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceManuallyUpdated.html.golden
@@ -12,14 +12,14 @@ Content-Type: text/plain; charset=UTF-8
Hello Bobby,
-Your workspace bobby-workspace has been manually updated to template versio=
-n alpha.
+A new workspace build has been manually created for your workspace bobby-wo=
+rkspace by bobby to update it to version alpha of template bobby-template.
-See workspace: http://test.com/@bobby/bobby-workspace
+View workspace: http://test.com/@bobby/bobby-workspace
-See template version: http://test.com/templates/bobby-organization/bobby-te=
-mplate/versions/alpha
+View template version: http://test.com/templates/bobby-organization/bobby-t=
+emplate/versions/alpha
--bbe61b741255b6098bb6b3c1f41b885773df633cb18d2a3002b68e4bc9c4
Content-Transfer-Encoding: quoted-printable
@@ -51,22 +51,23 @@ argin: 8px 0 32px; line-height: 1.5;">
Hello Bobby,
-
Your workspace bobby-workspace has been manually update=
-d to template version alpha.
+
A new workspace build has been manually created for your workspace bobby-workspace by bobby to update it to versi=
+on alpha of template bobby-template.
diff --git a/coderd/notifications/testdata/rendered-templates/webhook/TemplateWorkspaceCreated.json.golden b/coderd/notifications/testdata/rendered-templates/webhook/TemplateWorkspaceCreated.json.golden
index 46354c4ffeef9..924f299b228b2 100644
--- a/coderd/notifications/testdata/rendered-templates/webhook/TemplateWorkspaceCreated.json.golden
+++ b/coderd/notifications/testdata/rendered-templates/webhook/TemplateWorkspaceCreated.json.golden
@@ -11,7 +11,7 @@
"user_username": "bobby",
"actions": [
{
- "label": "See workspace",
+ "label": "View workspace",
"url": "http://test.com/@bobby/bobby-workspace"
}
],
diff --git a/coderd/notifications/testdata/rendered-templates/webhook/TemplateWorkspaceManuallyUpdated.json.golden b/coderd/notifications/testdata/rendered-templates/webhook/TemplateWorkspaceManuallyUpdated.json.golden
index da9defe42bb75..7fbda32e194f4 100644
--- a/coderd/notifications/testdata/rendered-templates/webhook/TemplateWorkspaceManuallyUpdated.json.golden
+++ b/coderd/notifications/testdata/rendered-templates/webhook/TemplateWorkspaceManuallyUpdated.json.golden
@@ -11,15 +11,16 @@
"user_username": "bobby",
"actions": [
{
- "label": "See workspace",
+ "label": "View workspace",
"url": "http://test.com/@bobby/bobby-workspace"
},
{
- "label": "See template version",
+ "label": "View template version",
"url": "http://test.com/templates/bobby-organization/bobby-template/versions/alpha"
}
],
"labels": {
+ "initiator": "bobby",
"organization": "bobby-organization",
"template": "bobby-template",
"version": "alpha",
@@ -29,6 +30,6 @@
},
"title": "Workspace 'bobby-workspace' has been manually updated",
"title_markdown": "Workspace 'bobby-workspace' has been manually updated",
- "body": "Hello Bobby,\n\nYour workspace bobby-workspace has been manually updated to template version alpha.",
- "body_markdown": "Hello Bobby,\n\nYour workspace **bobby-workspace** has been manually updated to template version **alpha**."
+ "body": "Hello Bobby,\n\nA new workspace build has been manually created for your workspace bobby-workspace by bobby to update it to version alpha of template bobby-template.",
+ "body_markdown": "Hello Bobby,\n\nA new workspace build has been manually created for your workspace **bobby-workspace** by **bobby** to update it to version **alpha** of template **bobby-template**."
}
\ No newline at end of file
diff --git a/coderd/workspacebuilds.go b/coderd/workspacebuilds.go
index ab055534978d5..8339d66722971 100644
--- a/coderd/workspacebuilds.go
+++ b/coderd/workspacebuilds.go
@@ -434,7 +434,7 @@ func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) {
// If this workspace build has a different template version ID to the previous build
// we can assume it has just been updated.
if createBuild.TemplateVersionID != uuid.Nil && createBuild.TemplateVersionID != previousWorkspaceBuild.TemplateVersionID {
- api.notifyWorkspaceUpdated(ctx, workspace.ID)
+ api.notifyWorkspaceUpdated(ctx, apiKey.UserID, workspace, createBuild.RichParameterValues)
}
api.publishWorkspaceUpdate(ctx, workspace.OwnerID, wspubsub.WorkspaceEvent{
@@ -445,14 +445,13 @@ func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) {
httpapi.Write(ctx, rw, http.StatusCreated, apiBuild)
}
-func (api *API) notifyWorkspaceUpdated(ctx context.Context, workspaceID uuid.UUID) {
- log := api.Logger.With(slog.F("workspace_id", workspaceID))
-
- workspace, err := api.Database.GetWorkspaceByID(ctx, workspaceID)
- if err != nil {
- log.Warn(ctx, "failed to fetch workspace for workspace update notification", slog.Error(err))
- return
- }
+func (api *API) notifyWorkspaceUpdated(
+ ctx context.Context,
+ initiatorID uuid.UUID,
+ workspace database.Workspace,
+ parameters []codersdk.WorkspaceBuildParameter,
+) {
+ log := api.Logger.With(slog.F("workspace_id", workspace.ID))
template, err := api.Database.GetTemplateByID(ctx, workspace.TemplateID)
if err != nil {
@@ -460,27 +459,21 @@ func (api *API) notifyWorkspaceUpdated(ctx context.Context, workspaceID uuid.UUI
return
}
- owner, err := api.Database.GetUserByID(ctx, workspace.OwnerID)
+ version, err := api.Database.GetTemplateVersionByID(ctx, workspace.TemplateID)
if err != nil {
- log.Warn(ctx, "failed to fetch user for workspace update notification", slog.F("owner_id", workspace.OwnerID), slog.Error(err))
+ log.Warn(ctx, "failed to fetch template version for workspace creation notification", slog.F("template_id", workspace.TemplateID), slog.Error(err))
return
}
- version, err := api.Database.GetTemplateVersionByID(ctx, template.ActiveVersionID)
+ initiator, err := api.Database.GetUserByID(ctx, initiatorID)
if err != nil {
- log.Warn(ctx, "failed to fetch template version for workspace update notification", slog.F("template_version_id", template.ActiveVersionID), slog.Error(err))
+ log.Warn(ctx, "failed to fetch user for workspace update notification", slog.F("initiator_id", initiatorID), slog.Error(err))
return
}
- build, err := api.Database.GetLatestWorkspaceBuildByWorkspaceID(ctx, workspace.ID)
- if err != nil {
- log.Warn(ctx, "failed to fetch latest workspace build for workspace update notification", slog.Error(err))
- return
- }
-
- parameters, err := api.Database.GetWorkspaceBuildParameters(ctx, build.ID)
+ owner, err := api.Database.GetUserByID(ctx, workspace.OwnerID)
if err != nil {
- log.Warn(ctx, "failed to fetch workspace build parameters for workspace update notification", slog.Error(err))
+ log.Warn(ctx, "failed to fetch user for workspace update notification", slog.F("owner_id", workspace.OwnerID), slog.Error(err))
return
}
@@ -499,6 +492,7 @@ func (api *API) notifyWorkspaceUpdated(ctx context.Context, workspaceID uuid.UUI
notifications.TemplateWorkspaceManuallyUpdated,
map[string]string{
"organization": template.OrganizationName,
+ "initiator": initiator.Name,
"workspace": workspace.Name,
"template": template.Name,
"version": version.Name,
From b3707a163eb2dc2be296d20e1affa5236fc61e1d Mon Sep 17 00:00:00 2001
From: Danielle Maywood
Date: Tue, 31 Dec 2024 14:59:14 +0000
Subject: [PATCH 05/10] fix: broken logic
---
coderd/workspacebuilds.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/coderd/workspacebuilds.go b/coderd/workspacebuilds.go
index 8339d66722971..738ee9be1ba29 100644
--- a/coderd/workspacebuilds.go
+++ b/coderd/workspacebuilds.go
@@ -459,7 +459,7 @@ func (api *API) notifyWorkspaceUpdated(
return
}
- version, err := api.Database.GetTemplateVersionByID(ctx, workspace.TemplateID)
+ version, err := api.Database.GetTemplateVersionByID(ctx, template.ActiveVersionID)
if err != nil {
log.Warn(ctx, "failed to fetch template version for workspace creation notification", slog.F("template_id", workspace.TemplateID), slog.Error(err))
return
From 7f10eccbe1c9765595351b4615619c4cb974776b Mon Sep 17 00:00:00 2001
From: Danielle Maywood
Date: Thu, 2 Jan 2025 10:29:57 +0000
Subject: [PATCH 06/10] chore: wrap in transaction
---
coderd/workspacebuilds.go | 93 ++++++++++++++++++++++-----------------
1 file changed, 53 insertions(+), 40 deletions(-)
diff --git a/coderd/workspacebuilds.go b/coderd/workspacebuilds.go
index 738ee9be1ba29..808b0ba423059 100644
--- a/coderd/workspacebuilds.go
+++ b/coderd/workspacebuilds.go
@@ -328,53 +328,66 @@ func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) {
return
}
- previousWorkspaceBuild, err := api.Database.GetLatestWorkspaceBuildByWorkspaceID(ctx, workspace.ID)
- if err != nil && !xerrors.Is(err, sql.ErrNoRows) {
- api.Logger.Error(ctx, "failed fetching previous workspace build", slog.F("workspace_id", workspace.ID), slog.Error(err))
- httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
- Message: "Internal error fetching previous workspace build",
- Detail: err.Error(),
- })
- return
- }
-
- builder := wsbuilder.New(workspace, database.WorkspaceTransition(createBuild.Transition)).
- Initiator(apiKey.UserID).
- RichParameterValues(createBuild.RichParameterValues).
- LogLevel(string(createBuild.LogLevel)).
- DeploymentValues(api.Options.DeploymentValues)
+ var (
+ previousWorkspaceBuild database.WorkspaceBuild
+ workspaceBuild *database.WorkspaceBuild
+ provisionerJob *database.ProvisionerJob
+ provisionerDaemons []database.GetEligibleProvisionerDaemonsByProvisionerJobIDsRow
+ )
- if createBuild.TemplateVersionID != uuid.Nil {
- builder = builder.VersionID(createBuild.TemplateVersionID)
- }
+ err := api.Database.InTx(func(database.Store) error {
+ var err error
- if createBuild.Orphan {
- if createBuild.Transition != codersdk.WorkspaceTransitionDelete {
- httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
- Message: "Orphan is only permitted when deleting a workspace.",
+ previousWorkspaceBuild, err = api.Database.GetLatestWorkspaceBuildByWorkspaceID(ctx, workspace.ID)
+ if err != nil && !xerrors.Is(err, sql.ErrNoRows) {
+ api.Logger.Error(ctx, "failed fetching previous workspace build", slog.F("workspace_id", workspace.ID), slog.Error(err))
+ httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
+ Message: "Internal error fetching previous workspace build",
+ Detail: err.Error(),
})
- return
+ return nil
+ }
+
+ builder := wsbuilder.New(workspace, database.WorkspaceTransition(createBuild.Transition)).
+ Initiator(apiKey.UserID).
+ RichParameterValues(createBuild.RichParameterValues).
+ LogLevel(string(createBuild.LogLevel)).
+ DeploymentValues(api.Options.DeploymentValues)
+
+ if createBuild.TemplateVersionID != uuid.Nil {
+ builder = builder.VersionID(createBuild.TemplateVersionID)
+ }
+
+ if createBuild.Orphan {
+ if createBuild.Transition != codersdk.WorkspaceTransitionDelete {
+ httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
+ Message: "Orphan is only permitted when deleting a workspace.",
+ })
+ return nil
+ }
+ if len(createBuild.ProvisionerState) > 0 {
+ httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
+ Message: "ProvisionerState cannot be set alongside Orphan since state intent is unclear.",
+ })
+ return nil
+ }
+ builder = builder.Orphan()
}
if len(createBuild.ProvisionerState) > 0 {
- httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
- Message: "ProvisionerState cannot be set alongside Orphan since state intent is unclear.",
- })
- return
+ builder = builder.State(createBuild.ProvisionerState)
}
- builder = builder.Orphan()
- }
- if len(createBuild.ProvisionerState) > 0 {
- builder = builder.State(createBuild.ProvisionerState)
- }
- workspaceBuild, provisionerJob, provisionerDaemons, err := builder.Build(
- ctx,
- api.Database,
- func(action policy.Action, object rbac.Objecter) bool {
- return api.Authorize(r, action, object)
- },
- audit.WorkspaceBuildBaggageFromRequest(r),
- )
+ workspaceBuild, provisionerJob, provisionerDaemons, err = builder.Build(
+ ctx,
+ api.Database,
+ func(action policy.Action, object rbac.Objecter) bool {
+ return api.Authorize(r, action, object)
+ },
+ audit.WorkspaceBuildBaggageFromRequest(r),
+ )
+
+ return err
+ }, nil)
var buildErr wsbuilder.BuildError
if xerrors.As(err, &buildErr) {
var authErr dbauthz.NotAuthorizedError
From 1a493fff5496be0e84b48fb45cf618a7f5a938e1 Mon Sep 17 00:00:00 2001
From: Danielle Maywood
Date: Thu, 2 Jan 2025 10:31:25 +0000
Subject: [PATCH 07/10] chore: remove whitespace between call and return err
---
coderd/workspacebuilds.go | 1 -
1 file changed, 1 deletion(-)
diff --git a/coderd/workspacebuilds.go b/coderd/workspacebuilds.go
index 808b0ba423059..8874aa280873e 100644
--- a/coderd/workspacebuilds.go
+++ b/coderd/workspacebuilds.go
@@ -385,7 +385,6 @@ func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) {
},
audit.WorkspaceBuildBaggageFromRequest(r),
)
-
return err
}, nil)
var buildErr wsbuilder.BuildError
From 32adc00526a181a4c328164a0b927a39b23e942b Mon Sep 17 00:00:00 2001
From: Danielle Maywood
Date: Thu, 2 Jan 2025 11:21:25 +0000
Subject: [PATCH 08/10] chore: actually use the transaction, oops
---
coderd/workspacebuilds.go | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/coderd/workspacebuilds.go b/coderd/workspacebuilds.go
index 8874aa280873e..9434adc8e5c80 100644
--- a/coderd/workspacebuilds.go
+++ b/coderd/workspacebuilds.go
@@ -335,10 +335,10 @@ func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) {
provisionerDaemons []database.GetEligibleProvisionerDaemonsByProvisionerJobIDsRow
)
- err := api.Database.InTx(func(database.Store) error {
+ err := api.Database.InTx(func(tx database.Store) error {
var err error
- previousWorkspaceBuild, err = api.Database.GetLatestWorkspaceBuildByWorkspaceID(ctx, workspace.ID)
+ previousWorkspaceBuild, err = tx.GetLatestWorkspaceBuildByWorkspaceID(ctx, workspace.ID)
if err != nil && !xerrors.Is(err, sql.ErrNoRows) {
api.Logger.Error(ctx, "failed fetching previous workspace build", slog.F("workspace_id", workspace.ID), slog.Error(err))
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
@@ -379,7 +379,7 @@ func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) {
workspaceBuild, provisionerJob, provisionerDaemons, err = builder.Build(
ctx,
- api.Database,
+ tx,
func(action policy.Action, object rbac.Objecter) bool {
return api.Authorize(r, action, object)
},
From 90380f7c346cf7e5c009ce46f13fb12653330b67 Mon Sep 17 00:00:00 2001
From: Danielle Maywood
Date: Thu, 2 Jan 2025 11:37:18 +0000
Subject: [PATCH 09/10] chore: changes
---
coderd/workspacebuilds.go | 27 ++++++++++++++++-----------
1 file changed, 16 insertions(+), 11 deletions(-)
diff --git a/coderd/workspacebuilds.go b/coderd/workspacebuilds.go
index 9434adc8e5c80..cbb2a113a0c08 100644
--- a/coderd/workspacebuilds.go
+++ b/coderd/workspacebuilds.go
@@ -385,7 +385,17 @@ func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) {
},
audit.WorkspaceBuildBaggageFromRequest(r),
)
- return err
+ if err != nil {
+ return err
+ }
+
+ // If this workspace build has a different template version ID to the previous build
+ // we can assume it has just been updated.
+ if createBuild.TemplateVersionID != uuid.Nil && createBuild.TemplateVersionID != previousWorkspaceBuild.TemplateVersionID {
+ api.notifyWorkspaceUpdated(ctx, tx, apiKey.UserID, workspace, createBuild.RichParameterValues)
+ }
+
+ return nil
}, nil)
var buildErr wsbuilder.BuildError
if xerrors.As(err, &buildErr) {
@@ -443,12 +453,6 @@ func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) {
return
}
- // If this workspace build has a different template version ID to the previous build
- // we can assume it has just been updated.
- if createBuild.TemplateVersionID != uuid.Nil && createBuild.TemplateVersionID != previousWorkspaceBuild.TemplateVersionID {
- api.notifyWorkspaceUpdated(ctx, apiKey.UserID, workspace, createBuild.RichParameterValues)
- }
-
api.publishWorkspaceUpdate(ctx, workspace.OwnerID, wspubsub.WorkspaceEvent{
Kind: wspubsub.WorkspaceEventKindStateChange,
WorkspaceID: workspace.ID,
@@ -459,31 +463,32 @@ func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) {
func (api *API) notifyWorkspaceUpdated(
ctx context.Context,
+ db database.Store,
initiatorID uuid.UUID,
workspace database.Workspace,
parameters []codersdk.WorkspaceBuildParameter,
) {
log := api.Logger.With(slog.F("workspace_id", workspace.ID))
- template, err := api.Database.GetTemplateByID(ctx, workspace.TemplateID)
+ template, err := db.GetTemplateByID(ctx, workspace.TemplateID)
if err != nil {
log.Warn(ctx, "failed to fetch template for workspace creation notification", slog.F("template_id", workspace.TemplateID), slog.Error(err))
return
}
- version, err := api.Database.GetTemplateVersionByID(ctx, template.ActiveVersionID)
+ version, err := db.GetTemplateVersionByID(ctx, template.ActiveVersionID)
if err != nil {
log.Warn(ctx, "failed to fetch template version for workspace creation notification", slog.F("template_id", workspace.TemplateID), slog.Error(err))
return
}
- initiator, err := api.Database.GetUserByID(ctx, initiatorID)
+ initiator, err := db.GetUserByID(ctx, initiatorID)
if err != nil {
log.Warn(ctx, "failed to fetch user for workspace update notification", slog.F("initiator_id", initiatorID), slog.Error(err))
return
}
- owner, err := api.Database.GetUserByID(ctx, workspace.OwnerID)
+ owner, err := db.GetUserByID(ctx, workspace.OwnerID)
if err != nil {
log.Warn(ctx, "failed to fetch user for workspace update notification", slog.F("owner_id", workspace.OwnerID), slog.Error(err))
return
From 71ec1ac108339cde47aaae060fcb1249ba6b574c Mon Sep 17 00:00:00 2001
From: Danielle Maywood
Date: Thu, 2 Jan 2025 11:57:46 +0000
Subject: [PATCH 10/10] chore: move prep of builder and notify out of tx
---
coderd/workspacebuilds.go | 39 +++++++++++++++++----------------------
1 file changed, 17 insertions(+), 22 deletions(-)
diff --git a/coderd/workspacebuilds.go b/coderd/workspacebuilds.go
index cbb2a113a0c08..0a19f7dfdaf0a 100644
--- a/coderd/workspacebuilds.go
+++ b/coderd/workspacebuilds.go
@@ -328,6 +328,12 @@ func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) {
return
}
+ builder := wsbuilder.New(workspace, database.WorkspaceTransition(createBuild.Transition)).
+ Initiator(apiKey.UserID).
+ RichParameterValues(createBuild.RichParameterValues).
+ LogLevel(string(createBuild.LogLevel)).
+ DeploymentValues(api.Options.DeploymentValues)
+
var (
previousWorkspaceBuild database.WorkspaceBuild
workspaceBuild *database.WorkspaceBuild
@@ -348,12 +354,6 @@ func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) {
return nil
}
- builder := wsbuilder.New(workspace, database.WorkspaceTransition(createBuild.Transition)).
- Initiator(apiKey.UserID).
- RichParameterValues(createBuild.RichParameterValues).
- LogLevel(string(createBuild.LogLevel)).
- DeploymentValues(api.Options.DeploymentValues)
-
if createBuild.TemplateVersionID != uuid.Nil {
builder = builder.VersionID(createBuild.TemplateVersionID)
}
@@ -385,17 +385,7 @@ func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) {
},
audit.WorkspaceBuildBaggageFromRequest(r),
)
- if err != nil {
- return err
- }
-
- // If this workspace build has a different template version ID to the previous build
- // we can assume it has just been updated.
- if createBuild.TemplateVersionID != uuid.Nil && createBuild.TemplateVersionID != previousWorkspaceBuild.TemplateVersionID {
- api.notifyWorkspaceUpdated(ctx, tx, apiKey.UserID, workspace, createBuild.RichParameterValues)
- }
-
- return nil
+ return err
}, nil)
var buildErr wsbuilder.BuildError
if xerrors.As(err, &buildErr) {
@@ -453,6 +443,12 @@ func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) {
return
}
+ // If this workspace build has a different template version ID to the previous build
+ // we can assume it has just been updated.
+ if createBuild.TemplateVersionID != uuid.Nil && createBuild.TemplateVersionID != previousWorkspaceBuild.TemplateVersionID {
+ api.notifyWorkspaceUpdated(ctx, apiKey.UserID, workspace, createBuild.RichParameterValues)
+ }
+
api.publishWorkspaceUpdate(ctx, workspace.OwnerID, wspubsub.WorkspaceEvent{
Kind: wspubsub.WorkspaceEventKindStateChange,
WorkspaceID: workspace.ID,
@@ -463,32 +459,31 @@ func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) {
func (api *API) notifyWorkspaceUpdated(
ctx context.Context,
- db database.Store,
initiatorID uuid.UUID,
workspace database.Workspace,
parameters []codersdk.WorkspaceBuildParameter,
) {
log := api.Logger.With(slog.F("workspace_id", workspace.ID))
- template, err := db.GetTemplateByID(ctx, workspace.TemplateID)
+ template, err := api.Database.GetTemplateByID(ctx, workspace.TemplateID)
if err != nil {
log.Warn(ctx, "failed to fetch template for workspace creation notification", slog.F("template_id", workspace.TemplateID), slog.Error(err))
return
}
- version, err := db.GetTemplateVersionByID(ctx, template.ActiveVersionID)
+ version, err := api.Database.GetTemplateVersionByID(ctx, template.ActiveVersionID)
if err != nil {
log.Warn(ctx, "failed to fetch template version for workspace creation notification", slog.F("template_id", workspace.TemplateID), slog.Error(err))
return
}
- initiator, err := db.GetUserByID(ctx, initiatorID)
+ initiator, err := api.Database.GetUserByID(ctx, initiatorID)
if err != nil {
log.Warn(ctx, "failed to fetch user for workspace update notification", slog.F("initiator_id", initiatorID), slog.Error(err))
return
}
- owner, err := db.GetUserByID(ctx, workspace.OwnerID)
+ owner, err := api.Database.GetUserByID(ctx, workspace.OwnerID)
if err != nil {
log.Warn(ctx, "failed to fetch user for workspace update notification", slog.F("owner_id", workspace.OwnerID), slog.Error(err))
return