@backstage/plugin-catalog-react 0.0.0-nightly-2021812202 → 0.0.0-nightly-202181722143
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -4
- package/dist/index.cjs.js +52 -10
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +46 -14
- package/dist/index.esm.js +53 -12
- package/dist/index.esm.js.map +1 -1
- package/package.json +11 -9
package/CHANGELOG.md
CHANGED
|
@@ -1,15 +1,34 @@
|
|
|
1
1
|
# @backstage/plugin-catalog-react
|
|
2
2
|
|
|
3
|
-
## 0.0.0-nightly-
|
|
3
|
+
## 0.0.0-nightly-202181722143
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- cc464a56b3: This makes Type and Lifecycle columns consistent for all table cases and adds a new line in Description column for better readability
|
|
8
|
+
|
|
9
|
+
## 0.4.6
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 9f1362dcc1: Upgrade `@material-ui/lab` to `4.0.0-alpha.57`.
|
|
14
|
+
- ea81a1f19c: Deprecated EntityContext in favor of using `useEntity`, `EntityProvider` and the new `AsyncEntityProvider` instead. This update also brings cross-version compatibility to `@backstage/catalog-react`, meaning that future versions can be used in parallel with this one.
|
|
15
|
+
- Updated dependencies
|
|
16
|
+
- @backstage/core-components@0.4.2
|
|
17
|
+
- @backstage/integration@0.6.4
|
|
18
|
+
- @backstage/core-app-api@0.1.13
|
|
19
|
+
- @backstage/core-plugin-api@0.1.8
|
|
20
|
+
|
|
21
|
+
## 0.4.5
|
|
4
22
|
|
|
5
23
|
### Patch Changes
|
|
6
24
|
|
|
7
25
|
- 3ed78fca3: Added a `useEntityKinds` hook to load a unique list of entity kinds from the catalog.
|
|
8
26
|
Fixed a bug in `EntityTypePicker` where the component did not hide when no types were available in returned entities.
|
|
9
27
|
- Updated dependencies
|
|
10
|
-
- @backstage/
|
|
11
|
-
- @backstage/
|
|
12
|
-
- @backstage/
|
|
28
|
+
- @backstage/integration@0.6.3
|
|
29
|
+
- @backstage/core-components@0.4.0
|
|
30
|
+
- @backstage/catalog-model@0.9.1
|
|
31
|
+
- @backstage/core-app-api@0.1.11
|
|
13
32
|
|
|
14
33
|
## 0.4.4
|
|
15
34
|
|
package/dist/index.cjs.js
CHANGED
|
@@ -6,6 +6,7 @@ var catalogClient = require('@backstage/catalog-client');
|
|
|
6
6
|
var corePluginApi = require('@backstage/core-plugin-api');
|
|
7
7
|
var React = require('react');
|
|
8
8
|
var lab = require('@material-ui/lab');
|
|
9
|
+
var versionBridge = require('@backstage/version-bridge');
|
|
9
10
|
var reactRouter = require('react-router');
|
|
10
11
|
var reactUse = require('react-use');
|
|
11
12
|
var catalogModel = require('@backstage/catalog-model');
|
|
@@ -80,6 +81,39 @@ const EntityContext = React.createContext({
|
|
|
80
81
|
refresh: () => {
|
|
81
82
|
}
|
|
82
83
|
});
|
|
84
|
+
const OldEntityProvider = EntityContext.Provider;
|
|
85
|
+
const NewEntityContext = versionBridge.createVersionedContext("entity-context");
|
|
86
|
+
const AsyncEntityProvider = ({
|
|
87
|
+
children,
|
|
88
|
+
entity,
|
|
89
|
+
loading,
|
|
90
|
+
error,
|
|
91
|
+
refresh
|
|
92
|
+
}) => {
|
|
93
|
+
const value = {entity, loading, error, refresh};
|
|
94
|
+
return /* @__PURE__ */ React__default['default'].createElement(OldEntityProvider, {
|
|
95
|
+
value
|
|
96
|
+
}, /* @__PURE__ */ React__default['default'].createElement(NewEntityContext.Provider, {
|
|
97
|
+
value: versionBridge.createVersionedValueMap({1: value})
|
|
98
|
+
}, children));
|
|
99
|
+
};
|
|
100
|
+
const EntityProvider = ({entity, children}) => /* @__PURE__ */ React__default['default'].createElement(AsyncEntityProvider, {
|
|
101
|
+
entity,
|
|
102
|
+
loading: !Boolean(entity),
|
|
103
|
+
error: void 0,
|
|
104
|
+
refresh: void 0,
|
|
105
|
+
children
|
|
106
|
+
});
|
|
107
|
+
const CompatibilityProvider = ({
|
|
108
|
+
value,
|
|
109
|
+
children
|
|
110
|
+
}) => {
|
|
111
|
+
return /* @__PURE__ */ React__default['default'].createElement(AsyncEntityProvider, {
|
|
112
|
+
...value,
|
|
113
|
+
children
|
|
114
|
+
});
|
|
115
|
+
};
|
|
116
|
+
EntityContext.Provider = CompatibilityProvider;
|
|
83
117
|
const useEntityFromUrl = () => {
|
|
84
118
|
const {kind, namespace, name} = useEntityCompoundName();
|
|
85
119
|
const navigate = reactRouter.useNavigate();
|
|
@@ -100,7 +134,21 @@ const useEntityFromUrl = () => {
|
|
|
100
134
|
return {entity, loading, error, refresh};
|
|
101
135
|
};
|
|
102
136
|
function useEntity() {
|
|
103
|
-
const
|
|
137
|
+
const versionedHolder = versionBridge.useVersionedContext("entity-context");
|
|
138
|
+
if (!versionedHolder) {
|
|
139
|
+
return {
|
|
140
|
+
entity: void 0,
|
|
141
|
+
loading: true,
|
|
142
|
+
error: void 0,
|
|
143
|
+
refresh: () => {
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
const value = versionedHolder.atVersion(1);
|
|
148
|
+
if (!value) {
|
|
149
|
+
throw new Error("EntityContext v1 not available");
|
|
150
|
+
}
|
|
151
|
+
const {entity, loading, error, refresh} = value;
|
|
104
152
|
return {entity, loading, error, refresh};
|
|
105
153
|
}
|
|
106
154
|
|
|
@@ -773,14 +821,6 @@ const EntityOwnerPicker = () => {
|
|
|
773
821
|
}));
|
|
774
822
|
};
|
|
775
823
|
|
|
776
|
-
const EntityProvider = ({entity, children}) => /* @__PURE__ */ React__default['default'].createElement(EntityContext.Provider, {
|
|
777
|
-
value: {
|
|
778
|
-
entity,
|
|
779
|
-
loading: !Boolean(entity),
|
|
780
|
-
error: void 0
|
|
781
|
-
}
|
|
782
|
-
}, children);
|
|
783
|
-
|
|
784
824
|
const useStyles$3 = core.makeStyles((_theme) => ({
|
|
785
825
|
searchToolbar: {
|
|
786
826
|
paddingLeft: 0,
|
|
@@ -903,7 +943,8 @@ function createMetadataDescriptionColumn() {
|
|
|
903
943
|
field: "metadata.description",
|
|
904
944
|
render: (entity) => /* @__PURE__ */ React__default['default'].createElement(coreComponents.OverflowTooltip, {
|
|
905
945
|
text: entity.metadata.description,
|
|
906
|
-
placement: "bottom-start"
|
|
946
|
+
placement: "bottom-start",
|
|
947
|
+
line: 2
|
|
907
948
|
}),
|
|
908
949
|
width: "auto"
|
|
909
950
|
};
|
|
@@ -1437,6 +1478,7 @@ Object.defineProperty(exports, 'CATALOG_FILTER_EXISTS', {
|
|
|
1437
1478
|
return catalogClient.CATALOG_FILTER_EXISTS;
|
|
1438
1479
|
}
|
|
1439
1480
|
});
|
|
1481
|
+
exports.AsyncEntityProvider = AsyncEntityProvider;
|
|
1440
1482
|
exports.EntityContext = EntityContext;
|
|
1441
1483
|
exports.EntityKindFilter = EntityKindFilter;
|
|
1442
1484
|
exports.EntityKindPicker = EntityKindPicker;
|
package/dist/index.cjs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs.js","sources":["../src/api.ts","../src/routes.ts","../src/hooks/useEntityCompoundName.ts","../src/hooks/useEntity.ts","../src/utils/filters.ts","../src/utils/getEntityMetadataUrl.ts","../src/utils/getEntityRelations.ts","../src/utils/getEntitySourceLocation.ts","../src/utils/isOwnerOf.ts","../src/hooks/useEntityListProvider.tsx","../src/components/EntityRefLink/format.ts","../src/components/EntityRefLink/EntityRefLink.tsx","../src/components/EntityRefLink/EntityRefLinks.tsx","../src/filters.ts","../src/hooks/useEntityTypeFilter.tsx","../src/hooks/useEntityKinds.ts","../src/hooks/useOwnUser.ts","../src/hooks/useRelatedEntities.ts","../src/hooks/useStarredEntities.ts","../src/hooks/useEntityOwnership.ts","../src/components/EntityKindPicker/EntityKindPicker.tsx","../src/components/EntityLifecyclePicker/EntityLifecyclePicker.tsx","../src/components/EntityOwnerPicker/EntityOwnerPicker.tsx","../src/components/EntityProvider/EntityProvider.tsx","../src/components/EntitySearchBar/EntitySearchBar.tsx","../src/components/EntityTable/columns.tsx","../src/components/EntityTable/presets.tsx","../src/components/EntityTable/EntityTable.tsx","../src/components/EntityTagPicker/EntityTagPicker.tsx","../src/components/EntityTypePicker/EntityTypePicker.tsx","../src/components/FavoriteEntity/FavoriteEntity.tsx","../src/components/UnregisterEntityDialog/useUnregisterEntityDialogState.ts","../src/components/UnregisterEntityDialog/UnregisterEntityDialog.tsx","../src/components/UserListPicker/UserListPicker.tsx","../src/testUtils/providers.tsx"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { CatalogApi } from '@backstage/catalog-client';\nimport { createApiRef } from '@backstage/core-plugin-api';\n\nexport const catalogApiRef = createApiRef<CatalogApi>({\n id: 'plugin.catalog.service',\n});\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity, ENTITY_DEFAULT_NAMESPACE } from '@backstage/catalog-model';\nimport { createRouteRef } from '@backstage/core-plugin-api';\n\nconst NoIcon = () => null;\n\n// TODO(Rugvip): Move these route refs back to the catalog plugin once we're all ported to using external routes\nexport const rootRoute = createRouteRef({\n icon: NoIcon,\n path: '',\n title: 'Catalog',\n});\nexport const catalogRouteRef = rootRoute;\n\nexport const entityRoute = createRouteRef({\n icon: NoIcon,\n path: ':namespace/:kind/:name/*',\n title: 'Entity',\n params: ['namespace', 'kind', 'name'],\n});\nexport const entityRouteRef = entityRoute;\n\n// Utility function to get suitable route params for entityRoute, given an\n// entity instance\nexport function entityRouteParams(entity: Entity) {\n return {\n kind: entity.kind.toLowerCase(),\n namespace:\n entity.metadata.namespace?.toLowerCase() ?? ENTITY_DEFAULT_NAMESPACE,\n name: entity.metadata.name,\n } as const;\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { entityRouteRef } from '../routes';\nimport { useRouteRefParams } from '@backstage/core-plugin-api';\n\n/**\n * Grabs entity kind, namespace, and name from the location\n */\nexport const useEntityCompoundName = () => {\n const { kind, namespace, name } = useRouteRefParams(entityRouteRef);\n return { kind, namespace, name };\n};\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { Entity } from '@backstage/catalog-model';\nimport { errorApiRef, useApi } from '@backstage/core-plugin-api';\nimport { createContext, useContext, useEffect } from 'react';\nimport { useNavigate } from 'react-router';\nimport { useAsyncRetry } from 'react-use';\nimport { catalogApiRef } from '../api';\nimport { useEntityCompoundName } from './useEntityCompoundName';\n\ntype EntityLoadingStatus = {\n entity?: Entity;\n loading: boolean;\n error?: Error;\n refresh?: VoidFunction;\n};\n\nexport const EntityContext = createContext<EntityLoadingStatus>({\n entity: undefined,\n loading: true,\n error: undefined,\n refresh: () => {},\n});\n\nexport const useEntityFromUrl = (): EntityLoadingStatus => {\n const { kind, namespace, name } = useEntityCompoundName();\n const navigate = useNavigate();\n const errorApi = useApi(errorApiRef);\n const catalogApi = useApi(catalogApiRef);\n\n const {\n value: entity,\n error,\n loading,\n retry: refresh,\n } = useAsyncRetry(\n () => catalogApi.getEntityByName({ kind, namespace, name }),\n [catalogApi, kind, namespace, name],\n );\n\n useEffect(() => {\n if (!name) {\n errorApi.post(new Error('No name provided!'));\n navigate('/');\n }\n }, [errorApi, navigate, error, loading, entity, name]);\n\n return { entity, loading, error, refresh };\n};\n\n/**\n * Grab the current entity from the context and its current loading state.\n */\nexport function useEntity<T extends Entity = Entity>() {\n const { entity, loading, error, refresh } = useContext(EntityContext);\n return { entity: entity as T, loading, error, refresh };\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity } from '@backstage/catalog-model';\nimport { EntityFilter } from '../types';\n\nexport function reduceCatalogFilters(\n filters: EntityFilter[],\n): Record<string, string | symbol | (string | symbol)[]> {\n return filters.reduce((compoundFilter, filter) => {\n return {\n ...compoundFilter,\n ...(filter.getCatalogFilters ? filter.getCatalogFilters() : {}),\n };\n }, {} as Record<string, string | symbol | (string | symbol)[]>);\n}\n\nexport function reduceEntityFilters(\n filters: EntityFilter[],\n): (entity: Entity) => boolean {\n return (entity: Entity) =>\n filters.every(\n filter => !filter.filterEntity || filter.filterEntity(entity),\n );\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n EDIT_URL_ANNOTATION,\n Entity,\n VIEW_URL_ANNOTATION,\n} from '@backstage/catalog-model';\n\nexport function getEntityMetadataViewUrl(entity: Entity): string | undefined {\n return entity.metadata.annotations?.[VIEW_URL_ANNOTATION];\n}\n\nexport function getEntityMetadataEditUrl(entity: Entity): string | undefined {\n return entity.metadata.annotations?.[EDIT_URL_ANNOTATION];\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity, EntityName } from '@backstage/catalog-model';\n\n/**\n * Get the related entity references.\n */\nexport function getEntityRelations(\n entity: Entity | undefined,\n relationType: string,\n filter?: { kind: string },\n): EntityName[] {\n let entityNames =\n entity?.relations\n ?.filter(r => r.type === relationType)\n ?.map(r => r.target) || [];\n\n if (filter?.kind) {\n entityNames = entityNames?.filter(\n e => e.kind.toLowerCase() === filter.kind.toLowerCase(),\n );\n }\n\n return entityNames;\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n Entity,\n parseLocationReference,\n SOURCE_LOCATION_ANNOTATION,\n} from '@backstage/catalog-model';\nimport { ScmIntegrationRegistry } from '@backstage/integration';\n\nexport type EntitySourceLocation = {\n locationTargetUrl: string;\n integrationType?: string;\n};\n\nexport function getEntitySourceLocation(\n entity: Entity,\n scmIntegrationsApi: ScmIntegrationRegistry,\n): EntitySourceLocation | undefined {\n const sourceLocation =\n entity.metadata.annotations?.[SOURCE_LOCATION_ANNOTATION];\n\n if (!sourceLocation) {\n return undefined;\n }\n\n try {\n const sourceLocationRef = parseLocationReference(sourceLocation);\n const integration = scmIntegrationsApi.byUrl(sourceLocationRef.target);\n return {\n locationTargetUrl: sourceLocationRef.target,\n integrationType: integration?.type,\n };\n } catch {\n return undefined;\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n Entity,\n getEntityName,\n RELATION_MEMBER_OF,\n RELATION_OWNED_BY,\n stringifyEntityRef,\n} from '@backstage/catalog-model';\nimport { getEntityRelations } from './getEntityRelations';\n\n/**\n * Get the related entity references.\n */\nexport function isOwnerOf(owner: Entity, owned: Entity) {\n const possibleOwners = new Set(\n [\n ...getEntityRelations(owner, RELATION_MEMBER_OF, { kind: 'group' }),\n ...(owner ? [getEntityName(owner)] : []),\n ].map(stringifyEntityRef),\n );\n\n const owners = getEntityRelations(owned, RELATION_OWNED_BY).map(\n stringifyEntityRef,\n );\n\n for (const ownerItem of owners) {\n if (possibleOwners.has(ownerItem)) {\n return true;\n }\n }\n\n return false;\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity } from '@backstage/catalog-model';\nimport { compact, isEqual } from 'lodash';\nimport qs from 'qs';\nimport React, {\n createContext,\n PropsWithChildren,\n useCallback,\n useContext,\n useMemo,\n useState,\n} from 'react';\nimport { useAsyncFn, useDebounce, useMountedState } from 'react-use';\nimport { catalogApiRef } from '../api';\nimport {\n EntityKindFilter,\n EntityLifecycleFilter,\n EntityOwnerFilter,\n EntityTagFilter,\n EntityTextFilter,\n EntityTypeFilter,\n UserListFilter,\n} from '../filters';\nimport { EntityFilter } from '../types';\nimport { reduceCatalogFilters, reduceEntityFilters } from '../utils';\nimport { useApi } from '@backstage/core-plugin-api';\n\nexport type DefaultEntityFilters = {\n kind?: EntityKindFilter;\n type?: EntityTypeFilter;\n user?: UserListFilter;\n owners?: EntityOwnerFilter;\n lifecycles?: EntityLifecycleFilter;\n tags?: EntityTagFilter;\n text?: EntityTextFilter;\n};\n\nexport type EntityListContextProps<\n EntityFilters extends DefaultEntityFilters = DefaultEntityFilters,\n> = {\n /**\n * The currently registered filters, adhering to the shape of DefaultEntityFilters or an extension\n * of that default (to add custom filter types).\n */\n filters: EntityFilters;\n\n /**\n * The resolved list of catalog entities, after all filters are applied.\n */\n entities: Entity[];\n\n /**\n * The resolved list of catalog entities, after _only catalog-backend_ filters are applied.\n */\n backendEntities: Entity[];\n\n /**\n * Update one or more of the registered filters. Optional filters can be set to `undefined` to\n * reset the filter.\n */\n updateFilters: (\n filters:\n | Partial<EntityFilters>\n | ((prevFilters: EntityFilters) => Partial<EntityFilters>),\n ) => void;\n\n /**\n * Filter values from query parameters.\n */\n queryParameters: Partial<Record<keyof EntityFilters, string | string[]>>;\n\n loading: boolean;\n error?: Error;\n};\n\nexport const EntityListContext = createContext<\n EntityListContextProps<any> | undefined\n>(undefined);\n\ntype OutputState<EntityFilters extends DefaultEntityFilters> = {\n appliedFilters: EntityFilters;\n entities: Entity[];\n backendEntities: Entity[];\n queryParameters: Record<string, string | string[]>;\n};\n\nexport const EntityListProvider = <EntityFilters extends DefaultEntityFilters>({\n children,\n}: PropsWithChildren<{}>) => {\n const isMounted = useMountedState();\n const catalogApi = useApi(catalogApiRef);\n const [requestedFilters, setRequestedFilters] = useState<EntityFilters>(\n {} as EntityFilters,\n );\n const [outputState, setOutputState] = useState<OutputState<EntityFilters>>(\n () => {\n const query = qs.parse(window.location.search, {\n ignoreQueryPrefix: true,\n });\n return {\n appliedFilters: {} as EntityFilters,\n entities: [],\n backendEntities: [],\n queryParameters: (query.filters ?? {}) as Record<\n string,\n string | string[]\n >,\n };\n },\n );\n\n // The main async filter worker. Note that while it has a lot of dependencies\n // in terms of its implementation, the triggering only happens (debounced)\n // based on the requested filters changing.\n const [{ loading, error }, refresh] = useAsyncFn(\n async () => {\n const compacted = compact(Object.values(requestedFilters));\n const entityFilter = reduceEntityFilters(compacted);\n const backendFilter = reduceCatalogFilters(compacted);\n const previousBackendFilter = reduceCatalogFilters(\n compact(Object.values(outputState.appliedFilters)),\n );\n\n const queryParams = Object.keys(requestedFilters).reduce(\n (params, key) => {\n const filter: EntityFilter | undefined =\n requestedFilters[key as keyof EntityFilters];\n if (filter?.toQueryValue) {\n params[key] = filter.toQueryValue();\n }\n return params;\n },\n {} as Record<string, string | string[]>,\n );\n\n // TODO(mtlewis): currently entities will never be requested unless\n // there's at least one filter, we should allow an initial request\n // to happen with no filters.\n if (!isEqual(previousBackendFilter, backendFilter)) {\n // TODO(timbonicus): should limit fields here, but would need filter\n // fields + table columns\n const response = await catalogApi.getEntities({\n filter: backendFilter,\n });\n setOutputState({\n appliedFilters: requestedFilters,\n backendEntities: response.items,\n entities: response.items.filter(entityFilter),\n queryParameters: queryParams,\n });\n } else {\n setOutputState({\n appliedFilters: requestedFilters,\n backendEntities: outputState.backendEntities,\n entities: outputState.backendEntities.filter(entityFilter),\n queryParameters: queryParams,\n });\n }\n\n if (isMounted()) {\n const oldParams = qs.parse(window.location.search, {\n ignoreQueryPrefix: true,\n });\n const newParams = qs.stringify(\n { ...oldParams, filters: queryParams },\n { addQueryPrefix: true },\n );\n const newUrl = `${window.location.pathname}${newParams}`;\n // We use direct history manipulation since useSearchParams and\n // useNavigate in react-router-dom cause unnecessary extra rerenders.\n // Also make sure to replace the state rather than pushing, since we\n // don't want there to be back/forward slots for every single filter\n // change.\n window.history?.replaceState(null, document.title, newUrl);\n }\n },\n [catalogApi, requestedFilters, outputState],\n { loading: true },\n );\n\n // Slight debounce on the refresh, since (especially on page load) several\n // filters will be calling this in rapid succession.\n useDebounce(refresh, 10, [requestedFilters]);\n\n const updateFilters = useCallback(\n (\n update:\n | Partial<EntityFilter>\n | ((prevFilters: EntityFilters) => Partial<EntityFilters>),\n ) => {\n setRequestedFilters(prevFilters => {\n const newFilters =\n typeof update === 'function' ? update(prevFilters) : update;\n return { ...prevFilters, ...newFilters };\n });\n },\n [],\n );\n\n const value = useMemo(\n () => ({\n filters: outputState.appliedFilters,\n entities: outputState.entities,\n backendEntities: outputState.backendEntities,\n updateFilters,\n queryParameters: outputState.queryParameters,\n loading,\n error,\n }),\n [outputState, updateFilters, loading, error],\n );\n\n return (\n <EntityListContext.Provider value={value}>\n {children}\n </EntityListContext.Provider>\n );\n};\n\nexport function useEntityListProvider<\n EntityFilters extends DefaultEntityFilters = DefaultEntityFilters,\n>(): EntityListContextProps<EntityFilters> {\n const context = useContext(EntityListContext);\n if (!context)\n throw new Error(\n 'useEntityListProvider must be used within EntityListProvider',\n );\n return context;\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n Entity,\n EntityName,\n ENTITY_DEFAULT_NAMESPACE,\n serializeEntityRef,\n} from '@backstage/catalog-model';\n\nexport function formatEntityRefTitle(\n entityRef: Entity | EntityName,\n opts?: { defaultKind?: string },\n) {\n const defaultKind = opts?.defaultKind;\n let kind;\n let namespace;\n let name;\n\n if ('metadata' in entityRef) {\n kind = entityRef.kind;\n namespace = entityRef.metadata.namespace;\n name = entityRef.metadata.name;\n } else {\n kind = entityRef.kind;\n namespace = entityRef.namespace;\n name = entityRef.name;\n }\n\n if (namespace === ENTITY_DEFAULT_NAMESPACE) {\n namespace = undefined;\n }\n\n kind = kind.toLowerCase();\n\n return `${serializeEntityRef({\n kind: defaultKind && defaultKind.toLowerCase() === kind ? undefined : kind,\n name,\n namespace,\n })}`;\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {\n Entity,\n EntityName,\n ENTITY_DEFAULT_NAMESPACE,\n} from '@backstage/catalog-model';\nimport React, { forwardRef } from 'react';\nimport { generatePath } from 'react-router';\nimport { entityRoute } from '../../routes';\nimport { formatEntityRefTitle } from './format';\nimport { Link, LinkProps } from '@backstage/core-components';\n\nexport type EntityRefLinkProps = {\n entityRef: Entity | EntityName;\n defaultKind?: string;\n children?: React.ReactNode;\n} & Omit<LinkProps, 'to'>;\n\nexport const EntityRefLink = forwardRef<any, EntityRefLinkProps>(\n (props, ref) => {\n const { entityRef, defaultKind, children, ...linkProps } = props;\n\n let kind;\n let namespace;\n let name;\n\n if ('metadata' in entityRef) {\n kind = entityRef.kind;\n namespace = entityRef.metadata.namespace;\n name = entityRef.metadata.name;\n } else {\n kind = entityRef.kind;\n namespace = entityRef.namespace;\n name = entityRef.name;\n }\n\n kind = kind.toLocaleLowerCase('en-US');\n\n const routeParams = {\n kind,\n namespace:\n namespace?.toLocaleLowerCase('en-US') ?? ENTITY_DEFAULT_NAMESPACE,\n name,\n };\n\n // TODO: Use useRouteRef here to generate the path\n return (\n <Link\n {...linkProps}\n ref={ref}\n to={generatePath(`/catalog/${entityRoute.path}`, routeParams)}\n >\n {children}\n {!children && formatEntityRefTitle(entityRef, { defaultKind })}\n </Link>\n );\n },\n);\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { Entity, EntityName } from '@backstage/catalog-model';\nimport React from 'react';\nimport { EntityRefLink } from './EntityRefLink';\nimport { LinkProps } from '@backstage/core-components';\n\nexport type EntityRefLinksProps = {\n entityRefs: (Entity | EntityName)[];\n defaultKind?: string;\n} & Omit<LinkProps, 'to'>;\n\nexport const EntityRefLinks = ({\n entityRefs,\n defaultKind,\n ...linkProps\n}: EntityRefLinksProps) => (\n <>\n {entityRefs.map((r, i) => (\n <React.Fragment key={i}>\n {i > 0 && ', '}\n <EntityRefLink {...linkProps} entityRef={r} defaultKind={defaultKind} />\n </React.Fragment>\n ))}\n </>\n);\n","/*\n * Copyright 2021 Spotify AB\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity, RELATION_OWNED_BY } from '@backstage/catalog-model';\nimport { formatEntityRefTitle } from './components/EntityRefLink';\nimport { EntityFilter, UserListFilterKind } from './types';\nimport { getEntityRelations } from './utils';\n\nexport class EntityKindFilter implements EntityFilter {\n constructor(readonly value: string) {}\n\n getCatalogFilters(): Record<string, string | string[]> {\n return { kind: this.value };\n }\n\n toQueryValue(): string {\n return this.value;\n }\n}\n\nexport class EntityTypeFilter implements EntityFilter {\n constructor(readonly value: string | string[]) {}\n\n // Simplify `string | string[]` for consumers, always returns an array\n getTypes(): string[] {\n return Array.isArray(this.value) ? this.value : [this.value];\n }\n\n getCatalogFilters(): Record<string, string | string[]> {\n return { 'spec.type': this.getTypes() };\n }\n\n toQueryValue(): string[] {\n return this.getTypes();\n }\n}\n\nexport class EntityTagFilter implements EntityFilter {\n constructor(readonly values: string[]) {}\n\n filterEntity(entity: Entity): boolean {\n return this.values.every(v => (entity.metadata.tags ?? []).includes(v));\n }\n\n toQueryValue(): string[] {\n return this.values;\n }\n}\n\nexport class EntityTextFilter implements EntityFilter {\n constructor(readonly value: string) {}\n\n filterEntity(entity: Entity): boolean {\n const upperCaseValue = this.value.toLocaleUpperCase('en-US');\n\n return (\n entity.metadata.name\n .toLocaleUpperCase('en-US')\n .includes(upperCaseValue) ||\n `${entity.metadata.title}`\n .toLocaleUpperCase('en-US')\n .includes(upperCaseValue) ||\n entity.metadata.tags\n ?.join('')\n .toLocaleUpperCase('en-US')\n .indexOf(upperCaseValue) !== -1\n );\n }\n}\n\nexport class EntityOwnerFilter implements EntityFilter {\n constructor(readonly values: string[]) {}\n\n filterEntity(entity: Entity): boolean {\n return this.values.some(v =>\n getEntityRelations(entity, RELATION_OWNED_BY).some(\n o => formatEntityRefTitle(o, { defaultKind: 'group' }) === v,\n ),\n );\n }\n\n toQueryValue(): string[] {\n return this.values;\n }\n}\n\nexport class EntityLifecycleFilter implements EntityFilter {\n constructor(readonly values: string[]) {}\n\n filterEntity(entity: Entity): boolean {\n return this.values.some(v => entity.spec?.lifecycle === v);\n }\n\n toQueryValue(): string[] {\n return this.values;\n }\n}\n\nexport class UserListFilter implements EntityFilter {\n constructor(\n readonly value: UserListFilterKind,\n readonly isOwnedEntity: (entity: Entity) => boolean,\n readonly isStarredEntity: (entity: Entity) => boolean,\n ) {}\n\n filterEntity(entity: Entity): boolean {\n switch (this.value) {\n case 'owned':\n return this.isOwnedEntity(entity);\n case 'starred':\n return this.isStarredEntity(entity);\n default:\n return true;\n }\n }\n\n toQueryValue(): string {\n return this.value;\n }\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { useEffect, useMemo, useRef, useState } from 'react';\nimport { useAsync } from 'react-use';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { catalogApiRef } from '../api';\nimport { useEntityListProvider } from './useEntityListProvider';\nimport { EntityTypeFilter } from '../filters';\n\ntype EntityTypeReturn = {\n loading: boolean;\n error?: Error;\n availableTypes: string[];\n selectedTypes: string[];\n setSelectedTypes: (types: string[]) => void;\n};\n\n/**\n * A hook built on top of `useEntityListProvider` for enabling selection of valid `spec.type` values\n * based on the selected EntityKindFilter.\n */\nexport function useEntityTypeFilter(): EntityTypeReturn {\n const catalogApi = useApi(catalogApiRef);\n const {\n filters: { kind: kindFilter, type: typeFilter },\n queryParameters,\n updateFilters,\n } = useEntityListProvider();\n\n const queryParamTypes = [queryParameters.type]\n .flat()\n .filter(Boolean) as string[];\n const [selectedTypes, setSelectedTypes] = useState(\n queryParamTypes.length ? queryParamTypes : typeFilter?.getTypes() ?? [],\n );\n\n const [availableTypes, setAvailableTypes] = useState<string[]>([]);\n const kind = useMemo(() => kindFilter?.value, [kindFilter]);\n\n // Load all valid spec.type values straight from the catalogApi, paying attention to only the\n // kind filter for a complete list.\n const {\n error,\n loading,\n value: entities,\n } = useAsync(async () => {\n if (kind) {\n const items = await catalogApi\n .getEntities({\n filter: { kind },\n fields: ['spec.type'],\n })\n .then(response => response.items);\n return items;\n }\n return [];\n }, [kind, catalogApi]);\n\n const entitiesRef = useRef(entities);\n useEffect(() => {\n const oldEntities = entitiesRef.current;\n entitiesRef.current = entities;\n // Delay processing hook until kind and entity load updates have settled to generate list of types;\n // This prevents reseting the type filter due to saved type value from query params not matching the\n // empty set of type values while values are still being loaded; also only run this hook on changes\n // to entities\n if (loading || !kind || oldEntities === entities) {\n return;\n }\n\n // Resolve the unique set of types from returned entities; could be optimized by a new endpoint\n // in the catalog-backend that does this, rather than loading entities with redundant types.\n if (!entities) return;\n\n // Sort by entity count descending, so the most common types appear on top\n const countByType = entities.reduce((acc, entity) => {\n if (typeof entity.spec?.type !== 'string') return acc;\n\n const entityType = entity.spec.type.toLocaleLowerCase('en-US');\n if (!acc[entityType]) {\n acc[entityType] = 0;\n }\n acc[entityType] += 1;\n return acc;\n }, {} as Record<string, number>);\n\n const newTypes = Object.entries(countByType)\n .sort(([, count1], [, count2]) => count2 - count1)\n .map(([type]) => type);\n setAvailableTypes(newTypes);\n\n // Update type filter to only valid values when the list of available types has changed\n const stillValidTypes = selectedTypes.filter(value =>\n newTypes.includes(value),\n );\n setSelectedTypes(stillValidTypes);\n }, [loading, kind, selectedTypes, setSelectedTypes, entities]);\n\n useEffect(() => {\n updateFilters({\n type: selectedTypes.length\n ? new EntityTypeFilter(selectedTypes)\n : undefined,\n });\n }, [selectedTypes, updateFilters]);\n\n return {\n loading,\n error,\n availableTypes,\n selectedTypes,\n setSelectedTypes,\n };\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { useAsync } from 'react-use';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { catalogApiRef } from '../api';\n\n// Retrieve a list of unique entity kinds present in the catalog\nexport function useEntityKinds() {\n const catalogApi = useApi(catalogApiRef);\n\n const {\n error,\n loading,\n value: kinds,\n } = useAsync(async () => {\n const entities = await catalogApi\n .getEntities({ fields: ['kind'] })\n .then(response => response.items);\n\n return [...new Set(entities.map(e => e.kind))].sort();\n });\n return { error, loading, kinds };\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { UserEntity } from '@backstage/catalog-model';\nimport { useAsync } from 'react-use';\nimport { AsyncState } from 'react-use/lib/useAsync';\nimport { catalogApiRef } from '../api';\nimport { identityApiRef, useApi } from '@backstage/core-plugin-api';\n\n/**\n * Get the catalog User entity (if any) that matches the logged-in user.\n */\nexport function useOwnUser(): AsyncState<UserEntity | undefined> {\n const catalogApi = useApi(catalogApiRef);\n const identityApi = useApi(identityApiRef);\n\n // TODO: get the full entity (or at least the full entity name) from the\n // identityApi\n return useAsync(\n () =>\n catalogApi.getEntityByName({\n kind: 'User',\n namespace: 'default',\n name: identityApi.getUserId(),\n }) as Promise<UserEntity | undefined>,\n [catalogApi, identityApi],\n );\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { Entity, EntityRelation } from '@backstage/catalog-model';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { chunk, groupBy } from 'lodash';\nimport { useAsync } from 'react-use';\nimport { catalogApiRef } from '../api';\n\nconst BATCH_SIZE = 20;\n\nexport function useRelatedEntities(\n entity: Entity,\n { type, kind }: { type?: string; kind?: string },\n): {\n entities: Entity[] | undefined;\n loading: boolean;\n error: Error | undefined;\n} {\n const catalogApi = useApi(catalogApiRef);\n const {\n loading,\n value: entities,\n error,\n } = useAsync(async () => {\n const relations =\n entity.relations &&\n entity.relations.filter(\n r =>\n (!type || r.type.toLowerCase() === type.toLowerCase()) &&\n (!kind || r.target.kind.toLowerCase() === kind.toLowerCase()),\n );\n\n if (!relations) {\n return [];\n }\n\n // Group the relations by kind and namespace to reduce the size of the request query string.\n // Without this grouping, the kind and namespace would need to be specified for each relation, e.g.\n // `filter=kind=component,namespace=default,name=example1&filter=kind=component,namespace=default,name=example2`\n // with grouping, we can generate a query a string like\n // `filter=kind=component,namespace=default,name=example1,example2`\n const relationsByKindAndNamespace: EntityRelation[][] = Object.values(\n groupBy(relations, ({ target }) => {\n return `${target.kind}:${target.namespace}`.toLowerCase();\n }),\n );\n\n // Split the names within each group into batches to further reduce the query string length.\n const batchedRelationsByKindAndNamespace: {\n kind: string;\n namespace: string;\n nameBatches: string[][];\n }[] = [];\n for (const rs of relationsByKindAndNamespace) {\n batchedRelationsByKindAndNamespace.push({\n // All relations in a group have the same kind and namespace, so its arbitrary which we pick\n kind: rs[0].target.kind,\n namespace: rs[0].target.namespace,\n nameBatches: chunk(\n rs.map(r => r.target.name),\n BATCH_SIZE,\n ),\n });\n }\n\n const results = await Promise.all(\n batchedRelationsByKindAndNamespace.flatMap(rs => {\n return rs.nameBatches.map(names => {\n return catalogApi.getEntities({\n filter: {\n kind: rs.kind,\n 'metadata.namespace': rs.namespace,\n 'metadata.name': names,\n },\n });\n });\n }),\n );\n\n return results.flatMap(r => r.items);\n }, [entity, type]);\n\n return {\n entities,\n loading,\n error,\n };\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity } from '@backstage/catalog-model';\nimport { storageApiRef, useApi } from '@backstage/core-plugin-api';\nimport { useCallback, useEffect, useState } from 'react';\nimport { useObservable } from 'react-use';\n\nconst buildEntityKey = (component: Entity) =>\n `entity:${component.kind}:${component.metadata.namespace ?? 'default'}:${\n component.metadata.name\n }`;\n\nexport const useStarredEntities = () => {\n const storageApi = useApi(storageApiRef);\n const settingsStore = storageApi.forBucket('settings');\n const rawStarredEntityKeys =\n settingsStore.get<string[]>('starredEntities') ?? [];\n\n const [starredEntities, setStarredEntities] = useState(\n new Set(rawStarredEntityKeys),\n );\n\n const observedItems = useObservable(\n settingsStore.observe$<string[]>('starredEntities'),\n );\n\n useEffect(() => {\n if (observedItems?.newValue) {\n const currentValue = observedItems?.newValue ?? [];\n setStarredEntities(new Set(currentValue));\n }\n }, [observedItems?.newValue]);\n\n const toggleStarredEntity = useCallback(\n (entity: Entity) => {\n const entityKey = buildEntityKey(entity);\n if (starredEntities.has(entityKey)) {\n starredEntities.delete(entityKey);\n } else {\n starredEntities.add(entityKey);\n }\n\n settingsStore.set('starredEntities', Array.from(starredEntities));\n },\n [starredEntities, settingsStore],\n );\n\n const isStarredEntity = useCallback(\n (entity: Entity) => {\n const entityKey = buildEntityKey(entity);\n return starredEntities.has(entityKey);\n },\n [starredEntities],\n );\n\n return {\n starredEntities,\n toggleStarredEntity,\n isStarredEntity,\n };\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { CatalogApi } from '@backstage/catalog-client';\nimport {\n Entity,\n EntityName,\n parseEntityRef,\n RELATION_MEMBER_OF,\n RELATION_OWNED_BY,\n stringifyEntityRef,\n} from '@backstage/catalog-model';\nimport {\n IdentityApi,\n identityApiRef,\n useApi,\n} from '@backstage/core-plugin-api';\nimport jwtDecoder from 'jwt-decode';\nimport { useMemo } from 'react';\nimport { useAsync } from 'react-use';\nimport { catalogApiRef } from '../api';\nimport { getEntityRelations } from '../utils/getEntityRelations';\n\n// Takes a user ID from the identity, which can be on basically any form, and\n// returns an entity ref. E.g. if the input is \"foo\", it returns\n// \"user:default/foo\" to make sure it's a full ref.\nfunction extendUserId(id: string): string {\n try {\n const ref = parseEntityRef(id, {\n defaultKind: 'User',\n defaultNamespace: 'default',\n });\n return stringifyEntityRef(ref);\n } catch {\n return id;\n }\n}\n\n// Takes the relevant parts of the Backstage identity, and translates them into\n// a list of entity refs on string form that represent the user's ownership\n// connections.\nexport async function loadIdentityOwnerRefs(\n identityApi: IdentityApi,\n): Promise<string[]> {\n const id = identityApi.getUserId();\n const token = await identityApi.getIdToken();\n const result: string[] = [];\n\n if (id) {\n result.push(extendUserId(id));\n }\n\n if (token) {\n try {\n const decoded = jwtDecoder(token) as any;\n if (decoded?.ent) {\n [decoded.ent]\n .flat()\n .filter(x => typeof x === 'string')\n .map(x => x.toLocaleLowerCase('en-US'))\n .forEach(x => result.push(x));\n }\n } catch {\n // ignore\n }\n }\n\n return result;\n}\n\n// Takes the relevant parts of the User entity corresponding to the Backstage\n// identity, and translates them into a list of entity refs on string form that\n// represent the user's ownership connections.\nexport async function loadCatalogOwnerRefs(\n catalogApi: CatalogApi,\n identityOwnerRefs: string[],\n): Promise<string[]> {\n const result = new Array<string>();\n\n const primaryUserRef = identityOwnerRefs.find(ref => ref.startsWith('user:'));\n if (primaryUserRef) {\n const entity = await catalogApi.getEntityByName(\n parseEntityRef(primaryUserRef),\n );\n if (entity) {\n const memberOf = getEntityRelations(entity, RELATION_MEMBER_OF, {\n kind: 'Group',\n });\n for (const group of memberOf) {\n result.push(stringifyEntityRef(group));\n }\n }\n }\n\n return result;\n}\n\n/**\n * Returns a function that checks whether the currently signed-in user is an\n * owner of a given entity. When the hook is initially mounted, the loading\n * flag will be true and the results returned from the function will always be\n * false.\n */\nexport function useEntityOwnership(): {\n loading: boolean;\n isOwnedEntity: (entity: Entity | EntityName) => boolean;\n} {\n const identityApi = useApi(identityApiRef);\n const catalogApi = useApi(catalogApiRef);\n\n // Trigger load only on mount\n const { loading, value: refs } = useAsync(async () => {\n const identityRefs = await loadIdentityOwnerRefs(identityApi);\n const catalogRefs = await loadCatalogOwnerRefs(catalogApi, identityRefs);\n return new Set([...identityRefs, ...catalogRefs]);\n }, []);\n\n const isOwnedEntity = useMemo(() => {\n const myOwnerRefs = new Set(refs ?? []);\n return (entity: Entity | EntityName) => {\n const entityOwnerRefs = (\n 'metadata' in entity\n ? getEntityRelations(entity, RELATION_OWNED_BY)\n : [entity]\n ).map(stringifyEntityRef);\n for (const ref of entityOwnerRefs) {\n if (myOwnerRefs.has(ref)) {\n return true;\n }\n }\n return false;\n };\n }, [refs]);\n\n return useMemo(() => ({ loading, isOwnedEntity }), [loading, isOwnedEntity]);\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React, { useEffect, useState } from 'react';\nimport { Alert } from '@material-ui/lab';\nimport { useEntityListProvider } from '../../hooks';\nimport { EntityKindFilter } from '../../filters';\n\ntype EntityKindFilterProps = {\n initialFilter?: string;\n hidden: boolean;\n};\n\nexport const EntityKindPicker = ({\n initialFilter,\n hidden,\n}: EntityKindFilterProps) => {\n const { updateFilters, queryParameters } = useEntityListProvider();\n const [selectedKind] = useState(\n [queryParameters.kind].flat()[0] ?? initialFilter,\n );\n\n useEffect(() => {\n updateFilters({\n kind: selectedKind ? new EntityKindFilter(selectedKind) : undefined,\n });\n }, [selectedKind, updateFilters]);\n\n if (hidden) return null;\n\n // TODO(timbonicus): This should load available kinds from the catalog-backend, similar to\n // EntityTypePicker.\n\n return <Alert severity=\"warning\">Kind filter not yet available</Alert>;\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity } from '@backstage/catalog-model';\nimport {\n Box,\n Checkbox,\n FormControlLabel,\n TextField,\n Typography,\n} from '@material-ui/core';\nimport CheckBoxIcon from '@material-ui/icons/CheckBox';\nimport CheckBoxOutlineBlankIcon from '@material-ui/icons/CheckBoxOutlineBlank';\nimport ExpandMoreIcon from '@material-ui/icons/ExpandMore';\nimport { Autocomplete } from '@material-ui/lab';\nimport React, { useEffect, useMemo, useState } from 'react';\nimport { useEntityListProvider } from '../../hooks/useEntityListProvider';\nimport { EntityLifecycleFilter } from '../../filters';\n\nconst icon = <CheckBoxOutlineBlankIcon fontSize=\"small\" />;\nconst checkedIcon = <CheckBoxIcon fontSize=\"small\" />;\n\nexport const EntityLifecyclePicker = () => {\n const { updateFilters, backendEntities, filters, queryParameters } =\n useEntityListProvider();\n\n const queryParamLifecycles = [queryParameters.lifecycles]\n .flat()\n .filter(Boolean) as string[];\n const [selectedLifecycles, setSelectedLifecycles] = useState(\n queryParamLifecycles.length\n ? queryParamLifecycles\n : filters.lifecycles?.values ?? [],\n );\n\n useEffect(() => {\n updateFilters({\n lifecycles: selectedLifecycles.length\n ? new EntityLifecycleFilter(selectedLifecycles)\n : undefined,\n });\n }, [selectedLifecycles, updateFilters]);\n\n const availableLifecycles = useMemo(\n () =>\n [\n ...new Set(\n backendEntities\n .map((e: Entity) => e.spec?.lifecycle)\n .filter(Boolean) as string[],\n ),\n ].sort(),\n [backendEntities],\n );\n\n if (!availableLifecycles.length) return null;\n\n return (\n <Box pb={1} pt={1}>\n <Typography variant=\"button\">Lifecycle</Typography>\n <Autocomplete<string>\n aria-label=\"Lifecycle\"\n multiple\n options={availableLifecycles}\n value={selectedLifecycles}\n onChange={(_: object, value: string[]) => setSelectedLifecycles(value)}\n renderOption={(option, { selected }) => (\n <FormControlLabel\n control={\n <Checkbox\n icon={icon}\n checkedIcon={checkedIcon}\n checked={selected}\n />\n }\n label={option}\n />\n )}\n size=\"small\"\n popupIcon={<ExpandMoreIcon data-testid=\"lifecycle-picker-expand\" />}\n renderInput={params => <TextField {...params} variant=\"outlined\" />}\n />\n </Box>\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity, RELATION_OWNED_BY } from '@backstage/catalog-model';\nimport {\n Box,\n Checkbox,\n FormControlLabel,\n TextField,\n Typography,\n} from '@material-ui/core';\nimport CheckBoxIcon from '@material-ui/icons/CheckBox';\nimport CheckBoxOutlineBlankIcon from '@material-ui/icons/CheckBoxOutlineBlank';\nimport ExpandMoreIcon from '@material-ui/icons/ExpandMore';\nimport { Autocomplete } from '@material-ui/lab';\nimport React, { useEffect, useMemo, useState } from 'react';\nimport { useEntityListProvider } from '../../hooks/useEntityListProvider';\nimport { EntityOwnerFilter } from '../../filters';\nimport { getEntityRelations } from '../../utils';\nimport { formatEntityRefTitle } from '../EntityRefLink';\n\nconst icon = <CheckBoxOutlineBlankIcon fontSize=\"small\" />;\nconst checkedIcon = <CheckBoxIcon fontSize=\"small\" />;\n\nexport const EntityOwnerPicker = () => {\n const { updateFilters, backendEntities, filters, queryParameters } =\n useEntityListProvider();\n\n const queryParamOwners = [queryParameters.owners]\n .flat()\n .filter(Boolean) as string[];\n const [selectedOwners, setSelectedOwners] = useState(\n queryParamOwners.length ? queryParamOwners : filters.owners?.values ?? [],\n );\n\n useEffect(() => {\n updateFilters({\n owners: selectedOwners.length\n ? new EntityOwnerFilter(selectedOwners)\n : undefined,\n });\n }, [selectedOwners, updateFilters]);\n\n const availableOwners = useMemo(\n () =>\n [\n ...new Set(\n backendEntities\n .flatMap((e: Entity) =>\n getEntityRelations(e, RELATION_OWNED_BY).map(o =>\n formatEntityRefTitle(o, { defaultKind: 'group' }),\n ),\n )\n .filter(Boolean) as string[],\n ),\n ].sort(),\n [backendEntities],\n );\n\n if (!availableOwners.length) return null;\n\n return (\n <Box pb={1} pt={1}>\n <Typography variant=\"button\">Owner</Typography>\n <Autocomplete<string>\n multiple\n aria-label=\"Owner\"\n options={availableOwners}\n value={selectedOwners}\n onChange={(_: object, value: string[]) => setSelectedOwners(value)}\n renderOption={(option, { selected }) => (\n <FormControlLabel\n control={\n <Checkbox\n icon={icon}\n checkedIcon={checkedIcon}\n checked={selected}\n />\n }\n label={option}\n />\n )}\n size=\"small\"\n popupIcon={<ExpandMoreIcon data-testid=\"owner-picker-expand\" />}\n renderInput={params => <TextField {...params} variant=\"outlined\" />}\n />\n </Box>\n );\n};\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { Entity } from '@backstage/catalog-model';\nimport React, { ReactNode } from 'react';\nimport { EntityContext } from '../../hooks';\n\ntype EntityProviderProps = {\n entity: Entity;\n children: ReactNode;\n};\n\nexport const EntityProvider = ({ entity, children }: EntityProviderProps) => (\n <EntityContext.Provider\n value={{\n entity,\n loading: !Boolean(entity),\n error: undefined,\n }}\n >\n {children}\n </EntityContext.Provider>\n);\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n FormControl,\n IconButton,\n Input,\n InputAdornment,\n makeStyles,\n Toolbar,\n} from '@material-ui/core';\nimport Clear from '@material-ui/icons/Clear';\nimport Search from '@material-ui/icons/Search';\nimport React, { useState } from 'react';\nimport { useDebounce } from 'react-use';\nimport { useEntityListProvider } from '../../hooks/useEntityListProvider';\nimport { EntityTextFilter } from '../../filters';\n\nconst useStyles = makeStyles(_theme => ({\n searchToolbar: {\n paddingLeft: 0,\n paddingRight: 0,\n },\n}));\n\nexport const EntitySearchBar = () => {\n const styles = useStyles();\n\n const { filters, updateFilters } = useEntityListProvider();\n const [search, setSearch] = useState(filters.text?.value ?? '');\n\n useDebounce(\n () => {\n updateFilters({\n text: search.length ? new EntityTextFilter(search) : undefined,\n });\n },\n 250,\n [search, updateFilters],\n );\n\n return (\n <Toolbar className={styles.searchToolbar}>\n <FormControl>\n <Input\n id=\"input-with-icon-adornment\"\n placeholder=\"Search\"\n autoComplete=\"off\"\n onChange={event => setSearch(event.target.value)}\n value={search}\n startAdornment={\n <InputAdornment position=\"start\">\n <Search />\n </InputAdornment>\n }\n endAdornment={\n <InputAdornment position=\"end\">\n <IconButton\n aria-label=\"clear search\"\n onClick={() => setSearch('')}\n edge=\"end\"\n disabled={search.length === 0}\n >\n <Clear />\n </IconButton>\n </InputAdornment>\n }\n />\n </FormControl>\n </Toolbar>\n );\n};\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n Entity,\n EntityName,\n RELATION_OWNED_BY,\n RELATION_PART_OF,\n} from '@backstage/catalog-model';\nimport React from 'react';\nimport { getEntityRelations } from '../../utils';\nimport {\n EntityRefLink,\n EntityRefLinks,\n formatEntityRefTitle,\n} from '../EntityRefLink';\nimport { OverflowTooltip, TableColumn } from '@backstage/core-components';\n\nexport function createEntityRefColumn<T extends Entity>({\n defaultKind,\n}: {\n defaultKind?: string;\n}): TableColumn<T> {\n function formatContent(entity: T): string {\n return formatEntityRefTitle(entity, {\n defaultKind,\n });\n }\n\n return {\n title: 'Name',\n highlight: true,\n customFilterAndSearch(filter, entity) {\n // TODO: We could implement this more efficiently, like searching over\n // each field that is displayed individually (kind, namespace, name).\n // but that migth confuse the user as it will behave different than a\n // simple text search.\n // Another alternative would be to cache the values. But writing them\n // into the entity feels bad too.\n return formatContent(entity).includes(filter);\n },\n customSort(entity1, entity2) {\n // TODO: We could implement this more efficiently by comparing field by field.\n // This has similar issues as above.\n return formatContent(entity1).localeCompare(formatContent(entity2));\n },\n render: entity => (\n <EntityRefLink entityRef={entity} defaultKind={defaultKind} />\n ),\n };\n}\n\nexport function createEntityRelationColumn<T extends Entity>({\n title,\n relation,\n defaultKind,\n filter: entityFilter,\n}: {\n title: string;\n relation: string;\n defaultKind?: string;\n filter?: { kind: string };\n}): TableColumn<T> {\n function getRelations(entity: T): EntityName[] {\n return getEntityRelations(entity, relation, entityFilter);\n }\n\n function formatContent(entity: T): string {\n return getRelations(entity)\n .map(r => formatEntityRefTitle(r, { defaultKind }))\n .join(', ');\n }\n\n return {\n title,\n customFilterAndSearch(filter, entity) {\n return formatContent(entity).includes(filter);\n },\n customSort(entity1, entity2) {\n return formatContent(entity1).localeCompare(formatContent(entity2));\n },\n render: entity => {\n return (\n <EntityRefLinks\n entityRefs={getRelations(entity)}\n defaultKind={defaultKind}\n />\n );\n },\n };\n}\n\nexport function createOwnerColumn<T extends Entity>(): TableColumn<T> {\n return createEntityRelationColumn({\n title: 'Owner',\n relation: RELATION_OWNED_BY,\n defaultKind: 'group',\n });\n}\n\nexport function createDomainColumn<T extends Entity>(): TableColumn<T> {\n return createEntityRelationColumn({\n title: 'Domain',\n relation: RELATION_PART_OF,\n defaultKind: 'domain',\n filter: {\n kind: 'domain',\n },\n });\n}\n\nexport function createSystemColumn<T extends Entity>(): TableColumn<T> {\n return createEntityRelationColumn({\n title: 'System',\n relation: RELATION_PART_OF,\n defaultKind: 'system',\n filter: {\n kind: 'system',\n },\n });\n}\n\nexport function createMetadataDescriptionColumn<\n T extends Entity,\n>(): TableColumn<T> {\n return {\n title: 'Description',\n field: 'metadata.description',\n render: entity => (\n <OverflowTooltip\n text={entity.metadata.description}\n placement=\"bottom-start\"\n />\n ),\n width: 'auto',\n };\n}\n\nexport function createSpecLifecycleColumn<T extends Entity>(): TableColumn<T> {\n return {\n title: 'Lifecycle',\n field: 'spec.lifecycle',\n };\n}\n\nexport function createSpecTypeColumn<T extends Entity>(): TableColumn<T> {\n return {\n title: 'Type',\n field: 'spec.type',\n };\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { ComponentEntity, SystemEntity } from '@backstage/catalog-model';\nimport {\n createDomainColumn,\n createEntityRefColumn,\n createMetadataDescriptionColumn,\n createOwnerColumn,\n createSpecLifecycleColumn,\n createSpecTypeColumn,\n createSystemColumn,\n} from './columns';\nimport { TableColumn } from '@backstage/core-components';\n\nexport const systemEntityColumns: TableColumn<SystemEntity>[] = [\n createEntityRefColumn({ defaultKind: 'system' }),\n createDomainColumn(),\n createOwnerColumn(),\n createMetadataDescriptionColumn(),\n];\n\nexport const componentEntityColumns: TableColumn<ComponentEntity>[] = [\n createEntityRefColumn({ defaultKind: 'component' }),\n createSystemColumn(),\n createOwnerColumn(),\n createSpecTypeColumn(),\n createSpecLifecycleColumn(),\n createMetadataDescriptionColumn(),\n];\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity } from '@backstage/catalog-model';\nimport { makeStyles } from '@material-ui/core';\nimport React, { ReactNode } from 'react';\nimport * as columnFactories from './columns';\nimport { componentEntityColumns, systemEntityColumns } from './presets';\nimport { Table, TableColumn } from '@backstage/core-components';\n\ntype Props<T extends Entity> = {\n title: string;\n variant?: 'gridItem';\n entities: T[];\n emptyContent?: ReactNode;\n columns: TableColumn<T>[];\n};\n\nconst useStyles = makeStyles(theme => ({\n empty: {\n padding: theme.spacing(2),\n display: 'flex',\n justifyContent: 'center',\n },\n}));\n\nexport function EntityTable<T extends Entity>({\n entities,\n title,\n emptyContent,\n variant = 'gridItem',\n columns,\n}: Props<T>) {\n const classes = useStyles();\n const tableStyle: React.CSSProperties = {\n minWidth: '0',\n width: '100%',\n };\n\n if (variant === 'gridItem') {\n tableStyle.height = 'calc(100% - 10px)';\n }\n\n return (\n <Table<T>\n columns={columns}\n title={title}\n style={tableStyle}\n emptyContent={\n emptyContent && <div className={classes.empty}>{emptyContent}</div>\n }\n options={{\n // TODO: Toolbar padding if off compared to other cards, should be: padding: 16px 24px;\n search: false,\n paging: false,\n actionsColumnIndex: -1,\n padding: 'dense',\n }}\n data={entities}\n />\n );\n}\n\nEntityTable.columns = columnFactories;\n\nEntityTable.systemEntityColumns = systemEntityColumns;\n\nEntityTable.componentEntityColumns = componentEntityColumns;\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity } from '@backstage/catalog-model';\nimport {\n Box,\n Checkbox,\n FormControlLabel,\n TextField,\n Typography,\n} from '@material-ui/core';\nimport CheckBoxIcon from '@material-ui/icons/CheckBox';\nimport CheckBoxOutlineBlankIcon from '@material-ui/icons/CheckBoxOutlineBlank';\nimport ExpandMoreIcon from '@material-ui/icons/ExpandMore';\nimport { Autocomplete } from '@material-ui/lab';\nimport React, { useEffect, useMemo, useState } from 'react';\nimport { useEntityListProvider } from '../../hooks/useEntityListProvider';\nimport { EntityTagFilter } from '../../filters';\n\nconst icon = <CheckBoxOutlineBlankIcon fontSize=\"small\" />;\nconst checkedIcon = <CheckBoxIcon fontSize=\"small\" />;\n\nexport const EntityTagPicker = () => {\n const { updateFilters, backendEntities, filters, queryParameters } =\n useEntityListProvider();\n\n const queryParamTags = [queryParameters.tags]\n .flat()\n .filter(Boolean) as string[];\n const [selectedTags, setSelectedTags] = useState(\n queryParamTags.length ? queryParamTags : filters.tags?.values ?? [],\n );\n\n useEffect(() => {\n updateFilters({\n tags: selectedTags.length ? new EntityTagFilter(selectedTags) : undefined,\n });\n }, [selectedTags, updateFilters]);\n\n const availableTags = useMemo(\n () =>\n [\n ...new Set(\n backendEntities\n .flatMap((e: Entity) => e.metadata.tags)\n .filter(Boolean) as string[],\n ),\n ].sort(),\n [backendEntities],\n );\n\n if (!availableTags.length) return null;\n\n return (\n <Box pb={1} pt={1}>\n <Typography variant=\"button\">Tags</Typography>\n <Autocomplete<string>\n multiple\n aria-label=\"Tags\"\n options={availableTags}\n value={selectedTags}\n onChange={(_: object, value: string[]) => setSelectedTags(value)}\n renderOption={(option, { selected }) => (\n <FormControlLabel\n control={\n <Checkbox\n icon={icon}\n checkedIcon={checkedIcon}\n checked={selected}\n />\n }\n label={option}\n />\n )}\n size=\"small\"\n popupIcon={<ExpandMoreIcon data-testid=\"tag-picker-expand\" />}\n renderInput={params => <TextField {...params} variant=\"outlined\" />}\n />\n </Box>\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React, { useEffect } from 'react';\nimport capitalize from 'lodash/capitalize';\nimport { Box } from '@material-ui/core';\nimport { useEntityTypeFilter } from '../../hooks/useEntityTypeFilter';\n\nimport { alertApiRef, useApi } from '@backstage/core-plugin-api';\nimport { Select } from '@backstage/core-components';\n\nexport const EntityTypePicker = () => {\n const alertApi = useApi(alertApiRef);\n const { error, availableTypes, selectedTypes, setSelectedTypes } =\n useEntityTypeFilter();\n\n useEffect(() => {\n if (error) {\n alertApi.post({\n message: `Failed to load entity types`,\n severity: 'error',\n });\n }\n }, [error, alertApi]);\n\n if (availableTypes.length === 0 || error) return null;\n\n const items = [\n { value: 'all', label: 'All' },\n ...availableTypes.map((type: string) => ({\n value: type,\n label: capitalize(type),\n })),\n ];\n\n return (\n <Box pb={1} pt={1}>\n <Select\n label=\"Type\"\n items={items}\n selected={(items.length > 1 ? selectedTypes[0] : undefined) ?? 'all'}\n onChange={value =>\n setSelectedTypes(value === 'all' ? [] : [String(value)])\n }\n />\n </Box>\n );\n};\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React, { ComponentProps } from 'react';\nimport { useStarredEntities } from '../../hooks/useStarredEntities';\nimport { IconButton, Tooltip, withStyles } from '@material-ui/core';\nimport StarBorder from '@material-ui/icons/StarBorder';\nimport Star from '@material-ui/icons/Star';\nimport { Entity } from '@backstage/catalog-model';\n\ntype Props = ComponentProps<typeof IconButton> & { entity: Entity };\n\nconst YellowStar = withStyles({\n root: {\n color: '#f3ba37',\n },\n})(Star);\n\nexport const favoriteEntityTooltip = (isStarred: boolean) =>\n isStarred ? 'Remove from favorites' : 'Add to favorites';\n\nexport const favoriteEntityIcon = (isStarred: boolean) =>\n isStarred ? <YellowStar /> : <StarBorder />;\n\n/**\n * IconButton for showing if a current entity is starred and adding/removing it from the favorite entities\n * @param props MaterialUI IconButton props extended by required `entity` prop\n */\nexport const FavoriteEntity = (props: Props) => {\n const { toggleStarredEntity, isStarredEntity } = useStarredEntities();\n const isStarred = isStarredEntity(props.entity);\n return (\n <IconButton\n color=\"inherit\"\n {...props}\n onClick={() => toggleStarredEntity(props.entity)}\n >\n <Tooltip title={favoriteEntityTooltip(isStarred)}>\n {favoriteEntityIcon(isStarred)}\n </Tooltip>\n </IconButton>\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n Entity,\n EntityName,\n getEntityName,\n ORIGIN_LOCATION_ANNOTATION,\n} from '@backstage/catalog-model';\nimport { catalogApiRef } from '../../api';\nimport { useCallback } from 'react';\nimport { useAsync } from 'react-use';\nimport { useApi } from '@backstage/core-plugin-api';\n\n/**\n * Each distinct state that the dialog can be in at any given time.\n */\nexport type UseUnregisterEntityDialogState =\n | {\n type: 'loading';\n }\n | {\n type: 'error';\n error: Error;\n }\n | {\n type: 'bootstrap';\n location: string;\n deleteEntity: () => Promise<void>;\n }\n | {\n type: 'unregister';\n location: string;\n colocatedEntities: EntityName[];\n unregisterLocation: () => Promise<void>;\n deleteEntity: () => Promise<void>;\n }\n | {\n type: 'only-delete';\n deleteEntity: () => Promise<void>;\n };\n\n/**\n * Houses the main logic for unregistering entities and their locations.\n */\nexport function useUnregisterEntityDialogState(\n entity: Entity,\n): UseUnregisterEntityDialogState {\n const catalogApi = useApi(catalogApiRef);\n const locationRef = entity.metadata.annotations?.[ORIGIN_LOCATION_ANNOTATION];\n const uid = entity.metadata.uid;\n const isBootstrap = locationRef === 'bootstrap:bootstrap';\n\n // Load the prerequisite data: what entities that are colocated with us, and\n // what location that spawned us\n const prerequisites = useAsync(async () => {\n const locationPromise = catalogApi.getOriginLocationByEntity(entity);\n\n let colocatedEntitiesPromise: Promise<Entity[]>;\n if (!locationRef) {\n colocatedEntitiesPromise = Promise.resolve([]);\n } else {\n const locationAnnotationFilter = `metadata.annotations.${ORIGIN_LOCATION_ANNOTATION}`;\n colocatedEntitiesPromise = catalogApi\n .getEntities({\n filter: { [locationAnnotationFilter]: locationRef },\n fields: [\n 'kind',\n 'metadata.uid',\n 'metadata.name',\n 'metadata.namespace',\n ],\n })\n .then(response => response.items);\n }\n\n return Promise.all([locationPromise, colocatedEntitiesPromise]).then(\n ([location, colocatedEntities]) => ({\n location,\n colocatedEntities,\n }),\n );\n }, [catalogApi, entity]);\n\n // Unregisters the underlying location and removes all of the entities that\n // are spawned from it. Can only ever be called when the prerequisites have\n // finished loading successfully, and if there was a matching location.\n const unregisterLocation = useCallback(\n async function unregisterLocationFn() {\n const { location, colocatedEntities } = prerequisites.value!;\n await catalogApi.removeLocationById(location!.id);\n await Promise.allSettled(\n colocatedEntities.map(e =>\n catalogApi.removeEntityByUid(e.metadata.uid!),\n ),\n );\n },\n [catalogApi, prerequisites],\n );\n\n // Just removes the entity, without affecting locations in any way.\n const deleteEntity = useCallback(\n async function deleteEntityFn() {\n await catalogApi.removeEntityByUid(uid!);\n },\n [catalogApi, uid],\n );\n\n // If this is a bootstrap location entity, don't even block on loading\n // prerequisites. We know that all that we will do is to offer to remove the\n // entity, and that doesn't require anything from the prerequisites.\n if (isBootstrap) {\n return { type: 'bootstrap', location: locationRef!, deleteEntity };\n }\n\n // Return early if prerequisites still loading or failing\n const { loading, error, value } = prerequisites;\n if (loading) {\n return { type: 'loading' };\n } else if (error) {\n return { type: 'error', error };\n }\n\n const { location, colocatedEntities } = value!;\n if (!location) {\n return { type: 'only-delete', deleteEntity };\n }\n return {\n type: 'unregister',\n location: locationRef!,\n colocatedEntities: colocatedEntities.map(getEntityName),\n unregisterLocation,\n deleteEntity,\n };\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity } from '@backstage/catalog-model';\nimport { EntityRefLink } from '../EntityRefLink';\nimport {\n Box,\n Button,\n Dialog,\n DialogActions,\n DialogContent,\n DialogContentText,\n DialogTitle,\n Divider,\n makeStyles,\n} from '@material-ui/core';\nimport Alert from '@material-ui/lab/Alert';\nimport React, { useCallback, useState } from 'react';\nimport { useUnregisterEntityDialogState } from './useUnregisterEntityDialogState';\n\nimport { alertApiRef, configApiRef, useApi } from '@backstage/core-plugin-api';\nimport { Progress, ResponseErrorPanel } from '@backstage/core-components';\n\nconst useStyles = makeStyles({\n advancedButton: {\n fontSize: '0.7em',\n },\n});\n\ntype Props = {\n open: boolean;\n onConfirm: () => any;\n onClose: () => any;\n entity: Entity;\n};\n\nconst Contents = ({\n entity,\n onConfirm,\n}: {\n entity: Entity;\n onConfirm: () => any;\n}) => {\n const alertApi = useApi(alertApiRef);\n const configApi = useApi(configApiRef);\n const classes = useStyles();\n const state = useUnregisterEntityDialogState(entity);\n const [showDelete, setShowDelete] = useState(false);\n const [busy, setBusy] = useState(false);\n const appTitle = configApi.getOptionalString('app.title') ?? 'Backstage';\n\n const onUnregister = useCallback(\n async function onUnregisterFn() {\n if ('unregisterLocation' in state) {\n setBusy(true);\n try {\n await state.unregisterLocation();\n onConfirm();\n } catch (err) {\n alertApi.post({ message: err.message });\n } finally {\n setBusy(false);\n }\n }\n },\n [alertApi, onConfirm, state],\n );\n\n const onDelete = useCallback(\n async function onDeleteFn() {\n if ('deleteEntity' in state) {\n setBusy(true);\n try {\n await state.deleteEntity();\n onConfirm();\n } catch (err) {\n alertApi.post({ message: err.message });\n } finally {\n setBusy(false);\n }\n }\n },\n [alertApi, onConfirm, state],\n );\n\n if (state.type === 'loading') {\n return <Progress />;\n }\n\n if (state.type === 'error') {\n return <ResponseErrorPanel error={state.error} />;\n }\n\n if (state.type === 'bootstrap') {\n return (\n <>\n <Alert severity=\"info\">\n You cannot unregister this entity, since it originates from a\n protected Backstage configuration (location \"{state.location}\"). If\n you believe this is in error, please contact the {appTitle}{' '}\n integrator.\n </Alert>\n\n <Box marginTop={2}>\n {!showDelete && (\n <Button\n variant=\"text\"\n size=\"small\"\n color=\"primary\"\n className={classes.advancedButton}\n onClick={() => setShowDelete(true)}\n >\n Advanced Options\n </Button>\n )}\n\n {showDelete && (\n <>\n <DialogContentText>\n You have the option to delete the entity itself from the\n catalog. Note that this should only be done if you know that the\n catalog file has been deleted at, or moved from, its origin\n location. If that is not the case, the entity will reappear\n shortly as the next refresh round is performed by the catalog.\n </DialogContentText>\n <Button\n variant=\"contained\"\n color=\"secondary\"\n disabled={busy}\n onClick={onDelete}\n >\n Delete Entity\n </Button>\n </>\n )}\n </Box>\n </>\n );\n }\n\n if (state.type === 'only-delete') {\n return (\n <>\n <DialogContentText>\n This entity does not seem to originate from a registered location. You\n therefore only have the option to delete it outright from the catalog.\n </DialogContentText>\n <Button\n variant=\"contained\"\n color=\"secondary\"\n disabled={busy}\n onClick={onDelete}\n >\n Delete Entity\n </Button>\n </>\n );\n }\n\n if (state.type === 'unregister') {\n return (\n <>\n <DialogContentText>\n This action will unregister the following entities:\n </DialogContentText>\n <DialogContentText component=\"ul\">\n {state.colocatedEntities.map(e => (\n <li key={`${e.kind}:${e.namespace}/${e.name}`}>\n <EntityRefLink entityRef={e} />\n </li>\n ))}\n </DialogContentText>\n <DialogContentText>\n Located at the following location:\n </DialogContentText>\n <DialogContentText component=\"ul\">\n <li>{state.location}</li>\n </DialogContentText>\n <DialogContentText>\n To undo, just re-register the entity in {appTitle}.\n </DialogContentText>\n <Box marginTop={2}>\n <Button\n variant=\"contained\"\n color=\"secondary\"\n disabled={busy}\n onClick={onUnregister}\n >\n Unregister Location\n </Button>\n {!showDelete && (\n <Box component=\"span\" marginLeft={2}>\n <Button\n variant=\"text\"\n size=\"small\"\n color=\"primary\"\n className={classes.advancedButton}\n onClick={() => setShowDelete(true)}\n >\n Advanced Options\n </Button>\n </Box>\n )}\n </Box>\n\n {showDelete && (\n <>\n <Box paddingTop={4} paddingBottom={4}>\n <Divider />\n </Box>\n <DialogContentText>\n You also have the option to delete the entity itself from the\n catalog. Note that this should only be done if you know that the\n catalog file has been deleted at, or moved from, its origin\n location. If that is not the case, the entity will reappear\n shortly as the next refresh round is performed by the catalog.\n </DialogContentText>\n <Button\n variant=\"contained\"\n color=\"secondary\"\n disabled={busy}\n onClick={onDelete}\n >\n Delete Entity\n </Button>\n </>\n )}\n </>\n );\n }\n\n return <Alert severity=\"error\">Internal error: Unknown state</Alert>;\n};\n\nexport const UnregisterEntityDialog = ({\n open,\n onConfirm,\n onClose,\n entity,\n}: Props) => (\n <Dialog open={open} onClose={onClose}>\n <DialogTitle id=\"responsive-dialog-title\">\n Are you sure you want to unregister this entity?\n </DialogTitle>\n <DialogContent>\n <Contents entity={entity} onConfirm={onConfirm} />\n </DialogContent>\n <DialogActions>\n <Button onClick={onClose} color=\"primary\">\n Cancel\n </Button>\n </DialogActions>\n </Dialog>\n);\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n configApiRef,\n IconComponent,\n useApi,\n} from '@backstage/core-plugin-api';\nimport {\n Card,\n List,\n ListItemIcon,\n ListItemSecondaryAction,\n ListItemText,\n makeStyles,\n MenuItem,\n Theme,\n Typography,\n} from '@material-ui/core';\nimport SettingsIcon from '@material-ui/icons/Settings';\nimport StarIcon from '@material-ui/icons/Star';\nimport { compact } from 'lodash';\nimport React, { Fragment, useEffect, useMemo, useState } from 'react';\nimport { UserListFilter } from '../../filters';\nimport {\n useEntityListProvider,\n useStarredEntities,\n useEntityOwnership,\n} from '../../hooks';\nimport { UserListFilterKind } from '../../types';\nimport { reduceEntityFilters } from '../../utils';\n\nconst useStyles = makeStyles<Theme>(theme => ({\n root: {\n backgroundColor: 'rgba(0, 0, 0, .11)',\n boxShadow: 'none',\n margin: theme.spacing(1, 0, 1, 0),\n },\n title: {\n margin: theme.spacing(1, 0, 0, 1),\n textTransform: 'uppercase',\n fontSize: 12,\n fontWeight: 'bold',\n },\n listIcon: {\n minWidth: 30,\n color: theme.palette.text.primary,\n },\n menuItem: {\n minHeight: theme.spacing(6),\n },\n groupWrapper: {\n margin: theme.spacing(1, 1, 2, 1),\n },\n}));\n\nexport type ButtonGroup = {\n name: string;\n items: {\n id: 'owned' | 'starred' | 'all';\n label: string;\n icon?: IconComponent;\n }[];\n};\n\nfunction getFilterGroups(orgName: string | undefined): ButtonGroup[] {\n return [\n {\n name: 'Personal',\n items: [\n {\n id: 'owned',\n label: 'Owned',\n icon: SettingsIcon,\n },\n {\n id: 'starred',\n label: 'Starred',\n icon: StarIcon,\n },\n ],\n },\n {\n name: orgName ?? 'Company',\n items: [\n {\n id: 'all',\n label: 'All',\n },\n ],\n },\n ];\n}\n\ntype UserListPickerProps = {\n initialFilter?: UserListFilterKind;\n availableFilters?: UserListFilterKind[];\n};\n\nexport const UserListPicker = ({\n initialFilter,\n availableFilters,\n}: UserListPickerProps) => {\n const classes = useStyles();\n const configApi = useApi(configApiRef);\n const orgName = configApi.getOptionalString('organization.name') ?? 'Company';\n\n // Remove group items that aren't in availableFilters and exclude\n // any now-empty groups.\n const filterGroups = getFilterGroups(orgName)\n .map(filterGroup => ({\n ...filterGroup,\n items: filterGroup.items.filter(\n ({ id }) => !availableFilters || availableFilters.includes(id),\n ),\n }))\n .filter(({ items }) => !!items.length);\n\n const { filters, updateFilters, backendEntities, queryParameters } =\n useEntityListProvider();\n\n const { isStarredEntity } = useStarredEntities();\n const { isOwnedEntity } = useEntityOwnership();\n const [selectedUserFilter, setSelectedUserFilter] = useState(\n [queryParameters.user].flat()[0] ?? initialFilter,\n );\n\n // Static filters; used for generating counts of potentially unselected kinds\n const ownedFilter = useMemo(\n () => new UserListFilter('owned', isOwnedEntity, isStarredEntity),\n [isOwnedEntity, isStarredEntity],\n );\n const starredFilter = useMemo(\n () => new UserListFilter('starred', isOwnedEntity, isStarredEntity),\n [isOwnedEntity, isStarredEntity],\n );\n\n useEffect(() => {\n updateFilters({\n user: selectedUserFilter\n ? new UserListFilter(\n selectedUserFilter as UserListFilterKind,\n isOwnedEntity,\n isStarredEntity,\n )\n : undefined,\n });\n }, [selectedUserFilter, isOwnedEntity, isStarredEntity, updateFilters]);\n\n // To show proper counts for each section, apply all other frontend filters _except_ the user\n // filter that's controlled by this picker.\n const [entitiesWithoutUserFilter, setEntitiesWithoutUserFilter] =\n useState(backendEntities);\n useEffect(() => {\n const filterFn = reduceEntityFilters(\n compact(Object.values({ ...filters, user: undefined })),\n );\n setEntitiesWithoutUserFilter(backendEntities.filter(filterFn));\n }, [filters, backendEntities]);\n\n function getFilterCount(id: UserListFilterKind) {\n switch (id) {\n case 'owned':\n return entitiesWithoutUserFilter.filter(entity =>\n ownedFilter.filterEntity(entity),\n ).length;\n case 'starred':\n return entitiesWithoutUserFilter.filter(entity =>\n starredFilter.filterEntity(entity),\n ).length;\n default:\n return entitiesWithoutUserFilter.length;\n }\n }\n\n return (\n <Card className={classes.root}>\n {filterGroups.map(group => (\n <Fragment key={group.name}>\n <Typography variant=\"subtitle2\" className={classes.title}>\n {group.name}\n </Typography>\n <Card className={classes.groupWrapper}>\n <List disablePadding dense>\n {group.items.map(item => (\n <MenuItem\n key={item.id}\n button\n divider\n onClick={() => setSelectedUserFilter(item.id)}\n selected={item.id === filters.user?.value}\n className={classes.menuItem}\n >\n {item.icon && (\n <ListItemIcon className={classes.listIcon}>\n <item.icon fontSize=\"small\" />\n </ListItemIcon>\n )}\n <ListItemText>\n <Typography\n variant=\"body1\"\n data-testid={`user-picker-${item.id}`}\n >\n {item.label}\n </Typography>\n </ListItemText>\n <ListItemSecondaryAction>\n {getFilterCount(item.id) ?? '-'}\n </ListItemSecondaryAction>\n </MenuItem>\n ))}\n </List>\n </Card>\n </Fragment>\n ))}\n </Card>\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React, { PropsWithChildren, useCallback, useState } from 'react';\nimport {\n DefaultEntityFilters,\n EntityListContext,\n EntityListContextProps,\n} from '../hooks/useEntityListProvider';\n\nexport const MockEntityListContextProvider = ({\n children,\n value,\n}: PropsWithChildren<{\n value?: Partial<EntityListContextProps>;\n}>) => {\n // Provides a default implementation that stores filter state, for testing components that\n // reflect filter state.\n const [filters, setFilters] = useState<DefaultEntityFilters>(\n value?.filters ?? {},\n );\n const updateFilters = useCallback(\n (\n update:\n | Partial<DefaultEntityFilters>\n | ((\n prevFilters: DefaultEntityFilters,\n ) => Partial<DefaultEntityFilters>),\n ) => {\n setFilters(prevFilters => {\n const newFilters =\n typeof update === 'function' ? update(prevFilters) : update;\n return { ...prevFilters, ...newFilters };\n });\n },\n [],\n );\n\n const defaultContext: EntityListContextProps = {\n entities: [],\n backendEntities: [],\n updateFilters,\n filters,\n loading: false,\n queryParameters: {},\n };\n\n // Extract value.filters to avoid overwriting it; some tests exercise filter updates. The value\n // provided is used as the initial seed in useState above.\n const { filters: _, ...otherContextFields } = value ?? {};\n\n return (\n <EntityListContext.Provider\n value={{ ...defaultContext, ...otherContextFields }}\n >\n {children}\n </EntityListContext.Provider>\n );\n};\n"],"names":["createApiRef","createRouteRef","ENTITY_DEFAULT_NAMESPACE","useRouteRefParams","createContext","useNavigate","useApi","errorApiRef","useAsyncRetry","useContext","VIEW_URL_ANNOTATION","EDIT_URL_ANNOTATION","SOURCE_LOCATION_ANNOTATION","parseLocationReference","RELATION_MEMBER_OF","getEntityName","stringifyEntityRef","RELATION_OWNED_BY","useMountedState","useState","qs","useAsyncFn","compact","isEqual","useCallback","useMemo","serializeEntityRef","forwardRef","Link","generatePath","React","useAsync","useRef","identityApiRef","groupBy","chunk","storageApiRef","useObservable","parseEntityRef","jwtDecoder","Alert","icon","CheckBoxOutlineBlankIcon","checkedIcon","CheckBoxIcon","Box","Typography","Autocomplete","FormControlLabel","Checkbox","ExpandMoreIcon","TextField","useStyles","makeStyles","Toolbar","FormControl","Input","InputAdornment","Search","IconButton","Clear","RELATION_PART_OF","OverflowTooltip","Table","alertApiRef","capitalize","Select","withStyles","Star","StarBorder","Tooltip","ORIGIN_LOCATION_ANNOTATION","configApiRef","Progress","ResponseErrorPanel","Button","DialogContentText","Divider","Dialog","DialogTitle","DialogContent","DialogActions","SettingsIcon","StarIcon","Card","Fragment","List","MenuItem","ListItemIcon","ListItemText","ListItemSecondaryAction"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs.js","sources":["../src/api.ts","../src/routes.ts","../src/hooks/useEntityCompoundName.ts","../src/hooks/useEntity.tsx","../src/utils/filters.ts","../src/utils/getEntityMetadataUrl.ts","../src/utils/getEntityRelations.ts","../src/utils/getEntitySourceLocation.ts","../src/utils/isOwnerOf.ts","../src/hooks/useEntityListProvider.tsx","../src/components/EntityRefLink/format.ts","../src/components/EntityRefLink/EntityRefLink.tsx","../src/components/EntityRefLink/EntityRefLinks.tsx","../src/filters.ts","../src/hooks/useEntityTypeFilter.tsx","../src/hooks/useEntityKinds.ts","../src/hooks/useOwnUser.ts","../src/hooks/useRelatedEntities.ts","../src/hooks/useStarredEntities.ts","../src/hooks/useEntityOwnership.ts","../src/components/EntityKindPicker/EntityKindPicker.tsx","../src/components/EntityLifecyclePicker/EntityLifecyclePicker.tsx","../src/components/EntityOwnerPicker/EntityOwnerPicker.tsx","../src/components/EntitySearchBar/EntitySearchBar.tsx","../src/components/EntityTable/columns.tsx","../src/components/EntityTable/presets.tsx","../src/components/EntityTable/EntityTable.tsx","../src/components/EntityTagPicker/EntityTagPicker.tsx","../src/components/EntityTypePicker/EntityTypePicker.tsx","../src/components/FavoriteEntity/FavoriteEntity.tsx","../src/components/UnregisterEntityDialog/useUnregisterEntityDialogState.ts","../src/components/UnregisterEntityDialog/UnregisterEntityDialog.tsx","../src/components/UserListPicker/UserListPicker.tsx","../src/testUtils/providers.tsx"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { CatalogApi } from '@backstage/catalog-client';\nimport { createApiRef } from '@backstage/core-plugin-api';\n\nexport const catalogApiRef = createApiRef<CatalogApi>({\n id: 'plugin.catalog.service',\n});\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity, ENTITY_DEFAULT_NAMESPACE } from '@backstage/catalog-model';\nimport { createRouteRef } from '@backstage/core-plugin-api';\n\nconst NoIcon = () => null;\n\n// TODO(Rugvip): Move these route refs back to the catalog plugin once we're all ported to using external routes\nexport const rootRoute = createRouteRef({\n icon: NoIcon,\n path: '',\n title: 'Catalog',\n});\nexport const catalogRouteRef = rootRoute;\n\nexport const entityRoute = createRouteRef({\n icon: NoIcon,\n path: ':namespace/:kind/:name/*',\n title: 'Entity',\n params: ['namespace', 'kind', 'name'],\n});\nexport const entityRouteRef = entityRoute;\n\n// Utility function to get suitable route params for entityRoute, given an\n// entity instance\nexport function entityRouteParams(entity: Entity) {\n return {\n kind: entity.kind.toLowerCase(),\n namespace:\n entity.metadata.namespace?.toLowerCase() ?? ENTITY_DEFAULT_NAMESPACE,\n name: entity.metadata.name,\n } as const;\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { entityRouteRef } from '../routes';\nimport { useRouteRefParams } from '@backstage/core-plugin-api';\n\n/**\n * Grabs entity kind, namespace, and name from the location\n */\nexport const useEntityCompoundName = () => {\n const { kind, namespace, name } = useRouteRefParams(entityRouteRef);\n return { kind, namespace, name };\n};\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { Entity } from '@backstage/catalog-model';\nimport { errorApiRef, useApi } from '@backstage/core-plugin-api';\nimport {\n createVersionedContext,\n createVersionedValueMap,\n useVersionedContext,\n} from '@backstage/version-bridge';\nimport React, {\n ReactNode,\n useEffect,\n createContext,\n Provider,\n Context,\n} from 'react';\nimport { useNavigate } from 'react-router';\nimport { useAsyncRetry } from 'react-use';\nimport { catalogApiRef } from '../api';\nimport { useEntityCompoundName } from './useEntityCompoundName';\n\ntype EntityLoadingStatus = {\n entity?: Entity;\n loading: boolean;\n error?: Error;\n refresh?: VoidFunction;\n};\n\n/**\n * @public\n * @deprecated use `useEntity` and `EntityProvider` or `AsyncEntityProvider` instead.\n */\nexport const EntityContext: Context<EntityLoadingStatus> =\n createContext<EntityLoadingStatus>({\n entity: undefined,\n loading: true,\n error: undefined,\n refresh: () => {},\n });\n// We grab this for use in the new provider, since we're overriding it later on\nconst OldEntityProvider = EntityContext.Provider;\n\n// This context has support for multiple concurrent versions of this package.\n// It is currently used in parallel with the old context in order to provide\n// a smooth transition, but will eventually be the only context we use.\nconst NewEntityContext =\n createVersionedContext<{ 1: EntityLoadingStatus }>('entity-context');\n\n/**\n * Properties for the AsyncEntityProvider component.\n *\n * @public\n */\nexport interface AsyncEntityProviderProps {\n children: ReactNode;\n entity?: Entity;\n loading: boolean;\n error?: Error;\n refresh?: VoidFunction;\n}\n\n/**\n * Provides a loaded entity to be picked up by the `useEntity` hook.\n *\n * @public\n */\nexport const AsyncEntityProvider = ({\n children,\n entity,\n loading,\n error,\n refresh,\n}: AsyncEntityProviderProps) => {\n const value = { entity, loading, error, refresh };\n // We provide both the old and the new context, since\n // consumers might be doing things like `useContext(EntityContext)`\n return (\n <OldEntityProvider value={value}>\n <NewEntityContext.Provider value={createVersionedValueMap({ 1: value })}>\n {children}\n </NewEntityContext.Provider>\n </OldEntityProvider>\n );\n};\n\n/**\n * Properties for the EntityProvider component.\n *\n * @public\n */\nexport interface EntityProviderProps {\n children: ReactNode;\n entity?: Entity;\n}\n\n/**\n * Provides an entity to be picked up by the `useEntity` hook.\n *\n * @public\n */\nexport const EntityProvider = ({ entity, children }: EntityProviderProps) => (\n <AsyncEntityProvider\n entity={entity}\n loading={!Boolean(entity)}\n error={undefined}\n refresh={undefined}\n children={children}\n />\n);\n\n// This is used for forwards compatibility with the new entity context\nconst CompatibilityProvider = ({\n value,\n children,\n}: {\n value: EntityLoadingStatus;\n children: ReactNode;\n}) => {\n return <AsyncEntityProvider {...value} children={children} />;\n};\nEntityContext.Provider = CompatibilityProvider as Provider<EntityLoadingStatus>;\n\nexport const useEntityFromUrl = (): EntityLoadingStatus => {\n const { kind, namespace, name } = useEntityCompoundName();\n const navigate = useNavigate();\n const errorApi = useApi(errorApiRef);\n const catalogApi = useApi(catalogApiRef);\n\n const {\n value: entity,\n error,\n loading,\n retry: refresh,\n } = useAsyncRetry(\n () => catalogApi.getEntityByName({ kind, namespace, name }),\n [catalogApi, kind, namespace, name],\n );\n\n useEffect(() => {\n if (!name) {\n errorApi.post(new Error('No name provided!'));\n navigate('/');\n }\n }, [errorApi, navigate, error, loading, entity, name]);\n\n return { entity, loading, error, refresh };\n};\n\n/**\n * Grab the current entity from the context and its current loading state.\n *\n * @public\n */\nexport function useEntity<T extends Entity = Entity>() {\n const versionedHolder =\n useVersionedContext<{ 1: EntityLoadingStatus }>('entity-context');\n\n if (!versionedHolder) {\n // TODO(Rugvip): Throw this once we fully migrate to the new context\n // throw new Error('Entity context is not available');\n\n return {\n entity: undefined as unknown as T,\n loading: true,\n error: undefined,\n refresh: () => {},\n };\n }\n\n const value = versionedHolder.atVersion(1);\n if (!value) {\n throw new Error('EntityContext v1 not available');\n }\n\n const { entity, loading, error, refresh } = value;\n return { entity: entity as T, loading, error, refresh };\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity } from '@backstage/catalog-model';\nimport { EntityFilter } from '../types';\n\nexport function reduceCatalogFilters(\n filters: EntityFilter[],\n): Record<string, string | symbol | (string | symbol)[]> {\n return filters.reduce((compoundFilter, filter) => {\n return {\n ...compoundFilter,\n ...(filter.getCatalogFilters ? filter.getCatalogFilters() : {}),\n };\n }, {} as Record<string, string | symbol | (string | symbol)[]>);\n}\n\nexport function reduceEntityFilters(\n filters: EntityFilter[],\n): (entity: Entity) => boolean {\n return (entity: Entity) =>\n filters.every(\n filter => !filter.filterEntity || filter.filterEntity(entity),\n );\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n EDIT_URL_ANNOTATION,\n Entity,\n VIEW_URL_ANNOTATION,\n} from '@backstage/catalog-model';\n\nexport function getEntityMetadataViewUrl(entity: Entity): string | undefined {\n return entity.metadata.annotations?.[VIEW_URL_ANNOTATION];\n}\n\nexport function getEntityMetadataEditUrl(entity: Entity): string | undefined {\n return entity.metadata.annotations?.[EDIT_URL_ANNOTATION];\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity, EntityName } from '@backstage/catalog-model';\n\n/**\n * Get the related entity references.\n */\nexport function getEntityRelations(\n entity: Entity | undefined,\n relationType: string,\n filter?: { kind: string },\n): EntityName[] {\n let entityNames =\n entity?.relations\n ?.filter(r => r.type === relationType)\n ?.map(r => r.target) || [];\n\n if (filter?.kind) {\n entityNames = entityNames?.filter(\n e => e.kind.toLowerCase() === filter.kind.toLowerCase(),\n );\n }\n\n return entityNames;\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n Entity,\n parseLocationReference,\n SOURCE_LOCATION_ANNOTATION,\n} from '@backstage/catalog-model';\nimport { ScmIntegrationRegistry } from '@backstage/integration';\n\nexport type EntitySourceLocation = {\n locationTargetUrl: string;\n integrationType?: string;\n};\n\nexport function getEntitySourceLocation(\n entity: Entity,\n scmIntegrationsApi: ScmIntegrationRegistry,\n): EntitySourceLocation | undefined {\n const sourceLocation =\n entity.metadata.annotations?.[SOURCE_LOCATION_ANNOTATION];\n\n if (!sourceLocation) {\n return undefined;\n }\n\n try {\n const sourceLocationRef = parseLocationReference(sourceLocation);\n const integration = scmIntegrationsApi.byUrl(sourceLocationRef.target);\n return {\n locationTargetUrl: sourceLocationRef.target,\n integrationType: integration?.type,\n };\n } catch {\n return undefined;\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n Entity,\n getEntityName,\n RELATION_MEMBER_OF,\n RELATION_OWNED_BY,\n stringifyEntityRef,\n} from '@backstage/catalog-model';\nimport { getEntityRelations } from './getEntityRelations';\n\n/**\n * Get the related entity references.\n */\nexport function isOwnerOf(owner: Entity, owned: Entity) {\n const possibleOwners = new Set(\n [\n ...getEntityRelations(owner, RELATION_MEMBER_OF, { kind: 'group' }),\n ...(owner ? [getEntityName(owner)] : []),\n ].map(stringifyEntityRef),\n );\n\n const owners = getEntityRelations(owned, RELATION_OWNED_BY).map(\n stringifyEntityRef,\n );\n\n for (const ownerItem of owners) {\n if (possibleOwners.has(ownerItem)) {\n return true;\n }\n }\n\n return false;\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity } from '@backstage/catalog-model';\nimport { compact, isEqual } from 'lodash';\nimport qs from 'qs';\nimport React, {\n createContext,\n PropsWithChildren,\n useCallback,\n useContext,\n useMemo,\n useState,\n} from 'react';\nimport { useAsyncFn, useDebounce, useMountedState } from 'react-use';\nimport { catalogApiRef } from '../api';\nimport {\n EntityKindFilter,\n EntityLifecycleFilter,\n EntityOwnerFilter,\n EntityTagFilter,\n EntityTextFilter,\n EntityTypeFilter,\n UserListFilter,\n} from '../filters';\nimport { EntityFilter } from '../types';\nimport { reduceCatalogFilters, reduceEntityFilters } from '../utils';\nimport { useApi } from '@backstage/core-plugin-api';\n\nexport type DefaultEntityFilters = {\n kind?: EntityKindFilter;\n type?: EntityTypeFilter;\n user?: UserListFilter;\n owners?: EntityOwnerFilter;\n lifecycles?: EntityLifecycleFilter;\n tags?: EntityTagFilter;\n text?: EntityTextFilter;\n};\n\nexport type EntityListContextProps<\n EntityFilters extends DefaultEntityFilters = DefaultEntityFilters,\n> = {\n /**\n * The currently registered filters, adhering to the shape of DefaultEntityFilters or an extension\n * of that default (to add custom filter types).\n */\n filters: EntityFilters;\n\n /**\n * The resolved list of catalog entities, after all filters are applied.\n */\n entities: Entity[];\n\n /**\n * The resolved list of catalog entities, after _only catalog-backend_ filters are applied.\n */\n backendEntities: Entity[];\n\n /**\n * Update one or more of the registered filters. Optional filters can be set to `undefined` to\n * reset the filter.\n */\n updateFilters: (\n filters:\n | Partial<EntityFilters>\n | ((prevFilters: EntityFilters) => Partial<EntityFilters>),\n ) => void;\n\n /**\n * Filter values from query parameters.\n */\n queryParameters: Partial<Record<keyof EntityFilters, string | string[]>>;\n\n loading: boolean;\n error?: Error;\n};\n\nexport const EntityListContext = createContext<\n EntityListContextProps<any> | undefined\n>(undefined);\n\ntype OutputState<EntityFilters extends DefaultEntityFilters> = {\n appliedFilters: EntityFilters;\n entities: Entity[];\n backendEntities: Entity[];\n queryParameters: Record<string, string | string[]>;\n};\n\nexport const EntityListProvider = <EntityFilters extends DefaultEntityFilters>({\n children,\n}: PropsWithChildren<{}>) => {\n const isMounted = useMountedState();\n const catalogApi = useApi(catalogApiRef);\n const [requestedFilters, setRequestedFilters] = useState<EntityFilters>(\n {} as EntityFilters,\n );\n const [outputState, setOutputState] = useState<OutputState<EntityFilters>>(\n () => {\n const query = qs.parse(window.location.search, {\n ignoreQueryPrefix: true,\n });\n return {\n appliedFilters: {} as EntityFilters,\n entities: [],\n backendEntities: [],\n queryParameters: (query.filters ?? {}) as Record<\n string,\n string | string[]\n >,\n };\n },\n );\n\n // The main async filter worker. Note that while it has a lot of dependencies\n // in terms of its implementation, the triggering only happens (debounced)\n // based on the requested filters changing.\n const [{ loading, error }, refresh] = useAsyncFn(\n async () => {\n const compacted = compact(Object.values(requestedFilters));\n const entityFilter = reduceEntityFilters(compacted);\n const backendFilter = reduceCatalogFilters(compacted);\n const previousBackendFilter = reduceCatalogFilters(\n compact(Object.values(outputState.appliedFilters)),\n );\n\n const queryParams = Object.keys(requestedFilters).reduce(\n (params, key) => {\n const filter: EntityFilter | undefined =\n requestedFilters[key as keyof EntityFilters];\n if (filter?.toQueryValue) {\n params[key] = filter.toQueryValue();\n }\n return params;\n },\n {} as Record<string, string | string[]>,\n );\n\n // TODO(mtlewis): currently entities will never be requested unless\n // there's at least one filter, we should allow an initial request\n // to happen with no filters.\n if (!isEqual(previousBackendFilter, backendFilter)) {\n // TODO(timbonicus): should limit fields here, but would need filter\n // fields + table columns\n const response = await catalogApi.getEntities({\n filter: backendFilter,\n });\n setOutputState({\n appliedFilters: requestedFilters,\n backendEntities: response.items,\n entities: response.items.filter(entityFilter),\n queryParameters: queryParams,\n });\n } else {\n setOutputState({\n appliedFilters: requestedFilters,\n backendEntities: outputState.backendEntities,\n entities: outputState.backendEntities.filter(entityFilter),\n queryParameters: queryParams,\n });\n }\n\n if (isMounted()) {\n const oldParams = qs.parse(window.location.search, {\n ignoreQueryPrefix: true,\n });\n const newParams = qs.stringify(\n { ...oldParams, filters: queryParams },\n { addQueryPrefix: true },\n );\n const newUrl = `${window.location.pathname}${newParams}`;\n // We use direct history manipulation since useSearchParams and\n // useNavigate in react-router-dom cause unnecessary extra rerenders.\n // Also make sure to replace the state rather than pushing, since we\n // don't want there to be back/forward slots for every single filter\n // change.\n window.history?.replaceState(null, document.title, newUrl);\n }\n },\n [catalogApi, requestedFilters, outputState],\n { loading: true },\n );\n\n // Slight debounce on the refresh, since (especially on page load) several\n // filters will be calling this in rapid succession.\n useDebounce(refresh, 10, [requestedFilters]);\n\n const updateFilters = useCallback(\n (\n update:\n | Partial<EntityFilter>\n | ((prevFilters: EntityFilters) => Partial<EntityFilters>),\n ) => {\n setRequestedFilters(prevFilters => {\n const newFilters =\n typeof update === 'function' ? update(prevFilters) : update;\n return { ...prevFilters, ...newFilters };\n });\n },\n [],\n );\n\n const value = useMemo(\n () => ({\n filters: outputState.appliedFilters,\n entities: outputState.entities,\n backendEntities: outputState.backendEntities,\n updateFilters,\n queryParameters: outputState.queryParameters,\n loading,\n error,\n }),\n [outputState, updateFilters, loading, error],\n );\n\n return (\n <EntityListContext.Provider value={value}>\n {children}\n </EntityListContext.Provider>\n );\n};\n\nexport function useEntityListProvider<\n EntityFilters extends DefaultEntityFilters = DefaultEntityFilters,\n>(): EntityListContextProps<EntityFilters> {\n const context = useContext(EntityListContext);\n if (!context)\n throw new Error(\n 'useEntityListProvider must be used within EntityListProvider',\n );\n return context;\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n Entity,\n EntityName,\n ENTITY_DEFAULT_NAMESPACE,\n serializeEntityRef,\n} from '@backstage/catalog-model';\n\nexport function formatEntityRefTitle(\n entityRef: Entity | EntityName,\n opts?: { defaultKind?: string },\n) {\n const defaultKind = opts?.defaultKind;\n let kind;\n let namespace;\n let name;\n\n if ('metadata' in entityRef) {\n kind = entityRef.kind;\n namespace = entityRef.metadata.namespace;\n name = entityRef.metadata.name;\n } else {\n kind = entityRef.kind;\n namespace = entityRef.namespace;\n name = entityRef.name;\n }\n\n if (namespace === ENTITY_DEFAULT_NAMESPACE) {\n namespace = undefined;\n }\n\n kind = kind.toLowerCase();\n\n return `${serializeEntityRef({\n kind: defaultKind && defaultKind.toLowerCase() === kind ? undefined : kind,\n name,\n namespace,\n })}`;\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {\n Entity,\n EntityName,\n ENTITY_DEFAULT_NAMESPACE,\n} from '@backstage/catalog-model';\nimport React, { forwardRef } from 'react';\nimport { generatePath } from 'react-router';\nimport { entityRoute } from '../../routes';\nimport { formatEntityRefTitle } from './format';\nimport { Link, LinkProps } from '@backstage/core-components';\n\nexport type EntityRefLinkProps = {\n entityRef: Entity | EntityName;\n defaultKind?: string;\n children?: React.ReactNode;\n} & Omit<LinkProps, 'to'>;\n\nexport const EntityRefLink = forwardRef<any, EntityRefLinkProps>(\n (props, ref) => {\n const { entityRef, defaultKind, children, ...linkProps } = props;\n\n let kind;\n let namespace;\n let name;\n\n if ('metadata' in entityRef) {\n kind = entityRef.kind;\n namespace = entityRef.metadata.namespace;\n name = entityRef.metadata.name;\n } else {\n kind = entityRef.kind;\n namespace = entityRef.namespace;\n name = entityRef.name;\n }\n\n kind = kind.toLocaleLowerCase('en-US');\n\n const routeParams = {\n kind,\n namespace:\n namespace?.toLocaleLowerCase('en-US') ?? ENTITY_DEFAULT_NAMESPACE,\n name,\n };\n\n // TODO: Use useRouteRef here to generate the path\n return (\n <Link\n {...linkProps}\n ref={ref}\n to={generatePath(`/catalog/${entityRoute.path}`, routeParams)}\n >\n {children}\n {!children && formatEntityRefTitle(entityRef, { defaultKind })}\n </Link>\n );\n },\n);\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { Entity, EntityName } from '@backstage/catalog-model';\nimport React from 'react';\nimport { EntityRefLink } from './EntityRefLink';\nimport { LinkProps } from '@backstage/core-components';\n\nexport type EntityRefLinksProps = {\n entityRefs: (Entity | EntityName)[];\n defaultKind?: string;\n} & Omit<LinkProps, 'to'>;\n\nexport const EntityRefLinks = ({\n entityRefs,\n defaultKind,\n ...linkProps\n}: EntityRefLinksProps) => (\n <>\n {entityRefs.map((r, i) => (\n <React.Fragment key={i}>\n {i > 0 && ', '}\n <EntityRefLink {...linkProps} entityRef={r} defaultKind={defaultKind} />\n </React.Fragment>\n ))}\n </>\n);\n","/*\n * Copyright 2021 Spotify AB\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity, RELATION_OWNED_BY } from '@backstage/catalog-model';\nimport { formatEntityRefTitle } from './components/EntityRefLink';\nimport { EntityFilter, UserListFilterKind } from './types';\nimport { getEntityRelations } from './utils';\n\nexport class EntityKindFilter implements EntityFilter {\n constructor(readonly value: string) {}\n\n getCatalogFilters(): Record<string, string | string[]> {\n return { kind: this.value };\n }\n\n toQueryValue(): string {\n return this.value;\n }\n}\n\nexport class EntityTypeFilter implements EntityFilter {\n constructor(readonly value: string | string[]) {}\n\n // Simplify `string | string[]` for consumers, always returns an array\n getTypes(): string[] {\n return Array.isArray(this.value) ? this.value : [this.value];\n }\n\n getCatalogFilters(): Record<string, string | string[]> {\n return { 'spec.type': this.getTypes() };\n }\n\n toQueryValue(): string[] {\n return this.getTypes();\n }\n}\n\nexport class EntityTagFilter implements EntityFilter {\n constructor(readonly values: string[]) {}\n\n filterEntity(entity: Entity): boolean {\n return this.values.every(v => (entity.metadata.tags ?? []).includes(v));\n }\n\n toQueryValue(): string[] {\n return this.values;\n }\n}\n\nexport class EntityTextFilter implements EntityFilter {\n constructor(readonly value: string) {}\n\n filterEntity(entity: Entity): boolean {\n const upperCaseValue = this.value.toLocaleUpperCase('en-US');\n\n return (\n entity.metadata.name\n .toLocaleUpperCase('en-US')\n .includes(upperCaseValue) ||\n `${entity.metadata.title}`\n .toLocaleUpperCase('en-US')\n .includes(upperCaseValue) ||\n entity.metadata.tags\n ?.join('')\n .toLocaleUpperCase('en-US')\n .indexOf(upperCaseValue) !== -1\n );\n }\n}\n\nexport class EntityOwnerFilter implements EntityFilter {\n constructor(readonly values: string[]) {}\n\n filterEntity(entity: Entity): boolean {\n return this.values.some(v =>\n getEntityRelations(entity, RELATION_OWNED_BY).some(\n o => formatEntityRefTitle(o, { defaultKind: 'group' }) === v,\n ),\n );\n }\n\n toQueryValue(): string[] {\n return this.values;\n }\n}\n\nexport class EntityLifecycleFilter implements EntityFilter {\n constructor(readonly values: string[]) {}\n\n filterEntity(entity: Entity): boolean {\n return this.values.some(v => entity.spec?.lifecycle === v);\n }\n\n toQueryValue(): string[] {\n return this.values;\n }\n}\n\nexport class UserListFilter implements EntityFilter {\n constructor(\n readonly value: UserListFilterKind,\n readonly isOwnedEntity: (entity: Entity) => boolean,\n readonly isStarredEntity: (entity: Entity) => boolean,\n ) {}\n\n filterEntity(entity: Entity): boolean {\n switch (this.value) {\n case 'owned':\n return this.isOwnedEntity(entity);\n case 'starred':\n return this.isStarredEntity(entity);\n default:\n return true;\n }\n }\n\n toQueryValue(): string {\n return this.value;\n }\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { useEffect, useMemo, useRef, useState } from 'react';\nimport { useAsync } from 'react-use';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { catalogApiRef } from '../api';\nimport { useEntityListProvider } from './useEntityListProvider';\nimport { EntityTypeFilter } from '../filters';\n\ntype EntityTypeReturn = {\n loading: boolean;\n error?: Error;\n availableTypes: string[];\n selectedTypes: string[];\n setSelectedTypes: (types: string[]) => void;\n};\n\n/**\n * A hook built on top of `useEntityListProvider` for enabling selection of valid `spec.type` values\n * based on the selected EntityKindFilter.\n */\nexport function useEntityTypeFilter(): EntityTypeReturn {\n const catalogApi = useApi(catalogApiRef);\n const {\n filters: { kind: kindFilter, type: typeFilter },\n queryParameters,\n updateFilters,\n } = useEntityListProvider();\n\n const queryParamTypes = [queryParameters.type]\n .flat()\n .filter(Boolean) as string[];\n const [selectedTypes, setSelectedTypes] = useState(\n queryParamTypes.length ? queryParamTypes : typeFilter?.getTypes() ?? [],\n );\n\n const [availableTypes, setAvailableTypes] = useState<string[]>([]);\n const kind = useMemo(() => kindFilter?.value, [kindFilter]);\n\n // Load all valid spec.type values straight from the catalogApi, paying attention to only the\n // kind filter for a complete list.\n const {\n error,\n loading,\n value: entities,\n } = useAsync(async () => {\n if (kind) {\n const items = await catalogApi\n .getEntities({\n filter: { kind },\n fields: ['spec.type'],\n })\n .then(response => response.items);\n return items;\n }\n return [];\n }, [kind, catalogApi]);\n\n const entitiesRef = useRef(entities);\n useEffect(() => {\n const oldEntities = entitiesRef.current;\n entitiesRef.current = entities;\n // Delay processing hook until kind and entity load updates have settled to generate list of types;\n // This prevents reseting the type filter due to saved type value from query params not matching the\n // empty set of type values while values are still being loaded; also only run this hook on changes\n // to entities\n if (loading || !kind || oldEntities === entities) {\n return;\n }\n\n // Resolve the unique set of types from returned entities; could be optimized by a new endpoint\n // in the catalog-backend that does this, rather than loading entities with redundant types.\n if (!entities) return;\n\n // Sort by entity count descending, so the most common types appear on top\n const countByType = entities.reduce((acc, entity) => {\n if (typeof entity.spec?.type !== 'string') return acc;\n\n const entityType = entity.spec.type.toLocaleLowerCase('en-US');\n if (!acc[entityType]) {\n acc[entityType] = 0;\n }\n acc[entityType] += 1;\n return acc;\n }, {} as Record<string, number>);\n\n const newTypes = Object.entries(countByType)\n .sort(([, count1], [, count2]) => count2 - count1)\n .map(([type]) => type);\n setAvailableTypes(newTypes);\n\n // Update type filter to only valid values when the list of available types has changed\n const stillValidTypes = selectedTypes.filter(value =>\n newTypes.includes(value),\n );\n setSelectedTypes(stillValidTypes);\n }, [loading, kind, selectedTypes, setSelectedTypes, entities]);\n\n useEffect(() => {\n updateFilters({\n type: selectedTypes.length\n ? new EntityTypeFilter(selectedTypes)\n : undefined,\n });\n }, [selectedTypes, updateFilters]);\n\n return {\n loading,\n error,\n availableTypes,\n selectedTypes,\n setSelectedTypes,\n };\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { useAsync } from 'react-use';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { catalogApiRef } from '../api';\n\n// Retrieve a list of unique entity kinds present in the catalog\nexport function useEntityKinds() {\n const catalogApi = useApi(catalogApiRef);\n\n const {\n error,\n loading,\n value: kinds,\n } = useAsync(async () => {\n const entities = await catalogApi\n .getEntities({ fields: ['kind'] })\n .then(response => response.items);\n\n return [...new Set(entities.map(e => e.kind))].sort();\n });\n return { error, loading, kinds };\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { UserEntity } from '@backstage/catalog-model';\nimport { useAsync } from 'react-use';\nimport { AsyncState } from 'react-use/lib/useAsync';\nimport { catalogApiRef } from '../api';\nimport { identityApiRef, useApi } from '@backstage/core-plugin-api';\n\n/**\n * Get the catalog User entity (if any) that matches the logged-in user.\n */\nexport function useOwnUser(): AsyncState<UserEntity | undefined> {\n const catalogApi = useApi(catalogApiRef);\n const identityApi = useApi(identityApiRef);\n\n // TODO: get the full entity (or at least the full entity name) from the\n // identityApi\n return useAsync(\n () =>\n catalogApi.getEntityByName({\n kind: 'User',\n namespace: 'default',\n name: identityApi.getUserId(),\n }) as Promise<UserEntity | undefined>,\n [catalogApi, identityApi],\n );\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { Entity, EntityRelation } from '@backstage/catalog-model';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { chunk, groupBy } from 'lodash';\nimport { useAsync } from 'react-use';\nimport { catalogApiRef } from '../api';\n\nconst BATCH_SIZE = 20;\n\nexport function useRelatedEntities(\n entity: Entity,\n { type, kind }: { type?: string; kind?: string },\n): {\n entities: Entity[] | undefined;\n loading: boolean;\n error: Error | undefined;\n} {\n const catalogApi = useApi(catalogApiRef);\n const {\n loading,\n value: entities,\n error,\n } = useAsync(async () => {\n const relations =\n entity.relations &&\n entity.relations.filter(\n r =>\n (!type || r.type.toLowerCase() === type.toLowerCase()) &&\n (!kind || r.target.kind.toLowerCase() === kind.toLowerCase()),\n );\n\n if (!relations) {\n return [];\n }\n\n // Group the relations by kind and namespace to reduce the size of the request query string.\n // Without this grouping, the kind and namespace would need to be specified for each relation, e.g.\n // `filter=kind=component,namespace=default,name=example1&filter=kind=component,namespace=default,name=example2`\n // with grouping, we can generate a query a string like\n // `filter=kind=component,namespace=default,name=example1,example2`\n const relationsByKindAndNamespace: EntityRelation[][] = Object.values(\n groupBy(relations, ({ target }) => {\n return `${target.kind}:${target.namespace}`.toLowerCase();\n }),\n );\n\n // Split the names within each group into batches to further reduce the query string length.\n const batchedRelationsByKindAndNamespace: {\n kind: string;\n namespace: string;\n nameBatches: string[][];\n }[] = [];\n for (const rs of relationsByKindAndNamespace) {\n batchedRelationsByKindAndNamespace.push({\n // All relations in a group have the same kind and namespace, so its arbitrary which we pick\n kind: rs[0].target.kind,\n namespace: rs[0].target.namespace,\n nameBatches: chunk(\n rs.map(r => r.target.name),\n BATCH_SIZE,\n ),\n });\n }\n\n const results = await Promise.all(\n batchedRelationsByKindAndNamespace.flatMap(rs => {\n return rs.nameBatches.map(names => {\n return catalogApi.getEntities({\n filter: {\n kind: rs.kind,\n 'metadata.namespace': rs.namespace,\n 'metadata.name': names,\n },\n });\n });\n }),\n );\n\n return results.flatMap(r => r.items);\n }, [entity, type]);\n\n return {\n entities,\n loading,\n error,\n };\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity } from '@backstage/catalog-model';\nimport { storageApiRef, useApi } from '@backstage/core-plugin-api';\nimport { useCallback, useEffect, useState } from 'react';\nimport { useObservable } from 'react-use';\n\nconst buildEntityKey = (component: Entity) =>\n `entity:${component.kind}:${component.metadata.namespace ?? 'default'}:${\n component.metadata.name\n }`;\n\nexport const useStarredEntities = () => {\n const storageApi = useApi(storageApiRef);\n const settingsStore = storageApi.forBucket('settings');\n const rawStarredEntityKeys =\n settingsStore.get<string[]>('starredEntities') ?? [];\n\n const [starredEntities, setStarredEntities] = useState(\n new Set(rawStarredEntityKeys),\n );\n\n const observedItems = useObservable(\n settingsStore.observe$<string[]>('starredEntities'),\n );\n\n useEffect(() => {\n if (observedItems?.newValue) {\n const currentValue = observedItems?.newValue ?? [];\n setStarredEntities(new Set(currentValue));\n }\n }, [observedItems?.newValue]);\n\n const toggleStarredEntity = useCallback(\n (entity: Entity) => {\n const entityKey = buildEntityKey(entity);\n if (starredEntities.has(entityKey)) {\n starredEntities.delete(entityKey);\n } else {\n starredEntities.add(entityKey);\n }\n\n settingsStore.set('starredEntities', Array.from(starredEntities));\n },\n [starredEntities, settingsStore],\n );\n\n const isStarredEntity = useCallback(\n (entity: Entity) => {\n const entityKey = buildEntityKey(entity);\n return starredEntities.has(entityKey);\n },\n [starredEntities],\n );\n\n return {\n starredEntities,\n toggleStarredEntity,\n isStarredEntity,\n };\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { CatalogApi } from '@backstage/catalog-client';\nimport {\n Entity,\n EntityName,\n parseEntityRef,\n RELATION_MEMBER_OF,\n RELATION_OWNED_BY,\n stringifyEntityRef,\n} from '@backstage/catalog-model';\nimport {\n IdentityApi,\n identityApiRef,\n useApi,\n} from '@backstage/core-plugin-api';\nimport jwtDecoder from 'jwt-decode';\nimport { useMemo } from 'react';\nimport { useAsync } from 'react-use';\nimport { catalogApiRef } from '../api';\nimport { getEntityRelations } from '../utils/getEntityRelations';\n\n// Takes a user ID from the identity, which can be on basically any form, and\n// returns an entity ref. E.g. if the input is \"foo\", it returns\n// \"user:default/foo\" to make sure it's a full ref.\nfunction extendUserId(id: string): string {\n try {\n const ref = parseEntityRef(id, {\n defaultKind: 'User',\n defaultNamespace: 'default',\n });\n return stringifyEntityRef(ref);\n } catch {\n return id;\n }\n}\n\n// Takes the relevant parts of the Backstage identity, and translates them into\n// a list of entity refs on string form that represent the user's ownership\n// connections.\nexport async function loadIdentityOwnerRefs(\n identityApi: IdentityApi,\n): Promise<string[]> {\n const id = identityApi.getUserId();\n const token = await identityApi.getIdToken();\n const result: string[] = [];\n\n if (id) {\n result.push(extendUserId(id));\n }\n\n if (token) {\n try {\n const decoded = jwtDecoder(token) as any;\n if (decoded?.ent) {\n [decoded.ent]\n .flat()\n .filter(x => typeof x === 'string')\n .map(x => x.toLocaleLowerCase('en-US'))\n .forEach(x => result.push(x));\n }\n } catch {\n // ignore\n }\n }\n\n return result;\n}\n\n// Takes the relevant parts of the User entity corresponding to the Backstage\n// identity, and translates them into a list of entity refs on string form that\n// represent the user's ownership connections.\nexport async function loadCatalogOwnerRefs(\n catalogApi: CatalogApi,\n identityOwnerRefs: string[],\n): Promise<string[]> {\n const result = new Array<string>();\n\n const primaryUserRef = identityOwnerRefs.find(ref => ref.startsWith('user:'));\n if (primaryUserRef) {\n const entity = await catalogApi.getEntityByName(\n parseEntityRef(primaryUserRef),\n );\n if (entity) {\n const memberOf = getEntityRelations(entity, RELATION_MEMBER_OF, {\n kind: 'Group',\n });\n for (const group of memberOf) {\n result.push(stringifyEntityRef(group));\n }\n }\n }\n\n return result;\n}\n\n/**\n * Returns a function that checks whether the currently signed-in user is an\n * owner of a given entity. When the hook is initially mounted, the loading\n * flag will be true and the results returned from the function will always be\n * false.\n */\nexport function useEntityOwnership(): {\n loading: boolean;\n isOwnedEntity: (entity: Entity | EntityName) => boolean;\n} {\n const identityApi = useApi(identityApiRef);\n const catalogApi = useApi(catalogApiRef);\n\n // Trigger load only on mount\n const { loading, value: refs } = useAsync(async () => {\n const identityRefs = await loadIdentityOwnerRefs(identityApi);\n const catalogRefs = await loadCatalogOwnerRefs(catalogApi, identityRefs);\n return new Set([...identityRefs, ...catalogRefs]);\n }, []);\n\n const isOwnedEntity = useMemo(() => {\n const myOwnerRefs = new Set(refs ?? []);\n return (entity: Entity | EntityName) => {\n const entityOwnerRefs = (\n 'metadata' in entity\n ? getEntityRelations(entity, RELATION_OWNED_BY)\n : [entity]\n ).map(stringifyEntityRef);\n for (const ref of entityOwnerRefs) {\n if (myOwnerRefs.has(ref)) {\n return true;\n }\n }\n return false;\n };\n }, [refs]);\n\n return useMemo(() => ({ loading, isOwnedEntity }), [loading, isOwnedEntity]);\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React, { useEffect, useState } from 'react';\nimport { Alert } from '@material-ui/lab';\nimport { useEntityListProvider } from '../../hooks';\nimport { EntityKindFilter } from '../../filters';\n\ntype EntityKindFilterProps = {\n initialFilter?: string;\n hidden: boolean;\n};\n\nexport const EntityKindPicker = ({\n initialFilter,\n hidden,\n}: EntityKindFilterProps) => {\n const { updateFilters, queryParameters } = useEntityListProvider();\n const [selectedKind] = useState(\n [queryParameters.kind].flat()[0] ?? initialFilter,\n );\n\n useEffect(() => {\n updateFilters({\n kind: selectedKind ? new EntityKindFilter(selectedKind) : undefined,\n });\n }, [selectedKind, updateFilters]);\n\n if (hidden) return null;\n\n // TODO(timbonicus): This should load available kinds from the catalog-backend, similar to\n // EntityTypePicker.\n\n return <Alert severity=\"warning\">Kind filter not yet available</Alert>;\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity } from '@backstage/catalog-model';\nimport {\n Box,\n Checkbox,\n FormControlLabel,\n TextField,\n Typography,\n} from '@material-ui/core';\nimport CheckBoxIcon from '@material-ui/icons/CheckBox';\nimport CheckBoxOutlineBlankIcon from '@material-ui/icons/CheckBoxOutlineBlank';\nimport ExpandMoreIcon from '@material-ui/icons/ExpandMore';\nimport { Autocomplete } from '@material-ui/lab';\nimport React, { useEffect, useMemo, useState } from 'react';\nimport { useEntityListProvider } from '../../hooks/useEntityListProvider';\nimport { EntityLifecycleFilter } from '../../filters';\n\nconst icon = <CheckBoxOutlineBlankIcon fontSize=\"small\" />;\nconst checkedIcon = <CheckBoxIcon fontSize=\"small\" />;\n\nexport const EntityLifecyclePicker = () => {\n const { updateFilters, backendEntities, filters, queryParameters } =\n useEntityListProvider();\n\n const queryParamLifecycles = [queryParameters.lifecycles]\n .flat()\n .filter(Boolean) as string[];\n const [selectedLifecycles, setSelectedLifecycles] = useState(\n queryParamLifecycles.length\n ? queryParamLifecycles\n : filters.lifecycles?.values ?? [],\n );\n\n useEffect(() => {\n updateFilters({\n lifecycles: selectedLifecycles.length\n ? new EntityLifecycleFilter(selectedLifecycles)\n : undefined,\n });\n }, [selectedLifecycles, updateFilters]);\n\n const availableLifecycles = useMemo(\n () =>\n [\n ...new Set(\n backendEntities\n .map((e: Entity) => e.spec?.lifecycle)\n .filter(Boolean) as string[],\n ),\n ].sort(),\n [backendEntities],\n );\n\n if (!availableLifecycles.length) return null;\n\n return (\n <Box pb={1} pt={1}>\n <Typography variant=\"button\">Lifecycle</Typography>\n <Autocomplete\n aria-label=\"Lifecycle\"\n multiple\n options={availableLifecycles}\n value={selectedLifecycles}\n onChange={(_: object, value: string[]) => setSelectedLifecycles(value)}\n renderOption={(option, { selected }) => (\n <FormControlLabel\n control={\n <Checkbox\n icon={icon}\n checkedIcon={checkedIcon}\n checked={selected}\n />\n }\n label={option}\n />\n )}\n size=\"small\"\n popupIcon={<ExpandMoreIcon data-testid=\"lifecycle-picker-expand\" />}\n renderInput={params => <TextField {...params} variant=\"outlined\" />}\n />\n </Box>\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity, RELATION_OWNED_BY } from '@backstage/catalog-model';\nimport {\n Box,\n Checkbox,\n FormControlLabel,\n TextField,\n Typography,\n} from '@material-ui/core';\nimport CheckBoxIcon from '@material-ui/icons/CheckBox';\nimport CheckBoxOutlineBlankIcon from '@material-ui/icons/CheckBoxOutlineBlank';\nimport ExpandMoreIcon from '@material-ui/icons/ExpandMore';\nimport { Autocomplete } from '@material-ui/lab';\nimport React, { useEffect, useMemo, useState } from 'react';\nimport { useEntityListProvider } from '../../hooks/useEntityListProvider';\nimport { EntityOwnerFilter } from '../../filters';\nimport { getEntityRelations } from '../../utils';\nimport { formatEntityRefTitle } from '../EntityRefLink';\n\nconst icon = <CheckBoxOutlineBlankIcon fontSize=\"small\" />;\nconst checkedIcon = <CheckBoxIcon fontSize=\"small\" />;\n\nexport const EntityOwnerPicker = () => {\n const { updateFilters, backendEntities, filters, queryParameters } =\n useEntityListProvider();\n\n const queryParamOwners = [queryParameters.owners]\n .flat()\n .filter(Boolean) as string[];\n const [selectedOwners, setSelectedOwners] = useState(\n queryParamOwners.length ? queryParamOwners : filters.owners?.values ?? [],\n );\n\n useEffect(() => {\n updateFilters({\n owners: selectedOwners.length\n ? new EntityOwnerFilter(selectedOwners)\n : undefined,\n });\n }, [selectedOwners, updateFilters]);\n\n const availableOwners = useMemo(\n () =>\n [\n ...new Set(\n backendEntities\n .flatMap((e: Entity) =>\n getEntityRelations(e, RELATION_OWNED_BY).map(o =>\n formatEntityRefTitle(o, { defaultKind: 'group' }),\n ),\n )\n .filter(Boolean) as string[],\n ),\n ].sort(),\n [backendEntities],\n );\n\n if (!availableOwners.length) return null;\n\n return (\n <Box pb={1} pt={1}>\n <Typography variant=\"button\">Owner</Typography>\n <Autocomplete\n multiple\n aria-label=\"Owner\"\n options={availableOwners}\n value={selectedOwners}\n onChange={(_: object, value: string[]) => setSelectedOwners(value)}\n renderOption={(option, { selected }) => (\n <FormControlLabel\n control={\n <Checkbox\n icon={icon}\n checkedIcon={checkedIcon}\n checked={selected}\n />\n }\n label={option}\n />\n )}\n size=\"small\"\n popupIcon={<ExpandMoreIcon data-testid=\"owner-picker-expand\" />}\n renderInput={params => <TextField {...params} variant=\"outlined\" />}\n />\n </Box>\n );\n};\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n FormControl,\n IconButton,\n Input,\n InputAdornment,\n makeStyles,\n Toolbar,\n} from '@material-ui/core';\nimport Clear from '@material-ui/icons/Clear';\nimport Search from '@material-ui/icons/Search';\nimport React, { useState } from 'react';\nimport { useDebounce } from 'react-use';\nimport { useEntityListProvider } from '../../hooks/useEntityListProvider';\nimport { EntityTextFilter } from '../../filters';\n\nconst useStyles = makeStyles(_theme => ({\n searchToolbar: {\n paddingLeft: 0,\n paddingRight: 0,\n },\n}));\n\nexport const EntitySearchBar = () => {\n const styles = useStyles();\n\n const { filters, updateFilters } = useEntityListProvider();\n const [search, setSearch] = useState(filters.text?.value ?? '');\n\n useDebounce(\n () => {\n updateFilters({\n text: search.length ? new EntityTextFilter(search) : undefined,\n });\n },\n 250,\n [search, updateFilters],\n );\n\n return (\n <Toolbar className={styles.searchToolbar}>\n <FormControl>\n <Input\n id=\"input-with-icon-adornment\"\n placeholder=\"Search\"\n autoComplete=\"off\"\n onChange={event => setSearch(event.target.value)}\n value={search}\n startAdornment={\n <InputAdornment position=\"start\">\n <Search />\n </InputAdornment>\n }\n endAdornment={\n <InputAdornment position=\"end\">\n <IconButton\n aria-label=\"clear search\"\n onClick={() => setSearch('')}\n edge=\"end\"\n disabled={search.length === 0}\n >\n <Clear />\n </IconButton>\n </InputAdornment>\n }\n />\n </FormControl>\n </Toolbar>\n );\n};\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n Entity,\n EntityName,\n RELATION_OWNED_BY,\n RELATION_PART_OF,\n} from '@backstage/catalog-model';\nimport React from 'react';\nimport { getEntityRelations } from '../../utils';\nimport {\n EntityRefLink,\n EntityRefLinks,\n formatEntityRefTitle,\n} from '../EntityRefLink';\nimport { OverflowTooltip, TableColumn } from '@backstage/core-components';\n\nexport function createEntityRefColumn<T extends Entity>({\n defaultKind,\n}: {\n defaultKind?: string;\n}): TableColumn<T> {\n function formatContent(entity: T): string {\n return formatEntityRefTitle(entity, {\n defaultKind,\n });\n }\n\n return {\n title: 'Name',\n highlight: true,\n customFilterAndSearch(filter, entity) {\n // TODO: We could implement this more efficiently, like searching over\n // each field that is displayed individually (kind, namespace, name).\n // but that migth confuse the user as it will behave different than a\n // simple text search.\n // Another alternative would be to cache the values. But writing them\n // into the entity feels bad too.\n return formatContent(entity).includes(filter);\n },\n customSort(entity1, entity2) {\n // TODO: We could implement this more efficiently by comparing field by field.\n // This has similar issues as above.\n return formatContent(entity1).localeCompare(formatContent(entity2));\n },\n render: entity => (\n <EntityRefLink entityRef={entity} defaultKind={defaultKind} />\n ),\n };\n}\n\nexport function createEntityRelationColumn<T extends Entity>({\n title,\n relation,\n defaultKind,\n filter: entityFilter,\n}: {\n title: string;\n relation: string;\n defaultKind?: string;\n filter?: { kind: string };\n}): TableColumn<T> {\n function getRelations(entity: T): EntityName[] {\n return getEntityRelations(entity, relation, entityFilter);\n }\n\n function formatContent(entity: T): string {\n return getRelations(entity)\n .map(r => formatEntityRefTitle(r, { defaultKind }))\n .join(', ');\n }\n\n return {\n title,\n customFilterAndSearch(filter, entity) {\n return formatContent(entity).includes(filter);\n },\n customSort(entity1, entity2) {\n return formatContent(entity1).localeCompare(formatContent(entity2));\n },\n render: entity => {\n return (\n <EntityRefLinks\n entityRefs={getRelations(entity)}\n defaultKind={defaultKind}\n />\n );\n },\n };\n}\n\nexport function createOwnerColumn<T extends Entity>(): TableColumn<T> {\n return createEntityRelationColumn({\n title: 'Owner',\n relation: RELATION_OWNED_BY,\n defaultKind: 'group',\n });\n}\n\nexport function createDomainColumn<T extends Entity>(): TableColumn<T> {\n return createEntityRelationColumn({\n title: 'Domain',\n relation: RELATION_PART_OF,\n defaultKind: 'domain',\n filter: {\n kind: 'domain',\n },\n });\n}\n\nexport function createSystemColumn<T extends Entity>(): TableColumn<T> {\n return createEntityRelationColumn({\n title: 'System',\n relation: RELATION_PART_OF,\n defaultKind: 'system',\n filter: {\n kind: 'system',\n },\n });\n}\n\nexport function createMetadataDescriptionColumn<\n T extends Entity,\n>(): TableColumn<T> {\n return {\n title: 'Description',\n field: 'metadata.description',\n render: entity => (\n <OverflowTooltip\n text={entity.metadata.description}\n placement=\"bottom-start\"\n line={2}\n />\n ),\n width: 'auto',\n };\n}\n\nexport function createSpecLifecycleColumn<T extends Entity>(): TableColumn<T> {\n return {\n title: 'Lifecycle',\n field: 'spec.lifecycle',\n };\n}\n\nexport function createSpecTypeColumn<T extends Entity>(): TableColumn<T> {\n return {\n title: 'Type',\n field: 'spec.type',\n };\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { ComponentEntity, SystemEntity } from '@backstage/catalog-model';\nimport {\n createDomainColumn,\n createEntityRefColumn,\n createMetadataDescriptionColumn,\n createOwnerColumn,\n createSpecLifecycleColumn,\n createSpecTypeColumn,\n createSystemColumn,\n} from './columns';\nimport { TableColumn } from '@backstage/core-components';\n\nexport const systemEntityColumns: TableColumn<SystemEntity>[] = [\n createEntityRefColumn({ defaultKind: 'system' }),\n createDomainColumn(),\n createOwnerColumn(),\n createMetadataDescriptionColumn(),\n];\n\nexport const componentEntityColumns: TableColumn<ComponentEntity>[] = [\n createEntityRefColumn({ defaultKind: 'component' }),\n createSystemColumn(),\n createOwnerColumn(),\n createSpecTypeColumn(),\n createSpecLifecycleColumn(),\n createMetadataDescriptionColumn(),\n];\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity } from '@backstage/catalog-model';\nimport { makeStyles } from '@material-ui/core';\nimport React, { ReactNode } from 'react';\nimport * as columnFactories from './columns';\nimport { componentEntityColumns, systemEntityColumns } from './presets';\nimport { Table, TableColumn } from '@backstage/core-components';\n\ntype Props<T extends Entity> = {\n title: string;\n variant?: 'gridItem';\n entities: T[];\n emptyContent?: ReactNode;\n columns: TableColumn<T>[];\n};\n\nconst useStyles = makeStyles(theme => ({\n empty: {\n padding: theme.spacing(2),\n display: 'flex',\n justifyContent: 'center',\n },\n}));\n\nexport function EntityTable<T extends Entity>({\n entities,\n title,\n emptyContent,\n variant = 'gridItem',\n columns,\n}: Props<T>) {\n const classes = useStyles();\n const tableStyle: React.CSSProperties = {\n minWidth: '0',\n width: '100%',\n };\n\n if (variant === 'gridItem') {\n tableStyle.height = 'calc(100% - 10px)';\n }\n\n return (\n <Table<T>\n columns={columns}\n title={title}\n style={tableStyle}\n emptyContent={\n emptyContent && <div className={classes.empty}>{emptyContent}</div>\n }\n options={{\n // TODO: Toolbar padding if off compared to other cards, should be: padding: 16px 24px;\n search: false,\n paging: false,\n actionsColumnIndex: -1,\n padding: 'dense',\n }}\n data={entities}\n />\n );\n}\n\nEntityTable.columns = columnFactories;\n\nEntityTable.systemEntityColumns = systemEntityColumns;\n\nEntityTable.componentEntityColumns = componentEntityColumns;\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity } from '@backstage/catalog-model';\nimport {\n Box,\n Checkbox,\n FormControlLabel,\n TextField,\n Typography,\n} from '@material-ui/core';\nimport CheckBoxIcon from '@material-ui/icons/CheckBox';\nimport CheckBoxOutlineBlankIcon from '@material-ui/icons/CheckBoxOutlineBlank';\nimport ExpandMoreIcon from '@material-ui/icons/ExpandMore';\nimport { Autocomplete } from '@material-ui/lab';\nimport React, { useEffect, useMemo, useState } from 'react';\nimport { useEntityListProvider } from '../../hooks/useEntityListProvider';\nimport { EntityTagFilter } from '../../filters';\n\nconst icon = <CheckBoxOutlineBlankIcon fontSize=\"small\" />;\nconst checkedIcon = <CheckBoxIcon fontSize=\"small\" />;\n\nexport const EntityTagPicker = () => {\n const { updateFilters, backendEntities, filters, queryParameters } =\n useEntityListProvider();\n\n const queryParamTags = [queryParameters.tags]\n .flat()\n .filter(Boolean) as string[];\n const [selectedTags, setSelectedTags] = useState(\n queryParamTags.length ? queryParamTags : filters.tags?.values ?? [],\n );\n\n useEffect(() => {\n updateFilters({\n tags: selectedTags.length ? new EntityTagFilter(selectedTags) : undefined,\n });\n }, [selectedTags, updateFilters]);\n\n const availableTags = useMemo(\n () =>\n [\n ...new Set(\n backendEntities\n .flatMap((e: Entity) => e.metadata.tags)\n .filter(Boolean) as string[],\n ),\n ].sort(),\n [backendEntities],\n );\n\n if (!availableTags.length) return null;\n\n return (\n <Box pb={1} pt={1}>\n <Typography variant=\"button\">Tags</Typography>\n <Autocomplete\n multiple\n aria-label=\"Tags\"\n options={availableTags}\n value={selectedTags}\n onChange={(_: object, value: string[]) => setSelectedTags(value)}\n renderOption={(option, { selected }) => (\n <FormControlLabel\n control={\n <Checkbox\n icon={icon}\n checkedIcon={checkedIcon}\n checked={selected}\n />\n }\n label={option}\n />\n )}\n size=\"small\"\n popupIcon={<ExpandMoreIcon data-testid=\"tag-picker-expand\" />}\n renderInput={params => <TextField {...params} variant=\"outlined\" />}\n />\n </Box>\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React, { useEffect } from 'react';\nimport capitalize from 'lodash/capitalize';\nimport { Box } from '@material-ui/core';\nimport { useEntityTypeFilter } from '../../hooks/useEntityTypeFilter';\n\nimport { alertApiRef, useApi } from '@backstage/core-plugin-api';\nimport { Select } from '@backstage/core-components';\n\nexport const EntityTypePicker = () => {\n const alertApi = useApi(alertApiRef);\n const { error, availableTypes, selectedTypes, setSelectedTypes } =\n useEntityTypeFilter();\n\n useEffect(() => {\n if (error) {\n alertApi.post({\n message: `Failed to load entity types`,\n severity: 'error',\n });\n }\n }, [error, alertApi]);\n\n if (availableTypes.length === 0 || error) return null;\n\n const items = [\n { value: 'all', label: 'All' },\n ...availableTypes.map((type: string) => ({\n value: type,\n label: capitalize(type),\n })),\n ];\n\n return (\n <Box pb={1} pt={1}>\n <Select\n label=\"Type\"\n items={items}\n selected={(items.length > 1 ? selectedTypes[0] : undefined) ?? 'all'}\n onChange={value =>\n setSelectedTypes(value === 'all' ? [] : [String(value)])\n }\n />\n </Box>\n );\n};\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React, { ComponentProps } from 'react';\nimport { useStarredEntities } from '../../hooks/useStarredEntities';\nimport { IconButton, Tooltip, withStyles } from '@material-ui/core';\nimport StarBorder from '@material-ui/icons/StarBorder';\nimport Star from '@material-ui/icons/Star';\nimport { Entity } from '@backstage/catalog-model';\n\ntype Props = ComponentProps<typeof IconButton> & { entity: Entity };\n\nconst YellowStar = withStyles({\n root: {\n color: '#f3ba37',\n },\n})(Star);\n\nexport const favoriteEntityTooltip = (isStarred: boolean) =>\n isStarred ? 'Remove from favorites' : 'Add to favorites';\n\nexport const favoriteEntityIcon = (isStarred: boolean) =>\n isStarred ? <YellowStar /> : <StarBorder />;\n\n/**\n * IconButton for showing if a current entity is starred and adding/removing it from the favorite entities\n * @param props MaterialUI IconButton props extended by required `entity` prop\n */\nexport const FavoriteEntity = (props: Props) => {\n const { toggleStarredEntity, isStarredEntity } = useStarredEntities();\n const isStarred = isStarredEntity(props.entity);\n return (\n <IconButton\n color=\"inherit\"\n {...props}\n onClick={() => toggleStarredEntity(props.entity)}\n >\n <Tooltip title={favoriteEntityTooltip(isStarred)}>\n {favoriteEntityIcon(isStarred)}\n </Tooltip>\n </IconButton>\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n Entity,\n EntityName,\n getEntityName,\n ORIGIN_LOCATION_ANNOTATION,\n} from '@backstage/catalog-model';\nimport { catalogApiRef } from '../../api';\nimport { useCallback } from 'react';\nimport { useAsync } from 'react-use';\nimport { useApi } from '@backstage/core-plugin-api';\n\n/**\n * Each distinct state that the dialog can be in at any given time.\n */\nexport type UseUnregisterEntityDialogState =\n | {\n type: 'loading';\n }\n | {\n type: 'error';\n error: Error;\n }\n | {\n type: 'bootstrap';\n location: string;\n deleteEntity: () => Promise<void>;\n }\n | {\n type: 'unregister';\n location: string;\n colocatedEntities: EntityName[];\n unregisterLocation: () => Promise<void>;\n deleteEntity: () => Promise<void>;\n }\n | {\n type: 'only-delete';\n deleteEntity: () => Promise<void>;\n };\n\n/**\n * Houses the main logic for unregistering entities and their locations.\n */\nexport function useUnregisterEntityDialogState(\n entity: Entity,\n): UseUnregisterEntityDialogState {\n const catalogApi = useApi(catalogApiRef);\n const locationRef = entity.metadata.annotations?.[ORIGIN_LOCATION_ANNOTATION];\n const uid = entity.metadata.uid;\n const isBootstrap = locationRef === 'bootstrap:bootstrap';\n\n // Load the prerequisite data: what entities that are colocated with us, and\n // what location that spawned us\n const prerequisites = useAsync(async () => {\n const locationPromise = catalogApi.getOriginLocationByEntity(entity);\n\n let colocatedEntitiesPromise: Promise<Entity[]>;\n if (!locationRef) {\n colocatedEntitiesPromise = Promise.resolve([]);\n } else {\n const locationAnnotationFilter = `metadata.annotations.${ORIGIN_LOCATION_ANNOTATION}`;\n colocatedEntitiesPromise = catalogApi\n .getEntities({\n filter: { [locationAnnotationFilter]: locationRef },\n fields: [\n 'kind',\n 'metadata.uid',\n 'metadata.name',\n 'metadata.namespace',\n ],\n })\n .then(response => response.items);\n }\n\n return Promise.all([locationPromise, colocatedEntitiesPromise]).then(\n ([location, colocatedEntities]) => ({\n location,\n colocatedEntities,\n }),\n );\n }, [catalogApi, entity]);\n\n // Unregisters the underlying location and removes all of the entities that\n // are spawned from it. Can only ever be called when the prerequisites have\n // finished loading successfully, and if there was a matching location.\n const unregisterLocation = useCallback(\n async function unregisterLocationFn() {\n const { location, colocatedEntities } = prerequisites.value!;\n await catalogApi.removeLocationById(location!.id);\n await Promise.allSettled(\n colocatedEntities.map(e =>\n catalogApi.removeEntityByUid(e.metadata.uid!),\n ),\n );\n },\n [catalogApi, prerequisites],\n );\n\n // Just removes the entity, without affecting locations in any way.\n const deleteEntity = useCallback(\n async function deleteEntityFn() {\n await catalogApi.removeEntityByUid(uid!);\n },\n [catalogApi, uid],\n );\n\n // If this is a bootstrap location entity, don't even block on loading\n // prerequisites. We know that all that we will do is to offer to remove the\n // entity, and that doesn't require anything from the prerequisites.\n if (isBootstrap) {\n return { type: 'bootstrap', location: locationRef!, deleteEntity };\n }\n\n // Return early if prerequisites still loading or failing\n const { loading, error, value } = prerequisites;\n if (loading) {\n return { type: 'loading' };\n } else if (error) {\n return { type: 'error', error };\n }\n\n const { location, colocatedEntities } = value!;\n if (!location) {\n return { type: 'only-delete', deleteEntity };\n }\n return {\n type: 'unregister',\n location: locationRef!,\n colocatedEntities: colocatedEntities.map(getEntityName),\n unregisterLocation,\n deleteEntity,\n };\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity } from '@backstage/catalog-model';\nimport { EntityRefLink } from '../EntityRefLink';\nimport {\n Box,\n Button,\n Dialog,\n DialogActions,\n DialogContent,\n DialogContentText,\n DialogTitle,\n Divider,\n makeStyles,\n} from '@material-ui/core';\nimport Alert from '@material-ui/lab/Alert';\nimport React, { useCallback, useState } from 'react';\nimport { useUnregisterEntityDialogState } from './useUnregisterEntityDialogState';\n\nimport { alertApiRef, configApiRef, useApi } from '@backstage/core-plugin-api';\nimport { Progress, ResponseErrorPanel } from '@backstage/core-components';\n\nconst useStyles = makeStyles({\n advancedButton: {\n fontSize: '0.7em',\n },\n});\n\ntype Props = {\n open: boolean;\n onConfirm: () => any;\n onClose: () => any;\n entity: Entity;\n};\n\nconst Contents = ({\n entity,\n onConfirm,\n}: {\n entity: Entity;\n onConfirm: () => any;\n}) => {\n const alertApi = useApi(alertApiRef);\n const configApi = useApi(configApiRef);\n const classes = useStyles();\n const state = useUnregisterEntityDialogState(entity);\n const [showDelete, setShowDelete] = useState(false);\n const [busy, setBusy] = useState(false);\n const appTitle = configApi.getOptionalString('app.title') ?? 'Backstage';\n\n const onUnregister = useCallback(\n async function onUnregisterFn() {\n if ('unregisterLocation' in state) {\n setBusy(true);\n try {\n await state.unregisterLocation();\n onConfirm();\n } catch (err) {\n alertApi.post({ message: err.message });\n } finally {\n setBusy(false);\n }\n }\n },\n [alertApi, onConfirm, state],\n );\n\n const onDelete = useCallback(\n async function onDeleteFn() {\n if ('deleteEntity' in state) {\n setBusy(true);\n try {\n await state.deleteEntity();\n onConfirm();\n } catch (err) {\n alertApi.post({ message: err.message });\n } finally {\n setBusy(false);\n }\n }\n },\n [alertApi, onConfirm, state],\n );\n\n if (state.type === 'loading') {\n return <Progress />;\n }\n\n if (state.type === 'error') {\n return <ResponseErrorPanel error={state.error} />;\n }\n\n if (state.type === 'bootstrap') {\n return (\n <>\n <Alert severity=\"info\">\n You cannot unregister this entity, since it originates from a\n protected Backstage configuration (location \"{state.location}\"). If\n you believe this is in error, please contact the {appTitle}{' '}\n integrator.\n </Alert>\n\n <Box marginTop={2}>\n {!showDelete && (\n <Button\n variant=\"text\"\n size=\"small\"\n color=\"primary\"\n className={classes.advancedButton}\n onClick={() => setShowDelete(true)}\n >\n Advanced Options\n </Button>\n )}\n\n {showDelete && (\n <>\n <DialogContentText>\n You have the option to delete the entity itself from the\n catalog. Note that this should only be done if you know that the\n catalog file has been deleted at, or moved from, its origin\n location. If that is not the case, the entity will reappear\n shortly as the next refresh round is performed by the catalog.\n </DialogContentText>\n <Button\n variant=\"contained\"\n color=\"secondary\"\n disabled={busy}\n onClick={onDelete}\n >\n Delete Entity\n </Button>\n </>\n )}\n </Box>\n </>\n );\n }\n\n if (state.type === 'only-delete') {\n return (\n <>\n <DialogContentText>\n This entity does not seem to originate from a registered location. You\n therefore only have the option to delete it outright from the catalog.\n </DialogContentText>\n <Button\n variant=\"contained\"\n color=\"secondary\"\n disabled={busy}\n onClick={onDelete}\n >\n Delete Entity\n </Button>\n </>\n );\n }\n\n if (state.type === 'unregister') {\n return (\n <>\n <DialogContentText>\n This action will unregister the following entities:\n </DialogContentText>\n <DialogContentText component=\"ul\">\n {state.colocatedEntities.map(e => (\n <li key={`${e.kind}:${e.namespace}/${e.name}`}>\n <EntityRefLink entityRef={e} />\n </li>\n ))}\n </DialogContentText>\n <DialogContentText>\n Located at the following location:\n </DialogContentText>\n <DialogContentText component=\"ul\">\n <li>{state.location}</li>\n </DialogContentText>\n <DialogContentText>\n To undo, just re-register the entity in {appTitle}.\n </DialogContentText>\n <Box marginTop={2}>\n <Button\n variant=\"contained\"\n color=\"secondary\"\n disabled={busy}\n onClick={onUnregister}\n >\n Unregister Location\n </Button>\n {!showDelete && (\n <Box component=\"span\" marginLeft={2}>\n <Button\n variant=\"text\"\n size=\"small\"\n color=\"primary\"\n className={classes.advancedButton}\n onClick={() => setShowDelete(true)}\n >\n Advanced Options\n </Button>\n </Box>\n )}\n </Box>\n\n {showDelete && (\n <>\n <Box paddingTop={4} paddingBottom={4}>\n <Divider />\n </Box>\n <DialogContentText>\n You also have the option to delete the entity itself from the\n catalog. Note that this should only be done if you know that the\n catalog file has been deleted at, or moved from, its origin\n location. If that is not the case, the entity will reappear\n shortly as the next refresh round is performed by the catalog.\n </DialogContentText>\n <Button\n variant=\"contained\"\n color=\"secondary\"\n disabled={busy}\n onClick={onDelete}\n >\n Delete Entity\n </Button>\n </>\n )}\n </>\n );\n }\n\n return <Alert severity=\"error\">Internal error: Unknown state</Alert>;\n};\n\nexport const UnregisterEntityDialog = ({\n open,\n onConfirm,\n onClose,\n entity,\n}: Props) => (\n <Dialog open={open} onClose={onClose}>\n <DialogTitle id=\"responsive-dialog-title\">\n Are you sure you want to unregister this entity?\n </DialogTitle>\n <DialogContent>\n <Contents entity={entity} onConfirm={onConfirm} />\n </DialogContent>\n <DialogActions>\n <Button onClick={onClose} color=\"primary\">\n Cancel\n </Button>\n </DialogActions>\n </Dialog>\n);\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n configApiRef,\n IconComponent,\n useApi,\n} from '@backstage/core-plugin-api';\nimport {\n Card,\n List,\n ListItemIcon,\n ListItemSecondaryAction,\n ListItemText,\n makeStyles,\n MenuItem,\n Theme,\n Typography,\n} from '@material-ui/core';\nimport SettingsIcon from '@material-ui/icons/Settings';\nimport StarIcon from '@material-ui/icons/Star';\nimport { compact } from 'lodash';\nimport React, { Fragment, useEffect, useMemo, useState } from 'react';\nimport { UserListFilter } from '../../filters';\nimport {\n useEntityListProvider,\n useStarredEntities,\n useEntityOwnership,\n} from '../../hooks';\nimport { UserListFilterKind } from '../../types';\nimport { reduceEntityFilters } from '../../utils';\n\nconst useStyles = makeStyles<Theme>(theme => ({\n root: {\n backgroundColor: 'rgba(0, 0, 0, .11)',\n boxShadow: 'none',\n margin: theme.spacing(1, 0, 1, 0),\n },\n title: {\n margin: theme.spacing(1, 0, 0, 1),\n textTransform: 'uppercase',\n fontSize: 12,\n fontWeight: 'bold',\n },\n listIcon: {\n minWidth: 30,\n color: theme.palette.text.primary,\n },\n menuItem: {\n minHeight: theme.spacing(6),\n },\n groupWrapper: {\n margin: theme.spacing(1, 1, 2, 1),\n },\n}));\n\nexport type ButtonGroup = {\n name: string;\n items: {\n id: 'owned' | 'starred' | 'all';\n label: string;\n icon?: IconComponent;\n }[];\n};\n\nfunction getFilterGroups(orgName: string | undefined): ButtonGroup[] {\n return [\n {\n name: 'Personal',\n items: [\n {\n id: 'owned',\n label: 'Owned',\n icon: SettingsIcon,\n },\n {\n id: 'starred',\n label: 'Starred',\n icon: StarIcon,\n },\n ],\n },\n {\n name: orgName ?? 'Company',\n items: [\n {\n id: 'all',\n label: 'All',\n },\n ],\n },\n ];\n}\n\ntype UserListPickerProps = {\n initialFilter?: UserListFilterKind;\n availableFilters?: UserListFilterKind[];\n};\n\nexport const UserListPicker = ({\n initialFilter,\n availableFilters,\n}: UserListPickerProps) => {\n const classes = useStyles();\n const configApi = useApi(configApiRef);\n const orgName = configApi.getOptionalString('organization.name') ?? 'Company';\n\n // Remove group items that aren't in availableFilters and exclude\n // any now-empty groups.\n const filterGroups = getFilterGroups(orgName)\n .map(filterGroup => ({\n ...filterGroup,\n items: filterGroup.items.filter(\n ({ id }) => !availableFilters || availableFilters.includes(id),\n ),\n }))\n .filter(({ items }) => !!items.length);\n\n const { filters, updateFilters, backendEntities, queryParameters } =\n useEntityListProvider();\n\n const { isStarredEntity } = useStarredEntities();\n const { isOwnedEntity } = useEntityOwnership();\n const [selectedUserFilter, setSelectedUserFilter] = useState(\n [queryParameters.user].flat()[0] ?? initialFilter,\n );\n\n // Static filters; used for generating counts of potentially unselected kinds\n const ownedFilter = useMemo(\n () => new UserListFilter('owned', isOwnedEntity, isStarredEntity),\n [isOwnedEntity, isStarredEntity],\n );\n const starredFilter = useMemo(\n () => new UserListFilter('starred', isOwnedEntity, isStarredEntity),\n [isOwnedEntity, isStarredEntity],\n );\n\n useEffect(() => {\n updateFilters({\n user: selectedUserFilter\n ? new UserListFilter(\n selectedUserFilter as UserListFilterKind,\n isOwnedEntity,\n isStarredEntity,\n )\n : undefined,\n });\n }, [selectedUserFilter, isOwnedEntity, isStarredEntity, updateFilters]);\n\n // To show proper counts for each section, apply all other frontend filters _except_ the user\n // filter that's controlled by this picker.\n const [entitiesWithoutUserFilter, setEntitiesWithoutUserFilter] =\n useState(backendEntities);\n useEffect(() => {\n const filterFn = reduceEntityFilters(\n compact(Object.values({ ...filters, user: undefined })),\n );\n setEntitiesWithoutUserFilter(backendEntities.filter(filterFn));\n }, [filters, backendEntities]);\n\n function getFilterCount(id: UserListFilterKind) {\n switch (id) {\n case 'owned':\n return entitiesWithoutUserFilter.filter(entity =>\n ownedFilter.filterEntity(entity),\n ).length;\n case 'starred':\n return entitiesWithoutUserFilter.filter(entity =>\n starredFilter.filterEntity(entity),\n ).length;\n default:\n return entitiesWithoutUserFilter.length;\n }\n }\n\n return (\n <Card className={classes.root}>\n {filterGroups.map(group => (\n <Fragment key={group.name}>\n <Typography variant=\"subtitle2\" className={classes.title}>\n {group.name}\n </Typography>\n <Card className={classes.groupWrapper}>\n <List disablePadding dense>\n {group.items.map(item => (\n <MenuItem\n key={item.id}\n button\n divider\n onClick={() => setSelectedUserFilter(item.id)}\n selected={item.id === filters.user?.value}\n className={classes.menuItem}\n >\n {item.icon && (\n <ListItemIcon className={classes.listIcon}>\n <item.icon fontSize=\"small\" />\n </ListItemIcon>\n )}\n <ListItemText>\n <Typography\n variant=\"body1\"\n data-testid={`user-picker-${item.id}`}\n >\n {item.label}\n </Typography>\n </ListItemText>\n <ListItemSecondaryAction>\n {getFilterCount(item.id) ?? '-'}\n </ListItemSecondaryAction>\n </MenuItem>\n ))}\n </List>\n </Card>\n </Fragment>\n ))}\n </Card>\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React, { PropsWithChildren, useCallback, useState } from 'react';\nimport {\n DefaultEntityFilters,\n EntityListContext,\n EntityListContextProps,\n} from '../hooks/useEntityListProvider';\n\nexport const MockEntityListContextProvider = ({\n children,\n value,\n}: PropsWithChildren<{\n value?: Partial<EntityListContextProps>;\n}>) => {\n // Provides a default implementation that stores filter state, for testing components that\n // reflect filter state.\n const [filters, setFilters] = useState<DefaultEntityFilters>(\n value?.filters ?? {},\n );\n const updateFilters = useCallback(\n (\n update:\n | Partial<DefaultEntityFilters>\n | ((\n prevFilters: DefaultEntityFilters,\n ) => Partial<DefaultEntityFilters>),\n ) => {\n setFilters(prevFilters => {\n const newFilters =\n typeof update === 'function' ? update(prevFilters) : update;\n return { ...prevFilters, ...newFilters };\n });\n },\n [],\n );\n\n const defaultContext: EntityListContextProps = {\n entities: [],\n backendEntities: [],\n updateFilters,\n filters,\n loading: false,\n queryParameters: {},\n };\n\n // Extract value.filters to avoid overwriting it; some tests exercise filter updates. The value\n // provided is used as the initial seed in useState above.\n const { filters: _, ...otherContextFields } = value ?? {};\n\n return (\n <EntityListContext.Provider\n value={{ ...defaultContext, ...otherContextFields }}\n >\n {children}\n </EntityListContext.Provider>\n );\n};\n"],"names":["createApiRef","createRouteRef","ENTITY_DEFAULT_NAMESPACE","useRouteRefParams","createContext","createVersionedContext","createVersionedValueMap","useNavigate","useApi","errorApiRef","useAsyncRetry","useVersionedContext","VIEW_URL_ANNOTATION","EDIT_URL_ANNOTATION","SOURCE_LOCATION_ANNOTATION","parseLocationReference","RELATION_MEMBER_OF","getEntityName","stringifyEntityRef","RELATION_OWNED_BY","useMountedState","useState","qs","useAsyncFn","compact","isEqual","useCallback","useMemo","useContext","serializeEntityRef","forwardRef","Link","generatePath","React","useAsync","useRef","identityApiRef","groupBy","chunk","storageApiRef","useObservable","parseEntityRef","jwtDecoder","Alert","icon","CheckBoxOutlineBlankIcon","checkedIcon","CheckBoxIcon","Box","Typography","Autocomplete","FormControlLabel","Checkbox","ExpandMoreIcon","TextField","useStyles","makeStyles","Toolbar","FormControl","Input","InputAdornment","Search","IconButton","Clear","RELATION_PART_OF","OverflowTooltip","Table","alertApiRef","capitalize","Select","withStyles","Star","StarBorder","Tooltip","ORIGIN_LOCATION_ANNOTATION","configApiRef","Progress","ResponseErrorPanel","Button","DialogContentText","Divider","Dialog","DialogTitle","DialogContent","DialogActions","SettingsIcon","StarIcon","Card","Fragment","List","MenuItem","ListItemIcon","ListItemText","ListItemSecondaryAction"],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -4,8 +4,7 @@ export { CATALOG_FILTER_EXISTS, CatalogApi } from '@backstage/catalog-client';
|
|
|
4
4
|
import * as _backstage_core_plugin_api from '@backstage/core-plugin-api';
|
|
5
5
|
import * as _backstage_catalog_model from '@backstage/catalog-model';
|
|
6
6
|
import { Entity, EntityName, UserEntity } from '@backstage/catalog-model';
|
|
7
|
-
import
|
|
8
|
-
import React__default, { ReactNode, ComponentProps, PropsWithChildren } from 'react';
|
|
7
|
+
import React, { ReactNode, ComponentProps, Context, PropsWithChildren } from 'react';
|
|
9
8
|
import { LinkProps, TableColumn } from '@backstage/core-components';
|
|
10
9
|
import { IconButton } from '@material-ui/core';
|
|
11
10
|
import { AsyncState } from 'react-use/lib/useAsync';
|
|
@@ -23,18 +22,12 @@ declare const EntityLifecyclePicker: () => JSX.Element | null;
|
|
|
23
22
|
|
|
24
23
|
declare const EntityOwnerPicker: () => JSX.Element | null;
|
|
25
24
|
|
|
26
|
-
declare type EntityProviderProps = {
|
|
27
|
-
entity: Entity;
|
|
28
|
-
children: ReactNode;
|
|
29
|
-
};
|
|
30
|
-
declare const EntityProvider: ({ entity, children }: EntityProviderProps) => JSX.Element;
|
|
31
|
-
|
|
32
25
|
declare type EntityRefLinkProps = {
|
|
33
26
|
entityRef: Entity | EntityName;
|
|
34
27
|
defaultKind?: string;
|
|
35
|
-
children?:
|
|
28
|
+
children?: React.ReactNode;
|
|
36
29
|
} & Omit<LinkProps, 'to'>;
|
|
37
|
-
declare const EntityRefLink:
|
|
30
|
+
declare const EntityRefLink: React.ForwardRefExoticComponent<Pick<EntityRefLinkProps, "replace" | "children" | "slot" | "style" | "title" | "id" | "className" | "classes" | "innerRef" | "key" | "defaultChecked" | "defaultValue" | "suppressContentEditableWarning" | "suppressHydrationWarning" | "accessKey" | "contentEditable" | "contextMenu" | "dir" | "draggable" | "hidden" | "lang" | "placeholder" | "spellCheck" | "tabIndex" | "translate" | "radioGroup" | "role" | "about" | "datatype" | "inlist" | "prefix" | "property" | "resource" | "typeof" | "vocab" | "autoCapitalize" | "autoCorrect" | "autoSave" | "color" | "itemProp" | "itemScope" | "itemType" | "itemID" | "itemRef" | "results" | "security" | "unselectable" | "inputMode" | "is" | "aria-activedescendant" | "aria-atomic" | "aria-autocomplete" | "aria-busy" | "aria-checked" | "aria-colcount" | "aria-colindex" | "aria-colspan" | "aria-controls" | "aria-current" | "aria-describedby" | "aria-details" | "aria-disabled" | "aria-dropeffect" | "aria-errormessage" | "aria-expanded" | "aria-flowto" | "aria-grabbed" | "aria-haspopup" | "aria-hidden" | "aria-invalid" | "aria-keyshortcuts" | "aria-label" | "aria-labelledby" | "aria-level" | "aria-live" | "aria-modal" | "aria-multiline" | "aria-multiselectable" | "aria-orientation" | "aria-owns" | "aria-placeholder" | "aria-posinset" | "aria-pressed" | "aria-readonly" | "aria-relevant" | "aria-required" | "aria-roledescription" | "aria-rowcount" | "aria-rowindex" | "aria-rowspan" | "aria-selected" | "aria-setsize" | "aria-sort" | "aria-valuemax" | "aria-valuemin" | "aria-valuenow" | "aria-valuetext" | "dangerouslySetInnerHTML" | "onCopy" | "onCopyCapture" | "onCut" | "onCutCapture" | "onPaste" | "onPasteCapture" | "onCompositionEnd" | "onCompositionEndCapture" | "onCompositionStart" | "onCompositionStartCapture" | "onCompositionUpdate" | "onCompositionUpdateCapture" | "onFocus" | "onFocusCapture" | "onBlur" | "onBlurCapture" | "onChange" | "onChangeCapture" | "onBeforeInput" | "onBeforeInputCapture" | "onInput" | "onInputCapture" | "onReset" | "onResetCapture" | "onSubmit" | "onSubmitCapture" | "onInvalid" | "onInvalidCapture" | "onLoad" | "onLoadCapture" | "onError" | "onErrorCapture" | "onKeyDown" | "onKeyDownCapture" | "onKeyPress" | "onKeyPressCapture" | "onKeyUp" | "onKeyUpCapture" | "onAbort" | "onAbortCapture" | "onCanPlay" | "onCanPlayCapture" | "onCanPlayThrough" | "onCanPlayThroughCapture" | "onDurationChange" | "onDurationChangeCapture" | "onEmptied" | "onEmptiedCapture" | "onEncrypted" | "onEncryptedCapture" | "onEnded" | "onEndedCapture" | "onLoadedData" | "onLoadedDataCapture" | "onLoadedMetadata" | "onLoadedMetadataCapture" | "onLoadStart" | "onLoadStartCapture" | "onPause" | "onPauseCapture" | "onPlay" | "onPlayCapture" | "onPlaying" | "onPlayingCapture" | "onProgress" | "onProgressCapture" | "onRateChange" | "onRateChangeCapture" | "onSeeked" | "onSeekedCapture" | "onSeeking" | "onSeekingCapture" | "onStalled" | "onStalledCapture" | "onSuspend" | "onSuspendCapture" | "onTimeUpdate" | "onTimeUpdateCapture" | "onVolumeChange" | "onVolumeChangeCapture" | "onWaiting" | "onWaitingCapture" | "onAuxClick" | "onAuxClickCapture" | "onClick" | "onClickCapture" | "onContextMenu" | "onContextMenuCapture" | "onDoubleClick" | "onDoubleClickCapture" | "onDrag" | "onDragCapture" | "onDragEnd" | "onDragEndCapture" | "onDragEnter" | "onDragEnterCapture" | "onDragExit" | "onDragExitCapture" | "onDragLeave" | "onDragLeaveCapture" | "onDragOver" | "onDragOverCapture" | "onDragStart" | "onDragStartCapture" | "onDrop" | "onDropCapture" | "onMouseDown" | "onMouseDownCapture" | "onMouseEnter" | "onMouseLeave" | "onMouseMove" | "onMouseMoveCapture" | "onMouseOut" | "onMouseOutCapture" | "onMouseOver" | "onMouseOverCapture" | "onMouseUp" | "onMouseUpCapture" | "onSelect" | "onSelectCapture" | "onTouchCancel" | "onTouchCancelCapture" | "onTouchEnd" | "onTouchEndCapture" | "onTouchMove" | "onTouchMoveCapture" | "onTouchStart" | "onTouchStartCapture" | "onPointerDown" | "onPointerDownCapture" | "onPointerMove" | "onPointerMoveCapture" | "onPointerUp" | "onPointerUpCapture" | "onPointerCancel" | "onPointerCancelCapture" | "onPointerEnter" | "onPointerEnterCapture" | "onPointerLeave" | "onPointerLeaveCapture" | "onPointerOver" | "onPointerOverCapture" | "onPointerOut" | "onPointerOutCapture" | "onGotPointerCapture" | "onGotPointerCaptureCapture" | "onLostPointerCapture" | "onLostPointerCaptureCapture" | "onScroll" | "onScrollCapture" | "onWheel" | "onWheelCapture" | "onAnimationStart" | "onAnimationStartCapture" | "onAnimationEnd" | "onAnimationEndCapture" | "onAnimationIteration" | "onAnimationIterationCapture" | "onTransitionEnd" | "onTransitionEndCapture" | "type" | "display" | "underline" | "media" | "target" | "href" | "hrefLang" | "referrerPolicy" | "rel" | "download" | "ping" | "align" | "component" | "state" | "variant" | "noWrap" | "gutterBottom" | "paragraph" | "variantMapping" | "TypographyClasses" | "entityRef" | "defaultKind"> & React.RefAttributes<any>>;
|
|
38
31
|
|
|
39
32
|
declare type EntityRefLinksProps = {
|
|
40
33
|
entityRefs: (Entity | EntityName)[];
|
|
@@ -162,10 +155,49 @@ declare type EntityLoadingStatus = {
|
|
|
162
155
|
error?: Error;
|
|
163
156
|
refresh?: VoidFunction;
|
|
164
157
|
};
|
|
165
|
-
|
|
158
|
+
/**
|
|
159
|
+
* @public
|
|
160
|
+
* @deprecated use `useEntity` and `EntityProvider` or `AsyncEntityProvider` instead.
|
|
161
|
+
*/
|
|
162
|
+
declare const EntityContext: Context<EntityLoadingStatus>;
|
|
163
|
+
/**
|
|
164
|
+
* Properties for the AsyncEntityProvider component.
|
|
165
|
+
*
|
|
166
|
+
* @public
|
|
167
|
+
*/
|
|
168
|
+
interface AsyncEntityProviderProps {
|
|
169
|
+
children: ReactNode;
|
|
170
|
+
entity?: Entity;
|
|
171
|
+
loading: boolean;
|
|
172
|
+
error?: Error;
|
|
173
|
+
refresh?: VoidFunction;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Provides a loaded entity to be picked up by the `useEntity` hook.
|
|
177
|
+
*
|
|
178
|
+
* @public
|
|
179
|
+
*/
|
|
180
|
+
declare const AsyncEntityProvider: ({ children, entity, loading, error, refresh, }: AsyncEntityProviderProps) => JSX.Element;
|
|
181
|
+
/**
|
|
182
|
+
* Properties for the EntityProvider component.
|
|
183
|
+
*
|
|
184
|
+
* @public
|
|
185
|
+
*/
|
|
186
|
+
interface EntityProviderProps {
|
|
187
|
+
children: ReactNode;
|
|
188
|
+
entity?: Entity;
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Provides an entity to be picked up by the `useEntity` hook.
|
|
192
|
+
*
|
|
193
|
+
* @public
|
|
194
|
+
*/
|
|
195
|
+
declare const EntityProvider: ({ entity, children }: EntityProviderProps) => JSX.Element;
|
|
166
196
|
declare const useEntityFromUrl: () => EntityLoadingStatus;
|
|
167
197
|
/**
|
|
168
198
|
* Grab the current entity from the context and its current loading state.
|
|
199
|
+
*
|
|
200
|
+
* @public
|
|
169
201
|
*/
|
|
170
202
|
declare function useEntity<T extends Entity = Entity>(): {
|
|
171
203
|
entity: T;
|
|
@@ -263,7 +295,7 @@ declare type EntityListContextProps<EntityFilters extends DefaultEntityFilters =
|
|
|
263
295
|
loading: boolean;
|
|
264
296
|
error?: Error;
|
|
265
297
|
};
|
|
266
|
-
declare const EntityListContext:
|
|
298
|
+
declare const EntityListContext: React.Context<EntityListContextProps<any> | undefined>;
|
|
267
299
|
declare const EntityListProvider: <EntityFilters extends DefaultEntityFilters>({ children, }: PropsWithChildren<{}>) => JSX.Element;
|
|
268
300
|
declare function useEntityListProvider<EntityFilters extends DefaultEntityFilters = DefaultEntityFilters>(): EntityListContextProps<EntityFilters>;
|
|
269
301
|
|
|
@@ -335,7 +367,7 @@ declare function entityRouteParams(entity: Entity): {
|
|
|
335
367
|
readonly name: string;
|
|
336
368
|
};
|
|
337
369
|
|
|
338
|
-
declare const MockEntityListContextProvider: ({ children, value, }:
|
|
370
|
+
declare const MockEntityListContextProvider: ({ children, value, }: React.PropsWithChildren<{
|
|
339
371
|
value?: Partial<EntityListContextProps<DefaultEntityFilters>> | undefined;
|
|
340
372
|
}>) => JSX.Element;
|
|
341
373
|
|
|
@@ -363,4 +395,4 @@ declare function getEntitySourceLocation(entity: Entity, scmIntegrationsApi: Scm
|
|
|
363
395
|
*/
|
|
364
396
|
declare function isOwnerOf(owner: Entity, owned: Entity): boolean;
|
|
365
397
|
|
|
366
|
-
export { DefaultEntityFilters, EntityContext, EntityFilter, EntityKindFilter, EntityKindPicker, EntityLifecycleFilter, EntityLifecyclePicker, EntityListContext, EntityListProvider, EntityOwnerFilter, EntityOwnerPicker, EntityProvider, EntityRefLink, EntityRefLinks, EntitySearchBar, EntitySourceLocation, EntityTable, EntityTagFilter, EntityTagPicker, EntityTextFilter, EntityTypeFilter, EntityTypePicker, FavoriteEntity, MockEntityListContextProvider, UnregisterEntityDialog, UserListFilter, UserListFilterKind, UserListPicker, catalogApiRef, catalogRouteRef, entityRoute, entityRouteParams, entityRouteRef, favoriteEntityIcon, favoriteEntityTooltip, formatEntityRefTitle, getEntityMetadataEditUrl, getEntityMetadataViewUrl, getEntityRelations, getEntitySourceLocation, isOwnerOf, reduceCatalogFilters, reduceEntityFilters, rootRoute, useEntity, useEntityCompoundName, useEntityFromUrl, useEntityKinds, useEntityListProvider, useEntityOwnership, useEntityTypeFilter, useOwnUser, useRelatedEntities, useStarredEntities };
|
|
398
|
+
export { AsyncEntityProvider, AsyncEntityProviderProps, DefaultEntityFilters, EntityContext, EntityFilter, EntityKindFilter, EntityKindPicker, EntityLifecycleFilter, EntityLifecyclePicker, EntityListContext, EntityListProvider, EntityOwnerFilter, EntityOwnerPicker, EntityProvider, EntityProviderProps, EntityRefLink, EntityRefLinks, EntitySearchBar, EntitySourceLocation, EntityTable, EntityTagFilter, EntityTagPicker, EntityTextFilter, EntityTypeFilter, EntityTypePicker, FavoriteEntity, MockEntityListContextProvider, UnregisterEntityDialog, UserListFilter, UserListFilterKind, UserListPicker, catalogApiRef, catalogRouteRef, entityRoute, entityRouteParams, entityRouteRef, favoriteEntityIcon, favoriteEntityTooltip, formatEntityRefTitle, getEntityMetadataEditUrl, getEntityMetadataViewUrl, getEntityRelations, getEntitySourceLocation, isOwnerOf, reduceCatalogFilters, reduceEntityFilters, rootRoute, useEntity, useEntityCompoundName, useEntityFromUrl, useEntityKinds, useEntityListProvider, useEntityOwnership, useEntityTypeFilter, useOwnUser, useRelatedEntities, useStarredEntities };
|
package/dist/index.esm.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
export { CATALOG_FILTER_EXISTS } from '@backstage/catalog-client';
|
|
2
2
|
import { createApiRef, createRouteRef, useRouteRefParams, useApi, errorApiRef, identityApiRef, storageApiRef, alertApiRef, configApiRef } from '@backstage/core-plugin-api';
|
|
3
|
-
import React, { createContext, useEffect,
|
|
3
|
+
import React, { createContext, useEffect, useState, useCallback, useMemo, useContext, forwardRef, useRef, Fragment } from 'react';
|
|
4
4
|
import { Alert, Autocomplete } from '@material-ui/lab';
|
|
5
|
+
import { createVersionedContext, createVersionedValueMap, useVersionedContext } from '@backstage/version-bridge';
|
|
5
6
|
import { useNavigate, generatePath } from 'react-router';
|
|
6
7
|
import { useAsyncRetry, useMountedState, useAsyncFn, useDebounce, useAsync, useObservable } from 'react-use';
|
|
7
8
|
import { ENTITY_DEFAULT_NAMESPACE, VIEW_URL_ANNOTATION, EDIT_URL_ANNOTATION, SOURCE_LOCATION_ANNOTATION, parseLocationReference, RELATION_MEMBER_OF, getEntityName, stringifyEntityRef, RELATION_OWNED_BY, serializeEntityRef, parseEntityRef, RELATION_PART_OF, ORIGIN_LOCATION_ANNOTATION } from '@backstage/catalog-model';
|
|
@@ -60,6 +61,39 @@ const EntityContext = createContext({
|
|
|
60
61
|
refresh: () => {
|
|
61
62
|
}
|
|
62
63
|
});
|
|
64
|
+
const OldEntityProvider = EntityContext.Provider;
|
|
65
|
+
const NewEntityContext = createVersionedContext("entity-context");
|
|
66
|
+
const AsyncEntityProvider = ({
|
|
67
|
+
children,
|
|
68
|
+
entity,
|
|
69
|
+
loading,
|
|
70
|
+
error,
|
|
71
|
+
refresh
|
|
72
|
+
}) => {
|
|
73
|
+
const value = {entity, loading, error, refresh};
|
|
74
|
+
return /* @__PURE__ */ React.createElement(OldEntityProvider, {
|
|
75
|
+
value
|
|
76
|
+
}, /* @__PURE__ */ React.createElement(NewEntityContext.Provider, {
|
|
77
|
+
value: createVersionedValueMap({1: value})
|
|
78
|
+
}, children));
|
|
79
|
+
};
|
|
80
|
+
const EntityProvider = ({entity, children}) => /* @__PURE__ */ React.createElement(AsyncEntityProvider, {
|
|
81
|
+
entity,
|
|
82
|
+
loading: !Boolean(entity),
|
|
83
|
+
error: void 0,
|
|
84
|
+
refresh: void 0,
|
|
85
|
+
children
|
|
86
|
+
});
|
|
87
|
+
const CompatibilityProvider = ({
|
|
88
|
+
value,
|
|
89
|
+
children
|
|
90
|
+
}) => {
|
|
91
|
+
return /* @__PURE__ */ React.createElement(AsyncEntityProvider, {
|
|
92
|
+
...value,
|
|
93
|
+
children
|
|
94
|
+
});
|
|
95
|
+
};
|
|
96
|
+
EntityContext.Provider = CompatibilityProvider;
|
|
63
97
|
const useEntityFromUrl = () => {
|
|
64
98
|
const {kind, namespace, name} = useEntityCompoundName();
|
|
65
99
|
const navigate = useNavigate();
|
|
@@ -80,7 +114,21 @@ const useEntityFromUrl = () => {
|
|
|
80
114
|
return {entity, loading, error, refresh};
|
|
81
115
|
};
|
|
82
116
|
function useEntity() {
|
|
83
|
-
const
|
|
117
|
+
const versionedHolder = useVersionedContext("entity-context");
|
|
118
|
+
if (!versionedHolder) {
|
|
119
|
+
return {
|
|
120
|
+
entity: void 0,
|
|
121
|
+
loading: true,
|
|
122
|
+
error: void 0,
|
|
123
|
+
refresh: () => {
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
const value = versionedHolder.atVersion(1);
|
|
128
|
+
if (!value) {
|
|
129
|
+
throw new Error("EntityContext v1 not available");
|
|
130
|
+
}
|
|
131
|
+
const {entity, loading, error, refresh} = value;
|
|
84
132
|
return {entity, loading, error, refresh};
|
|
85
133
|
}
|
|
86
134
|
|
|
@@ -753,14 +801,6 @@ const EntityOwnerPicker = () => {
|
|
|
753
801
|
}));
|
|
754
802
|
};
|
|
755
803
|
|
|
756
|
-
const EntityProvider = ({entity, children}) => /* @__PURE__ */ React.createElement(EntityContext.Provider, {
|
|
757
|
-
value: {
|
|
758
|
-
entity,
|
|
759
|
-
loading: !Boolean(entity),
|
|
760
|
-
error: void 0
|
|
761
|
-
}
|
|
762
|
-
}, children);
|
|
763
|
-
|
|
764
804
|
const useStyles$3 = makeStyles((_theme) => ({
|
|
765
805
|
searchToolbar: {
|
|
766
806
|
paddingLeft: 0,
|
|
@@ -883,7 +923,8 @@ function createMetadataDescriptionColumn() {
|
|
|
883
923
|
field: "metadata.description",
|
|
884
924
|
render: (entity) => /* @__PURE__ */ React.createElement(OverflowTooltip, {
|
|
885
925
|
text: entity.metadata.description,
|
|
886
|
-
placement: "bottom-start"
|
|
926
|
+
placement: "bottom-start",
|
|
927
|
+
line: 2
|
|
887
928
|
}),
|
|
888
929
|
width: "auto"
|
|
889
930
|
};
|
|
@@ -1411,5 +1452,5 @@ const MockEntityListContextProvider = ({
|
|
|
1411
1452
|
}, children);
|
|
1412
1453
|
};
|
|
1413
1454
|
|
|
1414
|
-
export { EntityContext, EntityKindFilter, EntityKindPicker, EntityLifecycleFilter, EntityLifecyclePicker, EntityListContext, EntityListProvider, EntityOwnerFilter, EntityOwnerPicker, EntityProvider, EntityRefLink, EntityRefLinks, EntitySearchBar, EntityTable, EntityTagFilter, EntityTagPicker, EntityTextFilter, EntityTypeFilter, EntityTypePicker, FavoriteEntity, MockEntityListContextProvider, UnregisterEntityDialog, UserListFilter, UserListPicker, catalogApiRef, catalogRouteRef, entityRoute, entityRouteParams, entityRouteRef, favoriteEntityIcon, favoriteEntityTooltip, formatEntityRefTitle, getEntityMetadataEditUrl, getEntityMetadataViewUrl, getEntityRelations, getEntitySourceLocation, isOwnerOf, reduceCatalogFilters, reduceEntityFilters, rootRoute, useEntity, useEntityCompoundName, useEntityFromUrl, useEntityKinds, useEntityListProvider, useEntityOwnership, useEntityTypeFilter, useOwnUser, useRelatedEntities, useStarredEntities };
|
|
1455
|
+
export { AsyncEntityProvider, EntityContext, EntityKindFilter, EntityKindPicker, EntityLifecycleFilter, EntityLifecyclePicker, EntityListContext, EntityListProvider, EntityOwnerFilter, EntityOwnerPicker, EntityProvider, EntityRefLink, EntityRefLinks, EntitySearchBar, EntityTable, EntityTagFilter, EntityTagPicker, EntityTextFilter, EntityTypeFilter, EntityTypePicker, FavoriteEntity, MockEntityListContextProvider, UnregisterEntityDialog, UserListFilter, UserListPicker, catalogApiRef, catalogRouteRef, entityRoute, entityRouteParams, entityRouteRef, favoriteEntityIcon, favoriteEntityTooltip, formatEntityRefTitle, getEntityMetadataEditUrl, getEntityMetadataViewUrl, getEntityRelations, getEntitySourceLocation, isOwnerOf, reduceCatalogFilters, reduceEntityFilters, rootRoute, useEntity, useEntityCompoundName, useEntityFromUrl, useEntityKinds, useEntityListProvider, useEntityOwnership, useEntityTypeFilter, useOwnUser, useRelatedEntities, useStarredEntities };
|
|
1415
1456
|
//# sourceMappingURL=index.esm.js.map
|
package/dist/index.esm.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.esm.js","sources":["../src/api.ts","../src/routes.ts","../src/hooks/useEntityCompoundName.ts","../src/hooks/useEntity.ts","../src/utils/filters.ts","../src/utils/getEntityMetadataUrl.ts","../src/utils/getEntityRelations.ts","../src/utils/getEntitySourceLocation.ts","../src/utils/isOwnerOf.ts","../src/hooks/useEntityListProvider.tsx","../src/components/EntityRefLink/format.ts","../src/components/EntityRefLink/EntityRefLink.tsx","../src/components/EntityRefLink/EntityRefLinks.tsx","../src/filters.ts","../src/hooks/useEntityTypeFilter.tsx","../src/hooks/useEntityKinds.ts","../src/hooks/useOwnUser.ts","../src/hooks/useRelatedEntities.ts","../src/hooks/useStarredEntities.ts","../src/hooks/useEntityOwnership.ts","../src/components/EntityKindPicker/EntityKindPicker.tsx","../src/components/EntityLifecyclePicker/EntityLifecyclePicker.tsx","../src/components/EntityOwnerPicker/EntityOwnerPicker.tsx","../src/components/EntityProvider/EntityProvider.tsx","../src/components/EntitySearchBar/EntitySearchBar.tsx","../src/components/EntityTable/columns.tsx","../src/components/EntityTable/presets.tsx","../src/components/EntityTable/EntityTable.tsx","../src/components/EntityTagPicker/EntityTagPicker.tsx","../src/components/EntityTypePicker/EntityTypePicker.tsx","../src/components/FavoriteEntity/FavoriteEntity.tsx","../src/components/UnregisterEntityDialog/useUnregisterEntityDialogState.ts","../src/components/UnregisterEntityDialog/UnregisterEntityDialog.tsx","../src/components/UserListPicker/UserListPicker.tsx","../src/testUtils/providers.tsx"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { CatalogApi } from '@backstage/catalog-client';\nimport { createApiRef } from '@backstage/core-plugin-api';\n\nexport const catalogApiRef = createApiRef<CatalogApi>({\n id: 'plugin.catalog.service',\n});\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity, ENTITY_DEFAULT_NAMESPACE } from '@backstage/catalog-model';\nimport { createRouteRef } from '@backstage/core-plugin-api';\n\nconst NoIcon = () => null;\n\n// TODO(Rugvip): Move these route refs back to the catalog plugin once we're all ported to using external routes\nexport const rootRoute = createRouteRef({\n icon: NoIcon,\n path: '',\n title: 'Catalog',\n});\nexport const catalogRouteRef = rootRoute;\n\nexport const entityRoute = createRouteRef({\n icon: NoIcon,\n path: ':namespace/:kind/:name/*',\n title: 'Entity',\n params: ['namespace', 'kind', 'name'],\n});\nexport const entityRouteRef = entityRoute;\n\n// Utility function to get suitable route params for entityRoute, given an\n// entity instance\nexport function entityRouteParams(entity: Entity) {\n return {\n kind: entity.kind.toLowerCase(),\n namespace:\n entity.metadata.namespace?.toLowerCase() ?? ENTITY_DEFAULT_NAMESPACE,\n name: entity.metadata.name,\n } as const;\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { entityRouteRef } from '../routes';\nimport { useRouteRefParams } from '@backstage/core-plugin-api';\n\n/**\n * Grabs entity kind, namespace, and name from the location\n */\nexport const useEntityCompoundName = () => {\n const { kind, namespace, name } = useRouteRefParams(entityRouteRef);\n return { kind, namespace, name };\n};\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { Entity } from '@backstage/catalog-model';\nimport { errorApiRef, useApi } from '@backstage/core-plugin-api';\nimport { createContext, useContext, useEffect } from 'react';\nimport { useNavigate } from 'react-router';\nimport { useAsyncRetry } from 'react-use';\nimport { catalogApiRef } from '../api';\nimport { useEntityCompoundName } from './useEntityCompoundName';\n\ntype EntityLoadingStatus = {\n entity?: Entity;\n loading: boolean;\n error?: Error;\n refresh?: VoidFunction;\n};\n\nexport const EntityContext = createContext<EntityLoadingStatus>({\n entity: undefined,\n loading: true,\n error: undefined,\n refresh: () => {},\n});\n\nexport const useEntityFromUrl = (): EntityLoadingStatus => {\n const { kind, namespace, name } = useEntityCompoundName();\n const navigate = useNavigate();\n const errorApi = useApi(errorApiRef);\n const catalogApi = useApi(catalogApiRef);\n\n const {\n value: entity,\n error,\n loading,\n retry: refresh,\n } = useAsyncRetry(\n () => catalogApi.getEntityByName({ kind, namespace, name }),\n [catalogApi, kind, namespace, name],\n );\n\n useEffect(() => {\n if (!name) {\n errorApi.post(new Error('No name provided!'));\n navigate('/');\n }\n }, [errorApi, navigate, error, loading, entity, name]);\n\n return { entity, loading, error, refresh };\n};\n\n/**\n * Grab the current entity from the context and its current loading state.\n */\nexport function useEntity<T extends Entity = Entity>() {\n const { entity, loading, error, refresh } = useContext(EntityContext);\n return { entity: entity as T, loading, error, refresh };\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity } from '@backstage/catalog-model';\nimport { EntityFilter } from '../types';\n\nexport function reduceCatalogFilters(\n filters: EntityFilter[],\n): Record<string, string | symbol | (string | symbol)[]> {\n return filters.reduce((compoundFilter, filter) => {\n return {\n ...compoundFilter,\n ...(filter.getCatalogFilters ? filter.getCatalogFilters() : {}),\n };\n }, {} as Record<string, string | symbol | (string | symbol)[]>);\n}\n\nexport function reduceEntityFilters(\n filters: EntityFilter[],\n): (entity: Entity) => boolean {\n return (entity: Entity) =>\n filters.every(\n filter => !filter.filterEntity || filter.filterEntity(entity),\n );\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n EDIT_URL_ANNOTATION,\n Entity,\n VIEW_URL_ANNOTATION,\n} from '@backstage/catalog-model';\n\nexport function getEntityMetadataViewUrl(entity: Entity): string | undefined {\n return entity.metadata.annotations?.[VIEW_URL_ANNOTATION];\n}\n\nexport function getEntityMetadataEditUrl(entity: Entity): string | undefined {\n return entity.metadata.annotations?.[EDIT_URL_ANNOTATION];\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity, EntityName } from '@backstage/catalog-model';\n\n/**\n * Get the related entity references.\n */\nexport function getEntityRelations(\n entity: Entity | undefined,\n relationType: string,\n filter?: { kind: string },\n): EntityName[] {\n let entityNames =\n entity?.relations\n ?.filter(r => r.type === relationType)\n ?.map(r => r.target) || [];\n\n if (filter?.kind) {\n entityNames = entityNames?.filter(\n e => e.kind.toLowerCase() === filter.kind.toLowerCase(),\n );\n }\n\n return entityNames;\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n Entity,\n parseLocationReference,\n SOURCE_LOCATION_ANNOTATION,\n} from '@backstage/catalog-model';\nimport { ScmIntegrationRegistry } from '@backstage/integration';\n\nexport type EntitySourceLocation = {\n locationTargetUrl: string;\n integrationType?: string;\n};\n\nexport function getEntitySourceLocation(\n entity: Entity,\n scmIntegrationsApi: ScmIntegrationRegistry,\n): EntitySourceLocation | undefined {\n const sourceLocation =\n entity.metadata.annotations?.[SOURCE_LOCATION_ANNOTATION];\n\n if (!sourceLocation) {\n return undefined;\n }\n\n try {\n const sourceLocationRef = parseLocationReference(sourceLocation);\n const integration = scmIntegrationsApi.byUrl(sourceLocationRef.target);\n return {\n locationTargetUrl: sourceLocationRef.target,\n integrationType: integration?.type,\n };\n } catch {\n return undefined;\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n Entity,\n getEntityName,\n RELATION_MEMBER_OF,\n RELATION_OWNED_BY,\n stringifyEntityRef,\n} from '@backstage/catalog-model';\nimport { getEntityRelations } from './getEntityRelations';\n\n/**\n * Get the related entity references.\n */\nexport function isOwnerOf(owner: Entity, owned: Entity) {\n const possibleOwners = new Set(\n [\n ...getEntityRelations(owner, RELATION_MEMBER_OF, { kind: 'group' }),\n ...(owner ? [getEntityName(owner)] : []),\n ].map(stringifyEntityRef),\n );\n\n const owners = getEntityRelations(owned, RELATION_OWNED_BY).map(\n stringifyEntityRef,\n );\n\n for (const ownerItem of owners) {\n if (possibleOwners.has(ownerItem)) {\n return true;\n }\n }\n\n return false;\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity } from '@backstage/catalog-model';\nimport { compact, isEqual } from 'lodash';\nimport qs from 'qs';\nimport React, {\n createContext,\n PropsWithChildren,\n useCallback,\n useContext,\n useMemo,\n useState,\n} from 'react';\nimport { useAsyncFn, useDebounce, useMountedState } from 'react-use';\nimport { catalogApiRef } from '../api';\nimport {\n EntityKindFilter,\n EntityLifecycleFilter,\n EntityOwnerFilter,\n EntityTagFilter,\n EntityTextFilter,\n EntityTypeFilter,\n UserListFilter,\n} from '../filters';\nimport { EntityFilter } from '../types';\nimport { reduceCatalogFilters, reduceEntityFilters } from '../utils';\nimport { useApi } from '@backstage/core-plugin-api';\n\nexport type DefaultEntityFilters = {\n kind?: EntityKindFilter;\n type?: EntityTypeFilter;\n user?: UserListFilter;\n owners?: EntityOwnerFilter;\n lifecycles?: EntityLifecycleFilter;\n tags?: EntityTagFilter;\n text?: EntityTextFilter;\n};\n\nexport type EntityListContextProps<\n EntityFilters extends DefaultEntityFilters = DefaultEntityFilters,\n> = {\n /**\n * The currently registered filters, adhering to the shape of DefaultEntityFilters or an extension\n * of that default (to add custom filter types).\n */\n filters: EntityFilters;\n\n /**\n * The resolved list of catalog entities, after all filters are applied.\n */\n entities: Entity[];\n\n /**\n * The resolved list of catalog entities, after _only catalog-backend_ filters are applied.\n */\n backendEntities: Entity[];\n\n /**\n * Update one or more of the registered filters. Optional filters can be set to `undefined` to\n * reset the filter.\n */\n updateFilters: (\n filters:\n | Partial<EntityFilters>\n | ((prevFilters: EntityFilters) => Partial<EntityFilters>),\n ) => void;\n\n /**\n * Filter values from query parameters.\n */\n queryParameters: Partial<Record<keyof EntityFilters, string | string[]>>;\n\n loading: boolean;\n error?: Error;\n};\n\nexport const EntityListContext = createContext<\n EntityListContextProps<any> | undefined\n>(undefined);\n\ntype OutputState<EntityFilters extends DefaultEntityFilters> = {\n appliedFilters: EntityFilters;\n entities: Entity[];\n backendEntities: Entity[];\n queryParameters: Record<string, string | string[]>;\n};\n\nexport const EntityListProvider = <EntityFilters extends DefaultEntityFilters>({\n children,\n}: PropsWithChildren<{}>) => {\n const isMounted = useMountedState();\n const catalogApi = useApi(catalogApiRef);\n const [requestedFilters, setRequestedFilters] = useState<EntityFilters>(\n {} as EntityFilters,\n );\n const [outputState, setOutputState] = useState<OutputState<EntityFilters>>(\n () => {\n const query = qs.parse(window.location.search, {\n ignoreQueryPrefix: true,\n });\n return {\n appliedFilters: {} as EntityFilters,\n entities: [],\n backendEntities: [],\n queryParameters: (query.filters ?? {}) as Record<\n string,\n string | string[]\n >,\n };\n },\n );\n\n // The main async filter worker. Note that while it has a lot of dependencies\n // in terms of its implementation, the triggering only happens (debounced)\n // based on the requested filters changing.\n const [{ loading, error }, refresh] = useAsyncFn(\n async () => {\n const compacted = compact(Object.values(requestedFilters));\n const entityFilter = reduceEntityFilters(compacted);\n const backendFilter = reduceCatalogFilters(compacted);\n const previousBackendFilter = reduceCatalogFilters(\n compact(Object.values(outputState.appliedFilters)),\n );\n\n const queryParams = Object.keys(requestedFilters).reduce(\n (params, key) => {\n const filter: EntityFilter | undefined =\n requestedFilters[key as keyof EntityFilters];\n if (filter?.toQueryValue) {\n params[key] = filter.toQueryValue();\n }\n return params;\n },\n {} as Record<string, string | string[]>,\n );\n\n // TODO(mtlewis): currently entities will never be requested unless\n // there's at least one filter, we should allow an initial request\n // to happen with no filters.\n if (!isEqual(previousBackendFilter, backendFilter)) {\n // TODO(timbonicus): should limit fields here, but would need filter\n // fields + table columns\n const response = await catalogApi.getEntities({\n filter: backendFilter,\n });\n setOutputState({\n appliedFilters: requestedFilters,\n backendEntities: response.items,\n entities: response.items.filter(entityFilter),\n queryParameters: queryParams,\n });\n } else {\n setOutputState({\n appliedFilters: requestedFilters,\n backendEntities: outputState.backendEntities,\n entities: outputState.backendEntities.filter(entityFilter),\n queryParameters: queryParams,\n });\n }\n\n if (isMounted()) {\n const oldParams = qs.parse(window.location.search, {\n ignoreQueryPrefix: true,\n });\n const newParams = qs.stringify(\n { ...oldParams, filters: queryParams },\n { addQueryPrefix: true },\n );\n const newUrl = `${window.location.pathname}${newParams}`;\n // We use direct history manipulation since useSearchParams and\n // useNavigate in react-router-dom cause unnecessary extra rerenders.\n // Also make sure to replace the state rather than pushing, since we\n // don't want there to be back/forward slots for every single filter\n // change.\n window.history?.replaceState(null, document.title, newUrl);\n }\n },\n [catalogApi, requestedFilters, outputState],\n { loading: true },\n );\n\n // Slight debounce on the refresh, since (especially on page load) several\n // filters will be calling this in rapid succession.\n useDebounce(refresh, 10, [requestedFilters]);\n\n const updateFilters = useCallback(\n (\n update:\n | Partial<EntityFilter>\n | ((prevFilters: EntityFilters) => Partial<EntityFilters>),\n ) => {\n setRequestedFilters(prevFilters => {\n const newFilters =\n typeof update === 'function' ? update(prevFilters) : update;\n return { ...prevFilters, ...newFilters };\n });\n },\n [],\n );\n\n const value = useMemo(\n () => ({\n filters: outputState.appliedFilters,\n entities: outputState.entities,\n backendEntities: outputState.backendEntities,\n updateFilters,\n queryParameters: outputState.queryParameters,\n loading,\n error,\n }),\n [outputState, updateFilters, loading, error],\n );\n\n return (\n <EntityListContext.Provider value={value}>\n {children}\n </EntityListContext.Provider>\n );\n};\n\nexport function useEntityListProvider<\n EntityFilters extends DefaultEntityFilters = DefaultEntityFilters,\n>(): EntityListContextProps<EntityFilters> {\n const context = useContext(EntityListContext);\n if (!context)\n throw new Error(\n 'useEntityListProvider must be used within EntityListProvider',\n );\n return context;\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n Entity,\n EntityName,\n ENTITY_DEFAULT_NAMESPACE,\n serializeEntityRef,\n} from '@backstage/catalog-model';\n\nexport function formatEntityRefTitle(\n entityRef: Entity | EntityName,\n opts?: { defaultKind?: string },\n) {\n const defaultKind = opts?.defaultKind;\n let kind;\n let namespace;\n let name;\n\n if ('metadata' in entityRef) {\n kind = entityRef.kind;\n namespace = entityRef.metadata.namespace;\n name = entityRef.metadata.name;\n } else {\n kind = entityRef.kind;\n namespace = entityRef.namespace;\n name = entityRef.name;\n }\n\n if (namespace === ENTITY_DEFAULT_NAMESPACE) {\n namespace = undefined;\n }\n\n kind = kind.toLowerCase();\n\n return `${serializeEntityRef({\n kind: defaultKind && defaultKind.toLowerCase() === kind ? undefined : kind,\n name,\n namespace,\n })}`;\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {\n Entity,\n EntityName,\n ENTITY_DEFAULT_NAMESPACE,\n} from '@backstage/catalog-model';\nimport React, { forwardRef } from 'react';\nimport { generatePath } from 'react-router';\nimport { entityRoute } from '../../routes';\nimport { formatEntityRefTitle } from './format';\nimport { Link, LinkProps } from '@backstage/core-components';\n\nexport type EntityRefLinkProps = {\n entityRef: Entity | EntityName;\n defaultKind?: string;\n children?: React.ReactNode;\n} & Omit<LinkProps, 'to'>;\n\nexport const EntityRefLink = forwardRef<any, EntityRefLinkProps>(\n (props, ref) => {\n const { entityRef, defaultKind, children, ...linkProps } = props;\n\n let kind;\n let namespace;\n let name;\n\n if ('metadata' in entityRef) {\n kind = entityRef.kind;\n namespace = entityRef.metadata.namespace;\n name = entityRef.metadata.name;\n } else {\n kind = entityRef.kind;\n namespace = entityRef.namespace;\n name = entityRef.name;\n }\n\n kind = kind.toLocaleLowerCase('en-US');\n\n const routeParams = {\n kind,\n namespace:\n namespace?.toLocaleLowerCase('en-US') ?? ENTITY_DEFAULT_NAMESPACE,\n name,\n };\n\n // TODO: Use useRouteRef here to generate the path\n return (\n <Link\n {...linkProps}\n ref={ref}\n to={generatePath(`/catalog/${entityRoute.path}`, routeParams)}\n >\n {children}\n {!children && formatEntityRefTitle(entityRef, { defaultKind })}\n </Link>\n );\n },\n);\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { Entity, EntityName } from '@backstage/catalog-model';\nimport React from 'react';\nimport { EntityRefLink } from './EntityRefLink';\nimport { LinkProps } from '@backstage/core-components';\n\nexport type EntityRefLinksProps = {\n entityRefs: (Entity | EntityName)[];\n defaultKind?: string;\n} & Omit<LinkProps, 'to'>;\n\nexport const EntityRefLinks = ({\n entityRefs,\n defaultKind,\n ...linkProps\n}: EntityRefLinksProps) => (\n <>\n {entityRefs.map((r, i) => (\n <React.Fragment key={i}>\n {i > 0 && ', '}\n <EntityRefLink {...linkProps} entityRef={r} defaultKind={defaultKind} />\n </React.Fragment>\n ))}\n </>\n);\n","/*\n * Copyright 2021 Spotify AB\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity, RELATION_OWNED_BY } from '@backstage/catalog-model';\nimport { formatEntityRefTitle } from './components/EntityRefLink';\nimport { EntityFilter, UserListFilterKind } from './types';\nimport { getEntityRelations } from './utils';\n\nexport class EntityKindFilter implements EntityFilter {\n constructor(readonly value: string) {}\n\n getCatalogFilters(): Record<string, string | string[]> {\n return { kind: this.value };\n }\n\n toQueryValue(): string {\n return this.value;\n }\n}\n\nexport class EntityTypeFilter implements EntityFilter {\n constructor(readonly value: string | string[]) {}\n\n // Simplify `string | string[]` for consumers, always returns an array\n getTypes(): string[] {\n return Array.isArray(this.value) ? this.value : [this.value];\n }\n\n getCatalogFilters(): Record<string, string | string[]> {\n return { 'spec.type': this.getTypes() };\n }\n\n toQueryValue(): string[] {\n return this.getTypes();\n }\n}\n\nexport class EntityTagFilter implements EntityFilter {\n constructor(readonly values: string[]) {}\n\n filterEntity(entity: Entity): boolean {\n return this.values.every(v => (entity.metadata.tags ?? []).includes(v));\n }\n\n toQueryValue(): string[] {\n return this.values;\n }\n}\n\nexport class EntityTextFilter implements EntityFilter {\n constructor(readonly value: string) {}\n\n filterEntity(entity: Entity): boolean {\n const upperCaseValue = this.value.toLocaleUpperCase('en-US');\n\n return (\n entity.metadata.name\n .toLocaleUpperCase('en-US')\n .includes(upperCaseValue) ||\n `${entity.metadata.title}`\n .toLocaleUpperCase('en-US')\n .includes(upperCaseValue) ||\n entity.metadata.tags\n ?.join('')\n .toLocaleUpperCase('en-US')\n .indexOf(upperCaseValue) !== -1\n );\n }\n}\n\nexport class EntityOwnerFilter implements EntityFilter {\n constructor(readonly values: string[]) {}\n\n filterEntity(entity: Entity): boolean {\n return this.values.some(v =>\n getEntityRelations(entity, RELATION_OWNED_BY).some(\n o => formatEntityRefTitle(o, { defaultKind: 'group' }) === v,\n ),\n );\n }\n\n toQueryValue(): string[] {\n return this.values;\n }\n}\n\nexport class EntityLifecycleFilter implements EntityFilter {\n constructor(readonly values: string[]) {}\n\n filterEntity(entity: Entity): boolean {\n return this.values.some(v => entity.spec?.lifecycle === v);\n }\n\n toQueryValue(): string[] {\n return this.values;\n }\n}\n\nexport class UserListFilter implements EntityFilter {\n constructor(\n readonly value: UserListFilterKind,\n readonly isOwnedEntity: (entity: Entity) => boolean,\n readonly isStarredEntity: (entity: Entity) => boolean,\n ) {}\n\n filterEntity(entity: Entity): boolean {\n switch (this.value) {\n case 'owned':\n return this.isOwnedEntity(entity);\n case 'starred':\n return this.isStarredEntity(entity);\n default:\n return true;\n }\n }\n\n toQueryValue(): string {\n return this.value;\n }\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { useEffect, useMemo, useRef, useState } from 'react';\nimport { useAsync } from 'react-use';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { catalogApiRef } from '../api';\nimport { useEntityListProvider } from './useEntityListProvider';\nimport { EntityTypeFilter } from '../filters';\n\ntype EntityTypeReturn = {\n loading: boolean;\n error?: Error;\n availableTypes: string[];\n selectedTypes: string[];\n setSelectedTypes: (types: string[]) => void;\n};\n\n/**\n * A hook built on top of `useEntityListProvider` for enabling selection of valid `spec.type` values\n * based on the selected EntityKindFilter.\n */\nexport function useEntityTypeFilter(): EntityTypeReturn {\n const catalogApi = useApi(catalogApiRef);\n const {\n filters: { kind: kindFilter, type: typeFilter },\n queryParameters,\n updateFilters,\n } = useEntityListProvider();\n\n const queryParamTypes = [queryParameters.type]\n .flat()\n .filter(Boolean) as string[];\n const [selectedTypes, setSelectedTypes] = useState(\n queryParamTypes.length ? queryParamTypes : typeFilter?.getTypes() ?? [],\n );\n\n const [availableTypes, setAvailableTypes] = useState<string[]>([]);\n const kind = useMemo(() => kindFilter?.value, [kindFilter]);\n\n // Load all valid spec.type values straight from the catalogApi, paying attention to only the\n // kind filter for a complete list.\n const {\n error,\n loading,\n value: entities,\n } = useAsync(async () => {\n if (kind) {\n const items = await catalogApi\n .getEntities({\n filter: { kind },\n fields: ['spec.type'],\n })\n .then(response => response.items);\n return items;\n }\n return [];\n }, [kind, catalogApi]);\n\n const entitiesRef = useRef(entities);\n useEffect(() => {\n const oldEntities = entitiesRef.current;\n entitiesRef.current = entities;\n // Delay processing hook until kind and entity load updates have settled to generate list of types;\n // This prevents reseting the type filter due to saved type value from query params not matching the\n // empty set of type values while values are still being loaded; also only run this hook on changes\n // to entities\n if (loading || !kind || oldEntities === entities) {\n return;\n }\n\n // Resolve the unique set of types from returned entities; could be optimized by a new endpoint\n // in the catalog-backend that does this, rather than loading entities with redundant types.\n if (!entities) return;\n\n // Sort by entity count descending, so the most common types appear on top\n const countByType = entities.reduce((acc, entity) => {\n if (typeof entity.spec?.type !== 'string') return acc;\n\n const entityType = entity.spec.type.toLocaleLowerCase('en-US');\n if (!acc[entityType]) {\n acc[entityType] = 0;\n }\n acc[entityType] += 1;\n return acc;\n }, {} as Record<string, number>);\n\n const newTypes = Object.entries(countByType)\n .sort(([, count1], [, count2]) => count2 - count1)\n .map(([type]) => type);\n setAvailableTypes(newTypes);\n\n // Update type filter to only valid values when the list of available types has changed\n const stillValidTypes = selectedTypes.filter(value =>\n newTypes.includes(value),\n );\n setSelectedTypes(stillValidTypes);\n }, [loading, kind, selectedTypes, setSelectedTypes, entities]);\n\n useEffect(() => {\n updateFilters({\n type: selectedTypes.length\n ? new EntityTypeFilter(selectedTypes)\n : undefined,\n });\n }, [selectedTypes, updateFilters]);\n\n return {\n loading,\n error,\n availableTypes,\n selectedTypes,\n setSelectedTypes,\n };\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { useAsync } from 'react-use';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { catalogApiRef } from '../api';\n\n// Retrieve a list of unique entity kinds present in the catalog\nexport function useEntityKinds() {\n const catalogApi = useApi(catalogApiRef);\n\n const {\n error,\n loading,\n value: kinds,\n } = useAsync(async () => {\n const entities = await catalogApi\n .getEntities({ fields: ['kind'] })\n .then(response => response.items);\n\n return [...new Set(entities.map(e => e.kind))].sort();\n });\n return { error, loading, kinds };\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { UserEntity } from '@backstage/catalog-model';\nimport { useAsync } from 'react-use';\nimport { AsyncState } from 'react-use/lib/useAsync';\nimport { catalogApiRef } from '../api';\nimport { identityApiRef, useApi } from '@backstage/core-plugin-api';\n\n/**\n * Get the catalog User entity (if any) that matches the logged-in user.\n */\nexport function useOwnUser(): AsyncState<UserEntity | undefined> {\n const catalogApi = useApi(catalogApiRef);\n const identityApi = useApi(identityApiRef);\n\n // TODO: get the full entity (or at least the full entity name) from the\n // identityApi\n return useAsync(\n () =>\n catalogApi.getEntityByName({\n kind: 'User',\n namespace: 'default',\n name: identityApi.getUserId(),\n }) as Promise<UserEntity | undefined>,\n [catalogApi, identityApi],\n );\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { Entity, EntityRelation } from '@backstage/catalog-model';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { chunk, groupBy } from 'lodash';\nimport { useAsync } from 'react-use';\nimport { catalogApiRef } from '../api';\n\nconst BATCH_SIZE = 20;\n\nexport function useRelatedEntities(\n entity: Entity,\n { type, kind }: { type?: string; kind?: string },\n): {\n entities: Entity[] | undefined;\n loading: boolean;\n error: Error | undefined;\n} {\n const catalogApi = useApi(catalogApiRef);\n const {\n loading,\n value: entities,\n error,\n } = useAsync(async () => {\n const relations =\n entity.relations &&\n entity.relations.filter(\n r =>\n (!type || r.type.toLowerCase() === type.toLowerCase()) &&\n (!kind || r.target.kind.toLowerCase() === kind.toLowerCase()),\n );\n\n if (!relations) {\n return [];\n }\n\n // Group the relations by kind and namespace to reduce the size of the request query string.\n // Without this grouping, the kind and namespace would need to be specified for each relation, e.g.\n // `filter=kind=component,namespace=default,name=example1&filter=kind=component,namespace=default,name=example2`\n // with grouping, we can generate a query a string like\n // `filter=kind=component,namespace=default,name=example1,example2`\n const relationsByKindAndNamespace: EntityRelation[][] = Object.values(\n groupBy(relations, ({ target }) => {\n return `${target.kind}:${target.namespace}`.toLowerCase();\n }),\n );\n\n // Split the names within each group into batches to further reduce the query string length.\n const batchedRelationsByKindAndNamespace: {\n kind: string;\n namespace: string;\n nameBatches: string[][];\n }[] = [];\n for (const rs of relationsByKindAndNamespace) {\n batchedRelationsByKindAndNamespace.push({\n // All relations in a group have the same kind and namespace, so its arbitrary which we pick\n kind: rs[0].target.kind,\n namespace: rs[0].target.namespace,\n nameBatches: chunk(\n rs.map(r => r.target.name),\n BATCH_SIZE,\n ),\n });\n }\n\n const results = await Promise.all(\n batchedRelationsByKindAndNamespace.flatMap(rs => {\n return rs.nameBatches.map(names => {\n return catalogApi.getEntities({\n filter: {\n kind: rs.kind,\n 'metadata.namespace': rs.namespace,\n 'metadata.name': names,\n },\n });\n });\n }),\n );\n\n return results.flatMap(r => r.items);\n }, [entity, type]);\n\n return {\n entities,\n loading,\n error,\n };\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity } from '@backstage/catalog-model';\nimport { storageApiRef, useApi } from '@backstage/core-plugin-api';\nimport { useCallback, useEffect, useState } from 'react';\nimport { useObservable } from 'react-use';\n\nconst buildEntityKey = (component: Entity) =>\n `entity:${component.kind}:${component.metadata.namespace ?? 'default'}:${\n component.metadata.name\n }`;\n\nexport const useStarredEntities = () => {\n const storageApi = useApi(storageApiRef);\n const settingsStore = storageApi.forBucket('settings');\n const rawStarredEntityKeys =\n settingsStore.get<string[]>('starredEntities') ?? [];\n\n const [starredEntities, setStarredEntities] = useState(\n new Set(rawStarredEntityKeys),\n );\n\n const observedItems = useObservable(\n settingsStore.observe$<string[]>('starredEntities'),\n );\n\n useEffect(() => {\n if (observedItems?.newValue) {\n const currentValue = observedItems?.newValue ?? [];\n setStarredEntities(new Set(currentValue));\n }\n }, [observedItems?.newValue]);\n\n const toggleStarredEntity = useCallback(\n (entity: Entity) => {\n const entityKey = buildEntityKey(entity);\n if (starredEntities.has(entityKey)) {\n starredEntities.delete(entityKey);\n } else {\n starredEntities.add(entityKey);\n }\n\n settingsStore.set('starredEntities', Array.from(starredEntities));\n },\n [starredEntities, settingsStore],\n );\n\n const isStarredEntity = useCallback(\n (entity: Entity) => {\n const entityKey = buildEntityKey(entity);\n return starredEntities.has(entityKey);\n },\n [starredEntities],\n );\n\n return {\n starredEntities,\n toggleStarredEntity,\n isStarredEntity,\n };\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { CatalogApi } from '@backstage/catalog-client';\nimport {\n Entity,\n EntityName,\n parseEntityRef,\n RELATION_MEMBER_OF,\n RELATION_OWNED_BY,\n stringifyEntityRef,\n} from '@backstage/catalog-model';\nimport {\n IdentityApi,\n identityApiRef,\n useApi,\n} from '@backstage/core-plugin-api';\nimport jwtDecoder from 'jwt-decode';\nimport { useMemo } from 'react';\nimport { useAsync } from 'react-use';\nimport { catalogApiRef } from '../api';\nimport { getEntityRelations } from '../utils/getEntityRelations';\n\n// Takes a user ID from the identity, which can be on basically any form, and\n// returns an entity ref. E.g. if the input is \"foo\", it returns\n// \"user:default/foo\" to make sure it's a full ref.\nfunction extendUserId(id: string): string {\n try {\n const ref = parseEntityRef(id, {\n defaultKind: 'User',\n defaultNamespace: 'default',\n });\n return stringifyEntityRef(ref);\n } catch {\n return id;\n }\n}\n\n// Takes the relevant parts of the Backstage identity, and translates them into\n// a list of entity refs on string form that represent the user's ownership\n// connections.\nexport async function loadIdentityOwnerRefs(\n identityApi: IdentityApi,\n): Promise<string[]> {\n const id = identityApi.getUserId();\n const token = await identityApi.getIdToken();\n const result: string[] = [];\n\n if (id) {\n result.push(extendUserId(id));\n }\n\n if (token) {\n try {\n const decoded = jwtDecoder(token) as any;\n if (decoded?.ent) {\n [decoded.ent]\n .flat()\n .filter(x => typeof x === 'string')\n .map(x => x.toLocaleLowerCase('en-US'))\n .forEach(x => result.push(x));\n }\n } catch {\n // ignore\n }\n }\n\n return result;\n}\n\n// Takes the relevant parts of the User entity corresponding to the Backstage\n// identity, and translates them into a list of entity refs on string form that\n// represent the user's ownership connections.\nexport async function loadCatalogOwnerRefs(\n catalogApi: CatalogApi,\n identityOwnerRefs: string[],\n): Promise<string[]> {\n const result = new Array<string>();\n\n const primaryUserRef = identityOwnerRefs.find(ref => ref.startsWith('user:'));\n if (primaryUserRef) {\n const entity = await catalogApi.getEntityByName(\n parseEntityRef(primaryUserRef),\n );\n if (entity) {\n const memberOf = getEntityRelations(entity, RELATION_MEMBER_OF, {\n kind: 'Group',\n });\n for (const group of memberOf) {\n result.push(stringifyEntityRef(group));\n }\n }\n }\n\n return result;\n}\n\n/**\n * Returns a function that checks whether the currently signed-in user is an\n * owner of a given entity. When the hook is initially mounted, the loading\n * flag will be true and the results returned from the function will always be\n * false.\n */\nexport function useEntityOwnership(): {\n loading: boolean;\n isOwnedEntity: (entity: Entity | EntityName) => boolean;\n} {\n const identityApi = useApi(identityApiRef);\n const catalogApi = useApi(catalogApiRef);\n\n // Trigger load only on mount\n const { loading, value: refs } = useAsync(async () => {\n const identityRefs = await loadIdentityOwnerRefs(identityApi);\n const catalogRefs = await loadCatalogOwnerRefs(catalogApi, identityRefs);\n return new Set([...identityRefs, ...catalogRefs]);\n }, []);\n\n const isOwnedEntity = useMemo(() => {\n const myOwnerRefs = new Set(refs ?? []);\n return (entity: Entity | EntityName) => {\n const entityOwnerRefs = (\n 'metadata' in entity\n ? getEntityRelations(entity, RELATION_OWNED_BY)\n : [entity]\n ).map(stringifyEntityRef);\n for (const ref of entityOwnerRefs) {\n if (myOwnerRefs.has(ref)) {\n return true;\n }\n }\n return false;\n };\n }, [refs]);\n\n return useMemo(() => ({ loading, isOwnedEntity }), [loading, isOwnedEntity]);\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React, { useEffect, useState } from 'react';\nimport { Alert } from '@material-ui/lab';\nimport { useEntityListProvider } from '../../hooks';\nimport { EntityKindFilter } from '../../filters';\n\ntype EntityKindFilterProps = {\n initialFilter?: string;\n hidden: boolean;\n};\n\nexport const EntityKindPicker = ({\n initialFilter,\n hidden,\n}: EntityKindFilterProps) => {\n const { updateFilters, queryParameters } = useEntityListProvider();\n const [selectedKind] = useState(\n [queryParameters.kind].flat()[0] ?? initialFilter,\n );\n\n useEffect(() => {\n updateFilters({\n kind: selectedKind ? new EntityKindFilter(selectedKind) : undefined,\n });\n }, [selectedKind, updateFilters]);\n\n if (hidden) return null;\n\n // TODO(timbonicus): This should load available kinds from the catalog-backend, similar to\n // EntityTypePicker.\n\n return <Alert severity=\"warning\">Kind filter not yet available</Alert>;\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity } from '@backstage/catalog-model';\nimport {\n Box,\n Checkbox,\n FormControlLabel,\n TextField,\n Typography,\n} from '@material-ui/core';\nimport CheckBoxIcon from '@material-ui/icons/CheckBox';\nimport CheckBoxOutlineBlankIcon from '@material-ui/icons/CheckBoxOutlineBlank';\nimport ExpandMoreIcon from '@material-ui/icons/ExpandMore';\nimport { Autocomplete } from '@material-ui/lab';\nimport React, { useEffect, useMemo, useState } from 'react';\nimport { useEntityListProvider } from '../../hooks/useEntityListProvider';\nimport { EntityLifecycleFilter } from '../../filters';\n\nconst icon = <CheckBoxOutlineBlankIcon fontSize=\"small\" />;\nconst checkedIcon = <CheckBoxIcon fontSize=\"small\" />;\n\nexport const EntityLifecyclePicker = () => {\n const { updateFilters, backendEntities, filters, queryParameters } =\n useEntityListProvider();\n\n const queryParamLifecycles = [queryParameters.lifecycles]\n .flat()\n .filter(Boolean) as string[];\n const [selectedLifecycles, setSelectedLifecycles] = useState(\n queryParamLifecycles.length\n ? queryParamLifecycles\n : filters.lifecycles?.values ?? [],\n );\n\n useEffect(() => {\n updateFilters({\n lifecycles: selectedLifecycles.length\n ? new EntityLifecycleFilter(selectedLifecycles)\n : undefined,\n });\n }, [selectedLifecycles, updateFilters]);\n\n const availableLifecycles = useMemo(\n () =>\n [\n ...new Set(\n backendEntities\n .map((e: Entity) => e.spec?.lifecycle)\n .filter(Boolean) as string[],\n ),\n ].sort(),\n [backendEntities],\n );\n\n if (!availableLifecycles.length) return null;\n\n return (\n <Box pb={1} pt={1}>\n <Typography variant=\"button\">Lifecycle</Typography>\n <Autocomplete<string>\n aria-label=\"Lifecycle\"\n multiple\n options={availableLifecycles}\n value={selectedLifecycles}\n onChange={(_: object, value: string[]) => setSelectedLifecycles(value)}\n renderOption={(option, { selected }) => (\n <FormControlLabel\n control={\n <Checkbox\n icon={icon}\n checkedIcon={checkedIcon}\n checked={selected}\n />\n }\n label={option}\n />\n )}\n size=\"small\"\n popupIcon={<ExpandMoreIcon data-testid=\"lifecycle-picker-expand\" />}\n renderInput={params => <TextField {...params} variant=\"outlined\" />}\n />\n </Box>\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity, RELATION_OWNED_BY } from '@backstage/catalog-model';\nimport {\n Box,\n Checkbox,\n FormControlLabel,\n TextField,\n Typography,\n} from '@material-ui/core';\nimport CheckBoxIcon from '@material-ui/icons/CheckBox';\nimport CheckBoxOutlineBlankIcon from '@material-ui/icons/CheckBoxOutlineBlank';\nimport ExpandMoreIcon from '@material-ui/icons/ExpandMore';\nimport { Autocomplete } from '@material-ui/lab';\nimport React, { useEffect, useMemo, useState } from 'react';\nimport { useEntityListProvider } from '../../hooks/useEntityListProvider';\nimport { EntityOwnerFilter } from '../../filters';\nimport { getEntityRelations } from '../../utils';\nimport { formatEntityRefTitle } from '../EntityRefLink';\n\nconst icon = <CheckBoxOutlineBlankIcon fontSize=\"small\" />;\nconst checkedIcon = <CheckBoxIcon fontSize=\"small\" />;\n\nexport const EntityOwnerPicker = () => {\n const { updateFilters, backendEntities, filters, queryParameters } =\n useEntityListProvider();\n\n const queryParamOwners = [queryParameters.owners]\n .flat()\n .filter(Boolean) as string[];\n const [selectedOwners, setSelectedOwners] = useState(\n queryParamOwners.length ? queryParamOwners : filters.owners?.values ?? [],\n );\n\n useEffect(() => {\n updateFilters({\n owners: selectedOwners.length\n ? new EntityOwnerFilter(selectedOwners)\n : undefined,\n });\n }, [selectedOwners, updateFilters]);\n\n const availableOwners = useMemo(\n () =>\n [\n ...new Set(\n backendEntities\n .flatMap((e: Entity) =>\n getEntityRelations(e, RELATION_OWNED_BY).map(o =>\n formatEntityRefTitle(o, { defaultKind: 'group' }),\n ),\n )\n .filter(Boolean) as string[],\n ),\n ].sort(),\n [backendEntities],\n );\n\n if (!availableOwners.length) return null;\n\n return (\n <Box pb={1} pt={1}>\n <Typography variant=\"button\">Owner</Typography>\n <Autocomplete<string>\n multiple\n aria-label=\"Owner\"\n options={availableOwners}\n value={selectedOwners}\n onChange={(_: object, value: string[]) => setSelectedOwners(value)}\n renderOption={(option, { selected }) => (\n <FormControlLabel\n control={\n <Checkbox\n icon={icon}\n checkedIcon={checkedIcon}\n checked={selected}\n />\n }\n label={option}\n />\n )}\n size=\"small\"\n popupIcon={<ExpandMoreIcon data-testid=\"owner-picker-expand\" />}\n renderInput={params => <TextField {...params} variant=\"outlined\" />}\n />\n </Box>\n );\n};\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { Entity } from '@backstage/catalog-model';\nimport React, { ReactNode } from 'react';\nimport { EntityContext } from '../../hooks';\n\ntype EntityProviderProps = {\n entity: Entity;\n children: ReactNode;\n};\n\nexport const EntityProvider = ({ entity, children }: EntityProviderProps) => (\n <EntityContext.Provider\n value={{\n entity,\n loading: !Boolean(entity),\n error: undefined,\n }}\n >\n {children}\n </EntityContext.Provider>\n);\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n FormControl,\n IconButton,\n Input,\n InputAdornment,\n makeStyles,\n Toolbar,\n} from '@material-ui/core';\nimport Clear from '@material-ui/icons/Clear';\nimport Search from '@material-ui/icons/Search';\nimport React, { useState } from 'react';\nimport { useDebounce } from 'react-use';\nimport { useEntityListProvider } from '../../hooks/useEntityListProvider';\nimport { EntityTextFilter } from '../../filters';\n\nconst useStyles = makeStyles(_theme => ({\n searchToolbar: {\n paddingLeft: 0,\n paddingRight: 0,\n },\n}));\n\nexport const EntitySearchBar = () => {\n const styles = useStyles();\n\n const { filters, updateFilters } = useEntityListProvider();\n const [search, setSearch] = useState(filters.text?.value ?? '');\n\n useDebounce(\n () => {\n updateFilters({\n text: search.length ? new EntityTextFilter(search) : undefined,\n });\n },\n 250,\n [search, updateFilters],\n );\n\n return (\n <Toolbar className={styles.searchToolbar}>\n <FormControl>\n <Input\n id=\"input-with-icon-adornment\"\n placeholder=\"Search\"\n autoComplete=\"off\"\n onChange={event => setSearch(event.target.value)}\n value={search}\n startAdornment={\n <InputAdornment position=\"start\">\n <Search />\n </InputAdornment>\n }\n endAdornment={\n <InputAdornment position=\"end\">\n <IconButton\n aria-label=\"clear search\"\n onClick={() => setSearch('')}\n edge=\"end\"\n disabled={search.length === 0}\n >\n <Clear />\n </IconButton>\n </InputAdornment>\n }\n />\n </FormControl>\n </Toolbar>\n );\n};\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n Entity,\n EntityName,\n RELATION_OWNED_BY,\n RELATION_PART_OF,\n} from '@backstage/catalog-model';\nimport React from 'react';\nimport { getEntityRelations } from '../../utils';\nimport {\n EntityRefLink,\n EntityRefLinks,\n formatEntityRefTitle,\n} from '../EntityRefLink';\nimport { OverflowTooltip, TableColumn } from '@backstage/core-components';\n\nexport function createEntityRefColumn<T extends Entity>({\n defaultKind,\n}: {\n defaultKind?: string;\n}): TableColumn<T> {\n function formatContent(entity: T): string {\n return formatEntityRefTitle(entity, {\n defaultKind,\n });\n }\n\n return {\n title: 'Name',\n highlight: true,\n customFilterAndSearch(filter, entity) {\n // TODO: We could implement this more efficiently, like searching over\n // each field that is displayed individually (kind, namespace, name).\n // but that migth confuse the user as it will behave different than a\n // simple text search.\n // Another alternative would be to cache the values. But writing them\n // into the entity feels bad too.\n return formatContent(entity).includes(filter);\n },\n customSort(entity1, entity2) {\n // TODO: We could implement this more efficiently by comparing field by field.\n // This has similar issues as above.\n return formatContent(entity1).localeCompare(formatContent(entity2));\n },\n render: entity => (\n <EntityRefLink entityRef={entity} defaultKind={defaultKind} />\n ),\n };\n}\n\nexport function createEntityRelationColumn<T extends Entity>({\n title,\n relation,\n defaultKind,\n filter: entityFilter,\n}: {\n title: string;\n relation: string;\n defaultKind?: string;\n filter?: { kind: string };\n}): TableColumn<T> {\n function getRelations(entity: T): EntityName[] {\n return getEntityRelations(entity, relation, entityFilter);\n }\n\n function formatContent(entity: T): string {\n return getRelations(entity)\n .map(r => formatEntityRefTitle(r, { defaultKind }))\n .join(', ');\n }\n\n return {\n title,\n customFilterAndSearch(filter, entity) {\n return formatContent(entity).includes(filter);\n },\n customSort(entity1, entity2) {\n return formatContent(entity1).localeCompare(formatContent(entity2));\n },\n render: entity => {\n return (\n <EntityRefLinks\n entityRefs={getRelations(entity)}\n defaultKind={defaultKind}\n />\n );\n },\n };\n}\n\nexport function createOwnerColumn<T extends Entity>(): TableColumn<T> {\n return createEntityRelationColumn({\n title: 'Owner',\n relation: RELATION_OWNED_BY,\n defaultKind: 'group',\n });\n}\n\nexport function createDomainColumn<T extends Entity>(): TableColumn<T> {\n return createEntityRelationColumn({\n title: 'Domain',\n relation: RELATION_PART_OF,\n defaultKind: 'domain',\n filter: {\n kind: 'domain',\n },\n });\n}\n\nexport function createSystemColumn<T extends Entity>(): TableColumn<T> {\n return createEntityRelationColumn({\n title: 'System',\n relation: RELATION_PART_OF,\n defaultKind: 'system',\n filter: {\n kind: 'system',\n },\n });\n}\n\nexport function createMetadataDescriptionColumn<\n T extends Entity,\n>(): TableColumn<T> {\n return {\n title: 'Description',\n field: 'metadata.description',\n render: entity => (\n <OverflowTooltip\n text={entity.metadata.description}\n placement=\"bottom-start\"\n />\n ),\n width: 'auto',\n };\n}\n\nexport function createSpecLifecycleColumn<T extends Entity>(): TableColumn<T> {\n return {\n title: 'Lifecycle',\n field: 'spec.lifecycle',\n };\n}\n\nexport function createSpecTypeColumn<T extends Entity>(): TableColumn<T> {\n return {\n title: 'Type',\n field: 'spec.type',\n };\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { ComponentEntity, SystemEntity } from '@backstage/catalog-model';\nimport {\n createDomainColumn,\n createEntityRefColumn,\n createMetadataDescriptionColumn,\n createOwnerColumn,\n createSpecLifecycleColumn,\n createSpecTypeColumn,\n createSystemColumn,\n} from './columns';\nimport { TableColumn } from '@backstage/core-components';\n\nexport const systemEntityColumns: TableColumn<SystemEntity>[] = [\n createEntityRefColumn({ defaultKind: 'system' }),\n createDomainColumn(),\n createOwnerColumn(),\n createMetadataDescriptionColumn(),\n];\n\nexport const componentEntityColumns: TableColumn<ComponentEntity>[] = [\n createEntityRefColumn({ defaultKind: 'component' }),\n createSystemColumn(),\n createOwnerColumn(),\n createSpecTypeColumn(),\n createSpecLifecycleColumn(),\n createMetadataDescriptionColumn(),\n];\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity } from '@backstage/catalog-model';\nimport { makeStyles } from '@material-ui/core';\nimport React, { ReactNode } from 'react';\nimport * as columnFactories from './columns';\nimport { componentEntityColumns, systemEntityColumns } from './presets';\nimport { Table, TableColumn } from '@backstage/core-components';\n\ntype Props<T extends Entity> = {\n title: string;\n variant?: 'gridItem';\n entities: T[];\n emptyContent?: ReactNode;\n columns: TableColumn<T>[];\n};\n\nconst useStyles = makeStyles(theme => ({\n empty: {\n padding: theme.spacing(2),\n display: 'flex',\n justifyContent: 'center',\n },\n}));\n\nexport function EntityTable<T extends Entity>({\n entities,\n title,\n emptyContent,\n variant = 'gridItem',\n columns,\n}: Props<T>) {\n const classes = useStyles();\n const tableStyle: React.CSSProperties = {\n minWidth: '0',\n width: '100%',\n };\n\n if (variant === 'gridItem') {\n tableStyle.height = 'calc(100% - 10px)';\n }\n\n return (\n <Table<T>\n columns={columns}\n title={title}\n style={tableStyle}\n emptyContent={\n emptyContent && <div className={classes.empty}>{emptyContent}</div>\n }\n options={{\n // TODO: Toolbar padding if off compared to other cards, should be: padding: 16px 24px;\n search: false,\n paging: false,\n actionsColumnIndex: -1,\n padding: 'dense',\n }}\n data={entities}\n />\n );\n}\n\nEntityTable.columns = columnFactories;\n\nEntityTable.systemEntityColumns = systemEntityColumns;\n\nEntityTable.componentEntityColumns = componentEntityColumns;\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity } from '@backstage/catalog-model';\nimport {\n Box,\n Checkbox,\n FormControlLabel,\n TextField,\n Typography,\n} from '@material-ui/core';\nimport CheckBoxIcon from '@material-ui/icons/CheckBox';\nimport CheckBoxOutlineBlankIcon from '@material-ui/icons/CheckBoxOutlineBlank';\nimport ExpandMoreIcon from '@material-ui/icons/ExpandMore';\nimport { Autocomplete } from '@material-ui/lab';\nimport React, { useEffect, useMemo, useState } from 'react';\nimport { useEntityListProvider } from '../../hooks/useEntityListProvider';\nimport { EntityTagFilter } from '../../filters';\n\nconst icon = <CheckBoxOutlineBlankIcon fontSize=\"small\" />;\nconst checkedIcon = <CheckBoxIcon fontSize=\"small\" />;\n\nexport const EntityTagPicker = () => {\n const { updateFilters, backendEntities, filters, queryParameters } =\n useEntityListProvider();\n\n const queryParamTags = [queryParameters.tags]\n .flat()\n .filter(Boolean) as string[];\n const [selectedTags, setSelectedTags] = useState(\n queryParamTags.length ? queryParamTags : filters.tags?.values ?? [],\n );\n\n useEffect(() => {\n updateFilters({\n tags: selectedTags.length ? new EntityTagFilter(selectedTags) : undefined,\n });\n }, [selectedTags, updateFilters]);\n\n const availableTags = useMemo(\n () =>\n [\n ...new Set(\n backendEntities\n .flatMap((e: Entity) => e.metadata.tags)\n .filter(Boolean) as string[],\n ),\n ].sort(),\n [backendEntities],\n );\n\n if (!availableTags.length) return null;\n\n return (\n <Box pb={1} pt={1}>\n <Typography variant=\"button\">Tags</Typography>\n <Autocomplete<string>\n multiple\n aria-label=\"Tags\"\n options={availableTags}\n value={selectedTags}\n onChange={(_: object, value: string[]) => setSelectedTags(value)}\n renderOption={(option, { selected }) => (\n <FormControlLabel\n control={\n <Checkbox\n icon={icon}\n checkedIcon={checkedIcon}\n checked={selected}\n />\n }\n label={option}\n />\n )}\n size=\"small\"\n popupIcon={<ExpandMoreIcon data-testid=\"tag-picker-expand\" />}\n renderInput={params => <TextField {...params} variant=\"outlined\" />}\n />\n </Box>\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React, { useEffect } from 'react';\nimport capitalize from 'lodash/capitalize';\nimport { Box } from '@material-ui/core';\nimport { useEntityTypeFilter } from '../../hooks/useEntityTypeFilter';\n\nimport { alertApiRef, useApi } from '@backstage/core-plugin-api';\nimport { Select } from '@backstage/core-components';\n\nexport const EntityTypePicker = () => {\n const alertApi = useApi(alertApiRef);\n const { error, availableTypes, selectedTypes, setSelectedTypes } =\n useEntityTypeFilter();\n\n useEffect(() => {\n if (error) {\n alertApi.post({\n message: `Failed to load entity types`,\n severity: 'error',\n });\n }\n }, [error, alertApi]);\n\n if (availableTypes.length === 0 || error) return null;\n\n const items = [\n { value: 'all', label: 'All' },\n ...availableTypes.map((type: string) => ({\n value: type,\n label: capitalize(type),\n })),\n ];\n\n return (\n <Box pb={1} pt={1}>\n <Select\n label=\"Type\"\n items={items}\n selected={(items.length > 1 ? selectedTypes[0] : undefined) ?? 'all'}\n onChange={value =>\n setSelectedTypes(value === 'all' ? [] : [String(value)])\n }\n />\n </Box>\n );\n};\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React, { ComponentProps } from 'react';\nimport { useStarredEntities } from '../../hooks/useStarredEntities';\nimport { IconButton, Tooltip, withStyles } from '@material-ui/core';\nimport StarBorder from '@material-ui/icons/StarBorder';\nimport Star from '@material-ui/icons/Star';\nimport { Entity } from '@backstage/catalog-model';\n\ntype Props = ComponentProps<typeof IconButton> & { entity: Entity };\n\nconst YellowStar = withStyles({\n root: {\n color: '#f3ba37',\n },\n})(Star);\n\nexport const favoriteEntityTooltip = (isStarred: boolean) =>\n isStarred ? 'Remove from favorites' : 'Add to favorites';\n\nexport const favoriteEntityIcon = (isStarred: boolean) =>\n isStarred ? <YellowStar /> : <StarBorder />;\n\n/**\n * IconButton for showing if a current entity is starred and adding/removing it from the favorite entities\n * @param props MaterialUI IconButton props extended by required `entity` prop\n */\nexport const FavoriteEntity = (props: Props) => {\n const { toggleStarredEntity, isStarredEntity } = useStarredEntities();\n const isStarred = isStarredEntity(props.entity);\n return (\n <IconButton\n color=\"inherit\"\n {...props}\n onClick={() => toggleStarredEntity(props.entity)}\n >\n <Tooltip title={favoriteEntityTooltip(isStarred)}>\n {favoriteEntityIcon(isStarred)}\n </Tooltip>\n </IconButton>\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n Entity,\n EntityName,\n getEntityName,\n ORIGIN_LOCATION_ANNOTATION,\n} from '@backstage/catalog-model';\nimport { catalogApiRef } from '../../api';\nimport { useCallback } from 'react';\nimport { useAsync } from 'react-use';\nimport { useApi } from '@backstage/core-plugin-api';\n\n/**\n * Each distinct state that the dialog can be in at any given time.\n */\nexport type UseUnregisterEntityDialogState =\n | {\n type: 'loading';\n }\n | {\n type: 'error';\n error: Error;\n }\n | {\n type: 'bootstrap';\n location: string;\n deleteEntity: () => Promise<void>;\n }\n | {\n type: 'unregister';\n location: string;\n colocatedEntities: EntityName[];\n unregisterLocation: () => Promise<void>;\n deleteEntity: () => Promise<void>;\n }\n | {\n type: 'only-delete';\n deleteEntity: () => Promise<void>;\n };\n\n/**\n * Houses the main logic for unregistering entities and their locations.\n */\nexport function useUnregisterEntityDialogState(\n entity: Entity,\n): UseUnregisterEntityDialogState {\n const catalogApi = useApi(catalogApiRef);\n const locationRef = entity.metadata.annotations?.[ORIGIN_LOCATION_ANNOTATION];\n const uid = entity.metadata.uid;\n const isBootstrap = locationRef === 'bootstrap:bootstrap';\n\n // Load the prerequisite data: what entities that are colocated with us, and\n // what location that spawned us\n const prerequisites = useAsync(async () => {\n const locationPromise = catalogApi.getOriginLocationByEntity(entity);\n\n let colocatedEntitiesPromise: Promise<Entity[]>;\n if (!locationRef) {\n colocatedEntitiesPromise = Promise.resolve([]);\n } else {\n const locationAnnotationFilter = `metadata.annotations.${ORIGIN_LOCATION_ANNOTATION}`;\n colocatedEntitiesPromise = catalogApi\n .getEntities({\n filter: { [locationAnnotationFilter]: locationRef },\n fields: [\n 'kind',\n 'metadata.uid',\n 'metadata.name',\n 'metadata.namespace',\n ],\n })\n .then(response => response.items);\n }\n\n return Promise.all([locationPromise, colocatedEntitiesPromise]).then(\n ([location, colocatedEntities]) => ({\n location,\n colocatedEntities,\n }),\n );\n }, [catalogApi, entity]);\n\n // Unregisters the underlying location and removes all of the entities that\n // are spawned from it. Can only ever be called when the prerequisites have\n // finished loading successfully, and if there was a matching location.\n const unregisterLocation = useCallback(\n async function unregisterLocationFn() {\n const { location, colocatedEntities } = prerequisites.value!;\n await catalogApi.removeLocationById(location!.id);\n await Promise.allSettled(\n colocatedEntities.map(e =>\n catalogApi.removeEntityByUid(e.metadata.uid!),\n ),\n );\n },\n [catalogApi, prerequisites],\n );\n\n // Just removes the entity, without affecting locations in any way.\n const deleteEntity = useCallback(\n async function deleteEntityFn() {\n await catalogApi.removeEntityByUid(uid!);\n },\n [catalogApi, uid],\n );\n\n // If this is a bootstrap location entity, don't even block on loading\n // prerequisites. We know that all that we will do is to offer to remove the\n // entity, and that doesn't require anything from the prerequisites.\n if (isBootstrap) {\n return { type: 'bootstrap', location: locationRef!, deleteEntity };\n }\n\n // Return early if prerequisites still loading or failing\n const { loading, error, value } = prerequisites;\n if (loading) {\n return { type: 'loading' };\n } else if (error) {\n return { type: 'error', error };\n }\n\n const { location, colocatedEntities } = value!;\n if (!location) {\n return { type: 'only-delete', deleteEntity };\n }\n return {\n type: 'unregister',\n location: locationRef!,\n colocatedEntities: colocatedEntities.map(getEntityName),\n unregisterLocation,\n deleteEntity,\n };\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity } from '@backstage/catalog-model';\nimport { EntityRefLink } from '../EntityRefLink';\nimport {\n Box,\n Button,\n Dialog,\n DialogActions,\n DialogContent,\n DialogContentText,\n DialogTitle,\n Divider,\n makeStyles,\n} from '@material-ui/core';\nimport Alert from '@material-ui/lab/Alert';\nimport React, { useCallback, useState } from 'react';\nimport { useUnregisterEntityDialogState } from './useUnregisterEntityDialogState';\n\nimport { alertApiRef, configApiRef, useApi } from '@backstage/core-plugin-api';\nimport { Progress, ResponseErrorPanel } from '@backstage/core-components';\n\nconst useStyles = makeStyles({\n advancedButton: {\n fontSize: '0.7em',\n },\n});\n\ntype Props = {\n open: boolean;\n onConfirm: () => any;\n onClose: () => any;\n entity: Entity;\n};\n\nconst Contents = ({\n entity,\n onConfirm,\n}: {\n entity: Entity;\n onConfirm: () => any;\n}) => {\n const alertApi = useApi(alertApiRef);\n const configApi = useApi(configApiRef);\n const classes = useStyles();\n const state = useUnregisterEntityDialogState(entity);\n const [showDelete, setShowDelete] = useState(false);\n const [busy, setBusy] = useState(false);\n const appTitle = configApi.getOptionalString('app.title') ?? 'Backstage';\n\n const onUnregister = useCallback(\n async function onUnregisterFn() {\n if ('unregisterLocation' in state) {\n setBusy(true);\n try {\n await state.unregisterLocation();\n onConfirm();\n } catch (err) {\n alertApi.post({ message: err.message });\n } finally {\n setBusy(false);\n }\n }\n },\n [alertApi, onConfirm, state],\n );\n\n const onDelete = useCallback(\n async function onDeleteFn() {\n if ('deleteEntity' in state) {\n setBusy(true);\n try {\n await state.deleteEntity();\n onConfirm();\n } catch (err) {\n alertApi.post({ message: err.message });\n } finally {\n setBusy(false);\n }\n }\n },\n [alertApi, onConfirm, state],\n );\n\n if (state.type === 'loading') {\n return <Progress />;\n }\n\n if (state.type === 'error') {\n return <ResponseErrorPanel error={state.error} />;\n }\n\n if (state.type === 'bootstrap') {\n return (\n <>\n <Alert severity=\"info\">\n You cannot unregister this entity, since it originates from a\n protected Backstage configuration (location \"{state.location}\"). If\n you believe this is in error, please contact the {appTitle}{' '}\n integrator.\n </Alert>\n\n <Box marginTop={2}>\n {!showDelete && (\n <Button\n variant=\"text\"\n size=\"small\"\n color=\"primary\"\n className={classes.advancedButton}\n onClick={() => setShowDelete(true)}\n >\n Advanced Options\n </Button>\n )}\n\n {showDelete && (\n <>\n <DialogContentText>\n You have the option to delete the entity itself from the\n catalog. Note that this should only be done if you know that the\n catalog file has been deleted at, or moved from, its origin\n location. If that is not the case, the entity will reappear\n shortly as the next refresh round is performed by the catalog.\n </DialogContentText>\n <Button\n variant=\"contained\"\n color=\"secondary\"\n disabled={busy}\n onClick={onDelete}\n >\n Delete Entity\n </Button>\n </>\n )}\n </Box>\n </>\n );\n }\n\n if (state.type === 'only-delete') {\n return (\n <>\n <DialogContentText>\n This entity does not seem to originate from a registered location. You\n therefore only have the option to delete it outright from the catalog.\n </DialogContentText>\n <Button\n variant=\"contained\"\n color=\"secondary\"\n disabled={busy}\n onClick={onDelete}\n >\n Delete Entity\n </Button>\n </>\n );\n }\n\n if (state.type === 'unregister') {\n return (\n <>\n <DialogContentText>\n This action will unregister the following entities:\n </DialogContentText>\n <DialogContentText component=\"ul\">\n {state.colocatedEntities.map(e => (\n <li key={`${e.kind}:${e.namespace}/${e.name}`}>\n <EntityRefLink entityRef={e} />\n </li>\n ))}\n </DialogContentText>\n <DialogContentText>\n Located at the following location:\n </DialogContentText>\n <DialogContentText component=\"ul\">\n <li>{state.location}</li>\n </DialogContentText>\n <DialogContentText>\n To undo, just re-register the entity in {appTitle}.\n </DialogContentText>\n <Box marginTop={2}>\n <Button\n variant=\"contained\"\n color=\"secondary\"\n disabled={busy}\n onClick={onUnregister}\n >\n Unregister Location\n </Button>\n {!showDelete && (\n <Box component=\"span\" marginLeft={2}>\n <Button\n variant=\"text\"\n size=\"small\"\n color=\"primary\"\n className={classes.advancedButton}\n onClick={() => setShowDelete(true)}\n >\n Advanced Options\n </Button>\n </Box>\n )}\n </Box>\n\n {showDelete && (\n <>\n <Box paddingTop={4} paddingBottom={4}>\n <Divider />\n </Box>\n <DialogContentText>\n You also have the option to delete the entity itself from the\n catalog. Note that this should only be done if you know that the\n catalog file has been deleted at, or moved from, its origin\n location. If that is not the case, the entity will reappear\n shortly as the next refresh round is performed by the catalog.\n </DialogContentText>\n <Button\n variant=\"contained\"\n color=\"secondary\"\n disabled={busy}\n onClick={onDelete}\n >\n Delete Entity\n </Button>\n </>\n )}\n </>\n );\n }\n\n return <Alert severity=\"error\">Internal error: Unknown state</Alert>;\n};\n\nexport const UnregisterEntityDialog = ({\n open,\n onConfirm,\n onClose,\n entity,\n}: Props) => (\n <Dialog open={open} onClose={onClose}>\n <DialogTitle id=\"responsive-dialog-title\">\n Are you sure you want to unregister this entity?\n </DialogTitle>\n <DialogContent>\n <Contents entity={entity} onConfirm={onConfirm} />\n </DialogContent>\n <DialogActions>\n <Button onClick={onClose} color=\"primary\">\n Cancel\n </Button>\n </DialogActions>\n </Dialog>\n);\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n configApiRef,\n IconComponent,\n useApi,\n} from '@backstage/core-plugin-api';\nimport {\n Card,\n List,\n ListItemIcon,\n ListItemSecondaryAction,\n ListItemText,\n makeStyles,\n MenuItem,\n Theme,\n Typography,\n} from '@material-ui/core';\nimport SettingsIcon from '@material-ui/icons/Settings';\nimport StarIcon from '@material-ui/icons/Star';\nimport { compact } from 'lodash';\nimport React, { Fragment, useEffect, useMemo, useState } from 'react';\nimport { UserListFilter } from '../../filters';\nimport {\n useEntityListProvider,\n useStarredEntities,\n useEntityOwnership,\n} from '../../hooks';\nimport { UserListFilterKind } from '../../types';\nimport { reduceEntityFilters } from '../../utils';\n\nconst useStyles = makeStyles<Theme>(theme => ({\n root: {\n backgroundColor: 'rgba(0, 0, 0, .11)',\n boxShadow: 'none',\n margin: theme.spacing(1, 0, 1, 0),\n },\n title: {\n margin: theme.spacing(1, 0, 0, 1),\n textTransform: 'uppercase',\n fontSize: 12,\n fontWeight: 'bold',\n },\n listIcon: {\n minWidth: 30,\n color: theme.palette.text.primary,\n },\n menuItem: {\n minHeight: theme.spacing(6),\n },\n groupWrapper: {\n margin: theme.spacing(1, 1, 2, 1),\n },\n}));\n\nexport type ButtonGroup = {\n name: string;\n items: {\n id: 'owned' | 'starred' | 'all';\n label: string;\n icon?: IconComponent;\n }[];\n};\n\nfunction getFilterGroups(orgName: string | undefined): ButtonGroup[] {\n return [\n {\n name: 'Personal',\n items: [\n {\n id: 'owned',\n label: 'Owned',\n icon: SettingsIcon,\n },\n {\n id: 'starred',\n label: 'Starred',\n icon: StarIcon,\n },\n ],\n },\n {\n name: orgName ?? 'Company',\n items: [\n {\n id: 'all',\n label: 'All',\n },\n ],\n },\n ];\n}\n\ntype UserListPickerProps = {\n initialFilter?: UserListFilterKind;\n availableFilters?: UserListFilterKind[];\n};\n\nexport const UserListPicker = ({\n initialFilter,\n availableFilters,\n}: UserListPickerProps) => {\n const classes = useStyles();\n const configApi = useApi(configApiRef);\n const orgName = configApi.getOptionalString('organization.name') ?? 'Company';\n\n // Remove group items that aren't in availableFilters and exclude\n // any now-empty groups.\n const filterGroups = getFilterGroups(orgName)\n .map(filterGroup => ({\n ...filterGroup,\n items: filterGroup.items.filter(\n ({ id }) => !availableFilters || availableFilters.includes(id),\n ),\n }))\n .filter(({ items }) => !!items.length);\n\n const { filters, updateFilters, backendEntities, queryParameters } =\n useEntityListProvider();\n\n const { isStarredEntity } = useStarredEntities();\n const { isOwnedEntity } = useEntityOwnership();\n const [selectedUserFilter, setSelectedUserFilter] = useState(\n [queryParameters.user].flat()[0] ?? initialFilter,\n );\n\n // Static filters; used for generating counts of potentially unselected kinds\n const ownedFilter = useMemo(\n () => new UserListFilter('owned', isOwnedEntity, isStarredEntity),\n [isOwnedEntity, isStarredEntity],\n );\n const starredFilter = useMemo(\n () => new UserListFilter('starred', isOwnedEntity, isStarredEntity),\n [isOwnedEntity, isStarredEntity],\n );\n\n useEffect(() => {\n updateFilters({\n user: selectedUserFilter\n ? new UserListFilter(\n selectedUserFilter as UserListFilterKind,\n isOwnedEntity,\n isStarredEntity,\n )\n : undefined,\n });\n }, [selectedUserFilter, isOwnedEntity, isStarredEntity, updateFilters]);\n\n // To show proper counts for each section, apply all other frontend filters _except_ the user\n // filter that's controlled by this picker.\n const [entitiesWithoutUserFilter, setEntitiesWithoutUserFilter] =\n useState(backendEntities);\n useEffect(() => {\n const filterFn = reduceEntityFilters(\n compact(Object.values({ ...filters, user: undefined })),\n );\n setEntitiesWithoutUserFilter(backendEntities.filter(filterFn));\n }, [filters, backendEntities]);\n\n function getFilterCount(id: UserListFilterKind) {\n switch (id) {\n case 'owned':\n return entitiesWithoutUserFilter.filter(entity =>\n ownedFilter.filterEntity(entity),\n ).length;\n case 'starred':\n return entitiesWithoutUserFilter.filter(entity =>\n starredFilter.filterEntity(entity),\n ).length;\n default:\n return entitiesWithoutUserFilter.length;\n }\n }\n\n return (\n <Card className={classes.root}>\n {filterGroups.map(group => (\n <Fragment key={group.name}>\n <Typography variant=\"subtitle2\" className={classes.title}>\n {group.name}\n </Typography>\n <Card className={classes.groupWrapper}>\n <List disablePadding dense>\n {group.items.map(item => (\n <MenuItem\n key={item.id}\n button\n divider\n onClick={() => setSelectedUserFilter(item.id)}\n selected={item.id === filters.user?.value}\n className={classes.menuItem}\n >\n {item.icon && (\n <ListItemIcon className={classes.listIcon}>\n <item.icon fontSize=\"small\" />\n </ListItemIcon>\n )}\n <ListItemText>\n <Typography\n variant=\"body1\"\n data-testid={`user-picker-${item.id}`}\n >\n {item.label}\n </Typography>\n </ListItemText>\n <ListItemSecondaryAction>\n {getFilterCount(item.id) ?? '-'}\n </ListItemSecondaryAction>\n </MenuItem>\n ))}\n </List>\n </Card>\n </Fragment>\n ))}\n </Card>\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React, { PropsWithChildren, useCallback, useState } from 'react';\nimport {\n DefaultEntityFilters,\n EntityListContext,\n EntityListContextProps,\n} from '../hooks/useEntityListProvider';\n\nexport const MockEntityListContextProvider = ({\n children,\n value,\n}: PropsWithChildren<{\n value?: Partial<EntityListContextProps>;\n}>) => {\n // Provides a default implementation that stores filter state, for testing components that\n // reflect filter state.\n const [filters, setFilters] = useState<DefaultEntityFilters>(\n value?.filters ?? {},\n );\n const updateFilters = useCallback(\n (\n update:\n | Partial<DefaultEntityFilters>\n | ((\n prevFilters: DefaultEntityFilters,\n ) => Partial<DefaultEntityFilters>),\n ) => {\n setFilters(prevFilters => {\n const newFilters =\n typeof update === 'function' ? update(prevFilters) : update;\n return { ...prevFilters, ...newFilters };\n });\n },\n [],\n );\n\n const defaultContext: EntityListContextProps = {\n entities: [],\n backendEntities: [],\n updateFilters,\n filters,\n loading: false,\n queryParameters: {},\n };\n\n // Extract value.filters to avoid overwriting it; some tests exercise filter updates. The value\n // provided is used as the initial seed in useState above.\n const { filters: _, ...otherContextFields } = value ?? {};\n\n return (\n <EntityListContext.Provider\n value={{ ...defaultContext, ...otherContextFields }}\n >\n {children}\n </EntityListContext.Provider>\n );\n};\n"],"names":["icon","checkedIcon","useStyles","Alert","StarIcon"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.esm.js","sources":["../src/api.ts","../src/routes.ts","../src/hooks/useEntityCompoundName.ts","../src/hooks/useEntity.tsx","../src/utils/filters.ts","../src/utils/getEntityMetadataUrl.ts","../src/utils/getEntityRelations.ts","../src/utils/getEntitySourceLocation.ts","../src/utils/isOwnerOf.ts","../src/hooks/useEntityListProvider.tsx","../src/components/EntityRefLink/format.ts","../src/components/EntityRefLink/EntityRefLink.tsx","../src/components/EntityRefLink/EntityRefLinks.tsx","../src/filters.ts","../src/hooks/useEntityTypeFilter.tsx","../src/hooks/useEntityKinds.ts","../src/hooks/useOwnUser.ts","../src/hooks/useRelatedEntities.ts","../src/hooks/useStarredEntities.ts","../src/hooks/useEntityOwnership.ts","../src/components/EntityKindPicker/EntityKindPicker.tsx","../src/components/EntityLifecyclePicker/EntityLifecyclePicker.tsx","../src/components/EntityOwnerPicker/EntityOwnerPicker.tsx","../src/components/EntitySearchBar/EntitySearchBar.tsx","../src/components/EntityTable/columns.tsx","../src/components/EntityTable/presets.tsx","../src/components/EntityTable/EntityTable.tsx","../src/components/EntityTagPicker/EntityTagPicker.tsx","../src/components/EntityTypePicker/EntityTypePicker.tsx","../src/components/FavoriteEntity/FavoriteEntity.tsx","../src/components/UnregisterEntityDialog/useUnregisterEntityDialogState.ts","../src/components/UnregisterEntityDialog/UnregisterEntityDialog.tsx","../src/components/UserListPicker/UserListPicker.tsx","../src/testUtils/providers.tsx"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { CatalogApi } from '@backstage/catalog-client';\nimport { createApiRef } from '@backstage/core-plugin-api';\n\nexport const catalogApiRef = createApiRef<CatalogApi>({\n id: 'plugin.catalog.service',\n});\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity, ENTITY_DEFAULT_NAMESPACE } from '@backstage/catalog-model';\nimport { createRouteRef } from '@backstage/core-plugin-api';\n\nconst NoIcon = () => null;\n\n// TODO(Rugvip): Move these route refs back to the catalog plugin once we're all ported to using external routes\nexport const rootRoute = createRouteRef({\n icon: NoIcon,\n path: '',\n title: 'Catalog',\n});\nexport const catalogRouteRef = rootRoute;\n\nexport const entityRoute = createRouteRef({\n icon: NoIcon,\n path: ':namespace/:kind/:name/*',\n title: 'Entity',\n params: ['namespace', 'kind', 'name'],\n});\nexport const entityRouteRef = entityRoute;\n\n// Utility function to get suitable route params for entityRoute, given an\n// entity instance\nexport function entityRouteParams(entity: Entity) {\n return {\n kind: entity.kind.toLowerCase(),\n namespace:\n entity.metadata.namespace?.toLowerCase() ?? ENTITY_DEFAULT_NAMESPACE,\n name: entity.metadata.name,\n } as const;\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { entityRouteRef } from '../routes';\nimport { useRouteRefParams } from '@backstage/core-plugin-api';\n\n/**\n * Grabs entity kind, namespace, and name from the location\n */\nexport const useEntityCompoundName = () => {\n const { kind, namespace, name } = useRouteRefParams(entityRouteRef);\n return { kind, namespace, name };\n};\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { Entity } from '@backstage/catalog-model';\nimport { errorApiRef, useApi } from '@backstage/core-plugin-api';\nimport {\n createVersionedContext,\n createVersionedValueMap,\n useVersionedContext,\n} from '@backstage/version-bridge';\nimport React, {\n ReactNode,\n useEffect,\n createContext,\n Provider,\n Context,\n} from 'react';\nimport { useNavigate } from 'react-router';\nimport { useAsyncRetry } from 'react-use';\nimport { catalogApiRef } from '../api';\nimport { useEntityCompoundName } from './useEntityCompoundName';\n\ntype EntityLoadingStatus = {\n entity?: Entity;\n loading: boolean;\n error?: Error;\n refresh?: VoidFunction;\n};\n\n/**\n * @public\n * @deprecated use `useEntity` and `EntityProvider` or `AsyncEntityProvider` instead.\n */\nexport const EntityContext: Context<EntityLoadingStatus> =\n createContext<EntityLoadingStatus>({\n entity: undefined,\n loading: true,\n error: undefined,\n refresh: () => {},\n });\n// We grab this for use in the new provider, since we're overriding it later on\nconst OldEntityProvider = EntityContext.Provider;\n\n// This context has support for multiple concurrent versions of this package.\n// It is currently used in parallel with the old context in order to provide\n// a smooth transition, but will eventually be the only context we use.\nconst NewEntityContext =\n createVersionedContext<{ 1: EntityLoadingStatus }>('entity-context');\n\n/**\n * Properties for the AsyncEntityProvider component.\n *\n * @public\n */\nexport interface AsyncEntityProviderProps {\n children: ReactNode;\n entity?: Entity;\n loading: boolean;\n error?: Error;\n refresh?: VoidFunction;\n}\n\n/**\n * Provides a loaded entity to be picked up by the `useEntity` hook.\n *\n * @public\n */\nexport const AsyncEntityProvider = ({\n children,\n entity,\n loading,\n error,\n refresh,\n}: AsyncEntityProviderProps) => {\n const value = { entity, loading, error, refresh };\n // We provide both the old and the new context, since\n // consumers might be doing things like `useContext(EntityContext)`\n return (\n <OldEntityProvider value={value}>\n <NewEntityContext.Provider value={createVersionedValueMap({ 1: value })}>\n {children}\n </NewEntityContext.Provider>\n </OldEntityProvider>\n );\n};\n\n/**\n * Properties for the EntityProvider component.\n *\n * @public\n */\nexport interface EntityProviderProps {\n children: ReactNode;\n entity?: Entity;\n}\n\n/**\n * Provides an entity to be picked up by the `useEntity` hook.\n *\n * @public\n */\nexport const EntityProvider = ({ entity, children }: EntityProviderProps) => (\n <AsyncEntityProvider\n entity={entity}\n loading={!Boolean(entity)}\n error={undefined}\n refresh={undefined}\n children={children}\n />\n);\n\n// This is used for forwards compatibility with the new entity context\nconst CompatibilityProvider = ({\n value,\n children,\n}: {\n value: EntityLoadingStatus;\n children: ReactNode;\n}) => {\n return <AsyncEntityProvider {...value} children={children} />;\n};\nEntityContext.Provider = CompatibilityProvider as Provider<EntityLoadingStatus>;\n\nexport const useEntityFromUrl = (): EntityLoadingStatus => {\n const { kind, namespace, name } = useEntityCompoundName();\n const navigate = useNavigate();\n const errorApi = useApi(errorApiRef);\n const catalogApi = useApi(catalogApiRef);\n\n const {\n value: entity,\n error,\n loading,\n retry: refresh,\n } = useAsyncRetry(\n () => catalogApi.getEntityByName({ kind, namespace, name }),\n [catalogApi, kind, namespace, name],\n );\n\n useEffect(() => {\n if (!name) {\n errorApi.post(new Error('No name provided!'));\n navigate('/');\n }\n }, [errorApi, navigate, error, loading, entity, name]);\n\n return { entity, loading, error, refresh };\n};\n\n/**\n * Grab the current entity from the context and its current loading state.\n *\n * @public\n */\nexport function useEntity<T extends Entity = Entity>() {\n const versionedHolder =\n useVersionedContext<{ 1: EntityLoadingStatus }>('entity-context');\n\n if (!versionedHolder) {\n // TODO(Rugvip): Throw this once we fully migrate to the new context\n // throw new Error('Entity context is not available');\n\n return {\n entity: undefined as unknown as T,\n loading: true,\n error: undefined,\n refresh: () => {},\n };\n }\n\n const value = versionedHolder.atVersion(1);\n if (!value) {\n throw new Error('EntityContext v1 not available');\n }\n\n const { entity, loading, error, refresh } = value;\n return { entity: entity as T, loading, error, refresh };\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity } from '@backstage/catalog-model';\nimport { EntityFilter } from '../types';\n\nexport function reduceCatalogFilters(\n filters: EntityFilter[],\n): Record<string, string | symbol | (string | symbol)[]> {\n return filters.reduce((compoundFilter, filter) => {\n return {\n ...compoundFilter,\n ...(filter.getCatalogFilters ? filter.getCatalogFilters() : {}),\n };\n }, {} as Record<string, string | symbol | (string | symbol)[]>);\n}\n\nexport function reduceEntityFilters(\n filters: EntityFilter[],\n): (entity: Entity) => boolean {\n return (entity: Entity) =>\n filters.every(\n filter => !filter.filterEntity || filter.filterEntity(entity),\n );\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n EDIT_URL_ANNOTATION,\n Entity,\n VIEW_URL_ANNOTATION,\n} from '@backstage/catalog-model';\n\nexport function getEntityMetadataViewUrl(entity: Entity): string | undefined {\n return entity.metadata.annotations?.[VIEW_URL_ANNOTATION];\n}\n\nexport function getEntityMetadataEditUrl(entity: Entity): string | undefined {\n return entity.metadata.annotations?.[EDIT_URL_ANNOTATION];\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity, EntityName } from '@backstage/catalog-model';\n\n/**\n * Get the related entity references.\n */\nexport function getEntityRelations(\n entity: Entity | undefined,\n relationType: string,\n filter?: { kind: string },\n): EntityName[] {\n let entityNames =\n entity?.relations\n ?.filter(r => r.type === relationType)\n ?.map(r => r.target) || [];\n\n if (filter?.kind) {\n entityNames = entityNames?.filter(\n e => e.kind.toLowerCase() === filter.kind.toLowerCase(),\n );\n }\n\n return entityNames;\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n Entity,\n parseLocationReference,\n SOURCE_LOCATION_ANNOTATION,\n} from '@backstage/catalog-model';\nimport { ScmIntegrationRegistry } from '@backstage/integration';\n\nexport type EntitySourceLocation = {\n locationTargetUrl: string;\n integrationType?: string;\n};\n\nexport function getEntitySourceLocation(\n entity: Entity,\n scmIntegrationsApi: ScmIntegrationRegistry,\n): EntitySourceLocation | undefined {\n const sourceLocation =\n entity.metadata.annotations?.[SOURCE_LOCATION_ANNOTATION];\n\n if (!sourceLocation) {\n return undefined;\n }\n\n try {\n const sourceLocationRef = parseLocationReference(sourceLocation);\n const integration = scmIntegrationsApi.byUrl(sourceLocationRef.target);\n return {\n locationTargetUrl: sourceLocationRef.target,\n integrationType: integration?.type,\n };\n } catch {\n return undefined;\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n Entity,\n getEntityName,\n RELATION_MEMBER_OF,\n RELATION_OWNED_BY,\n stringifyEntityRef,\n} from '@backstage/catalog-model';\nimport { getEntityRelations } from './getEntityRelations';\n\n/**\n * Get the related entity references.\n */\nexport function isOwnerOf(owner: Entity, owned: Entity) {\n const possibleOwners = new Set(\n [\n ...getEntityRelations(owner, RELATION_MEMBER_OF, { kind: 'group' }),\n ...(owner ? [getEntityName(owner)] : []),\n ].map(stringifyEntityRef),\n );\n\n const owners = getEntityRelations(owned, RELATION_OWNED_BY).map(\n stringifyEntityRef,\n );\n\n for (const ownerItem of owners) {\n if (possibleOwners.has(ownerItem)) {\n return true;\n }\n }\n\n return false;\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity } from '@backstage/catalog-model';\nimport { compact, isEqual } from 'lodash';\nimport qs from 'qs';\nimport React, {\n createContext,\n PropsWithChildren,\n useCallback,\n useContext,\n useMemo,\n useState,\n} from 'react';\nimport { useAsyncFn, useDebounce, useMountedState } from 'react-use';\nimport { catalogApiRef } from '../api';\nimport {\n EntityKindFilter,\n EntityLifecycleFilter,\n EntityOwnerFilter,\n EntityTagFilter,\n EntityTextFilter,\n EntityTypeFilter,\n UserListFilter,\n} from '../filters';\nimport { EntityFilter } from '../types';\nimport { reduceCatalogFilters, reduceEntityFilters } from '../utils';\nimport { useApi } from '@backstage/core-plugin-api';\n\nexport type DefaultEntityFilters = {\n kind?: EntityKindFilter;\n type?: EntityTypeFilter;\n user?: UserListFilter;\n owners?: EntityOwnerFilter;\n lifecycles?: EntityLifecycleFilter;\n tags?: EntityTagFilter;\n text?: EntityTextFilter;\n};\n\nexport type EntityListContextProps<\n EntityFilters extends DefaultEntityFilters = DefaultEntityFilters,\n> = {\n /**\n * The currently registered filters, adhering to the shape of DefaultEntityFilters or an extension\n * of that default (to add custom filter types).\n */\n filters: EntityFilters;\n\n /**\n * The resolved list of catalog entities, after all filters are applied.\n */\n entities: Entity[];\n\n /**\n * The resolved list of catalog entities, after _only catalog-backend_ filters are applied.\n */\n backendEntities: Entity[];\n\n /**\n * Update one or more of the registered filters. Optional filters can be set to `undefined` to\n * reset the filter.\n */\n updateFilters: (\n filters:\n | Partial<EntityFilters>\n | ((prevFilters: EntityFilters) => Partial<EntityFilters>),\n ) => void;\n\n /**\n * Filter values from query parameters.\n */\n queryParameters: Partial<Record<keyof EntityFilters, string | string[]>>;\n\n loading: boolean;\n error?: Error;\n};\n\nexport const EntityListContext = createContext<\n EntityListContextProps<any> | undefined\n>(undefined);\n\ntype OutputState<EntityFilters extends DefaultEntityFilters> = {\n appliedFilters: EntityFilters;\n entities: Entity[];\n backendEntities: Entity[];\n queryParameters: Record<string, string | string[]>;\n};\n\nexport const EntityListProvider = <EntityFilters extends DefaultEntityFilters>({\n children,\n}: PropsWithChildren<{}>) => {\n const isMounted = useMountedState();\n const catalogApi = useApi(catalogApiRef);\n const [requestedFilters, setRequestedFilters] = useState<EntityFilters>(\n {} as EntityFilters,\n );\n const [outputState, setOutputState] = useState<OutputState<EntityFilters>>(\n () => {\n const query = qs.parse(window.location.search, {\n ignoreQueryPrefix: true,\n });\n return {\n appliedFilters: {} as EntityFilters,\n entities: [],\n backendEntities: [],\n queryParameters: (query.filters ?? {}) as Record<\n string,\n string | string[]\n >,\n };\n },\n );\n\n // The main async filter worker. Note that while it has a lot of dependencies\n // in terms of its implementation, the triggering only happens (debounced)\n // based on the requested filters changing.\n const [{ loading, error }, refresh] = useAsyncFn(\n async () => {\n const compacted = compact(Object.values(requestedFilters));\n const entityFilter = reduceEntityFilters(compacted);\n const backendFilter = reduceCatalogFilters(compacted);\n const previousBackendFilter = reduceCatalogFilters(\n compact(Object.values(outputState.appliedFilters)),\n );\n\n const queryParams = Object.keys(requestedFilters).reduce(\n (params, key) => {\n const filter: EntityFilter | undefined =\n requestedFilters[key as keyof EntityFilters];\n if (filter?.toQueryValue) {\n params[key] = filter.toQueryValue();\n }\n return params;\n },\n {} as Record<string, string | string[]>,\n );\n\n // TODO(mtlewis): currently entities will never be requested unless\n // there's at least one filter, we should allow an initial request\n // to happen with no filters.\n if (!isEqual(previousBackendFilter, backendFilter)) {\n // TODO(timbonicus): should limit fields here, but would need filter\n // fields + table columns\n const response = await catalogApi.getEntities({\n filter: backendFilter,\n });\n setOutputState({\n appliedFilters: requestedFilters,\n backendEntities: response.items,\n entities: response.items.filter(entityFilter),\n queryParameters: queryParams,\n });\n } else {\n setOutputState({\n appliedFilters: requestedFilters,\n backendEntities: outputState.backendEntities,\n entities: outputState.backendEntities.filter(entityFilter),\n queryParameters: queryParams,\n });\n }\n\n if (isMounted()) {\n const oldParams = qs.parse(window.location.search, {\n ignoreQueryPrefix: true,\n });\n const newParams = qs.stringify(\n { ...oldParams, filters: queryParams },\n { addQueryPrefix: true },\n );\n const newUrl = `${window.location.pathname}${newParams}`;\n // We use direct history manipulation since useSearchParams and\n // useNavigate in react-router-dom cause unnecessary extra rerenders.\n // Also make sure to replace the state rather than pushing, since we\n // don't want there to be back/forward slots for every single filter\n // change.\n window.history?.replaceState(null, document.title, newUrl);\n }\n },\n [catalogApi, requestedFilters, outputState],\n { loading: true },\n );\n\n // Slight debounce on the refresh, since (especially on page load) several\n // filters will be calling this in rapid succession.\n useDebounce(refresh, 10, [requestedFilters]);\n\n const updateFilters = useCallback(\n (\n update:\n | Partial<EntityFilter>\n | ((prevFilters: EntityFilters) => Partial<EntityFilters>),\n ) => {\n setRequestedFilters(prevFilters => {\n const newFilters =\n typeof update === 'function' ? update(prevFilters) : update;\n return { ...prevFilters, ...newFilters };\n });\n },\n [],\n );\n\n const value = useMemo(\n () => ({\n filters: outputState.appliedFilters,\n entities: outputState.entities,\n backendEntities: outputState.backendEntities,\n updateFilters,\n queryParameters: outputState.queryParameters,\n loading,\n error,\n }),\n [outputState, updateFilters, loading, error],\n );\n\n return (\n <EntityListContext.Provider value={value}>\n {children}\n </EntityListContext.Provider>\n );\n};\n\nexport function useEntityListProvider<\n EntityFilters extends DefaultEntityFilters = DefaultEntityFilters,\n>(): EntityListContextProps<EntityFilters> {\n const context = useContext(EntityListContext);\n if (!context)\n throw new Error(\n 'useEntityListProvider must be used within EntityListProvider',\n );\n return context;\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n Entity,\n EntityName,\n ENTITY_DEFAULT_NAMESPACE,\n serializeEntityRef,\n} from '@backstage/catalog-model';\n\nexport function formatEntityRefTitle(\n entityRef: Entity | EntityName,\n opts?: { defaultKind?: string },\n) {\n const defaultKind = opts?.defaultKind;\n let kind;\n let namespace;\n let name;\n\n if ('metadata' in entityRef) {\n kind = entityRef.kind;\n namespace = entityRef.metadata.namespace;\n name = entityRef.metadata.name;\n } else {\n kind = entityRef.kind;\n namespace = entityRef.namespace;\n name = entityRef.name;\n }\n\n if (namespace === ENTITY_DEFAULT_NAMESPACE) {\n namespace = undefined;\n }\n\n kind = kind.toLowerCase();\n\n return `${serializeEntityRef({\n kind: defaultKind && defaultKind.toLowerCase() === kind ? undefined : kind,\n name,\n namespace,\n })}`;\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {\n Entity,\n EntityName,\n ENTITY_DEFAULT_NAMESPACE,\n} from '@backstage/catalog-model';\nimport React, { forwardRef } from 'react';\nimport { generatePath } from 'react-router';\nimport { entityRoute } from '../../routes';\nimport { formatEntityRefTitle } from './format';\nimport { Link, LinkProps } from '@backstage/core-components';\n\nexport type EntityRefLinkProps = {\n entityRef: Entity | EntityName;\n defaultKind?: string;\n children?: React.ReactNode;\n} & Omit<LinkProps, 'to'>;\n\nexport const EntityRefLink = forwardRef<any, EntityRefLinkProps>(\n (props, ref) => {\n const { entityRef, defaultKind, children, ...linkProps } = props;\n\n let kind;\n let namespace;\n let name;\n\n if ('metadata' in entityRef) {\n kind = entityRef.kind;\n namespace = entityRef.metadata.namespace;\n name = entityRef.metadata.name;\n } else {\n kind = entityRef.kind;\n namespace = entityRef.namespace;\n name = entityRef.name;\n }\n\n kind = kind.toLocaleLowerCase('en-US');\n\n const routeParams = {\n kind,\n namespace:\n namespace?.toLocaleLowerCase('en-US') ?? ENTITY_DEFAULT_NAMESPACE,\n name,\n };\n\n // TODO: Use useRouteRef here to generate the path\n return (\n <Link\n {...linkProps}\n ref={ref}\n to={generatePath(`/catalog/${entityRoute.path}`, routeParams)}\n >\n {children}\n {!children && formatEntityRefTitle(entityRef, { defaultKind })}\n </Link>\n );\n },\n);\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { Entity, EntityName } from '@backstage/catalog-model';\nimport React from 'react';\nimport { EntityRefLink } from './EntityRefLink';\nimport { LinkProps } from '@backstage/core-components';\n\nexport type EntityRefLinksProps = {\n entityRefs: (Entity | EntityName)[];\n defaultKind?: string;\n} & Omit<LinkProps, 'to'>;\n\nexport const EntityRefLinks = ({\n entityRefs,\n defaultKind,\n ...linkProps\n}: EntityRefLinksProps) => (\n <>\n {entityRefs.map((r, i) => (\n <React.Fragment key={i}>\n {i > 0 && ', '}\n <EntityRefLink {...linkProps} entityRef={r} defaultKind={defaultKind} />\n </React.Fragment>\n ))}\n </>\n);\n","/*\n * Copyright 2021 Spotify AB\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity, RELATION_OWNED_BY } from '@backstage/catalog-model';\nimport { formatEntityRefTitle } from './components/EntityRefLink';\nimport { EntityFilter, UserListFilterKind } from './types';\nimport { getEntityRelations } from './utils';\n\nexport class EntityKindFilter implements EntityFilter {\n constructor(readonly value: string) {}\n\n getCatalogFilters(): Record<string, string | string[]> {\n return { kind: this.value };\n }\n\n toQueryValue(): string {\n return this.value;\n }\n}\n\nexport class EntityTypeFilter implements EntityFilter {\n constructor(readonly value: string | string[]) {}\n\n // Simplify `string | string[]` for consumers, always returns an array\n getTypes(): string[] {\n return Array.isArray(this.value) ? this.value : [this.value];\n }\n\n getCatalogFilters(): Record<string, string | string[]> {\n return { 'spec.type': this.getTypes() };\n }\n\n toQueryValue(): string[] {\n return this.getTypes();\n }\n}\n\nexport class EntityTagFilter implements EntityFilter {\n constructor(readonly values: string[]) {}\n\n filterEntity(entity: Entity): boolean {\n return this.values.every(v => (entity.metadata.tags ?? []).includes(v));\n }\n\n toQueryValue(): string[] {\n return this.values;\n }\n}\n\nexport class EntityTextFilter implements EntityFilter {\n constructor(readonly value: string) {}\n\n filterEntity(entity: Entity): boolean {\n const upperCaseValue = this.value.toLocaleUpperCase('en-US');\n\n return (\n entity.metadata.name\n .toLocaleUpperCase('en-US')\n .includes(upperCaseValue) ||\n `${entity.metadata.title}`\n .toLocaleUpperCase('en-US')\n .includes(upperCaseValue) ||\n entity.metadata.tags\n ?.join('')\n .toLocaleUpperCase('en-US')\n .indexOf(upperCaseValue) !== -1\n );\n }\n}\n\nexport class EntityOwnerFilter implements EntityFilter {\n constructor(readonly values: string[]) {}\n\n filterEntity(entity: Entity): boolean {\n return this.values.some(v =>\n getEntityRelations(entity, RELATION_OWNED_BY).some(\n o => formatEntityRefTitle(o, { defaultKind: 'group' }) === v,\n ),\n );\n }\n\n toQueryValue(): string[] {\n return this.values;\n }\n}\n\nexport class EntityLifecycleFilter implements EntityFilter {\n constructor(readonly values: string[]) {}\n\n filterEntity(entity: Entity): boolean {\n return this.values.some(v => entity.spec?.lifecycle === v);\n }\n\n toQueryValue(): string[] {\n return this.values;\n }\n}\n\nexport class UserListFilter implements EntityFilter {\n constructor(\n readonly value: UserListFilterKind,\n readonly isOwnedEntity: (entity: Entity) => boolean,\n readonly isStarredEntity: (entity: Entity) => boolean,\n ) {}\n\n filterEntity(entity: Entity): boolean {\n switch (this.value) {\n case 'owned':\n return this.isOwnedEntity(entity);\n case 'starred':\n return this.isStarredEntity(entity);\n default:\n return true;\n }\n }\n\n toQueryValue(): string {\n return this.value;\n }\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { useEffect, useMemo, useRef, useState } from 'react';\nimport { useAsync } from 'react-use';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { catalogApiRef } from '../api';\nimport { useEntityListProvider } from './useEntityListProvider';\nimport { EntityTypeFilter } from '../filters';\n\ntype EntityTypeReturn = {\n loading: boolean;\n error?: Error;\n availableTypes: string[];\n selectedTypes: string[];\n setSelectedTypes: (types: string[]) => void;\n};\n\n/**\n * A hook built on top of `useEntityListProvider` for enabling selection of valid `spec.type` values\n * based on the selected EntityKindFilter.\n */\nexport function useEntityTypeFilter(): EntityTypeReturn {\n const catalogApi = useApi(catalogApiRef);\n const {\n filters: { kind: kindFilter, type: typeFilter },\n queryParameters,\n updateFilters,\n } = useEntityListProvider();\n\n const queryParamTypes = [queryParameters.type]\n .flat()\n .filter(Boolean) as string[];\n const [selectedTypes, setSelectedTypes] = useState(\n queryParamTypes.length ? queryParamTypes : typeFilter?.getTypes() ?? [],\n );\n\n const [availableTypes, setAvailableTypes] = useState<string[]>([]);\n const kind = useMemo(() => kindFilter?.value, [kindFilter]);\n\n // Load all valid spec.type values straight from the catalogApi, paying attention to only the\n // kind filter for a complete list.\n const {\n error,\n loading,\n value: entities,\n } = useAsync(async () => {\n if (kind) {\n const items = await catalogApi\n .getEntities({\n filter: { kind },\n fields: ['spec.type'],\n })\n .then(response => response.items);\n return items;\n }\n return [];\n }, [kind, catalogApi]);\n\n const entitiesRef = useRef(entities);\n useEffect(() => {\n const oldEntities = entitiesRef.current;\n entitiesRef.current = entities;\n // Delay processing hook until kind and entity load updates have settled to generate list of types;\n // This prevents reseting the type filter due to saved type value from query params not matching the\n // empty set of type values while values are still being loaded; also only run this hook on changes\n // to entities\n if (loading || !kind || oldEntities === entities) {\n return;\n }\n\n // Resolve the unique set of types from returned entities; could be optimized by a new endpoint\n // in the catalog-backend that does this, rather than loading entities with redundant types.\n if (!entities) return;\n\n // Sort by entity count descending, so the most common types appear on top\n const countByType = entities.reduce((acc, entity) => {\n if (typeof entity.spec?.type !== 'string') return acc;\n\n const entityType = entity.spec.type.toLocaleLowerCase('en-US');\n if (!acc[entityType]) {\n acc[entityType] = 0;\n }\n acc[entityType] += 1;\n return acc;\n }, {} as Record<string, number>);\n\n const newTypes = Object.entries(countByType)\n .sort(([, count1], [, count2]) => count2 - count1)\n .map(([type]) => type);\n setAvailableTypes(newTypes);\n\n // Update type filter to only valid values when the list of available types has changed\n const stillValidTypes = selectedTypes.filter(value =>\n newTypes.includes(value),\n );\n setSelectedTypes(stillValidTypes);\n }, [loading, kind, selectedTypes, setSelectedTypes, entities]);\n\n useEffect(() => {\n updateFilters({\n type: selectedTypes.length\n ? new EntityTypeFilter(selectedTypes)\n : undefined,\n });\n }, [selectedTypes, updateFilters]);\n\n return {\n loading,\n error,\n availableTypes,\n selectedTypes,\n setSelectedTypes,\n };\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { useAsync } from 'react-use';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { catalogApiRef } from '../api';\n\n// Retrieve a list of unique entity kinds present in the catalog\nexport function useEntityKinds() {\n const catalogApi = useApi(catalogApiRef);\n\n const {\n error,\n loading,\n value: kinds,\n } = useAsync(async () => {\n const entities = await catalogApi\n .getEntities({ fields: ['kind'] })\n .then(response => response.items);\n\n return [...new Set(entities.map(e => e.kind))].sort();\n });\n return { error, loading, kinds };\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { UserEntity } from '@backstage/catalog-model';\nimport { useAsync } from 'react-use';\nimport { AsyncState } from 'react-use/lib/useAsync';\nimport { catalogApiRef } from '../api';\nimport { identityApiRef, useApi } from '@backstage/core-plugin-api';\n\n/**\n * Get the catalog User entity (if any) that matches the logged-in user.\n */\nexport function useOwnUser(): AsyncState<UserEntity | undefined> {\n const catalogApi = useApi(catalogApiRef);\n const identityApi = useApi(identityApiRef);\n\n // TODO: get the full entity (or at least the full entity name) from the\n // identityApi\n return useAsync(\n () =>\n catalogApi.getEntityByName({\n kind: 'User',\n namespace: 'default',\n name: identityApi.getUserId(),\n }) as Promise<UserEntity | undefined>,\n [catalogApi, identityApi],\n );\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { Entity, EntityRelation } from '@backstage/catalog-model';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { chunk, groupBy } from 'lodash';\nimport { useAsync } from 'react-use';\nimport { catalogApiRef } from '../api';\n\nconst BATCH_SIZE = 20;\n\nexport function useRelatedEntities(\n entity: Entity,\n { type, kind }: { type?: string; kind?: string },\n): {\n entities: Entity[] | undefined;\n loading: boolean;\n error: Error | undefined;\n} {\n const catalogApi = useApi(catalogApiRef);\n const {\n loading,\n value: entities,\n error,\n } = useAsync(async () => {\n const relations =\n entity.relations &&\n entity.relations.filter(\n r =>\n (!type || r.type.toLowerCase() === type.toLowerCase()) &&\n (!kind || r.target.kind.toLowerCase() === kind.toLowerCase()),\n );\n\n if (!relations) {\n return [];\n }\n\n // Group the relations by kind and namespace to reduce the size of the request query string.\n // Without this grouping, the kind and namespace would need to be specified for each relation, e.g.\n // `filter=kind=component,namespace=default,name=example1&filter=kind=component,namespace=default,name=example2`\n // with grouping, we can generate a query a string like\n // `filter=kind=component,namespace=default,name=example1,example2`\n const relationsByKindAndNamespace: EntityRelation[][] = Object.values(\n groupBy(relations, ({ target }) => {\n return `${target.kind}:${target.namespace}`.toLowerCase();\n }),\n );\n\n // Split the names within each group into batches to further reduce the query string length.\n const batchedRelationsByKindAndNamespace: {\n kind: string;\n namespace: string;\n nameBatches: string[][];\n }[] = [];\n for (const rs of relationsByKindAndNamespace) {\n batchedRelationsByKindAndNamespace.push({\n // All relations in a group have the same kind and namespace, so its arbitrary which we pick\n kind: rs[0].target.kind,\n namespace: rs[0].target.namespace,\n nameBatches: chunk(\n rs.map(r => r.target.name),\n BATCH_SIZE,\n ),\n });\n }\n\n const results = await Promise.all(\n batchedRelationsByKindAndNamespace.flatMap(rs => {\n return rs.nameBatches.map(names => {\n return catalogApi.getEntities({\n filter: {\n kind: rs.kind,\n 'metadata.namespace': rs.namespace,\n 'metadata.name': names,\n },\n });\n });\n }),\n );\n\n return results.flatMap(r => r.items);\n }, [entity, type]);\n\n return {\n entities,\n loading,\n error,\n };\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity } from '@backstage/catalog-model';\nimport { storageApiRef, useApi } from '@backstage/core-plugin-api';\nimport { useCallback, useEffect, useState } from 'react';\nimport { useObservable } from 'react-use';\n\nconst buildEntityKey = (component: Entity) =>\n `entity:${component.kind}:${component.metadata.namespace ?? 'default'}:${\n component.metadata.name\n }`;\n\nexport const useStarredEntities = () => {\n const storageApi = useApi(storageApiRef);\n const settingsStore = storageApi.forBucket('settings');\n const rawStarredEntityKeys =\n settingsStore.get<string[]>('starredEntities') ?? [];\n\n const [starredEntities, setStarredEntities] = useState(\n new Set(rawStarredEntityKeys),\n );\n\n const observedItems = useObservable(\n settingsStore.observe$<string[]>('starredEntities'),\n );\n\n useEffect(() => {\n if (observedItems?.newValue) {\n const currentValue = observedItems?.newValue ?? [];\n setStarredEntities(new Set(currentValue));\n }\n }, [observedItems?.newValue]);\n\n const toggleStarredEntity = useCallback(\n (entity: Entity) => {\n const entityKey = buildEntityKey(entity);\n if (starredEntities.has(entityKey)) {\n starredEntities.delete(entityKey);\n } else {\n starredEntities.add(entityKey);\n }\n\n settingsStore.set('starredEntities', Array.from(starredEntities));\n },\n [starredEntities, settingsStore],\n );\n\n const isStarredEntity = useCallback(\n (entity: Entity) => {\n const entityKey = buildEntityKey(entity);\n return starredEntities.has(entityKey);\n },\n [starredEntities],\n );\n\n return {\n starredEntities,\n toggleStarredEntity,\n isStarredEntity,\n };\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { CatalogApi } from '@backstage/catalog-client';\nimport {\n Entity,\n EntityName,\n parseEntityRef,\n RELATION_MEMBER_OF,\n RELATION_OWNED_BY,\n stringifyEntityRef,\n} from '@backstage/catalog-model';\nimport {\n IdentityApi,\n identityApiRef,\n useApi,\n} from '@backstage/core-plugin-api';\nimport jwtDecoder from 'jwt-decode';\nimport { useMemo } from 'react';\nimport { useAsync } from 'react-use';\nimport { catalogApiRef } from '../api';\nimport { getEntityRelations } from '../utils/getEntityRelations';\n\n// Takes a user ID from the identity, which can be on basically any form, and\n// returns an entity ref. E.g. if the input is \"foo\", it returns\n// \"user:default/foo\" to make sure it's a full ref.\nfunction extendUserId(id: string): string {\n try {\n const ref = parseEntityRef(id, {\n defaultKind: 'User',\n defaultNamespace: 'default',\n });\n return stringifyEntityRef(ref);\n } catch {\n return id;\n }\n}\n\n// Takes the relevant parts of the Backstage identity, and translates them into\n// a list of entity refs on string form that represent the user's ownership\n// connections.\nexport async function loadIdentityOwnerRefs(\n identityApi: IdentityApi,\n): Promise<string[]> {\n const id = identityApi.getUserId();\n const token = await identityApi.getIdToken();\n const result: string[] = [];\n\n if (id) {\n result.push(extendUserId(id));\n }\n\n if (token) {\n try {\n const decoded = jwtDecoder(token) as any;\n if (decoded?.ent) {\n [decoded.ent]\n .flat()\n .filter(x => typeof x === 'string')\n .map(x => x.toLocaleLowerCase('en-US'))\n .forEach(x => result.push(x));\n }\n } catch {\n // ignore\n }\n }\n\n return result;\n}\n\n// Takes the relevant parts of the User entity corresponding to the Backstage\n// identity, and translates them into a list of entity refs on string form that\n// represent the user's ownership connections.\nexport async function loadCatalogOwnerRefs(\n catalogApi: CatalogApi,\n identityOwnerRefs: string[],\n): Promise<string[]> {\n const result = new Array<string>();\n\n const primaryUserRef = identityOwnerRefs.find(ref => ref.startsWith('user:'));\n if (primaryUserRef) {\n const entity = await catalogApi.getEntityByName(\n parseEntityRef(primaryUserRef),\n );\n if (entity) {\n const memberOf = getEntityRelations(entity, RELATION_MEMBER_OF, {\n kind: 'Group',\n });\n for (const group of memberOf) {\n result.push(stringifyEntityRef(group));\n }\n }\n }\n\n return result;\n}\n\n/**\n * Returns a function that checks whether the currently signed-in user is an\n * owner of a given entity. When the hook is initially mounted, the loading\n * flag will be true and the results returned from the function will always be\n * false.\n */\nexport function useEntityOwnership(): {\n loading: boolean;\n isOwnedEntity: (entity: Entity | EntityName) => boolean;\n} {\n const identityApi = useApi(identityApiRef);\n const catalogApi = useApi(catalogApiRef);\n\n // Trigger load only on mount\n const { loading, value: refs } = useAsync(async () => {\n const identityRefs = await loadIdentityOwnerRefs(identityApi);\n const catalogRefs = await loadCatalogOwnerRefs(catalogApi, identityRefs);\n return new Set([...identityRefs, ...catalogRefs]);\n }, []);\n\n const isOwnedEntity = useMemo(() => {\n const myOwnerRefs = new Set(refs ?? []);\n return (entity: Entity | EntityName) => {\n const entityOwnerRefs = (\n 'metadata' in entity\n ? getEntityRelations(entity, RELATION_OWNED_BY)\n : [entity]\n ).map(stringifyEntityRef);\n for (const ref of entityOwnerRefs) {\n if (myOwnerRefs.has(ref)) {\n return true;\n }\n }\n return false;\n };\n }, [refs]);\n\n return useMemo(() => ({ loading, isOwnedEntity }), [loading, isOwnedEntity]);\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React, { useEffect, useState } from 'react';\nimport { Alert } from '@material-ui/lab';\nimport { useEntityListProvider } from '../../hooks';\nimport { EntityKindFilter } from '../../filters';\n\ntype EntityKindFilterProps = {\n initialFilter?: string;\n hidden: boolean;\n};\n\nexport const EntityKindPicker = ({\n initialFilter,\n hidden,\n}: EntityKindFilterProps) => {\n const { updateFilters, queryParameters } = useEntityListProvider();\n const [selectedKind] = useState(\n [queryParameters.kind].flat()[0] ?? initialFilter,\n );\n\n useEffect(() => {\n updateFilters({\n kind: selectedKind ? new EntityKindFilter(selectedKind) : undefined,\n });\n }, [selectedKind, updateFilters]);\n\n if (hidden) return null;\n\n // TODO(timbonicus): This should load available kinds from the catalog-backend, similar to\n // EntityTypePicker.\n\n return <Alert severity=\"warning\">Kind filter not yet available</Alert>;\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity } from '@backstage/catalog-model';\nimport {\n Box,\n Checkbox,\n FormControlLabel,\n TextField,\n Typography,\n} from '@material-ui/core';\nimport CheckBoxIcon from '@material-ui/icons/CheckBox';\nimport CheckBoxOutlineBlankIcon from '@material-ui/icons/CheckBoxOutlineBlank';\nimport ExpandMoreIcon from '@material-ui/icons/ExpandMore';\nimport { Autocomplete } from '@material-ui/lab';\nimport React, { useEffect, useMemo, useState } from 'react';\nimport { useEntityListProvider } from '../../hooks/useEntityListProvider';\nimport { EntityLifecycleFilter } from '../../filters';\n\nconst icon = <CheckBoxOutlineBlankIcon fontSize=\"small\" />;\nconst checkedIcon = <CheckBoxIcon fontSize=\"small\" />;\n\nexport const EntityLifecyclePicker = () => {\n const { updateFilters, backendEntities, filters, queryParameters } =\n useEntityListProvider();\n\n const queryParamLifecycles = [queryParameters.lifecycles]\n .flat()\n .filter(Boolean) as string[];\n const [selectedLifecycles, setSelectedLifecycles] = useState(\n queryParamLifecycles.length\n ? queryParamLifecycles\n : filters.lifecycles?.values ?? [],\n );\n\n useEffect(() => {\n updateFilters({\n lifecycles: selectedLifecycles.length\n ? new EntityLifecycleFilter(selectedLifecycles)\n : undefined,\n });\n }, [selectedLifecycles, updateFilters]);\n\n const availableLifecycles = useMemo(\n () =>\n [\n ...new Set(\n backendEntities\n .map((e: Entity) => e.spec?.lifecycle)\n .filter(Boolean) as string[],\n ),\n ].sort(),\n [backendEntities],\n );\n\n if (!availableLifecycles.length) return null;\n\n return (\n <Box pb={1} pt={1}>\n <Typography variant=\"button\">Lifecycle</Typography>\n <Autocomplete\n aria-label=\"Lifecycle\"\n multiple\n options={availableLifecycles}\n value={selectedLifecycles}\n onChange={(_: object, value: string[]) => setSelectedLifecycles(value)}\n renderOption={(option, { selected }) => (\n <FormControlLabel\n control={\n <Checkbox\n icon={icon}\n checkedIcon={checkedIcon}\n checked={selected}\n />\n }\n label={option}\n />\n )}\n size=\"small\"\n popupIcon={<ExpandMoreIcon data-testid=\"lifecycle-picker-expand\" />}\n renderInput={params => <TextField {...params} variant=\"outlined\" />}\n />\n </Box>\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity, RELATION_OWNED_BY } from '@backstage/catalog-model';\nimport {\n Box,\n Checkbox,\n FormControlLabel,\n TextField,\n Typography,\n} from '@material-ui/core';\nimport CheckBoxIcon from '@material-ui/icons/CheckBox';\nimport CheckBoxOutlineBlankIcon from '@material-ui/icons/CheckBoxOutlineBlank';\nimport ExpandMoreIcon from '@material-ui/icons/ExpandMore';\nimport { Autocomplete } from '@material-ui/lab';\nimport React, { useEffect, useMemo, useState } from 'react';\nimport { useEntityListProvider } from '../../hooks/useEntityListProvider';\nimport { EntityOwnerFilter } from '../../filters';\nimport { getEntityRelations } from '../../utils';\nimport { formatEntityRefTitle } from '../EntityRefLink';\n\nconst icon = <CheckBoxOutlineBlankIcon fontSize=\"small\" />;\nconst checkedIcon = <CheckBoxIcon fontSize=\"small\" />;\n\nexport const EntityOwnerPicker = () => {\n const { updateFilters, backendEntities, filters, queryParameters } =\n useEntityListProvider();\n\n const queryParamOwners = [queryParameters.owners]\n .flat()\n .filter(Boolean) as string[];\n const [selectedOwners, setSelectedOwners] = useState(\n queryParamOwners.length ? queryParamOwners : filters.owners?.values ?? [],\n );\n\n useEffect(() => {\n updateFilters({\n owners: selectedOwners.length\n ? new EntityOwnerFilter(selectedOwners)\n : undefined,\n });\n }, [selectedOwners, updateFilters]);\n\n const availableOwners = useMemo(\n () =>\n [\n ...new Set(\n backendEntities\n .flatMap((e: Entity) =>\n getEntityRelations(e, RELATION_OWNED_BY).map(o =>\n formatEntityRefTitle(o, { defaultKind: 'group' }),\n ),\n )\n .filter(Boolean) as string[],\n ),\n ].sort(),\n [backendEntities],\n );\n\n if (!availableOwners.length) return null;\n\n return (\n <Box pb={1} pt={1}>\n <Typography variant=\"button\">Owner</Typography>\n <Autocomplete\n multiple\n aria-label=\"Owner\"\n options={availableOwners}\n value={selectedOwners}\n onChange={(_: object, value: string[]) => setSelectedOwners(value)}\n renderOption={(option, { selected }) => (\n <FormControlLabel\n control={\n <Checkbox\n icon={icon}\n checkedIcon={checkedIcon}\n checked={selected}\n />\n }\n label={option}\n />\n )}\n size=\"small\"\n popupIcon={<ExpandMoreIcon data-testid=\"owner-picker-expand\" />}\n renderInput={params => <TextField {...params} variant=\"outlined\" />}\n />\n </Box>\n );\n};\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n FormControl,\n IconButton,\n Input,\n InputAdornment,\n makeStyles,\n Toolbar,\n} from '@material-ui/core';\nimport Clear from '@material-ui/icons/Clear';\nimport Search from '@material-ui/icons/Search';\nimport React, { useState } from 'react';\nimport { useDebounce } from 'react-use';\nimport { useEntityListProvider } from '../../hooks/useEntityListProvider';\nimport { EntityTextFilter } from '../../filters';\n\nconst useStyles = makeStyles(_theme => ({\n searchToolbar: {\n paddingLeft: 0,\n paddingRight: 0,\n },\n}));\n\nexport const EntitySearchBar = () => {\n const styles = useStyles();\n\n const { filters, updateFilters } = useEntityListProvider();\n const [search, setSearch] = useState(filters.text?.value ?? '');\n\n useDebounce(\n () => {\n updateFilters({\n text: search.length ? new EntityTextFilter(search) : undefined,\n });\n },\n 250,\n [search, updateFilters],\n );\n\n return (\n <Toolbar className={styles.searchToolbar}>\n <FormControl>\n <Input\n id=\"input-with-icon-adornment\"\n placeholder=\"Search\"\n autoComplete=\"off\"\n onChange={event => setSearch(event.target.value)}\n value={search}\n startAdornment={\n <InputAdornment position=\"start\">\n <Search />\n </InputAdornment>\n }\n endAdornment={\n <InputAdornment position=\"end\">\n <IconButton\n aria-label=\"clear search\"\n onClick={() => setSearch('')}\n edge=\"end\"\n disabled={search.length === 0}\n >\n <Clear />\n </IconButton>\n </InputAdornment>\n }\n />\n </FormControl>\n </Toolbar>\n );\n};\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n Entity,\n EntityName,\n RELATION_OWNED_BY,\n RELATION_PART_OF,\n} from '@backstage/catalog-model';\nimport React from 'react';\nimport { getEntityRelations } from '../../utils';\nimport {\n EntityRefLink,\n EntityRefLinks,\n formatEntityRefTitle,\n} from '../EntityRefLink';\nimport { OverflowTooltip, TableColumn } from '@backstage/core-components';\n\nexport function createEntityRefColumn<T extends Entity>({\n defaultKind,\n}: {\n defaultKind?: string;\n}): TableColumn<T> {\n function formatContent(entity: T): string {\n return formatEntityRefTitle(entity, {\n defaultKind,\n });\n }\n\n return {\n title: 'Name',\n highlight: true,\n customFilterAndSearch(filter, entity) {\n // TODO: We could implement this more efficiently, like searching over\n // each field that is displayed individually (kind, namespace, name).\n // but that migth confuse the user as it will behave different than a\n // simple text search.\n // Another alternative would be to cache the values. But writing them\n // into the entity feels bad too.\n return formatContent(entity).includes(filter);\n },\n customSort(entity1, entity2) {\n // TODO: We could implement this more efficiently by comparing field by field.\n // This has similar issues as above.\n return formatContent(entity1).localeCompare(formatContent(entity2));\n },\n render: entity => (\n <EntityRefLink entityRef={entity} defaultKind={defaultKind} />\n ),\n };\n}\n\nexport function createEntityRelationColumn<T extends Entity>({\n title,\n relation,\n defaultKind,\n filter: entityFilter,\n}: {\n title: string;\n relation: string;\n defaultKind?: string;\n filter?: { kind: string };\n}): TableColumn<T> {\n function getRelations(entity: T): EntityName[] {\n return getEntityRelations(entity, relation, entityFilter);\n }\n\n function formatContent(entity: T): string {\n return getRelations(entity)\n .map(r => formatEntityRefTitle(r, { defaultKind }))\n .join(', ');\n }\n\n return {\n title,\n customFilterAndSearch(filter, entity) {\n return formatContent(entity).includes(filter);\n },\n customSort(entity1, entity2) {\n return formatContent(entity1).localeCompare(formatContent(entity2));\n },\n render: entity => {\n return (\n <EntityRefLinks\n entityRefs={getRelations(entity)}\n defaultKind={defaultKind}\n />\n );\n },\n };\n}\n\nexport function createOwnerColumn<T extends Entity>(): TableColumn<T> {\n return createEntityRelationColumn({\n title: 'Owner',\n relation: RELATION_OWNED_BY,\n defaultKind: 'group',\n });\n}\n\nexport function createDomainColumn<T extends Entity>(): TableColumn<T> {\n return createEntityRelationColumn({\n title: 'Domain',\n relation: RELATION_PART_OF,\n defaultKind: 'domain',\n filter: {\n kind: 'domain',\n },\n });\n}\n\nexport function createSystemColumn<T extends Entity>(): TableColumn<T> {\n return createEntityRelationColumn({\n title: 'System',\n relation: RELATION_PART_OF,\n defaultKind: 'system',\n filter: {\n kind: 'system',\n },\n });\n}\n\nexport function createMetadataDescriptionColumn<\n T extends Entity,\n>(): TableColumn<T> {\n return {\n title: 'Description',\n field: 'metadata.description',\n render: entity => (\n <OverflowTooltip\n text={entity.metadata.description}\n placement=\"bottom-start\"\n line={2}\n />\n ),\n width: 'auto',\n };\n}\n\nexport function createSpecLifecycleColumn<T extends Entity>(): TableColumn<T> {\n return {\n title: 'Lifecycle',\n field: 'spec.lifecycle',\n };\n}\n\nexport function createSpecTypeColumn<T extends Entity>(): TableColumn<T> {\n return {\n title: 'Type',\n field: 'spec.type',\n };\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { ComponentEntity, SystemEntity } from '@backstage/catalog-model';\nimport {\n createDomainColumn,\n createEntityRefColumn,\n createMetadataDescriptionColumn,\n createOwnerColumn,\n createSpecLifecycleColumn,\n createSpecTypeColumn,\n createSystemColumn,\n} from './columns';\nimport { TableColumn } from '@backstage/core-components';\n\nexport const systemEntityColumns: TableColumn<SystemEntity>[] = [\n createEntityRefColumn({ defaultKind: 'system' }),\n createDomainColumn(),\n createOwnerColumn(),\n createMetadataDescriptionColumn(),\n];\n\nexport const componentEntityColumns: TableColumn<ComponentEntity>[] = [\n createEntityRefColumn({ defaultKind: 'component' }),\n createSystemColumn(),\n createOwnerColumn(),\n createSpecTypeColumn(),\n createSpecLifecycleColumn(),\n createMetadataDescriptionColumn(),\n];\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity } from '@backstage/catalog-model';\nimport { makeStyles } from '@material-ui/core';\nimport React, { ReactNode } from 'react';\nimport * as columnFactories from './columns';\nimport { componentEntityColumns, systemEntityColumns } from './presets';\nimport { Table, TableColumn } from '@backstage/core-components';\n\ntype Props<T extends Entity> = {\n title: string;\n variant?: 'gridItem';\n entities: T[];\n emptyContent?: ReactNode;\n columns: TableColumn<T>[];\n};\n\nconst useStyles = makeStyles(theme => ({\n empty: {\n padding: theme.spacing(2),\n display: 'flex',\n justifyContent: 'center',\n },\n}));\n\nexport function EntityTable<T extends Entity>({\n entities,\n title,\n emptyContent,\n variant = 'gridItem',\n columns,\n}: Props<T>) {\n const classes = useStyles();\n const tableStyle: React.CSSProperties = {\n minWidth: '0',\n width: '100%',\n };\n\n if (variant === 'gridItem') {\n tableStyle.height = 'calc(100% - 10px)';\n }\n\n return (\n <Table<T>\n columns={columns}\n title={title}\n style={tableStyle}\n emptyContent={\n emptyContent && <div className={classes.empty}>{emptyContent}</div>\n }\n options={{\n // TODO: Toolbar padding if off compared to other cards, should be: padding: 16px 24px;\n search: false,\n paging: false,\n actionsColumnIndex: -1,\n padding: 'dense',\n }}\n data={entities}\n />\n );\n}\n\nEntityTable.columns = columnFactories;\n\nEntityTable.systemEntityColumns = systemEntityColumns;\n\nEntityTable.componentEntityColumns = componentEntityColumns;\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity } from '@backstage/catalog-model';\nimport {\n Box,\n Checkbox,\n FormControlLabel,\n TextField,\n Typography,\n} from '@material-ui/core';\nimport CheckBoxIcon from '@material-ui/icons/CheckBox';\nimport CheckBoxOutlineBlankIcon from '@material-ui/icons/CheckBoxOutlineBlank';\nimport ExpandMoreIcon from '@material-ui/icons/ExpandMore';\nimport { Autocomplete } from '@material-ui/lab';\nimport React, { useEffect, useMemo, useState } from 'react';\nimport { useEntityListProvider } from '../../hooks/useEntityListProvider';\nimport { EntityTagFilter } from '../../filters';\n\nconst icon = <CheckBoxOutlineBlankIcon fontSize=\"small\" />;\nconst checkedIcon = <CheckBoxIcon fontSize=\"small\" />;\n\nexport const EntityTagPicker = () => {\n const { updateFilters, backendEntities, filters, queryParameters } =\n useEntityListProvider();\n\n const queryParamTags = [queryParameters.tags]\n .flat()\n .filter(Boolean) as string[];\n const [selectedTags, setSelectedTags] = useState(\n queryParamTags.length ? queryParamTags : filters.tags?.values ?? [],\n );\n\n useEffect(() => {\n updateFilters({\n tags: selectedTags.length ? new EntityTagFilter(selectedTags) : undefined,\n });\n }, [selectedTags, updateFilters]);\n\n const availableTags = useMemo(\n () =>\n [\n ...new Set(\n backendEntities\n .flatMap((e: Entity) => e.metadata.tags)\n .filter(Boolean) as string[],\n ),\n ].sort(),\n [backendEntities],\n );\n\n if (!availableTags.length) return null;\n\n return (\n <Box pb={1} pt={1}>\n <Typography variant=\"button\">Tags</Typography>\n <Autocomplete\n multiple\n aria-label=\"Tags\"\n options={availableTags}\n value={selectedTags}\n onChange={(_: object, value: string[]) => setSelectedTags(value)}\n renderOption={(option, { selected }) => (\n <FormControlLabel\n control={\n <Checkbox\n icon={icon}\n checkedIcon={checkedIcon}\n checked={selected}\n />\n }\n label={option}\n />\n )}\n size=\"small\"\n popupIcon={<ExpandMoreIcon data-testid=\"tag-picker-expand\" />}\n renderInput={params => <TextField {...params} variant=\"outlined\" />}\n />\n </Box>\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React, { useEffect } from 'react';\nimport capitalize from 'lodash/capitalize';\nimport { Box } from '@material-ui/core';\nimport { useEntityTypeFilter } from '../../hooks/useEntityTypeFilter';\n\nimport { alertApiRef, useApi } from '@backstage/core-plugin-api';\nimport { Select } from '@backstage/core-components';\n\nexport const EntityTypePicker = () => {\n const alertApi = useApi(alertApiRef);\n const { error, availableTypes, selectedTypes, setSelectedTypes } =\n useEntityTypeFilter();\n\n useEffect(() => {\n if (error) {\n alertApi.post({\n message: `Failed to load entity types`,\n severity: 'error',\n });\n }\n }, [error, alertApi]);\n\n if (availableTypes.length === 0 || error) return null;\n\n const items = [\n { value: 'all', label: 'All' },\n ...availableTypes.map((type: string) => ({\n value: type,\n label: capitalize(type),\n })),\n ];\n\n return (\n <Box pb={1} pt={1}>\n <Select\n label=\"Type\"\n items={items}\n selected={(items.length > 1 ? selectedTypes[0] : undefined) ?? 'all'}\n onChange={value =>\n setSelectedTypes(value === 'all' ? [] : [String(value)])\n }\n />\n </Box>\n );\n};\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React, { ComponentProps } from 'react';\nimport { useStarredEntities } from '../../hooks/useStarredEntities';\nimport { IconButton, Tooltip, withStyles } from '@material-ui/core';\nimport StarBorder from '@material-ui/icons/StarBorder';\nimport Star from '@material-ui/icons/Star';\nimport { Entity } from '@backstage/catalog-model';\n\ntype Props = ComponentProps<typeof IconButton> & { entity: Entity };\n\nconst YellowStar = withStyles({\n root: {\n color: '#f3ba37',\n },\n})(Star);\n\nexport const favoriteEntityTooltip = (isStarred: boolean) =>\n isStarred ? 'Remove from favorites' : 'Add to favorites';\n\nexport const favoriteEntityIcon = (isStarred: boolean) =>\n isStarred ? <YellowStar /> : <StarBorder />;\n\n/**\n * IconButton for showing if a current entity is starred and adding/removing it from the favorite entities\n * @param props MaterialUI IconButton props extended by required `entity` prop\n */\nexport const FavoriteEntity = (props: Props) => {\n const { toggleStarredEntity, isStarredEntity } = useStarredEntities();\n const isStarred = isStarredEntity(props.entity);\n return (\n <IconButton\n color=\"inherit\"\n {...props}\n onClick={() => toggleStarredEntity(props.entity)}\n >\n <Tooltip title={favoriteEntityTooltip(isStarred)}>\n {favoriteEntityIcon(isStarred)}\n </Tooltip>\n </IconButton>\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n Entity,\n EntityName,\n getEntityName,\n ORIGIN_LOCATION_ANNOTATION,\n} from '@backstage/catalog-model';\nimport { catalogApiRef } from '../../api';\nimport { useCallback } from 'react';\nimport { useAsync } from 'react-use';\nimport { useApi } from '@backstage/core-plugin-api';\n\n/**\n * Each distinct state that the dialog can be in at any given time.\n */\nexport type UseUnregisterEntityDialogState =\n | {\n type: 'loading';\n }\n | {\n type: 'error';\n error: Error;\n }\n | {\n type: 'bootstrap';\n location: string;\n deleteEntity: () => Promise<void>;\n }\n | {\n type: 'unregister';\n location: string;\n colocatedEntities: EntityName[];\n unregisterLocation: () => Promise<void>;\n deleteEntity: () => Promise<void>;\n }\n | {\n type: 'only-delete';\n deleteEntity: () => Promise<void>;\n };\n\n/**\n * Houses the main logic for unregistering entities and their locations.\n */\nexport function useUnregisterEntityDialogState(\n entity: Entity,\n): UseUnregisterEntityDialogState {\n const catalogApi = useApi(catalogApiRef);\n const locationRef = entity.metadata.annotations?.[ORIGIN_LOCATION_ANNOTATION];\n const uid = entity.metadata.uid;\n const isBootstrap = locationRef === 'bootstrap:bootstrap';\n\n // Load the prerequisite data: what entities that are colocated with us, and\n // what location that spawned us\n const prerequisites = useAsync(async () => {\n const locationPromise = catalogApi.getOriginLocationByEntity(entity);\n\n let colocatedEntitiesPromise: Promise<Entity[]>;\n if (!locationRef) {\n colocatedEntitiesPromise = Promise.resolve([]);\n } else {\n const locationAnnotationFilter = `metadata.annotations.${ORIGIN_LOCATION_ANNOTATION}`;\n colocatedEntitiesPromise = catalogApi\n .getEntities({\n filter: { [locationAnnotationFilter]: locationRef },\n fields: [\n 'kind',\n 'metadata.uid',\n 'metadata.name',\n 'metadata.namespace',\n ],\n })\n .then(response => response.items);\n }\n\n return Promise.all([locationPromise, colocatedEntitiesPromise]).then(\n ([location, colocatedEntities]) => ({\n location,\n colocatedEntities,\n }),\n );\n }, [catalogApi, entity]);\n\n // Unregisters the underlying location and removes all of the entities that\n // are spawned from it. Can only ever be called when the prerequisites have\n // finished loading successfully, and if there was a matching location.\n const unregisterLocation = useCallback(\n async function unregisterLocationFn() {\n const { location, colocatedEntities } = prerequisites.value!;\n await catalogApi.removeLocationById(location!.id);\n await Promise.allSettled(\n colocatedEntities.map(e =>\n catalogApi.removeEntityByUid(e.metadata.uid!),\n ),\n );\n },\n [catalogApi, prerequisites],\n );\n\n // Just removes the entity, without affecting locations in any way.\n const deleteEntity = useCallback(\n async function deleteEntityFn() {\n await catalogApi.removeEntityByUid(uid!);\n },\n [catalogApi, uid],\n );\n\n // If this is a bootstrap location entity, don't even block on loading\n // prerequisites. We know that all that we will do is to offer to remove the\n // entity, and that doesn't require anything from the prerequisites.\n if (isBootstrap) {\n return { type: 'bootstrap', location: locationRef!, deleteEntity };\n }\n\n // Return early if prerequisites still loading or failing\n const { loading, error, value } = prerequisites;\n if (loading) {\n return { type: 'loading' };\n } else if (error) {\n return { type: 'error', error };\n }\n\n const { location, colocatedEntities } = value!;\n if (!location) {\n return { type: 'only-delete', deleteEntity };\n }\n return {\n type: 'unregister',\n location: locationRef!,\n colocatedEntities: colocatedEntities.map(getEntityName),\n unregisterLocation,\n deleteEntity,\n };\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity } from '@backstage/catalog-model';\nimport { EntityRefLink } from '../EntityRefLink';\nimport {\n Box,\n Button,\n Dialog,\n DialogActions,\n DialogContent,\n DialogContentText,\n DialogTitle,\n Divider,\n makeStyles,\n} from '@material-ui/core';\nimport Alert from '@material-ui/lab/Alert';\nimport React, { useCallback, useState } from 'react';\nimport { useUnregisterEntityDialogState } from './useUnregisterEntityDialogState';\n\nimport { alertApiRef, configApiRef, useApi } from '@backstage/core-plugin-api';\nimport { Progress, ResponseErrorPanel } from '@backstage/core-components';\n\nconst useStyles = makeStyles({\n advancedButton: {\n fontSize: '0.7em',\n },\n});\n\ntype Props = {\n open: boolean;\n onConfirm: () => any;\n onClose: () => any;\n entity: Entity;\n};\n\nconst Contents = ({\n entity,\n onConfirm,\n}: {\n entity: Entity;\n onConfirm: () => any;\n}) => {\n const alertApi = useApi(alertApiRef);\n const configApi = useApi(configApiRef);\n const classes = useStyles();\n const state = useUnregisterEntityDialogState(entity);\n const [showDelete, setShowDelete] = useState(false);\n const [busy, setBusy] = useState(false);\n const appTitle = configApi.getOptionalString('app.title') ?? 'Backstage';\n\n const onUnregister = useCallback(\n async function onUnregisterFn() {\n if ('unregisterLocation' in state) {\n setBusy(true);\n try {\n await state.unregisterLocation();\n onConfirm();\n } catch (err) {\n alertApi.post({ message: err.message });\n } finally {\n setBusy(false);\n }\n }\n },\n [alertApi, onConfirm, state],\n );\n\n const onDelete = useCallback(\n async function onDeleteFn() {\n if ('deleteEntity' in state) {\n setBusy(true);\n try {\n await state.deleteEntity();\n onConfirm();\n } catch (err) {\n alertApi.post({ message: err.message });\n } finally {\n setBusy(false);\n }\n }\n },\n [alertApi, onConfirm, state],\n );\n\n if (state.type === 'loading') {\n return <Progress />;\n }\n\n if (state.type === 'error') {\n return <ResponseErrorPanel error={state.error} />;\n }\n\n if (state.type === 'bootstrap') {\n return (\n <>\n <Alert severity=\"info\">\n You cannot unregister this entity, since it originates from a\n protected Backstage configuration (location \"{state.location}\"). If\n you believe this is in error, please contact the {appTitle}{' '}\n integrator.\n </Alert>\n\n <Box marginTop={2}>\n {!showDelete && (\n <Button\n variant=\"text\"\n size=\"small\"\n color=\"primary\"\n className={classes.advancedButton}\n onClick={() => setShowDelete(true)}\n >\n Advanced Options\n </Button>\n )}\n\n {showDelete && (\n <>\n <DialogContentText>\n You have the option to delete the entity itself from the\n catalog. Note that this should only be done if you know that the\n catalog file has been deleted at, or moved from, its origin\n location. If that is not the case, the entity will reappear\n shortly as the next refresh round is performed by the catalog.\n </DialogContentText>\n <Button\n variant=\"contained\"\n color=\"secondary\"\n disabled={busy}\n onClick={onDelete}\n >\n Delete Entity\n </Button>\n </>\n )}\n </Box>\n </>\n );\n }\n\n if (state.type === 'only-delete') {\n return (\n <>\n <DialogContentText>\n This entity does not seem to originate from a registered location. You\n therefore only have the option to delete it outright from the catalog.\n </DialogContentText>\n <Button\n variant=\"contained\"\n color=\"secondary\"\n disabled={busy}\n onClick={onDelete}\n >\n Delete Entity\n </Button>\n </>\n );\n }\n\n if (state.type === 'unregister') {\n return (\n <>\n <DialogContentText>\n This action will unregister the following entities:\n </DialogContentText>\n <DialogContentText component=\"ul\">\n {state.colocatedEntities.map(e => (\n <li key={`${e.kind}:${e.namespace}/${e.name}`}>\n <EntityRefLink entityRef={e} />\n </li>\n ))}\n </DialogContentText>\n <DialogContentText>\n Located at the following location:\n </DialogContentText>\n <DialogContentText component=\"ul\">\n <li>{state.location}</li>\n </DialogContentText>\n <DialogContentText>\n To undo, just re-register the entity in {appTitle}.\n </DialogContentText>\n <Box marginTop={2}>\n <Button\n variant=\"contained\"\n color=\"secondary\"\n disabled={busy}\n onClick={onUnregister}\n >\n Unregister Location\n </Button>\n {!showDelete && (\n <Box component=\"span\" marginLeft={2}>\n <Button\n variant=\"text\"\n size=\"small\"\n color=\"primary\"\n className={classes.advancedButton}\n onClick={() => setShowDelete(true)}\n >\n Advanced Options\n </Button>\n </Box>\n )}\n </Box>\n\n {showDelete && (\n <>\n <Box paddingTop={4} paddingBottom={4}>\n <Divider />\n </Box>\n <DialogContentText>\n You also have the option to delete the entity itself from the\n catalog. Note that this should only be done if you know that the\n catalog file has been deleted at, or moved from, its origin\n location. If that is not the case, the entity will reappear\n shortly as the next refresh round is performed by the catalog.\n </DialogContentText>\n <Button\n variant=\"contained\"\n color=\"secondary\"\n disabled={busy}\n onClick={onDelete}\n >\n Delete Entity\n </Button>\n </>\n )}\n </>\n );\n }\n\n return <Alert severity=\"error\">Internal error: Unknown state</Alert>;\n};\n\nexport const UnregisterEntityDialog = ({\n open,\n onConfirm,\n onClose,\n entity,\n}: Props) => (\n <Dialog open={open} onClose={onClose}>\n <DialogTitle id=\"responsive-dialog-title\">\n Are you sure you want to unregister this entity?\n </DialogTitle>\n <DialogContent>\n <Contents entity={entity} onConfirm={onConfirm} />\n </DialogContent>\n <DialogActions>\n <Button onClick={onClose} color=\"primary\">\n Cancel\n </Button>\n </DialogActions>\n </Dialog>\n);\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n configApiRef,\n IconComponent,\n useApi,\n} from '@backstage/core-plugin-api';\nimport {\n Card,\n List,\n ListItemIcon,\n ListItemSecondaryAction,\n ListItemText,\n makeStyles,\n MenuItem,\n Theme,\n Typography,\n} from '@material-ui/core';\nimport SettingsIcon from '@material-ui/icons/Settings';\nimport StarIcon from '@material-ui/icons/Star';\nimport { compact } from 'lodash';\nimport React, { Fragment, useEffect, useMemo, useState } from 'react';\nimport { UserListFilter } from '../../filters';\nimport {\n useEntityListProvider,\n useStarredEntities,\n useEntityOwnership,\n} from '../../hooks';\nimport { UserListFilterKind } from '../../types';\nimport { reduceEntityFilters } from '../../utils';\n\nconst useStyles = makeStyles<Theme>(theme => ({\n root: {\n backgroundColor: 'rgba(0, 0, 0, .11)',\n boxShadow: 'none',\n margin: theme.spacing(1, 0, 1, 0),\n },\n title: {\n margin: theme.spacing(1, 0, 0, 1),\n textTransform: 'uppercase',\n fontSize: 12,\n fontWeight: 'bold',\n },\n listIcon: {\n minWidth: 30,\n color: theme.palette.text.primary,\n },\n menuItem: {\n minHeight: theme.spacing(6),\n },\n groupWrapper: {\n margin: theme.spacing(1, 1, 2, 1),\n },\n}));\n\nexport type ButtonGroup = {\n name: string;\n items: {\n id: 'owned' | 'starred' | 'all';\n label: string;\n icon?: IconComponent;\n }[];\n};\n\nfunction getFilterGroups(orgName: string | undefined): ButtonGroup[] {\n return [\n {\n name: 'Personal',\n items: [\n {\n id: 'owned',\n label: 'Owned',\n icon: SettingsIcon,\n },\n {\n id: 'starred',\n label: 'Starred',\n icon: StarIcon,\n },\n ],\n },\n {\n name: orgName ?? 'Company',\n items: [\n {\n id: 'all',\n label: 'All',\n },\n ],\n },\n ];\n}\n\ntype UserListPickerProps = {\n initialFilter?: UserListFilterKind;\n availableFilters?: UserListFilterKind[];\n};\n\nexport const UserListPicker = ({\n initialFilter,\n availableFilters,\n}: UserListPickerProps) => {\n const classes = useStyles();\n const configApi = useApi(configApiRef);\n const orgName = configApi.getOptionalString('organization.name') ?? 'Company';\n\n // Remove group items that aren't in availableFilters and exclude\n // any now-empty groups.\n const filterGroups = getFilterGroups(orgName)\n .map(filterGroup => ({\n ...filterGroup,\n items: filterGroup.items.filter(\n ({ id }) => !availableFilters || availableFilters.includes(id),\n ),\n }))\n .filter(({ items }) => !!items.length);\n\n const { filters, updateFilters, backendEntities, queryParameters } =\n useEntityListProvider();\n\n const { isStarredEntity } = useStarredEntities();\n const { isOwnedEntity } = useEntityOwnership();\n const [selectedUserFilter, setSelectedUserFilter] = useState(\n [queryParameters.user].flat()[0] ?? initialFilter,\n );\n\n // Static filters; used for generating counts of potentially unselected kinds\n const ownedFilter = useMemo(\n () => new UserListFilter('owned', isOwnedEntity, isStarredEntity),\n [isOwnedEntity, isStarredEntity],\n );\n const starredFilter = useMemo(\n () => new UserListFilter('starred', isOwnedEntity, isStarredEntity),\n [isOwnedEntity, isStarredEntity],\n );\n\n useEffect(() => {\n updateFilters({\n user: selectedUserFilter\n ? new UserListFilter(\n selectedUserFilter as UserListFilterKind,\n isOwnedEntity,\n isStarredEntity,\n )\n : undefined,\n });\n }, [selectedUserFilter, isOwnedEntity, isStarredEntity, updateFilters]);\n\n // To show proper counts for each section, apply all other frontend filters _except_ the user\n // filter that's controlled by this picker.\n const [entitiesWithoutUserFilter, setEntitiesWithoutUserFilter] =\n useState(backendEntities);\n useEffect(() => {\n const filterFn = reduceEntityFilters(\n compact(Object.values({ ...filters, user: undefined })),\n );\n setEntitiesWithoutUserFilter(backendEntities.filter(filterFn));\n }, [filters, backendEntities]);\n\n function getFilterCount(id: UserListFilterKind) {\n switch (id) {\n case 'owned':\n return entitiesWithoutUserFilter.filter(entity =>\n ownedFilter.filterEntity(entity),\n ).length;\n case 'starred':\n return entitiesWithoutUserFilter.filter(entity =>\n starredFilter.filterEntity(entity),\n ).length;\n default:\n return entitiesWithoutUserFilter.length;\n }\n }\n\n return (\n <Card className={classes.root}>\n {filterGroups.map(group => (\n <Fragment key={group.name}>\n <Typography variant=\"subtitle2\" className={classes.title}>\n {group.name}\n </Typography>\n <Card className={classes.groupWrapper}>\n <List disablePadding dense>\n {group.items.map(item => (\n <MenuItem\n key={item.id}\n button\n divider\n onClick={() => setSelectedUserFilter(item.id)}\n selected={item.id === filters.user?.value}\n className={classes.menuItem}\n >\n {item.icon && (\n <ListItemIcon className={classes.listIcon}>\n <item.icon fontSize=\"small\" />\n </ListItemIcon>\n )}\n <ListItemText>\n <Typography\n variant=\"body1\"\n data-testid={`user-picker-${item.id}`}\n >\n {item.label}\n </Typography>\n </ListItemText>\n <ListItemSecondaryAction>\n {getFilterCount(item.id) ?? '-'}\n </ListItemSecondaryAction>\n </MenuItem>\n ))}\n </List>\n </Card>\n </Fragment>\n ))}\n </Card>\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React, { PropsWithChildren, useCallback, useState } from 'react';\nimport {\n DefaultEntityFilters,\n EntityListContext,\n EntityListContextProps,\n} from '../hooks/useEntityListProvider';\n\nexport const MockEntityListContextProvider = ({\n children,\n value,\n}: PropsWithChildren<{\n value?: Partial<EntityListContextProps>;\n}>) => {\n // Provides a default implementation that stores filter state, for testing components that\n // reflect filter state.\n const [filters, setFilters] = useState<DefaultEntityFilters>(\n value?.filters ?? {},\n );\n const updateFilters = useCallback(\n (\n update:\n | Partial<DefaultEntityFilters>\n | ((\n prevFilters: DefaultEntityFilters,\n ) => Partial<DefaultEntityFilters>),\n ) => {\n setFilters(prevFilters => {\n const newFilters =\n typeof update === 'function' ? update(prevFilters) : update;\n return { ...prevFilters, ...newFilters };\n });\n },\n [],\n );\n\n const defaultContext: EntityListContextProps = {\n entities: [],\n backendEntities: [],\n updateFilters,\n filters,\n loading: false,\n queryParameters: {},\n };\n\n // Extract value.filters to avoid overwriting it; some tests exercise filter updates. The value\n // provided is used as the initial seed in useState above.\n const { filters: _, ...otherContextFields } = value ?? {};\n\n return (\n <EntityListContext.Provider\n value={{ ...defaultContext, ...otherContextFields }}\n >\n {children}\n </EntityListContext.Provider>\n );\n};\n"],"names":["icon","checkedIcon","useStyles","Alert","StarIcon"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@backstage/plugin-catalog-react",
|
|
3
|
-
"
|
|
3
|
+
"description": "A frontend library that helps other Backstage plugins interact with the catalog",
|
|
4
|
+
"version": "0.0.0-nightly-202181722143",
|
|
4
5
|
"main": "dist/index.esm.js",
|
|
5
6
|
"types": "dist/index.d.ts",
|
|
6
7
|
"license": "Apache-2.0",
|
|
@@ -29,14 +30,15 @@
|
|
|
29
30
|
},
|
|
30
31
|
"dependencies": {
|
|
31
32
|
"@backstage/catalog-client": "^0.3.18",
|
|
32
|
-
"@backstage/catalog-model": "^0.
|
|
33
|
-
"@backstage/core-app-api": "^0.
|
|
34
|
-
"@backstage/core-components": "^0.
|
|
35
|
-
"@backstage/core-plugin-api": "^0.1.
|
|
36
|
-
"@backstage/integration": "^0.6.
|
|
33
|
+
"@backstage/catalog-model": "^0.9.1",
|
|
34
|
+
"@backstage/core-app-api": "^0.1.13",
|
|
35
|
+
"@backstage/core-components": "^0.4.2",
|
|
36
|
+
"@backstage/core-plugin-api": "^0.1.8",
|
|
37
|
+
"@backstage/integration": "^0.6.4",
|
|
38
|
+
"@backstage/version-bridge": "^0.1.0",
|
|
37
39
|
"@material-ui/core": "^4.12.2",
|
|
38
40
|
"@material-ui/icons": "^4.9.1",
|
|
39
|
-
"@material-ui/lab": "4.0.0-alpha.
|
|
41
|
+
"@material-ui/lab": "4.0.0-alpha.57",
|
|
40
42
|
"@types/react": "*",
|
|
41
43
|
"jwt-decode": "^3.1.0",
|
|
42
44
|
"lodash": "^4.17.15",
|
|
@@ -47,8 +49,8 @@
|
|
|
47
49
|
"react-use": "^17.2.4"
|
|
48
50
|
},
|
|
49
51
|
"devDependencies": {
|
|
50
|
-
"@backstage/cli": "^0.
|
|
51
|
-
"@backstage/dev-utils": "^0.
|
|
52
|
+
"@backstage/cli": "^0.7.12",
|
|
53
|
+
"@backstage/dev-utils": "^0.2.8",
|
|
52
54
|
"@backstage/test-utils": "^0.1.17",
|
|
53
55
|
"@testing-library/jest-dom": "^5.10.1",
|
|
54
56
|
"@testing-library/react": "^11.2.5",
|