Added EntityOrderFilter to sort entities by different fields

Signed-off-by: Mahendra Patel <mahendramp197@gmail.com>
This commit is contained in:
Mahendra Patel
2025-03-20 03:16:34 +05:30
parent 28c6806a92
commit d47aaa3250
8 changed files with 98 additions and 2 deletions
+20
View File
@@ -0,0 +1,20 @@
---
'@backstage/plugin-catalog-react': minor
---
Added EntityOrderFilter to sort entities by different fields/columns. This new filter allows users to specify the order in which entities are displayed in the catalog.
Example usage:
```ts
import { EntityOrderFilter } from '@backstage/plugin-catalog-react';
updateFilters({
order: new EntityOrderFilter([
{
field: 'metadata.name',
order: 'desc',
},
]),
});
```
@@ -60,6 +60,12 @@ const mockUser: UserEntity = {
};
const ownershipEntityRefs = ['user:default/testuser'];
const orderFields = [
{
field: 'metadata.name',
order: 'asc',
},
];
const mockConfigApi = mockApis.config({
data: { organization: { name: 'Test Company' } },
@@ -190,6 +196,7 @@ describe('<UserListPicker />', () => {
'metadata.namespace': ['default'],
},
limit: 0,
orderFields,
});
expect(mockCatalogApi.queryEntities).toHaveBeenCalledWith({
filter: {
@@ -197,6 +204,7 @@ describe('<UserListPicker />', () => {
'relations.ownedBy': ['user:default/testuser'],
},
limit: 0,
orderFields,
});
expect(mockCatalogApi.queryEntities).toHaveBeenCalledWith({
filter: {
@@ -204,6 +212,7 @@ describe('<UserListPicker />', () => {
'metadata.name': ['e-1', 'e-2'],
},
limit: 1000,
orderFields,
});
});
@@ -229,10 +238,12 @@ describe('<UserListPicker />', () => {
expect(mockCatalogApi.queryEntities).toHaveBeenCalledWith({
filter: { 'metadata.tags': ['tag1'] },
limit: 0,
orderFields,
});
expect(mockCatalogApi.queryEntities).toHaveBeenCalledWith({
filter: { 'metadata.name': ['e-1', 'e-2'], 'metadata.tags': ['tag1'] },
limit: 1000,
orderFields,
});
expect(mockCatalogApi.queryEntities).toHaveBeenCalledWith({
filter: {
@@ -240,6 +251,7 @@ describe('<UserListPicker />', () => {
'metadata.tags': ['tag1'],
},
limit: 0,
orderFields,
});
});
@@ -272,10 +284,12 @@ describe('<UserListPicker />', () => {
expect(mockCatalogApi.queryEntities).toHaveBeenCalledWith({
filter: { kind: 'component' },
limit: 0,
orderFields,
});
expect(mockCatalogApi.queryEntities).toHaveBeenCalledWith({
filter: { kind: 'component', 'metadata.name': ['e-1', 'e-2'] },
limit: 1000,
orderFields,
});
expect(mockCatalogApi.queryEntities).toHaveBeenCalledWith({
filter: {
@@ -283,6 +297,7 @@ describe('<UserListPicker />', () => {
'relations.ownedBy': ['user:default/testuser'],
},
limit: 0,
orderFields,
});
});
@@ -308,10 +323,12 @@ describe('<UserListPicker />', () => {
expect(mockCatalogApi.queryEntities).toHaveBeenCalledWith({
filter: { kind: 'component', 'metadata.name': ['e-1', 'e-2'] },
limit: 1000,
orderFields,
});
expect(mockCatalogApi.queryEntities).toHaveBeenCalledWith({
filter: { kind: 'component' },
limit: 0,
orderFields,
});
expect(mockCatalogApi.queryEntities).toHaveBeenCalledWith({
filter: {
@@ -319,6 +336,7 @@ describe('<UserListPicker />', () => {
'relations.ownedBy': ['user:default/testuser'],
},
limit: 0,
orderFields,
});
});
@@ -451,6 +469,7 @@ describe('<UserListPicker />', () => {
expect(mockCatalogApi.queryEntities).toHaveBeenCalledWith({
filter: { 'metadata.name': ['e-1', 'e-2'] },
limit: 1000,
orderFields,
});
});
expect(updateFilters).not.toHaveBeenCalledWith({
@@ -624,6 +643,7 @@ describe('<UserListPicker />', () => {
expect(mockCatalogApi.queryEntities).toHaveBeenCalledWith({
filter: { 'metadata.name': ['e-1', 'e-2'] },
limit: 1000,
orderFields,
});
});
expect(updateFilters).not.toHaveBeenCalledWith({
@@ -74,6 +74,12 @@ describe('useAllEntitiesCount', () => {
'relations.ownedBy': ['user:default/owner'],
},
limit: 0,
orderFields: [
{
field: 'metadata.name',
order: 'asc',
},
],
}),
);
expect(result.current).toEqual({ count: 10, loading: false });
@@ -115,6 +115,12 @@ describe('useOwnedEntitiesCount', () => {
'relations.ownedBy': ['user:default/spiderman', 'user:group/a-group'],
},
limit: 0,
orderFields: [
{
field: 'metadata.name',
order: 'asc',
},
],
}),
);
@@ -190,6 +196,12 @@ describe('useOwnedEntitiesCount', () => {
'relations.ownedBy': ['user:group/a-group'],
},
limit: 0,
orderFields: [
{
field: 'metadata.name',
order: 'asc',
},
],
}),
);
@@ -87,6 +87,12 @@ describe('useStarredEntitiesCount', () => {
'metadata.name': ['favourite1', 'favourite2'],
},
limit: 1000,
orderFields: [
{
field: 'metadata.name',
order: 'asc',
},
],
});
expect(result.current).toEqual({
count: 2,
+17
View File
@@ -23,6 +23,7 @@ import {
import { AlphaEntity } from '@backstage/catalog-model/alpha';
import { EntityFilter, UserListFilterKind } from './types';
import { getEntityRelations } from './utils/getEntityRelations';
import { EntityOrderQuery } from '@backstage/catalog-client';
/**
* Filter entities based on Kind.
@@ -331,3 +332,19 @@ export class EntityErrorFilter implements EntityFilter {
return error !== undefined && this.value === error;
}
}
/**
* Sort entities by a given field/column.
* @public
*/
export class EntityOrderFilter implements EntityFilter {
constructor(readonly value: EntityOrderQuery) {}
getOrderFilters(): EntityOrderQuery {
return this.value;
}
filterEntity(_: Entity): boolean {
return true;
}
}
@@ -35,6 +35,7 @@ import {
EntityKindFilter,
EntityLifecycleFilter,
EntityNamespaceFilter,
EntityOrderFilter,
EntityOrphanFilter,
EntityOwnerFilter,
EntityTagFilter,
@@ -64,6 +65,7 @@ export type DefaultEntityFilters = {
orphan?: EntityOrphanFilter;
error?: EntityErrorFilter;
namespace?: EntityNamespaceFilter;
order?: EntityOrderFilter;
};
/** @public */
@@ -287,7 +289,6 @@ export const EntityListProvider = <EntityFilters extends DefaultEntityFilters>(
...backendFilter,
limit,
offset,
orderFields: [{ field: 'metadata.name', order: 'asc' }],
});
setOutputState({
appliedFilters: requestedFilters,
+15 -1
View File
@@ -19,6 +19,7 @@ import { EntityFilter } from '../types';
import {
EntityLifecycleFilter,
EntityNamespaceFilter,
EntityOrderFilter,
EntityOrphanFilter,
EntityOwnerFilter,
EntityTagFilter,
@@ -26,18 +27,24 @@ import {
EntityUserFilter,
UserListFilter,
} from '../filters';
import { EntityOrderQuery } from '@backstage/catalog-client';
export interface CatalogFilters {
filter: Record<string, string | symbol | (string | symbol)[]>;
fullTextFilter?: {
term: string;
};
orderFields?: EntityOrderQuery;
}
function isEntityTextFilter(t: EntityFilter): t is EntityTextFilter {
return !!(t as EntityTextFilter).getFullTextFilters;
}
function isEntityOrderFilter(t: EntityFilter): t is EntityOrderFilter {
return !!(t as EntityOrderFilter).getOrderFilters;
}
export function reduceCatalogFilters(filters: EntityFilter[]): CatalogFilters {
const condensedFilters = filters.reduce<CatalogFilters['filter']>(
(compoundFilter, filter) => {
@@ -50,7 +57,14 @@ export function reduceCatalogFilters(filters: EntityFilter[]): CatalogFilters {
);
const fullTextFilter = filters.find(isEntityTextFilter)?.getFullTextFilters();
return { filter: condensedFilters, fullTextFilter };
const orderFields = filters.find(isEntityOrderFilter)?.getOrderFilters() || [
{
field: 'metadata.name',
order: 'asc',
},
];
return { filter: condensedFilters, fullTextFilter, orderFields };
}
/**