mirror of https://github.com/coder/coder.git
feat: Add initiator_username to workspace builds in apis (#2174)
* feat: Add initiator_username to workspace builds in apis
This commit is contained in:
parent
14701498c9
commit
74fe38eb3d
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
})
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
@ -232,7 +233,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.
|
||||
|
@ -465,7 +475,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.",
|
||||
|
@ -474,7 +484,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) {
|
||||
|
@ -691,7 +702,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)
|
||||
|
@ -702,7 +713,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()
|
||||
|
@ -714,7 +725,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
|
||||
}
|
||||
|
@ -724,16 +736,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)
|
||||
}
|
||||
|
@ -744,7 +760,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)
|
||||
}
|
||||
|
@ -803,11 +819,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
|
||||
}
|
||||
|
@ -816,7 +836,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
|
||||
|
@ -830,7 +852,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,
|
||||
|
|
|
@ -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"`
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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: "",
|
||||
|
|
Loading…
Reference in New Issue