update useEntityTypeFilter too

Signed-off-by: Fredrik Adelöw <freben@gmail.com>
This commit is contained in:
Fredrik Adelöw
2022-02-13 21:57:07 +01:00
parent 6e1cbc12a6
commit 4fb71d3f6f
5 changed files with 45 additions and 38 deletions
+1
View File
@@ -8,6 +8,7 @@
'@backstage/plugin-catalog-react': patch
'@backstage/plugin-explore': patch
'@backstage/plugin-fossa': patch
'@backstage/plugin-scaffolder': patch
'@backstage/plugin-todo-backend': patch
---
@@ -26,6 +26,7 @@ import { EntityKindFilter, EntityTypeFilter } from '../../filters';
import { alertApiRef } from '@backstage/core-plugin-api';
import { ApiProvider } from '@backstage/core-app-api';
import { renderWithEffects, TestApiRegistry } from '@backstage/test-utils';
import { GetEntityFacetsResponse } from '@backstage/catalog-client';
const entities: Entity[] = [
{
@@ -64,7 +65,14 @@ const apis = TestApiRegistry.from(
[
catalogApiRef,
{
getEntities: jest.fn().mockResolvedValue({ items: entities }),
getEntityFacets: jest.fn().mockResolvedValue({
facets: {
'spec.type': entities.map(e => ({
value: (e.spec as any).type,
count: 1,
})),
},
} as GetEntityFacetsResponse),
},
],
[
@@ -17,6 +17,7 @@
import { useEffect, useMemo, useRef, useState } from 'react';
import useAsync from 'react-use/lib/useAsync';
import isEqual from 'lodash/isEqual';
import sortBy from 'lodash/sortBy';
import { useApi } from '@backstage/core-plugin-api';
import { catalogApiRef } from '../api';
import { useEntityListProvider } from './useEntityListProvider';
@@ -67,51 +68,34 @@ export function useEntityTypeFilter(): EntityTypeReturn {
const {
error,
loading,
value: entities,
value: facets,
} = useAsync(async () => {
if (kind) {
const items = await catalogApi
.getEntities({
.getEntityFacets({
filter: { kind },
fields: ['spec.type'],
facets: ['spec.type'],
})
.then(response => response.items);
.then(response => response.facets['spec.type'] || []);
return items;
}
return [];
}, [kind, catalogApi]);
const entitiesRef = useRef(entities);
const facetsRef = useRef(facets);
useEffect(() => {
const oldEntities = entitiesRef.current;
entitiesRef.current = entities;
// Delay processing hook until kind and entity load updates have settled to generate list of types;
// This prevents reseting the type filter due to saved type value from query params not matching the
const oldFacets = facetsRef.current;
facetsRef.current = facets;
// Delay processing hook until kind and facets load updates have settled to generate list of types;
// This prevents resetting the type filter due to saved type value from query params not matching the
// empty set of type values while values are still being loaded; also only run this hook on changes
// to entities
if (loading || !kind || oldEntities === entities) {
// to facets
if (loading || !kind || oldFacets === facets || !facets) {
return;
}
// Resolve the unique set of types from returned entities; could be optimized by a new endpoint
// in the catalog-backend that does this, rather than loading entities with redundant types.
if (!entities) return;
// Sort by entity count descending, so the most common types appear on top
const countByType = entities.reduce((acc, entity) => {
if (typeof entity.spec?.type !== 'string') return acc;
const entityType = entity.spec.type.toLocaleLowerCase('en-US');
if (!acc[entityType]) {
acc[entityType] = 0;
}
acc[entityType] += 1;
return acc;
}, {} as Record<string, number>);
const newTypes = Object.entries(countByType)
.sort(([, count1], [, count2]) => count2 - count1)
.map(([type]) => type);
// Sort by facet count descending, so the most common types appear on top
const newTypes = sortBy(facets, f => -f.count).map(f => f.value);
setAvailableTypes(newTypes);
// Update type filter to only valid values when the list of available types has changed
@@ -121,7 +105,7 @@ export function useEntityTypeFilter(): EntityTypeReturn {
if (!isEqual(selectedTypes, stillValidTypes)) {
setSelectedTypes(stillValidTypes);
}
}, [loading, kind, selectedTypes, setSelectedTypes, entities]);
}, [loading, kind, selectedTypes, setSelectedTypes, facets]);
useEffect(() => {
updateFilters({
@@ -16,6 +16,7 @@
import React from 'react';
import { fireEvent } from '@testing-library/react';
import { GetEntityFacetsResponse } from '@backstage/catalog-client';
import { Entity } from '@backstage/catalog-model';
import {
catalogApiRef,
@@ -60,7 +61,15 @@ const entities: Entity[] = [
const apis = TestApiRegistry.from([
catalogApiRef,
{
getEntities: jest.fn().mockResolvedValue({ items: entities }),
getEntityFacets: jest.fn().mockResolvedValue({
facets: {
kind: [
{ value: 'Component', count: 2 },
{ value: 'Template', count: 1 },
{ value: 'System', count: 1 },
],
},
} as GetEntityFacetsResponse),
},
]);
@@ -17,7 +17,6 @@
import React from 'react';
import { fireEvent } from '@testing-library/react';
import { capitalize } from 'lodash';
import { CatalogApi } from '@backstage/catalog-client';
import { Entity } from '@backstage/catalog-model';
import { TemplateTypePicker } from './TemplateTypePicker';
import {
@@ -28,6 +27,7 @@ import {
import { AlertApi, alertApiRef } from '@backstage/core-plugin-api';
import { ApiProvider } from '@backstage/core-app-api';
import { renderWithEffects, TestApiRegistry } from '@backstage/test-utils';
import { GetEntityFacetsResponse } from '@backstage/catalog-client';
const entities: Entity[] = [
{
@@ -66,10 +66,15 @@ const apis = TestApiRegistry.from(
[
catalogApiRef,
{
getEntities: jest
.fn()
.mockImplementation(() => Promise.resolve({ items: entities })),
} as unknown as CatalogApi,
getEntityFacets: jest.fn().mockResolvedValue({
facets: {
'spec.type': entities.map(e => ({
value: (e.spec as any).type,
count: 1,
})),
},
} as GetEntityFacetsResponse),
},
],
[
alertApiRef,