feat: handle template build error

This commit is contained in:
2024-12-02 13:45:49 +00:00
parent 8a8582e06e
commit dc97c2c498
9 changed files with 123 additions and 11 deletions

View File

@@ -1,5 +1,12 @@
import { promiseOrThrow } from "./lib/errors";
interface ApiErrorResponse {
code: string;
error: string;
}
const API_ERROR_BAD_TEMPLATE = "BAD_TEMPLATE";
enum ApiError {
NotFound = "NOT_FOUND",
BadRequest = "BAD_REQUEST",
@@ -16,10 +23,9 @@ async function fetchApi(
() => ApiError.Network,
);
if (res.status !== 200) {
console.log(res.status);
switch (res.status) {
case 401:
throw ApiError.BadRequest;
case 400:
throw await res.json();
case 404:
throw ApiError.NotFound;
default:
@@ -29,4 +35,15 @@ async function fetchApi(
return res;
}
export { ApiError, fetchApi };
function isApiErrorResponse(error: unknown): error is ApiErrorResponse {
return (
error !== null &&
error !== undefined &&
typeof error === "object" &&
"code" in error &&
"error" in error
);
}
export { API_ERROR_BAD_TEMPLATE, ApiError, fetchApi, isApiErrorResponse };
export type { ApiErrorResponse };

View File

@@ -144,6 +144,10 @@ async function buildTemplate({
Accept: "text/event-stream",
},
});
if (res.status !== 200) {
const errBody = await res.json();
throw errBody;
}
const stream = res.body?.pipeThrough(new TextDecoderStream()).getReader();
if (stream) {
while (true) {

View File

@@ -2,6 +2,7 @@ import { createStore, useStore } from "zustand";
import type { Template } from "./types";
import { createContext, useContext } from "react";
import { buildTemplate } from "./api";
import { isApiErrorResponse, type ApiErrorResponse } from "@/api";
interface TemplateEditorState {
template: Template;
@@ -9,6 +10,7 @@ interface TemplateEditorState {
isBuildInProgress: boolean;
isBuildOutputVisible: boolean;
buildOutput: string;
buildError: ApiErrorResponse | null;
startBuild: ({
imageTag,
@@ -34,6 +36,7 @@ function createTemplateEditorStore({
isBuildInProgress: false,
isBuildOutputVisible: false,
buildOutput: "",
buildError: null,
startBuild: async ({ imageTag, buildArgs }) => {
const state = get();
@@ -42,6 +45,7 @@ function createTemplateEditorStore({
isBuildInProgress: true,
isBuildOutputVisible: true,
buildOutput: "",
buildError: null,
});
try {
@@ -51,8 +55,12 @@ function createTemplateEditorStore({
templateName: state.template.name,
onBuildOutput: state.addBuildOutputChunk,
});
} catch {
// TODO: handle build error
} catch (error) {
console.error(error);
if (isApiErrorResponse(error)) {
console.log("askdjskdjk");
set({ buildError: error });
}
} finally {
set({ isBuildInProgress: false });
}

View File

@@ -1,4 +1,4 @@
import { ApiError } from "@/api";
import { API_ERROR_BAD_TEMPLATE, ApiError } from "@/api";
import { CodeMirrorEditor } from "@/components/codemirror-editor";
import { Button } from "@/components/ui/button.tsx";
import { Dialog, DialogTrigger } from "@/components/ui/dialog";
@@ -35,7 +35,7 @@ import {
} from "./template-editor-store";
import type { Template } from "./types";
import { useToast } from "@/hooks/use-toast";
import { ToastAction } from "@/components/ui/toast";
import { Toaster } from "@/components/ui/toaster";
function TemplateEditor() {
const { templateName, _splat } = templateEditorRoute.useParams();
@@ -122,6 +122,8 @@ function _TemplateEditor({
<TemplateBuildOutputPanel />
</main>
</div>
<Toaster />
<BuildErrorToast />
</SidebarProvider>
</TemplateEditorStoreContext.Provider>
);
@@ -332,4 +334,33 @@ function BuildOutput() {
);
}
function BuildErrorToast() {
const buildError = useTemplateEditorStore((state) => state.buildError);
const { toast } = useToast();
useEffect(() => {
if (!buildError) return;
switch (buildError.code) {
case API_ERROR_BAD_TEMPLATE:
toast({
variant: "destructive",
title: "Invalid template",
description: buildError.error,
});
break;
default:
toast({
variant: "destructive",
title: "Unexpected error",
description: buildError.error,
});
break;
}
}, [buildError, toast]);
return false;
}
export { TemplateEditor };