Skip to content

Commit

Permalink
feat(console): add inflights information when clicking an edge (#3099)
Browse files Browse the repository at this point in the history
Adds a new section to the right side of the Console that provides information about the inflight methods used. This section is displayed when clicking on a connection between two nodes on the map

![image](https://github.com/winglang/wing/assets/5547636/6e1d2928-4e2c-47a1-b217-4452343b65be)


resolves #2981
  • Loading branch information
polamoros authored Aug 8, 2023
1 parent 2a820fb commit 13244da
Show file tree
Hide file tree
Showing 42 changed files with 1,778 additions and 1,459 deletions.
3 changes: 1 addition & 2 deletions apps/wing-console/console/app/demo/index.w
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
bring cloud;
bring ex;
// bring redis;

let bucket = new cloud.Bucket();
let queue = new cloud.Queue();
Expand Down Expand Up @@ -46,7 +45,7 @@ topic.onMessage(inflight (message: str): str => {
return message;
});

// let r = new redis.Redis();
// let r = new ex.Redis();
// new cloud.Function(inflight (message :str) :str => {
// log("${r.url()}");
// r.set("wing", message);
Expand Down
16 changes: 5 additions & 11 deletions apps/wing-console/console/design-system/src/attribute.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ interface AttributeProps {
type?: "url";
url?: string;
noLeftPadding?: boolean;
centerLabel?: boolean;
dataTestId?: string;
}

Expand All @@ -20,15 +21,17 @@ export const Attribute = ({
url,
children,
noLeftPadding = false,
centerLabel = true,
dataTestId,
}: PropsWithChildren<AttributeProps>) => {
const { theme } = useTheme();
const id = useId();
return (
<div
className={classNames(
"flex flex-row items-center",
"flex flex-row",
!noLeftPadding && "pl-4",
centerLabel && "items-center",
)}
>
<label htmlFor={id} className={classNames(theme.text2, "min-w-[100px]")}>
Expand Down Expand Up @@ -57,16 +60,7 @@ export const Attribute = ({
data-testid={dataTestId}
/>
)}
{value === undefined && (
<div
className={classNames(
theme.text2,
"w-full bg-transparent items-center select-text text-sm transition truncate",
)}
>
{children}
</div>
)}
{value === undefined && children}
</div>
);
};
73 changes: 73 additions & 0 deletions apps/wing-console/console/server/src/router/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,79 @@ export const createAppRouter = () => {
}),
};
}),
"app.edgeMetadata": createProcedure
.input(
z.object({
edgeId: z.string(),
showTests: z.boolean().optional(),
}),
)
.query(async ({ ctx, input }) => {
const { edgeId, showTests } = input;
const simulator = await ctx.simulator();

const { tree } = simulator.tree().rawData();
const nodeMap = buildConstructTreeNodeMap(shakeTree(tree));

const sourcePath = edgeId.split("->")[0]?.trim();
const targetPath = edgeId.split("->")[1]?.trim();
const sourceNode = nodeMap.get(sourcePath);
if (!sourceNode) {
throw new TRPCError({
code: "NOT_FOUND",
message: "Node was not found.",
});
}
const connections =
sourceNode?.attributes?.["wing:resource:connections"];

const targetResource = connections?.find(
(connection) => connection.resource === targetPath,
);
const targetNode = nodeMap.get(targetResource?.resource);

const inflights = sourceNode.display?.hidden
? []
: connections
?.filter(({ direction, resource, relationship }) => {
if (direction !== "outbound") {
return false;
}

if (resource !== targetPath) {
return false;
}

if (relationship === "$inflight_init()") {
return false;
}

if (!showTests && matchTest(sourceNode.path)) {
return false;
}

return true;
})
.map((connection) => {
return {
name: connection.relationship,
};
}) ?? [];

return {
source: {
id: sourceNode.id,
path: sourceNode.path,
type: getResourceType(sourceNode, simulator),
},
target: {
id: targetNode?.id ?? "",
path: targetNode?.path ?? "",
type: (targetNode && getResourceType(targetNode, simulator)) ?? "",
},
inflights,
};
}),
"app.invalidateQuery": createProcedure.subscription(({ ctx }) => {
return observable<string | undefined>((emit) => {
ctx.emitter.on("invalidateQuery", emit.next);
Expand Down
6 changes: 6 additions & 0 deletions apps/wing-console/console/ui/src/features/map-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,17 @@ export interface MapViewProps {
showTests?: boolean;
showMapControls?: boolean;
onSelectedNodeIdChange?: (id: string | undefined) => void;
selectedEdgeId?: string;
onSelectedEdgeIdChange?: (id: string | undefined) => void;
}

export const MapView = ({
showMapControls = true,
showTests,
selectedNodeId,
onSelectedNodeIdChange,
selectedEdgeId,
onSelectedEdgeIdChange,
}: MapViewProps) => {
const { mapData } = useMap({ showTests: showTests ?? false });

Expand All @@ -41,6 +45,8 @@ export const MapView = ({
edges={mapData?.edges ?? []}
selectedNodeId={selectedNodeId}
onSelectedNodeIdChange={onSelectedNodeIdChange}
selectedEdgeId={selectedEdgeId}
onSelectedEdgeIdChange={onSelectedEdgeIdChange}
node={({ node, depth }) => (
<div className="h-full flex flex-col relative">
<ContainerNode
Expand Down
17 changes: 17 additions & 0 deletions apps/wing-console/console/ui/src/layout/default-layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { ConsoleLogs } from "../features/console-logs.js";
import { MapView } from "../features/map-view.js";
import { TestsTreeView } from "../features/tests-tree-view.js";
import { BlueScreenOfDeath } from "../ui/blue-screen-of-death.js";
import { EdgeMetadata } from "../ui/edge-metadata.js";
import { Explorer } from "../ui/explorer.js";
import { ResourceMetadata } from "../ui/resource-metadata.js";

Expand Down Expand Up @@ -79,6 +80,9 @@ export const DefaultLayout = ({
errorMessage,
loading,
metadata,
selectedEdgeId,
setSelectedEdgeId,
edgeMetadata,
setSearchText,
selectedLogTypeFilters,
setSelectedLogTypeFilters,
Expand Down Expand Up @@ -296,6 +300,8 @@ export const DefaultLayout = ({
onSelectedNodeIdChange={(nodeId) =>
setSelectedItems(nodeId ? [nodeId] : [])
}
selectedEdgeId={selectedEdgeId}
onSelectedEdgeIdChange={setSelectedEdgeId}
/>
</div>

Expand All @@ -317,6 +323,17 @@ export const DefaultLayout = ({
}}
/>
)}
{selectedEdgeId && edgeMetadata.data && (
<EdgeMetadata
source={edgeMetadata.data.source}
target={edgeMetadata.data.target}
inflights={edgeMetadata.data.inflights}
onConnectionNodeClick={(path) => {
expand(path);
setSelectedItems([path]);
}}
/>
)}
</LeftResizableWidget>
</div>
</div>
Expand Down
36 changes: 34 additions & 2 deletions apps/wing-console/console/ui/src/layout/use-layout.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
import { useTheme } from "@wingconsole/design-system";
import type { LogEntry, LogLevel, State } from "@wingconsole/server";
import { useLoading } from "@wingconsole/use-loading";
import { useEffect, useState, useContext, useRef, useMemo } from "react";
import {
useEffect,
useState,
useContext,
useRef,
useMemo,
useCallback,
} from "react";

import { trpc } from "../services/trpc.js";
import { useExplorer } from "../services/use-explorer.js";
Expand Down Expand Up @@ -38,6 +45,18 @@ export const useLayout = ({

const [logsTimeFilter, setLogsTimeFilter] = useState(0);

const [selectedEdgeId, setSelectedEdgeId] = useState<string | undefined>();

const onSelectedItemsChange = useCallback((items: string[]) => {
setSelectedEdgeId(undefined);
setSelectedItems(items);
}, []);

Check warning on line 53 in apps/wing-console/console/ui/src/layout/use-layout.tsx

View workflow job for this annotation

GitHub Actions / Test

React Hook useCallback has a missing dependency: 'setSelectedItems'. Either include it or remove the dependency array

const onSelectedEdgeIdChange = useCallback((edgeId: string | undefined) => {
onSelectedItemsChange([]);
setSelectedEdgeId(edgeId);
}, []);

Check warning on line 58 in apps/wing-console/console/ui/src/layout/use-layout.tsx

View workflow job for this annotation

GitHub Actions / Test

React Hook useCallback has a missing dependency: 'onSelectedItemsChange'. Either include it or remove the dependency array

const wingfile = trpc["app.wingfile"].useQuery();
const title = useMemo(() => {
if (!wingfile.data) {
Expand Down Expand Up @@ -89,6 +108,16 @@ export const useLayout = ({
},
);

const edgeMetadata = trpc["app.edgeMetadata"].useQuery(
{
edgeId: selectedEdgeId || "",
showTests,
},
{
enabled: !!selectedEdgeId,
},
);

const { loading, setLoading } = useLoading({
duration: 400,
});
Expand All @@ -112,7 +141,7 @@ export const useLayout = ({
return {
items,
selectedItems,
setSelectedItems,
setSelectedItems: onSelectedItemsChange,
expandedItems,
setExpandedItems,
expand,
Expand All @@ -122,6 +151,9 @@ export const useLayout = ({
errorMessage,
loading,
metadata,
selectedEdgeId,
setSelectedEdgeId: onSelectedEdgeIdChange,
edgeMetadata,
setSearchText,
selectedLogTypeFilters,
setSelectedLogTypeFilters,
Expand Down
5 changes: 1 addition & 4 deletions apps/wing-console/console/ui/src/services/use-explorer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,9 @@ export const useExplorer = () => {

const onSelectedItemsChange = useCallback(
(selectedItems: string[]) => {
if (selectedItems.length === 0 || !selectedItems[0]) {
return;
}
setSelectedItems(selectedItems);
setSelectedNode.mutate({
resourcePath: selectedItems[0],
resourcePath: selectedItems[0] ?? "",
});
},
[setSelectedNode, setSelectedItems],
Expand Down
49 changes: 36 additions & 13 deletions apps/wing-console/console/ui/src/ui/edge-item.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import classNames from "classnames";
import { ElkExtendedEdge } from "elkjs/lib/elk.bundled.js";
import { motion } from "framer-motion";
import { useMemo, useState } from "react";

export const EdgeItem = ({
edge,
Expand All @@ -10,6 +11,10 @@ export const EdgeItem = ({
highlighted,
fade,
transitionDuration,
selected,
onMouseEnter,
onMouseLeave,
onClick,
}: {
edge: ElkExtendedEdge;
offset?: { x: number; y: number };
Expand All @@ -18,16 +23,23 @@ export const EdgeItem = ({
highlighted?: boolean;
fade?: boolean;
transitionDuration?: number;
selected?: boolean;
onMouseEnter?: () => void;
onMouseLeave?: () => void;
onClick?: (id: string) => void;
}) => {
const d = edge.sections
?.map((section) => {
const points =
[...(section.bendPoints ?? []), section.endPoint]
?.map((point) => `L${point.x},${point.y}`)
.join(" ") ?? "";
return `M${section.startPoint.x},${section.startPoint.y} ${points}`;
})
.join(" ");
const d = useMemo(() => {
return edge.sections
?.map((section) => {
const points =
[...(section.bendPoints ?? []), section.endPoint]
?.map((point) => `L${point.x},${point.y}`)
.join(" ") ?? "";

return `M${section.startPoint.x},${section.startPoint.y} ${points}`;
})
.join(" ");
}, [edge.sections]);

const [source] = edge.sources;
const [target] = edge.targets;
Expand All @@ -37,10 +49,10 @@ export const EdgeItem = ({
return (
<g
className={classNames(
"stroke-1 fill-none",
highlighted
? "stroke-[1.5px] stroke-sky-500 z-10"
: "stroke-slate-400 dark:stroke-slate-800",
"stroke-1 fill-none cursor-pointer hover:stroke-[1.5px]",
highlighted && "stroke-sky-500",
selected && "stroke-[1.5px] stroke-sky-500",
!highlighted && !selected && "stroke-slate-400 dark:stroke-slate-800",
fade && "opacity-40",
"transition-all",
)}
Expand All @@ -62,6 +74,17 @@ export const EdgeItem = ({
d={d}
strokeDasharray={goesOutside ? "3" : undefined}
/>
<motion.path
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
onClick={() => onClick?.(edge.id)}
className="opacity-0 pointer-events-auto"
style={{ translateX: offset.x, translateY: offset.y }}
markerStart={`url(#${markerStart})`}
markerEnd={`url(#${markerEnd})`}
d={d}
strokeWidth="8"
/>
</g>
);
};
Loading

0 comments on commit 13244da

Please sign in to comment.