diff --git a/.changeset/few-meals-buy.md b/.changeset/few-meals-buy.md new file mode 100644 index 0000000000..c49eb8c7ac --- /dev/null +++ b/.changeset/few-meals-buy.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend-module-aws': patch +--- + +Update to import `LocationSpec` from the correct package. diff --git a/.changeset/few-trees-cross.md b/.changeset/few-trees-cross.md new file mode 100644 index 0000000000..ed70c3bddb --- /dev/null +++ b/.changeset/few-trees-cross.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': patch +--- + +Fixed Webpack loader patterns so that they properly match the file suffix only. diff --git a/.changeset/three-geckos-press.md b/.changeset/three-geckos-press.md new file mode 100644 index 0000000000..fc1af4fdc0 --- /dev/null +++ b/.changeset/three-geckos-press.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-search': patch +--- + +Implement a `showCounts` option to display result counts per type in `SearchType.Accordion` diff --git a/packages/app/src/components/search/SearchPage.tsx b/packages/app/src/components/search/SearchPage.tsx index 8167889b02..6c4f06aed9 100644 --- a/packages/app/src/components/search/SearchPage.tsx +++ b/packages/app/src/components/search/SearchPage.tsx @@ -79,6 +79,7 @@ const SearchPage = () => { { ], }, { - test: /\.(js|mjs|cjs)/, + test: /\.(js|mjs|cjs)$/, resolve: { fullySpecified: false, }, @@ -141,9 +141,10 @@ export const transforms = (options: TransformOptions): Transforms => { /\.gif$/, /\.jpe?g$/, /\.png$/, - /\.frag/, - { and: [/\.svg/, { not: [/\.icon\.svg/] }] }, - /\.xml/, + /\.frag$/, + /\.vert$/, + { and: [/\.svg$/, { not: [/\.icon\.svg$/] }] }, + /\.xml$/, ], type: 'asset/resource', generator: { diff --git a/plugins/catalog-backend-module-aws/api-report.md b/plugins/catalog-backend-module-aws/api-report.md index 63b85f223b..1725692cb1 100644 --- a/plugins/catalog-backend-module-aws/api-report.md +++ b/plugins/catalog-backend-module-aws/api-report.md @@ -11,7 +11,7 @@ import { Config } from '@backstage/config'; import { Credentials } from 'aws-sdk'; import { EntityProvider } from '@backstage/plugin-catalog-backend'; import { EntityProviderConnection } from '@backstage/plugin-catalog-backend'; -import { LocationSpec } from '@backstage/plugin-catalog-backend'; +import { LocationSpec } from '@backstage/plugin-catalog-common'; import { Logger } from 'winston'; import { PluginTaskScheduler } from '@backstage/backend-tasks'; import { TaskRunner } from '@backstage/backend-tasks'; diff --git a/plugins/catalog-backend-module-aws/src/processors/AwsEKSClusterProcessor.test.ts b/plugins/catalog-backend-module-aws/src/processors/AwsEKSClusterProcessor.test.ts index 87b7f2c487..ecf3b1a961 100644 --- a/plugins/catalog-backend-module-aws/src/processors/AwsEKSClusterProcessor.test.ts +++ b/plugins/catalog-backend-module-aws/src/processors/AwsEKSClusterProcessor.test.ts @@ -20,10 +20,12 @@ import aws from 'aws-sdk'; describe('AwsEKSClusterProcessor', () => { AWSMock.setSDKInstance(aws); + describe('readLocation', () => { const processor = new (AwsEKSClusterProcessor as any)({}); const location = { type: 'aws-eks', target: '957140518395/us-west-2' }; const emit = jest.fn(); + it('generates cluster correctly', async () => { const clusters: aws.EKS.Types.ListClustersResponse = { clusters: ['backstage-test'], @@ -40,11 +42,12 @@ describe('AwsEKSClusterProcessor', () => { }, }, }; - AWSMock.mock('EKS', 'listClusters', clusters); + AWSMock.mock('EKS', 'listClusters', clusters); AWSMock.mock('EKS', 'describeCluster', cluster); await processor.readLocation(location, false, emit); + expect(emit).toHaveBeenCalledWith({ type: 'entity', location, diff --git a/plugins/catalog-backend-module-aws/src/processors/AwsEKSClusterProcessor.ts b/plugins/catalog-backend-module-aws/src/processors/AwsEKSClusterProcessor.ts index c34b247f4d..b6418ade03 100644 --- a/plugins/catalog-backend-module-aws/src/processors/AwsEKSClusterProcessor.ts +++ b/plugins/catalog-backend-module-aws/src/processors/AwsEKSClusterProcessor.ts @@ -13,16 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import { CatalogProcessor, CatalogProcessorEmit, - LocationSpec, } from '@backstage/plugin-catalog-backend'; +import { LocationSpec } from '@backstage/plugin-catalog-common'; import { ANNOTATION_KUBERNETES_API_SERVER, ANNOTATION_KUBERNETES_API_SERVER_CA, ANNOTATION_KUBERNETES_AUTH_PROVIDER, -} from '@backstage/catalog-model'; +} from '@backstage/plugin-kubernetes-common'; import { Credentials, EKS } from 'aws-sdk'; import { AWSCredentialFactory } from '../types'; diff --git a/plugins/catalog-backend-module-aws/src/processors/AwsOrganizationCloudAccountProcessor.ts b/plugins/catalog-backend-module-aws/src/processors/AwsOrganizationCloudAccountProcessor.ts index 1018e49a41..ecc42e69ea 100644 --- a/plugins/catalog-backend-module-aws/src/processors/AwsOrganizationCloudAccountProcessor.ts +++ b/plugins/catalog-backend-module-aws/src/processors/AwsOrganizationCloudAccountProcessor.ts @@ -19,9 +19,9 @@ import { Config } from '@backstage/config'; import { CatalogProcessor, CatalogProcessorEmit, - LocationSpec, processingResult, } from '@backstage/plugin-catalog-backend'; +import { LocationSpec } from '@backstage/plugin-catalog-common'; import AWS, { Credentials, Organizations } from 'aws-sdk'; import { Account, ListAccountsResponse } from 'aws-sdk/clients/organizations'; import { Logger } from 'winston'; diff --git a/plugins/catalog-backend-module-aws/src/processors/AwsS3DiscoveryProcessor.ts b/plugins/catalog-backend-module-aws/src/processors/AwsS3DiscoveryProcessor.ts index 84f2289c94..072c3d8384 100644 --- a/plugins/catalog-backend-module-aws/src/processors/AwsS3DiscoveryProcessor.ts +++ b/plugins/catalog-backend-module-aws/src/processors/AwsS3DiscoveryProcessor.ts @@ -20,9 +20,9 @@ import { CatalogProcessor, CatalogProcessorEmit, CatalogProcessorParser, - LocationSpec, processingResult, } from '@backstage/plugin-catalog-backend'; +import { LocationSpec } from '@backstage/plugin-catalog-common'; import limiterFactory from 'p-limit'; /** diff --git a/plugins/catalog-backend-module-aws/src/providers/AwsS3EntityProvider.ts b/plugins/catalog-backend-module-aws/src/providers/AwsS3EntityProvider.ts index b92fcfc022..5e24d4dc13 100644 --- a/plugins/catalog-backend-module-aws/src/providers/AwsS3EntityProvider.ts +++ b/plugins/catalog-backend-module-aws/src/providers/AwsS3EntityProvider.ts @@ -20,9 +20,9 @@ import { AwsS3Integration, ScmIntegrations } from '@backstage/integration'; import { EntityProvider, EntityProviderConnection, - LocationSpec, locationSpecToLocationEntity, } from '@backstage/plugin-catalog-backend'; +import { LocationSpec } from '@backstage/plugin-catalog-common'; import { AwsCredentials } from '../credentials/AwsCredentials'; import { readAwsS3Configs } from './config'; import { AwsS3Config } from './types'; diff --git a/plugins/search/api-report.md b/plugins/search/api-report.md index cdbdc4c095..09c12cd776 100644 --- a/plugins/search/api-report.md +++ b/plugins/search/api-report.md @@ -97,6 +97,7 @@ export type SearchTypeAccordionProps = { icon: JSX.Element; }>; defaultValue?: string; + showCounts?: boolean; }; // @public diff --git a/plugins/search/src/components/SearchType/SearchType.Accordion.test.tsx b/plugins/search/src/components/SearchType/SearchType.Accordion.test.tsx index da1cae46b9..e0acee025a 100644 --- a/plugins/search/src/components/SearchType/SearchType.Accordion.test.tsx +++ b/plugins/search/src/components/SearchType/SearchType.Accordion.test.tsx @@ -16,7 +16,7 @@ import React from 'react'; import { TestApiProvider } from '@backstage/test-utils'; -import { act, render } from '@testing-library/react'; +import { act, render, waitFor } from '@testing-library/react'; import user from '@testing-library/user-event'; import { searchApiRef, @@ -34,6 +34,8 @@ jest.mock('@backstage/plugin-search-react', () => ({ setTypes: (types: any) => setTypesMock(types), pageCursor: '', setPageCursor: (pageCursor: any) => setPageCursorMock(pageCursor), + term: 'abc', + filters: { foo: 'bar' }, }), })); @@ -48,7 +50,7 @@ describe('SearchType.Accordion', () => { }; beforeEach(() => { - query.mockResolvedValue({ results: [] }); + query.mockResolvedValue({ results: [], numberOfResults: 1234 }); }); const Wrapper = ({ children }: { children: React.ReactNode }) => { @@ -132,4 +134,34 @@ describe('SearchType.Accordion', () => { expect(queryByText('Collapse')).not.toBeInTheDocument(); }); + + it('should show result counts if enabled', async () => { + const { getAllByText } = render( + + + , + ); + + expect(query).toHaveBeenCalledWith({ + term: 'abc', + types: [], + filters: { foo: 'bar' }, + pageLimit: 0, + }); + expect(query).toHaveBeenCalledWith({ + term: 'abc', + types: [expectedType.value], + filters: {}, + pageLimit: 0, + }); + await waitFor(() => { + const countLabels = getAllByText('1234 results'); + expect(countLabels.length).toEqual(2); + expect(countLabels[0]).toBeInTheDocument(); + }); + }); }); diff --git a/plugins/search/src/components/SearchType/SearchType.Accordion.tsx b/plugins/search/src/components/SearchType/SearchType.Accordion.tsx index 49f5cf74ca..67785b89cb 100644 --- a/plugins/search/src/components/SearchType/SearchType.Accordion.tsx +++ b/plugins/search/src/components/SearchType/SearchType.Accordion.tsx @@ -15,7 +15,8 @@ */ import React, { cloneElement, Fragment, useEffect, useState } from 'react'; -import { useSearch } from '@backstage/plugin-search-react'; +import { useApi } from '@backstage/core-plugin-api'; +import { searchApiRef, useSearch } from '@backstage/plugin-search-react'; import { Accordion, AccordionSummary, @@ -32,6 +33,7 @@ import { } from '@material-ui/core'; import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; import AllIcon from '@material-ui/icons/FontDownload'; +import useAsync from 'react-use/lib/useAsync'; const useStyles = makeStyles(theme => ({ card: { @@ -81,13 +83,15 @@ export type SearchTypeAccordionProps = { icon: JSX.Element; }>; defaultValue?: string; + showCounts?: boolean; }; export const SearchTypeAccordion = (props: SearchTypeAccordionProps) => { const classes = useStyles(); - const { setPageCursor, setTypes, types } = useSearch(); + const { filters, setPageCursor, setTypes, term, types } = useSearch(); + const searchApi = useApi(searchApiRef); const [expanded, setExpanded] = useState(true); - const { defaultValue, name, types: givenTypes } = props; + const { defaultValue, name, showCounts, types: givenTypes } = props; const toggleExpanded = () => setExpanded(prevState => !prevState); const handleClick = (type: string) => { @@ -116,6 +120,37 @@ export const SearchTypeAccordion = (props: SearchTypeAccordionProps) => { ]; const selected = types[0] || ''; + const { value: resultCounts } = useAsync(async () => { + if (!showCounts) { + return {}; + } + + const counts = await Promise.all( + definedTypes + .map(t => t.value) + .map(async type => { + const { numberOfResults } = await searchApi.query({ + term, + types: type ? [type] : [], + filters: + types.includes(type) || (!types.length && !type) ? filters : {}, + pageLimit: 0, + }); + + return [ + type, + numberOfResults !== undefined + ? `${ + numberOfResults >= 10000 ? `>10000` : numberOfResults + } results` + : ' -- ', + ]; + }), + ); + + return Object.fromEntries(counts); + }, [filters, showCounts, term, types]); + return ( @@ -161,7 +196,10 @@ export const SearchTypeAccordion = (props: SearchTypeAccordionProps) => { className: classes.listItemIcon, })} - + ))}