Canvas: Implement delete project

Change-Id: I0c2abfde3d813376b9f22ab8dc8b1752262679cb
diff --git a/apps/canvas/back/index.js b/apps/canvas/back/index.js
index ef3449f..32c28cd 100644
--- a/apps/canvas/back/index.js
+++ b/apps/canvas/back/index.js
@@ -123,6 +123,42 @@
         resp.end();
     }
 });
+const handleDelete = (req, resp) => __awaiter(void 0, void 0, void 0, function* () {
+    try {
+        const projectId = Number(req.params["projectId"]);
+        const p = yield db.project.findUnique({
+            where: {
+                id: projectId,
+            },
+            select: {
+                instanceId: true,
+            },
+        });
+        if (p === null) {
+            resp.status(404);
+            return;
+        }
+        const r = yield axios_1.default.request({
+            url: `http://appmanager.hgrz-appmanager.svc.cluster.local/api/instance/${p.instanceId}/remove`,
+            method: "post",
+        });
+        if (r.status === 200) {
+            yield db.project.delete({
+                where: {
+                    id: projectId,
+                },
+            });
+        }
+        resp.status(200);
+    }
+    catch (e) {
+        console.log(e);
+        resp.status(500);
+    }
+    finally {
+        resp.end();
+    }
+});
 const handleDeploy = (req, resp) => __awaiter(void 0, void 0, void 0, function* () {
     try {
         const projectId = Number(req.params["projectId"]);
@@ -245,6 +281,7 @@
         app.get("/api/project/:projectId/saved", handleSavedGet);
         app.post("/api/project/:projectId/deploy", handleDeploy);
         app.get("/api/project/:projectId/status", handleStatus);
+        app.delete("/api/project/:projectId", handleDelete);
         app.get("/api/project", handleProjectAll);
         app.post("/api/project", handleProjectCreate);
         app.use("/", express_1.default.static("../front/dist"));
diff --git a/apps/canvas/back/index.ts b/apps/canvas/back/index.ts
index d10a4a6..8b22348 100644
--- a/apps/canvas/back/index.ts
+++ b/apps/canvas/back/index.ts
@@ -110,6 +110,41 @@
     }
 };
 
+const handleDelete: express.Handler = async (req, resp) => {
+    try {
+        const projectId = Number(req.params["projectId"]);
+        const p = await db.project.findUnique({
+            where: {
+                id: projectId,
+            },
+            select: {
+                instanceId: true,
+            },
+        });
+        if (p === null) {
+            resp.status(404);
+            return;
+        }
+        const r = await axios.request({
+            url: `http://appmanager.hgrz-appmanager.svc.cluster.local/api/instance/${p.instanceId}/remove`,
+            method: "post",
+        });
+        if (r.status === 200) {
+            await db.project.delete({
+                where: {
+                    id: projectId,
+                },
+            });    
+        }
+        resp.status(200);
+    } catch (e) {
+        console.log(e);
+        resp.status(500);
+    } finally {
+        resp.end();
+    }
+};
+
 const handleDeploy: express.Handler = async (req, resp) => {
     try {
         const projectId = Number(req.params["projectId"]);
@@ -228,6 +263,7 @@
     app.get("/api/project/:projectId/saved", handleSavedGet);
     app.post("/api/project/:projectId/deploy", handleDeploy);
     app.get("/api/project/:projectId/status", handleStatus);
+    app.delete("/api/project/:projectId", handleDelete);
     app.get("/api/project", handleProjectAll);
     app.post("/api/project", handleProjectCreate);
     app.use("/", express.static("../front/dist"));
diff --git a/apps/canvas/front/src/components/actions.tsx b/apps/canvas/front/src/components/actions.tsx
index 7d0afe7..66235b3 100644
--- a/apps/canvas/front/src/components/actions.tsx
+++ b/apps/canvas/front/src/components/actions.tsx
@@ -125,7 +125,30 @@
     const clear = useCallback(() => {
         store.setEdges([]);
         store.setNodes([]);
+        instance.setViewport({ x: 0, y: 0, zoom: 1 });
     }, [store]);
+    // TODO(gio): Update store
+    const deleteProject = useCallback(async () => {
+        if (projectId == null) {
+            return;
+        }
+        const resp = await fetch(`/api/project/${projectId}`, {
+            method: "DELETE",
+        });
+        if (resp.ok) {
+            clear();
+            store.setProject(undefined);
+            toast({
+                title: "Save succeeded",
+            });
+        } else {
+            toast({
+                variant: "destructive",
+                title: "Save failed",
+                description: await resp.text(),
+            });
+        }            
+    }, [store, clear]);
     const [props, setProps] = useState({});
     useEffect(() => {
         if (loading) {
@@ -141,7 +164,8 @@
             <Button onClick={deploy} {...props}>Deploy</Button>
             <Button onClick={save}>Save</Button>
             <Button onClick={restoreSaved}>Restore</Button>
-            <Button onClick={clear}>Clear</Button>
+            <Button onClick={clear} variant="destructive">Clear</Button>
+            <Button onClick={deleteProject} variant="destructive" disabled={projectId === undefined}>Delete</Button>
         </>
     )
 }
\ No newline at end of file
diff --git a/apps/canvas/front/src/lib/state.ts b/apps/canvas/front/src/lib/state.ts
index f2836f1..51f67a6 100644
--- a/apps/canvas/front/src/lib/state.ts
+++ b/apps/canvas/front/src/lib/state.ts
@@ -273,7 +273,7 @@
   onConnect: OnConnect;
   setNodes: (nodes: AppNode[]) => void;
   setEdges: (edges: Edge[]) => void;
-  setProject: (projectId: string) => void;
+  setProject: (projectId: string | undefined) => void;
   setProjects: (projects: Project[]) => void;
   updateNode: <T extends NodeType>(id: string, data: DeepPartial<(AppNode & (Pick<AppNode, "type"> | { type: T }))>) => void;
   updateNodeData: <T extends NodeType>(id: string, data: DeepPartial<(AppNode & (Pick<AppNode, "type"> | { type: T }))["data"]>) => void;