From b5c5aa7565705c5eff688814582ced38eb6087af Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Wed, 8 Jun 2022 10:15:45 -0500 Subject: [PATCH 1/4] feat: Add initiator_username to workspace builds in apis --- coderd/users.go | 9 +++++++ coderd/workspacebuilds.go | 49 +++++++++++++++++++++++++++++-------- coderd/workspaces.go | 10 +++++--- codersdk/workspacebuilds.go | 1 + 4 files changed, 56 insertions(+), 13 deletions(-) diff --git a/coderd/users.go b/coderd/users.go index 5ef87e7ead824..f892262e21ee6 100644 --- a/coderd/users.go +++ b/coderd/users.go @@ -912,3 +912,12 @@ func userOrganizationIDs(ctx context.Context, api *API, user database.User) ([]u member := organizationIDsByMemberIDsRows[0] return member.OrganizationIDs, nil } + +func findUser(id uuid.UUID, users []database.User) *database.User { + for _, u := range users { + if u.ID == id { + return &u + } + } + return nil +} diff --git a/coderd/workspacebuilds.go b/coderd/workspacebuilds.go index 5a2f567572f84..e6505a5395c83 100644 --- a/coderd/workspacebuilds.go +++ b/coderd/workspacebuilds.go @@ -37,7 +37,7 @@ func (api *API) workspaceBuild(rw http.ResponseWriter, r *http.Request) { return } - owner, err := api.Database.GetUserByID(r.Context(), workspace.OwnerID) + users, err := api.Database.GetUsersByIDs(r.Context(), []uuid.UUID{workspace.OwnerID, workspaceBuild.InitiatorID}) if err != nil { httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ Message: "Internal error fetching user.", @@ -46,7 +46,9 @@ func (api *API) workspaceBuild(rw http.ResponseWriter, r *http.Request) { return } - httpapi.Write(rw, http.StatusOK, convertWorkspaceBuild(owner, workspace, workspaceBuild, job)) + httpapi.Write(rw, http.StatusOK, + convertWorkspaceBuild(findUser(workspace.OwnerID, users), findUser(workspaceBuild.InitiatorID, users), + workspace, workspaceBuild, job)) } func (api *API) workspaceBuilds(rw http.ResponseWriter, r *http.Request) { @@ -128,7 +130,11 @@ func (api *API) workspaceBuilds(rw http.ResponseWriter, r *http.Request) { jobByID[job.ID.String()] = job } - owner, err := api.Database.GetUserByID(r.Context(), workspace.OwnerID) + userIDs := []uuid.UUID{workspace.OwnerID} + for _, build := range builds { + userIDs = append(userIDs, build.InitiatorID) + } + users, err := api.Database.GetUsersByIDs(r.Context(), userIDs) if err != nil { httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ Message: "Internal error fetching user.", @@ -146,7 +152,9 @@ func (api *API) workspaceBuilds(rw http.ResponseWriter, r *http.Request) { }) return } - apiBuilds = append(apiBuilds, convertWorkspaceBuild(owner, workspace, build, job)) + apiBuilds = append(apiBuilds, + convertWorkspaceBuild(findUser(workspace.OwnerID, users), findUser(build.InitiatorID, users), + workspace, build, job)) } httpapi.Write(rw, http.StatusOK, apiBuilds) @@ -185,7 +193,7 @@ func (api *API) workspaceBuildByName(rw http.ResponseWriter, r *http.Request) { }) return } - owner, err := api.Database.GetUserByID(r.Context(), workspace.OwnerID) + users, err := api.Database.GetUsersByIDs(r.Context(), []uuid.UUID{workspace.OwnerID, workspaceBuild.InitiatorID}) if err != nil { httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ Message: "Internal error getting user.", @@ -194,7 +202,9 @@ func (api *API) workspaceBuildByName(rw http.ResponseWriter, r *http.Request) { return } - httpapi.Write(rw, http.StatusOK, convertWorkspaceBuild(owner, workspace, workspaceBuild, job)) + httpapi.Write(rw, http.StatusOK, + convertWorkspaceBuild(findUser(workspace.OwnerID, users), findUser(workspaceBuild.InitiatorID, users), + workspace, workspaceBuild, job)) } func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) { @@ -368,7 +378,10 @@ func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) { return } - owner, err := api.Database.GetUserByID(r.Context(), workspace.OwnerID) + users, err := api.Database.GetUsersByIDs(r.Context(), []uuid.UUID{ + workspace.OwnerID, + workspaceBuild.InitiatorID, + }) if err != nil { httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ Message: "Internal error getting user.", @@ -378,7 +391,8 @@ func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) { } httpapi.Write(rw, http.StatusCreated, - convertWorkspaceBuild(owner, workspace, workspaceBuild, provisionerJob)) + convertWorkspaceBuild(findUser(workspace.OwnerID, users), findUser(workspaceBuild.InitiatorID, users), + workspace, workspaceBuild, provisionerJob)) } func (api *API) patchCancelWorkspaceBuild(rw http.ResponseWriter, r *http.Request) { @@ -508,7 +522,8 @@ func (api *API) workspaceBuildState(rw http.ResponseWriter, r *http.Request) { } func convertWorkspaceBuild( - workspaceOwner database.User, + workspaceOwner *database.User, + buildInitiator *database.User, workspace database.Workspace, workspaceBuild database.WorkspaceBuild, job database.ProvisionerJob) codersdk.WorkspaceBuild { @@ -516,12 +531,25 @@ func convertWorkspaceBuild( if workspace.ID != workspaceBuild.WorkspaceID { panic("workspace and build do not match") } + + // Both owner and initiator should always be present. But from a static + // code analysis POV, these could be nil. + ownerName := "unknown" + if workspaceOwner != nil { + ownerName = workspaceOwner.Username + } + + initiatorName := "unknown" + if workspaceOwner != nil { + initiatorName = buildInitiator.Username + } + return codersdk.WorkspaceBuild{ ID: workspaceBuild.ID, CreatedAt: workspaceBuild.CreatedAt, UpdatedAt: workspaceBuild.UpdatedAt, WorkspaceOwnerID: workspace.OwnerID, - WorkspaceOwnerName: workspaceOwner.Username, + WorkspaceOwnerName: ownerName, WorkspaceID: workspaceBuild.WorkspaceID, WorkspaceName: workspace.Name, TemplateVersionID: workspaceBuild.TemplateVersionID, @@ -529,6 +557,7 @@ func convertWorkspaceBuild( Name: workspaceBuild.Name, Transition: codersdk.WorkspaceTransition(workspaceBuild.Transition), InitiatorID: workspaceBuild.InitiatorID, + InitiatorUsername: initiatorName, Job: convertProvisionerJob(job), Deadline: workspaceBuild.Deadline, } diff --git a/coderd/workspaces.go b/coderd/workspaces.go index 25fd67d581cba..3374394ad8fe8 100644 --- a/coderd/workspaces.go +++ b/coderd/workspaces.go @@ -702,16 +702,20 @@ func (api *API) watchWorkspace(rw http.ResponseWriter, r *http.Request) { func convertWorkspaces(ctx context.Context, db database.Store, workspaces []database.Workspace) ([]codersdk.Workspace, error) { workspaceIDs := make([]uuid.UUID, 0, len(workspaces)) templateIDs := make([]uuid.UUID, 0, len(workspaces)) - ownerIDs := make([]uuid.UUID, 0, len(workspaces)) + userIDs := make([]uuid.UUID, 0, len(workspaces)) for _, workspace := range workspaces { workspaceIDs = append(workspaceIDs, workspace.ID) templateIDs = append(templateIDs, workspace.TemplateID) - ownerIDs = append(ownerIDs, workspace.OwnerID) + userIDs = append(userIDs, workspace.OwnerID) } workspaceBuilds, err := db.GetLatestWorkspaceBuildsByWorkspaceIDs(ctx, workspaceIDs) if errors.Is(err, sql.ErrNoRows) { err = nil } + for _, build := range workspaceBuilds { + userIDs = append(userIDs, build.InitiatorID) + } + if err != nil { return nil, xerrors.Errorf("get workspace builds: %w", err) } @@ -722,7 +726,7 @@ func convertWorkspaces(ctx context.Context, db database.Store, workspaces []data if err != nil { return nil, xerrors.Errorf("get templates: %w", err) } - users, err := db.GetUsersByIDs(ctx, ownerIDs) + users, err := db.GetUsersByIDs(ctx, userIDs) if err != nil { return nil, xerrors.Errorf("get users: %w", err) } diff --git a/codersdk/workspacebuilds.go b/codersdk/workspacebuilds.go index ccf3e917f5d63..ec5dfcbc63ccd 100644 --- a/codersdk/workspacebuilds.go +++ b/codersdk/workspacebuilds.go @@ -34,6 +34,7 @@ type WorkspaceBuild struct { Name string `json:"name"` Transition WorkspaceTransition `json:"transition"` InitiatorID uuid.UUID `json:"initiator_id"` + InitiatorUsername string `json:"initiator_name"` Job ProvisionerJob `json:"job"` Deadline time.Time `json:"deadline"` } From 187c01f2fcd86689ef60fca5aa708e4f28921253 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Wed, 8 Jun 2022 10:45:17 -0500 Subject: [PATCH 2/4] feat: Convertworkspace fixed --- coderd/workspacebuilds_test.go | 11 +++++---- coderd/workspaces.go | 44 ++++++++++++++++++++++++---------- 2 files changed, 38 insertions(+), 17 deletions(-) diff --git a/coderd/workspacebuilds_test.go b/coderd/workspacebuilds_test.go index 5b15f54c29d52..1734f52b836f9 100644 --- a/coderd/workspacebuilds_test.go +++ b/coderd/workspacebuilds_test.go @@ -33,15 +33,18 @@ func TestWorkspaceBuilds(t *testing.T) { t.Run("Single", func(t *testing.T) { t.Parallel() client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerD: true}) - user := coderdtest.CreateFirstUser(t, client) - version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil) - template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) + first := coderdtest.CreateFirstUser(t, client) + user, err := client.User(context.Background(), codersdk.Me) + require.NoError(t, err, "fetch me") + version := coderdtest.CreateTemplateVersion(t, client, first.OrganizationID, nil) + template := coderdtest.CreateTemplate(t, client, first.OrganizationID, version.ID) coderdtest.AwaitTemplateVersionJob(t, client, version.ID) - workspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID) + workspace := coderdtest.CreateWorkspace(t, client, first.OrganizationID, template.ID) builds, err := client.WorkspaceBuilds(context.Background(), codersdk.WorkspaceBuildsRequest{WorkspaceID: workspace.ID}) require.Len(t, builds, 1) require.Equal(t, int32(1), builds[0].BuildNumber) + require.Equal(t, user.Username, builds[0].InitiatorUsername) require.NoError(t, err) }) diff --git a/coderd/workspaces.go b/coderd/workspaces.go index 3374394ad8fe8..69a1a7c0dd8e0 100644 --- a/coderd/workspaces.go +++ b/coderd/workspaces.go @@ -73,7 +73,7 @@ func (api *API) workspace(rw http.ResponseWriter, r *http.Request) { group errgroup.Group job database.ProvisionerJob template database.Template - owner database.User + users []database.User ) group.Go(func() (err error) { job, err = api.Database.GetProvisionerJobByID(r.Context(), build.JobID) @@ -84,7 +84,7 @@ func (api *API) workspace(rw http.ResponseWriter, r *http.Request) { return err }) group.Go(func() (err error) { - owner, err = api.Database.GetUserByID(r.Context(), workspace.OwnerID) + users, err = api.Database.GetUsersByIDs(r.Context(), []uuid.UUID{workspace.OwnerID, build.InitiatorID}) return err }) err = group.Wait() @@ -96,7 +96,8 @@ func (api *API) workspace(rw http.ResponseWriter, r *http.Request) { return } - httpapi.Write(rw, http.StatusOK, convertWorkspace(workspace, build, job, template, owner)) + httpapi.Write(rw, http.StatusOK, convertWorkspace(workspace, build, job, template, + findUser(workspace.OwnerID, users), findUser(build.InitiatorID, users))) } // workspaces returns all workspaces a user can read. @@ -210,7 +211,16 @@ func (api *API) workspaceByOwnerAndName(rw http.ResponseWriter, r *http.Request) return } - httpapi.Write(rw, http.StatusOK, convertWorkspace(workspace, build, job, template, owner)) + initiator, err := api.Database.GetUserByID(r.Context(), build.InitiatorID) + if err != nil { + httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + Message: "Internal error fetching template.", + Detail: err.Error(), + }) + return + } + + httpapi.Write(rw, http.StatusOK, convertWorkspace(workspace, build, job, template, &owner, &initiator)) } // Create a new workspace for the currently authenticated user. @@ -443,7 +453,7 @@ func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Req }) return } - user, err := api.Database.GetUserByID(r.Context(), apiKey.UserID) + users, err := api.Database.GetUsersByIDs(r.Context(), []uuid.UUID{apiKey.UserID, workspaceBuild.InitiatorID}) if err != nil { httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ Message: "Internal error fetching user.", @@ -452,7 +462,8 @@ func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Req return } - httpapi.Write(rw, http.StatusCreated, convertWorkspace(workspace, workspaceBuild, templateVersionJob, template, user)) + httpapi.Write(rw, http.StatusCreated, convertWorkspace(workspace, workspaceBuild, templateVersionJob, template, + findUser(apiKey.UserID, users), findUser(workspaceBuild.InitiatorID, users))) } func (api *API) putWorkspaceAutostart(rw http.ResponseWriter, r *http.Request) { @@ -669,7 +680,7 @@ func (api *API) watchWorkspace(rw http.ResponseWriter, r *http.Request) { group errgroup.Group job database.ProvisionerJob template database.Template - owner database.User + users []database.User ) group.Go(func() (err error) { job, err = api.Database.GetProvisionerJobByID(r.Context(), build.JobID) @@ -680,7 +691,7 @@ func (api *API) watchWorkspace(rw http.ResponseWriter, r *http.Request) { return err }) group.Go(func() (err error) { - owner, err = api.Database.GetUserByID(r.Context(), workspace.OwnerID) + users, err = api.Database.GetUsersByIDs(r.Context(), []uuid.UUID{workspace.OwnerID, build.InitiatorID}) return err }) err = group.Wait() @@ -692,7 +703,8 @@ func (api *API) watchWorkspace(rw http.ResponseWriter, r *http.Request) { return } - _ = wsjson.Write(ctx, c, convertWorkspace(workspace, build, job, template, owner)) + _ = wsjson.Write(ctx, c, convertWorkspace(workspace, build, job, template, + findUser(workspace.OwnerID, users), findUser(build.InitiatorID, users))) case <-ctx.Done(): return } @@ -785,11 +797,15 @@ func convertWorkspaces(ctx context.Context, db database.Store, workspaces []data if !exists { return nil, xerrors.Errorf("build job not found for workspace: %w", err) } - user, exists := userByID[workspace.OwnerID] + owner, exists := userByID[workspace.OwnerID] if !exists { return nil, xerrors.Errorf("owner not found for workspace: %q", workspace.Name) } - apiWorkspaces = append(apiWorkspaces, convertWorkspace(workspace, build, job, template, user)) + initiator, exists := userByID[build.InitiatorID] + if !exists { + return nil, xerrors.Errorf("build initiator not found for workspace: %q", workspace.Name) + } + apiWorkspaces = append(apiWorkspaces, convertWorkspace(workspace, build, job, template, &owner, &initiator)) } return apiWorkspaces, nil } @@ -798,7 +814,9 @@ func convertWorkspace( workspaceBuild database.WorkspaceBuild, job database.ProvisionerJob, template database.Template, - owner database.User) codersdk.Workspace { + owner *database.User, + initiator *database.User, +) codersdk.Workspace { var autostartSchedule *string if workspace.AutostartSchedule.Valid { autostartSchedule = &workspace.AutostartSchedule.String @@ -812,7 +830,7 @@ func convertWorkspace( OwnerID: workspace.OwnerID, OwnerName: owner.Username, TemplateID: workspace.TemplateID, - LatestBuild: convertWorkspaceBuild(owner, workspace, workspaceBuild, job), + LatestBuild: convertWorkspaceBuild(owner, initiator, workspace, workspaceBuild, job), TemplateName: template.Name, Outdated: workspaceBuild.TemplateVersionID.String() != template.ActiveVersionID.String(), Name: workspace.Name, From 6bd8b29637078f8b63dc6eb1ec36b6d1f1fd43ae Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Wed, 8 Jun 2022 11:15:40 -0500 Subject: [PATCH 3/4] Make gen --- site/src/api/typesGenerated.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index 05432f7e90b1a..810135f8b7ff6 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -454,6 +454,7 @@ export interface WorkspaceBuild { readonly name: string readonly transition: WorkspaceTransition readonly initiator_id: string + readonly initiator_name: string readonly job: ProvisionerJob readonly deadline: string } From 9f5515fc6d0cac340e2d6ddf740317e36271ffb4 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Wed, 8 Jun 2022 11:23:06 -0500 Subject: [PATCH 4/4] Update mock type --- site/src/testHelpers/entities.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/site/src/testHelpers/entities.ts b/site/src/testHelpers/entities.ts index c58097d01d9be..53414283e4cc9 100644 --- a/site/src/testHelpers/entities.ts +++ b/site/src/testHelpers/entities.ts @@ -131,6 +131,7 @@ export const MockWorkspaceBuild: TypesGen.WorkspaceBuild = { created_at: "2022-05-17T17:39:01.382927298Z", id: "1", initiator_id: "", + initiator_name: "", job: MockProvisionerJob, name: "a-workspace-build", template_version_id: "", 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