From 81c54d1f25255af82a4060d5d71ea61f7f51379d Mon Sep 17 00:00:00 2001 From: Will Sewell Date: Mon, 3 May 2021 12:07:48 +0100 Subject: [PATCH] Fetch useRelatedEntities in batches Rather than making a request per relation, filter multiple relations in a single request. To avoid the query string getting very large, make multiple requests on bacthes of 100 relations. Signed-off-by: Will Sewell --- .changeset/friendly-readers-promise.md | 5 ++++ .../src/hooks/useRelatedEntities.ts | 30 ++++++++++++++----- 2 files changed, 27 insertions(+), 8 deletions(-) create mode 100644 .changeset/friendly-readers-promise.md diff --git a/.changeset/friendly-readers-promise.md b/.changeset/friendly-readers-promise.md new file mode 100644 index 0000000000..30607d5db2 --- /dev/null +++ b/.changeset/friendly-readers-promise.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-react': patch +--- + +Fetch relations in batches in `useRelatedEntities` diff --git a/plugins/catalog-react/src/hooks/useRelatedEntities.ts b/plugins/catalog-react/src/hooks/useRelatedEntities.ts index baf6cf5fc7..15182d3c2f 100644 --- a/plugins/catalog-react/src/hooks/useRelatedEntities.ts +++ b/plugins/catalog-react/src/hooks/useRelatedEntities.ts @@ -18,6 +18,8 @@ import { useApi } from '@backstage/core'; import { useAsync } from 'react-use'; import { catalogApiRef } from '../api'; +const BATCH_SIZE = 100; + export function useRelatedEntities( entity: Entity, { type, kind }: { type?: string; kind?: string }, @@ -40,16 +42,28 @@ export function useRelatedEntities( return []; } - // TODO: This code could be more efficient if there was an endpoint in the - // backend that either returns the relations of entity (filtered by type) - // or if there is a way to perform a batch request by entity name. However, - // such an implementation would probably be better placed in the graphql API. + // Make requests in separate batches to limit query string size + // (there is a `filter` param for each relation) + const relationBatches = []; + for (let i = 0; i < relations.length; i += BATCH_SIZE) { + relationBatches.push(relations.slice(i, i + BATCH_SIZE)); + } + const results = await Promise.all( - relations?.map(r => catalogApi.getEntityByName(r.target)), + relationBatches.map(batch => { + return catalogApi.getEntities({ + filter: batch.map(({ target }) => { + return { + kind: target.kind, + 'metadata.name': target.name, + 'metadata.namespace': target.namespace, + }; + }), + }); + }), ); - // Skip entities that where not found, for example if a relation references - // an entity that doesn't exist. - return results.filter(e => e) as Entity[]; + + return results.map(r => r.items).flat(); }, [entity, type]); return {