go.charczuk.com@v0.0.0-20240327042549-bc490516bd1a/projects/nodes/static/_nextjs/src/api/nodes.ts (about) 1 /** 2 * Copyright (c) 2024 - Present. Will Charczuk. All rights reserved. 3 * Use of this source code is governed by a MIT license that can be found in the LICENSE file at the root of the repository. 4 */ 5 export interface Node { 6 id: string; 7 label: string; 8 metadata: NodeMetadata; 9 } 10 11 export interface NodeMetadata { 12 collapsed?: boolean; 13 expression?: string; 14 input_type?: string; 15 node_type: string; 16 output_type?: string; 17 parent_node_id?: string; 18 position_x: number; 19 position_y: number; 20 height?: number; 21 width?: number; 22 watched?: boolean; 23 watched_collapsed?: boolean; 24 } 25 26 export const defaultNodeType = 'var'; 27 28 export const emptyNode: Node = { 29 id: '', 30 label: '', 31 metadata: { 32 node_type: defaultNodeType, 33 position_x: 0, 34 position_y: 0, 35 } 36 } 37 38 export interface Edge { 39 parent_id: string; 40 child_id: string; 41 child_input_name: string | null; 42 } 43 44 export interface Table { 45 columns: TableColumn[]; 46 } 47 48 export interface TableColumn { 49 name?: string; 50 value_type?: string; 51 values: any[]; 52 } 53 54 export interface TableRange { 55 top: number; 56 left: number; 57 bottom: number; 58 right: number; 59 } 60 61 export interface TableInfo { 62 rows: number; 63 columns: string[]; 64 } 65 66 export function tableColumnNames(table: Table): Array<string> { 67 if (!table.columns || table.columns.length === 0) { 68 return [] 69 } 70 return table.columns.map(c => c.name || '') 71 } 72 73 export function tableMaxRows(table: Table): number { 74 const columnRowCounts = table && table.columns ? (table.columns.map(c => c.values ? c.values.length : 0)) : []; 75 let maxRowCount = 0; 76 for (const count of columnRowCounts) { 77 if (count > maxRowCount) { 78 maxRowCount = count 79 } 80 } 81 return maxRowCount 82 } 83 84 export function tableRowCol(table: Table, row: number, col: number): any { 85 if (!table.columns) { 86 return '' 87 } 88 if (col < 0 || col >= table.columns.length) { 89 return '' 90 } 91 if (!table.columns[col].values) { 92 return '' 93 } 94 if (row < 0 || row >= table.columns[col].values.length) { 95 return '' 96 } 97 return table.columns[col].values[row] 98 } 99 100 export interface NodeValue { 101 id: string; 102 value: any; 103 } 104 105 export function nodeLabelOrID(n: Node): string { 106 if (n.label !== undefined && n.label !== null && n.label !== "") { 107 return n.label 108 } 109 return n.id 110 } 111 112 export const sortNodesByLabelAsc = (a: Node, b: Node) => { 113 if (a.label < b.label) { 114 return -1; 115 } 116 if (a.label > b.label) { 117 return 1 118 } 119 return 0 120 } 121 export const sortNodesByLabelDesc = (a: Node, b: Node) => { 122 if (a.label < b.label) { 123 return 1; 124 } 125 if (a.label > b.label) { 126 return -1 127 } 128 return 0 129 } 130 131 export type NativeValueScalarType = boolean | string | number | Date; 132 export type NativeValueArrayType = Array<NativeValueScalarType>; 133 export type NativeValueType = NativeValueScalarType | NativeValueArrayType | Table; 134 135 export async function getNode(graphId: string, node_id: string): Promise<Node> { 136 const res = await fetch(`/api/v1/graph/${graphId}/node/${node_id}`); 137 if (!res?.ok) { 138 const message = (await res.json()) as string; 139 throw new Error(message) 140 } 141 const data = await res.json(); 142 return data as Node; 143 }; 144 145 export async function getNodes(graphId: string): Promise<Node[]> { 146 const res = await fetch(`/api/v1/graph/${graphId}/nodes`); 147 if (!res?.ok) { 148 const message = (await res.json()) as string; 149 throw new Error(message) 150 } 151 const data = await res.json(); 152 return data as Node[]; 153 }; 154 155 156 export async function getEdges(graphId: string): Promise<Edge[]> { 157 const res = await fetch(`/api/v1/graph/${graphId}/edges`); 158 if (!res?.ok) { 159 const message = (await res.json()) as string; 160 throw new Error(message) 161 } 162 const data = await res.json(); 163 return data as Edge[]; 164 }; 165 166 export async function getNodeValue(graphId: string, id: string): Promise<any> { 167 const res = await fetch(`/api/v1/graph/${graphId}/node.value/${id}`); 168 if (res.status == 404) { 169 return undefined 170 } 171 if (!res?.ok) { 172 const message = (await res.json()) as string; 173 throw new Error(message) 174 } 175 const data = await res.json(); 176 return data as any; 177 }; 178 179 export async function getNodeValues(graphId: string, ids: string[]): Promise<NodeValue[]> { 180 const res = await fetch(`/api/v1/graph/${graphId}/node.values?ids=${ids.join(',')}`); 181 const data = await res.json(); 182 return data as NodeValue[]; 183 }; 184 185 export async function getNodeValueTableInfo(graphId: string, id: string): Promise<TableInfo> { 186 const res = await fetch(`/api/v1/graph/${graphId}/node.value.table/${id}/info`) 187 if (res.status == 404) { 188 return { rows: 0, columns: [] } 189 } 190 if (!res?.ok) { 191 const message = (await res.json()) as string; 192 throw new Error(message) 193 } 194 const data = await res.json(); 195 return data as TableInfo; 196 }; 197 198 export async function getNodeValueTableRange(graphId: string, id: string, range: { top: number, left: number, bottom: number, right: number }): Promise<any[][]> { 199 const res = await fetch(`/api/v1/graph/${graphId}/node.value.table/${id}/range?range=${range.top},${range.left},${range.bottom},${range.right}`); 200 if (res.status == 404) { 201 return [] 202 } 203 if (!res?.ok) { 204 const message = (await res.json()) as string; 205 throw new Error(message) 206 } 207 const data = await res.json(); 208 return data as any[][]; 209 }; 210 211 export async function putNodeValueTable(graphId: string, id: string, ops: Array<any>): Promise<void> { 212 const res = await fetch(`/api/v1/graph/${graphId}/node.value.table/${id}`, { 213 method: "PUT", 214 body: JSON.stringify(ops), 215 }); 216 if (!res?.ok) { 217 const message = (await res.json()) as string; 218 throw new Error(message) 219 } 220 } 221 222 type PostedNode = Omit<Node, 'id'> & { id?: string }; 223 224 export async function postNode(graphId: string, node: PostedNode): Promise<string> { 225 // rip id off the payload (we can't use interfaces 226 // because types in typescript are an illusion). 227 const { id, ...body }: PostedNode = node; 228 const res = await fetch(`/api/v1/graph/${graphId}/node`, { 229 method: "POST", 230 body: JSON.stringify(body), 231 }); 232 if (!res?.ok) { 233 const message = (await res.json()) as string; 234 throw new Error(message) 235 } 236 const data = await res.json() 237 return data as string; 238 }; 239 240 export async function patchNodes(graphId: string, patchData: { [key: string]: any }): Promise<void> { 241 const res = await fetch(`/api/v1/graph/${graphId}/nodes`, { 242 method: "PATCH", 243 body: JSON.stringify(patchData), 244 }); 245 if (!res?.ok) { 246 const message = (await res.json()) as string; 247 throw new Error(message) 248 } 249 }; 250 251 export async function patchNode(graphId: string, id: string, patchData: { [key: string]: any }): Promise<void> { 252 const res = await fetch(`/api/v1/graph/${graphId}/node/${id}`, { 253 method: "PATCH", 254 body: JSON.stringify(patchData), 255 }); 256 if (!res?.ok) { 257 const message = (await res.json()) as string; 258 throw new Error(message) 259 } 260 }; 261 262 export async function postNodeStale(graphId: string, id: string): Promise<void> { 263 const res = await fetch(`/api/v1/graph/${graphId}/node.stale/${id}`, { 264 method: "POST", 265 }); 266 if (!res?.ok) { 267 const message = (await res.json()) as string; 268 throw new Error(message) 269 } 270 }; 271 272 export async function putNodeValue(graphId: string, id: string, value: any): Promise<void> { 273 const res = await fetch(`/api/v1/graph/${graphId}/node.value/${id}`, { 274 method: "PUT", 275 body: JSON.stringify(value), 276 }); 277 if (!res?.ok) { 278 const message = (await res.json()) as string; 279 throw new Error(message) 280 } 281 }; 282 283 export async function postEdge(graphId: string, e: Edge): Promise<void> { 284 let url = `/api/v1/graph/${graphId}/edge` 285 const res = await fetch(url, { 286 method: "POST", 287 body: JSON.stringify(e) 288 }); 289 if (!res?.ok) { 290 const message = (await res.json()) as string; 291 throw new Error(message) 292 } 293 }; 294 295 export async function deleteEdge(graphId: string, e: Edge): Promise<void> { 296 let url = `/api/v1/graph/${graphId}/edge` 297 const res = await fetch(url, { 298 method: "DELETE", 299 body: JSON.stringify(e) 300 }); 301 if (!res?.ok) { 302 const message = (await res.json()) as string; 303 throw new Error(message) 304 } 305 }; 306 307 export async function deleteNode(graphId: string, nodeID: string): Promise<void> { 308 const res = await fetch(`/api/v1/graph/${graphId}/node/${nodeID}`, { 309 method: "DELETE", 310 }); 311 if (!res?.ok) { 312 const message = (await res.json()) as string; 313 throw new Error(message) 314 } 315 }; 316 317 export async function postNodeTableImportCsv(graphId: string, nodeId: string, file: FileList): Promise<void> { 318 const formData = new FormData(); 319 formData.append('file', file[0]); 320 321 // Make a POST request to the server 322 const res = await fetch(`/api/v1/graph/${graphId}/node.value.table/${nodeId}/csv`, { 323 method: 'POST', 324 body: formData, 325 }); 326 if (!res?.ok) { 327 const message = (await res.json()) as string; 328 throw new Error(message) 329 } 330 }