feat: handle template build error
This commit is contained in:
@@ -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 };
|
||||
|
@@ -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) {
|
||||
|
@@ -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 });
|
||||
}
|
||||
|
@@ -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 };
|
||||
|
Reference in New Issue
Block a user