diff --git a/coderd/database/databasefake/databasefake.go b/coderd/database/databasefake/databasefake.go index 72441d83e8..a91e2d1962 100644 --- a/coderd/database/databasefake/databasefake.go +++ b/coderd/database/databasefake/databasefake.go @@ -1684,6 +1684,26 @@ func (q *fakeQuerier) GetTemplateVersionByID(_ context.Context, templateVersionI return database.TemplateVersion{}, sql.ErrNoRows } +func (q *fakeQuerier) GetTemplateVersionsByIDs(_ context.Context, ids []uuid.UUID) ([]database.TemplateVersion, error) { + q.mutex.RLock() + defer q.mutex.RUnlock() + + versions := make([]database.TemplateVersion, 0) + for _, version := range q.templateVersions { + for _, id := range ids { + if id == version.ID { + versions = append(versions, version) + break + } + } + } + if len(versions) == 0 { + return nil, sql.ErrNoRows + } + + return versions, nil +} + func (q *fakeQuerier) GetTemplateVersionByJobID(_ context.Context, jobID uuid.UUID) (database.TemplateVersion, error) { q.mutex.RLock() defer q.mutex.RUnlock() diff --git a/coderd/database/querier.go b/coderd/database/querier.go index 01a9d6ee22..2f03e2533e 100644 --- a/coderd/database/querier.go +++ b/coderd/database/querier.go @@ -82,6 +82,7 @@ type sqlcQuerier interface { GetTemplateVersionByJobID(ctx context.Context, jobID uuid.UUID) (TemplateVersion, error) GetTemplateVersionByOrganizationAndName(ctx context.Context, arg GetTemplateVersionByOrganizationAndNameParams) (TemplateVersion, error) GetTemplateVersionByTemplateIDAndName(ctx context.Context, arg GetTemplateVersionByTemplateIDAndNameParams) (TemplateVersion, error) + GetTemplateVersionsByIDs(ctx context.Context, ids []uuid.UUID) ([]TemplateVersion, error) GetTemplateVersionsByTemplateID(ctx context.Context, arg GetTemplateVersionsByTemplateIDParams) ([]TemplateVersion, error) GetTemplateVersionsCreatedAfter(ctx context.Context, createdAt time.Time) ([]TemplateVersion, error) GetTemplates(ctx context.Context) ([]Template, error) diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go index 2d4656becc..e7b5459c9c 100644 --- a/coderd/database/queries.sql.go +++ b/coderd/database/queries.sql.go @@ -3596,6 +3596,48 @@ func (q *sqlQuerier) GetTemplateVersionByTemplateIDAndName(ctx context.Context, return i, err } +const getTemplateVersionsByIDs = `-- name: GetTemplateVersionsByIDs :many +SELECT + id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by +FROM + template_versions +WHERE + id = ANY($1 :: uuid [ ]) +` + +func (q *sqlQuerier) GetTemplateVersionsByIDs(ctx context.Context, ids []uuid.UUID) ([]TemplateVersion, error) { + rows, err := q.db.QueryContext(ctx, getTemplateVersionsByIDs, pq.Array(ids)) + if err != nil { + return nil, err + } + defer rows.Close() + var items []TemplateVersion + for rows.Next() { + var i TemplateVersion + if err := rows.Scan( + &i.ID, + &i.TemplateID, + &i.OrganizationID, + &i.CreatedAt, + &i.UpdatedAt, + &i.Name, + &i.Readme, + &i.JobID, + &i.CreatedBy, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + const getTemplateVersionsByTemplateID = `-- name: GetTemplateVersionsByTemplateID :many SELECT id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by diff --git a/coderd/database/queries/templateversions.sql b/coderd/database/queries/templateversions.sql index 292b1343d7..aad417b5b3 100644 --- a/coderd/database/queries/templateversions.sql +++ b/coderd/database/queries/templateversions.sql @@ -69,6 +69,14 @@ FROM WHERE id = $1; +-- name: GetTemplateVersionsByIDs :many +SELECT + * +FROM + template_versions +WHERE + id = ANY(@ids :: uuid [ ]); + -- name: InsertTemplateVersion :one INSERT INTO template_versions ( diff --git a/coderd/workspacebuilds.go b/coderd/workspacebuilds.go index c00432f8ce..5f94485109 100644 --- a/coderd/workspacebuilds.go +++ b/coderd/workspacebuilds.go @@ -51,6 +51,7 @@ func (api *API) workspaceBuild(rw http.ResponseWriter, r *http.Request) { data.metadata, data.agents, data.apps, + data.templateVersions[0], ) if err != nil { httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ @@ -157,6 +158,7 @@ func (api *API) workspaceBuilds(rw http.ResponseWriter, r *http.Request) { data.metadata, data.agents, data.apps, + data.templateVersions, ) if err != nil { httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ @@ -239,6 +241,7 @@ func (api *API) workspaceBuildByBuildNumber(rw http.ResponseWriter, r *http.Requ data.metadata, data.agents, data.apps, + data.templateVersions[0], ) if err != nil { httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ @@ -517,6 +520,7 @@ func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) { []database.WorkspaceResourceMetadatum{}, []database.WorkspaceAgent{}, []database.WorkspaceApp{}, + database.TemplateVersion{}, ) if err != nil { httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ @@ -702,12 +706,13 @@ func (api *API) workspaceBuildState(rw http.ResponseWriter, r *http.Request) { } type workspaceBuildsData struct { - users []database.User - jobs []database.ProvisionerJob - resources []database.WorkspaceResource - metadata []database.WorkspaceResourceMetadatum - agents []database.WorkspaceAgent - apps []database.WorkspaceApp + users []database.User + jobs []database.ProvisionerJob + templateVersions []database.TemplateVersion + resources []database.WorkspaceResource + metadata []database.WorkspaceResourceMetadatum + agents []database.WorkspaceAgent + apps []database.WorkspaceApp } func (api *API) workspaceBuildsData(ctx context.Context, workspaces []database.Workspace, workspaceBuilds []database.WorkspaceBuild) (workspaceBuildsData, error) { @@ -732,6 +737,15 @@ func (api *API) workspaceBuildsData(ctx context.Context, workspaces []database.W return workspaceBuildsData{}, xerrors.Errorf("get provisioner jobs: %w", err) } + templateVersionIDs := make([]uuid.UUID, 0, len(workspaceBuilds)) + for _, build := range workspaceBuilds { + templateVersionIDs = append(templateVersionIDs, build.TemplateVersionID) + } + templateVersions, err := api.Database.GetTemplateVersionsByIDs(ctx, templateVersionIDs) + if err != nil && !errors.Is(err, sql.ErrNoRows) { + return workspaceBuildsData{}, xerrors.Errorf("get template versions: %w", err) + } + resources, err := api.Database.GetWorkspaceResourcesByJobIDs(ctx, jobIDs) if err != nil && !errors.Is(err, sql.ErrNoRows) { return workspaceBuildsData{}, xerrors.Errorf("get workspace resources by job: %w", err) @@ -739,8 +753,9 @@ func (api *API) workspaceBuildsData(ctx context.Context, workspaces []database.W if len(resources) == 0 { return workspaceBuildsData{ - users: users, - jobs: jobs, + users: users, + jobs: jobs, + templateVersions: templateVersions, }, nil } @@ -761,10 +776,11 @@ func (api *API) workspaceBuildsData(ctx context.Context, workspaces []database.W if len(resources) == 0 { return workspaceBuildsData{ - users: users, - jobs: jobs, - resources: resources, - metadata: metadata, + users: users, + jobs: jobs, + templateVersions: templateVersions, + resources: resources, + metadata: metadata, }, nil } @@ -779,12 +795,13 @@ func (api *API) workspaceBuildsData(ctx context.Context, workspaces []database.W } return workspaceBuildsData{ - users: users, - jobs: jobs, - resources: resources, - metadata: metadata, - agents: agents, - apps: apps, + users: users, + jobs: jobs, + templateVersions: templateVersions, + resources: resources, + metadata: metadata, + agents: agents, + apps: apps, }, nil } @@ -797,6 +814,7 @@ func (api *API) convertWorkspaceBuilds( resourceMetadata []database.WorkspaceResourceMetadatum, resourceAgents []database.WorkspaceAgent, agentApps []database.WorkspaceApp, + templateVersions []database.TemplateVersion, ) ([]codersdk.WorkspaceBuild, error) { workspaceByID := map[uuid.UUID]database.Workspace{} for _, workspace := range workspaces { @@ -806,6 +824,10 @@ func (api *API) convertWorkspaceBuilds( for _, job := range jobs { jobByID[job.ID] = job } + templateVersionByID := map[uuid.UUID]database.TemplateVersion{} + for _, templateVersion := range templateVersions { + templateVersionByID[templateVersion.ID] = templateVersion + } var apiBuilds []codersdk.WorkspaceBuild for _, build := range workspaceBuilds { @@ -817,6 +839,10 @@ func (api *API) convertWorkspaceBuilds( if !exists { return nil, xerrors.New("workspace not found") } + templateVersion, exists := templateVersionByID[build.TemplateVersionID] + if !exists { + return nil, xerrors.New("template version not found") + } apiBuild, err := api.convertWorkspaceBuild( build, @@ -827,6 +853,7 @@ func (api *API) convertWorkspaceBuilds( resourceMetadata, resourceAgents, agentApps, + templateVersion, ) if err != nil { return nil, xerrors.Errorf("converting workspace build: %w", err) @@ -847,6 +874,7 @@ func (api *API) convertWorkspaceBuild( resourceMetadata []database.WorkspaceResourceMetadatum, resourceAgents []database.WorkspaceAgent, agentApps []database.WorkspaceApp, + templateVersion database.TemplateVersion, ) (codersdk.WorkspaceBuild, error) { userByID := map[uuid.UUID]database.User{} for _, user := range users { @@ -897,24 +925,25 @@ func (api *API) convertWorkspaceBuild( apiJob := convertProvisionerJob(job) transition := codersdk.WorkspaceTransition(build.Transition) return codersdk.WorkspaceBuild{ - ID: build.ID, - CreatedAt: build.CreatedAt, - UpdatedAt: build.UpdatedAt, - WorkspaceOwnerID: workspace.OwnerID, - WorkspaceOwnerName: owner.Username, - WorkspaceID: build.WorkspaceID, - WorkspaceName: workspace.Name, - TemplateVersionID: build.TemplateVersionID, - BuildNumber: build.BuildNumber, - Transition: transition, - InitiatorID: build.InitiatorID, - InitiatorUsername: initiator.Username, - Job: apiJob, - Deadline: codersdk.NewNullTime(build.Deadline, !build.Deadline.IsZero()), - Reason: codersdk.BuildReason(build.Reason), - Resources: apiResources, - Status: convertWorkspaceStatus(apiJob.Status, transition), - DailyCost: build.DailyCost, + ID: build.ID, + CreatedAt: build.CreatedAt, + UpdatedAt: build.UpdatedAt, + WorkspaceOwnerID: workspace.OwnerID, + WorkspaceOwnerName: owner.Username, + WorkspaceID: build.WorkspaceID, + WorkspaceName: workspace.Name, + TemplateVersionID: build.TemplateVersionID, + TemplateVersionName: templateVersion.Name, + BuildNumber: build.BuildNumber, + Transition: transition, + InitiatorID: build.InitiatorID, + InitiatorUsername: initiator.Username, + Job: apiJob, + Deadline: codersdk.NewNullTime(build.Deadline, !build.Deadline.IsZero()), + Reason: codersdk.BuildReason(build.Reason), + Resources: apiResources, + Status: convertWorkspaceStatus(apiJob.Status, transition), + DailyCost: build.DailyCost, }, nil } diff --git a/coderd/workspaces.go b/coderd/workspaces.go index e142a0cacd..d46b0d7d2c 100644 --- a/coderd/workspaces.go +++ b/coderd/workspaces.go @@ -493,6 +493,7 @@ func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Req []database.WorkspaceResourceMetadatum{}, []database.WorkspaceAgent{}, []database.WorkspaceApp{}, + database.TemplateVersion{}, ) if err != nil { httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ @@ -932,6 +933,7 @@ func (api *API) workspaceData(ctx context.Context, workspaces []database.Workspa data.metadata, data.agents, data.apps, + data.templateVersions, ) if err != nil { return workspaceData{}, xerrors.Errorf("convert workspace builds: %w", err) diff --git a/codersdk/workspacebuilds.go b/codersdk/workspacebuilds.go index c98ed4c3ee..4192403246 100644 --- a/codersdk/workspacebuilds.go +++ b/codersdk/workspacebuilds.go @@ -51,24 +51,25 @@ const ( // WorkspaceBuild is an at-point representation of a workspace state. // BuildNumbers start at 1 and increase by 1 for each subsequent build type WorkspaceBuild struct { - ID uuid.UUID `json:"id"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - WorkspaceID uuid.UUID `json:"workspace_id"` - WorkspaceName string `json:"workspace_name"` - WorkspaceOwnerID uuid.UUID `json:"workspace_owner_id"` - WorkspaceOwnerName string `json:"workspace_owner_name"` - TemplateVersionID uuid.UUID `json:"template_version_id"` - BuildNumber int32 `json:"build_number"` - Transition WorkspaceTransition `json:"transition"` - InitiatorID uuid.UUID `json:"initiator_id"` - InitiatorUsername string `json:"initiator_name"` - Job ProvisionerJob `json:"job"` - Reason BuildReason `db:"reason" json:"reason"` - Resources []WorkspaceResource `json:"resources"` - Deadline NullTime `json:"deadline,omitempty"` - Status WorkspaceStatus `json:"status"` - DailyCost int32 `json:"daily_cost"` + ID uuid.UUID `json:"id"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + WorkspaceID uuid.UUID `json:"workspace_id"` + WorkspaceName string `json:"workspace_name"` + WorkspaceOwnerID uuid.UUID `json:"workspace_owner_id"` + WorkspaceOwnerName string `json:"workspace_owner_name"` + TemplateVersionID uuid.UUID `json:"template_version_id"` + TemplateVersionName string `json:"template_version_name"` + BuildNumber int32 `json:"build_number"` + Transition WorkspaceTransition `json:"transition"` + InitiatorID uuid.UUID `json:"initiator_id"` + InitiatorUsername string `json:"initiator_name"` + Job ProvisionerJob `json:"job"` + Reason BuildReason `db:"reason" json:"reason"` + Resources []WorkspaceResource `json:"resources"` + Deadline NullTime `json:"deadline,omitempty"` + Status WorkspaceStatus `json:"status"` + DailyCost int32 `json:"daily_cost"` } type WorkspaceResource struct { diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index ed54708059..4d54790b2c 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -879,6 +879,7 @@ export interface WorkspaceBuild { readonly workspace_owner_id: string readonly workspace_owner_name: string readonly template_version_id: string + readonly template_version_name: string readonly build_number: number readonly transition: WorkspaceTransition readonly initiator_id: string diff --git a/site/src/testHelpers/entities.ts b/site/src/testHelpers/entities.ts index 327b274798..abf89a9982 100644 --- a/site/src/testHelpers/entities.ts +++ b/site/src/testHelpers/entities.ts @@ -401,6 +401,7 @@ export const MockWorkspaceBuild: TypesGen.WorkspaceBuild = { initiator_name: MockUser.username, job: MockProvisionerJob, template_version_id: MockTemplateVersion.id, + template_version_name: MockTemplateVersion.name, transition: "start", updated_at: "2022-05-17T17:39:01.382927298Z", workspace_name: "test-workspace", @@ -424,6 +425,7 @@ export const MockFailedWorkspaceBuild = ( initiator_name: MockUser.username, job: MockFailedProvisionerJob, template_version_id: MockTemplateVersion.id, + template_version_name: MockTemplateVersion.name, transition: transition, updated_at: "2022-05-17T17:39:01.382927298Z", workspace_name: "test-workspace",