Skip to content

Commit 4fd48ba

Browse files
Merge branch 'main' into trevor/fix-2743
2 parents f81f1fc + 4fd562b commit 4fd48ba

File tree

9 files changed

+273
-11
lines changed

9 files changed

+273
-11
lines changed

.changeset/chilled-geckos-change.md

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
3+
---
4+
5+
6+

.circleci/config.yml

+3
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ jobs:
2424
- run:
2525
name: Run tests
2626
command: npm run test:ci
27+
- run:
28+
name: Upload coverage
29+
command: npm run coverage:upload
2730
- store_test_results:
2831
path: junit.xml
2932

.cspell/cspell-dict.txt

+1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ caes
3535
cand
3636
Childs
3737
childs
38+
codecov
3839
compabitle
3940
compatbility
4041
Compatiblity

codecov.yml

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
coverage:
2+
status:
3+
project:
4+
default:
5+
threshold: 1%
6+
patch:
7+
default:
8+
target: 0%

package-lock.json

+112
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+4-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@
1414
"test:clean": "jest --clearCache",
1515
"test:watch": "jest --verbose --watchAll",
1616
"testonly": "npm test",
17-
"test:ci": "npm test -- --ci --maxWorkers=2 --reporters=default --reporters=jest-junit",
17+
"test:ci": "npm run coverage -- --ci --maxWorkers=2 --reporters=default --reporters=jest-junit",
18+
"coverage": "npm test -- --coverage",
19+
"coverage:upload": "codecov",
1820
"codegen": "graphql-codegen --config codegen.yml",
1921
"codegen:check": "npm run codegen && git diff --exit-code",
2022
"lint": "eslint . --ext .ts",
@@ -67,6 +69,7 @@
6769
"@types/uuid": "9.0.3",
6870
"@typescript-eslint/eslint-plugin": "5.62.0",
6971
"bunyan": "1.8.15",
72+
"codecov": "3.8.3",
7073
"cspell": "6.31.3",
7174
"graphql": "16.8.0",
7275
"graphql-http": "1.22.0",

query-graphs-js/src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ export * from './transition';
66
export * from './pathContext';
77
export * from './conditionsCaching';
88
export * from './conditionsValidation';
9+
export * from './mermaid';

query-graphs-js/src/mermaid.ts

+116
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/* Functions used to output query graphs as [mermaid graphs](https://mermaid.js.org/syntax/flowchart.html). */
2+
3+
import { ObjectType } from "@apollo/federation-internals";
4+
import { Edge, FEDERATED_GRAPH_ROOT_SOURCE, QueryGraph, Vertex, isFederatedGraphRootType, simpleTraversal } from "./querygraph";
5+
6+
export type MermaidOptions = {
7+
includeRootTypeLinks?: boolean,
8+
}
9+
10+
export class MermaidGraph {
11+
private readonly before: string[] = [];
12+
private readonly after: string[] = [];
13+
private readonly subgraphs = new Map<string, string[]>();
14+
15+
private isBuilt = false;
16+
17+
constructor(
18+
private readonly graph: QueryGraph,
19+
private readonly options: MermaidOptions = {},
20+
) {
21+
for (const name of graph.sources.keys()) {
22+
if (name === this.graph.name || name === FEDERATED_GRAPH_ROOT_SOURCE) {
23+
continue;
24+
}
25+
this.subgraphs.set(name, []);
26+
}
27+
}
28+
29+
private subgraphName(vertex: Vertex): string | undefined {
30+
if (vertex.source === this.graph.name || vertex.source === FEDERATED_GRAPH_ROOT_SOURCE) {
31+
return undefined;
32+
}
33+
return vertex.source;
34+
}
35+
36+
private vertexName(vertex: Vertex): string {
37+
if (isFederatedGraphRootType(vertex.type)) {
38+
return `root-${vertex.type.name.slice(1, vertex.type.name.length-1)}`;
39+
}
40+
const sg = this.subgraphName(vertex);
41+
const n = sg ? `${vertex.type.name}-${sg}` : `${vertex.type.name}`;
42+
return vertex.provideId ? `${n}-${vertex.provideId}` : n;
43+
}
44+
45+
addVertex(vertex: Vertex): void {
46+
const sg = this.subgraphName(vertex);
47+
const addTo = sg ? this.subgraphs.get(sg)! : this.before;
48+
if (isFederatedGraphRootType(vertex.type)) {
49+
addTo.push(`${this.vertexName(vertex)}(["root(${vertex.type.name.slice(1, vertex.type.name.length)})"])`);
50+
} else {
51+
addTo.push(`${this.vertexName(vertex)}["${vertex.toString()}"]`);
52+
}
53+
}
54+
55+
addEdge(edge: Edge): boolean {
56+
switch (edge.transition.kind) {
57+
case 'FieldCollection':
58+
if (edge.transition.definition.name.startsWith('_')) {
59+
return false;
60+
}
61+
break;
62+
case 'RootTypeResolution':
63+
if (!(this.options.includeRootTypeLinks ?? true)) {
64+
return false;
65+
}
66+
break;
67+
case 'SubgraphEnteringTransition':
68+
const rt = edge.tail.type as ObjectType;
69+
if (rt.fields().filter((f) => !f.name.startsWith('_')).length === 0) {
70+
return false;
71+
}
72+
break;
73+
}
74+
75+
const head = this.vertexName(edge.head);
76+
const tail = this.vertexName(edge.tail);
77+
const addTo = edge.head.source !== this.graph.name && edge.head.source === edge.tail.source
78+
? this.subgraphs.get(edge.head.source)!
79+
: this.after;
80+
const label = edge.label();
81+
if (label.length === 0) {
82+
addTo.push(`${head} --> ${tail}`);
83+
} else {
84+
addTo.push(`${head} -->|"${label}"| ${tail}`);
85+
}
86+
return true;
87+
}
88+
89+
build(): void {
90+
if (this.isBuilt) {
91+
return;
92+
}
93+
94+
simpleTraversal(
95+
this.graph,
96+
(v) => this.addVertex(v),
97+
(e) => this.addEdge(e),
98+
);
99+
100+
this.isBuilt = true;
101+
}
102+
103+
toString(): string {
104+
this.build();
105+
106+
const final = [ 'flowchart TD' ];
107+
this.before.forEach((b) => final.push(' ' + b));
108+
for (const [name, data] of this.subgraphs.entries()) {
109+
final.push(` subgraph ${name}`);
110+
data.forEach((d) => final.push(' ' + d));
111+
final.push(' end');
112+
}
113+
this.after.forEach((a) => final.push(' ' + a));
114+
return final.join('\n');
115+
}
116+
}

0 commit comments

Comments
 (0)