Skip to content

Commit 5edfccf

Browse files
stirbyParkreinerBrunoQuaresmaaslilac
authored
chore: patch 2.13.1 (#13927)
* fix: let workspace pages download partial logs for unhealthy workspaces (#13761) * fix: get basic fix in for preventing download logs from blowing up UI * fix: make sure blob units can't go out of bounds * fix: make sure timeout is cleared on component unmount * fix: reduce risk of shared cache state breaking useAgentLogs * fix: allow partial downloading of logs * fix: make sure useMemo cache is used properly * wip: commit current progress on updated logs functionality * docs: rewrite comment for clarity * refactor: clean up current code * fix: update styles for unavailable logs * fix: resolve linter violations * fix: update type signature of getErrorDetail * fix: revert log/enabled logic for useAgentLogs * fix: remove memoization from DownloadLogsDialog * fix: update name of timeout state * refactor: make log web sockets logic more clear * docs: reword comment for clarity * fix: commit current style update progress * fix: finish style updates (cherry picked from commit 940afa1) * fix(site): enable dormant workspace to be deleted (#13850) (cherry picked from commit 01b30ea) * chore: remove `organizationIds` from `AuthProvider` (#13917) (cherry picked from commit 80cbffe) --------- Co-authored-by: Michael Smith <throwawayclover@gmail.com> Co-authored-by: Bruno Quaresma <bruno@coder.com> Co-authored-by: Kayla Washburn-Love <mckayla@hey.com>
1 parent 56bf386 commit 5edfccf

File tree

12 files changed

+271
-115
lines changed

12 files changed

+271
-115
lines changed

site/src/api/errors.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,15 +110,18 @@ export const getValidationErrorMessage = (error: unknown): string => {
110110
return validationErrors.map((error) => error.detail).join("\n");
111111
};
112112

113-
export const getErrorDetail = (error: unknown): string | undefined | null => {
113+
export const getErrorDetail = (error: unknown): string | undefined => {
114114
if (error instanceof Error) {
115115
return "Please check the developer console for more details.";
116116
}
117+
117118
if (isApiError(error)) {
118119
return error.response.data.detail;
119120
}
121+
120122
if (isApiErrorResponse(error)) {
121123
return error.detail;
122124
}
123-
return null;
125+
126+
return undefined;
124127
};

site/src/contexts/auth/AuthProvider.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ export type AuthContextValue = {
3030
isUpdatingProfile: boolean;
3131
user: User | undefined;
3232
permissions: Permissions | undefined;
33-
organizationIds: readonly string[] | undefined;
3433
signInError: unknown;
3534
updateProfileError: unknown;
3635
signOut: () => void;
@@ -119,7 +118,6 @@ export const AuthProvider: FC<PropsWithChildren> = ({ children }) => {
119118
permissions: permissionsQuery.data as Permissions | undefined,
120119
signInError: loginMutation.error,
121120
updateProfileError: updateProfileMutation.error,
122-
organizationIds: userQuery.data?.organization_ids,
123121
}}
124122
>
125123
{children}

site/src/contexts/auth/RequireAuth.test.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,6 @@ describe("useAuthenticated", () => {
9595
wrapper: createAuthWrapper({
9696
user: MockUser,
9797
permissions: MockPermissions,
98-
organizationIds: [],
9998
}),
10099
});
101100
}).not.toThrow();

site/src/contexts/auth/RequireAuth.tsx

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ type RequireKeys<T, R extends keyof T> = Omit<T, R> & {
7474
// values are not undefined when authenticated
7575
type AuthenticatedAuthContextValue = RequireKeys<
7676
AuthContextValue,
77-
"user" | "permissions" | "organizationIds"
77+
"user" | "permissions"
7878
>;
7979

8080
export const useAuthenticated = (): AuthenticatedAuthContextValue => {
@@ -88,9 +88,5 @@ export const useAuthenticated = (): AuthenticatedAuthContextValue => {
8888
throw new Error("Permissions are not available.");
8989
}
9090

91-
if (!auth.organizationIds) {
92-
throw new Error("Organization ID is not available.");
93-
}
94-
9591
return auth as AuthenticatedAuthContextValue;
9692
};

site/src/modules/dashboard/DashboardProvider.tsx

Lines changed: 7 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,4 @@
1-
import {
2-
createContext,
3-
type FC,
4-
type PropsWithChildren,
5-
useState,
6-
} from "react";
1+
import { createContext, type FC, type PropsWithChildren } from "react";
72
import { useQuery } from "react-query";
83
import { appearance } from "api/queries/appearance";
94
import { entitlements } from "api/queries/entitlements";
@@ -15,12 +10,14 @@ import type {
1510
} from "api/typesGenerated";
1611
import { Loader } from "components/Loader/Loader";
1712
import { useAuthenticated } from "contexts/auth/RequireAuth";
18-
import { useEffectEvent } from "hooks/hookPolyfills";
1913
import { useEmbeddedMetadata } from "hooks/useEmbeddedMetadata";
2014

2115
export interface DashboardValue {
16+
/**
17+
* @deprecated Do not add new usage of this value. It is being removed as part
18+
* of the multi-org work.
19+
*/
2220
organizationId: string;
23-
setOrganizationId: (id: string) => void;
2421
entitlements: Entitlements;
2522
experiments: Experiments;
2623
appearance: AppearanceConfig;
@@ -32,40 +29,22 @@ export const DashboardContext = createContext<DashboardValue | undefined>(
3229

3330
export const DashboardProvider: FC<PropsWithChildren> = ({ children }) => {
3431
const { metadata } = useEmbeddedMetadata();
35-
const { user, organizationIds } = useAuthenticated();
32+
const { user } = useAuthenticated();
3633
const entitlementsQuery = useQuery(entitlements(metadata.entitlements));
3734
const experimentsQuery = useQuery(experiments(metadata.experiments));
3835
const appearanceQuery = useQuery(appearance(metadata.appearance));
3936

4037
const isLoading =
4138
!entitlementsQuery.data || !appearanceQuery.data || !experimentsQuery.data;
4239

43-
const lastUsedOrganizationId = localStorage.getItem(
44-
`user:${user.id}.lastUsedOrganizationId`,
45-
);
46-
const [activeOrganizationId, setActiveOrganizationId] = useState(() =>
47-
lastUsedOrganizationId && organizationIds.includes(lastUsedOrganizationId)
48-
? lastUsedOrganizationId
49-
: organizationIds[0],
50-
);
51-
52-
const setOrganizationId = useEffectEvent((id: string) => {
53-
if (!organizationIds.includes(id)) {
54-
throw new ReferenceError("Invalid organization ID");
55-
}
56-
localStorage.setItem(`user:${user.id}.lastUsedOrganizationId`, id);
57-
setActiveOrganizationId(id);
58-
});
59-
6040
if (isLoading) {
6141
return <Loader fullscreen />;
6242
}
6343

6444
return (
6545
<DashboardContext.Provider
6646
value={{
67-
organizationId: activeOrganizationId,
68-
setOrganizationId: setOrganizationId,
47+
organizationId: user.organization_ids[0] ?? "default",
6948
entitlements: entitlementsQuery.data,
7049
experiments: experimentsQuery.data,
7150
appearance: appearanceQuery.data,

site/src/modules/resources/AgentLogs/useAgentLogs.ts

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,22 +15,35 @@ export type UseAgentLogsOptions = Readonly<{
1515
enabled?: boolean;
1616
}>;
1717

18+
/**
19+
* Defines a custom hook that gives you all workspace agent logs for a given
20+
* workspace.
21+
*
22+
* Depending on the status of the workspace, all logs may or may not be
23+
* available.
24+
*/
1825
export function useAgentLogs(
1926
options: UseAgentLogsOptions,
2027
): readonly WorkspaceAgentLog[] | undefined {
2128
const { workspaceId, agentId, agentLifeCycleState, enabled = true } = options;
29+
2230
const queryClient = useQueryClient();
2331
const queryOptions = agentLogs(workspaceId, agentId);
24-
const query = useQuery({
25-
...queryOptions,
26-
enabled,
27-
});
28-
const logs = query.data;
32+
const { data: logs, isFetched } = useQuery({ ...queryOptions, enabled });
2933

34+
// Track the ID of the last log received when the initial logs response comes
35+
// back. If the logs are not complete, the ID will mark the start point of the
36+
// Web sockets response so that the remaining logs can be received over time
3037
const lastQueriedLogId = useRef(0);
3138
useEffect(() => {
32-
if (logs && lastQueriedLogId.current === 0) {
33-
lastQueriedLogId.current = logs[logs.length - 1].id;
39+
const isAlreadyTracking = lastQueriedLogId.current !== 0;
40+
if (isAlreadyTracking) {
41+
return;
42+
}
43+
44+
const lastLog = logs?.at(-1);
45+
if (lastLog !== undefined) {
46+
lastQueriedLogId.current = lastLog.id;
3447
}
3548
}, [logs]);
3649

@@ -42,7 +55,7 @@ export function useAgentLogs(
4255
});
4356

4457
useEffect(() => {
45-
if (agentLifeCycleState !== "starting" || !query.isFetched) {
58+
if (agentLifeCycleState !== "starting" || !isFetched) {
4659
return;
4760
}
4861

@@ -69,7 +82,7 @@ export function useAgentLogs(
6982
return () => {
7083
socket.close();
7184
};
72-
}, [addLogs, agentId, agentLifeCycleState, query.isFetched]);
85+
}, [addLogs, agentId, agentLifeCycleState, isFetched]);
7386

7487
return logs;
7588
}

site/src/pages/OrganizationSettingsPage/OrganizationSettingsLayout.tsx

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { createContext, type FC, Suspense, useContext } from "react";
22
import { useQuery } from "react-query";
3-
import { Outlet, useParams } from "react-router-dom";
3+
import { Outlet, useLocation, useParams } from "react-router-dom";
44
import { myOrganizations } from "api/queries/users";
55
import type { Organization } from "api/typesGenerated";
66
import { Loader } from "components/Loader/Loader";
@@ -13,7 +13,7 @@ import NotFoundPage from "pages/404Page/404Page";
1313
import { Sidebar } from "./Sidebar";
1414

1515
type OrganizationSettingsContextValue = {
16-
currentOrganizationId: string;
16+
currentOrganizationId?: string;
1717
organizations: Organization[];
1818
};
1919

@@ -32,13 +32,18 @@ export const useOrganizationSettings = (): OrganizationSettingsContextValue => {
3232
};
3333

3434
export const OrganizationSettingsLayout: FC = () => {
35-
const { permissions, organizationIds } = useAuthenticated();
35+
const location = useLocation();
36+
const { permissions } = useAuthenticated();
3637
const { experiments } = useDashboard();
3738
const { organization } = useParams() as { organization: string };
3839
const organizationsQuery = useQuery(myOrganizations());
3940

4041
const multiOrgExperimentEnabled = experiments.includes("multi-organization");
4142

43+
const inOrganizationSettings =
44+
location.pathname.startsWith("/organizations") &&
45+
location.pathname !== "/organizations/new";
46+
4247
if (!multiOrgExperimentEnabled) {
4348
return <NotFoundPage />;
4449
}
@@ -50,10 +55,13 @@ export const OrganizationSettingsLayout: FC = () => {
5055
{organizationsQuery.data ? (
5156
<OrganizationSettingsContext.Provider
5257
value={{
53-
currentOrganizationId:
54-
organizationsQuery.data.find(
55-
(org) => org.name === organization,
56-
)?.id ?? organizationIds[0],
58+
currentOrganizationId: !inOrganizationSettings
59+
? undefined
60+
: !organization
61+
? organizationsQuery.data[0]?.id
62+
: organizationsQuery.data.find(
63+
(org) => org.name === organization,
64+
)?.id,
5765
organizations: organizationsQuery.data,
5866
}}
5967
>

site/src/pages/OrganizationSettingsPage/OrganizationSettingsPage.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ const OrganizationSettingsPage: FC = () => {
140140
css={styles.dangerButton}
141141
variant="contained"
142142
onClick={() =>
143-
deleteOrganizationMutation.mutate(currentOrganizationId)
143+
deleteOrganizationMutation.mutate(currentOrganizationId!)
144144
}
145145
>
146146
Delete this organization

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy