From c290855e52beb97ce88f1c60350da4339cd06282 Mon Sep 17 00:00:00 2001 From: BrunoQuaresma Date: Tue, 31 Oct 2023 17:39:57 +0000 Subject: [PATCH 01/24] Refactor initial loading --- site/src/api/queries/files.ts | 7 +++ .../TemplateVersionEditorPage.tsx | 58 +++++++++++++------ 2 files changed, 46 insertions(+), 19 deletions(-) diff --git a/site/src/api/queries/files.ts b/site/src/api/queries/files.ts index 5fd3250d50106..cc840b52eb63f 100644 --- a/site/src/api/queries/files.ts +++ b/site/src/api/queries/files.ts @@ -5,3 +5,10 @@ export const uploadFile = () => { mutationFn: API.uploadFile, }; }; + +export const file = (fileId: string) => { + return { + queryKey: ["files", fileId], + queryFn: () => API.getFile(fileId), + }; +}; diff --git a/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditorPage.tsx b/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditorPage.tsx index 3e571900c90f5..d291c12e013d9 100644 --- a/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditorPage.tsx +++ b/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditorPage.tsx @@ -1,12 +1,17 @@ import { useMachine } from "@xstate/react"; import { TemplateVersionEditor } from "./TemplateVersionEditor"; import { useOrganizationId } from "hooks/useOrganizationId"; -import { FC } from "react"; +import { FC, useEffect, useState } from "react"; import { Helmet } from "react-helmet-async"; import { useNavigate, useParams } from "react-router-dom"; import { pageTitle } from "utils/page"; import { templateVersionEditorMachine } from "xServices/templateVersionEditor/templateVersionEditorXService"; -import { useTemplateVersionData } from "./data"; +import { useQuery } from "react-query"; +import { templateByName, templateVersionByName } from "api/queries/templates"; +import { file } from "api/queries/files"; +import { TarReader } from "utils/tar"; +import { FileTree } from "utils/filetree"; +import { createTemplateVersionFileTree } from "utils/templateVersion"; type Params = { version: string; @@ -18,21 +23,34 @@ export const TemplateVersionEditorPage: FC = () => { const { version: versionName, template: templateName } = useParams() as Params; const orgId = useOrganizationId(); + const templateQuery = useQuery(templateByName(orgId, templateName)); + const templateVersionQuery = useQuery( + templateVersionByName(orgId, templateName, versionName), + ); + const fileQuery = useQuery({ + ...file(templateVersionQuery.data?.job.file_id ?? ""), + enabled: templateVersionQuery.isSuccess, + }); const [editorState, sendEvent] = useMachine(templateVersionEditorMachine, { context: { orgId }, }); - const { isSuccess, data } = useTemplateVersionData( - { - orgId, - templateName, - versionName, - }, - { - onSuccess(data) { - sendEvent({ type: "INITIALIZE", tarReader: data.tarReader }); - }, - }, - ); + const [fileTree, setFileTree] = useState(); + + useEffect(() => { + const initialize = async (file: ArrayBuffer) => { + const tarReader = new TarReader(); + await tarReader.readFile(file); + const fileTree = await createTemplateVersionFileTree(tarReader); + sendEvent({ type: "INITIALIZE", tarReader }); + setFileTree(fileTree); + }; + + if (fileQuery.data) { + initialize(fileQuery.data).catch(() => { + console.error("Error on initializing the editor"); + }); + } + }, [fileQuery.data, sendEvent]); return ( <> @@ -40,17 +58,19 @@ export const TemplateVersionEditorPage: FC = () => { Codestin Search App - {isSuccess && ( + {templateQuery.data && templateVersionQuery.data && fileTree && ( { sendEvent({ type: "CREATE_VERSION", fileTree, - templateId: data.template.id, + templateId: templateQuery.data.id, }); }} onPublish={() => { From 93df525231be2256798fc6907b3c3541aa944199 Mon Sep 17 00:00:00 2001 From: BrunoQuaresma Date: Tue, 31 Oct 2023 19:04:22 +0000 Subject: [PATCH 02/24] Remove non used events --- .../templateVersionEditorXService.ts | 8 -------- 1 file changed, 8 deletions(-) diff --git a/site/src/xServices/templateVersionEditor/templateVersionEditorXService.ts b/site/src/xServices/templateVersionEditor/templateVersionEditorXService.ts index 7ef9fbd0b9379..441c3760471ab 100644 --- a/site/src/xServices/templateVersionEditor/templateVersionEditorXService.ts +++ b/site/src/xServices/templateVersionEditor/templateVersionEditorXService.ts @@ -43,7 +43,6 @@ export const templateVersionEditorMachine = createMachine( fileTree: FileTree; templateId: string; } - | { type: "CANCEL_VERSION" } | { type: "SET_MISSING_VARIABLE_VALUES"; values: VariableValue[] } | { type: "CANCEL_MISSING_VARIABLE_VALUES" } | { type: "ADD_BUILD_LOG"; log: ProvisionerJobLog } @@ -172,13 +171,6 @@ export const templateVersionEditorMachine = createMachine( actions: "addBuildLog", }, BUILD_DONE: "fetchingVersion", - CANCEL_VERSION: { - target: "cancelingInProgressBuild", - }, - CREATE_VERSION: { - actions: ["assignCreateBuild"], - target: "uploadTar", - }, }, }, From b3c712c11c5779ebe0049fe3c645437ca14c0f28 Mon Sep 17 00:00:00 2001 From: BrunoQuaresma Date: Tue, 31 Oct 2023 19:10:56 +0000 Subject: [PATCH 03/24] Move cancel build to create build service --- .../templateVersionEditorXService.ts | 29 ++++--------------- 1 file changed, 5 insertions(+), 24 deletions(-) diff --git a/site/src/xServices/templateVersionEditor/templateVersionEditorXService.ts b/site/src/xServices/templateVersionEditor/templateVersionEditorXService.ts index 441c3760471ab..38a51f49bd1b9 100644 --- a/site/src/xServices/templateVersionEditor/templateVersionEditorXService.ts +++ b/site/src/xServices/templateVersionEditor/templateVersionEditorXService.ts @@ -58,9 +58,6 @@ export const templateVersionEditorMachine = createMachine( createBuild: { data: TemplateVersion; }; - cancelBuild: { - data: void; - }; fetchVersion: { data: TemplateVersion; }; @@ -91,7 +88,7 @@ export const templateVersionEditorMachine = createMachine( on: { CREATE_VERSION: { actions: ["assignCreateBuild"], - target: "cancelingInProgressBuild", + target: "uploadTar", }, PUBLISH: { target: "askPublishParameters", @@ -125,17 +122,6 @@ export const templateVersionEditorMachine = createMachine( }, }, - cancelingInProgressBuild: { - tags: "loading", - invoke: { - id: "cancelBuild", - src: "cancelBuild", - onDone: { - target: "uploadTar", - }, - }, - }, - uploadTar: { tags: "loading", invoke: { @@ -344,10 +330,13 @@ export const templateVersionEditorMachine = createMachine( const blob = (await tar.write()) as Blob; return API.uploadFile(new File([blob], "template.tar")); }, - createBuild: (ctx) => { + createBuild: async (ctx) => { if (!ctx.uploadResponse) { throw new Error("no upload response"); } + if (ctx.version?.job.status === "running") { + await API.cancelTemplateVersionBuild(ctx.version.id); + } return API.createTemplateVersion(ctx.orgId, { provisioner: "terraform", storage_method: "file", @@ -392,14 +381,6 @@ export const templateVersionEditorMachine = createMachine( } return API.getTemplateVersionResources(ctx.version.id); }, - cancelBuild: async (ctx) => { - if (!ctx.version) { - return; - } - if (ctx.version.job.status === "running") { - await API.cancelTemplateVersionBuild(ctx.version.id); - } - }, publishingVersion: async ( { version, templateId }, { name, message, isActiveVersion }, From 10d6b4e888a9feca40a11088646fce5656b3eb3e Mon Sep 17 00:00:00 2001 From: BrunoQuaresma Date: Tue, 31 Oct 2023 21:12:19 +0000 Subject: [PATCH 04/24] Extract upload --- .../TemplateVersionEditorPage.tsx | 82 +++++++++++-- .../templateVersionEditorXService.ts | 114 ++---------------- 2 files changed, 83 insertions(+), 113 deletions(-) diff --git a/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditorPage.tsx b/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditorPage.tsx index d291c12e013d9..82b7e1417e4d1 100644 --- a/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditorPage.tsx +++ b/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditorPage.tsx @@ -1,17 +1,20 @@ import { useMachine } from "@xstate/react"; import { TemplateVersionEditor } from "./TemplateVersionEditor"; import { useOrganizationId } from "hooks/useOrganizationId"; -import { FC, useEffect, useState } from "react"; +import { FC, useEffect, useRef, useState } from "react"; import { Helmet } from "react-helmet-async"; import { useNavigate, useParams } from "react-router-dom"; import { pageTitle } from "utils/page"; import { templateVersionEditorMachine } from "xServices/templateVersionEditor/templateVersionEditorXService"; -import { useQuery } from "react-query"; +import { useMutation, useQuery } from "react-query"; import { templateByName, templateVersionByName } from "api/queries/templates"; -import { file } from "api/queries/files"; -import { TarReader } from "utils/tar"; -import { FileTree } from "utils/filetree"; -import { createTemplateVersionFileTree } from "utils/templateVersion"; +import { file, uploadFile } from "api/queries/files"; +import { TarReader, TarWriter } from "utils/tar"; +import { FileTree, traverse } from "utils/filetree"; +import { + createTemplateVersionFileTree, + isAllowedFile, +} from "utils/templateVersion"; type Params = { version: string; @@ -32,16 +35,18 @@ export const TemplateVersionEditorPage: FC = () => { enabled: templateVersionQuery.isSuccess, }); const [editorState, sendEvent] = useMachine(templateVersionEditorMachine, { - context: { orgId }, + context: { orgId, templateId: templateQuery.data?.id }, }); const [fileTree, setFileTree] = useState(); + const uploadFileMutation = useMutation(uploadFile()); + const currentTarFileRef = useRef(null); useEffect(() => { const initialize = async (file: ArrayBuffer) => { const tarReader = new TarReader(); await tarReader.readFile(file); + currentTarFileRef.current = tarReader; const fileTree = await createTemplateVersionFileTree(tarReader); - sendEvent({ type: "INITIALIZE", tarReader }); setFileTree(fileTree); }; @@ -66,11 +71,21 @@ export const TemplateVersionEditorPage: FC = () => { } isBuildingNewVersion={Boolean(editorState.context.version)} defaultFileTree={fileTree} - onPreview={(fileTree) => { + onPreview={async (newFileTree) => { + if (!currentTarFileRef.current) { + return; + } + + const newVersionFile = await generateVersionFiles( + currentTarFileRef.current, + newFileTree, + ); + const newVersionUpload = await uploadFileMutation.mutateAsync( + newVersionFile, + ); sendEvent({ type: "CREATE_VERSION", - fileTree, - templateId: templateQuery.data.id, + fileId: newVersionUpload.hash, }); }} onPublish={() => { @@ -119,6 +134,7 @@ export const TemplateVersionEditorPage: FC = () => { sendEvent({ type: "SET_MISSING_VARIABLE_VALUES", values, + fileId: uploadFileMutation.data!.hash, }); }} onCancelSubmitMissingVariableValues={() => { @@ -132,4 +148,48 @@ export const TemplateVersionEditorPage: FC = () => { ); }; +const generateVersionFiles = async ( + tarReader: TarReader, + fileTree: FileTree, +) => { + const tar = new TarWriter(); + + // Add previous non editable files + for (const file of tarReader.fileInfo) { + if (!isAllowedFile(file.name)) { + if (file.type === "5") { + tar.addFolder(file.name, { + mode: file.mode, // https://github.com/beatgammit/tar-js/blob/master/lib/tar.js#L42 + mtime: file.mtime, + user: file.user, + group: file.group, + }); + } else { + tar.addFile(file.name, tarReader.getTextFile(file.name) as string, { + mode: file.mode, // https://github.com/beatgammit/tar-js/blob/master/lib/tar.js#L42 + mtime: file.mtime, + user: file.user, + group: file.group, + }); + } + } + } + // Add the editable files + traverse(fileTree, (content, _filename, fullPath) => { + // When a file is deleted. Don't add it to the tar. + if (content === undefined) { + return; + } + + if (typeof content === "string") { + tar.addFile(fullPath, content); + return; + } + + tar.addFolder(fullPath); + }); + const blob = (await tar.write()) as Blob; + return new File([blob], "template.tar"); +}; + export default TemplateVersionEditorPage; diff --git a/site/src/xServices/templateVersionEditor/templateVersionEditorXService.ts b/site/src/xServices/templateVersionEditor/templateVersionEditorXService.ts index 38a51f49bd1b9..6f7b4f663636a 100644 --- a/site/src/xServices/templateVersionEditor/templateVersionEditorXService.ts +++ b/site/src/xServices/templateVersionEditor/templateVersionEditorXService.ts @@ -3,26 +3,19 @@ import { ProvisionerJobStatus, TemplateVersion, TemplateVersionVariable, - UploadResponse, VariableValue, WorkspaceResource, } from "api/typesGenerated"; import { assign, createMachine } from "xstate"; import * as API from "api/api"; -import { FileTree, traverse } from "utils/filetree"; -import { isAllowedFile } from "utils/templateVersion"; -import { TarReader, TarWriter } from "utils/tar"; import { PublishVersionData } from "pages/TemplateVersionEditorPage/types"; export interface TemplateVersionEditorMachineContext { orgId: string; templateId?: string; - fileTree?: FileTree; - uploadResponse?: UploadResponse; version?: TemplateVersion; resources?: WorkspaceResource[]; buildLogs?: ProvisionerJobLog[]; - tarReader?: TarReader; publishingError?: unknown; lastSuccessfulPublishedVersion?: TemplateVersion; missingVariables?: TemplateVersionVariable[]; @@ -37,13 +30,15 @@ export const templateVersionEditorMachine = createMachine( schema: { context: {} as TemplateVersionEditorMachineContext, events: {} as - | { type: "INITIALIZE"; tarReader: TarReader } | { type: "CREATE_VERSION"; - fileTree: FileTree; - templateId: string; + fileId: string; + } + | { + type: "SET_MISSING_VARIABLE_VALUES"; + values: VariableValue[]; + fileId: string; } - | { type: "SET_MISSING_VARIABLE_VALUES"; values: VariableValue[] } | { type: "CANCEL_MISSING_VARIABLE_VALUES" } | { type: "ADD_BUILD_LOG"; log: ProvisionerJobLog } | { type: "BUILD_DONE" } @@ -52,9 +47,6 @@ export const templateVersionEditorMachine = createMachine( | { type: "CANCEL_PUBLISH" }, services: {} as { - uploadTar: { - data: UploadResponse; - }; createBuild: { data: TemplateVersion; }; @@ -73,22 +65,13 @@ export const templateVersionEditorMachine = createMachine( }, }, tsTypes: {} as import("./templateVersionEditorXService.typegen").Typegen0, - initial: "initializing", + initial: "idle", states: { - initializing: { - on: { - INITIALIZE: { - actions: ["assignTarReader"], - target: "idle", - }, - }, - }, - idle: { on: { CREATE_VERSION: { - actions: ["assignCreateBuild"], - target: "uploadTar", + actions: ["resetCreateBuildData"], + target: "creatingBuild", }, PUBLISH: { target: "askPublishParameters", @@ -122,18 +105,6 @@ export const templateVersionEditorMachine = createMachine( }, }, - uploadTar: { - tags: "loading", - invoke: { - id: "uploadTar", - src: "uploadTar", - onDone: { - target: "creatingBuild", - actions: "assignUploadResponse", - }, - }, - }, - creatingBuild: { tags: "loading", invoke: { @@ -221,18 +192,13 @@ export const templateVersionEditorMachine = createMachine( }, { actions: { - assignCreateBuild: assign({ - fileTree: (_, event) => event.fileTree, - templateId: (_, event) => event.templateId, + resetCreateBuildData: assign({ buildLogs: (_, _1) => [], resources: (_, _1) => [], }), assignResources: assign({ resources: (_, event) => event.data, }), - assignUploadResponse: assign({ - uploadResponse: (_, event) => event.data, - }), assignBuild: assign({ version: (_, event) => event.data, }), @@ -262,9 +228,6 @@ export const templateVersionEditorMachine = createMachine( }; }, }), - assignTarReader: assign({ - tarReader: (_, { tarReader }) => tarReader, - }), assignPublishingError: assign({ publishingError: (_, event) => event.data, }), @@ -280,60 +243,7 @@ export const templateVersionEditorMachine = createMachine( }), }, services: { - uploadTar: async ({ fileTree, tarReader }) => { - if (!fileTree) { - throw new Error("file tree must to be set"); - } - if (!tarReader) { - throw new Error("tar reader must to be set"); - } - const tar = new TarWriter(); - - // Add previous non editable files - for (const file of tarReader.fileInfo) { - if (!isAllowedFile(file.name)) { - if (file.type === "5") { - tar.addFolder(file.name, { - mode: file.mode, // https://github.com/beatgammit/tar-js/blob/master/lib/tar.js#L42 - mtime: file.mtime, - user: file.user, - group: file.group, - }); - } else { - tar.addFile( - file.name, - tarReader.getTextFile(file.name) as string, - { - mode: file.mode, // https://github.com/beatgammit/tar-js/blob/master/lib/tar.js#L42 - mtime: file.mtime, - user: file.user, - group: file.group, - }, - ); - } - } - } - // Add the editable files - traverse(fileTree, (content, _filename, fullPath) => { - // When a file is deleted. Don't add it to the tar. - if (content === undefined) { - return; - } - - if (typeof content === "string") { - tar.addFile(fullPath, content); - return; - } - - tar.addFolder(fullPath); - }); - const blob = (await tar.write()) as Blob; - return API.uploadFile(new File([blob], "template.tar")); - }, - createBuild: async (ctx) => { - if (!ctx.uploadResponse) { - throw new Error("no upload response"); - } + createBuild: async (ctx, event) => { if (ctx.version?.job.status === "running") { await API.cancelTemplateVersionBuild(ctx.version.id); } @@ -342,7 +252,7 @@ export const templateVersionEditorMachine = createMachine( storage_method: "file", tags: {}, template_id: ctx.templateId, - file_id: ctx.uploadResponse.hash, + file_id: event.fileId, user_variable_values: ctx.missingVariableValues, }); }, From 7eb5c91932e7cdf9fdeed640a220a0d98bd989f8 Mon Sep 17 00:00:00 2001 From: BrunoQuaresma Date: Wed, 1 Nov 2023 12:34:58 +0000 Subject: [PATCH 05/24] Extract create version --- site/src/api/queries/templates.ts | 9 ++++++++ .../TemplateVersionEditorPage.tsx | 23 +++++++++++++++---- .../templateVersionEditorXService.ts | 11 ++++----- 3 files changed, 32 insertions(+), 11 deletions(-) diff --git a/site/src/api/queries/templates.ts b/site/src/api/queries/templates.ts index 337925a6583a4..6176d8994b30a 100644 --- a/site/src/api/queries/templates.ts +++ b/site/src/api/queries/templates.ts @@ -129,6 +129,15 @@ export const templateVersionVariables = (versionId: string) => { }; }; +export const createTemplateVersion = (orgId: string) => { + return { + mutationFn: async (request: CreateTemplateVersionRequest) => { + const newVersion = await API.createTemplateVersion(orgId, request); + return newVersion; + }, + }; +}; + export const createAndBuildTemplateVersion = (orgId: string) => { return { mutationFn: async (request: CreateTemplateVersionRequest) => { diff --git a/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditorPage.tsx b/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditorPage.tsx index 82b7e1417e4d1..5fd26b999ff90 100644 --- a/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditorPage.tsx +++ b/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditorPage.tsx @@ -7,7 +7,11 @@ import { useNavigate, useParams } from "react-router-dom"; import { pageTitle } from "utils/page"; import { templateVersionEditorMachine } from "xServices/templateVersionEditor/templateVersionEditorXService"; import { useMutation, useQuery } from "react-query"; -import { templateByName, templateVersionByName } from "api/queries/templates"; +import { + createTemplateVersion, + templateByName, + templateVersionByName, +} from "api/queries/templates"; import { file, uploadFile } from "api/queries/files"; import { TarReader, TarWriter } from "utils/tar"; import { FileTree, traverse } from "utils/filetree"; @@ -40,6 +44,9 @@ export const TemplateVersionEditorPage: FC = () => { const [fileTree, setFileTree] = useState(); const uploadFileMutation = useMutation(uploadFile()); const currentTarFileRef = useRef(null); + const createTemplateVersionMutation = useMutation( + createTemplateVersion(orgId), + ); useEffect(() => { const initialize = async (file: ArrayBuffer) => { @@ -75,17 +82,23 @@ export const TemplateVersionEditorPage: FC = () => { if (!currentTarFileRef.current) { return; } - const newVersionFile = await generateVersionFiles( currentTarFileRef.current, newFileTree, ); - const newVersionUpload = await uploadFileMutation.mutateAsync( + const serverFile = await uploadFileMutation.mutateAsync( newVersionFile, ); + const newVersion = await createTemplateVersionMutation.mutateAsync({ + provisioner: "terraform", + storage_method: "file", + tags: {}, + template_id: templateQuery.data.id, + file_id: serverFile.hash, + }); sendEvent({ - type: "CREATE_VERSION", - fileId: newVersionUpload.hash, + type: "CREATED_VERSION", + data: newVersion, }); }} onPublish={() => { diff --git a/site/src/xServices/templateVersionEditor/templateVersionEditorXService.ts b/site/src/xServices/templateVersionEditor/templateVersionEditorXService.ts index 6f7b4f663636a..e1af1273f079c 100644 --- a/site/src/xServices/templateVersionEditor/templateVersionEditorXService.ts +++ b/site/src/xServices/templateVersionEditor/templateVersionEditorXService.ts @@ -31,8 +31,8 @@ export const templateVersionEditorMachine = createMachine( context: {} as TemplateVersionEditorMachineContext, events: {} as | { - type: "CREATE_VERSION"; - fileId: string; + type: "CREATED_VERSION"; + data: TemplateVersion; } | { type: "SET_MISSING_VARIABLE_VALUES"; @@ -69,9 +69,9 @@ export const templateVersionEditorMachine = createMachine( states: { idle: { on: { - CREATE_VERSION: { - actions: ["resetCreateBuildData"], - target: "creatingBuild", + CREATED_VERSION: { + actions: ["resetCreateBuildData", "assignBuild"], + target: "watchingBuildLogs", }, PUBLISH: { target: "askPublishParameters", @@ -104,7 +104,6 @@ export const templateVersionEditorMachine = createMachine( }, }, }, - creatingBuild: { tags: "loading", invoke: { From c024a1ddb5490082ee1c53dbcea21ab5e331a8b6 Mon Sep 17 00:00:00 2001 From: BrunoQuaresma Date: Wed, 1 Nov 2023 12:58:49 +0000 Subject: [PATCH 06/24] Extract logs --- .../TemplateVersionEditorPage.tsx | 53 ++++++++++++++--- .../templateVersionEditorXService.ts | 57 ------------------- 2 files changed, 46 insertions(+), 64 deletions(-) diff --git a/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditorPage.tsx b/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditorPage.tsx index 5fd26b999ff90..5666fe6026c35 100644 --- a/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditorPage.tsx +++ b/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditorPage.tsx @@ -19,6 +19,8 @@ import { createTemplateVersionFileTree, isAllowedFile, } from "utils/templateVersion"; +import { watchBuildLogsByTemplateVersionId } from "api/api"; +import { ProvisionerJobLog, TemplateVersion } from "api/typesGenerated"; type Params = { version: string; @@ -47,9 +49,12 @@ export const TemplateVersionEditorPage: FC = () => { const createTemplateVersionMutation = useMutation( createTemplateVersion(orgId), ); + const [currentVersionOnEditor, setCurrentVersionOnEditor] = + useState(); + // Initialize file tree useEffect(() => { - const initialize = async (file: ArrayBuffer) => { + const initializeFileTree = async (file: ArrayBuffer) => { const tarReader = new TarReader(); await tarReader.readFile(file); currentTarFileRef.current = tarReader; @@ -58,24 +63,56 @@ export const TemplateVersionEditorPage: FC = () => { }; if (fileQuery.data) { - initialize(fileQuery.data).catch(() => { + initializeFileTree(fileQuery.data).catch(() => { console.error("Error on initializing the editor"); }); } }, [fileQuery.data, sendEvent]); + // Initialize current version used on editor + useEffect(() => { + if (templateVersionQuery.data) { + setCurrentVersionOnEditor(templateVersionQuery.data); + } + }, [templateVersionQuery.data]); + + // Watch version logs + const [logs, setLogs] = useState([]); + useEffect(() => { + if (!currentVersionOnEditor) { + return; + } + + const socket = watchBuildLogsByTemplateVersionId( + currentVersionOnEditor.id, + { + onMessage: (log) => { + setLogs((logs) => [...logs, log]); + }, + onDone: () => { + sendEvent({ type: "BUILD_DONE" }); + }, + onError: (error) => { + console.error(error); + }, + }, + ); + + return () => { + socket.close(); + }; + }, [currentVersionOnEditor, sendEvent]); + return ( <> Codestin Search App - {templateQuery.data && templateVersionQuery.data && fileTree && ( + {templateQuery.data && currentVersionOnEditor && fileTree && ( { @@ -96,6 +133,8 @@ export const TemplateVersionEditorPage: FC = () => { template_id: templateQuery.data.id, file_id: serverFile.hash, }); + setLogs([]); + setCurrentVersionOnEditor(newVersion); sendEvent({ type: "CREATED_VERSION", data: newVersion, @@ -140,7 +179,7 @@ export const TemplateVersionEditorPage: FC = () => { editorState.context.version?.job.status !== "succeeded" } resources={editorState.context.resources} - buildLogs={editorState.context.buildLogs} + buildLogs={logs} isPromptingMissingVariables={editorState.matches("promptVariables")} missingVariables={editorState.context.missingVariables} onSubmitMissingVariableValues={(values) => { diff --git a/site/src/xServices/templateVersionEditor/templateVersionEditorXService.ts b/site/src/xServices/templateVersionEditor/templateVersionEditorXService.ts index e1af1273f079c..32b3ecd563aa3 100644 --- a/site/src/xServices/templateVersionEditor/templateVersionEditorXService.ts +++ b/site/src/xServices/templateVersionEditor/templateVersionEditorXService.ts @@ -1,6 +1,4 @@ import { - ProvisionerJobLog, - ProvisionerJobStatus, TemplateVersion, TemplateVersionVariable, VariableValue, @@ -15,7 +13,6 @@ export interface TemplateVersionEditorMachineContext { templateId?: string; version?: TemplateVersion; resources?: WorkspaceResource[]; - buildLogs?: ProvisionerJobLog[]; publishingError?: unknown; lastSuccessfulPublishedVersion?: TemplateVersion; missingVariables?: TemplateVersionVariable[]; @@ -40,7 +37,6 @@ export const templateVersionEditorMachine = createMachine( fileId: string; } | { type: "CANCEL_MISSING_VARIABLE_VALUES" } - | { type: "ADD_BUILD_LOG"; log: ProvisionerJobLog } | { type: "BUILD_DONE" } | { type: "PUBLISH" } | ({ type: "CONFIRM_PUBLISH" } & PublishVersionData) @@ -118,14 +114,7 @@ export const templateVersionEditorMachine = createMachine( watchingBuildLogs: { tags: "loading", - invoke: { - id: "watchBuildLogs", - src: "watchBuildLogs", - }, on: { - ADD_BUILD_LOG: { - actions: "addBuildLog", - }, BUILD_DONE: "fetchingVersion", }, }, @@ -192,7 +181,6 @@ export const templateVersionEditorMachine = createMachine( { actions: { resetCreateBuildData: assign({ - buildLogs: (_, _1) => [], resources: (_, _1) => [], }), assignResources: assign({ @@ -205,28 +193,6 @@ export const templateVersionEditorMachine = createMachine( lastSuccessfulPublishedVersion: (ctx) => ctx.version, version: () => undefined, }), - addBuildLog: assign({ - buildLogs: (context, event) => { - const previousLogs = context.buildLogs ?? []; - return [...previousLogs, event.log]; - }, - // Instead of periodically fetching the version, - // we just assume the state is running after the first log. - // - // The machine fetches the version after the log stream ends anyways! - version: (context) => { - if (!context.version || context.buildLogs?.length !== 0) { - return context.version; - } - return { - ...context.version, - job: { - ...context.version.job, - status: "running" as ProvisionerJobStatus, - }, - }; - }, - }), assignPublishingError: assign({ publishingError: (_, event) => event.data, }), @@ -261,29 +227,6 @@ export const templateVersionEditorMachine = createMachine( } return API.getTemplateVersion(ctx.version.id); }, - watchBuildLogs: - ({ version }) => - async (callback) => { - if (!version) { - throw new Error("version must be set"); - } - - const socket = API.watchBuildLogsByTemplateVersionId(version.id, { - onMessage: (log) => { - callback({ type: "ADD_BUILD_LOG", log }); - }, - onDone: () => { - callback({ type: "BUILD_DONE" }); - }, - onError: (error) => { - console.error(error); - }, - }); - - return () => { - socket.close(); - }; - }, getResources: (ctx) => { if (!ctx.version) { throw new Error("template version must be set"); From e7713feacd23eca4705f530214a91a890a7dd5fb Mon Sep 17 00:00:00 2001 From: BrunoQuaresma Date: Wed, 1 Nov 2023 13:59:29 +0000 Subject: [PATCH 07/24] Refactor version fetching and refetch --- .../TemplateVersionEditorPage.tsx | 69 ++++++++++--------- .../templateVersionEditorXService.ts | 12 +--- 2 files changed, 40 insertions(+), 41 deletions(-) diff --git a/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditorPage.tsx b/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditorPage.tsx index 5666fe6026c35..fa975e0d0d849 100644 --- a/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditorPage.tsx +++ b/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditorPage.tsx @@ -6,7 +6,7 @@ import { Helmet } from "react-helmet-async"; import { useNavigate, useParams } from "react-router-dom"; import { pageTitle } from "utils/page"; import { templateVersionEditorMachine } from "xServices/templateVersionEditor/templateVersionEditorXService"; -import { useMutation, useQuery } from "react-query"; +import { useMutation, useQuery, useQueryClient } from "react-query"; import { createTemplateVersion, templateByName, @@ -20,7 +20,7 @@ import { isAllowedFile, } from "utils/templateVersion"; import { watchBuildLogsByTemplateVersionId } from "api/api"; -import { ProvisionerJobLog, TemplateVersion } from "api/typesGenerated"; +import { ProvisionerJobLog } from "api/typesGenerated"; type Params = { version: string; @@ -28,14 +28,22 @@ type Params = { }; export const TemplateVersionEditorPage: FC = () => { + const queryClient = useQueryClient(); const navigate = useNavigate(); const { version: versionName, template: templateName } = useParams() as Params; const orgId = useOrganizationId(); + const [currentVersionName, setCurrentVersionName] = useState(versionName); const templateQuery = useQuery(templateByName(orgId, templateName)); - const templateVersionQuery = useQuery( - templateVersionByName(orgId, templateName, versionName), + const templateVersionOptions = templateVersionByName( + orgId, + templateName, + currentVersionName, ); + const templateVersionQuery = useQuery({ + ...templateVersionOptions, + keepPreviousData: true, + }); const fileQuery = useQuery({ ...file(templateVersionQuery.data?.job.file_id ?? ""), enabled: templateVersionQuery.isSuccess, @@ -49,8 +57,6 @@ export const TemplateVersionEditorPage: FC = () => { const createTemplateVersionMutation = useMutation( createTemplateVersion(orgId), ); - const [currentVersionOnEditor, setCurrentVersionOnEditor] = - useState(); // Initialize file tree useEffect(() => { @@ -69,39 +75,36 @@ export const TemplateVersionEditorPage: FC = () => { } }, [fileQuery.data, sendEvent]); - // Initialize current version used on editor - useEffect(() => { - if (templateVersionQuery.data) { - setCurrentVersionOnEditor(templateVersionQuery.data); - } - }, [templateVersionQuery.data]); - // Watch version logs const [logs, setLogs] = useState([]); + const templateVersionId = templateVersionQuery.data?.id; + const refetchTemplateVersion = templateVersionQuery.refetch; + const templateVersionStatus = templateVersionQuery.data?.job.status; useEffect(() => { - if (!currentVersionOnEditor) { + if (!templateVersionId || !templateVersionStatus) { + return; + } + + if (templateVersionStatus !== "running") { return; } - const socket = watchBuildLogsByTemplateVersionId( - currentVersionOnEditor.id, - { - onMessage: (log) => { - setLogs((logs) => [...logs, log]); - }, - onDone: () => { - sendEvent({ type: "BUILD_DONE" }); - }, - onError: (error) => { - console.error(error); - }, + const socket = watchBuildLogsByTemplateVersionId(templateVersionId, { + onMessage: (log) => { + setLogs((logs) => [...logs, log]); + }, + onDone: async () => { + await refetchTemplateVersion(); }, - ); + onError: (error) => { + console.error(error); + }, + }); return () => { socket.close(); }; - }, [currentVersionOnEditor, sendEvent]); + }, [refetchTemplateVersion, templateVersionId, templateVersionStatus]); return ( <> @@ -109,10 +112,10 @@ export const TemplateVersionEditorPage: FC = () => { Codestin Search App - {templateQuery.data && currentVersionOnEditor && fileTree && ( + {templateQuery.data && templateVersionQuery.data && fileTree && ( { @@ -134,7 +137,11 @@ export const TemplateVersionEditorPage: FC = () => { file_id: serverFile.hash, }); setLogs([]); - setCurrentVersionOnEditor(newVersion); + setCurrentVersionName(newVersion.name); + queryClient.setQueryData( + templateVersionOptions.queryKey, + newVersion, + ); sendEvent({ type: "CREATED_VERSION", data: newVersion, diff --git a/site/src/xServices/templateVersionEditor/templateVersionEditorXService.ts b/site/src/xServices/templateVersionEditor/templateVersionEditorXService.ts index 32b3ecd563aa3..471dbb1ca3564 100644 --- a/site/src/xServices/templateVersionEditor/templateVersionEditorXService.ts +++ b/site/src/xServices/templateVersionEditor/templateVersionEditorXService.ts @@ -37,7 +37,6 @@ export const templateVersionEditorMachine = createMachine( fileId: string; } | { type: "CANCEL_MISSING_VARIABLE_VALUES" } - | { type: "BUILD_DONE" } | { type: "PUBLISH" } | ({ type: "CONFIRM_PUBLISH" } & PublishVersionData) | { type: "CANCEL_PUBLISH" }, @@ -67,7 +66,7 @@ export const templateVersionEditorMachine = createMachine( on: { CREATED_VERSION: { actions: ["resetCreateBuildData", "assignBuild"], - target: "watchingBuildLogs", + target: "fetchingVersion", }, PUBLISH: { target: "askPublishParameters", @@ -107,18 +106,11 @@ export const templateVersionEditorMachine = createMachine( src: "createBuild", onDone: { actions: "assignBuild", - target: "watchingBuildLogs", + target: "fetchingVersion", }, }, }, - watchingBuildLogs: { - tags: "loading", - on: { - BUILD_DONE: "fetchingVersion", - }, - }, - fetchingVersion: { tags: "loading", invoke: { From f782c7d1bc499cd4d6336e4ea2152fc45ab05661 Mon Sep 17 00:00:00 2001 From: BrunoQuaresma Date: Wed, 1 Nov 2023 14:11:11 +0000 Subject: [PATCH 08/24] Extract resources --- site/src/api/queries/templates.ts | 7 ++++ .../TemplateVersionEditorPage.tsx | 18 +++++++--- .../templateVersionEditorXService.ts | 33 ++----------------- 3 files changed, 22 insertions(+), 36 deletions(-) diff --git a/site/src/api/queries/templates.ts b/site/src/api/queries/templates.ts index 6176d8994b30a..406f7a0bb53e1 100644 --- a/site/src/api/queries/templates.ts +++ b/site/src/api/queries/templates.ts @@ -225,6 +225,13 @@ export const richParameters = (versionId: string) => { }; }; +export const resources = (versionId: string) => { + return { + queryKey: ["templateVersion", versionId, "resources"], + queryFn: () => API.getTemplateVersionResources(versionId), + }; +}; + const waitBuildToBeFinished = async (version: TemplateVersion) => { let data: TemplateVersion; let jobStatus: ProvisionerJobStatus; diff --git a/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditorPage.tsx b/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditorPage.tsx index fa975e0d0d849..fb46eb58a0901 100644 --- a/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditorPage.tsx +++ b/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditorPage.tsx @@ -9,6 +9,7 @@ import { templateVersionEditorMachine } from "xServices/templateVersionEditor/te import { useMutation, useQuery, useQueryClient } from "react-query"; import { createTemplateVersion, + resources, templateByName, templateVersionByName, } from "api/queries/templates"; @@ -57,6 +58,10 @@ export const TemplateVersionEditorPage: FC = () => { const createTemplateVersionMutation = useMutation( createTemplateVersion(orgId), ); + const resourcesQuery = useQuery({ + ...resources(templateVersionQuery.data?.id ?? ""), + enabled: templateVersionQuery.data !== undefined, + }); // Initialize file tree useEffect(() => { @@ -89,6 +94,7 @@ export const TemplateVersionEditorPage: FC = () => { return; } + setLogs([]); const socket = watchBuildLogsByTemplateVersionId(templateVersionId, { onMessage: (log) => { setLogs((logs) => [...logs, log]); @@ -116,7 +122,9 @@ export const TemplateVersionEditorPage: FC = () => { { if (!currentTarFileRef.current) { @@ -136,7 +144,7 @@ export const TemplateVersionEditorPage: FC = () => { template_id: templateQuery.data.id, file_id: serverFile.hash, }); - setLogs([]); + setCurrentVersionName(newVersion.name); queryClient.setQueryData( templateVersionOptions.queryKey, @@ -182,10 +190,10 @@ export const TemplateVersionEditorPage: FC = () => { }} disablePreview={editorState.hasTag("loading")} disableUpdate={ - editorState.hasTag("loading") || - editorState.context.version?.job.status !== "succeeded" + templateVersionQuery.data.job.status === "running" || + templateVersionQuery.data.job.status !== "succeeded" } - resources={editorState.context.resources} + resources={resourcesQuery.data} buildLogs={logs} isPromptingMissingVariables={editorState.matches("promptVariables")} missingVariables={editorState.context.missingVariables} diff --git a/site/src/xServices/templateVersionEditor/templateVersionEditorXService.ts b/site/src/xServices/templateVersionEditor/templateVersionEditorXService.ts index 471dbb1ca3564..23e197afab7e1 100644 --- a/site/src/xServices/templateVersionEditor/templateVersionEditorXService.ts +++ b/site/src/xServices/templateVersionEditor/templateVersionEditorXService.ts @@ -2,7 +2,6 @@ import { TemplateVersion, TemplateVersionVariable, VariableValue, - WorkspaceResource, } from "api/typesGenerated"; import { assign, createMachine } from "xstate"; import * as API from "api/api"; @@ -12,7 +11,6 @@ export interface TemplateVersionEditorMachineContext { orgId: string; templateId?: string; version?: TemplateVersion; - resources?: WorkspaceResource[]; publishingError?: unknown; lastSuccessfulPublishedVersion?: TemplateVersion; missingVariables?: TemplateVersionVariable[]; @@ -48,9 +46,6 @@ export const templateVersionEditorMachine = createMachine( fetchVersion: { data: TemplateVersion; }; - getResources: { - data: WorkspaceResource[]; - }; publishingVersion: { data: void; }; @@ -65,7 +60,7 @@ export const templateVersionEditorMachine = createMachine( idle: { on: { CREATED_VERSION: { - actions: ["resetCreateBuildData", "assignBuild"], + actions: ["assignBuild"], target: "fetchingVersion", }, PUBLISH: { @@ -125,7 +120,7 @@ export const templateVersionEditorMachine = createMachine( }, { actions: ["assignBuild"], - target: "fetchResources", + target: "idle", }, ], }, @@ -156,28 +151,10 @@ export const templateVersionEditorMachine = createMachine( }, }, }, - - fetchResources: { - tags: "loading", - invoke: { - id: "getResources", - src: "getResources", - onDone: { - actions: ["assignResources"], - target: "idle", - }, - }, - }, }, }, { actions: { - resetCreateBuildData: assign({ - resources: (_, _1) => [], - }), - assignResources: assign({ - resources: (_, event) => event.data, - }), assignBuild: assign({ version: (_, event) => event.data, }), @@ -219,12 +196,6 @@ export const templateVersionEditorMachine = createMachine( } return API.getTemplateVersion(ctx.version.id); }, - getResources: (ctx) => { - if (!ctx.version) { - throw new Error("template version must be set"); - } - return API.getTemplateVersionResources(ctx.version.id); - }, publishingVersion: async ( { version, templateId }, { name, message, isActiveVersion }, From 8bf23af57d8a5e0a55ee241f6af78640321a0f65 Mon Sep 17 00:00:00 2001 From: BrunoQuaresma Date: Wed, 1 Nov 2023 15:43:08 +0000 Subject: [PATCH 09/24] Extract missing variables --- .../TemplateVersionEditorPage.tsx | 58 +++++--- .../templateVersionEditorXService.ts | 128 +----------------- 2 files changed, 42 insertions(+), 144 deletions(-) diff --git a/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditorPage.tsx b/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditorPage.tsx index fb46eb58a0901..243d648304963 100644 --- a/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditorPage.tsx +++ b/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditorPage.tsx @@ -12,6 +12,7 @@ import { resources, templateByName, templateVersionByName, + templateVersionVariables, } from "api/queries/templates"; import { file, uploadFile } from "api/queries/files"; import { TarReader, TarWriter } from "utils/tar"; @@ -31,10 +32,11 @@ type Params = { export const TemplateVersionEditorPage: FC = () => { const queryClient = useQueryClient(); const navigate = useNavigate(); - const { version: versionName, template: templateName } = + const { version: initialVersionName, template: templateName } = useParams() as Params; const orgId = useOrganizationId(); - const [currentVersionName, setCurrentVersionName] = useState(versionName); + const [currentVersionName, setCurrentVersionName] = + useState(initialVersionName); const templateQuery = useQuery(templateByName(orgId, templateName)); const templateVersionOptions = templateVersionByName( orgId, @@ -60,7 +62,7 @@ export const TemplateVersionEditorPage: FC = () => { ); const resourcesQuery = useQuery({ ...resources(templateVersionQuery.data?.id ?? ""), - enabled: templateVersionQuery.data !== undefined, + enabled: templateVersionQuery.data?.job.status === "succeeded", }); // Initialize file tree @@ -112,6 +114,21 @@ export const TemplateVersionEditorPage: FC = () => { }; }, [refetchTemplateVersion, templateVersionId, templateVersionStatus]); + // Handling missing variables + const missingVariablesQuery = useQuery({ + ...templateVersionVariables(templateVersionQuery.data?.id ?? ""), + enabled: + templateVersionQuery.data?.job.error_code === + "REQUIRED_TEMPLATE_VARIABLES", + }); + const [isMissingVariablesDialogOpen, setIsMissingVariablesDialogOpen] = + useState(false); + useEffect(() => { + if (missingVariablesQuery.data) { + setIsMissingVariablesDialogOpen(true); + } + }, [missingVariablesQuery.data]); + return ( <> @@ -150,10 +167,6 @@ export const TemplateVersionEditorPage: FC = () => { templateVersionOptions.queryKey, newVersion, ); - sendEvent({ - type: "CREATED_VERSION", - data: newVersion, - }); }} onPublish={() => { sendEvent({ @@ -195,19 +208,30 @@ export const TemplateVersionEditorPage: FC = () => { } resources={resourcesQuery.data} buildLogs={logs} - isPromptingMissingVariables={editorState.matches("promptVariables")} - missingVariables={editorState.context.missingVariables} - onSubmitMissingVariableValues={(values) => { - sendEvent({ - type: "SET_MISSING_VARIABLE_VALUES", - values, - fileId: uploadFileMutation.data!.hash, + isPromptingMissingVariables={isMissingVariablesDialogOpen} + missingVariables={missingVariablesQuery.data} + onSubmitMissingVariableValues={async (values) => { + if (!uploadFileMutation.data) { + return; + } + + const newVersion = await createTemplateVersionMutation.mutateAsync({ + provisioner: "terraform", + storage_method: "file", + tags: {}, + template_id: templateQuery.data.id, + file_id: uploadFileMutation.data.hash, + user_variable_values: values, }); + setCurrentVersionName(newVersion.name); + queryClient.setQueryData( + templateVersionOptions.queryKey, + newVersion, + ); + setIsMissingVariablesDialogOpen(false); }} onCancelSubmitMissingVariableValues={() => { - sendEvent({ - type: "CANCEL_MISSING_VARIABLE_VALUES", - }); + setIsMissingVariablesDialogOpen(false); }} /> )} diff --git a/site/src/xServices/templateVersionEditor/templateVersionEditorXService.ts b/site/src/xServices/templateVersionEditor/templateVersionEditorXService.ts index 23e197afab7e1..5c4018f56c3dc 100644 --- a/site/src/xServices/templateVersionEditor/templateVersionEditorXService.ts +++ b/site/src/xServices/templateVersionEditor/templateVersionEditorXService.ts @@ -1,8 +1,4 @@ -import { - TemplateVersion, - TemplateVersionVariable, - VariableValue, -} from "api/typesGenerated"; +import { TemplateVersion } from "api/typesGenerated"; import { assign, createMachine } from "xstate"; import * as API from "api/api"; import { PublishVersionData } from "pages/TemplateVersionEditorPage/types"; @@ -13,8 +9,6 @@ export interface TemplateVersionEditorMachineContext { version?: TemplateVersion; publishingError?: unknown; lastSuccessfulPublishedVersion?: TemplateVersion; - missingVariables?: TemplateVersionVariable[]; - missingVariableValues?: VariableValue[]; } export const templateVersionEditorMachine = createMachine( @@ -25,33 +19,14 @@ export const templateVersionEditorMachine = createMachine( schema: { context: {} as TemplateVersionEditorMachineContext, events: {} as - | { - type: "CREATED_VERSION"; - data: TemplateVersion; - } - | { - type: "SET_MISSING_VARIABLE_VALUES"; - values: VariableValue[]; - fileId: string; - } - | { type: "CANCEL_MISSING_VARIABLE_VALUES" } | { type: "PUBLISH" } | ({ type: "CONFIRM_PUBLISH" } & PublishVersionData) | { type: "CANCEL_PUBLISH" }, services: {} as { - createBuild: { - data: TemplateVersion; - }; - fetchVersion: { - data: TemplateVersion; - }; publishingVersion: { data: void; }; - loadMissingVariables: { - data: TemplateVersionVariable[]; - }; }, }, tsTypes: {} as import("./templateVersionEditorXService.typegen").Typegen0, @@ -59,10 +34,6 @@ export const templateVersionEditorMachine = createMachine( states: { idle: { on: { - CREATED_VERSION: { - actions: ["assignBuild"], - target: "fetchingVersion", - }, PUBLISH: { target: "askPublishParameters", }, @@ -94,70 +65,10 @@ export const templateVersionEditorMachine = createMachine( }, }, }, - creatingBuild: { - tags: "loading", - invoke: { - id: "createBuild", - src: "createBuild", - onDone: { - actions: "assignBuild", - target: "fetchingVersion", - }, - }, - }, - - fetchingVersion: { - tags: "loading", - invoke: { - id: "fetchVersion", - src: "fetchVersion", - - onDone: [ - { - actions: ["assignBuild"], - target: "promptVariables", - cond: "jobFailedWithMissingVariables", - }, - { - actions: ["assignBuild"], - target: "idle", - }, - ], - }, - }, - - promptVariables: { - initial: "loadingMissingVariables", - states: { - loadingMissingVariables: { - invoke: { - src: "loadMissingVariables", - onDone: { - actions: "assignMissingVariables", - target: "idle", - }, - }, - }, - idle: { - on: { - SET_MISSING_VARIABLE_VALUES: { - actions: "assignMissingVariableValues", - target: "#templateVersionEditor.creatingBuild", - }, - CANCEL_MISSING_VARIABLE_VALUES: { - target: "#templateVersionEditor.idle", - }, - }, - }, - }, - }, }, }, { actions: { - assignBuild: assign({ - version: (_, event) => event.data, - }), assignLastSuccessfulPublishedVersion: assign({ lastSuccessfulPublishedVersion: (ctx) => ctx.version, version: () => undefined, @@ -169,33 +80,8 @@ export const templateVersionEditorMachine = createMachine( clearLastSuccessfulPublishedVersion: assign({ lastSuccessfulPublishedVersion: (_) => undefined, }), - assignMissingVariables: assign({ - missingVariables: (_, event) => event.data, - }), - assignMissingVariableValues: assign({ - missingVariableValues: (_, event) => event.values, - }), }, services: { - createBuild: async (ctx, event) => { - if (ctx.version?.job.status === "running") { - await API.cancelTemplateVersionBuild(ctx.version.id); - } - return API.createTemplateVersion(ctx.orgId, { - provisioner: "terraform", - storage_method: "file", - tags: {}, - template_id: ctx.templateId, - file_id: event.fileId, - user_variable_values: ctx.missingVariableValues, - }); - }, - fetchVersion: (ctx) => { - if (!ctx.version) { - throw new Error("template version must be set"); - } - return API.getTemplateVersion(ctx.version.id); - }, publishingVersion: async ( { version, templateId }, { name, message, isActiveVersion }, @@ -219,18 +105,6 @@ export const templateVersionEditorMachine = createMachine( : Promise.resolve(), ]); }, - loadMissingVariables: ({ version }) => { - if (!version) { - throw new Error("Version is not set"); - } - const variables = API.getTemplateVersionVariables(version.id); - return variables; - }, - }, - guards: { - jobFailedWithMissingVariables: (_, { data }) => { - return data.job.error_code === "REQUIRED_TEMPLATE_VARIABLES"; - }, }, }, ); From 5502077e68283ace6c74739eb55175da4d3c8685 Mon Sep 17 00:00:00 2001 From: BrunoQuaresma Date: Wed, 1 Nov 2023 16:54:52 +0000 Subject: [PATCH 10/24] Extract publish from the machine and remove it --- .../TemplateVersionEditorPage.tsx | 81 ++++++++----- .../templateVersionEditorXService.ts | 110 ------------------ 2 files changed, 53 insertions(+), 138 deletions(-) delete mode 100644 site/src/xServices/templateVersionEditor/templateVersionEditorXService.ts diff --git a/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditorPage.tsx b/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditorPage.tsx index 243d648304963..731023580f64b 100644 --- a/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditorPage.tsx +++ b/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditorPage.tsx @@ -1,11 +1,9 @@ -import { useMachine } from "@xstate/react"; import { TemplateVersionEditor } from "./TemplateVersionEditor"; import { useOrganizationId } from "hooks/useOrganizationId"; import { FC, useEffect, useRef, useState } from "react"; import { Helmet } from "react-helmet-async"; import { useNavigate, useParams } from "react-router-dom"; import { pageTitle } from "utils/page"; -import { templateVersionEditorMachine } from "xServices/templateVersionEditor/templateVersionEditorXService"; import { useMutation, useQuery, useQueryClient } from "react-query"; import { createTemplateVersion, @@ -21,8 +19,16 @@ import { createTemplateVersionFileTree, isAllowedFile, } from "utils/templateVersion"; -import { watchBuildLogsByTemplateVersionId } from "api/api"; -import { ProvisionerJobLog } from "api/typesGenerated"; +import { + patchTemplateVersion, + updateActiveTemplateVersion, + watchBuildLogsByTemplateVersionId, +} from "api/api"; +import { + PatchTemplateVersionRequest, + ProvisionerJobLog, + TemplateVersion, +} from "api/typesGenerated"; type Params = { version: string; @@ -51,9 +57,6 @@ export const TemplateVersionEditorPage: FC = () => { ...file(templateVersionQuery.data?.job.file_id ?? ""), enabled: templateVersionQuery.isSuccess, }); - const [editorState, sendEvent] = useMachine(templateVersionEditorMachine, { - context: { orgId, templateId: templateQuery.data?.id }, - }); const [fileTree, setFileTree] = useState(); const uploadFileMutation = useMutation(uploadFile()); const currentTarFileRef = useRef(null); @@ -80,7 +83,7 @@ export const TemplateVersionEditorPage: FC = () => { console.error("Error on initializing the editor"); }); } - }, [fileQuery.data, sendEvent]); + }, [fileQuery.data]); // Watch version logs const [logs, setLogs] = useState([]); @@ -129,6 +132,14 @@ export const TemplateVersionEditorPage: FC = () => { } }, [missingVariablesQuery.data]); + // Handling publishing + const [isPublishingDialogOpen, setIsPublishingDialogOpen] = useState(false); + const publishVersionMutation = useMutation({ + mutationFn: publishVersion, + }); + const [lastSuccessfulPublishedVersion, setLastSuccessfulPublishedVersion] = + useState(); + return ( <> @@ -169,31 +180,27 @@ export const TemplateVersionEditorPage: FC = () => { ); }} onPublish={() => { - sendEvent({ - type: "PUBLISH", - }); + setIsPublishingDialogOpen(true); }} onCancelPublish={() => { - sendEvent({ - type: "CANCEL_PUBLISH", - }); + setIsPublishingDialogOpen(false); }} - onConfirmPublish={(data) => { - sendEvent({ - type: "CONFIRM_PUBLISH", - ...data, + onConfirmPublish={async ({ isActiveVersion, ...data }) => { + await publishVersionMutation.mutateAsync({ + isActiveVersion, + data, + version: templateVersionQuery.data, }); + setIsPublishingDialogOpen(false); + setLastSuccessfulPublishedVersion(templateVersionQuery.data); }} - isAskingPublishParameters={editorState.matches( - "askPublishParameters", - )} - isPublishing={editorState.matches("publishingVersion")} - publishingError={editorState.context.publishingError} - publishedVersion={editorState.context.lastSuccessfulPublishedVersion} + isAskingPublishParameters={isPublishingDialogOpen} + isPublishing={publishVersionMutation.isLoading} + publishingError={publishVersionMutation.error} + publishedVersion={lastSuccessfulPublishedVersion} onCreateWorkspace={() => { const params = new URLSearchParams(); - const publishedVersion = - editorState.context.lastSuccessfulPublishedVersion; + const publishedVersion = lastSuccessfulPublishedVersion; if (publishedVersion) { params.set("version", publishedVersion.id); } @@ -201,10 +208,10 @@ export const TemplateVersionEditorPage: FC = () => { `/templates/${templateName}/workspace?${params.toString()}`, ); }} - disablePreview={editorState.hasTag("loading")} + disablePreview={templateVersionQuery.data.job.status !== "succeeded"} disableUpdate={ templateVersionQuery.data.job.status === "running" || - templateVersionQuery.data.job.status !== "succeeded" + templateVersionQuery.data.job.status === "succeeded" } resources={resourcesQuery.data} buildLogs={logs} @@ -283,4 +290,22 @@ const generateVersionFiles = async ( return new File([blob], "template.tar"); }; +const publishVersion = async (options: { + version: TemplateVersion; + data: PatchTemplateVersionRequest; + isActiveVersion: boolean; +}) => { + const { version, data, isActiveVersion } = options; + const haveChanges = + data.name !== version.name || data.message !== version.message; + return Promise.all([ + haveChanges ? patchTemplateVersion(version.id, data) : Promise.resolve(), + isActiveVersion + ? updateActiveTemplateVersion(version.template_id!, { + id: version.id, + }) + : Promise.resolve(), + ]); +}; + export default TemplateVersionEditorPage; diff --git a/site/src/xServices/templateVersionEditor/templateVersionEditorXService.ts b/site/src/xServices/templateVersionEditor/templateVersionEditorXService.ts deleted file mode 100644 index 5c4018f56c3dc..0000000000000 --- a/site/src/xServices/templateVersionEditor/templateVersionEditorXService.ts +++ /dev/null @@ -1,110 +0,0 @@ -import { TemplateVersion } from "api/typesGenerated"; -import { assign, createMachine } from "xstate"; -import * as API from "api/api"; -import { PublishVersionData } from "pages/TemplateVersionEditorPage/types"; - -export interface TemplateVersionEditorMachineContext { - orgId: string; - templateId?: string; - version?: TemplateVersion; - publishingError?: unknown; - lastSuccessfulPublishedVersion?: TemplateVersion; -} - -export const templateVersionEditorMachine = createMachine( - { - /** @xstate-layout N4IgpgJg5mDOIC5QBcwFsAOAbAhqgamAE6wCWA9gHYCiEpy5RAdKZfaTlqQF6tQDEASQByggCqCAggBlBALWoBtAAwBdRKAzkyyCpQ0gAHogC0AFgDsypgDYAnAFZlFpzefKbADgA0IAJ6IAIzKngDMTIF2FmYONoFW9s4Avkm+qJi4BMRkVLT0jCwQWGD8AMIAStSSYtQA+vjU5QDKggDywirqSCBaOnoGxggmoTYOTKEATIEOThNxDmajvgEIgTFjDhaeNpPTZpbRKWno2HhghCR6eQzMpEUlAAoAqgBCsk0AEp0GvexUA6Y5mNlKEzBNPJ5Ap4JjCbEt-EEJsozEw7MoHKEQhYJqFPBYbBYjiB0qcspdcnQboVivwACKCJoAWQZTVqzzeDI+tRekmEwka326v10-26gxMcM8TGcoRGc0CNjMyg8yyCkzsqOUkWcnjMEJhRJJmXO2SulIKOFgAGsHgBXABGXFgAAsHjgiDg0GBUCQyrzStRpGzXu8vmofto-voxaZAvFxnZ9mFonY7NswarVuCLEwLPjQlCPGsJodUsSTsaLjkaObmJabQ6na73Z7vdkyu0AGKCcqM4Mcz6CzSRkXR0CDOM5ta6ya4jHbZR2TNx1O5iHTHEWQLgzyGitnKtm-LMDCN0guviHqj8CBUMAsSgAN3IVvvp8d5+dl9NVCHPRH-QxggSrWOioSOHCdhzCEMzLuCGoFqMkJmGsgRynuGQHj+NbHkw75Nt+5KUPwxBEAUpIAGaMGgeFnhelBQFelB-sKgHjkEcbStESKbGYaZFoEy6LlKcJePE4IzDYEwYaSJpEdcBQAMY4JQilgFwDGCJQDxkVARBwLALy2qQWAQDed4Ps+r5MMpqnqUZJkQCxAGiuxQyhA4UpmLiLhePYaEjMuFgFqibjOPmqbKNJZZGlh8m1kwtrYOQOAQGI7rmZQ96sFZ95JVgKVpe6zl9K5RimFETAljioI4mmdgjBYy7QhsepWE4DVQShMmVthCnMIp+l4HwDmmZl2VPi+96DWAZyjU54ZCi5Y7lcBDgTNKeITGm+LYutNjNRMrV4uii7gRM+w9XF1b9UwADueCKV+DHzdI5BQLA-CSLStLck8gjSL90itAA4iVUYAggjg5o4UxJutkzbEFDgakionbImMKeVdZI3QlD3IE9I3GaZb0ffwLz-YDtS0u0SiLcOpUrYMvlMJJowwgqiZpsumPSoWnjIrEMTBTjcl47hBNEy9JMQGTn2lP6gb1I0LTtODo6QyYeKoidaZxBd0JxMuUnWCjFipiMITeeBYtMbdUvPVAr3vQrlTVHUDTNG0HQM-+TNa3ENi5vEnjQ6m20o4dgS2GC6L1W4upmHbfUJRR3rS4x2HjZZU1MOnhPOkxGtsatUkorqmxWLKFuC4JCIIHDwcuAj60uKCiwp-FuEF5nTE5zlee90X2GKIEXSMxDQHBDsuZah5cbBHYAWZk3uYzJEDVxlJdhdxLVIYGRmDIPg7ocI6cBMAVqV8Iy55kAxp9EOfxSfbeWW59ZsW40eB9HxgJ8z44AvrAK+hVb730vEAkBCBB7KVHJ0EuZVBhhVRAFTwvFI5TFXrVJg3l9Qll1CEGwe9f7kX-oA5+wDX7UhKE0agYhajMiaC0YQIN6iSHKFIN4nsZBPGoE0JBzNEAEgiDuWUippgW28qvdaG0rBrB3iEKKhIYr7h-hSXCh9yDHyfi-S+dwaSK2EAGIMzDWHsPwJw7h0heHSH4YIv2rFkFBEklVUI0ipgNSsPsVeJZg5RCxmhWYkRQikM0VSYe5Q4DkFtEQNSb8LKD2sjAZA0TYCxPiXAIRkNixjDQpCDwgTtz4hNk4WwDgFTKgxHxLcYSiSUHIBAOABhv7izIUQCMAcgImC3MHGUcog4gQOg3VMYx7DREWDEaEkJorHEwhonCVJWDsE4DwPgXSp5uQlCFPpUIkTInsBmBucYPARGiFFSYJYPAFnCUsgohiwCbM1j0gs8jqlgjRKmbcy4PIbTxEnI6BYYIODubdesdoPwujdB6L0Pp4BLW6ds7cGow6eVlOta2YIRkrG3MiTUGIsQ4jxASMFCV8KfkItWZ5pdBgeSlM4GYwtrZOF8ScuM4QkTgX2ASZUaZ6nzNkvbBKtk1IaSgFpHS719KwEMrLGlLihhKgZSUuuQIwg4tcZVYSWp4hSW2GEMluF8qFXSp0xFWzVrDEcNKSY6JDYzxxA4Q64R1ptxBEcw5RqqQzWGjLRyCrhGrEWGzDxVgwjeXWpCHwJzoSuqOkCKEnlwQkLUQs9pESCiO2Jo5eWgbIZwg2nHeeSIrAesOv0o5BIwR6lxLvNNQrU49wzk7Ji+agJeE5QSfyTgUYwjMKvLUGwbl2FGAU7qDberdz-jogBejqEtItS8ty20Y6JkWDCFw0zjkrCxuEFCCxLBxDzF6yd10Ol4QofOkBYCb4MTvrKqBVCQHtrcrKDU66pIlgWJ5HdiAcRKjQQsD1wRtoCvLOm4VWir3QJoY819q0wioo6mjLY2JFg4MVHg6Y4FZzbn7d6goUSYlxISQhicHiEJxCVCMewiotSrwKbYXYwQPAoTxCkFIQA */ - predictableActionArguments: true, - id: "templateVersionEditor", - schema: { - context: {} as TemplateVersionEditorMachineContext, - events: {} as - | { type: "PUBLISH" } - | ({ type: "CONFIRM_PUBLISH" } & PublishVersionData) - | { type: "CANCEL_PUBLISH" }, - - services: {} as { - publishingVersion: { - data: void; - }; - }, - }, - tsTypes: {} as import("./templateVersionEditorXService.typegen").Typegen0, - initial: "idle", - states: { - idle: { - on: { - PUBLISH: { - target: "askPublishParameters", - }, - }, - }, - - askPublishParameters: { - on: { - CANCEL_PUBLISH: "idle", - CONFIRM_PUBLISH: "publishingVersion", - }, - }, - - publishingVersion: { - tags: "loading", - entry: ["clearPublishingError", "clearLastSuccessfulPublishedVersion"], - invoke: { - id: "publishingVersion", - src: "publishingVersion", - - onError: { - actions: ["assignPublishingError"], - target: "askPublishParameters", - }, - - onDone: { - actions: ["assignLastSuccessfulPublishedVersion"], - target: ["idle"], - }, - }, - }, - }, - }, - { - actions: { - assignLastSuccessfulPublishedVersion: assign({ - lastSuccessfulPublishedVersion: (ctx) => ctx.version, - version: () => undefined, - }), - assignPublishingError: assign({ - publishingError: (_, event) => event.data, - }), - clearPublishingError: assign({ publishingError: (_) => undefined }), - clearLastSuccessfulPublishedVersion: assign({ - lastSuccessfulPublishedVersion: (_) => undefined, - }), - }, - services: { - publishingVersion: async ( - { version, templateId }, - { name, message, isActiveVersion }, - ) => { - if (!version) { - throw new Error("Version is not set"); - } - if (!templateId) { - throw new Error("Template is not set"); - } - const haveChanges = - name !== version.name || message !== version.message; - await Promise.all([ - haveChanges - ? API.patchTemplateVersion(version.id, { name, message }) - : Promise.resolve(), - isActiveVersion - ? API.updateActiveTemplateVersion(templateId, { - id: version.id, - }) - : Promise.resolve(), - ]); - }, - }, - }, -); From 20d6100b41bd6d85ea88cd364e63cc29fb31dadb Mon Sep 17 00:00:00 2001 From: BrunoQuaresma Date: Wed, 1 Nov 2023 16:55:16 +0000 Subject: [PATCH 11/24] Remove unused data module --- .../pages/TemplateVersionEditorPage/data.ts | 47 ------------------- 1 file changed, 47 deletions(-) delete mode 100644 site/src/pages/TemplateVersionEditorPage/data.ts diff --git a/site/src/pages/TemplateVersionEditorPage/data.ts b/site/src/pages/TemplateVersionEditorPage/data.ts deleted file mode 100644 index 19039d212c7d9..0000000000000 --- a/site/src/pages/TemplateVersionEditorPage/data.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { useQuery, UseQueryOptions } from "react-query"; -import { getFile, getTemplateByName, getTemplateVersionByName } from "api/api"; -import { TarReader } from "utils/tar"; -import { createTemplateVersionFileTree } from "utils/templateVersion"; - -const getTemplateVersionData = async ( - orgId: string, - templateName: string, - versionName: string, -) => { - const [template, version] = await Promise.all([ - getTemplateByName(orgId, templateName), - getTemplateVersionByName(orgId, templateName, versionName), - ]); - const tarFile = await getFile(version.job.file_id); - const tarReader = new TarReader(); - await tarReader.readFile(tarFile); - const fileTree = await createTemplateVersionFileTree(tarReader); - - return { - template, - version, - fileTree, - tarReader, - }; -}; - -type GetTemplateVersionResponse = Awaited< - ReturnType ->; - -type UseTemplateVersionDataParams = { - orgId: string; - templateName: string; - versionName: string; -}; - -export const useTemplateVersionData = ( - { templateName, versionName, orgId }: UseTemplateVersionDataParams, - options?: UseQueryOptions, -) => { - return useQuery({ - queryKey: ["templateVersion", templateName, versionName], - queryFn: () => getTemplateVersionData(orgId, templateName, versionName), - ...options, - }); -}; From 9d1ca6190343c9dc41a2df9ab02b9d6e13e3161b Mon Sep 17 00:00:00 2001 From: BrunoQuaresma Date: Wed, 1 Nov 2023 17:42:39 +0000 Subject: [PATCH 12/24] Fix loading state and tabs code to make it clear --- .../TemplateVersionEditor.tsx | 23 +++++++++--------- .../TemplateVersionEditorPage.tsx | 24 +++++++++++++------ 2 files changed, 29 insertions(+), 18 deletions(-) diff --git a/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditor.tsx b/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditor.tsx index 82988be7b0cca..f8d6bb9294969 100644 --- a/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditor.tsx +++ b/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditor.tsx @@ -111,9 +111,9 @@ export const TemplateVersionEditor: FC = ({ onCancelSubmitMissingVariableValues, }) => { const theme = useTheme(); - // If resources are provided, show them by default! - // This is for Storybook! - const [selectedTab, setSelectedTab] = useState(() => (resources ? 1 : 0)); + const [selectedTab, setSelectedTab] = useState< + "logs" | "resources" | undefined // Undefined is to hide the tab + >(); const [fileTree, setFileTree] = useState(defaultFileTree); const [createFileOpen, setCreateFileOpen] = useState(false); const [deleteFileOpen, setDeleteFileOpen] = useState(); @@ -126,7 +126,7 @@ export const TemplateVersionEditor: FC = ({ const triggerPreview = useCallback(() => { onPreview(fileTree); // Switch to the build log! - setSelectedTab(0); + setSelectedTab("logs"); }, [fileTree, onPreview]); // Stop ctrl+s from saving files and make ctrl+enter trigger a preview. @@ -159,11 +159,12 @@ export const TemplateVersionEditor: FC = ({ previousVersion.current = templateVersion; return; } + if ( ["running", "pending"].includes(previousVersion.current.job.status) && templateVersion.job.status === "succeeded" ) { - setSelectedTab(1); + setSelectedTab("resources"); setDirty(false); } previousVersion.current = templateVersion; @@ -358,9 +359,9 @@ export const TemplateVersionEditor: FC = ({