Merge pull request #9604 from backstage/jhaals/catalog-react-cleanup

catalog-react: cleanup api report
This commit is contained in:
Johan Haals
2022-02-17 14:57:00 +01:00
committed by GitHub
35 changed files with 414 additions and 375 deletions
+7
View File
@@ -0,0 +1,7 @@
---
'@backstage/plugin-catalog-react': minor
---
Improved API documentation.
**BREAKING**: The individual table column factories (e.g. `createEntityRefColumn`) are now no longer available directly, but only through `EntityTable.columns`.
+128 -170
View File
@@ -66,9 +66,7 @@ export { CATALOG_FILTER_EXISTS };
export { CatalogApi };
// Warning: (ae-missing-release-tag) "catalogApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
// @public
export const catalogApiRef: ApiRef<CatalogApi>;
// @public (undocumented)
@@ -100,51 +98,37 @@ export type CatalogReactUserListPickerClassKey =
| 'menuItem'
| 'groupWrapper';
// Warning: (ae-missing-release-tag) "catalogRouteRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public @deprecated (undocumented)
export const catalogRouteRef: RouteRef<undefined>;
// @public (undocumented)
function createDomainColumn<T extends Entity>(): TableColumn<T>;
export const columnFactories: Readonly<{
createEntityRefColumn<T extends Entity>(options: {
defaultKind?: string;
}): TableColumn<T>;
createEntityRelationColumn<T_1 extends Entity>({
title,
relation,
defaultKind,
filter: entityFilter,
}: {
title: string;
relation: string;
defaultKind?: string | undefined;
filter?:
| {
kind: string;
}
| undefined;
}): TableColumn<T_1>;
createOwnerColumn<T_2 extends Entity>(): TableColumn<T_2>;
createDomainColumn<T_3 extends Entity>(): TableColumn<T_3>;
createSystemColumn<T_4 extends Entity>(): TableColumn<T_4>;
createMetadataDescriptionColumn<T_5 extends Entity>(): TableColumn<T_5>;
createSpecLifecycleColumn<T_6 extends Entity>(): TableColumn<T_6>;
createSpecTypeColumn<T_7 extends Entity>(): TableColumn<T_7>;
}>;
// @public (undocumented)
function createEntityRefColumn<T extends Entity>(options: {
defaultKind?: string;
}): TableColumn<T>;
// @public (undocumented)
function createEntityRelationColumn<T extends Entity>({
title,
relation,
defaultKind,
filter: entityFilter,
}: {
title: string;
relation: string;
defaultKind?: string;
filter?: {
kind: string;
};
}): TableColumn<T>;
// @public (undocumented)
function createMetadataDescriptionColumn<T extends Entity>(): TableColumn<T>;
// @public (undocumented)
function createOwnerColumn<T extends Entity>(): TableColumn<T>;
// @public (undocumented)
function createSpecLifecycleColumn<T extends Entity>(): TableColumn<T>;
// @public (undocumented)
function createSpecTypeColumn<T extends Entity>(): TableColumn<T>;
// @public (undocumented)
function createSystemColumn<T extends Entity>(): TableColumn<T>;
// Warning: (ae-missing-release-tag) "DefaultEntityFilters" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export type DefaultEntityFilters = {
kind?: EntityKindFilter;
@@ -167,13 +151,9 @@ export class DefaultStarredEntitiesApi implements StarredEntitiesApi {
toggleStarred(entityRef: string): Promise<void>;
}
// Warning: (ae-forgotten-export) The symbol "EntityLoadingStatus" needs to be exported by the entry point index.d.ts
//
// @public @deprecated (undocumented)
export const EntityContext: Context<EntityLoadingStatus>;
// Warning: (ae-missing-release-tag) "EntityFilter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export type EntityFilter = {
getCatalogFilters?: () => Record<
@@ -184,9 +164,7 @@ export type EntityFilter = {
toQueryValue?: () => string | string[];
};
// Warning: (ae-missing-release-tag) "EntityKindFilter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
// @public
export class EntityKindFilter implements EntityFilter {
constructor(value: string);
// (undocumented)
@@ -210,9 +188,7 @@ export interface EntityKindPickerProps {
initialFilter?: string;
}
// Warning: (ae-missing-release-tag) "EntityLifecycleFilter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
// @public
export class EntityLifecycleFilter implements EntityFilter {
constructor(values: string[]);
// (undocumented)
@@ -226,24 +202,42 @@ export class EntityLifecycleFilter implements EntityFilter {
// @public (undocumented)
export const EntityLifecyclePicker: () => JSX.Element | null;
// Warning: (ae-forgotten-export) The symbol "EntityListContextProps" needs to be exported by the entry point index.d.ts
// Warning: (ae-missing-release-tag) "EntityListContext" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
// @public
export const EntityListContext: React_2.Context<
EntityListContextProps<any> | undefined
>;
// Warning: (ae-missing-release-tag) "EntityListProvider" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export type EntityListContextProps<
EntityFilters extends DefaultEntityFilters = DefaultEntityFilters,
> = {
filters: EntityFilters;
entities: Entity[];
backendEntities: Entity[];
updateFilters: (
filters:
| Partial<EntityFilters>
| ((prevFilters: EntityFilters) => Partial<EntityFilters>),
) => void;
queryParameters: Partial<Record<keyof EntityFilters, string | string[]>>;
loading: boolean;
error?: Error;
};
// @public
export const EntityListProvider: <EntityFilters extends DefaultEntityFilters>({
children,
}: PropsWithChildren<{}>) => JSX.Element;
// Warning: (ae-missing-release-tag) "EntityOwnerFilter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export type EntityLoadingStatus = {
entity?: Entity;
loading: boolean;
error?: Error;
refresh?: VoidFunction;
};
// @public
export class EntityOwnerFilter implements EntityFilter {
constructor(values: string[]);
// (undocumented)
@@ -295,8 +289,6 @@ export type EntityRefLinksProps = {
defaultKind?: string;
} & Omit<LinkProps, 'to'>;
// Warning: (ae-missing-release-tag) "entityRoute" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public @deprecated (undocumented)
export const entityRoute: RouteRef<{
name: string;
@@ -304,17 +296,13 @@ export const entityRoute: RouteRef<{
namespace: string;
}>;
// Warning: (ae-missing-release-tag) "entityRouteParams" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
// @public
export function entityRouteParams(entity: Entity): {
readonly kind: string;
readonly namespace: string;
readonly name: string;
};
// Warning: (ae-missing-release-tag) "entityRouteRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public
export const entityRouteRef: RouteRef<{
name: string;
@@ -322,38 +310,48 @@ export const entityRouteRef: RouteRef<{
namespace: string;
}>;
// @public (undocumented)
// @public
export const EntitySearchBar: () => JSX.Element;
// Warning: (ae-missing-release-tag) "EntitySourceLocation" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export type EntitySourceLocation = {
locationTargetUrl: string;
integrationType?: string;
};
// Warning: (ae-missing-release-tag) "EntityTable" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public
export function EntityTable<T extends Entity>(
props: EntityTableProps<T>,
): JSX.Element;
export const EntityTable: {
<T extends Entity>(props: EntityTableProps<T>): JSX.Element;
columns: Readonly<{
createEntityRefColumn<T_1 extends Entity>(options: {
defaultKind?: string | undefined;
}): TableColumn<T_1>;
createEntityRelationColumn<T_2 extends Entity>({
title,
relation,
defaultKind,
filter: entityFilter,
}: {
title: string;
relation: string;
defaultKind?: string | undefined;
filter?:
| {
kind: string;
}
| undefined;
}): TableColumn<T_2>;
createOwnerColumn<T_3 extends Entity>(): TableColumn<T_3>;
createDomainColumn<T_4 extends Entity>(): TableColumn<T_4>;
createSystemColumn<T_5 extends Entity>(): TableColumn<T_5>;
createMetadataDescriptionColumn<T_6 extends Entity>(): TableColumn<T_6>;
createSpecLifecycleColumn<T_7 extends Entity>(): TableColumn<T_7>;
createSpecTypeColumn<T_8 extends Entity>(): TableColumn<T_8>;
}>;
systemEntityColumns: TableColumn<SystemEntity>[];
componentEntityColumns: TableColumn<ComponentEntity>[];
};
// @public (undocumented)
export namespace EntityTable {
var // Warning: (ae-forgotten-export) The symbol "columnFactories" needs to be exported by the entry point index.d.ts
//
// (undocumented)
columns: typeof columnFactories;
var // (undocumented)
systemEntityColumns: TableColumn<SystemEntity>[];
var // (undocumented)
componentEntityColumns: TableColumn<ComponentEntity>[];
}
// Warning: (ae-unresolved-link) The @link reference could not be resolved: The reference is ambiguous because "EntityTable" has more than one declaration; you need to add a TSDoc member reference selector
//
// @public
export interface EntityTableProps<T extends Entity> {
// (undocumented)
@@ -368,9 +366,7 @@ export interface EntityTableProps<T extends Entity> {
variant?: 'gridItem';
}
// Warning: (ae-missing-release-tag) "EntityTagFilter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
// @public
export class EntityTagFilter implements EntityFilter {
constructor(values: string[]);
// (undocumented)
@@ -384,9 +380,7 @@ export class EntityTagFilter implements EntityFilter {
// @public (undocumented)
export const EntityTagPicker: () => JSX.Element | null;
// Warning: (ae-missing-release-tag) "EntityTextFilter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
// @public
export class EntityTextFilter implements EntityFilter {
constructor(value: string);
// (undocumented)
@@ -395,9 +389,7 @@ export class EntityTextFilter implements EntityFilter {
readonly value: string;
}
// Warning: (ae-missing-release-tag) "EntityTypeFilter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
// @public
export class EntityTypeFilter implements EntityFilter {
constructor(value: string | string[]);
// (undocumented)
@@ -423,26 +415,31 @@ export interface EntityTypePickerProps {
initialFilter?: string;
}
// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts
// Warning: (ae-missing-release-tag) "FavoriteEntity" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public
export const FavoriteEntity: (props: Props) => JSX.Element;
// @public (undocumented)
export type EntityTypeReturn = {
loading: boolean;
error?: Error;
availableTypes: string[];
selectedTypes: string[];
setSelectedTypes: (types: string[]) => void;
};
// @public
export const FavoriteEntity: (props: FavoriteEntityProps) => JSX.Element;
// Warning: (ae-missing-release-tag) "favoriteEntityIcon" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export const favoriteEntityIcon: (isStarred: boolean) => JSX.Element;
// Warning: (ae-missing-release-tag) "favoriteEntityTooltip" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export type FavoriteEntityProps = ComponentProps<typeof IconButton> & {
entity: Entity;
};
// @public (undocumented)
export const favoriteEntityTooltip: (
isStarred: boolean,
) => 'Remove from favorites' | 'Add to favorites';
// Warning: (ae-missing-release-tag) "formatEntityRefTitle" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export function formatEntityRefTitle(
entityRef: Entity | EntityName,
@@ -451,18 +448,12 @@ export function formatEntityRefTitle(
},
): string;
// Warning: (ae-missing-release-tag) "getEntityMetadataEditUrl" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export function getEntityMetadataEditUrl(entity: Entity): string | undefined;
// Warning: (ae-missing-release-tag) "getEntityMetadataViewUrl" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export function getEntityMetadataViewUrl(entity: Entity): string | undefined;
// Warning: (ae-missing-release-tag) "getEntityRelations" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public
export function getEntityRelations(
entity: Entity | undefined,
@@ -472,8 +463,6 @@ export function getEntityRelations(
},
): EntityName[];
// Warning: (ae-missing-release-tag) "getEntitySourceLocation" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export function getEntitySourceLocation(
entity: Entity,
@@ -487,8 +476,6 @@ export function InspectEntityDialog(props: {
onClose: () => void;
}): JSX.Element | null;
// Warning: (ae-missing-release-tag) "isOwnerOf" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public
export function isOwnerOf(owner: Entity, owned: Entity): boolean;
@@ -503,8 +490,6 @@ export function loadIdentityOwnerRefs(
identityApi: IdentityApi,
): Promise<string[]>;
// Warning: (ae-missing-release-tag) "MockEntityListContextProvider" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export const MockEntityListContextProvider: ({
children,
@@ -513,22 +498,16 @@ export const MockEntityListContextProvider: ({
value?: Partial<EntityListContextProps<DefaultEntityFilters>> | undefined;
}>) => JSX.Element;
// Warning: (ae-missing-release-tag) "reduceCatalogFilters" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export function reduceCatalogFilters(
filters: EntityFilter[],
): Record<string, string | symbol | (string | symbol)[]>;
// Warning: (ae-missing-release-tag) "reduceEntityFilters" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export function reduceEntityFilters(
filters: EntityFilter[],
): (entity: Entity) => boolean;
// Warning: (ae-missing-release-tag) "rootRoute" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public @deprecated (undocumented)
export const rootRoute: RouteRef<undefined>;
@@ -541,16 +520,18 @@ export interface StarredEntitiesApi {
// @public
export const starredEntitiesApiRef: ApiRef<StarredEntitiesApi>;
// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts
// Warning: (ae-missing-release-tag) "UnregisterEntityDialog" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export const UnregisterEntityDialog: ({
open,
onConfirm,
onClose,
entity,
}: Props_2) => JSX.Element;
export const UnregisterEntityDialog: (
props: UnregisterEntityDialogProps,
) => JSX.Element;
// @public (undocumented)
export type UnregisterEntityDialogProps = {
open: boolean;
onConfirm: () => any;
onClose: () => any;
entity: Entity;
};
// @public
export function useEntity<T extends Entity = Entity>(): {
@@ -560,8 +541,6 @@ export function useEntity<T extends Entity = Entity>(): {
refresh: VoidFunction | undefined;
};
// Warning: (ae-missing-release-tag) "useEntityCompoundName" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public
export const useEntityCompoundName: () => {
kind: string;
@@ -569,23 +548,17 @@ export const useEntityCompoundName: () => {
name: string;
};
// Warning: (ae-missing-release-tag) "useEntityFromUrl" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export const useEntityFromUrl: () => EntityLoadingStatus;
// Warning: (ae-missing-release-tag) "useEntityKinds" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
// @public
export function useEntityKinds(): {
error: Error | undefined;
loading: boolean;
kinds: string[] | undefined;
};
// Warning: (ae-missing-release-tag) "useEntityListProvider" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
// @public
export function useEntityListProvider<
EntityFilters extends DefaultEntityFilters = DefaultEntityFilters,
>(): EntityListContextProps<EntityFilters>;
@@ -603,9 +576,6 @@ export function useEntityPermission(permission: Permission): {
error?: Error;
};
// Warning: (ae-forgotten-export) The symbol "EntityTypeReturn" needs to be exported by the entry point index.d.ts
// Warning: (ae-missing-release-tag) "useEntityTypeFilter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public
export function useEntityTypeFilter(): EntityTypeReturn;
@@ -615,13 +585,9 @@ export function useOwnedEntities(allowedKinds?: string[]): {
ownedEntities: GetEntitiesResponse | undefined;
};
// Warning: (ae-missing-release-tag) "useOwnUser" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public
export function useOwnUser(): AsyncState<UserEntity | undefined>;
// Warning: (ae-missing-release-tag) "useRelatedEntities" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export function useRelatedEntities(
entity: Entity,
@@ -638,9 +604,7 @@ export function useRelatedEntities(
error: Error | undefined;
};
// Warning: (ae-missing-release-tag) "UserListFilter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
// @public
export class UserListFilter implements EntityFilter {
constructor(
value: UserListFilterKind,
@@ -659,22 +623,18 @@ export class UserListFilter implements EntityFilter {
readonly value: UserListFilterKind;
}
// Warning: (ae-missing-release-tag) "UserListFilterKind" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export type UserListFilterKind = 'owned' | 'starred' | 'all';
// Warning: (ae-forgotten-export) The symbol "UserListPickerProps" needs to be exported by the entry point index.d.ts
// Warning: (ae-missing-release-tag) "UserListPicker" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export const UserListPicker: ({
initialFilter,
availableFilters,
}: UserListPickerProps) => JSX.Element;
export const UserListPicker: (props: UserListPickerProps) => JSX.Element;
// @public (undocumented)
export type UserListPickerProps = {
initialFilter?: UserListFilterKind;
availableFilters?: UserListFilterKind[];
};
// Warning: (ae-missing-release-tag) "useStarredEntities" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export function useStarredEntities(): {
starredEntities: Set<string>;
@@ -682,8 +642,6 @@ export function useStarredEntities(): {
isStarredEntity: (entityOrRef: Entity | EntityName | string) => boolean;
};
// Warning: (ae-missing-release-tag) "useStarredEntity" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export function useStarredEntity(entityOrRef: Entity | EntityName | string): {
toggleStarredEntity: () => void;
+4
View File
@@ -17,6 +17,10 @@
import { CatalogApi } from '@backstage/catalog-client';
import { createApiRef } from '@backstage/core-plugin-api';
/**
* The API reference for the {@link @backstage/catalog-client#CatalogApi}.
* @public
*/
export const catalogApiRef = createApiRef<CatalogApi>({
id: 'plugin.catalog.service',
});
@@ -20,6 +20,7 @@ import {
DEFAULT_NAMESPACE,
} from '@backstage/catalog-model';
/** @public */
export function formatEntityRefTitle(
entityRef: Entity | EntityName,
opts?: { defaultKind?: string },
@@ -45,7 +45,10 @@ const useStyles = makeStyles(
},
);
/** @public */
/**
* Renders search bar for filtering the entity list.
* @public
*/
export const EntitySearchBar = () => {
const classes = useStyles();
@@ -17,7 +17,7 @@
import { Entity } from '@backstage/catalog-model';
import { makeStyles } from '@material-ui/core';
import React, { ReactNode } from 'react';
import * as columnFactories from './columns';
import { columnFactories } from './columns';
import { componentEntityColumns, systemEntityColumns } from './presets';
import { Table, TableColumn } from '@backstage/core-components';
@@ -48,7 +48,7 @@ const useStyles = makeStyles(theme => ({
*
* @public
*/
export function EntityTable<T extends Entity>(props: EntityTableProps<T>) {
export const EntityTable = <T extends Entity>(props: EntityTableProps<T>) => {
const {
entities,
title,
@@ -85,7 +85,7 @@ export function EntityTable<T extends Entity>(props: EntityTableProps<T>) {
data={entities}
/>
);
}
};
EntityTable.columns = columnFactories;
@@ -30,150 +30,136 @@ import {
} from '../EntityRefLink';
/** @public */
export function createEntityRefColumn<T extends Entity>(options: {
defaultKind?: string;
}): TableColumn<T> {
const { defaultKind } = options;
function formatContent(entity: T): string {
return (
entity.metadata?.title ||
formatEntityRefTitle(entity, {
defaultKind,
})
);
}
return {
title: 'Name',
highlight: true,
customFilterAndSearch(filter, entity) {
// TODO: We could implement this more efficiently, like searching over
// each field that is displayed individually (kind, namespace, name).
// but that might confuse the user as it will behave different than a
// simple text search.
// Another alternative would be to cache the values. But writing them
// into the entity feels bad too.
return formatContent(entity).includes(filter);
},
customSort(entity1, entity2) {
// TODO: We could implement this more efficiently by comparing field by field.
// This has similar issues as above.
return formatContent(entity1).localeCompare(formatContent(entity2));
},
render: entity => (
<EntityRefLink
entityRef={entity}
defaultKind={defaultKind}
title={entity.metadata?.title}
/>
),
};
}
/** @public */
export function createEntityRelationColumn<T extends Entity>({
title,
relation,
defaultKind,
filter: entityFilter,
}: {
title: string;
relation: string;
defaultKind?: string;
filter?: { kind: string };
}): TableColumn<T> {
function getRelations(entity: T): EntityName[] {
return getEntityRelations(entity, relation, entityFilter);
}
function formatContent(entity: T): string {
return getRelations(entity)
.map(r => formatEntityRefTitle(r, { defaultKind }))
.join(', ');
}
return {
title,
customFilterAndSearch(filter, entity) {
return formatContent(entity).includes(filter);
},
customSort(entity1, entity2) {
return formatContent(entity1).localeCompare(formatContent(entity2));
},
render: entity => {
export const columnFactories = Object.freeze({
createEntityRefColumn<T extends Entity>(options: {
defaultKind?: string;
}): TableColumn<T> {
const { defaultKind } = options;
function formatContent(entity: T): string {
return (
<EntityRefLinks
entityRefs={getRelations(entity)}
defaultKind={defaultKind}
/>
entity.metadata?.title ||
formatEntityRefTitle(entity, {
defaultKind,
})
);
},
};
}
}
/** @public */
export function createOwnerColumn<T extends Entity>(): TableColumn<T> {
return createEntityRelationColumn({
title: 'Owner',
relation: RELATION_OWNED_BY,
defaultKind: 'group',
});
}
return {
title: 'Name',
highlight: true,
customFilterAndSearch(filter, entity) {
// TODO: We could implement this more efficiently, like searching over
// each field that is displayed individually (kind, namespace, name).
// but that might confuse the user as it will behave different than a
// simple text search.
// Another alternative would be to cache the values. But writing them
// into the entity feels bad too.
return formatContent(entity).includes(filter);
},
customSort(entity1, entity2) {
// TODO: We could implement this more efficiently by comparing field by field.
// This has similar issues as above.
return formatContent(entity1).localeCompare(formatContent(entity2));
},
render: entity => (
<EntityRefLink
entityRef={entity}
defaultKind={defaultKind}
title={entity.metadata?.title}
/>
),
};
},
createEntityRelationColumn<T extends Entity>({
title,
relation,
defaultKind,
filter: entityFilter,
}: {
title: string;
relation: string;
defaultKind?: string;
filter?: { kind: string };
}): TableColumn<T> {
function getRelations(entity: T): EntityName[] {
return getEntityRelations(entity, relation, entityFilter);
}
/** @public */
export function createDomainColumn<T extends Entity>(): TableColumn<T> {
return createEntityRelationColumn({
title: 'Domain',
relation: RELATION_PART_OF,
defaultKind: 'domain',
filter: {
kind: 'domain',
},
});
}
function formatContent(entity: T): string {
return getRelations(entity)
.map(r => formatEntityRefTitle(r, { defaultKind }))
.join(', ');
}
/** @public */
export function createSystemColumn<T extends Entity>(): TableColumn<T> {
return createEntityRelationColumn({
title: 'System',
relation: RELATION_PART_OF,
defaultKind: 'system',
filter: {
kind: 'system',
},
});
}
/** @public */
export function createMetadataDescriptionColumn<
T extends Entity,
>(): TableColumn<T> {
return {
title: 'Description',
field: 'metadata.description',
render: entity => (
<OverflowTooltip
text={entity.metadata.description}
placement="bottom-start"
line={2}
/>
),
width: 'auto',
};
}
/** @public */
export function createSpecLifecycleColumn<T extends Entity>(): TableColumn<T> {
return {
title: 'Lifecycle',
field: 'spec.lifecycle',
};
}
/** @public */
export function createSpecTypeColumn<T extends Entity>(): TableColumn<T> {
return {
title: 'Type',
field: 'spec.type',
};
}
return {
title,
customFilterAndSearch(filter, entity) {
return formatContent(entity).includes(filter);
},
customSort(entity1, entity2) {
return formatContent(entity1).localeCompare(formatContent(entity2));
},
render: entity => {
return (
<EntityRefLinks
entityRefs={getRelations(entity)}
defaultKind={defaultKind}
/>
);
},
};
},
createOwnerColumn<T extends Entity>(): TableColumn<T> {
return this.createEntityRelationColumn({
title: 'Owner',
relation: RELATION_OWNED_BY,
defaultKind: 'group',
});
},
createDomainColumn<T extends Entity>(): TableColumn<T> {
return this.createEntityRelationColumn({
title: 'Domain',
relation: RELATION_PART_OF,
defaultKind: 'domain',
filter: {
kind: 'domain',
},
});
},
createSystemColumn<T extends Entity>(): TableColumn<T> {
return this.createEntityRelationColumn({
title: 'System',
relation: RELATION_PART_OF,
defaultKind: 'system',
filter: {
kind: 'system',
},
});
},
createMetadataDescriptionColumn<T extends Entity>(): TableColumn<T> {
return {
title: 'Description',
field: 'metadata.description',
render: entity => (
<OverflowTooltip
text={entity.metadata.description}
placement="bottom-start"
line={2}
/>
),
width: 'auto',
};
},
createSpecLifecycleColumn<T extends Entity>(): TableColumn<T> {
return {
title: 'Lifecycle',
field: 'spec.lifecycle',
};
},
createSpecTypeColumn<T extends Entity>(): TableColumn<T> {
return {
title: 'Type',
field: 'spec.type',
};
},
});
@@ -15,4 +15,5 @@
*/
export { EntityTable } from './EntityTable';
export { columnFactories } from './columns';
export type { EntityTableProps } from './EntityTable';
@@ -15,29 +15,22 @@
*/
import { ComponentEntity, SystemEntity } from '@backstage/catalog-model';
import {
createDomainColumn,
createEntityRefColumn,
createMetadataDescriptionColumn,
createOwnerColumn,
createSpecLifecycleColumn,
createSpecTypeColumn,
createSystemColumn,
} from './columns';
import { columnFactories } from './columns';
import { TableColumn } from '@backstage/core-components';
export const systemEntityColumns: TableColumn<SystemEntity>[] = [
createEntityRefColumn({ defaultKind: 'system' }),
createDomainColumn(),
createOwnerColumn(),
createMetadataDescriptionColumn(),
columnFactories.createEntityRefColumn({ defaultKind: 'system' }),
columnFactories.createDomainColumn(),
columnFactories.createOwnerColumn(),
columnFactories.createMetadataDescriptionColumn(),
];
export const componentEntityColumns: TableColumn<ComponentEntity>[] = [
createEntityRefColumn({ defaultKind: 'component' }),
createSystemColumn(),
createOwnerColumn(),
createSpecTypeColumn(),
createSpecLifecycleColumn(),
createMetadataDescriptionColumn(),
columnFactories.createEntityRefColumn({ defaultKind: 'component' }),
columnFactories.createSystemColumn(),
columnFactories.createOwnerColumn(),
columnFactories.createSpecTypeColumn(),
columnFactories.createSpecLifecycleColumn(),
columnFactories.createMetadataDescriptionColumn(),
];
@@ -21,7 +21,10 @@ import StarBorder from '@material-ui/icons/StarBorder';
import React, { ComponentProps } from 'react';
import { useStarredEntity } from '../../hooks/useStarredEntity';
type Props = ComponentProps<typeof IconButton> & { entity: Entity };
/** @public */
export type FavoriteEntityProps = ComponentProps<typeof IconButton> & {
entity: Entity;
};
const YellowStar = withStyles({
root: {
@@ -29,17 +32,20 @@ const YellowStar = withStyles({
},
})(Star);
/** @public */
export const favoriteEntityTooltip = (isStarred: boolean) =>
isStarred ? 'Remove from favorites' : 'Add to favorites';
/** @public */
export const favoriteEntityIcon = (isStarred: boolean) =>
isStarred ? <YellowStar /> : <StarBorder />;
/**
* IconButton for showing if a current entity is starred and adding/removing it from the favorite entities
* @param props - MaterialUI IconButton props extended by required `entity` prop
* @public
*/
export const FavoriteEntity = (props: Props) => {
export const FavoriteEntity = (props: FavoriteEntityProps) => {
const { toggleStarredEntity, isStarredEntity } = useStarredEntity(
props.entity,
);
@@ -19,3 +19,4 @@ export {
favoriteEntityIcon,
FavoriteEntity,
} from './FavoriteEntity';
export type { FavoriteEntityProps } from './FavoriteEntity';
@@ -41,13 +41,6 @@ const useStyles = makeStyles({
},
});
type Props = {
open: boolean;
onConfirm: () => any;
onClose: () => any;
entity: Entity;
};
const Contents = ({
entity,
onConfirm,
@@ -248,23 +241,30 @@ const Contents = ({
return <Alert severity="error">Internal error: Unknown state</Alert>;
};
export const UnregisterEntityDialog = ({
open,
onConfirm,
onClose,
entity,
}: Props) => (
<Dialog open={open} onClose={onClose}>
<DialogTitle id="responsive-dialog-title">
Are you sure you want to unregister this entity?
</DialogTitle>
<DialogContent>
<Contents entity={entity} onConfirm={onConfirm} />
</DialogContent>
<DialogActions>
<Button onClick={onClose} color="primary">
Cancel
</Button>
</DialogActions>
</Dialog>
);
/** @public */
export type UnregisterEntityDialogProps = {
open: boolean;
onConfirm: () => any;
onClose: () => any;
entity: Entity;
};
/** @public */
export const UnregisterEntityDialog = (props: UnregisterEntityDialogProps) => {
const { open, onConfirm, onClose, entity } = props;
return (
<Dialog open={open} onClose={onClose}>
<DialogTitle id="responsive-dialog-title">
Are you sure you want to unregister this entity?
</DialogTitle>
<DialogContent>
<Contents entity={entity} onConfirm={onConfirm} />
</DialogContent>
<DialogActions>
<Button onClick={onClose} color="primary">
Cancel
</Button>
</DialogActions>
</Dialog>
);
};
@@ -15,3 +15,4 @@
*/
export { UnregisterEntityDialog } from './UnregisterEntityDialog';
export type { UnregisterEntityDialogProps } from './UnregisterEntityDialog';
@@ -118,15 +118,15 @@ function getFilterGroups(orgName: string | undefined): ButtonGroup[] {
];
}
type UserListPickerProps = {
/** @public */
export type UserListPickerProps = {
initialFilter?: UserListFilterKind;
availableFilters?: UserListFilterKind[];
};
export const UserListPicker = ({
initialFilter,
availableFilters,
}: UserListPickerProps) => {
/** @public */
export const UserListPicker = (props: UserListPickerProps) => {
const { initialFilter, availableFilters } = props;
const classes = useStyles();
const configApi = useApi(configApiRef);
const orgName = configApi.getOptionalString('organization.name') ?? 'Company';
@@ -15,4 +15,5 @@
*/
export { UserListPicker } from './UserListPicker';
export type { UserListPickerProps } from './UserListPicker';
export type { CatalogReactUserListPickerClassKey } from './UserListPicker';
+28
View File
@@ -19,6 +19,10 @@ import { formatEntityRefTitle } from './components/EntityRefLink';
import { EntityFilter, UserListFilterKind } from './types';
import { getEntityRelations } from './utils';
/**
* Filter entities based on Kind.
* @public
*/
export class EntityKindFilter implements EntityFilter {
constructor(readonly value: string) {}
@@ -31,6 +35,10 @@ export class EntityKindFilter implements EntityFilter {
}
}
/**
* Filters entities based on type
* @public
*/
export class EntityTypeFilter implements EntityFilter {
constructor(readonly value: string | string[]) {}
@@ -48,6 +56,10 @@ export class EntityTypeFilter implements EntityFilter {
}
}
/**
* Filters entities based on tag.
* @public
*/
export class EntityTagFilter implements EntityFilter {
constructor(readonly values: string[]) {}
@@ -60,6 +72,10 @@ export class EntityTagFilter implements EntityFilter {
}
}
/**
* Filters entities where the text matches spec, title or tags.
* @public
*/
export class EntityTextFilter implements EntityFilter {
constructor(readonly value: string) {}
@@ -81,6 +97,10 @@ export class EntityTextFilter implements EntityFilter {
}
}
/**
* Filter matching entities that are owned by group.
* @public
*/
export class EntityOwnerFilter implements EntityFilter {
constructor(readonly values: string[]) {}
@@ -97,6 +117,10 @@ export class EntityOwnerFilter implements EntityFilter {
}
}
/**
* Filters entities on lifecycle.
* @public
*/
export class EntityLifecycleFilter implements EntityFilter {
constructor(readonly values: string[]) {}
@@ -109,6 +133,10 @@ export class EntityLifecycleFilter implements EntityFilter {
}
}
/**
* Filters entities based on whatever the user has starred or owns them.
* @public
*/
export class UserListFilter implements EntityFilter {
constructor(
readonly value: UserListFilterKind,
+6 -1
View File
@@ -21,6 +21,7 @@ export {
AsyncEntityProvider,
} from './useEntity';
export type {
EntityLoadingStatus,
EntityProviderProps,
AsyncEntityProviderProps,
} from './useEntity';
@@ -30,8 +31,12 @@ export {
EntityListProvider,
useEntityListProvider,
} from './useEntityListProvider';
export type { DefaultEntityFilters } from './useEntityListProvider';
export type {
DefaultEntityFilters,
EntityListContextProps,
} from './useEntityListProvider';
export { useEntityTypeFilter } from './useEntityTypeFilter';
export type { EntityTypeReturn } from './useEntityTypeFilter';
export { useEntityKinds } from './useEntityKinds';
export { useOwnUser } from './useOwnUser';
export { useRelatedEntities } from './useRelatedEntities';
@@ -32,7 +32,8 @@ import useAsyncRetry from 'react-use/lib/useAsyncRetry';
import { catalogApiRef } from '../api';
import { useEntityCompoundName } from './useEntityCompoundName';
type EntityLoadingStatus = {
/** @public */
export type EntityLoadingStatus = {
entity?: Entity;
loading: boolean;
error?: Error;
@@ -133,6 +134,7 @@ const CompatibilityProvider = ({
};
EntityContext.Provider = CompatibilityProvider as Provider<EntityLoadingStatus>;
/** @public */
export const useEntityFromUrl = (): EntityLoadingStatus => {
const { kind, namespace, name } = useEntityCompoundName();
const navigate = useNavigate();
@@ -18,6 +18,7 @@ import { useRouteRefParams } from '@backstage/core-plugin-api';
/**
* Grabs entity kind, namespace, and name from the location
* @public
*/
export const useEntityCompoundName = () => {
const { kind, namespace, name } = useRouteRefParams(entityRouteRef);
@@ -18,7 +18,10 @@ import useAsync from 'react-use/lib/useAsync';
import { useApi } from '@backstage/core-plugin-api';
import { catalogApiRef } from '../api';
// Retrieve a list of unique entity kinds present in the catalog
/**
* Retrieve a list of unique entity kinds present in the catalog
* @public
*/
export function useEntityKinds() {
const catalogApi = useApi(catalogApiRef);
@@ -43,6 +43,7 @@ import { EntityFilter } from '../types';
import { reduceCatalogFilters, reduceEntityFilters } from '../utils';
import { useApi } from '@backstage/core-plugin-api';
/** @public */
export type DefaultEntityFilters = {
kind?: EntityKindFilter;
type?: EntityTypeFilter;
@@ -53,6 +54,7 @@ export type DefaultEntityFilters = {
text?: EntityTextFilter;
};
/** @public */
export type EntityListContextProps<
EntityFilters extends DefaultEntityFilters = DefaultEntityFilters,
> = {
@@ -91,6 +93,10 @@ export type EntityListContextProps<
error?: Error;
};
/**
* Creates new context for entity listing and filtering.
* @public
*/
export const EntityListContext = createContext<
EntityListContextProps<any> | undefined
>(undefined);
@@ -101,6 +107,10 @@ type OutputState<EntityFilters extends DefaultEntityFilters> = {
backendEntities: Entity[];
};
/**
* Provides entities and filters for a catalog listing.
* @public
*/
export const EntityListProvider = <EntityFilters extends DefaultEntityFilters>({
children,
}: PropsWithChildren<{}>) => {
@@ -239,6 +249,10 @@ export const EntityListProvider = <EntityFilters extends DefaultEntityFilters>({
);
};
/**
* Hook for interacting with the entity list context provided by the {@link EntityListProvider}.
* @public
*/
export function useEntityListProvider<
EntityFilters extends DefaultEntityFilters = DefaultEntityFilters,
>(): EntityListContextProps<EntityFilters> {
@@ -22,7 +22,8 @@ import { catalogApiRef } from '../api';
import { useEntityListProvider } from './useEntityListProvider';
import { EntityTypeFilter } from '../filters';
type EntityTypeReturn = {
/** @public */
export type EntityTypeReturn = {
loading: boolean;
error?: Error;
availableTypes: string[];
@@ -33,6 +34,7 @@ type EntityTypeReturn = {
/**
* A hook built on top of `useEntityListProvider` for enabling selection of valid `spec.type` values
* based on the selected EntityKindFilter.
* @public
*/
export function useEntityTypeFilter(): EntityTypeReturn {
const catalogApi = useApi(catalogApiRef);
@@ -25,6 +25,7 @@ import { identityApiRef, useApi } from '@backstage/core-plugin-api';
/**
* Get the catalog User entity (if any) that matches the logged-in user.
* @public
*/
export function useOwnUser(): AsyncState<UserEntity | undefined> {
const catalogApi = useApi(catalogApiRef);
@@ -21,6 +21,7 @@ import { catalogApiRef } from '../api';
const BATCH_SIZE = 20;
/** @public */
export function useRelatedEntities(
entity: Entity,
{ type, kind }: { type?: string; kind?: string },
@@ -30,6 +30,7 @@ function getEntityRef(entityOrRef: Entity | EntityName | string): string {
: stringifyEntityRef(entityOrRef);
}
/** @public */
export function useStarredEntities(): {
starredEntities: Set<string>;
toggleStarredEntity: (entityOrRef: Entity | EntityName | string) => void;
@@ -29,6 +29,7 @@ function getEntityRef(entityOrRef: Entity | EntityName | string): string {
: stringifyEntityRef(entityOrRef);
}
/** @public */
export function useStarredEntity(entityOrRef: Entity | EntityName | string): {
toggleStarredEntity: () => void;
isStarredEntity: boolean;
+8 -2
View File
@@ -21,6 +21,7 @@ import { getOrCreateGlobalSingleton } from '@backstage/version-bridge';
// TODO(Rugvip): Move these route refs back to the catalog plugin once we're all ported to using external routes
/**
* @deprecated Use an `ExternalRouteRef` instead, which can point to `catalogPlugin.routes.catalogIndex`.
* @public
*/
export const rootRoute = createRouteRef({
id: 'catalog',
@@ -28,6 +29,7 @@ export const rootRoute = createRouteRef({
/**
* @deprecated Use an `ExternalRouteRef` instead, which can point to `catalogPlugin.routes.catalogIndex`.
* @public
*/
export const catalogRouteRef = rootRoute;
@@ -39,6 +41,7 @@ export const catalogRouteRef = rootRoute;
*
* If you want to replace the `EntityPage` from `@backstage/catalog-plugin` in your app,
* you need to use the `entityRouteRef` as the mount point instead of your own.
* @public
*/
export const entityRouteRef = getOrCreateGlobalSingleton(
'catalog:entity-route-ref',
@@ -51,11 +54,14 @@ export const entityRouteRef = getOrCreateGlobalSingleton(
/**
* @deprecated use `entityRouteRef` instead.
* @public
*/
export const entityRoute = entityRouteRef;
// Utility function to get suitable route params for entityRoute, given an
// entity instance
/**
* Utility function to get suitable route params for entityRoute, given an
* @public
*/
export function entityRouteParams(entity: Entity) {
return {
kind: entity.kind.toLocaleLowerCase('en-US'),
@@ -26,6 +26,7 @@ import {
EntityListContextProps,
} from '../hooks/useEntityListProvider';
/** @public */
export const MockEntityListContextProvider = ({
children,
value,
+2
View File
@@ -16,6 +16,7 @@
import { Entity } from '@backstage/catalog-model';
/** @public */
export type EntityFilter = {
/**
* Get filters to add to the catalog-backend request. These are a dot-delimited field with
@@ -43,4 +44,5 @@ export type EntityFilter = {
toQueryValue?: () => string | string[];
};
/** @public */
export type UserListFilterKind = 'owned' | 'starred' | 'all';
@@ -17,6 +17,7 @@
import { Entity } from '@backstage/catalog-model';
import { EntityFilter } from '../types';
/** @public */
export function reduceCatalogFilters(
filters: EntityFilter[],
): Record<string, string | symbol | (string | symbol)[]> {
@@ -28,6 +29,7 @@ export function reduceCatalogFilters(
}, {} as Record<string, string | symbol | (string | symbol)[]>);
}
/** @public */
export function reduceEntityFilters(
filters: EntityFilter[],
): (entity: Entity) => boolean {
@@ -20,10 +20,12 @@ import {
Entity,
} from '@backstage/catalog-model';
/** @public */
export function getEntityMetadataViewUrl(entity: Entity): string | undefined {
return entity.metadata.annotations?.[ANNOTATION_VIEW_URL];
}
/** @public */
export function getEntityMetadataEditUrl(entity: Entity): string | undefined {
return entity.metadata.annotations?.[ANNOTATION_EDIT_URL];
}
@@ -18,6 +18,7 @@ import { Entity, EntityName } from '@backstage/catalog-model';
/**
* Get the related entity references.
* @public
*/
export function getEntityRelations(
entity: Entity | undefined,
@@ -21,11 +21,13 @@ import {
} from '@backstage/catalog-model';
import { ScmIntegrationRegistry } from '@backstage/integration';
/** @public */
export type EntitySourceLocation = {
locationTargetUrl: string;
integrationType?: string;
};
/** @public */
export function getEntitySourceLocation(
entity: Entity,
scmIntegrationsApi: ScmIntegrationRegistry,
@@ -25,6 +25,7 @@ import { getEntityRelations } from './getEntityRelations';
/**
* Get the related entity references.
* @public
*/
export function isOwnerOf(owner: Entity, owned: Entity) {
const possibleOwners = new Set(
+1
View File
@@ -223,6 +223,7 @@ const NO_WARNING_PACKAGES = [
'plugins/catalog-backend-module-msgraph',
'plugins/catalog-common',
'plugins/catalog-graph',
'plugins/catalog-react',
'plugins/permission-backend',
'plugins/permission-common',
'plugins/permission-node',