diff --git a/packages/web/docs/src/app/gateway/federation-compatible-benchmarks/benchmark-table-body.tsx b/packages/web/docs/src/app/gateway/federation-compatible-benchmarks/benchmark-table-body.tsx new file mode 100644 index 000000000..ae8c1ae2b --- /dev/null +++ b/packages/web/docs/src/app/gateway/federation-compatible-benchmarks/benchmark-table-body.tsx @@ -0,0 +1,101 @@ +'use client'; + +import { use } from 'react'; +import { cn, ComparisonTable as Table } from '@theguild/components'; +import { functionalTones } from './functional-tones'; +import { CheckmarkIcon, XIcon } from './icons'; + +interface BenchmarkDatum { + name: string; + cases: { + passed: number; + failed: number; + }; + suites: { + passed: number; + failed: number; + }; +} + +const dataJson = fetch( + 'https://the-guild.dev/graphql/hive/federation-gateway-audit/data.json', +).then( + res => + // we didn't parse this, because we trust @kamilkisiela + res.json() as Promise, +); + +export function BenchmarkTableBody() { + // we're fetching in client component to get fresh data without redeploy + // if we don't need it THAT fresh, feel free to just await it in the parent component + const data = use(dataJson); + + return ( + + {data.map(row => { + const compatibility = (row.cases.passed / (row.cases.passed + row.cases.failed)) * 100; + + return ( + + +
+
99 + ? functionalTones.positiveBright + : compatibility > 90 + ? functionalTones.warning + : functionalTones.criticalBright, + }} + /> + {row.name} +
+ + {compatibility.toFixed(2)}% + + + {row.cases.passed} + + {row.cases.failed > 0 && ( + + {row.cases.failed} + + )} + + + + {row.suites.passed} + + {row.suites.failed > 0 && ( + + {row.suites.failed} + + )} + + + ); + })} + + ); +} diff --git a/packages/web/docs/src/app/gateway/federation-compatible-benchmarks/functional-tones.ts b/packages/web/docs/src/app/gateway/federation-compatible-benchmarks/functional-tones.ts new file mode 100644 index 000000000..33d73114e --- /dev/null +++ b/packages/web/docs/src/app/gateway/federation-compatible-benchmarks/functional-tones.ts @@ -0,0 +1,10 @@ +/** + * todo: move this to the design system as Tailwind classes + */ +export const functionalTones = { + criticalBright: '#FD3325', + criticalDark: ' #F81202', + warning: '#FE8830', + positiveBright: '#24D551', + positiveDark: '#1BA13D', +}; diff --git a/packages/web/docs/src/app/gateway/federation-compatible-benchmarks/icons.tsx b/packages/web/docs/src/app/gateway/federation-compatible-benchmarks/icons.tsx new file mode 100644 index 000000000..8e16b7372 --- /dev/null +++ b/packages/web/docs/src/app/gateway/federation-compatible-benchmarks/icons.tsx @@ -0,0 +1,17 @@ +// these are different than CheckIcon and CloseIcon we have in the design system + +export function CheckmarkIcon(props: React.SVGProps) { + return ( + + + + ); +} + +export function XIcon(props: React.SVGProps) { + return ( + + + + ); +} diff --git a/packages/web/docs/src/app/gateway/federation-compatible-benchmarks/index.tsx b/packages/web/docs/src/app/gateway/federation-compatible-benchmarks/index.tsx new file mode 100644 index 000000000..da678cfc3 --- /dev/null +++ b/packages/web/docs/src/app/gateway/federation-compatible-benchmarks/index.tsx @@ -0,0 +1,117 @@ +import { Suspense } from 'react'; +import { CallToAction, cn, Heading, ComparisonTable as Table } from '@theguild/components'; +import { BenchmarkTableBody } from './benchmark-table-body'; +import { functionalTones } from './functional-tones'; +import { CheckmarkIcon, XIcon } from './icons'; + +export interface FederationCompatibleBenchmarksSectionProps + extends React.HTMLAttributes {} + +export function FederationCompatibleBenchmarksSection({ + className, + ...rest +}: FederationCompatibleBenchmarksSectionProps) { + return ( +
+
+ + Federation-Compatible Gateway Benchmarks + +

+ See the results of our open-source audit for Apollo Federation Gateways. +

+
+
+

+ Learn how Hive Gateway performs against other gateways in terms of correctness and + compliance with the Apollo Federation specification +

+ + Learn about our audit and methodology + +
+
+ + + + + Gateway + Name and variant + + + Compatibility + + Pass rate of test cases + + + + Test Cases + + All available test cases + + + + Test Suites + + Test cases grouped by feature + + + + + + + + + } + > + + +
+
+
+ +
+ ); +} + +function BenchmarkLegend() { + return ( +
+
+
+ {' '} + Passed tests +
+
+ Failed tests +
+
+
+
+ Perfect compatibility +
+
+
+ 75% and higher +
+
+
+ Less than 75% +
+
+ ); +} diff --git a/packages/web/docs/src/app/gateway/page.tsx b/packages/web/docs/src/app/gateway/page.tsx index 6b3bcac77..2ee666ba1 100644 --- a/packages/web/docs/src/app/gateway/page.tsx +++ b/packages/web/docs/src/app/gateway/page.tsx @@ -8,8 +8,10 @@ import { HeroLogo, HiveGatewayIcon, } from '@theguild/components'; +import { ErrorBoundary } from '../../components/error-boundary'; import { LandingPageContainer } from '../../components/landing-page-container'; import { metadata as rootMetadata } from '../layout'; +import { FederationCompatibleBenchmarksSection } from './federation-compatible-benchmarks'; import { GatewayFeatureTabs } from './gateway-feature-tabs'; import GatewayLandingFAQ from './gateway-landing-faq.mdx'; import { OrchestrateYourWay } from './orchestrate-your-way'; @@ -54,7 +56,14 @@ export default function HiveGatewayPage() { - {/* Federation-Compatible Gateway Benchmarks */} + + + {/* Let's get advanced */} {/* Cloud-Native Nature */} diff --git a/packages/web/docs/src/components/error-boundary.ts b/packages/web/docs/src/components/error-boundary.ts new file mode 100644 index 000000000..f3cc488e8 --- /dev/null +++ b/packages/web/docs/src/components/error-boundary.ts @@ -0,0 +1,27 @@ +'use client'; + +import { Component } from 'react'; + +export class ErrorBoundary extends Component<{ + fallback: React.ReactNode; + children: React.ReactNode; +}> { + state = { hasError: false }; + + static getDerivedStateFromError(error: Error) { + console.error(error); + return { hasError: true }; + } + + componentDidCatch(error: Error, info: { componentStack: string }) { + console.error(error, info); + } + + render() { + if (this.state.hasError) { + return this.props.fallback; + } + + return this.props.children; + } +}