Skip to content

Commit a30d617

Browse files
authored
Merge branch 'main' into hive-gateway-game-right
2 parents 7eb4a86 + 5307b0c commit a30d617

File tree

6 files changed

+282
-1
lines changed

6 files changed

+282
-1
lines changed
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
'use client';
2+
3+
import { use } from 'react';
4+
import { cn, ComparisonTable as Table } from '@theguild/components';
5+
import { functionalTones } from './functional-tones';
6+
import { CheckmarkIcon, XIcon } from './icons';
7+
8+
interface BenchmarkDatum {
9+
name: string;
10+
cases: {
11+
passed: number;
12+
failed: number;
13+
};
14+
suites: {
15+
passed: number;
16+
failed: number;
17+
};
18+
}
19+
20+
const dataJson = fetch(
21+
'https://the-guild.dev/graphql/hive/federation-gateway-audit/data.json',
22+
).then(
23+
res =>
24+
// we didn't parse this, because we trust @kamilkisiela
25+
res.json() as Promise<BenchmarkDatum[]>,
26+
);
27+
28+
export function BenchmarkTableBody() {
29+
// we're fetching in client component to get fresh data without redeploy
30+
// if we don't need it THAT fresh, feel free to just await it in the parent component
31+
const data = use(dataJson);
32+
33+
return (
34+
<tbody className="">
35+
{data.map(row => {
36+
const compatibility = (row.cases.passed / (row.cases.passed + row.cases.failed)) * 100;
37+
38+
return (
39+
<Table.Row key={row.name} highlight={row.name === 'Hive Gateway'}>
40+
<Table.Cell
41+
className={cn(
42+
// todo: this is a bug in Components: we diverged from design
43+
row.name === 'Hive Gateway' ? '!bg-green-100' : '',
44+
'pl-5', // yes, the dot cuts in to the left per design
45+
'max-sm:pr-1.5',
46+
)}
47+
>
48+
<div className="flex items-center gap-2.5 whitespace-nowrap">
49+
<div
50+
className="size-3 rounded-full"
51+
style={{
52+
background:
53+
compatibility > 99
54+
? functionalTones.positiveBright
55+
: compatibility > 90
56+
? functionalTones.warning
57+
: functionalTones.criticalBright,
58+
}}
59+
/>
60+
{row.name}
61+
</div>
62+
</Table.Cell>
63+
<Table.Cell className="text-sm text-green-800">{compatibility.toFixed(2)}%</Table.Cell>
64+
<Table.Cell>
65+
<span
66+
className="inline-flex items-center gap-0.5 text-sm"
67+
style={{ color: functionalTones.positiveDark }}
68+
>
69+
<CheckmarkIcon className="size-4" /> {row.cases.passed}
70+
</span>
71+
{row.cases.failed > 0 && (
72+
<span
73+
className="ml-2 inline-flex items-center text-sm"
74+
style={{ color: functionalTones.criticalDark }}
75+
>
76+
<XIcon className="size-4" /> {row.cases.failed}
77+
</span>
78+
)}
79+
</Table.Cell>
80+
<Table.Cell>
81+
<span
82+
className="inline-flex items-center gap-0.5 text-sm"
83+
style={{ color: functionalTones.positiveDark }}
84+
>
85+
<CheckmarkIcon className="size-4" /> {row.suites.passed}
86+
</span>
87+
{row.suites.failed > 0 && (
88+
<span
89+
className="ml-2 inline-flex items-center text-sm"
90+
style={{ color: functionalTones.criticalDark }}
91+
>
92+
<XIcon className="size-4" /> {row.suites.failed}
93+
</span>
94+
)}
95+
</Table.Cell>
96+
</Table.Row>
97+
);
98+
})}
99+
</tbody>
100+
);
101+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/**
2+
* todo: move this to the design system as Tailwind classes
3+
*/
4+
export const functionalTones = {
5+
criticalBright: '#FD3325',
6+
criticalDark: ' #F81202',
7+
warning: '#FE8830',
8+
positiveBright: '#24D551',
9+
positiveDark: '#1BA13D',
10+
};
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// these are different than CheckIcon and CloseIcon we have in the design system
2+
3+
export function CheckmarkIcon(props: React.SVGProps<SVGSVGElement>) {
4+
return (
5+
<svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor" {...props}>
6+
<path d="M6.66668 10.1134L12.7947 3.98608L13.7373 4.92875L6.66668 11.9994L2.42401 7.75675L3.36668 6.81408L6.66668 10.1134Z" />
7+
</svg>
8+
);
9+
}
10+
11+
export function XIcon(props: React.SVGProps<SVGSVGElement>) {
12+
return (
13+
<svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor" {...props}>
14+
<path d="M7.99999 7.05806L11.3 3.75806L12.2427 4.70072L8.94266 8.00072L12.2427 11.3007L11.2993 12.2434L7.99932 8.94339L4.69999 12.2434L3.75732 11.3001L7.05732 8.00006L3.75732 4.70006L4.69999 3.75872L7.99999 7.05806Z" />
15+
</svg>
16+
);
17+
}
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
import { Suspense } from 'react';
2+
import { CallToAction, cn, Heading, ComparisonTable as Table } from '@theguild/components';
3+
import { BenchmarkTableBody } from './benchmark-table-body';
4+
import { functionalTones } from './functional-tones';
5+
import { CheckmarkIcon, XIcon } from './icons';
6+
7+
export interface FederationCompatibleBenchmarksSectionProps
8+
extends React.HTMLAttributes<HTMLDivElement> {}
9+
10+
export function FederationCompatibleBenchmarksSection({
11+
className,
12+
...rest
13+
}: FederationCompatibleBenchmarksSectionProps) {
14+
return (
15+
<section
16+
className={cn(
17+
'text-green-1000 px-4 py-6 sm:py-12 md:px-6 lg:py-[120px] xl:px-[120px]',
18+
className,
19+
)}
20+
{...rest}
21+
>
22+
<header className="md:text-balance md:text-center">
23+
<Heading as="h1" size="lg">
24+
Federation-Compatible Gateway Benchmarks
25+
</Heading>
26+
<p className="mb-6 mt-4 text-green-800 md:mb-16">
27+
See the results of our open-source audit for Apollo Federation Gateways.
28+
</p>
29+
</header>
30+
<div className="my-6 flex items-start gap-6 max-md:flex-col md:mb-12 md:mt-16">
31+
<p className="text-pretty text-2xl/8 lg:text-[32px]/10">
32+
Learn how Hive Gateway performs against other gateways in&nbsp;terms of correctness and
33+
compliance with the Apollo Federation specification
34+
</p>
35+
<CallToAction
36+
variant="tertiary"
37+
href="https://the-guild.dev/graphql/hive/federation-gateway-audit"
38+
>
39+
Learn about our audit and methodology
40+
</CallToAction>
41+
</div>
42+
<div className="hive-focus nextra-scrollbar border-beige-400 [&_:is(td,th)]:border-beige-400 overflow-x-auto rounded-2xl border [scrollbar-width:auto] max-sm:-mx-8">
43+
<Table className="table w-full border-none max-sm:rounded-none max-sm:text-sm">
44+
<thead>
45+
<Table.Row className="*:text-left">
46+
<Table.Header className="whitespace-pre pl-6">
47+
Gateway
48+
<small className="block text-xs/[18px] text-green-800">Name and variant</small>
49+
</Table.Header>
50+
<Table.Header className="whitespace-pre sm:w-1/4">
51+
Compatibility
52+
<small className="block text-xs/[18px] text-green-800">
53+
Pass rate of test cases
54+
</small>
55+
</Table.Header>
56+
<Table.Header className="whitespace-pre sm:w-1/4">
57+
Test Cases
58+
<small className="block text-xs/[18px] text-green-800">
59+
All available test cases
60+
</small>
61+
</Table.Header>
62+
<Table.Header className="whitespace-pre sm:w-1/4">
63+
Test Suites
64+
<small className="block text-xs/[18px] text-green-800">
65+
Test cases grouped by feature
66+
</small>
67+
</Table.Header>
68+
</Table.Row>
69+
</thead>
70+
<Suspense
71+
fallback={
72+
<tbody aria-busy>
73+
<tr>
74+
<td colSpan={4} className="bg-beige-100 h-[347.5px] animate-pulse cursor-wait" />
75+
</tr>
76+
</tbody>
77+
}
78+
>
79+
<BenchmarkTableBody />
80+
</Suspense>
81+
</Table>
82+
</div>
83+
<BenchmarkLegend />
84+
</section>
85+
);
86+
}
87+
88+
function BenchmarkLegend() {
89+
return (
90+
<div className="mt-6 flex flex-wrap gap-2 whitespace-nowrap text-xs text-green-800 sm:gap-4">
91+
<div className="flex gap-2 max-sm:-mx-1 max-sm:w-full sm:contents">
92+
<div className="flex items-center gap-1">
93+
<CheckmarkIcon className="size-4" style={{ color: functionalTones.positiveDark }} />{' '}
94+
Passed tests
95+
</div>
96+
<div className="flex items-center gap-1">
97+
<XIcon className="size-4" style={{ color: functionalTones.criticalDark }} /> Failed tests
98+
</div>
99+
</div>
100+
<div className="flex items-center gap-2">
101+
<div
102+
className="size-2 rounded-full"
103+
style={{ background: functionalTones.positiveBright }}
104+
/>
105+
Perfect compatibility
106+
</div>
107+
<div className="flex items-center gap-2">
108+
<div className="size-2 rounded-full" style={{ background: functionalTones.warning }} />
109+
75% and higher
110+
</div>
111+
<div className="flex items-center gap-2">
112+
<div className="size-2 rounded-full" style={{ background: functionalTones.criticalDark }} />
113+
Less than 75%
114+
</div>
115+
</div>
116+
);
117+
}

packages/web/docs/src/app/gateway/page.tsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@ import {
99
HiveGatewayIcon,
1010
} from '@theguild/components';
1111
import { GetYourAPIGameRightList } from '../../components/get-your-api-game-right-list';
12+
import { ErrorBoundary } from '../../components/error-boundary';
1213
import { LandingPageContainer } from '../../components/landing-page-container';
1314
import { metadata as rootMetadata } from '../layout';
15+
import { FederationCompatibleBenchmarksSection } from './federation-compatible-benchmarks';
1416
import { GatewayFeatureTabs } from './gateway-feature-tabs';
1517
import GatewayLandingFAQ from './gateway-landing-faq.mdx';
1618
import { OrchestrateYourWay } from './orchestrate-your-way';
@@ -55,7 +57,14 @@ export default function HiveGatewayPage() {
5557
</Hero>
5658
<GatewayFeatureTabs className="relative mt-6 sm:mt-[-72px] sm:bg-blue-100" />
5759
<OrchestrateYourWay className="mx-4 mt-6 sm:mx-8" />
58-
{/* Federation-Compatible Gateway Benchmarks */}
60+
<ErrorBoundary
61+
fallback={
62+
// this section doesn't make sense if data didn't load, so we just unmount
63+
null
64+
}
65+
>
66+
<FederationCompatibleBenchmarksSection />
67+
</ErrorBoundary>
5968
{/* Let's get advanced */}
6069
{/* Cloud-Native Nature */}
6170
<ExploreMainProductCards className="max-lg:mx-4 max-lg:my-8" />
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
'use client';
2+
3+
import { Component } from 'react';
4+
5+
export class ErrorBoundary extends Component<{
6+
fallback: React.ReactNode;
7+
children: React.ReactNode;
8+
}> {
9+
state = { hasError: false };
10+
11+
static getDerivedStateFromError(error: Error) {
12+
console.error(error);
13+
return { hasError: true };
14+
}
15+
16+
componentDidCatch(error: Error, info: { componentStack: string }) {
17+
console.error(error, info);
18+
}
19+
20+
render() {
21+
if (this.state.hasError) {
22+
return this.props.fallback;
23+
}
24+
25+
return this.props.children;
26+
}
27+
}

0 commit comments

Comments
 (0)