chore(site): remove workspace build machine (#9813)

This commit is contained in:
Bruno Quaresma
2023-09-22 16:12:25 -03:00
committed by GitHub
parent df0c597843
commit 7bb0061804
6 changed files with 70 additions and 212 deletions
+6 -6
View File
@@ -792,11 +792,11 @@ export const getWorkspaceBuildByNumber = async (
};
export const getWorkspaceBuildLogs = async (
buildname: string,
buildId: string,
before: Date,
): Promise<TypesGen.ProvisionerJobLog[]> => {
const response = await axios.get<TypesGen.ProvisionerJobLog[]>(
`/api/v2/workspacebuilds/${buildname}/logs?before=${before.getTime()}`,
`/api/v2/workspacebuilds/${buildId}/logs?before=${before.getTime()}`,
);
return response.data;
};
@@ -1432,8 +1432,8 @@ export const watchWorkspaceAgentLogs = (
type WatchBuildLogsByBuildIdOptions = {
after?: number;
onMessage: (log: TypesGen.ProvisionerJobLog) => void;
onDone: () => void;
onError: (error: Error) => void;
onDone?: () => void;
onError?: (error: Error) => void;
};
export const watchBuildLogsByBuildId = (
buildId: string,
@@ -1454,12 +1454,12 @@ export const watchBuildLogsByBuildId = (
onMessage(JSON.parse(event.data) as TypesGen.ProvisionerJobLog),
);
socket.addEventListener("error", () => {
onError(new Error("Connection for logs failed."));
onError && onError(new Error("Connection for logs failed."));
socket.close();
});
socket.addEventListener("close", () => {
// When the socket closes, logs have finished streaming!
onDone();
onDone && onDone();
});
return socket;
};
+13
View File
@@ -0,0 +1,13 @@
import * as API from "api/api";
export const workspaceBuildByNumber = (
username: string,
workspaceName: string,
buildNumber: number,
) => {
return {
queryKey: [username, workspaceName, "workspaceBuild", buildNumber],
queryFn: () =>
API.getWorkspaceBuildByNumber(username, workspaceName, buildNumber),
};
};
+39
View File
@@ -0,0 +1,39 @@
import { watchBuildLogsByBuildId } from "api/api";
import { ProvisionerJobLog } from "api/typesGenerated";
import { displayError } from "components/GlobalSnackbar/utils";
import { useState, useEffect } from "react";
// buildId is optional because sometimes the build is not loaded yet
export const useWorkspaceBuildLogs = (buildId?: string) => {
const [logs, setLogs] = useState<ProvisionerJobLog[]>();
useEffect(() => {
if (!buildId) {
return;
}
// Every time this hook is called reset the values
setLogs(undefined);
const socket = watchBuildLogsByBuildId(buildId, {
// Retrieve all the logs
after: -1,
onMessage: (log) => {
setLogs((previousLogs) => {
if (!previousLogs) {
return [log];
}
return [...previousLogs, log];
});
},
onError: () => {
displayError("Error on getting the build logs");
},
});
return () => {
socket.close();
};
}, [buildId]);
return logs;
};
@@ -1,13 +1,13 @@
import { useMachine } from "@xstate/react";
import { FC, useEffect } from "react";
import { FC } from "react";
import { Helmet } from "react-helmet-async";
import { useParams } from "react-router-dom";
import { pageTitle } from "../../utils/page";
import { workspaceBuildMachine } from "../../xServices/workspaceBuild/workspaceBuildXService";
import { pageTitle } from "utils/page";
import { WorkspaceBuildPageView } from "./WorkspaceBuildPageView";
import { useQuery } from "@tanstack/react-query";
import { getWorkspaceBuilds } from "api/api";
import dayjs from "dayjs";
import { workspaceBuildByNumber } from "api/queries/workspaceBuilds";
import { useWorkspaceBuildLogs } from "hooks/useWorkspaceBuildLogs";
export const WorkspaceBuildPage: FC = () => {
const params = useParams() as {
@@ -18,10 +18,11 @@ export const WorkspaceBuildPage: FC = () => {
const workspaceName = params.workspace;
const buildNumber = Number(params.buildNumber);
const username = params.username.replace("@", "");
const [buildState, send] = useMachine(workspaceBuildMachine, {
context: { username, workspaceName, buildNumber, timeCursor: new Date() },
const wsBuildQuery = useQuery({
...workspaceBuildByNumber(username, workspaceName, buildNumber),
keepPreviousData: true,
});
const { logs, build } = buildState.context;
const build = wsBuildQuery.data;
const { data: builds } = useQuery({
queryKey: ["builds", username, build?.workspace_id],
queryFn: () => {
@@ -32,10 +33,7 @@ export const WorkspaceBuildPage: FC = () => {
},
enabled: Boolean(build),
});
useEffect(() => {
send("RESET", { buildNumber, timeCursor: new Date() });
}, [buildNumber, send]);
const logs = useWorkspaceBuildLogs(build?.id);
return (
<>
@@ -1,4 +1,4 @@
import { useActor, useMachine } from "@xstate/react";
import { useActor } from "@xstate/react";
import { useDashboard } from "components/Dashboard/DashboardProvider";
import dayjs from "dayjs";
import { useFeatureVisibility } from "hooks/useFeatureVisibility";
@@ -28,12 +28,12 @@ import {
ConfirmDialog,
ConfirmDialogProps,
} from "components/Dialogs/ConfirmDialog/ConfirmDialog";
import { workspaceBuildMachine } from "xServices/workspaceBuild/workspaceBuildXService";
import * as TypesGen from "api/typesGenerated";
import { WorkspaceBuildLogsSection } from "./WorkspaceBuildLogsSection";
import { templateVersion, templateVersions } from "api/queries/templates";
import { Alert } from "components/Alert/Alert";
import { Stack } from "components/Stack/Stack";
import { useWorkspaceBuildLogs } from "hooks/useWorkspaceBuildLogs";
interface WorkspaceReadyPageProps {
workspaceState: StateFrom<typeof workspaceMachine>;
@@ -91,7 +91,7 @@ export const WorkspaceReadyPage = ({
enabled: workspace.outdated,
});
const buildLogs = useBuildLogs(workspace);
const buildLogs = useWorkspaceBuildLogs(workspace.latest_build.id);
const shouldDisplayBuildLogs =
hasJobError(workspace) ||
["canceling", "deleting", "pending", "starting", "stopping"].includes(
@@ -285,22 +285,3 @@ const WarningDialog: FC<
> = (props) => {
return <ConfirmDialog type="info" hideCancel={false} {...props} />;
};
const useBuildLogs = (workspace: TypesGen.Workspace) => {
const buildNumber = workspace.latest_build.build_number;
const [buildState, buildSend] = useMachine(workspaceBuildMachine, {
context: {
buildNumber,
username: workspace.owner_name,
workspaceName: workspace.name,
timeCursor: new Date(),
},
});
const { logs } = buildState.context;
useEffect(() => {
buildSend({ type: "RESET", buildNumber, timeCursor: new Date() });
}, [buildNumber, buildSend]);
return logs;
};
@@ -1,173 +0,0 @@
import { assign, createMachine } from "xstate";
import * as API from "../../api/api";
import { ProvisionerJobLog, WorkspaceBuild } from "../../api/typesGenerated";
type LogsContext = {
// Build
username: string;
workspaceName: string;
buildNumber: number;
buildId: string;
// Used to reference logs before + after.
timeCursor: Date;
build?: WorkspaceBuild;
getBuildError?: unknown;
// Logs
logs?: ProvisionerJobLog[];
};
type LogsEvent =
| {
type: "ADD_LOG";
log: ProvisionerJobLog;
}
| {
type: "BUILD_DONE";
}
| {
type: "RESET";
buildNumber: number;
timeCursor: Date;
};
export const workspaceBuildMachine = createMachine(
{
id: "workspaceBuildState",
predictableActionArguments: true,
tsTypes: {} as import("./workspaceBuildXService.typegen").Typegen0,
schema: {
context: {} as LogsContext,
events: {} as LogsEvent,
services: {} as {
getWorkspaceBuild: {
data: WorkspaceBuild;
};
getLogs: {
data: ProvisionerJobLog[];
};
},
},
initial: "gettingBuild",
on: {
RESET: {
target: "gettingBuild",
actions: ["resetContext"],
},
},
states: {
gettingBuild: {
entry: "clearGetBuildError",
invoke: {
src: "getWorkspaceBuild",
onDone: {
target: "logs",
actions: ["assignBuild", "assignBuildId"],
},
onError: {
target: "idle",
actions: "assignGetBuildError",
},
},
},
idle: {},
logs: {
initial: "gettingExistentLogs",
states: {
gettingExistentLogs: {
invoke: {
id: "getLogs",
src: "getLogs",
onDone: {
actions: ["assignLogs"],
target: "watchingLogs",
},
},
},
watchingLogs: {
id: "watchingLogs",
invoke: {
id: "streamWorkspaceBuildLogs",
src: "streamWorkspaceBuildLogs",
},
on: {
ADD_LOG: {
actions: "addLog",
},
BUILD_DONE: {
target: "loaded",
},
},
},
loaded: {
type: "final",
},
},
},
},
},
{
actions: {
resetContext: assign({
buildNumber: (_, event) => event.buildNumber,
timeCursor: (_, event) => event.timeCursor,
logs: undefined,
}),
// Build ID
assignBuildId: assign({
buildId: (_, event) => event.data.id,
}),
// Build
assignBuild: assign({
build: (_, event) => event.data,
}),
assignGetBuildError: assign({
getBuildError: (_, event) => event.data,
}),
clearGetBuildError: assign({
getBuildError: (_) => undefined,
}),
// Logs
assignLogs: assign({
logs: (_, event) => event.data,
}),
addLog: assign({
logs: (context, event) => {
const previousLogs = context.logs ?? [];
return [...previousLogs, event.log];
},
}),
},
services: {
getWorkspaceBuild: (ctx) =>
API.getWorkspaceBuildByNumber(
ctx.username,
ctx.workspaceName,
ctx.buildNumber,
),
getLogs: async (ctx) =>
API.getWorkspaceBuildLogs(ctx.buildId, ctx.timeCursor),
streamWorkspaceBuildLogs: (ctx) => async (callback) => {
if (!ctx.logs) {
throw new Error("logs must be set");
}
const after =
ctx.logs.length > 0 ? ctx.logs[ctx.logs.length - 1].id : undefined;
const socket = API.watchBuildLogsByBuildId(ctx.buildId, {
after,
onMessage: (log) => {
callback({ type: "ADD_LOG", log });
},
onDone: () => {
callback({ type: "BUILD_DONE" });
},
onError: (err) => {
console.error(err);
},
});
return () => {
socket.close();
};
},
},
},
);