mirror of
https://github.com/coder/coder.git
synced 2026-09-22 05:05:20 +08:00
feat: show template.display_name on Workspace pages (#5082)
* feat: expose template.display_name via Workspaces endpoint * Fix: MockWorkspace * UI: Workspace stats and row * Show template.display_name on pages * Fix: address PR comments * Add helper function: getDisplayWorkspaceTemplateName
This commit is contained in:
+15
-14
@@ -1011,20 +1011,21 @@ func convertWorkspace(
|
||||
|
||||
ttlMillis := convertWorkspaceTTLMillis(workspace.Ttl)
|
||||
return codersdk.Workspace{
|
||||
ID: workspace.ID,
|
||||
CreatedAt: workspace.CreatedAt,
|
||||
UpdatedAt: workspace.UpdatedAt,
|
||||
OwnerID: workspace.OwnerID,
|
||||
OwnerName: owner.Username,
|
||||
TemplateID: workspace.TemplateID,
|
||||
LatestBuild: workspaceBuild,
|
||||
TemplateName: template.Name,
|
||||
TemplateIcon: template.Icon,
|
||||
Outdated: workspaceBuild.TemplateVersionID.String() != template.ActiveVersionID.String(),
|
||||
Name: workspace.Name,
|
||||
AutostartSchedule: autostartSchedule,
|
||||
TTLMillis: ttlMillis,
|
||||
LastUsedAt: workspace.LastUsedAt,
|
||||
ID: workspace.ID,
|
||||
CreatedAt: workspace.CreatedAt,
|
||||
UpdatedAt: workspace.UpdatedAt,
|
||||
OwnerID: workspace.OwnerID,
|
||||
OwnerName: owner.Username,
|
||||
TemplateID: workspace.TemplateID,
|
||||
LatestBuild: workspaceBuild,
|
||||
TemplateName: template.Name,
|
||||
TemplateIcon: template.Icon,
|
||||
TemplateDisplayName: template.DisplayName,
|
||||
Outdated: workspaceBuild.TemplateVersionID.String() != template.ActiveVersionID.String(),
|
||||
Name: workspace.Name,
|
||||
AutostartSchedule: autostartSchedule,
|
||||
TTLMillis: ttlMillis,
|
||||
LastUsedAt: workspace.LastUsedAt,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -115,6 +115,36 @@ func TestWorkspace(t *testing.T) {
|
||||
})
|
||||
require.Error(t, err, "workspace rename should have failed")
|
||||
})
|
||||
|
||||
t.Run("TemplateProperties", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
|
||||
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
||||
|
||||
const templateIcon = "/img/icon.svg"
|
||||
const templateDisplayName = "This is template"
|
||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) {
|
||||
ctr.Icon = templateIcon
|
||||
ctr.DisplayName = templateDisplayName
|
||||
})
|
||||
require.NotEmpty(t, template.Name)
|
||||
require.NotEmpty(t, template.DisplayName)
|
||||
require.NotEmpty(t, template.Icon)
|
||||
workspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
|
||||
defer cancel()
|
||||
|
||||
ws, err := client.Workspace(ctx, workspace.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, user.UserID, ws.LatestBuild.InitiatorID)
|
||||
assert.Equal(t, codersdk.BuildReasonInitiator, ws.LatestBuild.Reason)
|
||||
assert.Equal(t, template.Name, ws.TemplateName)
|
||||
assert.Equal(t, templateIcon, ws.TemplateIcon)
|
||||
assert.Equal(t, templateDisplayName, ws.TemplateDisplayName)
|
||||
})
|
||||
}
|
||||
|
||||
func TestAdminViewAllWorkspaces(t *testing.T) {
|
||||
|
||||
+15
-14
@@ -17,20 +17,21 @@ import (
|
||||
// Workspace is a deployment of a template. It references a specific
|
||||
// version and can be updated.
|
||||
type Workspace struct {
|
||||
ID uuid.UUID `json:"id"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
OwnerID uuid.UUID `json:"owner_id"`
|
||||
OwnerName string `json:"owner_name"`
|
||||
TemplateID uuid.UUID `json:"template_id"`
|
||||
TemplateName string `json:"template_name"`
|
||||
TemplateIcon string `json:"template_icon"`
|
||||
LatestBuild WorkspaceBuild `json:"latest_build"`
|
||||
Outdated bool `json:"outdated"`
|
||||
Name string `json:"name"`
|
||||
AutostartSchedule *string `json:"autostart_schedule,omitempty"`
|
||||
TTLMillis *int64 `json:"ttl_ms,omitempty"`
|
||||
LastUsedAt time.Time `json:"last_used_at"`
|
||||
ID uuid.UUID `json:"id"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
OwnerID uuid.UUID `json:"owner_id"`
|
||||
OwnerName string `json:"owner_name"`
|
||||
TemplateID uuid.UUID `json:"template_id"`
|
||||
TemplateName string `json:"template_name"`
|
||||
TemplateDisplayName string `json:"template_display_name"`
|
||||
TemplateIcon string `json:"template_icon"`
|
||||
LatestBuild WorkspaceBuild `json:"latest_build"`
|
||||
Outdated bool `json:"outdated"`
|
||||
Name string `json:"name"`
|
||||
AutostartSchedule *string `json:"autostart_schedule,omitempty"`
|
||||
TTLMillis *int64 `json:"ttl_ms,omitempty"`
|
||||
LastUsedAt time.Time `json:"last_used_at"`
|
||||
}
|
||||
|
||||
type WorkspacesRequest struct {
|
||||
|
||||
@@ -788,6 +788,7 @@ export interface Workspace {
|
||||
readonly owner_name: string
|
||||
readonly template_id: string
|
||||
readonly template_name: string
|
||||
readonly template_display_name: string
|
||||
readonly template_icon: string
|
||||
readonly latest_build: WorkspaceBuild
|
||||
readonly outdated: boolean
|
||||
|
||||
@@ -5,7 +5,10 @@ import { FC } from "react"
|
||||
import { Link as RouterLink } from "react-router-dom"
|
||||
import { combineClasses } from "util/combineClasses"
|
||||
import { createDayString } from "util/createDayString"
|
||||
import { getDisplayWorkspaceBuildInitiatedBy } from "util/workspace"
|
||||
import {
|
||||
getDisplayWorkspaceBuildInitiatedBy,
|
||||
getDisplayWorkspaceTemplateName,
|
||||
} from "util/workspace"
|
||||
import { Workspace } from "../../api/typesGenerated"
|
||||
|
||||
const Language = {
|
||||
@@ -36,6 +39,7 @@ export const WorkspaceStats: FC<WorkspaceStatsProps> = ({
|
||||
const initiatedBy = getDisplayWorkspaceBuildInitiatedBy(
|
||||
workspace.latest_build,
|
||||
)
|
||||
const displayTemplateName = getDisplayWorkspaceTemplateName(workspace)
|
||||
|
||||
return (
|
||||
<div className={styles.stats} aria-label={Language.workspaceDetails}>
|
||||
@@ -46,7 +50,7 @@ export const WorkspaceStats: FC<WorkspaceStatsProps> = ({
|
||||
to={`/templates/${workspace.template_name}`}
|
||||
className={combineClasses([styles.statsValue, styles.link])}
|
||||
>
|
||||
{workspace.template_name}
|
||||
{displayTemplateName}
|
||||
</Link>
|
||||
</div>
|
||||
<div className={styles.statItem}>
|
||||
|
||||
@@ -7,6 +7,7 @@ import { AvatarData } from "components/AvatarData/AvatarData"
|
||||
import { WorkspaceStatusBadge } from "components/WorkspaceStatusBadge/WorkspaceStatusBadge"
|
||||
import { FC } from "react"
|
||||
import { useNavigate } from "react-router-dom"
|
||||
import { getDisplayWorkspaceTemplateName } from "util/workspace"
|
||||
import { WorkspaceItemMachineRef } from "../../xServices/workspaces/workspacesXService"
|
||||
import { LastUsed } from "../LastUsed/LastUsed"
|
||||
import {
|
||||
@@ -32,6 +33,7 @@ export const WorkspacesRow: FC<
|
||||
const workspacePageLink = `/@${workspace.owner_name}/${workspace.name}`
|
||||
const hasTemplateIcon =
|
||||
workspace.template_icon && workspace.template_icon !== ""
|
||||
const displayTemplateName = getDisplayWorkspaceTemplateName(workspace)
|
||||
|
||||
return (
|
||||
<TableRow
|
||||
@@ -61,7 +63,7 @@ export const WorkspacesRow: FC<
|
||||
</TableCellLink>
|
||||
|
||||
<TableCellLink to={workspacePageLink}>
|
||||
<TableCellDataPrimary>{workspace.template_name}</TableCellDataPrimary>
|
||||
<TableCellDataPrimary>{displayTemplateName}</TableCellDataPrimary>
|
||||
</TableCellLink>
|
||||
<TableCellLink to={workspacePageLink}>
|
||||
<TableCellData>
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import { screen, waitFor } from "@testing-library/react"
|
||||
import { rest } from "msw"
|
||||
import * as CreateDayString from "util/createDayString"
|
||||
import { MockWorkspace } from "../../testHelpers/entities"
|
||||
import {
|
||||
MockWorkspace,
|
||||
MockWorkspacesResponse,
|
||||
} from "../../testHelpers/entities"
|
||||
import { history, render } from "../../testHelpers/renderHelpers"
|
||||
import { server } from "../../testHelpers/server"
|
||||
import WorkspacesPage from "./WorkspacesPage"
|
||||
@@ -54,5 +57,9 @@ describe("WorkspacesPage", () => {
|
||||
{ timeout: 2000 },
|
||||
)
|
||||
await screen.findByText(`${MockWorkspace.name}1`)
|
||||
const templateDisplayNames = await screen.findAllByText(
|
||||
`${MockWorkspace.template_display_name}`,
|
||||
)
|
||||
expect(templateDisplayNames).toHaveLength(MockWorkspacesResponse.count)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -434,6 +434,7 @@ export const MockWorkspace: TypesGen.Workspace = {
|
||||
template_id: MockTemplate.id,
|
||||
template_name: MockTemplate.name,
|
||||
template_icon: MockTemplate.icon,
|
||||
template_display_name: MockTemplate.display_name,
|
||||
outdated: false,
|
||||
owner_id: MockUser.id,
|
||||
owner_name: MockUser.username,
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
defaultWorkspaceExtension,
|
||||
getDisplayVersionStatus,
|
||||
getDisplayWorkspaceBuildInitiatedBy,
|
||||
getDisplayWorkspaceTemplateName,
|
||||
isWorkspaceOn,
|
||||
} from "./workspace"
|
||||
|
||||
@@ -120,4 +121,22 @@ describe("util > workspace", () => {
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
describe("getDisplayWorkspaceTemplateName", () => {
|
||||
it("display name is not set", async () => {
|
||||
const workspace: TypesGen.Workspace = {
|
||||
...Mocks.MockWorkspace,
|
||||
template_display_name: "",
|
||||
}
|
||||
const displayed = getDisplayWorkspaceTemplateName(workspace)
|
||||
expect(displayed).toEqual(workspace.template_name)
|
||||
})
|
||||
it("display name is set", async () => {
|
||||
const workspace: TypesGen.Workspace = {
|
||||
...Mocks.MockWorkspace,
|
||||
}
|
||||
const displayed = getDisplayWorkspaceTemplateName(workspace)
|
||||
expect(displayed).toEqual(workspace.template_display_name)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -182,3 +182,11 @@ export const getFaviconByStatus = (
|
||||
return "favicon"
|
||||
}
|
||||
}
|
||||
|
||||
export const getDisplayWorkspaceTemplateName = (
|
||||
workspace: TypesGen.Workspace,
|
||||
): string => {
|
||||
return workspace.template_display_name.length > 0
|
||||
? workspace.template_display_name
|
||||
: workspace.template_name
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user