From 740ee25c20c9118cc057ca472ad18c89a304e449 Mon Sep 17 00:00:00 2001 From: Mart Ganzevles Date: Fri, 27 Dec 2024 12:54:46 +0100 Subject: [PATCH 1/2] Show total unused fields (#5878) Closes #5870 Co-authored-by: Mart Ganzevles --- .../app/src/pages/target-explorer-unused.tsx | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/packages/web/app/src/pages/target-explorer-unused.tsx b/packages/web/app/src/pages/target-explorer-unused.tsx index 582fb896c4..50a29bb262 100644 --- a/packages/web/app/src/pages/target-explorer-unused.tsx +++ b/packages/web/app/src/pages/target-explorer-unused.tsx @@ -13,6 +13,7 @@ import { Subtitle, Title } from '@/components/ui/page'; import { QueryError } from '@/components/ui/query-error'; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'; import { FragmentType, graphql, useFragment } from '@/gql'; +import { GraphQlInputObjectType, GraphQlInterfaceType, GraphQlObjectType } from '@/gql/graphql'; import { useDateRangeController } from '@/lib/hooks/use-date-range-controller'; import { cn } from '@/lib/utils'; import { TypeRenderer, TypeRenderFragment } from './target-explorer-type'; @@ -97,8 +98,29 @@ const UnusedSchemaView = memo(function _UnusedSchemaView(props: { return null; } + const unusedTypes = types.length ?? 0; + const unusedFields = types.reduce((a, b) => { + if (b.__typename === 'GraphQLObjectType') { + return a + (b as GraphQlObjectType).fields.length; + } + if (b.__typename === 'GraphQLInterfaceType') { + return a + (b as GraphQlInterfaceType).fields.length; + } + if (b.__typename === 'GraphQLInputObjectType') { + return a + (b as GraphQlInputObjectType).fields.length; + } + + return a; + }, 0); + return (
+
+

+ You have a total of {unusedFields} unused fields within {unusedTypes} different types in + the selected time period +

+
{letters.map(letter => ( From a307b3f942550a02f49087654ea60d2a3f91b8d7 Mon Sep 17 00:00:00 2001 From: Kamil Kisiela Date: Fri, 27 Dec 2024 14:04:37 +0100 Subject: [PATCH 2/2] distinguish fully unused fields and fields with unused fragments --- .../app/src/pages/target-explorer-unused.tsx | 86 ++++++++++++++----- 1 file changed, 64 insertions(+), 22 deletions(-) diff --git a/packages/web/app/src/pages/target-explorer-unused.tsx b/packages/web/app/src/pages/target-explorer-unused.tsx index 50a29bb262..07f66d29d7 100644 --- a/packages/web/app/src/pages/target-explorer-unused.tsx +++ b/packages/web/app/src/pages/target-explorer-unused.tsx @@ -13,7 +13,6 @@ import { Subtitle, Title } from '@/components/ui/page'; import { QueryError } from '@/components/ui/query-error'; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'; import { FragmentType, graphql, useFragment } from '@/gql'; -import { GraphQlInputObjectType, GraphQlInterfaceType, GraphQlObjectType } from '@/gql/graphql'; import { useDateRangeController } from '@/lib/hooks/use-date-range-controller'; import { cn } from '@/lib/utils'; import { TypeRenderer, TypeRenderFragment } from './target-explorer-type'; @@ -24,9 +23,21 @@ const UnusedSchemaView_UnusedSchemaExplorerFragment = graphql(` __typename ... on GraphQLObjectType { name + fields { + __typename + args { + __typename + } + } } ... on GraphQLInterfaceType { name + fields { + __typename + args { + __typename + } + } } ... on GraphQLUnionType { name @@ -36,6 +47,9 @@ const UnusedSchemaView_UnusedSchemaExplorerFragment = graphql(` } ... on GraphQLInputObjectType { name + fields { + __typename + } } ... on GraphQLScalarType { name @@ -71,7 +85,9 @@ const UnusedSchemaView = memo(function _UnusedSchemaView(props: { return grouped; }, [types]); - const letters = Array.from(typesGroupedByFirstLetter.keys()).sort(); + const letters = useMemo(() => { + return Array.from(typesGroupedByFirstLetter.keys()).sort(); + }, [typesGroupedByFirstLetter]); useEffect(() => { if (!selectedLetter) { @@ -79,6 +95,38 @@ const UnusedSchemaView = memo(function _UnusedSchemaView(props: { } }, [selectedLetter, setSelectedLetter]); + const unused = useMemo(() => { + const count = { + fields: 0, + fieldArguments: 0, + types: 0, + }; + + for (const type of types) { + if (type.__typename === 'GraphQLInputObjectType') { + count.types++; + count.fields += type.fields.length; + } else if ( + type.__typename === 'GraphQLObjectType' || + type.__typename === 'GraphQLInterfaceType' + ) { + count.types++; + + for (const field of type.fields) { + if (field.args.length === 0) { + // if there are no arguments, it means the entire field is unused + count.fields++; + } else { + // if there are arguments, the field is used, but the arguments are not + count.fieldArguments += field.args.length; + } + } + } + } + + return count; + }, [types]); + if (types.length === 0) { return (
@@ -98,29 +146,23 @@ const UnusedSchemaView = memo(function _UnusedSchemaView(props: { return null; } - const unusedTypes = types.length ?? 0; - const unusedFields = types.reduce((a, b) => { - if (b.__typename === 'GraphQLObjectType') { - return a + (b as GraphQlObjectType).fields.length; - } - if (b.__typename === 'GraphQLInterfaceType') { - return a + (b as GraphQlInterfaceType).fields.length; - } - if (b.__typename === 'GraphQLInputObjectType') { - return a + (b as GraphQlInputObjectType).fields.length; - } - - return a; - }, 0); + const unusedFieldsMessage = [ + unused.fields ? `${unused.fields} unused fields` : null, + unused.fieldArguments ? `${unused.fieldArguments} unused field arguments` : null, + ] + .filter(Boolean) + .join(' and '); return (
-
-

- You have a total of {unusedFields} unused fields within {unusedTypes} different types in - the selected time period -

-
+ {unusedFieldsMessage.length ? ( +
+

+ You have a total of {unusedFieldsMessage} within {unused.types} different types in the + selected time period +

+
+ ) : null}
{letters.map(letter => (