catalog-react: remove UserListFilter usage
Signed-off-by: Vincenzo Scamporlino <vincenzos@spotify.com>
This commit is contained in:
@@ -142,7 +142,7 @@ export const columnFactories: Readonly<{
|
||||
export type DefaultEntityFilters = {
|
||||
kind?: EntityKindFilter;
|
||||
type?: EntityTypeFilter;
|
||||
user?: UserListFilter | UserOwnersFilter;
|
||||
user?: UserListFilter | EntityUserListFilter;
|
||||
owners?: EntityOwnerFilter;
|
||||
lifecycles?: EntityLifecycleFilter;
|
||||
tags?: EntityTagFilter;
|
||||
@@ -507,6 +507,26 @@ export interface EntityTypePickerProps {
|
||||
initialFilter?: string;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export class EntityUserListFilter implements EntityFilter {
|
||||
// (undocumented)
|
||||
static all(): EntityUserListFilter;
|
||||
// (undocumented)
|
||||
filterEntity(entity: Entity): boolean;
|
||||
// (undocumented)
|
||||
getCatalogFilters(): Record<string, string[]>;
|
||||
// (undocumented)
|
||||
static owned(ownershipEntityRefs: string[]): EntityUserListFilter;
|
||||
// (undocumented)
|
||||
readonly refs?: string[] | undefined;
|
||||
// (undocumented)
|
||||
static starred(starredEntityRefs: string[]): EntityUserListFilter;
|
||||
// (undocumented)
|
||||
toQueryValue(): string;
|
||||
// (undocumented)
|
||||
readonly value: UserListFilterKind;
|
||||
}
|
||||
|
||||
// @public
|
||||
export const FavoriteEntity: (
|
||||
props: FavoriteEntityProps,
|
||||
@@ -661,29 +681,8 @@ export const UserListPicker: (
|
||||
export type UserListPickerProps = {
|
||||
initialFilter?: UserListFilterKind;
|
||||
availableFilters?: UserListFilterKind[];
|
||||
useServerSideFilters?: boolean;
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export class UserOwnersFilter implements EntityFilter {
|
||||
// (undocumented)
|
||||
static all(): UserOwnersFilter;
|
||||
// (undocumented)
|
||||
filterEntity(entity: Entity): boolean;
|
||||
// (undocumented)
|
||||
getCatalogFilters(): Record<string, string[]>;
|
||||
// (undocumented)
|
||||
static owned(ownershipEntityRefs: string[]): UserOwnersFilter;
|
||||
// (undocumented)
|
||||
readonly refs?: string[] | undefined;
|
||||
// (undocumented)
|
||||
static starred(starredEntityRefs: string[]): UserOwnersFilter;
|
||||
// (undocumented)
|
||||
toQueryValue(): string;
|
||||
// (undocumented)
|
||||
readonly value: UserListFilterKind;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export function useStarredEntities(): {
|
||||
starredEntities: Set<string>;
|
||||
|
||||
@@ -16,14 +16,10 @@
|
||||
|
||||
import React from 'react';
|
||||
import { fireEvent, render, waitFor, screen } from '@testing-library/react';
|
||||
import { Entity, UserEntity } from '@backstage/catalog-model';
|
||||
import { UserEntity } from '@backstage/catalog-model';
|
||||
import { UserListPicker, UserListPickerProps } from './UserListPicker';
|
||||
import { MockEntityListContextProvider } from '../../testUtils/providers';
|
||||
import {
|
||||
EntityTagFilter,
|
||||
UserListFilter,
|
||||
EntityUserListFilter,
|
||||
} from '../../filters';
|
||||
import { EntityTagFilter, EntityUserListFilter } from '../../filters';
|
||||
import {
|
||||
CatalogApi,
|
||||
QueryEntitiesInitialRequest,
|
||||
@@ -75,14 +71,6 @@ const apis = TestApiRegistry.from(
|
||||
[starredEntitiesApiRef, mockStarredEntitiesApi],
|
||||
);
|
||||
|
||||
const mockIsOwnedEntity = jest.fn(
|
||||
(entity: Entity) => entity.metadata.name === 'component-1',
|
||||
);
|
||||
|
||||
const mockIsStarredEntity = jest.fn(
|
||||
(entity: Entity) => entity.metadata.name === 'component-3',
|
||||
);
|
||||
|
||||
const ownershipEntityRefs = ['user:default/testuser'];
|
||||
describe('<UserListPicker />', () => {
|
||||
const mockQueryEntitiesImplementation: CatalogApi['queryEntities'] =
|
||||
@@ -233,44 +221,6 @@ describe('<UserListPicker />', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('respects the query parameter filter value, legacy', async () => {
|
||||
const updateFilters = jest.fn();
|
||||
const queryParameters = { user: 'owned' };
|
||||
render(
|
||||
<ApiProvider apis={apis}>
|
||||
<MockEntityListContextProvider
|
||||
value={{ updateFilters, queryParameters }}
|
||||
>
|
||||
<UserListPicker />
|
||||
</MockEntityListContextProvider>
|
||||
</ApiProvider>,
|
||||
);
|
||||
|
||||
await waitFor(() =>
|
||||
expect(updateFilters).toHaveBeenLastCalledWith({
|
||||
user: new UserListFilter(
|
||||
'owned',
|
||||
expect.any(Function),
|
||||
expect.any(Function),
|
||||
),
|
||||
}),
|
||||
);
|
||||
expect(mockCatalogApi.queryEntities).toHaveBeenCalledWith({
|
||||
filter: {},
|
||||
limit: 0,
|
||||
});
|
||||
expect(mockCatalogApi.queryEntities).toHaveBeenCalledWith({
|
||||
filter: { 'metadata.name': ['e-1', 'e-2'] },
|
||||
limit: 1000,
|
||||
});
|
||||
expect(mockCatalogApi.queryEntities).toHaveBeenCalledWith({
|
||||
filter: {
|
||||
'relations.ownedBy': ['user:default/testuser'],
|
||||
},
|
||||
limit: 0,
|
||||
});
|
||||
});
|
||||
|
||||
it('respects the query parameter filter value', async () => {
|
||||
const updateFilters = jest.fn();
|
||||
const queryParameters = { user: 'owned' };
|
||||
@@ -279,7 +229,7 @@ describe('<UserListPicker />', () => {
|
||||
<MockEntityListContextProvider
|
||||
value={{ updateFilters, queryParameters }}
|
||||
>
|
||||
<UserListPicker useServerSideFilters />
|
||||
<UserListPicker />
|
||||
</MockEntityListContextProvider>
|
||||
</ApiProvider>,
|
||||
);
|
||||
@@ -306,35 +256,12 @@ describe('<UserListPicker />', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('updates user filter when a menuitem is selected, legacy', async () => {
|
||||
const updateFilters = jest.fn();
|
||||
render(
|
||||
<ApiProvider apis={apis}>
|
||||
<MockEntityListContextProvider value={{ updateFilters }}>
|
||||
<UserListPicker />
|
||||
</MockEntityListContextProvider>
|
||||
</ApiProvider>,
|
||||
);
|
||||
|
||||
fireEvent.click(screen.getByText('Starred'));
|
||||
|
||||
await waitFor(() =>
|
||||
expect(updateFilters).toHaveBeenLastCalledWith({
|
||||
user: new UserListFilter(
|
||||
'starred',
|
||||
expect.any(Function),
|
||||
expect.any(Function),
|
||||
),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('updates user filter when a menuitem is selected', async () => {
|
||||
const updateFilters = jest.fn();
|
||||
render(
|
||||
<ApiProvider apis={apis}>
|
||||
<MockEntityListContextProvider value={{ updateFilters }}>
|
||||
<UserListPicker useServerSideFilters />
|
||||
<UserListPicker />
|
||||
</MockEntityListContextProvider>
|
||||
</ApiProvider>,
|
||||
);
|
||||
@@ -351,52 +278,6 @@ describe('<UserListPicker />', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('responds to external queryParameters changes, legacy', async () => {
|
||||
const updateFilters = jest.fn();
|
||||
const rendered = render(
|
||||
<ApiProvider apis={apis}>
|
||||
<MockEntityListContextProvider
|
||||
value={{
|
||||
updateFilters,
|
||||
queryParameters: { user: ['all'] },
|
||||
}}
|
||||
>
|
||||
<UserListPicker />
|
||||
</MockEntityListContextProvider>
|
||||
</ApiProvider>,
|
||||
);
|
||||
|
||||
await waitFor(() =>
|
||||
expect(updateFilters).toHaveBeenLastCalledWith({
|
||||
user: new UserListFilter(
|
||||
'all',
|
||||
expect.any(Function),
|
||||
expect.any(Function),
|
||||
),
|
||||
}),
|
||||
);
|
||||
|
||||
rendered.rerender(
|
||||
<ApiProvider apis={apis}>
|
||||
<MockEntityListContextProvider
|
||||
value={{
|
||||
updateFilters,
|
||||
queryParameters: { user: ['owned'] },
|
||||
}}
|
||||
>
|
||||
<UserListPicker />
|
||||
</MockEntityListContextProvider>
|
||||
</ApiProvider>,
|
||||
);
|
||||
expect(updateFilters).toHaveBeenLastCalledWith({
|
||||
user: new UserListFilter(
|
||||
'owned',
|
||||
expect.any(Function),
|
||||
expect.any(Function),
|
||||
),
|
||||
});
|
||||
});
|
||||
|
||||
it('responds to external queryParameters changes', async () => {
|
||||
const updateFilters = jest.fn();
|
||||
const rendered = render(
|
||||
@@ -407,7 +288,7 @@ describe('<UserListPicker />', () => {
|
||||
queryParameters: { user: ['all'] },
|
||||
}}
|
||||
>
|
||||
<UserListPicker useServerSideFilters />
|
||||
<UserListPicker />
|
||||
</MockEntityListContextProvider>
|
||||
</ApiProvider>,
|
||||
);
|
||||
@@ -426,7 +307,7 @@ describe('<UserListPicker />', () => {
|
||||
queryParameters: { user: ['owned'] },
|
||||
}}
|
||||
>
|
||||
<UserListPicker useServerSideFilters />
|
||||
<UserListPicker />
|
||||
</MockEntityListContextProvider>
|
||||
</ApiProvider>,
|
||||
);
|
||||
@@ -489,34 +370,6 @@ describe('<UserListPicker />', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('resets the filter to "all" when entities are loaded, legacy', async () => {
|
||||
mockCatalogApi.queryEntities?.mockImplementation(async request => {
|
||||
if (
|
||||
(
|
||||
(request as QueryEntitiesInitialRequest).filter as Record<
|
||||
string,
|
||||
string
|
||||
>
|
||||
)['relations.ownedBy']
|
||||
) {
|
||||
return { items: [], totalItems: 0, pageInfo: {} };
|
||||
}
|
||||
return mockQueryEntitiesImplementation(request);
|
||||
});
|
||||
|
||||
render(<Picker initialFilter="owned" />);
|
||||
|
||||
await waitFor(() =>
|
||||
expect(updateFilters).toHaveBeenLastCalledWith({
|
||||
user: new UserListFilter(
|
||||
'all',
|
||||
expect.any(Function),
|
||||
expect.any(Function),
|
||||
),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('resets the filter to "all" when entities are loaded', async () => {
|
||||
mockCatalogApi.queryEntities?.mockImplementation(async request => {
|
||||
if (
|
||||
@@ -532,7 +385,7 @@ describe('<UserListPicker />', () => {
|
||||
return mockQueryEntitiesImplementation(request);
|
||||
});
|
||||
|
||||
render(<Picker initialFilter="owned" useServerSideFilters />);
|
||||
render(<Picker initialFilter="owned" />);
|
||||
|
||||
await waitFor(() =>
|
||||
expect(updateFilters).toHaveBeenLastCalledWith({
|
||||
@@ -581,34 +434,6 @@ describe('<UserListPicker />', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('resets the filter to "all" when entities are loaded, legacy', async () => {
|
||||
mockCatalogApi.queryEntities?.mockImplementation(async request => {
|
||||
if (
|
||||
(
|
||||
(request as QueryEntitiesInitialRequest).filter as Record<
|
||||
string,
|
||||
string
|
||||
>
|
||||
)['metadata.name']
|
||||
) {
|
||||
return { items: [], totalItems: 0, pageInfo: {} };
|
||||
}
|
||||
return mockQueryEntitiesImplementation(request);
|
||||
});
|
||||
|
||||
render(<Picker initialFilter="starred" />);
|
||||
|
||||
await waitFor(() =>
|
||||
expect(updateFilters).toHaveBeenLastCalledWith({
|
||||
user: new UserListFilter(
|
||||
'all',
|
||||
expect.any(Function),
|
||||
expect.any(Function),
|
||||
),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('resets the filter to "all" when entities are loaded', async () => {
|
||||
mockCatalogApi.queryEntities?.mockImplementation(async request => {
|
||||
if (
|
||||
@@ -624,7 +449,7 @@ describe('<UserListPicker />', () => {
|
||||
return mockQueryEntitiesImplementation(request);
|
||||
});
|
||||
|
||||
render(<Picker initialFilter="starred" useServerSideFilters />);
|
||||
render(<Picker initialFilter="starred" />);
|
||||
|
||||
await waitFor(() =>
|
||||
expect(updateFilters).toHaveBeenLastCalledWith({
|
||||
@@ -658,11 +483,7 @@ describe('<UserListPicker />', () => {
|
||||
expect(mockCatalogApi.queryEntities).toHaveBeenCalledTimes(3),
|
||||
);
|
||||
expect(updateFilters).not.toHaveBeenCalledWith({
|
||||
user: new UserListFilter(
|
||||
'all',
|
||||
mockIsOwnedEntity,
|
||||
mockIsStarredEntity,
|
||||
),
|
||||
user: EntityUserListFilter.all(),
|
||||
});
|
||||
});
|
||||
|
||||
@@ -675,11 +496,7 @@ describe('<UserListPicker />', () => {
|
||||
|
||||
await waitFor(() =>
|
||||
expect(updateFilters).toHaveBeenLastCalledWith({
|
||||
user: new UserListFilter(
|
||||
'owned',
|
||||
expect.any(Function),
|
||||
expect.any(Function),
|
||||
),
|
||||
user: EntityUserListFilter.owned(expect.any(Array)),
|
||||
}),
|
||||
);
|
||||
});
|
||||
@@ -709,11 +526,7 @@ describe('<UserListPicker />', () => {
|
||||
expect(mockCatalogApi.queryEntities).toHaveBeenCalledTimes(3),
|
||||
);
|
||||
expect(updateFilters).not.toHaveBeenCalledWith({
|
||||
user: new UserListFilter(
|
||||
'all',
|
||||
mockIsOwnedEntity,
|
||||
mockIsStarredEntity,
|
||||
),
|
||||
user: EntityUserListFilter.all(),
|
||||
});
|
||||
});
|
||||
|
||||
@@ -726,11 +539,7 @@ describe('<UserListPicker />', () => {
|
||||
|
||||
await waitFor(() =>
|
||||
expect(updateFilters).toHaveBeenLastCalledWith({
|
||||
user: new UserListFilter(
|
||||
'starred',
|
||||
expect.any(Function),
|
||||
expect.any(Function),
|
||||
),
|
||||
user: EntityUserListFilter.starred(expect.any(Array)),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -33,13 +33,12 @@ import {
|
||||
import SettingsIcon from '@material-ui/icons/Settings';
|
||||
import StarIcon from '@material-ui/icons/Star';
|
||||
import React, { Fragment, useEffect, useMemo, useState } from 'react';
|
||||
import { UserListFilter, EntityUserListFilter } from '../../filters';
|
||||
import { useEntityList, useStarredEntities } from '../../hooks';
|
||||
import { EntityUserListFilter } from '../../filters';
|
||||
import { useEntityList } from '../../hooks';
|
||||
import { UserListFilterKind } from '../../types';
|
||||
import { useOwnedEntitiesCount } from './useOwnedEntitiesCount';
|
||||
import { useAllEntitiesCount } from './useAllEntitiesCount';
|
||||
import { useStarredEntitiesCount } from './useStarredEntitiesCount';
|
||||
import { useIsOwnedEntity } from '../../hooks/useEntityOwnership';
|
||||
|
||||
/** @public */
|
||||
export type CatalogReactUserListPickerClassKey =
|
||||
@@ -120,12 +119,11 @@ function getFilterGroups(orgName: string | undefined): ButtonGroup[] {
|
||||
export type UserListPickerProps = {
|
||||
initialFilter?: UserListFilterKind;
|
||||
availableFilters?: UserListFilterKind[];
|
||||
useServerSideFilters?: boolean;
|
||||
};
|
||||
|
||||
/** @public */
|
||||
export const UserListPicker = (props: UserListPickerProps) => {
|
||||
const { initialFilter, availableFilters, useServerSideFilters } = props;
|
||||
const { initialFilter, availableFilters } = props;
|
||||
const classes = useStyles();
|
||||
const configApi = useApi(configApiRef);
|
||||
const orgName = configApi.getOptionalString('organization.name') ?? 'Company';
|
||||
@@ -154,7 +152,6 @@ export const UserListPicker = (props: UserListPickerProps) => {
|
||||
count: ownedEntitiesCount,
|
||||
loading: loadingOwnedEntities,
|
||||
filter: ownedEntitiesFilter,
|
||||
ownershipEntityRefs,
|
||||
} = useOwnedEntitiesCount();
|
||||
const { count: allCount } = useAllEntitiesCount();
|
||||
const {
|
||||
@@ -180,9 +177,6 @@ export const UserListPicker = (props: UserListPickerProps) => {
|
||||
};
|
||||
}, [starredEntitiesCount, ownedEntitiesCount, allCount]);
|
||||
|
||||
const { isStarredEntity } = useStarredEntities();
|
||||
const isOwnedEntity = useIsOwnedEntity(ownershipEntityRefs);
|
||||
|
||||
// Set selected user filter on query parameter updates; this happens at initial page load and from
|
||||
// external updates to the page location.
|
||||
useEffect(() => {
|
||||
@@ -211,38 +205,24 @@ export const UserListPicker = (props: UserListPickerProps) => {
|
||||
if (loading) {
|
||||
return;
|
||||
}
|
||||
if (useServerSideFilters) {
|
||||
const getFilter = () => {
|
||||
if (selectedUserFilter === 'owned') {
|
||||
return ownedEntitiesFilter;
|
||||
}
|
||||
if (selectedUserFilter === 'starred') {
|
||||
return starredEntitiesFilter;
|
||||
}
|
||||
return EntityUserListFilter.all();
|
||||
};
|
||||
|
||||
updateFilters({ user: getFilter() });
|
||||
} else {
|
||||
// legacy
|
||||
updateFilters({
|
||||
user: selectedUserFilter
|
||||
? new UserListFilter(
|
||||
selectedUserFilter as UserListFilterKind,
|
||||
isOwnedEntity,
|
||||
isStarredEntity,
|
||||
)
|
||||
: undefined,
|
||||
});
|
||||
}
|
||||
const getFilter = () => {
|
||||
if (selectedUserFilter === 'owned') {
|
||||
return ownedEntitiesFilter;
|
||||
}
|
||||
if (selectedUserFilter === 'starred') {
|
||||
return starredEntitiesFilter;
|
||||
}
|
||||
return EntityUserListFilter.all();
|
||||
};
|
||||
|
||||
updateFilters({ user: getFilter() });
|
||||
}, [
|
||||
selectedUserFilter,
|
||||
starredEntitiesFilter,
|
||||
ownedEntitiesFilter,
|
||||
updateFilters,
|
||||
useServerSideFilters,
|
||||
isOwnedEntity,
|
||||
isStarredEntity,
|
||||
|
||||
loading,
|
||||
]);
|
||||
|
||||
|
||||
@@ -237,6 +237,15 @@ export class EntityUserListFilter implements EntityFilter {
|
||||
if (this.value === 'starred') {
|
||||
return this.refs?.includes(stringifyEntityRef(entity)) ?? true;
|
||||
}
|
||||
if (this.value === 'owned') {
|
||||
return (
|
||||
this.refs?.some(v =>
|
||||
getEntityRelations(entity, RELATION_OWNED_BY).some(
|
||||
o => stringifyEntityRef(o) === v,
|
||||
),
|
||||
) ?? false
|
||||
);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,11 @@ import { MemoryRouter } from 'react-router-dom';
|
||||
import { catalogApiRef } from '../api';
|
||||
import { starredEntitiesApiRef, MockStarredEntitiesApi } from '../apis';
|
||||
import { EntityKindPicker, UserListPicker } from '../components';
|
||||
import { EntityKindFilter, EntityTypeFilter, UserListFilter } from '../filters';
|
||||
import {
|
||||
EntityKindFilter,
|
||||
EntityTypeFilter,
|
||||
EntityUserListFilter,
|
||||
} from '../filters';
|
||||
import { UserListFilterKind } from '../types';
|
||||
import { EntityListProvider, useEntityList } from './useEntityListProvider';
|
||||
|
||||
@@ -62,11 +66,14 @@ const entities: Entity[] = [
|
||||
const mockConfigApi = {
|
||||
getOptionalString: () => '',
|
||||
} as Partial<ConfigApi>;
|
||||
|
||||
const ownershipEntityRefs = ['user:default/guest'];
|
||||
|
||||
const mockIdentityApi: Partial<IdentityApi> = {
|
||||
getBackstageIdentity: async () => ({
|
||||
type: 'user',
|
||||
userEntityRef: 'user:default/guest',
|
||||
ownershipEntityRefs: [],
|
||||
ownershipEntityRefs,
|
||||
}),
|
||||
getCredentials: async () => ({ token: undefined }),
|
||||
};
|
||||
@@ -148,11 +155,7 @@ describe('<EntityListProvider />', () => {
|
||||
|
||||
act(() =>
|
||||
result.current.updateFilters({
|
||||
user: new UserListFilter(
|
||||
'owned',
|
||||
entity => entity.metadata.name === 'component-1',
|
||||
() => true,
|
||||
),
|
||||
user: EntityUserListFilter.owned(ownershipEntityRefs),
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -193,11 +196,7 @@ describe('<EntityListProvider />', () => {
|
||||
|
||||
act(() =>
|
||||
result.current.updateFilters({
|
||||
user: new UserListFilter(
|
||||
'owned',
|
||||
entity => entity.metadata.name === 'component-1',
|
||||
() => true,
|
||||
),
|
||||
user: EntityUserListFilter.owned(ownershipEntityRefs),
|
||||
}),
|
||||
);
|
||||
|
||||
|
||||
@@ -46,15 +46,10 @@ export function useEntityOwnership(): {
|
||||
return ownershipEntityRefs;
|
||||
}, []);
|
||||
|
||||
const isOwnedEntity = useIsOwnedEntity(refs);
|
||||
|
||||
return useMemo(() => ({ loading, isOwnedEntity }), [loading, isOwnedEntity]);
|
||||
}
|
||||
|
||||
export function useIsOwnedEntity(refs?: string[]) {
|
||||
return useMemo(() => {
|
||||
const isOwnedEntity = useMemo(() => {
|
||||
const myOwnerRefs = new Set(refs ?? []);
|
||||
const isOwnedEntity = (entity: Entity) => {
|
||||
|
||||
return (entity: Entity) => {
|
||||
const entityOwnerRefs = getEntityRelations(entity, RELATION_OWNED_BY).map(
|
||||
stringifyEntityRef,
|
||||
);
|
||||
@@ -65,6 +60,7 @@ export function useIsOwnedEntity(refs?: string[]) {
|
||||
}
|
||||
return false;
|
||||
};
|
||||
return isOwnedEntity;
|
||||
}, [refs]);
|
||||
|
||||
return { loading, isOwnedEntity };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user