@backstage/plugin-catalog-react 1.19.2-next.0 → 1.20.0-next.2

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 CHANGED
@@ -1,5 +1,54 @@
1
1
  # @backstage/plugin-catalog-react
2
2
 
3
+ ## 1.20.0-next.2
4
+
5
+ ### Minor Changes
6
+
7
+ - e4ddf22: **BREAKING ALPHA**: The `defaultPath`, `defaultTitle`, and `defaultGroup` params of `PageBlueprint` has been renamed to `path`, `title`, and `group`. The `convertLegacyEntityContentExtension` utility has also received the same change. This change does not affect the compatibility of extensions created with older versions of this blueprint.
8
+
9
+ ### Patch Changes
10
+
11
+ - Updated dependencies
12
+ - @backstage/frontend-plugin-api@0.11.0-next.1
13
+ - @backstage/frontend-test-utils@0.3.5-next.2
14
+ - @backstage/core-compat-api@0.5.0-next.2
15
+ - @backstage/core-components@0.17.5-next.1
16
+ - @backstage/catalog-client@1.11.0-next.0
17
+ - @backstage/catalog-model@1.7.5
18
+ - @backstage/core-plugin-api@1.10.9
19
+ - @backstage/errors@1.2.7
20
+ - @backstage/integration-react@1.2.9
21
+ - @backstage/types@1.2.1
22
+ - @backstage/version-bridge@1.0.11
23
+ - @backstage/plugin-catalog-common@1.1.5
24
+ - @backstage/plugin-permission-common@0.9.1
25
+ - @backstage/plugin-permission-react@0.4.36
26
+
27
+ ## 1.20.0-next.1
28
+
29
+ ### Minor Changes
30
+
31
+ - 3f4da39: Added the `analyzeLocation` method to `catalogApiMock`
32
+
33
+ ### Patch Changes
34
+
35
+ - defc243: hide pagination `queryparams` if pagination mode is set to none
36
+ - Updated dependencies
37
+ - @backstage/core-compat-api@0.4.5-next.1
38
+ - @backstage/frontend-plugin-api@0.11.0-next.0
39
+ - @backstage/catalog-client@1.11.0-next.0
40
+ - @backstage/frontend-test-utils@0.3.5-next.1
41
+ - @backstage/core-components@0.17.5-next.0
42
+ - @backstage/catalog-model@1.7.5
43
+ - @backstage/core-plugin-api@1.10.9
44
+ - @backstage/errors@1.2.7
45
+ - @backstage/integration-react@1.2.9
46
+ - @backstage/types@1.2.1
47
+ - @backstage/version-bridge@1.0.11
48
+ - @backstage/plugin-catalog-common@1.1.5
49
+ - @backstage/plugin-permission-common@0.9.1
50
+ - @backstage/plugin-permission-react@0.4.36
51
+
3
52
  ## 1.19.2-next.0
4
53
 
5
54
  ### Patch Changes
@@ -29,24 +29,19 @@ const EntityContentBlueprint = createExtensionBlueprint({
29
29
  group: (z) => z.literal(false).or(z.string()).optional()
30
30
  }
31
31
  },
32
- *factory({
33
- loader,
34
- defaultPath,
35
- defaultTitle,
36
- defaultGroup,
37
- filter,
38
- routeRef
39
- }, { node, config }) {
40
- const path = config.path ?? defaultPath;
41
- const title = config.title ?? defaultTitle;
42
- const group = config.group ?? defaultGroup;
43
- yield coreExtensionData.reactElement(ExtensionBoundary.lazy(node, loader));
32
+ *factory(params, { node, config }) {
33
+ const path = config.path ?? params.path;
34
+ const title = config.title ?? params.title;
35
+ const group = config.group ?? params.group;
36
+ yield coreExtensionData.reactElement(
37
+ ExtensionBoundary.lazy(node, params.loader)
38
+ );
44
39
  yield coreExtensionData.routePath(path);
45
40
  yield entityContentTitleDataRef(title);
46
- if (routeRef) {
47
- yield coreExtensionData.routeRef(routeRef);
41
+ if (params.routeRef) {
42
+ yield coreExtensionData.routeRef(params.routeRef);
48
43
  }
49
- yield* resolveEntityFilterData(filter, config, node);
44
+ yield* resolveEntityFilterData(params.filter, config, node);
50
45
  if (group) {
51
46
  yield entityContentGroupDataRef(group);
52
47
  }
@@ -1 +1 @@
1
- {"version":3,"file":"EntityContentBlueprint.esm.js","sources":["../../../src/alpha/blueprints/EntityContentBlueprint.ts"],"sourcesContent":["/*\n * Copyright 2024 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 coreExtensionData,\n createExtensionBlueprint,\n ExtensionBoundary,\n RouteRef,\n} from '@backstage/frontend-plugin-api';\nimport {\n entityContentTitleDataRef,\n entityFilterFunctionDataRef,\n entityFilterExpressionDataRef,\n entityContentGroupDataRef,\n defaultEntityContentGroups,\n} from './extensionData';\nimport { EntityPredicate } from '../predicates/types';\nimport { resolveEntityFilterData } from './resolveEntityFilterData';\nimport { createEntityPredicateSchema } from '../predicates/createEntityPredicateSchema';\nimport { Entity } from '@backstage/catalog-model';\n\n/**\n * @alpha\n * Creates an EntityContent extension.\n */\nexport const EntityContentBlueprint = createExtensionBlueprint({\n kind: 'entity-content',\n attachTo: { id: 'page:catalog/entity', input: 'contents' },\n output: [\n coreExtensionData.reactElement,\n coreExtensionData.routePath,\n entityContentTitleDataRef,\n coreExtensionData.routeRef.optional(),\n entityFilterFunctionDataRef.optional(),\n entityFilterExpressionDataRef.optional(),\n entityContentGroupDataRef.optional(),\n ],\n dataRefs: {\n title: entityContentTitleDataRef,\n filterFunction: entityFilterFunctionDataRef,\n filterExpression: entityFilterExpressionDataRef,\n group: entityContentGroupDataRef,\n },\n config: {\n schema: {\n path: z => z.string().optional(),\n title: z => z.string().optional(),\n filter: z =>\n z.union([z.string(), createEntityPredicateSchema(z)]).optional(),\n group: z => z.literal(false).or(z.string()).optional(),\n },\n },\n *factory(\n {\n loader,\n defaultPath,\n defaultTitle,\n defaultGroup,\n filter,\n routeRef,\n }: {\n loader: () => Promise<JSX.Element>;\n defaultPath: string;\n defaultTitle: string;\n defaultGroup?: keyof typeof defaultEntityContentGroups | (string & {});\n routeRef?: RouteRef;\n filter?: string | EntityPredicate | ((entity: Entity) => boolean);\n },\n { node, config },\n ) {\n const path = config.path ?? defaultPath;\n const title = config.title ?? defaultTitle;\n const group = config.group ?? defaultGroup;\n\n yield coreExtensionData.reactElement(ExtensionBoundary.lazy(node, loader));\n\n yield coreExtensionData.routePath(path);\n\n yield entityContentTitleDataRef(title);\n\n if (routeRef) {\n yield coreExtensionData.routeRef(routeRef);\n }\n\n yield* resolveEntityFilterData(filter, config, node);\n\n if (group) {\n yield entityContentGroupDataRef(group);\n }\n },\n});\n"],"names":[],"mappings":";;;;;AAsCO,MAAM,yBAAyB,wBAAyB,CAAA;AAAA,EAC7D,IAAM,EAAA,gBAAA;AAAA,EACN,QAAU,EAAA,EAAE,EAAI,EAAA,qBAAA,EAAuB,OAAO,UAAW,EAAA;AAAA,EACzD,MAAQ,EAAA;AAAA,IACN,iBAAkB,CAAA,YAAA;AAAA,IAClB,iBAAkB,CAAA,SAAA;AAAA,IAClB,yBAAA;AAAA,IACA,iBAAA,CAAkB,SAAS,QAAS,EAAA;AAAA,IACpC,4BAA4B,QAAS,EAAA;AAAA,IACrC,8BAA8B,QAAS,EAAA;AAAA,IACvC,0BAA0B,QAAS;AAAA,GACrC;AAAA,EACA,QAAU,EAAA;AAAA,IACR,KAAO,EAAA,yBAAA;AAAA,IACP,cAAgB,EAAA,2BAAA;AAAA,IAChB,gBAAkB,EAAA,6BAAA;AAAA,IAClB,KAAO,EAAA;AAAA,GACT;AAAA,EACA,MAAQ,EAAA;AAAA,IACN,MAAQ,EAAA;AAAA,MACN,IAAM,EAAA,CAAA,CAAA,KAAK,CAAE,CAAA,MAAA,GAAS,QAAS,EAAA;AAAA,MAC/B,KAAO,EAAA,CAAA,CAAA,KAAK,CAAE,CAAA,MAAA,GAAS,QAAS,EAAA;AAAA,MAChC,MAAQ,EAAA,CAAA,CAAA,KACN,CAAE,CAAA,KAAA,CAAM,CAAC,CAAA,CAAE,MAAO,EAAA,EAAG,2BAA4B,CAAA,CAAC,CAAC,CAAC,EAAE,QAAS,EAAA;AAAA,MACjE,KAAA,EAAO,CAAK,CAAA,KAAA,CAAA,CAAE,OAAQ,CAAA,KAAK,CAAE,CAAA,EAAA,CAAG,CAAE,CAAA,MAAA,EAAQ,CAAA,CAAE,QAAS;AAAA;AACvD,GACF;AAAA,EACA,CAAC,OACC,CAAA;AAAA,IACE,MAAA;AAAA,IACA,WAAA;AAAA,IACA,YAAA;AAAA,IACA,YAAA;AAAA,IACA,MAAA;AAAA,IACA;AAAA,GASF,EAAA,EAAE,IAAM,EAAA,MAAA,EACR,EAAA;AACA,IAAM,MAAA,IAAA,GAAO,OAAO,IAAQ,IAAA,WAAA;AAC5B,IAAM,MAAA,KAAA,GAAQ,OAAO,KAAS,IAAA,YAAA;AAC9B,IAAM,MAAA,KAAA,GAAQ,OAAO,KAAS,IAAA,YAAA;AAE9B,IAAA,MAAM,kBAAkB,YAAa,CAAA,iBAAA,CAAkB,IAAK,CAAA,IAAA,EAAM,MAAM,CAAC,CAAA;AAEzE,IAAM,MAAA,iBAAA,CAAkB,UAAU,IAAI,CAAA;AAEtC,IAAA,MAAM,0BAA0B,KAAK,CAAA;AAErC,IAAA,IAAI,QAAU,EAAA;AACZ,MAAM,MAAA,iBAAA,CAAkB,SAAS,QAAQ,CAAA;AAAA;AAG3C,IAAO,OAAA,uBAAA,CAAwB,MAAQ,EAAA,MAAA,EAAQ,IAAI,CAAA;AAEnD,IAAA,IAAI,KAAO,EAAA;AACT,MAAA,MAAM,0BAA0B,KAAK,CAAA;AAAA;AACvC;AAEJ,CAAC;;;;"}
1
+ {"version":3,"file":"EntityContentBlueprint.esm.js","sources":["../../../src/alpha/blueprints/EntityContentBlueprint.ts"],"sourcesContent":["/*\n * Copyright 2024 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 coreExtensionData,\n createExtensionBlueprint,\n ExtensionBoundary,\n RouteRef,\n} from '@backstage/frontend-plugin-api';\nimport {\n entityContentTitleDataRef,\n entityFilterFunctionDataRef,\n entityFilterExpressionDataRef,\n entityContentGroupDataRef,\n defaultEntityContentGroups,\n} from './extensionData';\nimport { EntityPredicate } from '../predicates/types';\nimport { resolveEntityFilterData } from './resolveEntityFilterData';\nimport { createEntityPredicateSchema } from '../predicates/createEntityPredicateSchema';\nimport { Entity } from '@backstage/catalog-model';\n\n/**\n * @alpha\n * Creates an EntityContent extension.\n */\nexport const EntityContentBlueprint = createExtensionBlueprint({\n kind: 'entity-content',\n attachTo: { id: 'page:catalog/entity', input: 'contents' },\n output: [\n coreExtensionData.reactElement,\n coreExtensionData.routePath,\n entityContentTitleDataRef,\n coreExtensionData.routeRef.optional(),\n entityFilterFunctionDataRef.optional(),\n entityFilterExpressionDataRef.optional(),\n entityContentGroupDataRef.optional(),\n ],\n dataRefs: {\n title: entityContentTitleDataRef,\n filterFunction: entityFilterFunctionDataRef,\n filterExpression: entityFilterExpressionDataRef,\n group: entityContentGroupDataRef,\n },\n config: {\n schema: {\n path: z => z.string().optional(),\n title: z => z.string().optional(),\n filter: z =>\n z.union([z.string(), createEntityPredicateSchema(z)]).optional(),\n group: z => z.literal(false).or(z.string()).optional(),\n },\n },\n *factory(\n params: {\n /**\n * @deprecated Use the `path` param instead.\n */\n defaultPath?: [Error: `Use the 'path' param instead`];\n path: string;\n /**\n * @deprecated Use the `path` param instead.\n */\n defaultTitle?: [Error: `Use the 'title' param instead`];\n title: string;\n /**\n * @deprecated Use the `path` param instead.\n */\n defaultGroup?: [Error: `Use the 'group' param instead`];\n group?: keyof typeof defaultEntityContentGroups | (string & {});\n loader: () => Promise<JSX.Element>;\n routeRef?: RouteRef;\n filter?: string | EntityPredicate | ((entity: Entity) => boolean);\n },\n { node, config },\n ) {\n const path = config.path ?? params.path;\n const title = config.title ?? params.title;\n const group = config.group ?? params.group;\n\n yield coreExtensionData.reactElement(\n ExtensionBoundary.lazy(node, params.loader),\n );\n\n yield coreExtensionData.routePath(path);\n\n yield entityContentTitleDataRef(title);\n\n if (params.routeRef) {\n yield coreExtensionData.routeRef(params.routeRef);\n }\n\n yield* resolveEntityFilterData(params.filter, config, node);\n\n if (group) {\n yield entityContentGroupDataRef(group);\n }\n },\n});\n"],"names":[],"mappings":";;;;;AAsCO,MAAM,yBAAyB,wBAAyB,CAAA;AAAA,EAC7D,IAAM,EAAA,gBAAA;AAAA,EACN,QAAU,EAAA,EAAE,EAAI,EAAA,qBAAA,EAAuB,OAAO,UAAW,EAAA;AAAA,EACzD,MAAQ,EAAA;AAAA,IACN,iBAAkB,CAAA,YAAA;AAAA,IAClB,iBAAkB,CAAA,SAAA;AAAA,IAClB,yBAAA;AAAA,IACA,iBAAA,CAAkB,SAAS,QAAS,EAAA;AAAA,IACpC,4BAA4B,QAAS,EAAA;AAAA,IACrC,8BAA8B,QAAS,EAAA;AAAA,IACvC,0BAA0B,QAAS;AAAA,GACrC;AAAA,EACA,QAAU,EAAA;AAAA,IACR,KAAO,EAAA,yBAAA;AAAA,IACP,cAAgB,EAAA,2BAAA;AAAA,IAChB,gBAAkB,EAAA,6BAAA;AAAA,IAClB,KAAO,EAAA;AAAA,GACT;AAAA,EACA,MAAQ,EAAA;AAAA,IACN,MAAQ,EAAA;AAAA,MACN,IAAM,EAAA,CAAA,CAAA,KAAK,CAAE,CAAA,MAAA,GAAS,QAAS,EAAA;AAAA,MAC/B,KAAO,EAAA,CAAA,CAAA,KAAK,CAAE,CAAA,MAAA,GAAS,QAAS,EAAA;AAAA,MAChC,MAAQ,EAAA,CAAA,CAAA,KACN,CAAE,CAAA,KAAA,CAAM,CAAC,CAAA,CAAE,MAAO,EAAA,EAAG,2BAA4B,CAAA,CAAC,CAAC,CAAC,EAAE,QAAS,EAAA;AAAA,MACjE,KAAA,EAAO,CAAK,CAAA,KAAA,CAAA,CAAE,OAAQ,CAAA,KAAK,CAAE,CAAA,EAAA,CAAG,CAAE,CAAA,MAAA,EAAQ,CAAA,CAAE,QAAS;AAAA;AACvD,GACF;AAAA,EACA,CAAC,OACC,CAAA,MAAA,EAoBA,EAAE,IAAA,EAAM,QACR,EAAA;AACA,IAAM,MAAA,IAAA,GAAO,MAAO,CAAA,IAAA,IAAQ,MAAO,CAAA,IAAA;AACnC,IAAM,MAAA,KAAA,GAAQ,MAAO,CAAA,KAAA,IAAS,MAAO,CAAA,KAAA;AACrC,IAAM,MAAA,KAAA,GAAQ,MAAO,CAAA,KAAA,IAAS,MAAO,CAAA,KAAA;AAErC,IAAA,MAAM,iBAAkB,CAAA,YAAA;AAAA,MACtB,iBAAkB,CAAA,IAAA,CAAK,IAAM,EAAA,MAAA,CAAO,MAAM;AAAA,KAC5C;AAEA,IAAM,MAAA,iBAAA,CAAkB,UAAU,IAAI,CAAA;AAEtC,IAAA,MAAM,0BAA0B,KAAK,CAAA;AAErC,IAAA,IAAI,OAAO,QAAU,EAAA;AACnB,MAAM,MAAA,iBAAA,CAAkB,QAAS,CAAA,MAAA,CAAO,QAAQ,CAAA;AAAA;AAGlD,IAAA,OAAO,uBAAwB,CAAA,MAAA,CAAO,MAAQ,EAAA,MAAA,EAAQ,IAAI,CAAA;AAE1D,IAAA,IAAI,KAAO,EAAA;AACT,MAAA,MAAM,0BAA0B,KAAK,CAAA;AAAA;AACvC;AAEJ,CAAC;;;;"}
@@ -31,8 +31,8 @@ function convertLegacyEntityContentExtension(LegacyExtension, overrides) {
31
31
  name: overrides?.name ?? name,
32
32
  params: {
33
33
  filter: overrides?.filter,
34
- defaultPath: overrides?.defaultPath ?? `/${kebabCase(infix)}`,
35
- defaultTitle: overrides?.defaultTitle ?? startCase(infix),
34
+ path: overrides?.path ?? `/${kebabCase(infix)}`,
35
+ title: overrides?.title ?? startCase(infix),
36
36
  routeRef: mountPoint && convertLegacyRouteRef(mountPoint),
37
37
  loader: async () => compatWrapper(element)
38
38
  }
@@ -1 +1 @@
1
- {"version":3,"file":"convertLegacyEntityContentExtension.esm.js","sources":["../../../src/alpha/converters/convertLegacyEntityContentExtension.tsx"],"sourcesContent":["/*\n * Copyright 2024 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 compatWrapper,\n convertLegacyRouteRef,\n} from '@backstage/core-compat-api';\nimport {\n BackstagePlugin,\n getComponentData,\n RouteRef as LegacyRouteRef,\n} from '@backstage/core-plugin-api';\nimport { ExtensionDefinition } from '@backstage/frontend-plugin-api';\nimport kebabCase from 'lodash/kebabCase';\nimport startCase from 'lodash/startCase';\nimport { ComponentType } from 'react';\nimport { EntityContentBlueprint } from '../blueprints/EntityContentBlueprint';\nimport { EntityPredicate } from '../predicates/types';\nimport { Entity } from '@backstage/catalog-model';\n\n/** @alpha */\nexport function convertLegacyEntityContentExtension(\n LegacyExtension: ComponentType<{}>,\n overrides?: {\n name?: string;\n filter?: string | EntityPredicate | ((entity: Entity) => boolean);\n defaultPath?: string;\n defaultTitle?: string;\n },\n): ExtensionDefinition {\n const element = <LegacyExtension />;\n\n const extName = getComponentData<string>(element, 'core.extensionName');\n if (!extName) {\n throw new Error('Extension has no name');\n }\n\n const mountPoint = getComponentData<LegacyRouteRef>(\n element,\n 'core.mountPoint',\n );\n\n const plugin = getComponentData<BackstagePlugin>(element, 'core.plugin');\n const pluginId = plugin?.getId();\n\n const match = extName.match(/^Entity(.*)Content$/);\n const infix = match?.[1] ?? extName;\n\n let name: string | undefined = infix;\n if (\n pluginId &&\n name\n .toLocaleLowerCase('en-US')\n .startsWith(pluginId.toLocaleLowerCase('en-US'))\n ) {\n name = name.slice(pluginId.length);\n if (!name) {\n name = undefined;\n }\n }\n name = name && kebabCase(name);\n\n return EntityContentBlueprint.make({\n name: overrides?.name ?? name,\n params: {\n filter: overrides?.filter,\n defaultPath: overrides?.defaultPath ?? `/${kebabCase(infix)}`,\n defaultTitle: overrides?.defaultTitle ?? startCase(infix),\n routeRef: mountPoint && convertLegacyRouteRef(mountPoint),\n loader: async () => compatWrapper(element),\n },\n });\n}\n"],"names":[],"mappings":";;;;;;;AAkCgB,SAAA,mCAAA,CACd,iBACA,SAMqB,EAAA;AACrB,EAAM,MAAA,OAAA,uBAAW,eAAgB,EAAA,EAAA,CAAA;AAEjC,EAAM,MAAA,OAAA,GAAU,gBAAyB,CAAA,OAAA,EAAS,oBAAoB,CAAA;AACtE,EAAA,IAAI,CAAC,OAAS,EAAA;AACZ,IAAM,MAAA,IAAI,MAAM,uBAAuB,CAAA;AAAA;AAGzC,EAAA,MAAM,UAAa,GAAA,gBAAA;AAAA,IACjB,OAAA;AAAA,IACA;AAAA,GACF;AAEA,EAAM,MAAA,MAAA,GAAS,gBAAkC,CAAA,OAAA,EAAS,aAAa,CAAA;AACvE,EAAM,MAAA,QAAA,GAAW,QAAQ,KAAM,EAAA;AAE/B,EAAM,MAAA,KAAA,GAAQ,OAAQ,CAAA,KAAA,CAAM,qBAAqB,CAAA;AACjD,EAAM,MAAA,KAAA,GAAQ,KAAQ,GAAA,CAAC,CAAK,IAAA,OAAA;AAE5B,EAAA,IAAI,IAA2B,GAAA,KAAA;AAC/B,EACE,IAAA,QAAA,IACA,IACG,CAAA,iBAAA,CAAkB,OAAO,CAAA,CACzB,WAAW,QAAS,CAAA,iBAAA,CAAkB,OAAO,CAAC,CACjD,EAAA;AACA,IAAO,IAAA,GAAA,IAAA,CAAK,KAAM,CAAA,QAAA,CAAS,MAAM,CAAA;AACjC,IAAA,IAAI,CAAC,IAAM,EAAA;AACT,MAAO,IAAA,GAAA,KAAA,CAAA;AAAA;AACT;AAEF,EAAO,IAAA,GAAA,IAAA,IAAQ,UAAU,IAAI,CAAA;AAE7B,EAAA,OAAO,uBAAuB,IAAK,CAAA;AAAA,IACjC,IAAA,EAAM,WAAW,IAAQ,IAAA,IAAA;AAAA,IACzB,MAAQ,EAAA;AAAA,MACN,QAAQ,SAAW,EAAA,MAAA;AAAA,MACnB,aAAa,SAAW,EAAA,WAAA,IAAe,CAAI,CAAA,EAAA,SAAA,CAAU,KAAK,CAAC,CAAA,CAAA;AAAA,MAC3D,YAAc,EAAA,SAAA,EAAW,YAAgB,IAAA,SAAA,CAAU,KAAK,CAAA;AAAA,MACxD,QAAA,EAAU,UAAc,IAAA,qBAAA,CAAsB,UAAU,CAAA;AAAA,MACxD,MAAA,EAAQ,YAAY,aAAA,CAAc,OAAO;AAAA;AAC3C,GACD,CAAA;AACH;;;;"}
1
+ {"version":3,"file":"convertLegacyEntityContentExtension.esm.js","sources":["../../../src/alpha/converters/convertLegacyEntityContentExtension.tsx"],"sourcesContent":["/*\n * Copyright 2024 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 compatWrapper,\n convertLegacyRouteRef,\n} from '@backstage/core-compat-api';\nimport {\n BackstagePlugin,\n getComponentData,\n RouteRef as LegacyRouteRef,\n} from '@backstage/core-plugin-api';\nimport { ExtensionDefinition } from '@backstage/frontend-plugin-api';\nimport kebabCase from 'lodash/kebabCase';\nimport startCase from 'lodash/startCase';\nimport { ComponentType } from 'react';\nimport { EntityContentBlueprint } from '../blueprints/EntityContentBlueprint';\nimport { EntityPredicate } from '../predicates/types';\nimport { Entity } from '@backstage/catalog-model';\n\n/** @alpha */\nexport function convertLegacyEntityContentExtension(\n LegacyExtension: ComponentType<{}>,\n overrides?: {\n name?: string;\n filter?: string | EntityPredicate | ((entity: Entity) => boolean);\n path?: string;\n title?: string;\n\n /**\n * @deprecated Use the `path` param instead.\n */\n defaultPath?: [Error: `Use the 'path' override instead`];\n\n /**\n * @deprecated Use the `path` param instead.\n */\n defaultTitle?: [Error: `Use the 'title' override instead`];\n },\n): ExtensionDefinition {\n const element = <LegacyExtension />;\n\n const extName = getComponentData<string>(element, 'core.extensionName');\n if (!extName) {\n throw new Error('Extension has no name');\n }\n\n const mountPoint = getComponentData<LegacyRouteRef>(\n element,\n 'core.mountPoint',\n );\n\n const plugin = getComponentData<BackstagePlugin>(element, 'core.plugin');\n const pluginId = plugin?.getId();\n\n const match = extName.match(/^Entity(.*)Content$/);\n const infix = match?.[1] ?? extName;\n\n let name: string | undefined = infix;\n if (\n pluginId &&\n name\n .toLocaleLowerCase('en-US')\n .startsWith(pluginId.toLocaleLowerCase('en-US'))\n ) {\n name = name.slice(pluginId.length);\n if (!name) {\n name = undefined;\n }\n }\n name = name && kebabCase(name);\n\n return EntityContentBlueprint.make({\n name: overrides?.name ?? name,\n params: {\n filter: overrides?.filter,\n path: overrides?.path ?? `/${kebabCase(infix)}`,\n title: overrides?.title ?? startCase(infix),\n routeRef: mountPoint && convertLegacyRouteRef(mountPoint),\n loader: async () => compatWrapper(element),\n },\n });\n}\n"],"names":[],"mappings":";;;;;;;AAkCgB,SAAA,mCAAA,CACd,iBACA,SAgBqB,EAAA;AACrB,EAAM,MAAA,OAAA,uBAAW,eAAgB,EAAA,EAAA,CAAA;AAEjC,EAAM,MAAA,OAAA,GAAU,gBAAyB,CAAA,OAAA,EAAS,oBAAoB,CAAA;AACtE,EAAA,IAAI,CAAC,OAAS,EAAA;AACZ,IAAM,MAAA,IAAI,MAAM,uBAAuB,CAAA;AAAA;AAGzC,EAAA,MAAM,UAAa,GAAA,gBAAA;AAAA,IACjB,OAAA;AAAA,IACA;AAAA,GACF;AAEA,EAAM,MAAA,MAAA,GAAS,gBAAkC,CAAA,OAAA,EAAS,aAAa,CAAA;AACvE,EAAM,MAAA,QAAA,GAAW,QAAQ,KAAM,EAAA;AAE/B,EAAM,MAAA,KAAA,GAAQ,OAAQ,CAAA,KAAA,CAAM,qBAAqB,CAAA;AACjD,EAAM,MAAA,KAAA,GAAQ,KAAQ,GAAA,CAAC,CAAK,IAAA,OAAA;AAE5B,EAAA,IAAI,IAA2B,GAAA,KAAA;AAC/B,EACE,IAAA,QAAA,IACA,IACG,CAAA,iBAAA,CAAkB,OAAO,CAAA,CACzB,WAAW,QAAS,CAAA,iBAAA,CAAkB,OAAO,CAAC,CACjD,EAAA;AACA,IAAO,IAAA,GAAA,IAAA,CAAK,KAAM,CAAA,QAAA,CAAS,MAAM,CAAA;AACjC,IAAA,IAAI,CAAC,IAAM,EAAA;AACT,MAAO,IAAA,GAAA,KAAA,CAAA;AAAA;AACT;AAEF,EAAO,IAAA,GAAA,IAAA,IAAQ,UAAU,IAAI,CAAA;AAE7B,EAAA,OAAO,uBAAuB,IAAK,CAAA;AAAA,IACjC,IAAA,EAAM,WAAW,IAAQ,IAAA,IAAA;AAAA,IACzB,MAAQ,EAAA;AAAA,MACN,QAAQ,SAAW,EAAA,MAAA;AAAA,MACnB,MAAM,SAAW,EAAA,IAAA,IAAQ,CAAI,CAAA,EAAA,SAAA,CAAU,KAAK,CAAC,CAAA,CAAA;AAAA,MAC7C,KAAO,EAAA,SAAA,EAAW,KAAS,IAAA,SAAA,CAAU,KAAK,CAAA;AAAA,MAC1C,QAAA,EAAU,UAAc,IAAA,qBAAA,CAAsB,UAAU,CAAA;AAAA,MACxD,MAAA,EAAQ,YAAY,aAAA,CAAc,OAAO;AAAA;AAC3C,GACD,CAAA;AACH;;;;"}
package/dist/alpha.d.ts CHANGED
@@ -25,11 +25,10 @@ declare function isOwnerOf(owner: Entity, entity: Entity): boolean;
25
25
  */
26
26
  declare const CatalogFilterBlueprint: _backstage_frontend_plugin_api.ExtensionBlueprint<{
27
27
  kind: "catalog-filter";
28
- name: undefined;
29
28
  params: {
30
29
  loader: () => Promise<JSX.Element>;
31
30
  };
32
- output: _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<react.JSX.Element, "core.reactElement", {}>;
31
+ output: _backstage_frontend_plugin_api.ExtensionDataRef<react.JSX.Element, "core.reactElement", {}>;
33
32
  inputs: {};
34
33
  config: {};
35
34
  configInput: {};
@@ -88,17 +87,16 @@ declare function entityPredicateToFilterFunction<T extends JsonValue>(entityPred
88
87
  */
89
88
  declare const EntityCardBlueprint: _backstage_frontend_plugin_api.ExtensionBlueprint<{
90
89
  kind: "entity-card";
91
- name: undefined;
92
90
  params: {
93
91
  loader: () => Promise<JSX.Element>;
94
92
  filter?: string | EntityPredicate | ((entity: Entity) => boolean);
95
93
  type?: EntityCardType;
96
94
  };
97
- output: _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<react.JSX.Element, "core.reactElement", {}> | _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<(entity: Entity) => boolean, "catalog.entity-filter-function", {
95
+ output: _backstage_frontend_plugin_api.ExtensionDataRef<react.JSX.Element, "core.reactElement", {}> | _backstage_frontend_plugin_api.ExtensionDataRef<(entity: Entity) => boolean, "catalog.entity-filter-function", {
98
96
  optional: true;
99
- }> | _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<string, "catalog.entity-filter-expression", {
97
+ }> | _backstage_frontend_plugin_api.ExtensionDataRef<string, "catalog.entity-filter-expression", {
100
98
  optional: true;
101
- }> | _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<EntityCardType, "catalog.entity-card-type", {
99
+ }> | _backstage_frontend_plugin_api.ExtensionDataRef<EntityCardType, "catalog.entity-card-type", {
102
100
  optional: true;
103
101
  }>;
104
102
  inputs: {};
@@ -123,22 +121,33 @@ declare const EntityCardBlueprint: _backstage_frontend_plugin_api.ExtensionBluep
123
121
  */
124
122
  declare const EntityContentBlueprint: _backstage_frontend_plugin_api.ExtensionBlueprint<{
125
123
  kind: "entity-content";
126
- name: undefined;
127
124
  params: {
125
+ /**
126
+ * @deprecated Use the `path` param instead.
127
+ */
128
+ defaultPath?: [Error: `Use the 'path' param instead`];
129
+ path: string;
130
+ /**
131
+ * @deprecated Use the `path` param instead.
132
+ */
133
+ defaultTitle?: [Error: `Use the 'title' param instead`];
134
+ title: string;
135
+ /**
136
+ * @deprecated Use the `path` param instead.
137
+ */
138
+ defaultGroup?: [Error: `Use the 'group' param instead`];
139
+ group?: keyof typeof defaultEntityContentGroups | (string & {});
128
140
  loader: () => Promise<JSX.Element>;
129
- defaultPath: string;
130
- defaultTitle: string;
131
- defaultGroup?: keyof typeof defaultEntityContentGroups | (string & {});
132
141
  routeRef?: RouteRef;
133
142
  filter?: string | EntityPredicate | ((entity: Entity) => boolean);
134
143
  };
135
- output: _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<react.JSX.Element, "core.reactElement", {}> | _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<string, "core.routing.path", {}> | _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<RouteRef<_backstage_frontend_plugin_api.AnyRouteRefParams>, "core.routing.ref", {
144
+ output: _backstage_frontend_plugin_api.ExtensionDataRef<string, "core.routing.path", {}> | _backstage_frontend_plugin_api.ExtensionDataRef<react.JSX.Element, "core.reactElement", {}> | _backstage_frontend_plugin_api.ExtensionDataRef<RouteRef<_backstage_frontend_plugin_api.AnyRouteRefParams>, "core.routing.ref", {
136
145
  optional: true;
137
- }> | _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<string, "catalog.entity-content-title", {}> | _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<(entity: Entity) => boolean, "catalog.entity-filter-function", {
146
+ }> | _backstage_frontend_plugin_api.ExtensionDataRef<(entity: Entity) => boolean, "catalog.entity-filter-function", {
138
147
  optional: true;
139
- }> | _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<string, "catalog.entity-filter-expression", {
148
+ }> | _backstage_frontend_plugin_api.ExtensionDataRef<string, "catalog.entity-filter-expression", {
140
149
  optional: true;
141
- }> | _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<string, "catalog.entity-content-group", {
150
+ }> | _backstage_frontend_plugin_api.ExtensionDataRef<string, "catalog.entity-content-title", {}> | _backstage_frontend_plugin_api.ExtensionDataRef<string, "catalog.entity-content-group", {
142
151
  optional: true;
143
152
  }>;
144
153
  inputs: {};
@@ -172,16 +181,15 @@ interface EntityContentLayoutProps {
172
181
  /** @alpha */
173
182
  declare const EntityContentLayoutBlueprint: _backstage_frontend_plugin_api.ExtensionBlueprint<{
174
183
  kind: "entity-content-layout";
175
- name: undefined;
176
184
  params: {
177
185
  filter?: string | EntityPredicate | ((entity: Entity) => boolean);
178
186
  loader: () => Promise<(props: EntityContentLayoutProps) => JSX$1.Element>;
179
187
  };
180
- output: _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<(entity: Entity) => boolean, "catalog.entity-filter-function", {
188
+ output: _backstage_frontend_plugin_api.ExtensionDataRef<(entity: Entity) => boolean, "catalog.entity-filter-function", {
181
189
  optional: true;
182
- }> | _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<string, "catalog.entity-filter-expression", {
190
+ }> | _backstage_frontend_plugin_api.ExtensionDataRef<string, "catalog.entity-filter-expression", {
183
191
  optional: true;
184
- }> | _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<(props: EntityContentLayoutProps) => React.JSX.Element, "catalog.entity-content-layout.component", {}>;
192
+ }> | _backstage_frontend_plugin_api.ExtensionDataRef<(props: EntityContentLayoutProps) => React.JSX.Element, "catalog.entity-content-layout.component", {}>;
185
193
  inputs: {};
186
194
  config: {
187
195
  type: string | undefined;
@@ -201,16 +209,15 @@ declare const EntityContentLayoutBlueprint: _backstage_frontend_plugin_api.Exten
201
209
  /** @alpha */
202
210
  declare const EntityHeaderBlueprint: _backstage_frontend_plugin_api.ExtensionBlueprint<{
203
211
  kind: "entity-header";
204
- name: undefined;
205
212
  params: {
206
213
  loader: () => Promise<JSX.Element>;
207
214
  filter?: EntityPredicate | ((entity: Entity) => boolean);
208
215
  };
209
- output: _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<(entity: Entity) => boolean, "catalog.entity-filter-function", {
216
+ output: _backstage_frontend_plugin_api.ExtensionDataRef<(entity: Entity) => boolean, "catalog.entity-filter-function", {
210
217
  optional: true;
211
- }> | _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<string, "catalog.entity-filter-expression", {
218
+ }> | _backstage_frontend_plugin_api.ExtensionDataRef<string, "catalog.entity-filter-expression", {
212
219
  optional: true;
213
- }> | _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<react.JSX.Element, "core.reactElement", {
220
+ }> | _backstage_frontend_plugin_api.ExtensionDataRef<react.JSX.Element, "core.reactElement", {
214
221
  optional: true;
215
222
  }>;
216
223
  inputs: {};
@@ -245,9 +252,8 @@ type EntityContextMenuItemParams = {
245
252
  /** @alpha */
246
253
  declare const EntityContextMenuItemBlueprint: _backstage_frontend_plugin_api.ExtensionBlueprint<{
247
254
  kind: "entity-context-menu-item";
248
- name: undefined;
249
255
  params: EntityContextMenuItemParams;
250
- output: _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<JSX$1.Element, "core.reactElement", {}> | _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<(entity: Entity) => boolean, "catalog.entity-filter-function", {
256
+ output: _backstage_frontend_plugin_api.ExtensionDataRef<JSX$1.Element, "core.reactElement", {}> | _backstage_frontend_plugin_api.ExtensionDataRef<(entity: Entity) => boolean, "catalog.entity-filter-function", {
251
257
  optional: true;
252
258
  }>;
253
259
  inputs: {};
@@ -265,16 +271,15 @@ declare const EntityContextMenuItemBlueprint: _backstage_frontend_plugin_api.Ext
265
271
  /** @alpha */
266
272
  declare const EntityIconLinkBlueprint: _backstage_frontend_plugin_api.ExtensionBlueprint<{
267
273
  kind: "entity-icon-link";
268
- name: undefined;
269
274
  params: {
270
275
  useProps: () => Omit<IconLinkVerticalProps, "color">;
271
276
  filter?: EntityPredicate | ((entity: Entity) => boolean);
272
277
  };
273
- output: _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<(entity: Entity) => boolean, "catalog.entity-filter-function", {
278
+ output: _backstage_frontend_plugin_api.ExtensionDataRef<(entity: Entity) => boolean, "catalog.entity-filter-function", {
274
279
  optional: true;
275
- }> | _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<string, "catalog.entity-filter-expression", {
280
+ }> | _backstage_frontend_plugin_api.ExtensionDataRef<string, "catalog.entity-filter-expression", {
276
281
  optional: true;
277
- }> | _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<() => IconLinkVerticalProps, "entity-icon-link-props", {}>;
282
+ }> | _backstage_frontend_plugin_api.ExtensionDataRef<() => IconLinkVerticalProps, "entity-icon-link-props", {}>;
278
283
  inputs: {};
279
284
  config: {
280
285
  label: string | undefined;
@@ -304,8 +309,16 @@ declare function convertLegacyEntityCardExtension(LegacyExtension: ComponentType
304
309
  declare function convertLegacyEntityContentExtension(LegacyExtension: ComponentType<{}>, overrides?: {
305
310
  name?: string;
306
311
  filter?: string | EntityPredicate | ((entity: Entity) => boolean);
307
- defaultPath?: string;
308
- defaultTitle?: string;
312
+ path?: string;
313
+ title?: string;
314
+ /**
315
+ * @deprecated Use the `path` param instead.
316
+ */
317
+ defaultPath?: [Error: `Use the 'path' override instead`];
318
+ /**
319
+ * @deprecated Use the `path` param instead.
320
+ */
321
+ defaultTitle?: [Error: `Use the 'title' override instead`];
309
322
  }): ExtensionDefinition;
310
323
 
311
324
  /** @alpha */
@@ -152,7 +152,11 @@ const EntityListProvider = (props) => {
152
152
  ignoreQueryPrefix: true
153
153
  });
154
154
  const newParams = qs.stringify(
155
- { ...oldParams, filters: queryParams, cursor, offset, limit },
155
+ {
156
+ ...oldParams,
157
+ filters: queryParams,
158
+ ...paginationMode === "none" ? {} : { cursor, limit, offset }
159
+ },
156
160
  { addQueryPrefix: true, arrayFormat: "repeat" }
157
161
  );
158
162
  const newUrl = `${window.location.pathname}${newParams}`;
@@ -1 +1 @@
1
- {"version":3,"file":"useEntityListProvider.esm.js","sources":["../../src/hooks/useEntityListProvider.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 { Entity } from '@backstage/catalog-model';\nimport { compact, isEqual } from 'lodash';\nimport qs from 'qs';\nimport {\n createContext,\n PropsWithChildren,\n useCallback,\n useContext,\n useMemo,\n useState,\n} from 'react';\nimport { useLocation } from 'react-router-dom';\nimport useAsyncFn from 'react-use/esm/useAsyncFn';\nimport useDebounce from 'react-use/esm/useDebounce';\nimport useMountedState from 'react-use/esm/useMountedState';\nimport { catalogApiRef } from '../api';\nimport {\n EntityErrorFilter,\n EntityKindFilter,\n EntityLifecycleFilter,\n EntityNamespaceFilter,\n EntityOrderFilter,\n EntityOrphanFilter,\n EntityOwnerFilter,\n EntityTagFilter,\n EntityTextFilter,\n EntityTypeFilter,\n EntityUserFilter,\n UserListFilter,\n} from '../filters';\nimport { EntityFilter, EntityListPagination } from '../types';\nimport {\n reduceBackendCatalogFilters,\n reduceCatalogFilters,\n reduceEntityFilters,\n} from '../utils/filters';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { QueryEntitiesResponse } from '@backstage/catalog-client';\n\n/** @public */\nexport type DefaultEntityFilters = {\n kind?: EntityKindFilter;\n type?: EntityTypeFilter;\n user?: UserListFilter | EntityUserFilter;\n owners?: EntityOwnerFilter;\n lifecycles?: EntityLifecycleFilter;\n tags?: EntityTagFilter;\n text?: EntityTextFilter;\n orphan?: EntityOrphanFilter;\n error?: EntityErrorFilter;\n namespace?: EntityNamespaceFilter;\n order?: EntityOrderFilter;\n};\n\n/** @public */\nexport type PaginationMode = 'cursor' | 'offset' | 'none';\n\n/** @public */\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 pageInfo?: {\n next?: () => void;\n prev?: () => void;\n };\n totalItems?: number;\n limit: number;\n offset?: number;\n setLimit: (limit: number) => void;\n setOffset?: (offset: number) => void;\n paginationMode: PaginationMode;\n};\n\n/**\n * Creates new context for entity listing and filtering.\n * @public\n */\nexport const EntityListContext = createContext<\n EntityListContextProps<any> | undefined\n>(undefined);\n\ntype OutputState<EntityFilters extends DefaultEntityFilters> = {\n appliedFilters: EntityFilters;\n appliedCursor?: string;\n entities: Entity[];\n backendEntities: Entity[];\n pageInfo?: QueryEntitiesResponse['pageInfo'];\n totalItems?: number;\n offset?: number;\n limit?: number;\n};\n\n/**\n * @public\n */\nexport type EntityListProviderProps = PropsWithChildren<{\n pagination?: EntityListPagination;\n}>;\n\n/**\n * Provides entities and filters for a catalog listing.\n * @public\n */\nexport const EntityListProvider = <EntityFilters extends DefaultEntityFilters>(\n props: EntityListProviderProps,\n) => {\n const isMounted = useMountedState();\n const catalogApi = useApi(catalogApiRef);\n const [requestedFilters, setRequestedFilters] = useState<EntityFilters>(\n {} as EntityFilters,\n );\n\n // We use react-router's useLocation hook so updates from external sources trigger an update to\n // the queryParameters in outputState. Updates from this hook use replaceState below and won't\n // trigger a useLocation change; this would instead come from an external source, such as a manual\n // update of the URL or two catalog sidebar links with different catalog filters.\n const location = useLocation();\n\n const getPaginationMode = (): PaginationMode => {\n if (props.pagination === true) {\n return 'cursor';\n }\n return typeof props.pagination === 'object'\n ? props.pagination.mode ?? 'cursor'\n : 'none';\n };\n\n const paginationMode = getPaginationMode();\n const paginationLimit =\n typeof props.pagination === 'object' ? props.pagination.limit ?? 20 : 20;\n\n const {\n queryParameters,\n cursor: initialCursor,\n offset: initialOffset,\n limit: initialLimit,\n } = useMemo(() => {\n const parsed = qs.parse(location.search, {\n ignoreQueryPrefix: true,\n });\n\n let limit = paginationLimit;\n if (typeof parsed.limit === 'string') {\n const queryLimit = Number.parseInt(parsed.limit, 10);\n if (!isNaN(queryLimit)) {\n limit = queryLimit;\n }\n }\n\n const offset =\n typeof parsed.offset === 'string' && paginationMode === 'offset'\n ? Number.parseInt(parsed.offset, 10)\n : undefined;\n\n return {\n queryParameters: (parsed.filters ?? {}) as Record<\n string,\n string | string[]\n >,\n cursor:\n typeof parsed.cursor === 'string' && paginationMode === 'cursor'\n ? parsed.cursor\n : undefined,\n offset:\n paginationMode === 'offset' && offset && !isNaN(offset)\n ? offset\n : undefined,\n limit,\n };\n }, [paginationMode, location.search, paginationLimit]);\n\n const [cursor, setCursor] = useState(initialCursor);\n const [offset, setOffset] = useState<number | undefined>(initialOffset);\n const [limit, setLimit] = useState(initialLimit);\n\n const [outputState, setOutputState] = useState<OutputState<EntityFilters>>(\n () => {\n return {\n appliedFilters: {} as EntityFilters,\n entities: [],\n backendEntities: [],\n pageInfo: {},\n offset,\n limit,\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 kindValue =\n requestedFilters.kind?.value?.toLocaleLowerCase('en-US');\n const adjustedFilters =\n kindValue === 'user' || kindValue === 'group'\n ? { ...requestedFilters, owners: undefined }\n : requestedFilters;\n const compacted = compact(Object.values(adjustedFilters));\n\n const queryParams = Object.keys(requestedFilters).reduce(\n (params, key) => {\n const filter = requestedFilters[key as keyof EntityFilters] as\n | EntityFilter\n | undefined;\n if (filter?.toQueryValue) {\n params[key] = filter.toQueryValue();\n }\n return params;\n },\n {} as Record<string, string | string[]>,\n );\n\n if (paginationMode !== 'none') {\n if (cursor) {\n if (cursor !== outputState.appliedCursor) {\n const entityFilter = reduceEntityFilters(compacted);\n const response = await catalogApi.queryEntities({\n cursor,\n limit,\n });\n setOutputState({\n appliedFilters: requestedFilters,\n appliedCursor: cursor,\n backendEntities: response.items,\n entities: response.items.filter(entityFilter),\n pageInfo: response.pageInfo,\n totalItems: response.totalItems,\n });\n }\n } else {\n const entityFilter = reduceEntityFilters(compacted);\n const backendFilter = reduceCatalogFilters(compacted);\n const previousBackendFilter = reduceCatalogFilters(\n compact(Object.values(outputState.appliedFilters)),\n );\n\n if (\n (paginationMode === 'offset' &&\n (outputState.limit !== limit || outputState.offset !== offset)) ||\n !isEqual(previousBackendFilter, backendFilter)\n ) {\n const response = await catalogApi.queryEntities({\n ...backendFilter,\n limit,\n offset,\n });\n setOutputState({\n appliedFilters: requestedFilters,\n backendEntities: response.items,\n entities: response.items.filter(entityFilter),\n pageInfo: response.pageInfo,\n totalItems: response.totalItems,\n limit,\n offset,\n });\n }\n }\n } else {\n const entityFilter = reduceEntityFilters(compacted);\n const backendFilter = reduceBackendCatalogFilters(compacted);\n const previousBackendFilter = reduceBackendCatalogFilters(\n compact(Object.values(outputState.appliedFilters)),\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 const entities = response.items.filter(entityFilter);\n setOutputState({\n appliedFilters: requestedFilters,\n backendEntities: response.items,\n entities,\n totalItems: entities.length,\n });\n } else {\n const entities = outputState.backendEntities.filter(entityFilter);\n setOutputState({\n appliedFilters: requestedFilters,\n backendEntities: outputState.backendEntities,\n entities,\n totalItems: entities.length,\n });\n }\n }\n\n if (isMounted()) {\n const oldParams = qs.parse(location.search, {\n ignoreQueryPrefix: true,\n });\n const newParams = qs.stringify(\n { ...oldParams, filters: queryParams, cursor, offset, limit },\n { addQueryPrefix: true, arrayFormat: 'repeat' },\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 [\n catalogApi,\n queryParameters,\n requestedFilters,\n outputState,\n cursor,\n paginationMode,\n limit,\n offset,\n ],\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, cursor, limit, offset]);\n\n const updateFilters = useCallback(\n (\n update:\n | Partial<EntityFilter>\n | ((prevFilters: EntityFilters) => Partial<EntityFilters>),\n ) => {\n // changing filters will affect pagination, so we need to reset\n // the cursor and start from the first page.\n // TODO(vinzscam): this is currently causing issues at page reload\n // where the state is not kept. Unfortunately we need to rethink\n // the way filters work in order to fix this.\n if (paginationMode === 'cursor') {\n setCursor(undefined);\n } else if (paginationMode === 'offset') {\n // Same thing with offset\n setOffset(0);\n }\n setRequestedFilters(prevFilters => {\n const newFilters =\n typeof update === 'function' ? update(prevFilters) : update;\n return { ...prevFilters, ...newFilters };\n });\n },\n [paginationMode],\n );\n\n const pageInfo = useMemo(() => {\n if (paginationMode !== 'cursor') {\n return undefined;\n }\n\n const prevCursor = outputState.pageInfo?.prevCursor;\n const nextCursor = outputState.pageInfo?.nextCursor;\n return {\n prev: prevCursor ? () => setCursor(prevCursor) : undefined,\n next: nextCursor ? () => setCursor(nextCursor) : undefined,\n };\n }, [paginationMode, outputState.pageInfo]);\n\n const value = useMemo(\n () => ({\n filters: outputState.appliedFilters,\n entities: outputState.entities,\n backendEntities: outputState.backendEntities,\n updateFilters,\n queryParameters,\n loading,\n error,\n pageInfo,\n totalItems: outputState.totalItems,\n limit,\n offset,\n setLimit,\n setOffset,\n paginationMode,\n }),\n [\n outputState,\n updateFilters,\n queryParameters,\n loading,\n error,\n pageInfo,\n limit,\n offset,\n paginationMode,\n setLimit,\n setOffset,\n ],\n );\n\n return (\n <EntityListContext.Provider value={value}>\n {props.children}\n </EntityListContext.Provider>\n );\n};\n\n/**\n * Hook for interacting with the entity list context provided by the {@link EntityListProvider}.\n * @public\n */\nexport function useEntityList<\n EntityFilters extends DefaultEntityFilters = DefaultEntityFilters,\n>(): EntityListContextProps<EntityFilters> {\n const context = useContext(EntityListContext);\n if (!context)\n throw new Error('useEntityList must be used within EntityListProvider');\n return context;\n}\n"],"names":["limit","offset"],"mappings":"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"}
1
+ {"version":3,"file":"useEntityListProvider.esm.js","sources":["../../src/hooks/useEntityListProvider.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 { Entity } from '@backstage/catalog-model';\nimport { compact, isEqual } from 'lodash';\nimport qs from 'qs';\nimport {\n createContext,\n PropsWithChildren,\n useCallback,\n useContext,\n useMemo,\n useState,\n} from 'react';\nimport { useLocation } from 'react-router-dom';\nimport useAsyncFn from 'react-use/esm/useAsyncFn';\nimport useDebounce from 'react-use/esm/useDebounce';\nimport useMountedState from 'react-use/esm/useMountedState';\nimport { catalogApiRef } from '../api';\nimport {\n EntityErrorFilter,\n EntityKindFilter,\n EntityLifecycleFilter,\n EntityNamespaceFilter,\n EntityOrderFilter,\n EntityOrphanFilter,\n EntityOwnerFilter,\n EntityTagFilter,\n EntityTextFilter,\n EntityTypeFilter,\n EntityUserFilter,\n UserListFilter,\n} from '../filters';\nimport { EntityFilter, EntityListPagination } from '../types';\nimport {\n reduceBackendCatalogFilters,\n reduceCatalogFilters,\n reduceEntityFilters,\n} from '../utils/filters';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { QueryEntitiesResponse } from '@backstage/catalog-client';\n\n/** @public */\nexport type DefaultEntityFilters = {\n kind?: EntityKindFilter;\n type?: EntityTypeFilter;\n user?: UserListFilter | EntityUserFilter;\n owners?: EntityOwnerFilter;\n lifecycles?: EntityLifecycleFilter;\n tags?: EntityTagFilter;\n text?: EntityTextFilter;\n orphan?: EntityOrphanFilter;\n error?: EntityErrorFilter;\n namespace?: EntityNamespaceFilter;\n order?: EntityOrderFilter;\n};\n\n/** @public */\nexport type PaginationMode = 'cursor' | 'offset' | 'none';\n\n/** @public */\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 pageInfo?: {\n next?: () => void;\n prev?: () => void;\n };\n totalItems?: number;\n limit: number;\n offset?: number;\n setLimit: (limit: number) => void;\n setOffset?: (offset: number) => void;\n paginationMode: PaginationMode;\n};\n\n/**\n * Creates new context for entity listing and filtering.\n * @public\n */\nexport const EntityListContext = createContext<\n EntityListContextProps<any> | undefined\n>(undefined);\n\ntype OutputState<EntityFilters extends DefaultEntityFilters> = {\n appliedFilters: EntityFilters;\n appliedCursor?: string;\n entities: Entity[];\n backendEntities: Entity[];\n pageInfo?: QueryEntitiesResponse['pageInfo'];\n totalItems?: number;\n offset?: number;\n limit?: number;\n};\n\n/**\n * @public\n */\nexport type EntityListProviderProps = PropsWithChildren<{\n pagination?: EntityListPagination;\n}>;\n\n/**\n * Provides entities and filters for a catalog listing.\n * @public\n */\nexport const EntityListProvider = <EntityFilters extends DefaultEntityFilters>(\n props: EntityListProviderProps,\n) => {\n const isMounted = useMountedState();\n const catalogApi = useApi(catalogApiRef);\n const [requestedFilters, setRequestedFilters] = useState<EntityFilters>(\n {} as EntityFilters,\n );\n\n // We use react-router's useLocation hook so updates from external sources trigger an update to\n // the queryParameters in outputState. Updates from this hook use replaceState below and won't\n // trigger a useLocation change; this would instead come from an external source, such as a manual\n // update of the URL or two catalog sidebar links with different catalog filters.\n const location = useLocation();\n\n const getPaginationMode = (): PaginationMode => {\n if (props.pagination === true) {\n return 'cursor';\n }\n return typeof props.pagination === 'object'\n ? props.pagination.mode ?? 'cursor'\n : 'none';\n };\n\n const paginationMode = getPaginationMode();\n const paginationLimit =\n typeof props.pagination === 'object' ? props.pagination.limit ?? 20 : 20;\n\n const {\n queryParameters,\n cursor: initialCursor,\n offset: initialOffset,\n limit: initialLimit,\n } = useMemo(() => {\n const parsed = qs.parse(location.search, {\n ignoreQueryPrefix: true,\n });\n\n let limit = paginationLimit;\n if (typeof parsed.limit === 'string') {\n const queryLimit = Number.parseInt(parsed.limit, 10);\n if (!isNaN(queryLimit)) {\n limit = queryLimit;\n }\n }\n\n const offset =\n typeof parsed.offset === 'string' && paginationMode === 'offset'\n ? Number.parseInt(parsed.offset, 10)\n : undefined;\n\n return {\n queryParameters: (parsed.filters ?? {}) as Record<\n string,\n string | string[]\n >,\n cursor:\n typeof parsed.cursor === 'string' && paginationMode === 'cursor'\n ? parsed.cursor\n : undefined,\n offset:\n paginationMode === 'offset' && offset && !isNaN(offset)\n ? offset\n : undefined,\n limit,\n };\n }, [paginationMode, location.search, paginationLimit]);\n\n const [cursor, setCursor] = useState(initialCursor);\n const [offset, setOffset] = useState<number | undefined>(initialOffset);\n const [limit, setLimit] = useState(initialLimit);\n\n const [outputState, setOutputState] = useState<OutputState<EntityFilters>>(\n () => {\n return {\n appliedFilters: {} as EntityFilters,\n entities: [],\n backendEntities: [],\n pageInfo: {},\n offset,\n limit,\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 kindValue =\n requestedFilters.kind?.value?.toLocaleLowerCase('en-US');\n const adjustedFilters =\n kindValue === 'user' || kindValue === 'group'\n ? { ...requestedFilters, owners: undefined }\n : requestedFilters;\n const compacted = compact(Object.values(adjustedFilters));\n\n const queryParams = Object.keys(requestedFilters).reduce(\n (params, key) => {\n const filter = requestedFilters[key as keyof EntityFilters] as\n | EntityFilter\n | undefined;\n if (filter?.toQueryValue) {\n params[key] = filter.toQueryValue();\n }\n return params;\n },\n {} as Record<string, string | string[]>,\n );\n\n if (paginationMode !== 'none') {\n if (cursor) {\n if (cursor !== outputState.appliedCursor) {\n const entityFilter = reduceEntityFilters(compacted);\n const response = await catalogApi.queryEntities({\n cursor,\n limit,\n });\n setOutputState({\n appliedFilters: requestedFilters,\n appliedCursor: cursor,\n backendEntities: response.items,\n entities: response.items.filter(entityFilter),\n pageInfo: response.pageInfo,\n totalItems: response.totalItems,\n });\n }\n } else {\n const entityFilter = reduceEntityFilters(compacted);\n const backendFilter = reduceCatalogFilters(compacted);\n const previousBackendFilter = reduceCatalogFilters(\n compact(Object.values(outputState.appliedFilters)),\n );\n\n if (\n (paginationMode === 'offset' &&\n (outputState.limit !== limit || outputState.offset !== offset)) ||\n !isEqual(previousBackendFilter, backendFilter)\n ) {\n const response = await catalogApi.queryEntities({\n ...backendFilter,\n limit,\n offset,\n });\n setOutputState({\n appliedFilters: requestedFilters,\n backendEntities: response.items,\n entities: response.items.filter(entityFilter),\n pageInfo: response.pageInfo,\n totalItems: response.totalItems,\n limit,\n offset,\n });\n }\n }\n } else {\n const entityFilter = reduceEntityFilters(compacted);\n const backendFilter = reduceBackendCatalogFilters(compacted);\n const previousBackendFilter = reduceBackendCatalogFilters(\n compact(Object.values(outputState.appliedFilters)),\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 const entities = response.items.filter(entityFilter);\n setOutputState({\n appliedFilters: requestedFilters,\n backendEntities: response.items,\n entities,\n totalItems: entities.length,\n });\n } else {\n const entities = outputState.backendEntities.filter(entityFilter);\n setOutputState({\n appliedFilters: requestedFilters,\n backendEntities: outputState.backendEntities,\n entities,\n totalItems: entities.length,\n });\n }\n }\n\n if (isMounted()) {\n const oldParams = qs.parse(location.search, {\n ignoreQueryPrefix: true,\n });\n const newParams = qs.stringify(\n {\n ...oldParams,\n filters: queryParams,\n ...(paginationMode === 'none' ? {} : { cursor, limit, offset }),\n },\n { addQueryPrefix: true, arrayFormat: 'repeat' },\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 [\n catalogApi,\n queryParameters,\n requestedFilters,\n outputState,\n cursor,\n paginationMode,\n limit,\n offset,\n ],\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, cursor, limit, offset]);\n\n const updateFilters = useCallback(\n (\n update:\n | Partial<EntityFilter>\n | ((prevFilters: EntityFilters) => Partial<EntityFilters>),\n ) => {\n // changing filters will affect pagination, so we need to reset\n // the cursor and start from the first page.\n // TODO(vinzscam): this is currently causing issues at page reload\n // where the state is not kept. Unfortunately we need to rethink\n // the way filters work in order to fix this.\n if (paginationMode === 'cursor') {\n setCursor(undefined);\n } else if (paginationMode === 'offset') {\n // Same thing with offset\n setOffset(0);\n }\n setRequestedFilters(prevFilters => {\n const newFilters =\n typeof update === 'function' ? update(prevFilters) : update;\n return { ...prevFilters, ...newFilters };\n });\n },\n [paginationMode],\n );\n\n const pageInfo = useMemo(() => {\n if (paginationMode !== 'cursor') {\n return undefined;\n }\n\n const prevCursor = outputState.pageInfo?.prevCursor;\n const nextCursor = outputState.pageInfo?.nextCursor;\n return {\n prev: prevCursor ? () => setCursor(prevCursor) : undefined,\n next: nextCursor ? () => setCursor(nextCursor) : undefined,\n };\n }, [paginationMode, outputState.pageInfo]);\n\n const value = useMemo(\n () => ({\n filters: outputState.appliedFilters,\n entities: outputState.entities,\n backendEntities: outputState.backendEntities,\n updateFilters,\n queryParameters,\n loading,\n error,\n pageInfo,\n totalItems: outputState.totalItems,\n limit,\n offset,\n setLimit,\n setOffset,\n paginationMode,\n }),\n [\n outputState,\n updateFilters,\n queryParameters,\n loading,\n error,\n pageInfo,\n limit,\n offset,\n paginationMode,\n setLimit,\n setOffset,\n ],\n );\n\n return (\n <EntityListContext.Provider value={value}>\n {props.children}\n </EntityListContext.Provider>\n );\n};\n\n/**\n * Hook for interacting with the entity list context provided by the {@link EntityListProvider}.\n * @public\n */\nexport function useEntityList<\n EntityFilters extends DefaultEntityFilters = DefaultEntityFilters,\n>(): EntityListContextProps<EntityFilters> {\n const context = useContext(EntityListContext);\n if (!context)\n throw new Error('useEntityList must be used within EntityListProvider');\n return context;\n}\n"],"names":["limit","offset"],"mappings":"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"}
@@ -47,7 +47,8 @@ function catalogApiMock(options) {
47
47
  addLocation: jest.fn(),
48
48
  removeLocationById: jest.fn(),
49
49
  getLocationByEntity: jest.fn(),
50
- validateEntity: jest.fn()
50
+ validateEntity: jest.fn(),
51
+ analyzeLocation: jest.fn()
51
52
  }));
52
53
  })(catalogApiMock || (catalogApiMock = {}));
53
54
 
@@ -1 +1 @@
1
- {"version":3,"file":"catalogApiMock.esm.js","sources":["../../src/testUtils/catalogApiMock.ts"],"sourcesContent":["/*\n * Copyright 2024 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 ApiFactory,\n ApiRef,\n createApiFactory,\n} from '@backstage/frontend-plugin-api';\nimport { InMemoryCatalogClient } from '@backstage/catalog-client/testUtils';\nimport { Entity } from '@backstage/catalog-model';\nimport { catalogApiRef } from '@backstage/plugin-catalog-react';\nimport { CatalogApi } from '@backstage/catalog-client';\nimport { ApiMock } from '@backstage/frontend-test-utils';\n\n/** @internal */\nfunction simpleMock<TApi>(\n ref: ApiRef<TApi>,\n mockFactory: () => jest.Mocked<TApi>,\n): (partialImpl?: Partial<TApi>) => ApiMock<TApi> {\n return partialImpl => {\n const mock = mockFactory();\n if (partialImpl) {\n for (const [key, impl] of Object.entries(partialImpl)) {\n if (typeof impl === 'function') {\n (mock as any)[key].mockImplementation(impl);\n } else {\n (mock as any)[key] = impl;\n }\n }\n }\n return Object.assign(mock, {\n factory: createApiFactory({\n api: ref,\n deps: {},\n factory: () => mock,\n }),\n }) as ApiMock<TApi>;\n };\n}\n\n/**\n * Creates a fake catalog client that handles entities in memory storage. Note\n * that this client may be severely limited in functionality, and advanced\n * functions may not be available at all.\n *\n * @public\n */\nexport function catalogApiMock(options?: { entities?: Entity[] }): CatalogApi {\n return new InMemoryCatalogClient(options);\n}\n\n/**\n * A collection of mock functionality for the catalog service.\n *\n * @public\n */\nexport namespace catalogApiMock {\n /**\n * Creates a fake catalog client that handles entities in memory storage. Note\n * that this client may be severely limited in functionality, and advanced\n * functions may not be available at all.\n */\n export const factory = (options?: {\n entities?: Entity[];\n }): ApiFactory<CatalogApi, CatalogApi, {}> =>\n createApiFactory({\n api: catalogApiRef,\n deps: {},\n factory: () => new InMemoryCatalogClient(options),\n });\n /**\n * Creates a catalog client whose methods are mock functions, possibly with\n * some of them overloaded by the caller.\n */\n export const mock = simpleMock(catalogApiRef, () => ({\n getEntities: jest.fn(),\n getEntitiesByRefs: jest.fn(),\n queryEntities: jest.fn(),\n getEntityAncestors: jest.fn(),\n getEntityByRef: jest.fn(),\n removeEntityByUid: jest.fn(),\n refreshEntity: jest.fn(),\n getEntityFacets: jest.fn(),\n getLocations: jest.fn(),\n getLocationById: jest.fn(),\n getLocationByRef: jest.fn(),\n addLocation: jest.fn(),\n removeLocationById: jest.fn(),\n getLocationByEntity: jest.fn(),\n validateEntity: jest.fn(),\n }));\n}\n"],"names":["catalogApiMock"],"mappings":";;;;AA4BA,SAAS,UAAA,CACP,KACA,WACgD,EAAA;AAChD,EAAA,OAAO,CAAe,WAAA,KAAA;AACpB,IAAA,MAAM,OAAO,WAAY,EAAA;AACzB,IAAA,IAAI,WAAa,EAAA;AACf,MAAA,KAAA,MAAW,CAAC,GAAK,EAAA,IAAI,KAAK,MAAO,CAAA,OAAA,CAAQ,WAAW,CAAG,EAAA;AACrD,QAAI,IAAA,OAAO,SAAS,UAAY,EAAA;AAC9B,UAAC,IAAa,CAAA,GAAG,CAAE,CAAA,kBAAA,CAAmB,IAAI,CAAA;AAAA,SACrC,MAAA;AACL,UAAC,IAAA,CAAa,GAAG,CAAI,GAAA,IAAA;AAAA;AACvB;AACF;AAEF,IAAO,OAAA,MAAA,CAAO,OAAO,IAAM,EAAA;AAAA,MACzB,SAAS,gBAAiB,CAAA;AAAA,QACxB,GAAK,EAAA,GAAA;AAAA,QACL,MAAM,EAAC;AAAA,QACP,SAAS,MAAM;AAAA,OAChB;AAAA,KACF,CAAA;AAAA,GACH;AACF;AASO,SAAS,eAAe,OAA+C,EAAA;AAC5E,EAAO,OAAA,IAAI,sBAAsB,OAAO,CAAA;AAC1C;AAAA,CAOO,CAAUA,eAAV,KAAA;AAME,EAAMA,eAAA,CAAA,OAAA,GAAU,CAAC,OAAA,KAGtB,gBAAiB,CAAA;AAAA,IACf,GAAK,EAAA,aAAA;AAAA,IACL,MAAM,EAAC;AAAA,IACP,OAAS,EAAA,MAAM,IAAI,qBAAA,CAAsB,OAAO;AAAA,GACjD,CAAA;AAKI,EAAMA,eAAA,CAAA,IAAA,GAAO,UAAW,CAAA,aAAA,EAAe,OAAO;AAAA,IACnD,WAAA,EAAa,KAAK,EAAG,EAAA;AAAA,IACrB,iBAAA,EAAmB,KAAK,EAAG,EAAA;AAAA,IAC3B,aAAA,EAAe,KAAK,EAAG,EAAA;AAAA,IACvB,kBAAA,EAAoB,KAAK,EAAG,EAAA;AAAA,IAC5B,cAAA,EAAgB,KAAK,EAAG,EAAA;AAAA,IACxB,iBAAA,EAAmB,KAAK,EAAG,EAAA;AAAA,IAC3B,aAAA,EAAe,KAAK,EAAG,EAAA;AAAA,IACvB,eAAA,EAAiB,KAAK,EAAG,EAAA;AAAA,IACzB,YAAA,EAAc,KAAK,EAAG,EAAA;AAAA,IACtB,eAAA,EAAiB,KAAK,EAAG,EAAA;AAAA,IACzB,gBAAA,EAAkB,KAAK,EAAG,EAAA;AAAA,IAC1B,WAAA,EAAa,KAAK,EAAG,EAAA;AAAA,IACrB,kBAAA,EAAoB,KAAK,EAAG,EAAA;AAAA,IAC5B,mBAAA,EAAqB,KAAK,EAAG,EAAA;AAAA,IAC7B,cAAA,EAAgB,KAAK,EAAG;AAAA,GACxB,CAAA,CAAA;AAAA,CAlCa,EAAA,cAAA,KAAA,cAAA,GAAA,EAAA,CAAA,CAAA;;;;"}
1
+ {"version":3,"file":"catalogApiMock.esm.js","sources":["../../src/testUtils/catalogApiMock.ts"],"sourcesContent":["/*\n * Copyright 2024 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 ApiFactory,\n ApiRef,\n createApiFactory,\n} from '@backstage/frontend-plugin-api';\nimport { InMemoryCatalogClient } from '@backstage/catalog-client/testUtils';\nimport { Entity } from '@backstage/catalog-model';\nimport { catalogApiRef } from '@backstage/plugin-catalog-react';\nimport { CatalogApi } from '@backstage/catalog-client';\nimport { ApiMock } from '@backstage/frontend-test-utils';\n\n/** @internal */\nfunction simpleMock<TApi>(\n ref: ApiRef<TApi>,\n mockFactory: () => jest.Mocked<TApi>,\n): (partialImpl?: Partial<TApi>) => ApiMock<TApi> {\n return partialImpl => {\n const mock = mockFactory();\n if (partialImpl) {\n for (const [key, impl] of Object.entries(partialImpl)) {\n if (typeof impl === 'function') {\n (mock as any)[key].mockImplementation(impl);\n } else {\n (mock as any)[key] = impl;\n }\n }\n }\n return Object.assign(mock, {\n factory: createApiFactory({\n api: ref,\n deps: {},\n factory: () => mock,\n }),\n }) as ApiMock<TApi>;\n };\n}\n\n/**\n * Creates a fake catalog client that handles entities in memory storage. Note\n * that this client may be severely limited in functionality, and advanced\n * functions may not be available at all.\n *\n * @public\n */\nexport function catalogApiMock(options?: { entities?: Entity[] }): CatalogApi {\n return new InMemoryCatalogClient(options);\n}\n\n/**\n * A collection of mock functionality for the catalog service.\n *\n * @public\n */\nexport namespace catalogApiMock {\n /**\n * Creates a fake catalog client that handles entities in memory storage. Note\n * that this client may be severely limited in functionality, and advanced\n * functions may not be available at all.\n */\n export const factory = (options?: {\n entities?: Entity[];\n }): ApiFactory<CatalogApi, CatalogApi, {}> =>\n createApiFactory({\n api: catalogApiRef,\n deps: {},\n factory: () => new InMemoryCatalogClient(options),\n });\n /**\n * Creates a catalog client whose methods are mock functions, possibly with\n * some of them overloaded by the caller.\n */\n export const mock = simpleMock(catalogApiRef, () => ({\n getEntities: jest.fn(),\n getEntitiesByRefs: jest.fn(),\n queryEntities: jest.fn(),\n getEntityAncestors: jest.fn(),\n getEntityByRef: jest.fn(),\n removeEntityByUid: jest.fn(),\n refreshEntity: jest.fn(),\n getEntityFacets: jest.fn(),\n getLocations: jest.fn(),\n getLocationById: jest.fn(),\n getLocationByRef: jest.fn(),\n addLocation: jest.fn(),\n removeLocationById: jest.fn(),\n getLocationByEntity: jest.fn(),\n validateEntity: jest.fn(),\n analyzeLocation: jest.fn(),\n }));\n}\n"],"names":["catalogApiMock"],"mappings":";;;;AA4BA,SAAS,UAAA,CACP,KACA,WACgD,EAAA;AAChD,EAAA,OAAO,CAAe,WAAA,KAAA;AACpB,IAAA,MAAM,OAAO,WAAY,EAAA;AACzB,IAAA,IAAI,WAAa,EAAA;AACf,MAAA,KAAA,MAAW,CAAC,GAAK,EAAA,IAAI,KAAK,MAAO,CAAA,OAAA,CAAQ,WAAW,CAAG,EAAA;AACrD,QAAI,IAAA,OAAO,SAAS,UAAY,EAAA;AAC9B,UAAC,IAAa,CAAA,GAAG,CAAE,CAAA,kBAAA,CAAmB,IAAI,CAAA;AAAA,SACrC,MAAA;AACL,UAAC,IAAA,CAAa,GAAG,CAAI,GAAA,IAAA;AAAA;AACvB;AACF;AAEF,IAAO,OAAA,MAAA,CAAO,OAAO,IAAM,EAAA;AAAA,MACzB,SAAS,gBAAiB,CAAA;AAAA,QACxB,GAAK,EAAA,GAAA;AAAA,QACL,MAAM,EAAC;AAAA,QACP,SAAS,MAAM;AAAA,OAChB;AAAA,KACF,CAAA;AAAA,GACH;AACF;AASO,SAAS,eAAe,OAA+C,EAAA;AAC5E,EAAO,OAAA,IAAI,sBAAsB,OAAO,CAAA;AAC1C;AAAA,CAOO,CAAUA,eAAV,KAAA;AAME,EAAMA,eAAA,CAAA,OAAA,GAAU,CAAC,OAAA,KAGtB,gBAAiB,CAAA;AAAA,IACf,GAAK,EAAA,aAAA;AAAA,IACL,MAAM,EAAC;AAAA,IACP,OAAS,EAAA,MAAM,IAAI,qBAAA,CAAsB,OAAO;AAAA,GACjD,CAAA;AAKI,EAAMA,eAAA,CAAA,IAAA,GAAO,UAAW,CAAA,aAAA,EAAe,OAAO;AAAA,IACnD,WAAA,EAAa,KAAK,EAAG,EAAA;AAAA,IACrB,iBAAA,EAAmB,KAAK,EAAG,EAAA;AAAA,IAC3B,aAAA,EAAe,KAAK,EAAG,EAAA;AAAA,IACvB,kBAAA,EAAoB,KAAK,EAAG,EAAA;AAAA,IAC5B,cAAA,EAAgB,KAAK,EAAG,EAAA;AAAA,IACxB,iBAAA,EAAmB,KAAK,EAAG,EAAA;AAAA,IAC3B,aAAA,EAAe,KAAK,EAAG,EAAA;AAAA,IACvB,eAAA,EAAiB,KAAK,EAAG,EAAA;AAAA,IACzB,YAAA,EAAc,KAAK,EAAG,EAAA;AAAA,IACtB,eAAA,EAAiB,KAAK,EAAG,EAAA;AAAA,IACzB,gBAAA,EAAkB,KAAK,EAAG,EAAA;AAAA,IAC1B,WAAA,EAAa,KAAK,EAAG,EAAA;AAAA,IACrB,kBAAA,EAAoB,KAAK,EAAG,EAAA;AAAA,IAC5B,mBAAA,EAAqB,KAAK,EAAG,EAAA;AAAA,IAC7B,cAAA,EAAgB,KAAK,EAAG,EAAA;AAAA,IACxB,eAAA,EAAiB,KAAK,EAAG;AAAA,GACzB,CAAA,CAAA;AAAA,CAnCa,EAAA,cAAA,KAAA,cAAA,GAAA,EAAA,CAAA,CAAA;;;;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backstage/plugin-catalog-react",
3
- "version": "1.19.2-next.0",
3
+ "version": "1.20.0-next.2",
4
4
  "description": "A frontend library that helps other Backstage plugins interact with the catalog",
5
5
  "backstage": {
6
6
  "role": "web-library",
@@ -73,14 +73,14 @@
73
73
  "test": "backstage-cli package test"
74
74
  },
75
75
  "dependencies": {
76
- "@backstage/catalog-client": "1.10.2",
76
+ "@backstage/catalog-client": "1.11.0-next.0",
77
77
  "@backstage/catalog-model": "1.7.5",
78
- "@backstage/core-compat-api": "0.4.5-next.0",
79
- "@backstage/core-components": "0.17.4",
78
+ "@backstage/core-compat-api": "0.5.0-next.2",
79
+ "@backstage/core-components": "0.17.5-next.1",
80
80
  "@backstage/core-plugin-api": "1.10.9",
81
81
  "@backstage/errors": "1.2.7",
82
- "@backstage/frontend-plugin-api": "0.10.4",
83
- "@backstage/frontend-test-utils": "0.3.5-next.0",
82
+ "@backstage/frontend-plugin-api": "0.11.0-next.1",
83
+ "@backstage/frontend-test-utils": "0.3.5-next.2",
84
84
  "@backstage/integration-react": "1.2.9",
85
85
  "@backstage/plugin-catalog-common": "1.1.5",
86
86
  "@backstage/plugin-permission-common": "0.9.1",
@@ -100,11 +100,11 @@
100
100
  "zen-observable": "^0.10.0"
101
101
  },
102
102
  "devDependencies": {
103
- "@backstage/cli": "0.33.1",
103
+ "@backstage/cli": "0.34.0-next.1",
104
104
  "@backstage/core-app-api": "1.18.0",
105
105
  "@backstage/plugin-catalog-common": "1.1.5",
106
- "@backstage/plugin-scaffolder-common": "1.6.0",
107
- "@backstage/test-utils": "1.7.10",
106
+ "@backstage/plugin-scaffolder-common": "1.7.0-next.0",
107
+ "@backstage/test-utils": "1.7.11-next.0",
108
108
  "@testing-library/dom": "^10.0.0",
109
109
  "@testing-library/jest-dom": "^6.0.0",
110
110
  "@testing-library/react": "^16.0.0",