diff --git a/codersdk/workspaceapps.go b/codersdk/workspaceapps.go
index a55db1911101e..3b3200616a0f3 100644
--- a/codersdk/workspaceapps.go
+++ b/codersdk/workspaceapps.go
@@ -60,14 +60,14 @@ type WorkspaceApp struct {
ID uuid.UUID `json:"id" format:"uuid"`
// URL is the address being proxied to inside the workspace.
// If external is specified, this will be opened on the client.
- URL string `json:"url"`
+ URL string `json:"url,omitempty"`
// External specifies whether the URL should be opened externally on
// the client or not.
External bool `json:"external"`
// Slug is a unique identifier within the agent.
Slug string `json:"slug"`
// DisplayName is a friendly name for the app.
- DisplayName string `json:"display_name"`
+ DisplayName string `json:"display_name,omitempty"`
Command string `json:"command,omitempty"`
// Icon is a relative path or external URL that specifies
// an icon to be displayed in the dashboard.
@@ -81,7 +81,7 @@ type WorkspaceApp struct {
SubdomainName string `json:"subdomain_name,omitempty"`
SharingLevel WorkspaceAppSharingLevel `json:"sharing_level" enums:"owner,authenticated,public"`
// Healthcheck specifies the configuration for checking app health.
- Healthcheck Healthcheck `json:"healthcheck"`
+ Healthcheck Healthcheck `json:"healthcheck,omitempty"`
Health WorkspaceAppHealth `json:"health"`
Hidden bool `json:"hidden"`
OpenIn WorkspaceAppOpenIn `json:"open_in"`
diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts
index b1fcb296de4e8..d195432f019c4 100644
--- a/site/src/api/typesGenerated.ts
+++ b/site/src/api/typesGenerated.ts
@@ -3474,16 +3474,16 @@ export const WorkspaceAgentStatuses: WorkspaceAgentStatus[] = [
// From codersdk/workspaceapps.go
export interface WorkspaceApp {
readonly id: string;
- readonly url: string;
+ readonly url?: string;
readonly external: boolean;
readonly slug: string;
- readonly display_name: string;
+ readonly display_name?: string;
readonly command?: string;
readonly icon?: string;
readonly subdomain: boolean;
readonly subdomain_name?: string;
readonly sharing_level: WorkspaceAppSharingLevel;
- readonly healthcheck: Healthcheck;
+ readonly healthcheck?: Healthcheck;
readonly health: WorkspaceAppHealth;
readonly hidden: boolean;
readonly open_in: WorkspaceAppOpenIn;
diff --git a/site/src/modules/resources/AgentRow.test.tsx b/site/src/modules/resources/AgentRow.test.tsx
index a0a2d37d2bab0..55be57bbc2c2b 100644
--- a/site/src/modules/resources/AgentRow.test.tsx
+++ b/site/src/modules/resources/AgentRow.test.tsx
@@ -150,9 +150,9 @@ describe.each<{
for (const app of props.agent.apps) {
if (app.hidden) {
- expect(screen.queryByText(app.display_name)).toBeNull();
+ expect(screen.queryByText(app.display_name as string)).toBeNull();
} else {
- expect(screen.getByText(app.display_name)).toBeVisible();
+ expect(screen.getByText(app.display_name as string)).toBeVisible();
}
}
});
diff --git a/site/src/modules/resources/AgentRowPreview.test.tsx b/site/src/modules/resources/AgentRowPreview.test.tsx
index 222e2b22ac9f8..c1b876b72ef3b 100644
--- a/site/src/modules/resources/AgentRowPreview.test.tsx
+++ b/site/src/modules/resources/AgentRowPreview.test.tsx
@@ -91,8 +91,10 @@ describe("AgentRowPreviewApps", () => {
" displays appropriately",
({ workspaceAgent }) => {
renderComponent();
- for (const module of workspaceAgent.apps) {
- expect(screen.getByText(module.display_name)).toBeInTheDocument();
+ for (const app of workspaceAgent.apps) {
+ expect(
+ screen.getByText(app.display_name as string),
+ ).toBeInTheDocument();
}
for (const app of workspaceAgent.display_apps) {
diff --git a/site/src/modules/resources/AgentRowPreview.tsx b/site/src/modules/resources/AgentRowPreview.tsx
index cace23e31b34c..eaccb5adca4fb 100644
--- a/site/src/modules/resources/AgentRowPreview.tsx
+++ b/site/src/modules/resources/AgentRowPreview.tsx
@@ -31,7 +31,7 @@ export const AgentRowPreview: FC = ({
>
= ({ app, workspace, agent }) => {
const appsHost = proxy.preferredWildcardHostname;
const [fetchingSessionToken, setFetchingSessionToken] = useState(false);
const [iconError, setIconError] = useState(false);
-
const theme = useTheme();
const username = workspace.owner_name;
-
- let appSlug = app.slug;
- let appDisplayName = app.display_name;
- if (!appSlug) {
- appSlug = appDisplayName;
- }
- if (!appDisplayName) {
- appDisplayName = appSlug;
- }
+ const displayName = app.display_name || app.slug;
const href = createAppLinkHref(
window.location.protocol,
preferredPathBase,
appsHost,
- appSlug,
+ app.slug,
username,
workspace,
agent,
@@ -118,7 +109,7 @@ export const AppLink: FC = ({ app, workspace, agent }) => {
// This is an external URI like "vscode://", so
// it needs to be opened with the browser protocol handler.
const shouldOpenAppExternally =
- app.external && !app.url.startsWith("http");
+ app.external && app.url?.startsWith("http");
if (shouldOpenAppExternally) {
// This is a magic undocumented string that is replaced
@@ -140,9 +131,7 @@ export const AppLink: FC = ({ app, workspace, agent }) => {
// an error message will be displayed.
const openAppExternallyFailedTimeout = 500;
const openAppExternallyFailed = setTimeout(() => {
- displayError(
- `${app.display_name !== "" ? app.display_name : app.slug} must be installed first.`,
- );
+ displayError(`${displayName} must be installed first.`);
}, openAppExternallyFailedTimeout);
window.addEventListener("blur", () => {
clearTimeout(openAppExternallyFailed);
@@ -156,7 +145,7 @@ export const AppLink: FC = ({ app, workspace, agent }) => {
case "slim-window": {
window.open(
href,
- Language.appTitle(appDisplayName, generateRandomString(12)),
+ Language.appTitle(displayName, generateRandomString(12)),
"width=900,height=600",
);
return;
@@ -169,7 +158,7 @@ export const AppLink: FC = ({ app, workspace, agent }) => {
}}
>
{icon}
- {appDisplayName}
+ {displayName}
{canShare && }
diff --git a/site/src/modules/workspaces/WorkspaceAppStatus/WorkspaceAppStatus.tsx b/site/src/modules/workspaces/WorkspaceAppStatus/WorkspaceAppStatus.tsx
index a8c06b711f514..0b9512d939ae7 100644
--- a/site/src/modules/workspaces/WorkspaceAppStatus/WorkspaceAppStatus.tsx
+++ b/site/src/modules/workspaces/WorkspaceAppStatus/WorkspaceAppStatus.tsx
@@ -124,12 +124,11 @@ export const WorkspaceAppStatus = ({
let appHref: string | undefined;
if (app && agent) {
- const appSlug = app.slug || app.display_name;
appHref = createAppLinkHref(
window.location.protocol,
preferredPathBase,
appsHost,
- appSlug,
+ app.slug,
workspace.owner_name,
workspace,
agent,
diff --git a/site/src/pages/WorkspacePage/AppStatuses.tsx b/site/src/pages/WorkspacePage/AppStatuses.tsx
index 95afb422de30b..6399e7ef40e65 100644
--- a/site/src/pages/WorkspacePage/AppStatuses.tsx
+++ b/site/src/pages/WorkspacePage/AppStatuses.tsx
@@ -198,12 +198,11 @@ export const AppStatuses: FC = ({
const agent = agents.find((agent) => agent.id === status.agent_id);
if (currentApp && agent) {
- const appSlug = currentApp.slug || currentApp.display_name;
appHref = createAppLinkHref(
window.location.protocol,
preferredPathBase,
appsHost,
- appSlug,
+ currentApp.slug,
workspace.owner_name,
workspace,
agent,
diff --git a/site/src/utils/apps.ts b/site/src/utils/apps.ts
index 9b1a50a76ce4c..90aa6566d08e3 100644
--- a/site/src/utils/apps.ts
+++ b/site/src/utils/apps.ts
@@ -11,7 +11,7 @@ export const createAppLinkHref = (
app: TypesGen.WorkspaceApp,
): string => {
if (app.external) {
- return app.url;
+ return app.url as string;
}
// The backend redirects if the trailing slash isn't included, so we add it