Skip to content

Commit

Permalink
Merge pull request #6 from confio/mermaid-sequence-send
Browse files Browse the repository at this point in the history
Render simple sequence diagram for Send
  • Loading branch information
abefernan authored Oct 2, 2024
2 parents 0819fef + 2b80642 commit 5ec4f25
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 91 deletions.
3 changes: 0 additions & 3 deletions app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,6 @@ import {
} from "lucide-react";
import Link from "next/link";

export const description =
"An products dashboard with a sidebar navigation. The sidebar has icon navigation. The content area has a breadcrumb and search in the header. It displays a list of products in a table with actions.";

export default function Dashboard() {
return (
<div className="flex min-h-screen w-full flex-col bg-muted/40">
Expand Down
73 changes: 2 additions & 71 deletions components/tx-data.tsx
Original file line number Diff line number Diff line change
@@ -1,78 +1,9 @@
"use client";

import { Tx } from "@/types/txs";
import { sequenceDiagramFromSpans } from "@/lib/mermaid";
import { useQuery } from "@tanstack/react-query";
import Mermaid from "./mermaid";

type TreeNode = {
id: string;
parentId: string | null;
children: Set<TreeNode>;
span: any;
};

function mermaidFromSpans(spans: any) {
const spanParentMap = new Map<string, string | null>();

for (const span of spans) {
const parentRef = span._source.references.find(
(ref: any) => ref.refType === "CHILD_OF",
);

if (parentRef) {
spanParentMap.set(span._source.spanID, parentRef.spanID);
} else {
spanParentMap.set(span._source.spanID, null);
}
}

const spanNodes: Array<TreeNode> = Array.from(spanParentMap).map(
([id, parentId]) => ({
id,
parentId,
children: new Set(),
span: spans.find((span: any) => span._source.spanID === id),
}),
);

for (const [spanId, parentId] of Array.from(spanParentMap)) {
const spanNode = spanNodes.find((node) => node.id === spanId);
const parentNode = spanNodes.find((node) => node.id === parentId);

if (!spanNode || !parentNode) {
continue;
}

parentNode.children.add(spanNode);
}

const sortedSpanNodes = spanNodes.toSorted((a, b) => {
if (a.parentId === null || a.children.has(b)) {
return -1;
}

if (a.parentId === b.parentId) {
return 0;
}

return 1;
});

let chart = "flowchart TD";

for (const node of sortedSpanNodes) {
for (const child of Array.from(node.children)) {
chart += `\n${node.id}[${node.span._source.operationName}] --> ${child.id}[${child.span._source.operationName}]`;
}
}

for (const node of sortedSpanNodes) {
chart += `\nclick ${node.id} "/txs/${node.span._source.traceID}/${node.id}"`;
}

return chart;
}

type TxDataProps = {
txId: string;
spanId: string;
Expand All @@ -95,7 +26,7 @@ export default function TxData({ txId, spanId }: TxDataProps) {

if (error) return "An error has occurred: " + error.message;

const mermaidChart = mermaidFromSpans(spans);
const mermaidChart = sequenceDiagramFromSpans(spans);

const span = spans.find((span: any) => span._source.spanID === spanId);

Expand Down
34 changes: 17 additions & 17 deletions components/txs-data.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ export default function TxsData() {
const { isPending, error, data } = useQuery({
queryKey: ["txs"],
queryFn: () =>
fetch("http://localhost:4000/api/v1/txs").then((res) => res.json()),
fetch(
`http://localhost:4000/api/v1/txs?operationName=execute_tx&tags=${encodeURIComponent('{"tx":"*Bank(Send*"}')}`,
).then((res) => res.json()),
});

if (isPending) return "Loading...";
Expand All @@ -19,22 +21,20 @@ export default function TxsData() {
<div>
<h1>Transactions</h1>
<ul>
{data.txs
.filter((tx: Tx) => !tx._source.references.length)
.map((tx: Tx) => (
<li key={tx._id}>
<Link href={`/txs/${tx._source.traceID}`}>
{tx._source.traceID} - {tx._source.operationName} -{" "}
{
data.txs.filter(
(txToFilter: Tx) =>
txToFilter._source.traceID === tx._source.traceID,
).length
}{" "}
spans - {tx._source.tags.length} tags
</Link>
</li>
))}
{data.txs.map((tx: Tx) => (
<li key={tx._id}>
<Link href={`/txs/${tx._source.traceID}`}>
{tx._source.traceID} - {tx._source.operationName} -{" "}
{
data.txs.filter(
(txToFilter: Tx) =>
txToFilter._source.traceID === tx._source.traceID,
).length
}{" "}
spans - {tx._source.tags.length} tags
</Link>
</li>
))}
</ul>
</div>
);
Expand Down
95 changes: 95 additions & 0 deletions lib/mermaid.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
type TreeNode = {
id: string;
parentId: string | null;
children: Set<TreeNode>;
span: any;
};

export function flowchartFromSpans(spans: any) {
const spanParentMap = new Map<string, string | null>();

for (const span of spans) {
const parentRef = span._source.references.find(
(ref: any) => ref.refType === "CHILD_OF",
);

if (parentRef) {
spanParentMap.set(span._source.spanID, parentRef.spanID);
} else {
spanParentMap.set(span._source.spanID, null);
}
}

const spanNodes: Array<TreeNode> = Array.from(spanParentMap).map(
([id, parentId]) => ({
id,
parentId,
children: new Set(),
span: spans.find((span: any) => span._source.spanID === id),
}),
);

for (const [spanId, parentId] of Array.from(spanParentMap)) {
const spanNode = spanNodes.find((node) => node.id === spanId);
const parentNode = spanNodes.find((node) => node.id === parentId);

if (!spanNode || !parentNode) {
continue;
}

parentNode.children.add(spanNode);
}

const sortedSpanNodes = spanNodes.toSorted((a, b) => {
if (a.parentId === null || a.children.has(b)) {
return -1;
}

if (a.parentId === b.parentId) {
return 0;
}

return 1;
});

let chart = "flowchart TD";

for (const node of sortedSpanNodes) {
for (const child of Array.from(node.children)) {
chart += `\n${node.id}[${node.span._source.operationName}] --> ${child.id}[${child.span._source.operationName}]`;
}
}

for (const node of sortedSpanNodes) {
chart += `\nclick ${node.id} "/txs/${node.span._source.traceID}/${node.id}"`;
}

console.log({ chart, spans });

return chart;
}

export function sequenceDiagramFromSpans(spans: any) {
let chart = "sequenceDiagram";

const msgSendSpan = spans.find((span: any) =>
span._source.tags.find(
(tag: any) => tag.key === "tx" && tag.value.includes("Bank(Send"),
),
);

if (msgSendSpan) {
const tx = msgSendSpan._source.tags.find(
(tag: any) => tag.key === "tx",
).value;

const sender = tx.match(/sender: (\w+)/)[1];
const recipient = tx.match(/recipient: (\w+)/)[1];

console.log({ tx, sender, recipient });

chart += `\n${sender}->>+${recipient}: Send`;
}

return chart;
}

0 comments on commit 5ec4f25

Please sign in to comment.