diff --git a/plugins/scaffolder/src/components/fields/EntityPicker/EntityPicker.test.tsx b/plugins/scaffolder/src/components/fields/EntityPicker/EntityPicker.test.tsx
index 942eef9d82..b6d6688a09 100644
--- a/plugins/scaffolder/src/components/fields/EntityPicker/EntityPicker.test.tsx
+++ b/plugins/scaffolder/src/components/fields/EntityPicker/EntityPicker.test.tsx
@@ -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('', () => {
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('', () => {
afterEach(() => jest.resetAllMocks());
- describe('without allowedKinds', () => {
+ describe('without allowedKinds and catalogFilter', () => {
beforeEach(() => {
uiSchema = { 'ui:options': {} };
props = {
@@ -136,4 +143,97 @@ describe('', () => {
});
});
});
+
+ 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;
+
+ catalogApi.getEntities.mockResolvedValue({ items: entities });
+ });
+
+ it('searches for a specific group entity', async () => {
+ await renderInTestApp(
+
+
+ ,
+ );
+
+ 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;
+
+ catalogApi.getEntities.mockResolvedValue({ items: entities });
+ });
+
+ it('searches for a Group entity', async () => {
+ await renderInTestApp(
+
+
+ ,
+ );
+
+ expect(catalogApi.getEntities).toHaveBeenCalledWith({
+ filter: [
+ {
+ kind: ['Group'],
+ 'metadata.name': 'test-group',
+ },
+ ],
+ });
+ });
+ });
});
diff --git a/plugins/scaffolder/src/components/fields/EntityPicker/EntityPicker.tsx b/plugins/scaffolder/src/components/fields/EntityPicker/EntityPicker.tsx
index bc78e00576..48e60b60ad 100644
--- a/plugins/scaffolder/src/components/fields/EntityPicker/EntityPicker.tsx
+++ b/plugins/scaffolder/src/components/fields/EntityPicker/EntityPicker.tsx
@@ -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,
),
);
diff --git a/plugins/scaffolder/src/components/fields/EntityPicker/schema.ts b/plugins/scaffolder/src/components/fields/EntityPicker/schema.ts
index 9236457fc0..38fc0c9dc2 100644
--- a/plugins/scaffolder/src/components/fields/EntityPicker/schema.ts
+++ b/plugins/scaffolder/src/components/fields/EntityPicker/schema.ts
@@ -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'),
}),
);