feat: add EntityFilterQuery to EntityPicker to allow filtering entities
Signed-off-by: Enrico Alvarenga <enrico.alvarenga@segment.com>
This commit is contained in:
@@ -14,6 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { EntityFilterQuery } from '@backstage/catalog-client';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { CatalogApi, catalogApiRef } from '@backstage/plugin-catalog-react';
|
||||
import { renderInTestApp, TestApiProvider } from '@backstage/test-utils';
|
||||
@@ -34,7 +35,13 @@ describe('<EntityPicker />', () => {
|
||||
const schema = {};
|
||||
const required = false;
|
||||
let uiSchema: {
|
||||
'ui:options': { allowedKinds?: string[]; defaultKind?: string };
|
||||
'ui:options': {
|
||||
allowedKinds?: string[];
|
||||
defaultKind?: string;
|
||||
allowArbitraryValues?: boolean;
|
||||
defaultNamespace?: string | false;
|
||||
catalogFilter?: EntityFilterQuery;
|
||||
};
|
||||
};
|
||||
const rawErrors: string[] = [];
|
||||
const formData = undefined;
|
||||
@@ -66,7 +73,7 @@ describe('<EntityPicker />', () => {
|
||||
|
||||
afterEach(() => jest.resetAllMocks());
|
||||
|
||||
describe('without allowedKinds', () => {
|
||||
describe('without allowedKinds and catalogFilter', () => {
|
||||
beforeEach(() => {
|
||||
uiSchema = { 'ui:options': {} };
|
||||
props = {
|
||||
@@ -136,4 +143,97 @@ describe('<EntityPicker />', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('with catalogFilter', () => {
|
||||
beforeEach(() => {
|
||||
uiSchema = {
|
||||
'ui:options': {
|
||||
catalogFilter: [
|
||||
{
|
||||
kind: ['Group'],
|
||||
'metadata.name': 'test-entity',
|
||||
},
|
||||
{
|
||||
kind: ['User'],
|
||||
'metadata.name': 'test-entity',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
props = {
|
||||
onChange,
|
||||
schema,
|
||||
required,
|
||||
uiSchema,
|
||||
rawErrors,
|
||||
formData,
|
||||
} as unknown as FieldProps<any>;
|
||||
|
||||
catalogApi.getEntities.mockResolvedValue({ items: entities });
|
||||
});
|
||||
|
||||
it('searches for a specific group entity', async () => {
|
||||
await renderInTestApp(
|
||||
<Wrapper>
|
||||
<EntityPicker {...props} />
|
||||
</Wrapper>,
|
||||
);
|
||||
|
||||
expect(catalogApi.getEntities).toHaveBeenCalledWith({
|
||||
filter: [
|
||||
{
|
||||
kind: ['Group'],
|
||||
'metadata.name': 'test-entity',
|
||||
},
|
||||
{
|
||||
kind: ['User'],
|
||||
'metadata.name': 'test-entity',
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('catalogFilter should take precedence over allowedKinds', () => {
|
||||
beforeEach(() => {
|
||||
uiSchema = {
|
||||
'ui:options': {
|
||||
catalogFilter: [
|
||||
{
|
||||
kind: ['Group'],
|
||||
'metadata.name': 'test-group',
|
||||
},
|
||||
],
|
||||
allowedKinds: ['User'],
|
||||
},
|
||||
};
|
||||
props = {
|
||||
onChange,
|
||||
schema,
|
||||
required,
|
||||
uiSchema,
|
||||
rawErrors,
|
||||
formData,
|
||||
} as unknown as FieldProps<any>;
|
||||
|
||||
catalogApi.getEntities.mockResolvedValue({ items: entities });
|
||||
});
|
||||
|
||||
it('searches for a Group entity', async () => {
|
||||
await renderInTestApp(
|
||||
<Wrapper>
|
||||
<EntityPicker {...props} />
|
||||
</Wrapper>,
|
||||
);
|
||||
|
||||
expect(catalogApi.getEntities).toHaveBeenCalledWith({
|
||||
filter: [
|
||||
{
|
||||
kind: ['Group'],
|
||||
'metadata.name': 'test-group',
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { type EntityFilterQuery } from '@backstage/catalog-client';
|
||||
import { useApi } from '@backstage/core-plugin-api';
|
||||
import {
|
||||
catalogApiRef,
|
||||
@@ -44,6 +45,11 @@ export const EntityPicker = (props: EntityPickerProps) => {
|
||||
idSchema,
|
||||
} = props;
|
||||
const allowedKinds = uiSchema['ui:options']?.allowedKinds;
|
||||
|
||||
const catalogFilter: EntityFilterQuery | undefined =
|
||||
uiSchema['ui:options']?.catalogFilter ||
|
||||
(allowedKinds && { kind: allowedKinds });
|
||||
|
||||
const defaultKind = uiSchema['ui:options']?.defaultKind;
|
||||
const defaultNamespace = uiSchema['ui:options']?.defaultNamespace;
|
||||
|
||||
@@ -51,7 +57,7 @@ export const EntityPicker = (props: EntityPickerProps) => {
|
||||
|
||||
const { value: entities, loading } = useAsync(() =>
|
||||
catalogApi.getEntities(
|
||||
allowedKinds ? { filter: { kind: allowedKinds } } : undefined,
|
||||
catalogFilter ? { filter: catalogFilter } : undefined,
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
@@ -16,6 +16,13 @@
|
||||
import { z } from 'zod';
|
||||
import { makeFieldSchemaFromZod } from '../utils';
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export const entityQueryFilterExpressionSchema = z.record(
|
||||
z.string().or(z.array(z.string())),
|
||||
);
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
@@ -42,6 +49,11 @@ export const EntityPickerFieldSchema = makeFieldSchemaFromZod(
|
||||
.describe(
|
||||
'The default namespace. Options with this namespace will not be prefixed.',
|
||||
),
|
||||
catalogFilter: z
|
||||
.array(entityQueryFilterExpressionSchema)
|
||||
.or(entityQueryFilterExpressionSchema)
|
||||
.optional()
|
||||
.describe('List of key-value filter expression for entities'),
|
||||
}),
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user