From e1477fa635127efa6790827a8e8bb8480721c1e2 Mon Sep 17 00:00:00 2001 From: Nikita Nek Dudnik Date: Fri, 5 Jun 2020 11:36:52 +0200 Subject: [PATCH] feature: extract getLocationById method, wrap location fetching in useAsync --- plugins/catalog/src/api/CatalogClient.ts | 26 ++++++--- plugins/catalog/src/api/types.ts | 2 + .../components/CatalogPage/CatalogPage.tsx | 56 +++++++++---------- .../ComponentRemovalDialog.tsx | 3 + plugins/catalog/src/data/utils.ts | 14 ++++- 5 files changed, 59 insertions(+), 42 deletions(-) diff --git a/plugins/catalog/src/api/CatalogClient.ts b/plugins/catalog/src/api/CatalogClient.ts index abba8a7523..804f2b3b6a 100644 --- a/plugins/catalog/src/api/CatalogClient.ts +++ b/plugins/catalog/src/api/CatalogClient.ts @@ -31,6 +31,22 @@ export class CatalogClient implements CatalogApi { this.apiOrigin = apiOrigin; this.basePath = basePath; } + async getLocationById(id: String): Promise { + const response = await fetch( + `${this.apiOrigin}${this.basePath}/locations/${id}`, + ); + if (response.ok) { + const location = await response.json(); + if (location) return location.data; + } + return undefined; + } + async getEntitiesByLocationId(id: string): Promise { + const response = await fetch( + `${this.apiOrigin}${this.basePath}/entities?backstage.io/managed-by-location=${id}`, + ); + return await response.json(); + } async getEntities(): Promise { const response = await fetch(`${this.apiOrigin}${this.basePath}/entities`); return await response.json(); @@ -50,14 +66,8 @@ export class CatalogClient implements CatalogApi { const locationId = findLocationIdInEntity(entity); if (!locationId) return undefined; - const response = await fetch( - `${this.apiOrigin}${this.basePath}/locations/${locationId}`, - ); - if (response.ok) { - const location = await response.json(); - if (location) return location.data; - } + const location = this.getLocationById(locationId); - return undefined; + return location; } } diff --git a/plugins/catalog/src/api/types.ts b/plugins/catalog/src/api/types.ts index 9e8dc5fbac..74184b8081 100644 --- a/plugins/catalog/src/api/types.ts +++ b/plugins/catalog/src/api/types.ts @@ -23,7 +23,9 @@ export const catalogApiRef = createApiRef({ }); export interface CatalogApi { + getLocationById(id: String): Promise; getEntities(): Promise; getEntityByName(name: string): Promise; + getEntitiesByLocationId(id: string): Promise; getLocationByEntity(entity: Entity): Promise; } diff --git a/plugins/catalog/src/components/CatalogPage/CatalogPage.tsx b/plugins/catalog/src/components/CatalogPage/CatalogPage.tsx index 25d2618a9a..1597ae2135 100644 --- a/plugins/catalog/src/components/CatalogPage/CatalogPage.tsx +++ b/plugins/catalog/src/components/CatalogPage/CatalogPage.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import React, { FC, useCallback, useState, useEffect } from 'react'; +import React, { FC, useCallback, useState } from 'react'; import { Content, ContentHeader, @@ -47,13 +47,12 @@ const useStyles = makeStyles(theme => ({ })); import { catalogApiRef } from '../..'; -import { envelopeToComponent } from '../../data/utils'; +import { envelopeToComponent, findLocationForEntity } from '../../data/utils'; import { Component } from '../../data/component'; const CatalogPage: FC<{}> = () => { const catalogApi = useApi(catalogApiRef); const { value, error, loading } = useAsync(() => catalogApi.getEntities()); - const [locations, setLocations] = useState([]); const [selectedFilter, setSelectedFilter] = useState( defaultFilter, ); @@ -65,7 +64,7 @@ const CatalogPage: FC<{}> = () => { ); const styles = useStyles(); - useEffect(() => { + const { value: locations = [] } = useAsync(async () => { const getLocationDataForEntities = async (entities: Entity[]) => { return Promise.all( entities.map(entity => catalogApi.getLocationByEntity(entity)), @@ -76,12 +75,14 @@ const CatalogPage: FC<{}> = () => { getLocationDataForEntities(value) .then( (location): Location[] => - location.filter(l => !!l) as Array, + location.filter(loc => !!loc) as Array, ) .then(location => { - if (isMounted()) setLocations(location); + if (isMounted()) return [location]; + return []; }); } + return []; }, [value, catalogApi, isMounted]); const actions = [ @@ -97,15 +98,6 @@ const CatalogPage: FC<{}> = () => { }), ]; - const findLocationForEntity = ( - entity: Entity, - l: Location[], - ): Location | undefined => { - const entityLocationId = - entity.metadata.annotations?.['backstage.io/managed-by-location']; - return l.find(location => location.id === entityLocationId); - }; - return (
@@ -142,22 +134,24 @@ const CatalogPage: FC<{}> = () => { onSelectedChange={onFilterSelected} /> - - envelopeToComponent( - val, - findLocationForEntity(val, locations), - ), - )) || - [] - } - loading={loading} - error={error} - actions={actions} - /> + {locations && ( + + envelopeToComponent( + val, + findLocationForEntity(val, locations) ?? undefined, + ), + )) || + [] + } + loading={loading} + error={error} + actions={actions} + /> + )} diff --git a/plugins/catalog/src/components/ComponentRemovalDialog/ComponentRemovalDialog.tsx b/plugins/catalog/src/components/ComponentRemovalDialog/ComponentRemovalDialog.tsx index b5a863cef8..a25e761dbb 100644 --- a/plugins/catalog/src/components/ComponentRemovalDialog/ComponentRemovalDialog.tsx +++ b/plugins/catalog/src/components/ComponentRemovalDialog/ComponentRemovalDialog.tsx @@ -25,6 +25,9 @@ import { useTheme, } from '@material-ui/core'; import { Component } from '../../data/component'; +import { useAsync } from 'react-use'; +import { useApi, Progress } from '@backstage/core'; +import { catalogApiRef } from '../../api/types'; type ComponentRemovalDialogProps = { onConfirm: () => any; diff --git a/plugins/catalog/src/data/utils.ts b/plugins/catalog/src/data/utils.ts index fe2ec62493..a3ce0c43ee 100644 --- a/plugins/catalog/src/data/utils.ts +++ b/plugins/catalog/src/data/utils.ts @@ -16,14 +16,22 @@ import { Component } from './component'; import { Entity, Location } from '@backstage/catalog-model'; -export function envelopeToComponent( +export const envelopeToComponent = ( envelope: Entity, location?: Location, -): Component { +): Component => { return { name: envelope.metadata?.name ?? '', kind: envelope.kind ?? 'unknown', description: envelope.metadata?.annotations?.description ?? 'placeholder', location, }; -} +}; +export const findLocationForEntity = ( + entity: Entity, + l: Location[], +): Location | undefined => { + const entityLocationId = + entity.metadata.annotations?.['backstage.io/managed-by-location']; + return l.find(location => location.id === entityLocationId); +};