From d146115ca078617a0cf886566ca13a536747794a Mon Sep 17 00:00:00 2001 From: Bruno Quaresma Date: Wed, 7 May 2025 09:01:47 -0300 Subject: [PATCH 1/2] chore: update browser list db (#17699) --- site/pnpm-lock.yaml | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/site/pnpm-lock.yaml b/site/pnpm-lock.yaml index e20e5b322b2c2..7b8e9c52ea4af 100644 --- a/site/pnpm-lock.yaml +++ b/site/pnpm-lock.yaml @@ -995,8 +995,8 @@ packages: cpu: [x64] os: [win32] - '@eslint-community/eslint-utils@4.6.1': - resolution: {integrity: sha512-KTsJMmobmbrFLe3LDh0PC2FXpcSYJt/MLjlkh/9LEnmKYLSYmT/0EW9JWANjeoemiuZrmogti0tW5Ch+qNUYDw==, tarball: https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.6.1.tgz} + '@eslint-community/eslint-utils@4.7.0': + resolution: {integrity: sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==, tarball: https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 @@ -3067,11 +3067,8 @@ packages: resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==, tarball: https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz} engines: {node: '>=10'} - caniuse-lite@1.0.30001677: - resolution: {integrity: sha512-fmfjsOlJUpMWu+mAAtZZZHz7UEwsUxIIvu1TJfO1HqFQvB/B+ii0xr9B5HpbZY/mC4XZ8SvjHJqtAY6pDPQEog==, tarball: https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001677.tgz} - - caniuse-lite@1.0.30001690: - resolution: {integrity: sha512-5ExiE3qQN6oF8Clf8ifIDcMRCRE/dMGcETG/XGMD8/XiXm6HXQgQTh1yZYLXXpSOsEUlJm1Xr7kGULZTuGtP/w==, tarball: https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001690.tgz} + caniuse-lite@1.0.30001717: + resolution: {integrity: sha512-auPpttCq6BDEG8ZAuHJIplGw6GODhjw+/11e7IjpnYCxZcW/ONgPs0KVBJ0d1bY3e2+7PRe5RCLyP+PfwVgkYw==, tarball: https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001717.tgz} case-anything@2.1.13: resolution: {integrity: sha512-zlOQ80VrQ2Ue+ymH5OuM/DlDq64mEm+B9UTdHULv5osUMD6HalNTblf2b1u/m6QecjsnOkBpqVZ+XPwIVsy7Ng==, tarball: https://registry.npmjs.org/case-anything/-/case-anything-2.1.13.tgz} @@ -3650,7 +3647,6 @@ packages: eslint@8.52.0: resolution: {integrity: sha512-zh/JHnaixqHZsolRB/w9/02akBk9EPrOs9JwcTP2ek7yL5bVvXuRariiaAjjoJ5DvuwQ1WAE/HsMz+w17YgBCg==, tarball: https://registry.npmjs.org/eslint/-/eslint-8.52.0.tgz} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. hasBin: true espree@9.6.1: @@ -6923,7 +6919,7 @@ snapshots: '@esbuild/win32-x64@0.25.3': optional: true - '@eslint-community/eslint-utils@4.6.1(eslint@8.52.0)': + '@eslint-community/eslint-utils@4.7.0(eslint@8.52.0)': dependencies: eslint: 8.52.0 eslint-visitor-keys: 3.4.3 @@ -9059,7 +9055,7 @@ snapshots: autoprefixer@10.4.20(postcss@8.5.1): dependencies: browserslist: 4.24.2 - caniuse-lite: 1.0.30001677 + caniuse-lite: 1.0.30001717 fraction.js: 4.3.7 normalize-range: 0.1.2 picocolors: 1.1.1 @@ -9195,14 +9191,14 @@ snapshots: browserslist@4.24.2: dependencies: - caniuse-lite: 1.0.30001677 + caniuse-lite: 1.0.30001717 electron-to-chromium: 1.5.50 node-releases: 2.0.18 update-browserslist-db: 1.1.1(browserslist@4.24.2) browserslist@4.24.3: dependencies: - caniuse-lite: 1.0.30001690 + caniuse-lite: 1.0.30001717 electron-to-chromium: 1.5.76 node-releases: 2.0.19 update-browserslist-db: 1.1.1(browserslist@4.24.3) @@ -9256,9 +9252,7 @@ snapshots: camelcase@6.3.0: {} - caniuse-lite@1.0.30001677: {} - - caniuse-lite@1.0.30001690: {} + caniuse-lite@1.0.30001717: {} case-anything@2.1.13: {} @@ -9809,7 +9803,7 @@ snapshots: eslint@8.52.0: dependencies: - '@eslint-community/eslint-utils': 4.6.1(eslint@8.52.0) + '@eslint-community/eslint-utils': 4.7.0(eslint@8.52.0) '@eslint-community/regexpp': 4.12.1 '@eslint/eslintrc': 2.1.4 '@eslint/js': 8.52.0 From 9fe5b71d31d896c9a7a358834dc8a1c25c103f30 Mon Sep 17 00:00:00 2001 From: Bruno Quaresma Date: Wed, 7 May 2025 10:05:07 -0300 Subject: [PATCH 2/2] chore!: fix workspace apps response (#17700) Fix WorkspaceApp response type to better reflect the schema from https://registry.terraform.io/providers/coder/coder/latest/docs/resources/app. --- codersdk/workspaceapps.go | 6 ++--- site/src/api/typesGenerated.ts | 6 ++--- site/src/modules/resources/AgentRow.test.tsx | 4 ++-- .../resources/AgentRowPreview.test.tsx | 6 +++-- .../src/modules/resources/AgentRowPreview.tsx | 2 +- .../src/modules/resources/AppLink/AppLink.tsx | 23 +++++-------------- .../WorkspaceAppStatus/WorkspaceAppStatus.tsx | 3 +-- site/src/pages/WorkspacePage/AppStatuses.tsx | 3 +-- site/src/utils/apps.ts | 2 +- 9 files changed, 22 insertions(+), 33 deletions(-) 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