Skip to content

Commit 7498c28

Browse files
committed
fix(www): FlowsPanel UI bugs — auto-load, graph import loading, evaluate context, delete mutation
- Fix auto-load effect re-firing when New Flow clears activeStoreId (use didAutoLoad ref) - Add loadGraphFromImport fallback for graphs created via platformImportGraphJson - Fix evaluate context mismatch (set graph context to 'js' for local eval) - Fix delete store mutation using non-existent field name - Remove payload_schema column from server SQL queries
1 parent f4294f8 commit 7498c28

2 files changed

Lines changed: 48 additions & 14 deletions

File tree

www/server/index.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ app.get('/api/functions', async (_req, res) => {
4141
const result = await pool.query(`
4242
SELECT name, task_identifier, service_url, is_invocable, is_built_in,
4343
scope, description, required_secrets, required_configs,
44-
payload_schema, namespace_id,
44+
namespace_id,
4545
(SELECT n.name FROM constructive_infra_public.platform_namespaces n WHERE n.id = f.namespace_id) as namespace,
4646
created_at, updated_at
4747
FROM constructive_compute_public.platform_function_definitions f
@@ -64,17 +64,14 @@ app.get('/api/definitions', async (_req, res) => {
6464
try {
6565
const result = await pool.query(`
6666
SELECT name, task_identifier, service_url, is_invocable,
67-
scope, description, required_secrets, required_configs,
68-
payload_schema
67+
scope, description, required_secrets, required_configs
6968
FROM constructive_compute_public.platform_function_definitions
7069
ORDER BY name
7170
`);
7271

7372
const definitions = result.rows.map((r: any) => {
7473
const secrets = parseRequirements(r.required_secrets);
7574
const configs = parseRequirements(r.required_configs);
76-
const schema = r.payload_schema;
77-
7875
const props = [
7976
...secrets.map((s: { name: string; required: boolean }) => ({
8077
name: s.name,
@@ -94,7 +91,6 @@ app.get('/api/definitions', async (_req, res) => {
9491
name: 'payload',
9592
type: 'json',
9693
description: 'Job payload object',
97-
...(schema ? { schema } : {}),
9894
}];
9995

10096
return {

www/src/components/FlowsPanel.tsx

Lines changed: 46 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,11 @@ async function loadGraphFromStore(storeId: string): Promise<{ graph: Graph; comm
159159
}
160160
`, { where: { storeId: { equalTo: storeId } } });
161161
const ref = refData?.platformFunctionGraphRefs?.nodes?.[0];
162-
if (!ref?.commitId) return null;
162+
if (!ref?.commitId) {
163+
// No ref/commit chain — try reading via platformReadFunctionGraph
164+
// (graph was created by platformImportGraphJson, not blob save)
165+
return loadGraphFromImport(storeId);
166+
}
163167

164168
// Get the commit
165169
const commitData = await gqlFetch(endpoint, `
@@ -170,7 +174,7 @@ async function loadGraphFromStore(storeId: string): Promise<{ graph: Graph; comm
170174
}
171175
`, { where: { id: { equalTo: ref.commitId } } });
172176
const commit = commitData?.platformFunctionGraphCommits?.nodes?.[0];
173-
if (!commit?.treeId) return null;
177+
if (!commit?.treeId) return loadGraphFromImport(storeId);
174178

175179
// Get the object (tree)
176180
const objData = await gqlFetch(endpoint, `
@@ -181,11 +185,41 @@ async function loadGraphFromStore(storeId: string): Promise<{ graph: Graph; comm
181185
}
182186
`, { where: { id: { equalTo: commit.treeId } } });
183187
const obj = objData?.platformFunctionGraphObjects?.nodes?.[0];
184-
if (!obj?.data) return null;
188+
if (!obj?.data) return loadGraphFromImport(storeId);
185189

186190
return { graph: obj.data as unknown as Graph, commitId: ref.commitId };
187191
}
188192

193+
async function loadGraphFromImport(storeId: string): Promise<{ graph: Graph; commitId: string | null } | null> {
194+
const endpoint = '/graphql/compute';
195+
196+
// Find the platform_function_graphs entry that uses this store
197+
const graphData = await gqlFetch(endpoint, `
198+
query ($where: PlatformFunctionGraphFilter) {
199+
platformFunctionGraphs(where: $where, first: 1) {
200+
nodes { id storeId name context }
201+
}
202+
}
203+
`, { where: { storeId: { equalTo: storeId } } });
204+
const graph = graphData?.platformFunctionGraphs?.nodes?.[0];
205+
if (!graph?.id) return null;
206+
207+
// Read the full graph using the SQL deserialization function
208+
const readData = await gqlFetch(endpoint, `
209+
query ($graphId: UUID!) {
210+
platformReadFunctionGraph(graphId: $graphId)
211+
}
212+
`, { graphId: graph.id });
213+
const graphJson = readData?.platformReadFunctionGraph;
214+
if (!graphJson) return null;
215+
216+
// Strip the SQL execution context — it's for the graph engine, not the local evaluator
217+
const parsed = graphJson as Record<string, unknown>;
218+
if (parsed.context === 'function') delete parsed.context;
219+
220+
return { graph: parsed as unknown as Graph, commitId: null };
221+
}
222+
189223
async function saveGraphToStore(
190224
storeId: string,
191225
graph: Graph,
@@ -293,7 +327,7 @@ async function deleteStore(id: string): Promise<void> {
293327
const endpoint = '/graphql/compute';
294328
await gqlFetch(endpoint, `
295329
mutation ($input: DeletePlatformFunctionGraphStoreInput!) {
296-
deletePlatformFunctionGraphStore(input: $input) { deletedPlatformFunctionGraphStoreNodeId }
330+
deletePlatformFunctionGraphStore(input: $input) { platformFunctionGraphStore { id } }
297331
}
298332
`, { input: { id } });
299333
}
@@ -412,6 +446,7 @@ export function FlowsPanel() {
412446
const [executionState, setExecutionState] = useState<ExecutionState | null>(null);
413447
const [isExecuting, setIsExecuting] = useState(false);
414448
const pollRef = useRef<ReturnType<typeof setInterval> | null>(null);
449+
const didAutoLoad = useRef(false);
415450

416451
const graphRef = useRef<Graph>(currentGraph);
417452
graphRef.current = currentGraph;
@@ -447,12 +482,13 @@ export function FlowsPanel() {
447482
}
448483
}, []);
449484

450-
// Auto-load first store when stores arrive
485+
// Auto-load first store only on initial mount
451486
useEffect(() => {
452-
if (stores.length > 0 && !activeStoreId) {
487+
if (stores.length > 0 && !didAutoLoad.current) {
488+
didAutoLoad.current = true;
453489
handleLoadStore(stores[0]);
454490
}
455-
}, [stores, activeStoreId, handleLoadStore]);
491+
}, [stores, handleLoadStore]);
456492

457493
const definitions: NodeDefinition[] = useMemo(() => [
458494
...functions.map(platformFnToDefinition),
@@ -517,7 +553,9 @@ export function FlowsPanel() {
517553
return;
518554
}
519555

520-
const result = await evaluate(graph, {
556+
// Set context to 'js' for local evaluation (definitions use context: 'js')
557+
const evalGraph = { ...graph, context: 'js' };
558+
const result = await evaluate(evalGraph, {
521559
definitions: implDefinitions,
522560
outputNode: outputNode.name,
523561
outputPort: 'value',

0 commit comments

Comments
 (0)