Merge pull request #29310 from mahendra8956/entity-order-filter

Added EntityOrderFilter to sort entities by different fields
This commit is contained in:
Vincenzo Scamporlino
2025-04-16 21:32:57 +02:00
committed by GitHub
8 changed files with 76 additions and 5 deletions
+26
View File
@@ -0,0 +1,26 @@
---
'@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,
useEntityList,
} from '@backstage/plugin-catalog-react';
// ...
const { updateFilters } = useEntityList();
// ...
updateFilters({
order: new EntityOrderFilter([
{
field: 'metadata.name',
order: 'desc',
},
]),
});
```
+13
View File
@@ -12,6 +12,7 @@ import { ComponentProps } from 'react';
import { CompoundEntityRef } from '@backstage/catalog-model';
import { Context } from 'react';
import { Entity } from '@backstage/catalog-model';
import { EntityOrderQuery } from '@backstage/catalog-client';
import IconButton from '@material-ui/core/IconButton';
import { IconComponent } from '@backstage/core-plugin-api';
import { InfoCardVariants } from '@backstage/core-components';
@@ -197,6 +198,7 @@ export type DefaultEntityFilters = {
orphan?: EntityOrphanFilter;
error?: EntityErrorFilter;
namespace?: EntityNamespaceFilter;
order?: EntityOrderFilter;
};
// @public
@@ -406,6 +408,17 @@ export interface EntityNamespacePickerProps {
initiallySelectedNamespaces?: string[];
}
// @public
export class EntityOrderFilter implements EntityFilter {
constructor(values: [string, 'asc' | 'desc'][]);
// (undocumented)
getOrderFilters(): EntityOrderQuery;
// (undocumented)
toQueryValue(): string[];
// (undocumented)
readonly values: [string, 'asc' | 'desc'][];
}
// @public
export class EntityOrphanFilter implements EntityFilter {
constructor(value: boolean);
@@ -30,7 +30,7 @@ export function useAllEntitiesCount() {
const request = useMemo(() => {
const { user, ...allFilters } = filters;
const compacted = compact(Object.values(allFilters));
const catalogFilters = reduceCatalogFilters(compacted);
const { orderFields, ...catalogFilters } = reduceCatalogFilters(compacted);
const newRequest: QueryEntitiesInitialRequest = {
...catalogFilters,
limit: 0,
@@ -38,7 +38,7 @@ export function useOwnedEntitiesCount() {
);
const { user, owners, ...allFilters } = filters;
const catalogFilters = reduceCatalogFilters(
const { orderFields, ...catalogFilters } = reduceCatalogFilters(
compact(Object.values(allFilters)),
);
@@ -34,7 +34,7 @@ export function useStarredEntitiesCount() {
const request = useMemo(() => {
const { user, ...allFilters } = filters;
const compacted = compact(Object.values(allFilters));
const catalogFilters = reduceCatalogFilters(compacted);
const { orderFields, ...catalogFilters } = reduceCatalogFilters(compacted);
const facet = 'metadata.name';
+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 values: [string, 'asc' | 'desc'][]) {}
getOrderFilters(): EntityOrderQuery {
return this.values.map(([field, order]) => ({ field, order }));
}
toQueryValue(): string[] {
return this.values.flat();
}
}
@@ -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 };
}
/**