1import {
2  ArrayVector,
3  FieldColorModeId,
4  FieldDTO,
5  FieldType,
6  MutableDataFrame,
7  NodeGraphDataFrameFieldNames,
8} from '@grafana/data';
9import { nodes, edges } from './testData/serviceMapResponse';
10
11export function generateRandomNodes(count = 10) {
12  const nodes = [];
13
14  const root = {
15    id: '0',
16    title: 'root',
17    subTitle: 'client',
18    success: 1,
19    error: 0,
20    stat1: Math.random(),
21    stat2: Math.random(),
22    edges: [] as any[],
23  };
24  nodes.push(root);
25  const nodesWithoutMaxEdges = [root];
26
27  const maxEdges = 3;
28
29  for (let i = 1; i < count; i++) {
30    const node = makeRandomNode(i);
31    nodes.push(node);
32    const sourceIndex = Math.floor(Math.random() * Math.floor(nodesWithoutMaxEdges.length - 1));
33    const source = nodesWithoutMaxEdges[sourceIndex];
34    source.edges.push(node.id);
35    if (source.edges.length >= maxEdges) {
36      nodesWithoutMaxEdges.splice(sourceIndex, 1);
37    }
38    nodesWithoutMaxEdges.push(node);
39  }
40
41  // Add some random edges to create possible cycle
42  const additionalEdges = Math.floor(count / 2);
43  for (let i = 0; i <= additionalEdges; i++) {
44    const sourceIndex = Math.floor(Math.random() * Math.floor(nodes.length - 1));
45    const targetIndex = Math.floor(Math.random() * Math.floor(nodes.length - 1));
46    if (sourceIndex === targetIndex || nodes[sourceIndex].id === '0' || nodes[sourceIndex].id === '0') {
47      continue;
48    }
49
50    nodes[sourceIndex].edges.push(nodes[sourceIndex].id);
51  }
52
53  const nodeFields: Record<string, Omit<FieldDTO, 'name'> & { values: ArrayVector }> = {
54    [NodeGraphDataFrameFieldNames.id]: {
55      values: new ArrayVector(),
56      type: FieldType.string,
57    },
58    [NodeGraphDataFrameFieldNames.title]: {
59      values: new ArrayVector(),
60      type: FieldType.string,
61    },
62    [NodeGraphDataFrameFieldNames.subTitle]: {
63      values: new ArrayVector(),
64      type: FieldType.string,
65    },
66    [NodeGraphDataFrameFieldNames.mainStat]: {
67      values: new ArrayVector(),
68      type: FieldType.number,
69      config: { displayName: 'Transactions per second' },
70    },
71    [NodeGraphDataFrameFieldNames.secondaryStat]: {
72      values: new ArrayVector(),
73      type: FieldType.number,
74      config: { displayName: 'Average duration' },
75    },
76    [NodeGraphDataFrameFieldNames.arc + 'success']: {
77      values: new ArrayVector(),
78      type: FieldType.number,
79      config: { color: { fixedColor: 'green', mode: FieldColorModeId.Fixed }, displayName: 'Success' },
80    },
81    [NodeGraphDataFrameFieldNames.arc + 'errors']: {
82      values: new ArrayVector(),
83      type: FieldType.number,
84      config: { color: { fixedColor: 'red', mode: FieldColorModeId.Fixed }, displayName: 'Errors' },
85    },
86  };
87
88  const nodeFrame = new MutableDataFrame({
89    name: 'nodes',
90    fields: Object.keys(nodeFields).map((key) => ({
91      ...nodeFields[key],
92      name: key,
93    })),
94    meta: { preferredVisualisationType: 'nodeGraph' },
95  });
96
97  const edgeFields: any = {
98    [NodeGraphDataFrameFieldNames.id]: {
99      values: new ArrayVector(),
100      type: FieldType.string,
101    },
102    [NodeGraphDataFrameFieldNames.source]: {
103      values: new ArrayVector(),
104      type: FieldType.string,
105    },
106    [NodeGraphDataFrameFieldNames.target]: {
107      values: new ArrayVector(),
108      type: FieldType.string,
109    },
110  };
111
112  const edgesFrame = new MutableDataFrame({
113    name: 'edges',
114    fields: Object.keys(edgeFields).map((key) => ({
115      ...edgeFields[key],
116      name: key,
117    })),
118    meta: { preferredVisualisationType: 'nodeGraph' },
119  });
120
121  const edgesSet = new Set();
122  for (const node of nodes) {
123    nodeFields.id.values.add(node.id);
124    nodeFields.title.values.add(node.title);
125    nodeFields.subTitle.values.add(node.subTitle);
126    nodeFields.mainStat.values.add(node.stat1);
127    nodeFields.secondaryStat.values.add(node.stat2);
128    nodeFields.arc__success.values.add(node.success);
129    nodeFields.arc__errors.values.add(node.error);
130    for (const edge of node.edges) {
131      const id = `${node.id}--${edge}`;
132      // We can have duplicate edges when we added some more by random
133      if (edgesSet.has(id)) {
134        continue;
135      }
136      edgesSet.add(id);
137      edgeFields.id.values.add(`${node.id}--${edge}`);
138      edgeFields.source.values.add(node.id);
139      edgeFields.target.values.add(edge);
140    }
141  }
142
143  return [nodeFrame, edgesFrame];
144}
145
146function makeRandomNode(index: number) {
147  const success = Math.random();
148  const error = 1 - success;
149  return {
150    id: index.toString(),
151    title: `service:${index}`,
152    subTitle: 'service',
153    success,
154    error,
155    stat1: Math.random(),
156    stat2: Math.random(),
157    edges: [],
158  };
159}
160
161export function savedNodesResponse(): any {
162  return [new MutableDataFrame(nodes), new MutableDataFrame(edges)];
163}
164