From ddad61d824d2869141ebbdc4638ac4f7163ae461 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Wed, 26 Aug 2020 11:42:09 +0200 Subject: [PATCH] reload the filtered entities context instead of direct fetch --- app-config.yaml | 22 ++++--- .../components/CatalogPage/CatalogPage.tsx | 28 ++++++++- .../CatalogTable/CatalogTable.test.tsx | 40 +++++-------- .../components/CatalogTable/CatalogTable.tsx | 57 +++++-------------- .../src/filter/EntityFilterGroupsProvider.tsx | 17 +++++- plugins/catalog/src/filter/context.ts | 1 + .../catalog/src/filter/useFilteredEntities.ts | 1 + 7 files changed, 79 insertions(+), 87 deletions(-) diff --git a/app-config.yaml b/app-config.yaml index 361ba8e7c8..6202d48560 100644 --- a/app-config.yaml +++ b/app-config.yaml @@ -51,18 +51,16 @@ newrelic: key: NEW_RELIC_REST_API_KEY catalog: - entities: - [ - 'artist-lookup-component.yaml', - 'playback-order-component.yaml', - 'podcast-api-component.yaml', - 'queue-proxy-component.yaml', - 'searcher-component.yaml', - 'playback-lib-component.yaml', - 'www-artist-component.yaml', - 'shuffle-api-component.yaml', - ] - baseUrl: 'https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples' + exampleEntityLocations: + github: + - https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/artist-lookup-component.yaml + - https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/playback-order-component.yaml + - https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/podcast-api-component.yaml + - https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/queue-proxy-component.yaml + - https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/searcher-component.yaml + - https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/playback-lib-component.yaml + - https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/www-artist-component.yaml + - https://github.com/spotify/backstage/blob/master/packages/catalog-model/examples/shuffle-api-component.yaml auth: providers: diff --git a/plugins/catalog/src/components/CatalogPage/CatalogPage.tsx b/plugins/catalog/src/components/CatalogPage/CatalogPage.tsx index a73fc5df96..db490cf542 100644 --- a/plugins/catalog/src/components/CatalogPage/CatalogPage.tsx +++ b/plugins/catalog/src/components/CatalogPage/CatalogPage.tsx @@ -18,6 +18,7 @@ import { configApiRef, Content, ContentHeader, + errorApiRef, identityApiRef, SupportButton, useApi, @@ -26,8 +27,9 @@ import { rootRoute as scaffolderRootRoute } from '@backstage/plugin-scaffolder'; import { Button, makeStyles } from '@material-ui/core'; import SettingsIcon from '@material-ui/icons/Settings'; import StarIcon from '@material-ui/icons/Star'; -import React, { useMemo, useState } from 'react'; +import React, { useCallback, useMemo, useState } from 'react'; import { Link as RouterLink } from 'react-router-dom'; +import { catalogApiRef } from '../../api/types'; import { EntityFilterGroupsProvider, useFilteredEntities } from '../../filter'; import { useStarredEntities } from '../../hooks/useStarredEntites'; import { ButtonGroup, CatalogFilter } from '../CatalogFilter/CatalogFilter'; @@ -51,15 +53,34 @@ const CatalogPageContents = () => { const { loading, error, + reload, matchingEntities, availableTags, } = useFilteredEntities(); + const configApi = useApi(configApiRef); + const catalogApi = useApi(catalogApiRef); + const errorApi = useApi(errorApiRef); const { isStarredEntity } = useStarredEntities(); const userId = useApi(identityApiRef).getUserId(); const [selectedTab, setSelectedTab] = useState(); const [selectedSidebarItem, setSelectedSidebarItem] = useState(); - const orgName = - useApi(configApiRef).getOptionalString('organization.name') ?? 'Company'; + const orgName = configApi.getOptionalString('organization.name') ?? 'Company'; + + const addMockData = useCallback(async () => { + try { + const promises: Promise[] = []; + const root = configApi.getConfig('catalog.exampleEntityLocations'); + for (const type of root.keys()) { + for (const target of root.getStringArray(type)) { + promises.push(catalogApi.addLocation(type, target)); + } + } + await Promise.all(promises); + await reload(); + } catch (err) { + errorApi.post(err); + } + }, [catalogApi, configApi, errorApi, reload]); const tabs = useMemo( () => [ @@ -153,6 +174,7 @@ const CatalogPageContents = () => { entities={matchingEntities} loading={loading} error={error} + onAddMockData={addMockData} /> diff --git a/plugins/catalog/src/components/CatalogTable/CatalogTable.test.tsx b/plugins/catalog/src/components/CatalogTable/CatalogTable.test.tsx index 99f251664d..d1eef2a005 100644 --- a/plugins/catalog/src/components/CatalogTable/CatalogTable.test.tsx +++ b/plugins/catalog/src/components/CatalogTable/CatalogTable.test.tsx @@ -15,15 +15,8 @@ */ import { Entity } from '@backstage/catalog-model'; -import { - ApiProvider, - ApiRegistry, - ConfigApi, - configApiRef, -} from '@backstage/core'; import { renderWithEffects, wrapInTestApp } from '@backstage/test-utils'; import * as React from 'react'; -import { CatalogApi, catalogApiRef } from '../../api/types'; import { CatalogTable } from './CatalogTable'; const entities: Entity[] = [ @@ -45,22 +38,16 @@ const entities: Entity[] = [ ]; describe('CatalogTable component', () => { - const apis = ApiRegistry.from([ - [configApiRef, ({} as Partial) as ConfigApi], - [catalogApiRef, ({} as Partial) as CatalogApi], - ]); - it('should render error message when error is passed in props', async () => { const rendered = await renderWithEffects( wrapInTestApp( - - - , + {}} + />, ), ); const errorMessage = await rendered.findByText( @@ -72,13 +59,12 @@ describe('CatalogTable component', () => { it('should display entity names when loading has finished and no error occurred', async () => { const rendered = await renderWithEffects( wrapInTestApp( - - - , + {}} + />, ), ); expect(rendered.getByText(/Owned \(3\)/)).toBeInTheDocument(); diff --git a/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx b/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx index 4cee1a5889..c68eb10e66 100644 --- a/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx +++ b/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx @@ -14,21 +14,14 @@ * limitations under the License. */ import { Entity, LocationSpec } from '@backstage/catalog-model'; -import { - configApiRef, - Table, - TableColumn, - TableProps, - useApi, -} from '@backstage/core'; +import { Table, TableColumn, TableProps } from '@backstage/core'; import { Chip, Link } from '@material-ui/core'; import Add from '@material-ui/icons/Add'; import Edit from '@material-ui/icons/Edit'; import GitHub from '@material-ui/icons/GitHub'; import { Alert } from '@material-ui/lab'; -import React, { useEffect, useState } from 'react'; +import React, { Dispatch } from 'react'; import { generatePath, Link as RouterLink } from 'react-router-dom'; -import { catalogApiRef } from '../../api/types'; import { findLocationForEntityMeta } from '../../data/utils'; import { useStarredEntities } from '../../hooks/useStarredEntites'; import { entityRoute } from '../../routes'; @@ -82,7 +75,12 @@ const columns: TableColumn[] = [ <> {entity.metadata.tags && entity.metadata.tags.map(t => ( - + ))} ), @@ -94,6 +92,7 @@ type CatalogTableProps = { titlePreamble: string; loading: boolean; error?: any; + onAddMockData: Dispatch; }; export const CatalogTable = ({ @@ -101,46 +100,20 @@ export const CatalogTable = ({ loading, error, titlePreamble, + onAddMockData, }: CatalogTableProps) => { const { isStarredEntity, toggleStarredEntity } = useStarredEntities(); - const configApi = useApi(configApiRef); - const catalogApi = useApi(catalogApiRef); - const [entitiesState, setEntitiesState] = useState([]); - const [errorState, setError] = useState(); - - useEffect(() => { - setError(error); - setEntitiesState(entities); - }, [error, entities]); - if (errorState) { + if (error) { return (
- Error encountered while fetching catalog entities.{' '} - {errorState.toString()} + Error encountered while fetching catalog entities. {error.toString()}
); } - const addMockData = async () => { - try { - const _promises = configApi - .getStringArray('catalog.entities') - .map(file => - catalogApi.addLocation( - 'github', - `${configApi.getString('catalog.baseUrl')}/${file}`, - ), - ); - await Promise.all(_promises); - const data: Entity[] = await catalogApi.getEntities(); - setEntitiesState(data); - } catch (err) { - setError(err); - } - }; const actions: TableProps['actions'] = [ (rowData: Entity) => { const location = findLocationForEntityMeta(rowData.metadata); @@ -187,8 +160,8 @@ export const CatalogTable = ({ icon: () => , tooltip: 'Add example components', isFreeAction: true, - onClick: () => addMockData(), - hidden: entitiesState && entitiesState.length > 0, + onClick: onAddMockData, + hidden: !(entities && entities.length === 0), }, ]; @@ -203,7 +176,7 @@ export const CatalogTable = ({ showEmptyDataSourceMessage: !loading, }} title={`${titlePreamble} (${(entities && entities.length) || 0})`} - data={entitiesState} + data={entities} actions={actions} /> ); diff --git a/plugins/catalog/src/filter/EntityFilterGroupsProvider.tsx b/plugins/catalog/src/filter/EntityFilterGroupsProvider.tsx index db8e21e071..94bc428a45 100644 --- a/plugins/catalog/src/filter/EntityFilterGroupsProvider.tsx +++ b/plugins/catalog/src/filter/EntityFilterGroupsProvider.tsx @@ -16,8 +16,8 @@ import { Entity } from '@backstage/catalog-model'; import { useApi } from '@backstage/core'; -import React, { useCallback, useRef, useState } from 'react'; -import { useAsync } from 'react-use'; +import React, { useCallback, useEffect, useRef, useState } from 'react'; +import { useAsyncFn } from 'react-use'; import { catalogApiRef } from '../api/types'; import { filterGroupsContext, FilterGroupsContext } from './context'; import { @@ -46,7 +46,9 @@ export const EntityFilterGroupsProvider = ({ // The hook that implements the actual context building function useProvideEntityFilters(): FilterGroupsContext { const catalogApi = useApi(catalogApiRef); - const { value: entities, error } = useAsync(() => catalogApi.getEntities()); + const [{ value: entities, error }, doReload] = useAsyncFn(() => + catalogApi.getEntities(), + ); const filterGroups = useRef<{ [filterGroupId: string]: FilterGroup; @@ -61,6 +63,10 @@ function useProvideEntityFilters(): FilterGroupsContext { const [matchingEntities, setMatchingEntities] = useState([]); const [availableTags, setAvailableTags] = useState([]); + useEffect(() => { + doReload(); + }, [doReload]); + const rebuild = useCallback(() => { setFilterGroupStates( buildStates( @@ -122,11 +128,16 @@ function useProvideEntityFilters(): FilterGroupsContext { [rebuild], ); + const reload = useCallback(async () => { + await doReload(); + }, [doReload]); + return { register, unregister, setGroupSelectedFilters, setSelectedTags, + reload, loading: !error && !entities, error, filterGroupStates, diff --git a/plugins/catalog/src/filter/context.ts b/plugins/catalog/src/filter/context.ts index 7f16107f09..838ec8d532 100644 --- a/plugins/catalog/src/filter/context.ts +++ b/plugins/catalog/src/filter/context.ts @@ -27,6 +27,7 @@ export type FilterGroupsContext = { unregister: (filterGroupId: string) => void; setGroupSelectedFilters: (filterGroupId: string, filterIds: string[]) => void; setSelectedTags: (tags: string[]) => void; + reload: () => Promise; loading: boolean; error?: Error; filterGroupStates: { [filterGroupId: string]: FilterGroupStates }; diff --git a/plugins/catalog/src/filter/useFilteredEntities.ts b/plugins/catalog/src/filter/useFilteredEntities.ts index 3f70c0bd85..138a7547c0 100644 --- a/plugins/catalog/src/filter/useFilteredEntities.ts +++ b/plugins/catalog/src/filter/useFilteredEntities.ts @@ -31,5 +31,6 @@ export function useFilteredEntities() { error: context.error, matchingEntities: context.matchingEntities, availableTags: context.availableTags, + reload: context.reload, }; }