Console 1383 open non existing schema version results in infinite loading (#7316)

This commit is contained in:
Jonathan Brennan 2025-11-25 15:44:41 -06:00 committed by GitHub
parent 114cd80a8e
commit 0ac46404d5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 91 additions and 66 deletions

View file

@ -26,7 +26,7 @@ import { CHART_PRIMARY_COLOR } from '@/constants';
import { env } from '@/env/frontend';
import { DocumentType, FragmentType, graphql, useFragment } from '@/gql';
import { theme } from '@/lib/charts';
import { useChartStyles } from '@/utils';
import { useChartStyles } from '@/lib/utils';
import { ChevronUpIcon } from '@radix-ui/react-icons';
import {
createColumnHelper,

View file

@ -2,17 +2,27 @@ import ghost from '../../../public/images/figures/ghost.svg?url';
import { useRouter } from '@tanstack/react-router';
import { Button } from '../ui/button';
export function NotFoundContent(props: { heading: React.ReactNode; subheading: React.ReactNode }) {
export function NotFoundContent({
heading,
subheading,
includeBackButton = true,
}: {
heading: React.ReactNode;
subheading: React.ReactNode;
includeBackButton?: boolean;
}) {
const router = useRouter();
return (
<div className="flex h-full flex-1 flex-col items-center justify-center gap-2.5 py-6">
<img src={ghost} alt="Ghost illustration" width="200" height="200" className="drag-none" />
<h2 className="text-xl font-bold">{props.heading}</h2>
<h3 className="font-semibold">{props.subheading}</h3>
<Button variant="secondary" className="mt-2" onClick={router.history.back}>
Go back
</Button>
<h2 className="text-xl font-bold">{heading}</h2>
<h3 className="font-semibold">{subheading}</h3>
{includeBackButton && (
<Button variant="secondary" className="mt-2" onClick={router.history.back}>
Go back
</Button>
)}
</div>
);
}

View file

@ -9,7 +9,7 @@ import {
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
import { OrganizationAccessScope, ProjectAccessScope, TargetAccessScope } from '@/gql/graphql';
import { NoAccess, Scope } from '@/lib/access/common';
import { truthy } from '@/utils';
import { truthy } from '@/lib/utils';
function isLowerThen<T>(targetScope: T, sourceScope: T, scopesInLowerToHigherOrder: readonly T[]) {
const sourceIndex = scopesInLowerToHigherOrder.indexOf(sourceScope);

View file

@ -7,8 +7,7 @@ import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/comp
import { Markdown } from '@/components/v2/markdown';
import { FragmentType, graphql, useFragment } from '@/gql';
import { formatNumber, toDecimal } from '@/lib/hooks';
import { cn } from '@/lib/utils';
import { capitalize } from '@/utils';
import { capitalize, cn } from '@/lib/utils';
import { Link as NextLink, useRouter } from '@tanstack/react-router';
import { useDescriptionsVisibleToggle } from './provider';
import { SupergraphMetadataList } from './super-graph-metadata';

View file

@ -28,7 +28,7 @@ import {
useFormattedThroughput,
} from '@/lib/hooks';
import { pick } from '@/lib/object';
import { useChartStyles } from '@/utils';
import { useChartStyles } from '@/lib/utils';
import { useRouter } from '@tanstack/react-router';
import { OperationsFallback } from './Fallback';
import { resolutionToMilliseconds } from './utils';

View file

@ -15,7 +15,7 @@ export function CopyText(props: { children: ReactNode; copy?: string; className?
</div>
<TooltipProvider delayDuration={0}>
<Tooltip>
<TooltipTrigger>
<TooltipTrigger asChild>
<Button
className="invisible -my-3 p-2 py-3 group-hover:visible"
variant="link"

View file

@ -1,10 +1,24 @@
import { clsx, type ClassValue } from 'clsx';
import { twMerge } from 'tailwind-merge';
// Style-related
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
const darkChartStyles = {
backgroundColor: 'transparent',
textStyle: { color: '#fff' },
legend: {
textStyle: { color: '#fff' },
},
};
export function useChartStyles() {
return darkChartStyles;
}
// Strings
export function pluralize(count: number, singular: string, plural: string): string {
if (count === 1) {
return singular;
@ -13,8 +27,24 @@ export function pluralize(count: number, singular: string, plural: string): stri
return plural;
}
export function capitalize(str: string) {
return str.charAt(0).toUpperCase() + str.slice(1);
}
// Errors
export function exhaustiveGuard(_value: never): never {
throw new Error(
`Reached forbidden guard function with unexpected value: ${JSON.stringify(_value)}`,
);
}
// Validation
export function isValidUUID(value: string): boolean {
return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value);
}
type Truthy<T> = T extends false | '' | 0 | null | undefined ? never : T; // from lodash
export function truthy<T>(value: T): value is Truthy<T> {
return !!value;
}

View file

@ -18,7 +18,7 @@ import { Card } from '@/components/v2/card';
import Stat from '@/components/v2/stat';
import { graphql, useFragment } from '@/gql';
import { formatNumber } from '@/lib/hooks';
import { useChartStyles } from '@/utils';
import { useChartStyles } from '@/lib/utils';
import { Link } from '@tanstack/react-router';
const DateFormatter = Intl.DateTimeFormat('en-US', {

View file

@ -1,6 +1,7 @@
import { ReactElement, useMemo, useState } from 'react';
import { CheckIcon, GitCompareIcon } from 'lucide-react';
import { useQuery } from 'urql';
import { NotFoundContent } from '@/components/common/not-found-content';
import {
ChangesBlock,
CompositionErrorsSection,
@ -15,7 +16,7 @@ import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/comp
import { DiffEditor, TimeAgo } from '@/components/v2';
import { FragmentType, graphql, useFragment } from '@/gql';
import { ProjectType, SeverityLevelType } from '@/gql/graphql';
import { cn } from '@/lib/utils';
import { cn, isValidUUID } from '@/lib/utils';
import {
CheckCircledIcon,
CrossCircledIcon,
@ -242,15 +243,11 @@ const DefaultSchemaVersionView_SchemaVersionFragment = graphql(`
log {
... on PushedSchemaLog {
id
author
service
commit
serviceSdl
previousServiceSdl
}
... on DeletedSchemaLog {
id
deletedService
previousServiceSdl
}
}
@ -443,9 +440,6 @@ function DefaultSchemaVersionView(props: {
const ContractVersionView_ContractVersionFragment = graphql(`
fragment ContractVersionView_ContractVersionFragment on ContractVersion {
id
contractName
isComposable
hasSchemaChanges
isFirstComposableVersion
supergraphSDL
compositeSchemaSDL
@ -640,6 +634,8 @@ function ActiveSchemaVersion(props: {
projectSlug: string;
targetSlug: string;
}) {
const isValidVersionId = isValidUUID(props.versionId);
const [query] = useQuery({
query: ActiveSchemaVersion_SchemaVersionQuery,
variables: {
@ -648,6 +644,7 @@ function ActiveSchemaVersion(props: {
targetSlug: props.targetSlug,
versionId: props.versionId,
},
pause: !isValidVersionId,
});
const { error } = query;
@ -657,7 +654,28 @@ function ActiveSchemaVersion(props: {
const schemaVersion = project?.target?.schemaVersion;
const projectType = query.data?.project?.type;
if (isLoading || !schemaVersion || !projectType) {
// Order of these conditionals is important...relocate carefully!
if (!isValidVersionId) {
return (
<NotFoundContent
heading="Invalid version ID"
subheading="The provided version ID is not a valid UUID format."
includeBackButton={false}
/>
);
}
if (!isLoading && !schemaVersion) {
return (
<NotFoundContent
heading="Version ID does not exist"
subheading="The provided version ID is not in our database."
includeBackButton={false}
/>
);
}
if (isLoading || !projectType) {
return (
<div className="flex size-full flex-col items-center justify-center self-center text-sm text-gray-500">
<Spinner className="mb-3 size-8" />

View file

@ -49,7 +49,6 @@ const HistoryPage_VersionsPageQuery = graphql(`
deletedService
}
}
baseSchema
githubMetadata {
repository
commit

View file

@ -17,7 +17,7 @@ import { graphql } from '@/gql';
import { formatNumber, formatThroughput, toDecimal } from '@/lib/hooks';
import { useDateRangeController } from '@/lib/hooks/use-date-range-controller';
import { pick } from '@/lib/object';
import { useChartStyles } from '@/utils';
import { useChartStyles } from '@/lib/utils';
import { Link } from '@tanstack/react-router';
const ClientView_ClientStatsQuery = graphql(`

View file

@ -26,7 +26,7 @@ import { CHART_PRIMARY_COLOR } from '@/constants';
import { graphql } from '@/gql';
import { formatNumber, formatThroughput, toDecimal } from '@/lib/hooks';
import { useDateRangeController } from '@/lib/hooks/use-date-range-controller';
import { useChartStyles } from '@/utils';
import { useChartStyles } from '@/lib/utils';
import { Link } from '@tanstack/react-router';
const SchemaCoordinateView_SchemaCoordinateStatsQuery = graphql(`

View file

@ -1,31 +0,0 @@
type Truthy<T> = T extends false | '' | 0 | null | undefined ? never : T; // from lodash
export function truthy<T>(value: T): value is Truthy<T> {
return !!value;
}
const darkChartStyles = {
backgroundColor: 'transparent',
textStyle: { color: '#fff' },
legend: {
textStyle: { color: '#fff' },
},
};
export function useChartStyles() {
return darkChartStyles;
// TODO: fix it when Hive will have white theme
// useColorModeValue(
// {
// backgroundColor: '#fff',
// textStyle: { color: '#52525b' },
// legend: {
// textStyle: { color: '#52525b' },
// },
// },
// );
}
export function capitalize(str: string) {
return str.charAt(0).toUpperCase() + str.slice(1);
}

View file

@ -19364,8 +19364,8 @@ snapshots:
dependencies:
'@aws-crypto/sha256-browser': 3.0.0
'@aws-crypto/sha256-js': 3.0.0
'@aws-sdk/client-sso-oidc': 3.596.0
'@aws-sdk/client-sts': 3.596.0(@aws-sdk/client-sso-oidc@3.596.0)
'@aws-sdk/client-sso-oidc': 3.596.0(@aws-sdk/client-sts@3.596.0)
'@aws-sdk/client-sts': 3.596.0
'@aws-sdk/core': 3.592.0
'@aws-sdk/credential-provider-node': 3.596.0(@aws-sdk/client-sso-oidc@3.596.0)(@aws-sdk/client-sts@3.596.0)
'@aws-sdk/middleware-host-header': 3.577.0
@ -19517,11 +19517,11 @@ snapshots:
transitivePeerDependencies:
- aws-crt
'@aws-sdk/client-sso-oidc@3.596.0':
'@aws-sdk/client-sso-oidc@3.596.0(@aws-sdk/client-sts@3.596.0)':
dependencies:
'@aws-crypto/sha256-browser': 3.0.0
'@aws-crypto/sha256-js': 3.0.0
'@aws-sdk/client-sts': 3.596.0(@aws-sdk/client-sso-oidc@3.596.0)
'@aws-sdk/client-sts': 3.596.0
'@aws-sdk/core': 3.592.0
'@aws-sdk/credential-provider-node': 3.596.0(@aws-sdk/client-sso-oidc@3.596.0)(@aws-sdk/client-sts@3.596.0)
'@aws-sdk/middleware-host-header': 3.577.0
@ -19560,6 +19560,7 @@ snapshots:
'@smithy/util-utf8': 3.0.0
tslib: 2.8.1
transitivePeerDependencies:
- '@aws-sdk/client-sts'
- aws-crt
'@aws-sdk/client-sso-oidc@3.723.0(@aws-sdk/client-sts@3.723.0)':
@ -19779,11 +19780,11 @@ snapshots:
transitivePeerDependencies:
- aws-crt
'@aws-sdk/client-sts@3.596.0(@aws-sdk/client-sso-oidc@3.596.0)':
'@aws-sdk/client-sts@3.596.0':
dependencies:
'@aws-crypto/sha256-browser': 3.0.0
'@aws-crypto/sha256-js': 3.0.0
'@aws-sdk/client-sso-oidc': 3.596.0
'@aws-sdk/client-sso-oidc': 3.596.0(@aws-sdk/client-sts@3.596.0)
'@aws-sdk/core': 3.592.0
'@aws-sdk/credential-provider-node': 3.596.0(@aws-sdk/client-sso-oidc@3.596.0)(@aws-sdk/client-sts@3.596.0)
'@aws-sdk/middleware-host-header': 3.577.0
@ -19822,7 +19823,6 @@ snapshots:
'@smithy/util-utf8': 3.0.0
tslib: 2.8.1
transitivePeerDependencies:
- '@aws-sdk/client-sso-oidc'
- aws-crt
'@aws-sdk/client-sts@3.723.0':
@ -20054,7 +20054,7 @@ snapshots:
'@aws-sdk/credential-provider-ini@3.596.0(@aws-sdk/client-sso-oidc@3.596.0)(@aws-sdk/client-sts@3.596.0)':
dependencies:
'@aws-sdk/client-sts': 3.596.0(@aws-sdk/client-sso-oidc@3.596.0)
'@aws-sdk/client-sts': 3.596.0
'@aws-sdk/credential-provider-env': 3.587.0
'@aws-sdk/credential-provider-http': 3.596.0
'@aws-sdk/credential-provider-process': 3.587.0
@ -20301,7 +20301,7 @@ snapshots:
'@aws-sdk/credential-provider-web-identity@3.587.0(@aws-sdk/client-sts@3.596.0)':
dependencies:
'@aws-sdk/client-sts': 3.596.0(@aws-sdk/client-sso-oidc@3.596.0)
'@aws-sdk/client-sts': 3.596.0
'@aws-sdk/types': 3.577.0
'@smithy/property-provider': 3.1.11
'@smithy/types': 3.7.2
@ -20690,7 +20690,7 @@ snapshots:
'@aws-sdk/token-providers@3.587.0(@aws-sdk/client-sso-oidc@3.596.0)':
dependencies:
'@aws-sdk/client-sso-oidc': 3.596.0
'@aws-sdk/client-sso-oidc': 3.596.0(@aws-sdk/client-sts@3.596.0)
'@aws-sdk/types': 3.577.0
'@smithy/property-provider': 3.1.11
'@smithy/shared-ini-file-loader': 3.1.12