@backstage/catalog-client 0.6.0 → 0.7.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,67 @@
1
1
  # @backstage/catalog-client
2
2
 
3
+ ## 0.7.2
4
+
5
+ ### Patch Changes
6
+
7
+ - ed09ad8093: Updated usage of the `LocationSpec` type from `@backstage/catalog-model`, which is deprecated.
8
+ - 46dee04eba: Deprecated `AddLocationRequest.presence`, as it is already being ignored.
9
+ - 6e1cbc12a6: Added `CatalogApi.getEntityFacets`. Marking this as a breaking change since it
10
+ is a non-optional addition to the API and depends on the backend being in place.
11
+ If you are mocking this interface in your tests, you will need to add an extra
12
+ `getEntityFacets: jest.fn()` or similar to that interface.
13
+ - Updated dependencies
14
+ - @backstage/catalog-model@0.11.0
15
+
16
+ ## 0.7.1
17
+
18
+ ### Patch Changes
19
+
20
+ - Fix for the previous release with missing type declarations.
21
+ - Updated dependencies
22
+ - @backstage/catalog-model@0.10.1
23
+ - @backstage/errors@0.2.2
24
+
25
+ ## 0.7.0
26
+
27
+ ### Minor Changes
28
+
29
+ - 8eda0e7a9c: **BREAKING**: Removed the explicit `DiscoveryApi` and `FetchApi` export symbols,
30
+ which were unnecessary duplicates from the well known core ones.
31
+
32
+ The `CATALOG_FILTER_EXISTS` symbol's value has changed. However, this should not
33
+ affect any code in practice.
34
+
35
+ - deaf6065db: Removed `CatalogApi.geLocationByEntity` and `CatalogApi.getOriginLocationByEntity`, and replaced them with `CatalogApi.getLocationByRef`.
36
+
37
+ If you were using one of the two old methods, you can update your code as follows:
38
+
39
+ ```diff
40
+ -const originLocation = catalogApi.getOriginLocationByEntity(entity);
41
+ +const originLocation = catalogApi.getLocationByRef(entity.metadata.annotations[ANNOTATION_ORIGIN_LOCATION]!);
42
+ -const location = catalogApi.getLocationByEntity(entity);
43
+ +const location = catalogApi.getLocationByRef(entity.metadata.annotations[ANNOTATION_LOCATION]!);
44
+ ```
45
+
46
+ ### Patch Changes
47
+
48
+ - 1ed305728b: Bump `node-fetch` to version 2.6.7 and `cross-fetch` to version 3.1.5
49
+ - c77c5c7eb6: Added `backstage.role` to `package.json`
50
+ - 216725b434: Updated to use new names for `parseLocationRef` and `stringifyLocationRef`
51
+ - 244d24ebc4: Export the `Location` type that was previously exported by the `@backstage/catalog-model` package.
52
+ - 538ca90790: Deprecated the following types used by the catalog client, and created new
53
+ corresponding types to make them more consistent:
54
+
55
+ - `CatalogEntitiesRequest` -> `GetEntitiesRequest`
56
+ - `CatalogListResponse` was removed and generally replaced with `GetEntitiesResponse` (which does not use a type parameter argument)
57
+ - `CatalogEntityAncestorsRequest`-> `GetEntityAncestorsRequest`
58
+ - `CatalogEntityAncestorsResponse` -> `GetEntityAncestorsResponse`
59
+
60
+ - 27eccab216: Replaces use of deprecated catalog-model constants.
61
+ - Updated dependencies
62
+ - @backstage/errors@0.2.1
63
+ - @backstage/catalog-model@0.10.0
64
+
3
65
  ## 0.6.0
4
66
 
5
67
  ### Minor Changes
package/dist/index.cjs.js CHANGED
@@ -10,7 +10,7 @@ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'defau
10
10
 
11
11
  var crossFetch__default = /*#__PURE__*/_interopDefaultLegacy(crossFetch);
12
12
 
13
- const CATALOG_FILTER_EXISTS = Symbol("CATALOG_FILTER_EXISTS");
13
+ const CATALOG_FILTER_EXISTS = Symbol.for("CATALOG_FILTER_EXISTS_0e15b590c0b343a2bae3e787e84c2111");
14
14
 
15
15
  class CatalogClient {
16
16
  constructor(options) {
@@ -26,9 +26,8 @@ class CatalogClient {
26
26
  }
27
27
  async getEntities(request, options) {
28
28
  const { filter = [], fields = [], offset, limit, after } = request != null ? request : {};
29
- const filterItems = [filter].flat();
30
29
  const params = [];
31
- for (const filterItem of filterItems) {
30
+ for (const filterItem of [filter].flat()) {
32
31
  const filterParts = [];
33
32
  for (const [key, value] of Object.entries(filterItem)) {
34
33
  for (const v of [value].flat()) {
@@ -91,6 +90,30 @@ class CatalogClient {
91
90
  throw new Error(await response.text());
92
91
  }
93
92
  }
93
+ async getEntityFacets(request, options) {
94
+ const { filter = [], facets } = request;
95
+ const params = [];
96
+ for (const filterItem of [filter].flat()) {
97
+ const filterParts = [];
98
+ for (const [key, value] of Object.entries(filterItem)) {
99
+ for (const v of [value].flat()) {
100
+ if (v === CATALOG_FILTER_EXISTS) {
101
+ filterParts.push(encodeURIComponent(key));
102
+ } else if (typeof v === "string") {
103
+ filterParts.push(`${encodeURIComponent(key)}=${encodeURIComponent(v)}`);
104
+ }
105
+ }
106
+ }
107
+ if (filterParts.length) {
108
+ params.push(`filter=${filterParts.join(",")}`);
109
+ }
110
+ }
111
+ for (const facet of facets) {
112
+ params.push(`facet=${encodeURIComponent(facet)}`);
113
+ }
114
+ const query = params.length ? `?${params.join("&")}` : "";
115
+ return await this.requestOptional("GET", `/entity-facets${query}`, options);
116
+ }
94
117
  async addLocation({ type = "url", target, dryRun, presence }, options) {
95
118
  const response = await this.fetchApi.fetch(`${await this.discoveryApi.getBaseUrl("catalog")}/locations${dryRun ? "?dryRun=true" : ""}`, {
96
119
  headers: {
@@ -115,21 +138,25 @@ class CatalogClient {
115
138
  }
116
139
  async getOriginLocationByEntity(entity, options) {
117
140
  var _a;
118
- const locationCompound = (_a = entity.metadata.annotations) == null ? void 0 : _a[catalogModel.ORIGIN_LOCATION_ANNOTATION];
141
+ const locationCompound = (_a = entity.metadata.annotations) == null ? void 0 : _a[catalogModel.ANNOTATION_ORIGIN_LOCATION];
119
142
  if (!locationCompound) {
120
143
  return void 0;
121
144
  }
122
145
  const all = await this.requestRequired("GET", "/locations", options);
123
- return all.map((r) => r.data).find((l) => locationCompound === catalogModel.stringifyLocationReference(l));
146
+ return all.map((r) => r.data).find((l) => locationCompound === catalogModel.stringifyLocationRef(l));
124
147
  }
125
148
  async getLocationByEntity(entity, options) {
126
149
  var _a;
127
- const locationCompound = (_a = entity.metadata.annotations) == null ? void 0 : _a[catalogModel.LOCATION_ANNOTATION];
150
+ const locationCompound = (_a = entity.metadata.annotations) == null ? void 0 : _a[catalogModel.ANNOTATION_LOCATION];
128
151
  if (!locationCompound) {
129
152
  return void 0;
130
153
  }
131
154
  const all = await this.requestRequired("GET", "/locations", options);
132
- return all.map((r) => r.data).find((l) => locationCompound === catalogModel.stringifyLocationReference(l));
155
+ return all.map((r) => r.data).find((l) => locationCompound === catalogModel.stringifyLocationRef(l));
156
+ }
157
+ async getLocationByRef(locationRef, options) {
158
+ const all = await this.requestRequired("GET", "/locations", options);
159
+ return all.map((r) => r.data).find((l) => locationRef === catalogModel.stringifyLocationRef(l));
133
160
  }
134
161
  async removeLocationById(id, options) {
135
162
  await this.requestIgnored("DELETE", `/locations/${encodeURIComponent(id)}`, options);
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs.js","sources":["../src/types/api.ts","../src/CatalogClient.ts","../src/types/status.ts"],"sourcesContent":["/*\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, EntityName, Location } from '@backstage/catalog-model';\n\n/**\n * A Symbol to define if a catalog filter exists or not.\n *\n * @public\n */\nexport const CATALOG_FILTER_EXISTS = Symbol('CATALOG_FILTER_EXISTS');\n\n/**\n * A request type for retrieving catalog Entities.\n *\n * @public\n */\nexport type CatalogEntitiesRequest = {\n /**\n * If given, return only entities that match the given patterns.\n *\n * @remarks\n *\n * If multiple filter sets are given as an array, then there is effectively an\n * OR between each filter set.\n *\n * Within one filter set, there is effectively an AND between the various\n * keys.\n *\n * Within one key, if there are more than one value, then there is effectively\n * an OR between them.\n *\n * Example: For an input of\n *\n * ```\n * [\n * { kind: ['API', 'Component'] },\n * { 'metadata.name': 'a', 'metadata.namespace': 'b' }\n * ]\n * ```\n *\n * This effectively means\n *\n * ```\n * (kind = EITHER 'API' OR 'Component')\n * OR\n * (metadata.name = 'a' AND metadata.namespace = 'b' )\n * ```\n *\n * Each key is a dot separated path in each object.\n *\n * As a value you can also pass in the symbol `CATALOG_FILTER_EXISTS`\n * (exported from this package), which means that you assert on the existence\n * of that key, no matter what its value is.\n */\n filter?:\n | Record<string, string | symbol | (string | symbol)[]>[]\n | Record<string, string | symbol | (string | symbol)[]>\n | undefined;\n /**\n * If given, return only the parts of each entity that match those dot\n * separated paths in each object.\n *\n * @remarks\n *\n * Example: For an input of `['kind', 'metadata.annotations']`, then response\n * objects will be shaped like\n *\n * ```\n * {\n * \"kind\": \"Component\",\n * \"metadata\": {\n * \"annotations\": {\n * \"foo\": \"bar\"\n * }\n * }\n * }\n * ```\n */\n fields?: string[] | undefined;\n /**\n * If given, skips over the first N items in the result set.\n */\n offset?: number;\n /**\n * If given, returns at most N items from the result set.\n */\n limit?: number;\n /**\n * If given, skips over all items before that cursor as returned by a previous\n * request.\n */\n after?: string;\n};\n\n/**\n * A request type for Catalog Entity Ancestor information.\n *\n * @public\n */\nexport type CatalogEntityAncestorsRequest = {\n entityRef: string;\n};\n\n/**\n * A response type for Catalog Entity Ancestor information.\n *\n * @public\n */\nexport type CatalogEntityAncestorsResponse = {\n rootEntityRef: string;\n items: { entity: Entity; parentEntityRefs: string[] }[];\n};\n\n/**\n * A response type for the result of a catalog operation in list form.\n *\n * @public\n */\nexport type CatalogListResponse<T> = {\n items: T[];\n};\n\n/**\n * Options you can pass into a catalog request for additional information.\n *\n * @public\n */\nexport type CatalogRequestOptions = {\n token?: string;\n};\n\n/**\n * Public functions for interacting with the Catalog API.\n *\n * @public\n */\nexport interface CatalogApi {\n /**\n * Gets the Entities from the catalog based on your request and options.\n *\n * @param request - An object with your filters and fields.\n * @param options - An object with your preferred options.\n *\n * @returns A CatalogListResponse with items typed Catalog Model Entity.\n *\n */\n getEntities(\n request?: CatalogEntitiesRequest,\n options?: CatalogRequestOptions,\n ): Promise<CatalogListResponse<Entity>>;\n /**\n * Gets the Entity ancestor information from the catalog based on your request and options.\n *\n * @param request - An object with your filters and fields.\n * @param options - An object with your preferred options.\n *\n * @returns A CatalogEntityAncestorsResponse.\n */\n getEntityAncestors(\n request: CatalogEntityAncestorsRequest,\n options?: CatalogRequestOptions,\n ): Promise<CatalogEntityAncestorsResponse>;\n /**\n * Gets a single Entity from the catalog by Entity name.\n *\n * @param name - A complete Entity name, with the full kind-namespace-name triplet.\n * @param options - An object with your preferred options.\n *\n * @returns A {@link catalog-model#Entity}.\n */\n getEntityByName(\n name: EntityName,\n options?: CatalogRequestOptions,\n ): Promise<Entity | undefined>;\n /**\n * Removes a single Entity from the catalog by Entity UID.\n *\n * @param uid - A string of the Entity UID.\n * @param options - An object with your preferred options.\n *\n */\n removeEntityByUid(\n uid: string,\n options?: CatalogRequestOptions,\n ): Promise<void>;\n /**\n * Refreshes an Entity in the catalog.\n *\n * @param entityRef - A string in the form of 'Kind/default:foo'.\n * @param options - An object with your preferred options.\n *\n */\n refreshEntity(\n entityRef: string,\n options?: CatalogRequestOptions,\n ): Promise<void>;\n\n // Locations\n /**\n * Gets a Location object by ID from the catalog.\n *\n * @param id - A string in of the Location Id.\n * @param options - An object with your preferred options.\n *\n * @returns A {@link catalog-model#Location_2}.\n */\n getLocationById(\n id: string,\n options?: CatalogRequestOptions,\n ): Promise<Location | undefined>;\n /**\n * Gets origin location by Entity.\n *\n * @param entity - An {@link catalog-model#Entity}.\n * @param options - An object with your preferred options.\n *\n * @returns A {@link catalog-model#Location_2}.\n */\n getOriginLocationByEntity(\n entity: Entity,\n options?: CatalogRequestOptions,\n ): Promise<Location | undefined>;\n /**\n * Gets Location by Entity.\n *\n * @param entity - An {@link catalog-model#Entity}.\n * @param options - An object with your preferred options.\n *\n * @returns A {@link catalog-model#Location_2}.\n */\n getLocationByEntity(\n entity: Entity,\n options?: CatalogRequestOptions,\n ): Promise<Location | undefined>;\n /**\n * Adds a Location.\n *\n * @param location - A request type for adding a Location to the catalog.\n * @param options - An object with your preferred options.\n *\n * @returns A AddLocationResponse.\n */\n addLocation(\n location: AddLocationRequest,\n options?: CatalogRequestOptions,\n ): Promise<AddLocationResponse>;\n /**\n * Removes a Location by Id.\n *\n * @param id - A string in of the Location Id.\n * @param options - An object with your preferred options.\n *\n */\n removeLocationById(\n id: string,\n options?: CatalogRequestOptions,\n ): Promise<void>;\n}\n\n/**\n * A request type for adding a Location to the catalog.\n *\n * @public\n */\nexport type AddLocationRequest = {\n type?: string;\n target: string;\n dryRun?: boolean;\n presence?: 'optional' | 'required';\n};\n\n/**\n * A response type for adding a Location to the catalog.\n *\n * @public\n */\nexport type AddLocationResponse = {\n location: Location;\n entities: Entity[];\n // Exists is only set in DryRun mode.\n exists?: boolean;\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 Location,\n LOCATION_ANNOTATION,\n ORIGIN_LOCATION_ANNOTATION,\n parseEntityRef,\n stringifyEntityRef,\n stringifyLocationReference,\n} from '@backstage/catalog-model';\nimport { ResponseError } from '@backstage/errors';\nimport crossFetch from 'cross-fetch';\nimport {\n CATALOG_FILTER_EXISTS,\n AddLocationRequest,\n AddLocationResponse,\n CatalogApi,\n CatalogEntitiesRequest,\n CatalogListResponse,\n CatalogRequestOptions,\n CatalogEntityAncestorsRequest,\n CatalogEntityAncestorsResponse,\n} from './types/api';\nimport { DiscoveryApi } from './types/discovery';\nimport { FetchApi } from './types/fetch';\n\n/**\n * A frontend and backend compatible client for communicating with the Backstage Catalog.\n *\n * @public\n * */\nexport class CatalogClient implements CatalogApi {\n private readonly discoveryApi: DiscoveryApi;\n private readonly fetchApi: FetchApi;\n\n constructor(options: { discoveryApi: DiscoveryApi; fetchApi?: FetchApi }) {\n this.discoveryApi = options.discoveryApi;\n this.fetchApi = options.fetchApi || { fetch: crossFetch };\n }\n\n /**\n * Gets the Ancestors of an Entity.\n *\n * @param request - A request type for retrieving Entity ancestors.\n * @param options - An object with your preferred options.\n *\n * @returns A CatalogEntityAncestorsResponse.\n *\n * @public\n */\n async getEntityAncestors(\n request: CatalogEntityAncestorsRequest,\n options?: CatalogRequestOptions,\n ): Promise<CatalogEntityAncestorsResponse> {\n const { kind, namespace, name } = parseEntityRef(request.entityRef);\n return await this.requestRequired(\n 'GET',\n `/entities/by-name/${encodeURIComponent(kind)}/${encodeURIComponent(\n namespace,\n )}/${encodeURIComponent(name)}/ancestry`,\n options,\n );\n }\n\n /**\n * Gets a Location by Id.\n *\n * @param id - A string containing the Id.\n * @param options - An object with your preferred options.\n *\n * @returns A {@link catalog-model#Location_2}.\n *\n * @public\n */\n async getLocationById(\n id: string,\n options?: CatalogRequestOptions,\n ): Promise<Location | undefined> {\n return await this.requestOptional(\n 'GET',\n `/locations/${encodeURIComponent(id)}`,\n options,\n );\n }\n\n /**\n * Gets a set of Entities.\n *\n * @param request - A request type for retrieving an Entity.\n * @param options - An object with your preferred options.\n *\n * @returns A CatalogListResponse.\n *\n * @public\n */\n async getEntities(\n request?: CatalogEntitiesRequest,\n options?: CatalogRequestOptions,\n ): Promise<CatalogListResponse<Entity>> {\n const { filter = [], fields = [], offset, limit, after } = request ?? {};\n const filterItems = [filter].flat();\n const params: string[] = [];\n\n // filter param can occur multiple times, for example\n // /api/catalog/entities?filter=metadata.name=wayback-search,kind=component&filter=metadata.name=www-artist,kind=component'\n // the \"outer array\" defined by `filter` occurrences corresponds to \"anyOf\" filters\n // the \"inner array\" defined within a `filter` param corresponds to \"allOf\" filters\n for (const filterItem of filterItems) {\n const filterParts: string[] = [];\n for (const [key, value] of Object.entries(filterItem)) {\n for (const v of [value].flat()) {\n if (v === CATALOG_FILTER_EXISTS) {\n filterParts.push(encodeURIComponent(key));\n } else if (typeof v === 'string') {\n filterParts.push(\n `${encodeURIComponent(key)}=${encodeURIComponent(v)}`,\n );\n }\n }\n }\n\n if (filterParts.length) {\n params.push(`filter=${filterParts.join(',')}`);\n }\n }\n\n if (fields.length) {\n params.push(`fields=${fields.map(encodeURIComponent).join(',')}`);\n }\n\n if (offset !== undefined) {\n params.push(`offset=${offset}`);\n }\n if (limit !== undefined) {\n params.push(`limit=${limit}`);\n }\n if (after !== undefined) {\n params.push(`after=${encodeURIComponent(after)}`);\n }\n\n const query = params.length ? `?${params.join('&')}` : '';\n const entities: Entity[] = await this.requestRequired(\n 'GET',\n `/entities${query}`,\n options,\n );\n\n const refCompare = (a: Entity, b: Entity) => {\n // in case field filtering is used, these fields might not be part of the response\n if (\n a.metadata?.name === undefined ||\n a.kind === undefined ||\n b.metadata?.name === undefined ||\n b.kind === undefined\n ) {\n return 0;\n }\n\n const aRef = stringifyEntityRef(a);\n const bRef = stringifyEntityRef(b);\n if (aRef < bRef) {\n return -1;\n }\n if (aRef > bRef) {\n return 1;\n }\n return 0;\n };\n\n return { items: entities.sort(refCompare) };\n }\n\n /**\n * Gets a given Entity based on a provided name.\n *\n * @param compoundName - A string containing the name.\n * @param options - An object with your preferred options.\n *\n * @returns An {@link catalog-model#Entity}.\n *\n * @public\n */\n async getEntityByName(\n compoundName: EntityName,\n options?: CatalogRequestOptions,\n ): Promise<Entity | undefined> {\n const { kind, namespace = 'default', name } = compoundName;\n return this.requestOptional(\n 'GET',\n `/entities/by-name/${encodeURIComponent(kind)}/${encodeURIComponent(\n namespace,\n )}/${encodeURIComponent(name)}`,\n options,\n );\n }\n\n /**\n * Refreshes an Entity.\n *\n * @param entityRef - A string containing the entityREf\n * @param options - An object with your preferred options.\n *\n * @public\n */\n async refreshEntity(entityRef: string, options?: CatalogRequestOptions) {\n const response = await this.fetchApi.fetch(\n `${await this.discoveryApi.getBaseUrl('catalog')}/refresh`,\n {\n headers: {\n 'Content-Type': 'application/json',\n ...(options?.token && { Authorization: `Bearer ${options?.token}` }),\n },\n method: 'POST',\n body: JSON.stringify({ entityRef }),\n },\n );\n\n if (response.status !== 200) {\n throw new Error(await response.text());\n }\n }\n\n /**\n * Adds a location.\n *\n * @param options - An object with your preferred options.\n * @param AddLocationRequest - A request object for adding locations.\n *\n * @returns An AddLocationResponse\n *\n * @public\n */\n async addLocation(\n { type = 'url', target, dryRun, presence }: AddLocationRequest,\n options?: CatalogRequestOptions,\n ): Promise<AddLocationResponse> {\n const response = await this.fetchApi.fetch(\n `${await this.discoveryApi.getBaseUrl('catalog')}/locations${\n dryRun ? '?dryRun=true' : ''\n }`,\n {\n headers: {\n 'Content-Type': 'application/json',\n ...(options?.token && { Authorization: `Bearer ${options?.token}` }),\n },\n method: 'POST',\n body: JSON.stringify({ type, target, presence }),\n },\n );\n\n if (response.status !== 201) {\n throw new Error(await response.text());\n }\n\n const { location, entities, exists } = await response.json();\n\n if (!location) {\n throw new Error(`Location wasn't added: ${target}`);\n }\n\n return {\n location,\n entities,\n exists,\n };\n }\n\n /**\n * Gets an origin Location By Entity.\n *\n * @param entity - An Entity\n * @param options - An object with your preferred options.\n *\n * @returns A {@link catalog-model#Location_2}.\n *\n * @public\n */\n async getOriginLocationByEntity(\n entity: Entity,\n options?: CatalogRequestOptions,\n ): Promise<Location | undefined> {\n const locationCompound =\n entity.metadata.annotations?.[ORIGIN_LOCATION_ANNOTATION];\n if (!locationCompound) {\n return undefined;\n }\n const all: { data: Location }[] = await this.requestRequired(\n 'GET',\n '/locations',\n options,\n );\n return all\n .map(r => r.data)\n .find(l => locationCompound === stringifyLocationReference(l));\n }\n\n /**\n * Gets a Location by Entity.\n *\n * @param entity - An Entity\n * @param options - An object with your preferred options.\n *\n * @returns A {@link catalog-model#Location_2}.\n *\n * @public\n */\n async getLocationByEntity(\n entity: Entity,\n options?: CatalogRequestOptions,\n ): Promise<Location | undefined> {\n const locationCompound = entity.metadata.annotations?.[LOCATION_ANNOTATION];\n if (!locationCompound) {\n return undefined;\n }\n const all: { data: Location }[] = await this.requestRequired(\n 'GET',\n '/locations',\n options,\n );\n return all\n .map(r => r.data)\n .find(l => locationCompound === stringifyLocationReference(l));\n }\n\n /**\n * Removes a location as identified by Id.\n *\n * @param id - A string containing the Id\n * @param options - An object with your preferred options.\n *\n * @public\n */\n async removeLocationById(\n id: string,\n options?: CatalogRequestOptions,\n ): Promise<void> {\n await this.requestIgnored(\n 'DELETE',\n `/locations/${encodeURIComponent(id)}`,\n options,\n );\n }\n\n /**\n * Removes an Entity as identified by Uid.\n *\n * @param uid - A string containing the Uid\n * @param options - An object with your preferred options.\n *\n * @public\n */\n async removeEntityByUid(\n uid: string,\n options?: CatalogRequestOptions,\n ): Promise<void> {\n await this.requestIgnored(\n 'DELETE',\n `/entities/by-uid/${encodeURIComponent(uid)}`,\n options,\n );\n }\n\n //\n // Private methods\n //\n\n private async requestIgnored(\n method: string,\n path: string,\n options?: CatalogRequestOptions,\n ): Promise<void> {\n const url = `${await this.discoveryApi.getBaseUrl('catalog')}${path}`;\n const headers: Record<string, string> = options?.token\n ? { Authorization: `Bearer ${options.token}` }\n : {};\n const response = await this.fetchApi.fetch(url, { method, headers });\n\n if (!response.ok) {\n throw await ResponseError.fromResponse(response);\n }\n }\n\n private async requestRequired(\n method: string,\n path: string,\n options?: CatalogRequestOptions,\n ): Promise<any> {\n const url = `${await this.discoveryApi.getBaseUrl('catalog')}${path}`;\n const headers: Record<string, string> = options?.token\n ? { Authorization: `Bearer ${options.token}` }\n : {};\n const response = await this.fetchApi.fetch(url, { method, headers });\n\n if (!response.ok) {\n throw await ResponseError.fromResponse(response);\n }\n\n return await response.json();\n }\n\n private async requestOptional(\n method: string,\n path: string,\n options?: CatalogRequestOptions,\n ): Promise<any | undefined> {\n const url = `${await this.discoveryApi.getBaseUrl('catalog')}${path}`;\n const headers: Record<string, string> = options?.token\n ? { Authorization: `Bearer ${options.token}` }\n : {};\n const response = await this.fetchApi.fetch(url, { method, headers });\n\n if (!response.ok) {\n if (response.status === 404) {\n return undefined;\n }\n throw await ResponseError.fromResponse(response);\n }\n\n return await response.json();\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\n/**\n * The entity `status.items[].type` for the status of the processing engine in\n * regards to an entity.\n *\n * @public\n */\nexport const ENTITY_STATUS_CATALOG_PROCESSING_TYPE =\n 'backstage.io/catalog-processing';\n"],"names":["crossFetch","parseEntityRef","stringifyEntityRef","ORIGIN_LOCATION_ANNOTATION","stringifyLocationReference","LOCATION_ANNOTATION","ResponseError"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs.js","sources":["../src/types/api.ts","../src/CatalogClient.ts","../src/types/status.ts"],"sourcesContent":["/*\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, EntityName } from '@backstage/catalog-model';\n\n/**\n * This symbol can be used in place of a value when passed to filters in e.g.\n * {@link CatalogClient.getEntities}, to signify that you want to filter on the\n * presence of that key no matter what its value is.\n *\n * @public\n */\nexport const CATALOG_FILTER_EXISTS = Symbol.for(\n // Random UUID to ensure no collisions\n 'CATALOG_FILTER_EXISTS_0e15b590c0b343a2bae3e787e84c2111',\n);\n\n/**\n * The request type for {@link CatalogClient.getEntities}.\n *\n * @public\n */\nexport interface GetEntitiesRequest {\n /**\n * If given, return only entities that match the given patterns.\n *\n * @remarks\n *\n * If multiple filter sets are given as an array, then there is effectively an\n * OR between each filter set.\n *\n * Within one filter set, there is effectively an AND between the various\n * keys.\n *\n * Within one key, if there are more than one value, then there is effectively\n * an OR between them.\n *\n * Example: For an input of\n *\n * ```\n * [\n * { kind: ['API', 'Component'] },\n * { 'metadata.name': 'a', 'metadata.namespace': 'b' }\n * ]\n * ```\n *\n * This effectively means\n *\n * ```\n * (kind = EITHER 'API' OR 'Component')\n * OR\n * (metadata.name = 'a' AND metadata.namespace = 'b' )\n * ```\n *\n * Each key is a dot separated path in each object.\n *\n * As a value you can also pass in the symbol `CATALOG_FILTER_EXISTS`\n * (exported from this package), which means that you assert on the existence\n * of that key, no matter what its value is.\n */\n filter?:\n | Record<string, string | symbol | (string | symbol)[]>[]\n | Record<string, string | symbol | (string | symbol)[]>\n | undefined;\n /**\n * If given, return only the parts of each entity that match those dot\n * separated paths in each object.\n *\n * @remarks\n *\n * Example: For an input of `['kind', 'metadata.annotations']`, then response\n * objects will be shaped like\n *\n * ```\n * {\n * \"kind\": \"Component\",\n * \"metadata\": {\n * \"annotations\": {\n * \"foo\": \"bar\"\n * }\n * }\n * }\n * ```\n */\n fields?: string[] | undefined;\n /**\n * If given, skips over the first N items in the result set.\n */\n offset?: number;\n /**\n * If given, returns at most N items from the result set.\n */\n limit?: number;\n /**\n * If given, skips over all items before that cursor as returned by a previous\n * request.\n */\n after?: string;\n}\n\n/**\n * The response type for {@link CatalogClient.getEntities}.\n *\n * @public\n */\nexport interface GetEntitiesResponse {\n items: Entity[];\n}\n\n/**\n * The request type for {@link CatalogClient.getEntityAncestors}.\n *\n * @public\n */\nexport interface GetEntityAncestorsRequest {\n entityRef: string;\n}\n\n/**\n * The response type for {@link CatalogClient.getEntityAncestors}.\n *\n * @public\n */\nexport interface GetEntityAncestorsResponse {\n rootEntityRef: string;\n items: Array<{\n entity: Entity;\n parentEntityRefs: string[];\n }>;\n}\n\n/**\n * The request type for {@link CatalogClient.getEntityFacets}.\n *\n * @public\n */\nexport interface GetEntityFacetsRequest {\n /**\n * If given, return only entities that match the given patterns.\n *\n * @remarks\n *\n * If multiple filter sets are given as an array, then there is effectively an\n * OR between each filter set.\n *\n * Within one filter set, there is effectively an AND between the various\n * keys.\n *\n * Within one key, if there are more than one value, then there is effectively\n * an OR between them.\n *\n * Example: For an input of\n *\n * ```\n * [\n * { kind: ['API', 'Component'] },\n * { 'metadata.name': 'a', 'metadata.namespace': 'b' }\n * ]\n * ```\n *\n * This effectively means\n *\n * ```\n * (kind = EITHER 'API' OR 'Component')\n * OR\n * (metadata.name = 'a' AND metadata.namespace = 'b' )\n * ```\n *\n * Each key is a dot separated path in each object.\n *\n * As a value you can also pass in the symbol `CATALOG_FILTER_EXISTS`\n * (exported from this package), which means that you assert on the existence\n * of that key, no matter what its value is.\n */\n filter?:\n | Record<string, string | symbol | (string | symbol)[]>[]\n | Record<string, string | symbol | (string | symbol)[]>\n | undefined;\n /**\n * Dot separated paths for the facets to extract from each entity.\n *\n * @remarks\n *\n * Example: For an input of `['kind', 'metadata.annotations.backstage.io/orphan']`, then the\n * response will be shaped like\n *\n * ```\n * {\n * \"facets\": {\n * \"kind\": [\n * { \"key\": \"Component\", \"count\": 22 },\n * { \"key\": \"API\", \"count\": 13 }\n * ],\n * \"metadata.annotations.backstage.io/orphan\": [\n * { \"key\": \"true\", \"count\": 2 }\n * ]\n * }\n * }\n * ```\n */\n facets: string[];\n}\n\n/**\n * The response type for {@link CatalogClient.getEntityFacets}.\n *\n * @public\n */\nexport interface GetEntityFacetsResponse {\n /**\n * The computed facets, one entry per facet in the request.\n */\n facets: Record<string, Array<{ value: string; count: number }>>;\n}\n\n/**\n * Options you can pass into a catalog request for additional information.\n *\n * @public\n */\nexport interface CatalogRequestOptions {\n token?: string;\n}\n\n/**\n * Entity location for a specific entity.\n *\n * @public\n */\nexport type Location = {\n id: string;\n type: string;\n target: string;\n /** @deprecated This field is is ignored */\n presence?: 'optional' | 'required';\n};\n\n/**\n * The request type for {@link CatalogClient.addLocation}.\n *\n * @public\n */\nexport type AddLocationRequest = {\n type?: string;\n target: string;\n dryRun?: boolean;\n /** @deprecated This field is is ignored */\n presence?: 'optional' | 'required';\n};\n\n/**\n * The response type for {@link CatalogClient.addLocation}.\n *\n * @public\n */\nexport type AddLocationResponse = {\n location: Location;\n entities: Entity[];\n // Only set in dryRun mode.\n exists?: boolean;\n};\n\n/**\n * A client for interacting with the Backstage software catalog through its API.\n *\n * @public\n */\nexport interface CatalogApi {\n /**\n * Lists catalog entities.\n *\n * @param request - Request parameters\n * @param options - Additional options\n */\n getEntities(\n request?: GetEntitiesRequest,\n options?: CatalogRequestOptions,\n ): Promise<GetEntitiesResponse>;\n\n /**\n * Gets entity ancestor information, i.e. the hierarchy of parent entities\n * whose processing resulted in a given entity appearing in the catalog.\n *\n * @param request - Request parameters\n * @param options - Additional options\n */\n getEntityAncestors(\n request: GetEntityAncestorsRequest,\n options?: CatalogRequestOptions,\n ): Promise<GetEntityAncestorsResponse>;\n\n /**\n * Gets a single entity from the catalog by its ref (kind, namespace, name)\n * triplet.\n *\n * @param name - A complete entity ref\n * @param options - Additional options\n */\n getEntityByName(\n name: EntityName,\n options?: CatalogRequestOptions,\n ): Promise<Entity | undefined>;\n\n /**\n * Removes a single entity from the catalog by entity UID.\n *\n * @param uid - An entity UID\n * @param options - Additional options\n */\n removeEntityByUid(\n uid: string,\n options?: CatalogRequestOptions,\n ): Promise<void>;\n\n /**\n * Refreshes (marks for reprocessing) an entity in the catalog.\n *\n * @param entityRef - An entity ref on string form (e.g.\n * 'component/default:my-component')\n * @param options - Additional options\n */\n refreshEntity(\n entityRef: string,\n options?: CatalogRequestOptions,\n ): Promise<void>;\n\n /**\n * Gets a summary of field facets of entities in the catalog.\n *\n * @param request - Request parameters\n * @param options - Additional options\n */\n getEntityFacets(\n request: GetEntityFacetsRequest,\n options?: CatalogRequestOptions,\n ): Promise<GetEntityFacetsResponse>;\n\n // Locations\n\n /**\n * Gets a registered location by its ID.\n *\n * @param id - A location ID\n * @param options - Additional options\n */\n getLocationById(\n id: string,\n options?: CatalogRequestOptions,\n ): Promise<Location | undefined>;\n\n /**\n * Gets a registered location by its ref.\n *\n * @param locationRef - A location ref, e.g. \"url:https://github.com/...\"\n * @param options - Additional options\n */\n getLocationByRef(\n locationRef: string,\n options?: CatalogRequestOptions,\n ): Promise<Location | undefined>;\n\n /**\n * Registers a new location.\n *\n * @param location - Request parameters\n * @param options - Additional options\n */\n addLocation(\n location: AddLocationRequest,\n options?: CatalogRequestOptions,\n ): Promise<AddLocationResponse>;\n\n /**\n * Removes a registered Location by its ID.\n *\n * @param id - A location ID\n * @param options - Additional options\n */\n removeLocationById(\n id: string,\n options?: CatalogRequestOptions,\n ): Promise<void>;\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 ANNOTATION_LOCATION,\n ANNOTATION_ORIGIN_LOCATION,\n Entity,\n EntityName,\n parseEntityRef,\n stringifyEntityRef,\n stringifyLocationRef,\n} from '@backstage/catalog-model';\nimport { ResponseError } from '@backstage/errors';\nimport crossFetch from 'cross-fetch';\nimport {\n CATALOG_FILTER_EXISTS,\n AddLocationRequest,\n AddLocationResponse,\n CatalogApi,\n GetEntitiesRequest,\n GetEntitiesResponse,\n CatalogRequestOptions,\n GetEntityAncestorsRequest,\n GetEntityAncestorsResponse,\n Location,\n GetEntityFacetsRequest,\n GetEntityFacetsResponse,\n} from './types/api';\nimport { DiscoveryApi } from './types/discovery';\nimport { FetchApi } from './types/fetch';\n\n/**\n * A frontend and backend compatible client for communicating with the Backstage\n * software catalog.\n *\n * @public\n */\nexport class CatalogClient implements CatalogApi {\n private readonly discoveryApi: DiscoveryApi;\n private readonly fetchApi: FetchApi;\n\n constructor(options: {\n discoveryApi: { getBaseUrl(pluginId: string): Promise<string> };\n fetchApi?: { fetch: typeof fetch };\n }) {\n this.discoveryApi = options.discoveryApi;\n this.fetchApi = options.fetchApi || { fetch: crossFetch };\n }\n\n /**\n * {@inheritdoc CatalogApi.getEntityAncestors}\n */\n async getEntityAncestors(\n request: GetEntityAncestorsRequest,\n options?: CatalogRequestOptions,\n ): Promise<GetEntityAncestorsResponse> {\n const { kind, namespace, name } = parseEntityRef(request.entityRef);\n return await this.requestRequired(\n 'GET',\n `/entities/by-name/${encodeURIComponent(kind)}/${encodeURIComponent(\n namespace,\n )}/${encodeURIComponent(name)}/ancestry`,\n options,\n );\n }\n\n /**\n * {@inheritdoc CatalogApi.getLocationById}\n */\n async getLocationById(\n id: string,\n options?: CatalogRequestOptions,\n ): Promise<Location | undefined> {\n return await this.requestOptional(\n 'GET',\n `/locations/${encodeURIComponent(id)}`,\n options,\n );\n }\n\n /**\n * {@inheritdoc CatalogApi.getEntities}\n */\n async getEntities(\n request?: GetEntitiesRequest,\n options?: CatalogRequestOptions,\n ): Promise<GetEntitiesResponse> {\n const { filter = [], fields = [], offset, limit, after } = request ?? {};\n const params: string[] = [];\n\n // filter param can occur multiple times, for example\n // /api/catalog/entities?filter=metadata.name=wayback-search,kind=component&filter=metadata.name=www-artist,kind=component'\n // the \"outer array\" defined by `filter` occurrences corresponds to \"anyOf\" filters\n // the \"inner array\" defined within a `filter` param corresponds to \"allOf\" filters\n for (const filterItem of [filter].flat()) {\n const filterParts: string[] = [];\n for (const [key, value] of Object.entries(filterItem)) {\n for (const v of [value].flat()) {\n if (v === CATALOG_FILTER_EXISTS) {\n filterParts.push(encodeURIComponent(key));\n } else if (typeof v === 'string') {\n filterParts.push(\n `${encodeURIComponent(key)}=${encodeURIComponent(v)}`,\n );\n }\n }\n }\n\n if (filterParts.length) {\n params.push(`filter=${filterParts.join(',')}`);\n }\n }\n\n if (fields.length) {\n params.push(`fields=${fields.map(encodeURIComponent).join(',')}`);\n }\n\n if (offset !== undefined) {\n params.push(`offset=${offset}`);\n }\n if (limit !== undefined) {\n params.push(`limit=${limit}`);\n }\n if (after !== undefined) {\n params.push(`after=${encodeURIComponent(after)}`);\n }\n\n const query = params.length ? `?${params.join('&')}` : '';\n const entities: Entity[] = await this.requestRequired(\n 'GET',\n `/entities${query}`,\n options,\n );\n\n const refCompare = (a: Entity, b: Entity) => {\n // in case field filtering is used, these fields might not be part of the response\n if (\n a.metadata?.name === undefined ||\n a.kind === undefined ||\n b.metadata?.name === undefined ||\n b.kind === undefined\n ) {\n return 0;\n }\n\n const aRef = stringifyEntityRef(a);\n const bRef = stringifyEntityRef(b);\n if (aRef < bRef) {\n return -1;\n }\n if (aRef > bRef) {\n return 1;\n }\n return 0;\n };\n\n return { items: entities.sort(refCompare) };\n }\n\n /**\n * {@inheritdoc CatalogApi.getEntityByName}\n */\n async getEntityByName(\n compoundName: EntityName,\n options?: CatalogRequestOptions,\n ): Promise<Entity | undefined> {\n const { kind, namespace = 'default', name } = compoundName;\n return this.requestOptional(\n 'GET',\n `/entities/by-name/${encodeURIComponent(kind)}/${encodeURIComponent(\n namespace,\n )}/${encodeURIComponent(name)}`,\n options,\n );\n }\n\n /**\n * {@inheritdoc CatalogApi.refreshEntity}\n */\n async refreshEntity(entityRef: string, options?: CatalogRequestOptions) {\n const response = await this.fetchApi.fetch(\n `${await this.discoveryApi.getBaseUrl('catalog')}/refresh`,\n {\n headers: {\n 'Content-Type': 'application/json',\n ...(options?.token && { Authorization: `Bearer ${options?.token}` }),\n },\n method: 'POST',\n body: JSON.stringify({ entityRef }),\n },\n );\n\n if (response.status !== 200) {\n throw new Error(await response.text());\n }\n }\n\n /**\n * {@inheritdoc CatalogApi.getEntityFacets}\n */\n async getEntityFacets(\n request: GetEntityFacetsRequest,\n options?: CatalogRequestOptions,\n ): Promise<GetEntityFacetsResponse> {\n const { filter = [], facets } = request;\n const params: string[] = [];\n\n // filter param can occur multiple times, for example\n // /api/catalog/entities?filter=metadata.name=wayback-search,kind=component&filter=metadata.name=www-artist,kind=component'\n // the \"outer array\" defined by `filter` occurrences corresponds to \"anyOf\" filters\n // the \"inner array\" defined within a `filter` param corresponds to \"allOf\" filters\n for (const filterItem of [filter].flat()) {\n const filterParts: string[] = [];\n for (const [key, value] of Object.entries(filterItem)) {\n for (const v of [value].flat()) {\n if (v === CATALOG_FILTER_EXISTS) {\n filterParts.push(encodeURIComponent(key));\n } else if (typeof v === 'string') {\n filterParts.push(\n `${encodeURIComponent(key)}=${encodeURIComponent(v)}`,\n );\n }\n }\n }\n\n if (filterParts.length) {\n params.push(`filter=${filterParts.join(',')}`);\n }\n }\n\n for (const facet of facets) {\n params.push(`facet=${encodeURIComponent(facet)}`);\n }\n\n const query = params.length ? `?${params.join('&')}` : '';\n return await this.requestOptional('GET', `/entity-facets${query}`, options);\n }\n\n /**\n * {@inheritdoc CatalogApi.addLocation}\n */\n async addLocation(\n { type = 'url', target, dryRun, presence }: AddLocationRequest,\n options?: CatalogRequestOptions,\n ): Promise<AddLocationResponse> {\n const response = await this.fetchApi.fetch(\n `${await this.discoveryApi.getBaseUrl('catalog')}/locations${\n dryRun ? '?dryRun=true' : ''\n }`,\n {\n headers: {\n 'Content-Type': 'application/json',\n ...(options?.token && { Authorization: `Bearer ${options?.token}` }),\n },\n method: 'POST',\n body: JSON.stringify({ type, target, presence }),\n },\n );\n\n if (response.status !== 201) {\n throw new Error(await response.text());\n }\n\n const { location, entities, exists } = await response.json();\n\n if (!location) {\n throw new Error(`Location wasn't added: ${target}`);\n }\n\n return {\n location,\n entities,\n exists,\n };\n }\n\n /**\n * @deprecated please use getLocationByRef instead\n */\n async getOriginLocationByEntity(\n entity: Entity,\n options?: CatalogRequestOptions,\n ): Promise<Location | undefined> {\n const locationCompound =\n entity.metadata.annotations?.[ANNOTATION_ORIGIN_LOCATION];\n if (!locationCompound) {\n return undefined;\n }\n const all: { data: Location }[] = await this.requestRequired(\n 'GET',\n '/locations',\n options,\n );\n return all\n .map(r => r.data)\n .find(l => locationCompound === stringifyLocationRef(l));\n }\n\n /**\n * @deprecated please use getLocationByRef instead\n */\n async getLocationByEntity(\n entity: Entity,\n options?: CatalogRequestOptions,\n ): Promise<Location | undefined> {\n const locationCompound = entity.metadata.annotations?.[ANNOTATION_LOCATION];\n if (!locationCompound) {\n return undefined;\n }\n const all: { data: Location }[] = await this.requestRequired(\n 'GET',\n '/locations',\n options,\n );\n return all\n .map(r => r.data)\n .find(l => locationCompound === stringifyLocationRef(l));\n }\n\n /**\n * {@inheritdoc CatalogApi.getLocationByRef}\n */\n async getLocationByRef(\n locationRef: string,\n options?: CatalogRequestOptions,\n ): Promise<Location | undefined> {\n const all: { data: Location }[] = await this.requestRequired(\n 'GET',\n '/locations',\n options,\n );\n return all\n .map(r => r.data)\n .find(l => locationRef === stringifyLocationRef(l));\n }\n\n /**\n * {@inheritdoc CatalogApi.removeLocationById}\n */\n async removeLocationById(\n id: string,\n options?: CatalogRequestOptions,\n ): Promise<void> {\n await this.requestIgnored(\n 'DELETE',\n `/locations/${encodeURIComponent(id)}`,\n options,\n );\n }\n\n /**\n * {@inheritdoc CatalogApi.removeEntityByUid}\n */\n async removeEntityByUid(\n uid: string,\n options?: CatalogRequestOptions,\n ): Promise<void> {\n await this.requestIgnored(\n 'DELETE',\n `/entities/by-uid/${encodeURIComponent(uid)}`,\n options,\n );\n }\n\n //\n // Private methods\n //\n\n private async requestIgnored(\n method: string,\n path: string,\n options?: CatalogRequestOptions,\n ): Promise<void> {\n const url = `${await this.discoveryApi.getBaseUrl('catalog')}${path}`;\n const headers: Record<string, string> = options?.token\n ? { Authorization: `Bearer ${options.token}` }\n : {};\n const response = await this.fetchApi.fetch(url, { method, headers });\n\n if (!response.ok) {\n throw await ResponseError.fromResponse(response);\n }\n }\n\n private async requestRequired(\n method: string,\n path: string,\n options?: CatalogRequestOptions,\n ): Promise<any> {\n const url = `${await this.discoveryApi.getBaseUrl('catalog')}${path}`;\n const headers: Record<string, string> = options?.token\n ? { Authorization: `Bearer ${options.token}` }\n : {};\n const response = await this.fetchApi.fetch(url, { method, headers });\n\n if (!response.ok) {\n throw await ResponseError.fromResponse(response);\n }\n\n return await response.json();\n }\n\n private async requestOptional(\n method: string,\n path: string,\n options?: CatalogRequestOptions,\n ): Promise<any | undefined> {\n const url = `${await this.discoveryApi.getBaseUrl('catalog')}${path}`;\n const headers: Record<string, string> = options?.token\n ? { Authorization: `Bearer ${options.token}` }\n : {};\n const response = await this.fetchApi.fetch(url, { method, headers });\n\n if (!response.ok) {\n if (response.status === 404) {\n return undefined;\n }\n throw await ResponseError.fromResponse(response);\n }\n\n return await response.json();\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\n/**\n * The entity `status.items[].type` for the status of the processing engine in\n * regards to an entity.\n *\n * @public\n */\nexport const ENTITY_STATUS_CATALOG_PROCESSING_TYPE =\n 'backstage.io/catalog-processing';\n"],"names":["crossFetch","parseEntityRef","stringifyEntityRef","ANNOTATION_ORIGIN_LOCATION","stringifyLocationRef","ANNOTATION_LOCATION","ResponseError"],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,17 +1,19 @@
1
- import { Entity, EntityName, Location } from '@backstage/catalog-model';
1
+ import { Entity, EntityName } from '@backstage/catalog-model';
2
2
 
3
3
  /**
4
- * A Symbol to define if a catalog filter exists or not.
4
+ * This symbol can be used in place of a value when passed to filters in e.g.
5
+ * {@link CatalogClient.getEntities}, to signify that you want to filter on the
6
+ * presence of that key no matter what its value is.
5
7
  *
6
8
  * @public
7
9
  */
8
10
  declare const CATALOG_FILTER_EXISTS: unique symbol;
9
11
  /**
10
- * A request type for retrieving catalog Entities.
12
+ * The request type for {@link CatalogClient.getEntities}.
11
13
  *
12
14
  * @public
13
15
  */
14
- declare type CatalogEntitiesRequest = {
16
+ interface GetEntitiesRequest {
15
17
  /**
16
18
  * If given, return only entities that match the given patterns.
17
19
  *
@@ -84,292 +86,303 @@ declare type CatalogEntitiesRequest = {
84
86
  * request.
85
87
  */
86
88
  after?: string;
87
- };
89
+ }
90
+ /**
91
+ * The response type for {@link CatalogClient.getEntities}.
92
+ *
93
+ * @public
94
+ */
95
+ interface GetEntitiesResponse {
96
+ items: Entity[];
97
+ }
88
98
  /**
89
- * A request type for Catalog Entity Ancestor information.
99
+ * The request type for {@link CatalogClient.getEntityAncestors}.
90
100
  *
91
101
  * @public
92
102
  */
93
- declare type CatalogEntityAncestorsRequest = {
103
+ interface GetEntityAncestorsRequest {
94
104
  entityRef: string;
95
- };
105
+ }
96
106
  /**
97
- * A response type for Catalog Entity Ancestor information.
107
+ * The response type for {@link CatalogClient.getEntityAncestors}.
98
108
  *
99
109
  * @public
100
110
  */
101
- declare type CatalogEntityAncestorsResponse = {
111
+ interface GetEntityAncestorsResponse {
102
112
  rootEntityRef: string;
103
- items: {
113
+ items: Array<{
104
114
  entity: Entity;
105
115
  parentEntityRefs: string[];
106
- }[];
107
- };
116
+ }>;
117
+ }
108
118
  /**
109
- * A response type for the result of a catalog operation in list form.
119
+ * The request type for {@link CatalogClient.getEntityFacets}.
110
120
  *
111
121
  * @public
112
122
  */
113
- declare type CatalogListResponse<T> = {
114
- items: T[];
115
- };
123
+ interface GetEntityFacetsRequest {
124
+ /**
125
+ * If given, return only entities that match the given patterns.
126
+ *
127
+ * @remarks
128
+ *
129
+ * If multiple filter sets are given as an array, then there is effectively an
130
+ * OR between each filter set.
131
+ *
132
+ * Within one filter set, there is effectively an AND between the various
133
+ * keys.
134
+ *
135
+ * Within one key, if there are more than one value, then there is effectively
136
+ * an OR between them.
137
+ *
138
+ * Example: For an input of
139
+ *
140
+ * ```
141
+ * [
142
+ * { kind: ['API', 'Component'] },
143
+ * { 'metadata.name': 'a', 'metadata.namespace': 'b' }
144
+ * ]
145
+ * ```
146
+ *
147
+ * This effectively means
148
+ *
149
+ * ```
150
+ * (kind = EITHER 'API' OR 'Component')
151
+ * OR
152
+ * (metadata.name = 'a' AND metadata.namespace = 'b' )
153
+ * ```
154
+ *
155
+ * Each key is a dot separated path in each object.
156
+ *
157
+ * As a value you can also pass in the symbol `CATALOG_FILTER_EXISTS`
158
+ * (exported from this package), which means that you assert on the existence
159
+ * of that key, no matter what its value is.
160
+ */
161
+ filter?: Record<string, string | symbol | (string | symbol)[]>[] | Record<string, string | symbol | (string | symbol)[]> | undefined;
162
+ /**
163
+ * Dot separated paths for the facets to extract from each entity.
164
+ *
165
+ * @remarks
166
+ *
167
+ * Example: For an input of `['kind', 'metadata.annotations.backstage.io/orphan']`, then the
168
+ * response will be shaped like
169
+ *
170
+ * ```
171
+ * {
172
+ * "facets": {
173
+ * "kind": [
174
+ * { "key": "Component", "count": 22 },
175
+ * { "key": "API", "count": 13 }
176
+ * ],
177
+ * "metadata.annotations.backstage.io/orphan": [
178
+ * { "key": "true", "count": 2 }
179
+ * ]
180
+ * }
181
+ * }
182
+ * ```
183
+ */
184
+ facets: string[];
185
+ }
186
+ /**
187
+ * The response type for {@link CatalogClient.getEntityFacets}.
188
+ *
189
+ * @public
190
+ */
191
+ interface GetEntityFacetsResponse {
192
+ /**
193
+ * The computed facets, one entry per facet in the request.
194
+ */
195
+ facets: Record<string, Array<{
196
+ value: string;
197
+ count: number;
198
+ }>>;
199
+ }
116
200
  /**
117
201
  * Options you can pass into a catalog request for additional information.
118
202
  *
119
203
  * @public
120
204
  */
121
- declare type CatalogRequestOptions = {
205
+ interface CatalogRequestOptions {
122
206
  token?: string;
207
+ }
208
+ /**
209
+ * Entity location for a specific entity.
210
+ *
211
+ * @public
212
+ */
213
+ declare type Location = {
214
+ id: string;
215
+ type: string;
216
+ target: string;
217
+ /** @deprecated This field is is ignored */
218
+ presence?: 'optional' | 'required';
219
+ };
220
+ /**
221
+ * The request type for {@link CatalogClient.addLocation}.
222
+ *
223
+ * @public
224
+ */
225
+ declare type AddLocationRequest = {
226
+ type?: string;
227
+ target: string;
228
+ dryRun?: boolean;
229
+ /** @deprecated This field is is ignored */
230
+ presence?: 'optional' | 'required';
231
+ };
232
+ /**
233
+ * The response type for {@link CatalogClient.addLocation}.
234
+ *
235
+ * @public
236
+ */
237
+ declare type AddLocationResponse = {
238
+ location: Location;
239
+ entities: Entity[];
240
+ exists?: boolean;
123
241
  };
124
242
  /**
125
- * Public functions for interacting with the Catalog API.
243
+ * A client for interacting with the Backstage software catalog through its API.
126
244
  *
127
245
  * @public
128
246
  */
129
247
  interface CatalogApi {
130
248
  /**
131
- * Gets the Entities from the catalog based on your request and options.
132
- *
133
- * @param request - An object with your filters and fields.
134
- * @param options - An object with your preferred options.
135
- *
136
- * @returns A CatalogListResponse with items typed Catalog Model Entity.
249
+ * Lists catalog entities.
137
250
  *
251
+ * @param request - Request parameters
252
+ * @param options - Additional options
138
253
  */
139
- getEntities(request?: CatalogEntitiesRequest, options?: CatalogRequestOptions): Promise<CatalogListResponse<Entity>>;
254
+ getEntities(request?: GetEntitiesRequest, options?: CatalogRequestOptions): Promise<GetEntitiesResponse>;
140
255
  /**
141
- * Gets the Entity ancestor information from the catalog based on your request and options.
142
- *
143
- * @param request - An object with your filters and fields.
144
- * @param options - An object with your preferred options.
256
+ * Gets entity ancestor information, i.e. the hierarchy of parent entities
257
+ * whose processing resulted in a given entity appearing in the catalog.
145
258
  *
146
- * @returns A CatalogEntityAncestorsResponse.
259
+ * @param request - Request parameters
260
+ * @param options - Additional options
147
261
  */
148
- getEntityAncestors(request: CatalogEntityAncestorsRequest, options?: CatalogRequestOptions): Promise<CatalogEntityAncestorsResponse>;
262
+ getEntityAncestors(request: GetEntityAncestorsRequest, options?: CatalogRequestOptions): Promise<GetEntityAncestorsResponse>;
149
263
  /**
150
- * Gets a single Entity from the catalog by Entity name.
264
+ * Gets a single entity from the catalog by its ref (kind, namespace, name)
265
+ * triplet.
151
266
  *
152
- * @param name - A complete Entity name, with the full kind-namespace-name triplet.
153
- * @param options - An object with your preferred options.
154
- *
155
- * @returns A {@link catalog-model#Entity}.
267
+ * @param name - A complete entity ref
268
+ * @param options - Additional options
156
269
  */
157
270
  getEntityByName(name: EntityName, options?: CatalogRequestOptions): Promise<Entity | undefined>;
158
271
  /**
159
- * Removes a single Entity from the catalog by Entity UID.
160
- *
161
- * @param uid - A string of the Entity UID.
162
- * @param options - An object with your preferred options.
272
+ * Removes a single entity from the catalog by entity UID.
163
273
  *
274
+ * @param uid - An entity UID
275
+ * @param options - Additional options
164
276
  */
165
277
  removeEntityByUid(uid: string, options?: CatalogRequestOptions): Promise<void>;
166
278
  /**
167
- * Refreshes an Entity in the catalog.
168
- *
169
- * @param entityRef - A string in the form of 'Kind/default:foo'.
170
- * @param options - An object with your preferred options.
279
+ * Refreshes (marks for reprocessing) an entity in the catalog.
171
280
  *
281
+ * @param entityRef - An entity ref on string form (e.g.
282
+ * 'component/default:my-component')
283
+ * @param options - Additional options
172
284
  */
173
285
  refreshEntity(entityRef: string, options?: CatalogRequestOptions): Promise<void>;
174
286
  /**
175
- * Gets a Location object by ID from the catalog.
287
+ * Gets a summary of field facets of entities in the catalog.
176
288
  *
177
- * @param id - A string in of the Location Id.
178
- * @param options - An object with your preferred options.
179
- *
180
- * @returns A {@link catalog-model#Location_2}.
289
+ * @param request - Request parameters
290
+ * @param options - Additional options
181
291
  */
182
- getLocationById(id: string, options?: CatalogRequestOptions): Promise<Location | undefined>;
292
+ getEntityFacets(request: GetEntityFacetsRequest, options?: CatalogRequestOptions): Promise<GetEntityFacetsResponse>;
183
293
  /**
184
- * Gets origin location by Entity.
185
- *
186
- * @param entity - An {@link catalog-model#Entity}.
187
- * @param options - An object with your preferred options.
294
+ * Gets a registered location by its ID.
188
295
  *
189
- * @returns A {@link catalog-model#Location_2}.
296
+ * @param id - A location ID
297
+ * @param options - Additional options
190
298
  */
191
- getOriginLocationByEntity(entity: Entity, options?: CatalogRequestOptions): Promise<Location | undefined>;
299
+ getLocationById(id: string, options?: CatalogRequestOptions): Promise<Location | undefined>;
192
300
  /**
193
- * Gets Location by Entity.
194
- *
195
- * @param entity - An {@link catalog-model#Entity}.
196
- * @param options - An object with your preferred options.
301
+ * Gets a registered location by its ref.
197
302
  *
198
- * @returns A {@link catalog-model#Location_2}.
303
+ * @param locationRef - A location ref, e.g. "url:https://github.com/..."
304
+ * @param options - Additional options
199
305
  */
200
- getLocationByEntity(entity: Entity, options?: CatalogRequestOptions): Promise<Location | undefined>;
306
+ getLocationByRef(locationRef: string, options?: CatalogRequestOptions): Promise<Location | undefined>;
201
307
  /**
202
- * Adds a Location.
203
- *
204
- * @param location - A request type for adding a Location to the catalog.
205
- * @param options - An object with your preferred options.
308
+ * Registers a new location.
206
309
  *
207
- * @returns A AddLocationResponse.
310
+ * @param location - Request parameters
311
+ * @param options - Additional options
208
312
  */
209
313
  addLocation(location: AddLocationRequest, options?: CatalogRequestOptions): Promise<AddLocationResponse>;
210
314
  /**
211
- * Removes a Location by Id.
212
- *
213
- * @param id - A string in of the Location Id.
214
- * @param options - An object with your preferred options.
315
+ * Removes a registered Location by its ID.
215
316
  *
317
+ * @param id - A location ID
318
+ * @param options - Additional options
216
319
  */
217
320
  removeLocationById(id: string, options?: CatalogRequestOptions): Promise<void>;
218
321
  }
219
- /**
220
- * A request type for adding a Location to the catalog.
221
- *
222
- * @public
223
- */
224
- declare type AddLocationRequest = {
225
- type?: string;
226
- target: string;
227
- dryRun?: boolean;
228
- presence?: 'optional' | 'required';
229
- };
230
- /**
231
- * A response type for adding a Location to the catalog.
232
- *
233
- * @public
234
- */
235
- declare type AddLocationResponse = {
236
- location: Location;
237
- entities: Entity[];
238
- exists?: boolean;
239
- };
240
322
 
241
323
  /**
242
- * This is a copy of the DiscoveryApi, to avoid importing core-plugin-api.
324
+ * A frontend and backend compatible client for communicating with the Backstage
325
+ * software catalog.
243
326
  *
244
327
  * @public
245
328
  */
246
- declare type DiscoveryApi = {
247
- getBaseUrl(pluginId: string): Promise<string>;
248
- };
249
-
250
- /**
251
- * This is a copy of FetchApi, to avoid importing core-plugin-api.
252
- *
253
- * @public
254
- */
255
- declare type FetchApi = {
256
- fetch: typeof fetch;
257
- };
258
-
259
- /**
260
- * A frontend and backend compatible client for communicating with the Backstage Catalog.
261
- *
262
- * @public
263
- * */
264
329
  declare class CatalogClient implements CatalogApi {
265
330
  private readonly discoveryApi;
266
331
  private readonly fetchApi;
267
332
  constructor(options: {
268
- discoveryApi: DiscoveryApi;
269
- fetchApi?: FetchApi;
333
+ discoveryApi: {
334
+ getBaseUrl(pluginId: string): Promise<string>;
335
+ };
336
+ fetchApi?: {
337
+ fetch: typeof fetch;
338
+ };
270
339
  });
271
340
  /**
272
- * Gets the Ancestors of an Entity.
273
- *
274
- * @param request - A request type for retrieving Entity ancestors.
275
- * @param options - An object with your preferred options.
276
- *
277
- * @returns A CatalogEntityAncestorsResponse.
278
- *
279
- * @public
341
+ * {@inheritdoc CatalogApi.getEntityAncestors}
280
342
  */
281
- getEntityAncestors(request: CatalogEntityAncestorsRequest, options?: CatalogRequestOptions): Promise<CatalogEntityAncestorsResponse>;
343
+ getEntityAncestors(request: GetEntityAncestorsRequest, options?: CatalogRequestOptions): Promise<GetEntityAncestorsResponse>;
282
344
  /**
283
- * Gets a Location by Id.
284
- *
285
- * @param id - A string containing the Id.
286
- * @param options - An object with your preferred options.
287
- *
288
- * @returns A {@link catalog-model#Location_2}.
289
- *
290
- * @public
345
+ * {@inheritdoc CatalogApi.getLocationById}
291
346
  */
292
347
  getLocationById(id: string, options?: CatalogRequestOptions): Promise<Location | undefined>;
293
348
  /**
294
- * Gets a set of Entities.
295
- *
296
- * @param request - A request type for retrieving an Entity.
297
- * @param options - An object with your preferred options.
298
- *
299
- * @returns A CatalogListResponse.
300
- *
301
- * @public
349
+ * {@inheritdoc CatalogApi.getEntities}
302
350
  */
303
- getEntities(request?: CatalogEntitiesRequest, options?: CatalogRequestOptions): Promise<CatalogListResponse<Entity>>;
351
+ getEntities(request?: GetEntitiesRequest, options?: CatalogRequestOptions): Promise<GetEntitiesResponse>;
304
352
  /**
305
- * Gets a given Entity based on a provided name.
306
- *
307
- * @param compoundName - A string containing the name.
308
- * @param options - An object with your preferred options.
309
- *
310
- * @returns An {@link catalog-model#Entity}.
311
- *
312
- * @public
353
+ * {@inheritdoc CatalogApi.getEntityByName}
313
354
  */
314
355
  getEntityByName(compoundName: EntityName, options?: CatalogRequestOptions): Promise<Entity | undefined>;
315
356
  /**
316
- * Refreshes an Entity.
317
- *
318
- * @param entityRef - A string containing the entityREf
319
- * @param options - An object with your preferred options.
320
- *
321
- * @public
357
+ * {@inheritdoc CatalogApi.refreshEntity}
322
358
  */
323
359
  refreshEntity(entityRef: string, options?: CatalogRequestOptions): Promise<void>;
324
360
  /**
325
- * Adds a location.
326
- *
327
- * @param options - An object with your preferred options.
328
- * @param AddLocationRequest - A request object for adding locations.
329
- *
330
- * @returns An AddLocationResponse
331
- *
332
- * @public
361
+ * {@inheritdoc CatalogApi.getEntityFacets}
362
+ */
363
+ getEntityFacets(request: GetEntityFacetsRequest, options?: CatalogRequestOptions): Promise<GetEntityFacetsResponse>;
364
+ /**
365
+ * {@inheritdoc CatalogApi.addLocation}
333
366
  */
334
367
  addLocation({ type, target, dryRun, presence }: AddLocationRequest, options?: CatalogRequestOptions): Promise<AddLocationResponse>;
335
368
  /**
336
- * Gets an origin Location By Entity.
337
- *
338
- * @param entity - An Entity
339
- * @param options - An object with your preferred options.
340
- *
341
- * @returns A {@link catalog-model#Location_2}.
342
- *
343
- * @public
369
+ * @deprecated please use getLocationByRef instead
344
370
  */
345
371
  getOriginLocationByEntity(entity: Entity, options?: CatalogRequestOptions): Promise<Location | undefined>;
346
372
  /**
347
- * Gets a Location by Entity.
348
- *
349
- * @param entity - An Entity
350
- * @param options - An object with your preferred options.
351
- *
352
- * @returns A {@link catalog-model#Location_2}.
353
- *
354
- * @public
373
+ * @deprecated please use getLocationByRef instead
355
374
  */
356
375
  getLocationByEntity(entity: Entity, options?: CatalogRequestOptions): Promise<Location | undefined>;
357
376
  /**
358
- * Removes a location as identified by Id.
359
- *
360
- * @param id - A string containing the Id
361
- * @param options - An object with your preferred options.
362
- *
363
- * @public
377
+ * {@inheritdoc CatalogApi.getLocationByRef}
378
+ */
379
+ getLocationByRef(locationRef: string, options?: CatalogRequestOptions): Promise<Location | undefined>;
380
+ /**
381
+ * {@inheritdoc CatalogApi.removeLocationById}
364
382
  */
365
383
  removeLocationById(id: string, options?: CatalogRequestOptions): Promise<void>;
366
384
  /**
367
- * Removes an Entity as identified by Uid.
368
- *
369
- * @param uid - A string containing the Uid
370
- * @param options - An object with your preferred options.
371
- *
372
- * @public
385
+ * {@inheritdoc CatalogApi.removeEntityByUid}
373
386
  */
374
387
  removeEntityByUid(uid: string, options?: CatalogRequestOptions): Promise<void>;
375
388
  private requestIgnored;
@@ -377,6 +390,27 @@ declare class CatalogClient implements CatalogApi {
377
390
  private requestOptional;
378
391
  }
379
392
 
393
+ /**
394
+ * @public
395
+ * @deprecated use GetEntitiesRequest instead
396
+ */
397
+ declare type CatalogEntitiesRequest = GetEntitiesRequest;
398
+ /**
399
+ * @public
400
+ * @deprecated use GetEntitiesResponse instead
401
+ */
402
+ declare type CatalogListResponse<_Entity> = GetEntitiesResponse;
403
+ /**
404
+ * @public
405
+ * @deprecated use GetEntityAncestorsRequest instead
406
+ */
407
+ declare type CatalogEntityAncestorsRequest = GetEntityAncestorsRequest;
408
+ /**
409
+ * @public
410
+ * @deprecated use GetEntityAncestorsResponse instead
411
+ */
412
+ declare type CatalogEntityAncestorsResponse = GetEntityAncestorsResponse;
413
+
380
414
  /**
381
415
  * The entity `status.items[].type` for the status of the processing engine in
382
416
  * regards to an entity.
@@ -385,4 +419,4 @@ declare class CatalogClient implements CatalogApi {
385
419
  */
386
420
  declare const ENTITY_STATUS_CATALOG_PROCESSING_TYPE = "backstage.io/catalog-processing";
387
421
 
388
- export { AddLocationRequest, AddLocationResponse, CATALOG_FILTER_EXISTS, CatalogApi, CatalogClient, CatalogEntitiesRequest, CatalogEntityAncestorsRequest, CatalogEntityAncestorsResponse, CatalogListResponse, CatalogRequestOptions, DiscoveryApi, ENTITY_STATUS_CATALOG_PROCESSING_TYPE, FetchApi };
422
+ export { AddLocationRequest, AddLocationResponse, CATALOG_FILTER_EXISTS, CatalogApi, CatalogClient, CatalogEntitiesRequest, CatalogEntityAncestorsRequest, CatalogEntityAncestorsResponse, CatalogListResponse, CatalogRequestOptions, ENTITY_STATUS_CATALOG_PROCESSING_TYPE, GetEntitiesRequest, GetEntitiesResponse, GetEntityAncestorsRequest, GetEntityAncestorsResponse, GetEntityFacetsRequest, GetEntityFacetsResponse, Location };
package/dist/index.esm.js CHANGED
@@ -1,8 +1,8 @@
1
- import { parseEntityRef, ORIGIN_LOCATION_ANNOTATION, stringifyLocationReference, LOCATION_ANNOTATION, stringifyEntityRef } from '@backstage/catalog-model';
1
+ import { parseEntityRef, ANNOTATION_ORIGIN_LOCATION, stringifyLocationRef, ANNOTATION_LOCATION, stringifyEntityRef } from '@backstage/catalog-model';
2
2
  import { ResponseError } from '@backstage/errors';
3
3
  import crossFetch from 'cross-fetch';
4
4
 
5
- const CATALOG_FILTER_EXISTS = Symbol("CATALOG_FILTER_EXISTS");
5
+ const CATALOG_FILTER_EXISTS = Symbol.for("CATALOG_FILTER_EXISTS_0e15b590c0b343a2bae3e787e84c2111");
6
6
 
7
7
  class CatalogClient {
8
8
  constructor(options) {
@@ -18,9 +18,8 @@ class CatalogClient {
18
18
  }
19
19
  async getEntities(request, options) {
20
20
  const { filter = [], fields = [], offset, limit, after } = request != null ? request : {};
21
- const filterItems = [filter].flat();
22
21
  const params = [];
23
- for (const filterItem of filterItems) {
22
+ for (const filterItem of [filter].flat()) {
24
23
  const filterParts = [];
25
24
  for (const [key, value] of Object.entries(filterItem)) {
26
25
  for (const v of [value].flat()) {
@@ -83,6 +82,30 @@ class CatalogClient {
83
82
  throw new Error(await response.text());
84
83
  }
85
84
  }
85
+ async getEntityFacets(request, options) {
86
+ const { filter = [], facets } = request;
87
+ const params = [];
88
+ for (const filterItem of [filter].flat()) {
89
+ const filterParts = [];
90
+ for (const [key, value] of Object.entries(filterItem)) {
91
+ for (const v of [value].flat()) {
92
+ if (v === CATALOG_FILTER_EXISTS) {
93
+ filterParts.push(encodeURIComponent(key));
94
+ } else if (typeof v === "string") {
95
+ filterParts.push(`${encodeURIComponent(key)}=${encodeURIComponent(v)}`);
96
+ }
97
+ }
98
+ }
99
+ if (filterParts.length) {
100
+ params.push(`filter=${filterParts.join(",")}`);
101
+ }
102
+ }
103
+ for (const facet of facets) {
104
+ params.push(`facet=${encodeURIComponent(facet)}`);
105
+ }
106
+ const query = params.length ? `?${params.join("&")}` : "";
107
+ return await this.requestOptional("GET", `/entity-facets${query}`, options);
108
+ }
86
109
  async addLocation({ type = "url", target, dryRun, presence }, options) {
87
110
  const response = await this.fetchApi.fetch(`${await this.discoveryApi.getBaseUrl("catalog")}/locations${dryRun ? "?dryRun=true" : ""}`, {
88
111
  headers: {
@@ -107,21 +130,25 @@ class CatalogClient {
107
130
  }
108
131
  async getOriginLocationByEntity(entity, options) {
109
132
  var _a;
110
- const locationCompound = (_a = entity.metadata.annotations) == null ? void 0 : _a[ORIGIN_LOCATION_ANNOTATION];
133
+ const locationCompound = (_a = entity.metadata.annotations) == null ? void 0 : _a[ANNOTATION_ORIGIN_LOCATION];
111
134
  if (!locationCompound) {
112
135
  return void 0;
113
136
  }
114
137
  const all = await this.requestRequired("GET", "/locations", options);
115
- return all.map((r) => r.data).find((l) => locationCompound === stringifyLocationReference(l));
138
+ return all.map((r) => r.data).find((l) => locationCompound === stringifyLocationRef(l));
116
139
  }
117
140
  async getLocationByEntity(entity, options) {
118
141
  var _a;
119
- const locationCompound = (_a = entity.metadata.annotations) == null ? void 0 : _a[LOCATION_ANNOTATION];
142
+ const locationCompound = (_a = entity.metadata.annotations) == null ? void 0 : _a[ANNOTATION_LOCATION];
120
143
  if (!locationCompound) {
121
144
  return void 0;
122
145
  }
123
146
  const all = await this.requestRequired("GET", "/locations", options);
124
- return all.map((r) => r.data).find((l) => locationCompound === stringifyLocationReference(l));
147
+ return all.map((r) => r.data).find((l) => locationCompound === stringifyLocationRef(l));
148
+ }
149
+ async getLocationByRef(locationRef, options) {
150
+ const all = await this.requestRequired("GET", "/locations", options);
151
+ return all.map((r) => r.data).find((l) => locationRef === stringifyLocationRef(l));
125
152
  }
126
153
  async removeLocationById(id, options) {
127
154
  await this.requestIgnored("DELETE", `/locations/${encodeURIComponent(id)}`, options);
@@ -1 +1 @@
1
- {"version":3,"file":"index.esm.js","sources":["../src/types/api.ts","../src/CatalogClient.ts","../src/types/status.ts"],"sourcesContent":["/*\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, EntityName, Location } from '@backstage/catalog-model';\n\n/**\n * A Symbol to define if a catalog filter exists or not.\n *\n * @public\n */\nexport const CATALOG_FILTER_EXISTS = Symbol('CATALOG_FILTER_EXISTS');\n\n/**\n * A request type for retrieving catalog Entities.\n *\n * @public\n */\nexport type CatalogEntitiesRequest = {\n /**\n * If given, return only entities that match the given patterns.\n *\n * @remarks\n *\n * If multiple filter sets are given as an array, then there is effectively an\n * OR between each filter set.\n *\n * Within one filter set, there is effectively an AND between the various\n * keys.\n *\n * Within one key, if there are more than one value, then there is effectively\n * an OR between them.\n *\n * Example: For an input of\n *\n * ```\n * [\n * { kind: ['API', 'Component'] },\n * { 'metadata.name': 'a', 'metadata.namespace': 'b' }\n * ]\n * ```\n *\n * This effectively means\n *\n * ```\n * (kind = EITHER 'API' OR 'Component')\n * OR\n * (metadata.name = 'a' AND metadata.namespace = 'b' )\n * ```\n *\n * Each key is a dot separated path in each object.\n *\n * As a value you can also pass in the symbol `CATALOG_FILTER_EXISTS`\n * (exported from this package), which means that you assert on the existence\n * of that key, no matter what its value is.\n */\n filter?:\n | Record<string, string | symbol | (string | symbol)[]>[]\n | Record<string, string | symbol | (string | symbol)[]>\n | undefined;\n /**\n * If given, return only the parts of each entity that match those dot\n * separated paths in each object.\n *\n * @remarks\n *\n * Example: For an input of `['kind', 'metadata.annotations']`, then response\n * objects will be shaped like\n *\n * ```\n * {\n * \"kind\": \"Component\",\n * \"metadata\": {\n * \"annotations\": {\n * \"foo\": \"bar\"\n * }\n * }\n * }\n * ```\n */\n fields?: string[] | undefined;\n /**\n * If given, skips over the first N items in the result set.\n */\n offset?: number;\n /**\n * If given, returns at most N items from the result set.\n */\n limit?: number;\n /**\n * If given, skips over all items before that cursor as returned by a previous\n * request.\n */\n after?: string;\n};\n\n/**\n * A request type for Catalog Entity Ancestor information.\n *\n * @public\n */\nexport type CatalogEntityAncestorsRequest = {\n entityRef: string;\n};\n\n/**\n * A response type for Catalog Entity Ancestor information.\n *\n * @public\n */\nexport type CatalogEntityAncestorsResponse = {\n rootEntityRef: string;\n items: { entity: Entity; parentEntityRefs: string[] }[];\n};\n\n/**\n * A response type for the result of a catalog operation in list form.\n *\n * @public\n */\nexport type CatalogListResponse<T> = {\n items: T[];\n};\n\n/**\n * Options you can pass into a catalog request for additional information.\n *\n * @public\n */\nexport type CatalogRequestOptions = {\n token?: string;\n};\n\n/**\n * Public functions for interacting with the Catalog API.\n *\n * @public\n */\nexport interface CatalogApi {\n /**\n * Gets the Entities from the catalog based on your request and options.\n *\n * @param request - An object with your filters and fields.\n * @param options - An object with your preferred options.\n *\n * @returns A CatalogListResponse with items typed Catalog Model Entity.\n *\n */\n getEntities(\n request?: CatalogEntitiesRequest,\n options?: CatalogRequestOptions,\n ): Promise<CatalogListResponse<Entity>>;\n /**\n * Gets the Entity ancestor information from the catalog based on your request and options.\n *\n * @param request - An object with your filters and fields.\n * @param options - An object with your preferred options.\n *\n * @returns A CatalogEntityAncestorsResponse.\n */\n getEntityAncestors(\n request: CatalogEntityAncestorsRequest,\n options?: CatalogRequestOptions,\n ): Promise<CatalogEntityAncestorsResponse>;\n /**\n * Gets a single Entity from the catalog by Entity name.\n *\n * @param name - A complete Entity name, with the full kind-namespace-name triplet.\n * @param options - An object with your preferred options.\n *\n * @returns A {@link catalog-model#Entity}.\n */\n getEntityByName(\n name: EntityName,\n options?: CatalogRequestOptions,\n ): Promise<Entity | undefined>;\n /**\n * Removes a single Entity from the catalog by Entity UID.\n *\n * @param uid - A string of the Entity UID.\n * @param options - An object with your preferred options.\n *\n */\n removeEntityByUid(\n uid: string,\n options?: CatalogRequestOptions,\n ): Promise<void>;\n /**\n * Refreshes an Entity in the catalog.\n *\n * @param entityRef - A string in the form of 'Kind/default:foo'.\n * @param options - An object with your preferred options.\n *\n */\n refreshEntity(\n entityRef: string,\n options?: CatalogRequestOptions,\n ): Promise<void>;\n\n // Locations\n /**\n * Gets a Location object by ID from the catalog.\n *\n * @param id - A string in of the Location Id.\n * @param options - An object with your preferred options.\n *\n * @returns A {@link catalog-model#Location_2}.\n */\n getLocationById(\n id: string,\n options?: CatalogRequestOptions,\n ): Promise<Location | undefined>;\n /**\n * Gets origin location by Entity.\n *\n * @param entity - An {@link catalog-model#Entity}.\n * @param options - An object with your preferred options.\n *\n * @returns A {@link catalog-model#Location_2}.\n */\n getOriginLocationByEntity(\n entity: Entity,\n options?: CatalogRequestOptions,\n ): Promise<Location | undefined>;\n /**\n * Gets Location by Entity.\n *\n * @param entity - An {@link catalog-model#Entity}.\n * @param options - An object with your preferred options.\n *\n * @returns A {@link catalog-model#Location_2}.\n */\n getLocationByEntity(\n entity: Entity,\n options?: CatalogRequestOptions,\n ): Promise<Location | undefined>;\n /**\n * Adds a Location.\n *\n * @param location - A request type for adding a Location to the catalog.\n * @param options - An object with your preferred options.\n *\n * @returns A AddLocationResponse.\n */\n addLocation(\n location: AddLocationRequest,\n options?: CatalogRequestOptions,\n ): Promise<AddLocationResponse>;\n /**\n * Removes a Location by Id.\n *\n * @param id - A string in of the Location Id.\n * @param options - An object with your preferred options.\n *\n */\n removeLocationById(\n id: string,\n options?: CatalogRequestOptions,\n ): Promise<void>;\n}\n\n/**\n * A request type for adding a Location to the catalog.\n *\n * @public\n */\nexport type AddLocationRequest = {\n type?: string;\n target: string;\n dryRun?: boolean;\n presence?: 'optional' | 'required';\n};\n\n/**\n * A response type for adding a Location to the catalog.\n *\n * @public\n */\nexport type AddLocationResponse = {\n location: Location;\n entities: Entity[];\n // Exists is only set in DryRun mode.\n exists?: boolean;\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 Location,\n LOCATION_ANNOTATION,\n ORIGIN_LOCATION_ANNOTATION,\n parseEntityRef,\n stringifyEntityRef,\n stringifyLocationReference,\n} from '@backstage/catalog-model';\nimport { ResponseError } from '@backstage/errors';\nimport crossFetch from 'cross-fetch';\nimport {\n CATALOG_FILTER_EXISTS,\n AddLocationRequest,\n AddLocationResponse,\n CatalogApi,\n CatalogEntitiesRequest,\n CatalogListResponse,\n CatalogRequestOptions,\n CatalogEntityAncestorsRequest,\n CatalogEntityAncestorsResponse,\n} from './types/api';\nimport { DiscoveryApi } from './types/discovery';\nimport { FetchApi } from './types/fetch';\n\n/**\n * A frontend and backend compatible client for communicating with the Backstage Catalog.\n *\n * @public\n * */\nexport class CatalogClient implements CatalogApi {\n private readonly discoveryApi: DiscoveryApi;\n private readonly fetchApi: FetchApi;\n\n constructor(options: { discoveryApi: DiscoveryApi; fetchApi?: FetchApi }) {\n this.discoveryApi = options.discoveryApi;\n this.fetchApi = options.fetchApi || { fetch: crossFetch };\n }\n\n /**\n * Gets the Ancestors of an Entity.\n *\n * @param request - A request type for retrieving Entity ancestors.\n * @param options - An object with your preferred options.\n *\n * @returns A CatalogEntityAncestorsResponse.\n *\n * @public\n */\n async getEntityAncestors(\n request: CatalogEntityAncestorsRequest,\n options?: CatalogRequestOptions,\n ): Promise<CatalogEntityAncestorsResponse> {\n const { kind, namespace, name } = parseEntityRef(request.entityRef);\n return await this.requestRequired(\n 'GET',\n `/entities/by-name/${encodeURIComponent(kind)}/${encodeURIComponent(\n namespace,\n )}/${encodeURIComponent(name)}/ancestry`,\n options,\n );\n }\n\n /**\n * Gets a Location by Id.\n *\n * @param id - A string containing the Id.\n * @param options - An object with your preferred options.\n *\n * @returns A {@link catalog-model#Location_2}.\n *\n * @public\n */\n async getLocationById(\n id: string,\n options?: CatalogRequestOptions,\n ): Promise<Location | undefined> {\n return await this.requestOptional(\n 'GET',\n `/locations/${encodeURIComponent(id)}`,\n options,\n );\n }\n\n /**\n * Gets a set of Entities.\n *\n * @param request - A request type for retrieving an Entity.\n * @param options - An object with your preferred options.\n *\n * @returns A CatalogListResponse.\n *\n * @public\n */\n async getEntities(\n request?: CatalogEntitiesRequest,\n options?: CatalogRequestOptions,\n ): Promise<CatalogListResponse<Entity>> {\n const { filter = [], fields = [], offset, limit, after } = request ?? {};\n const filterItems = [filter].flat();\n const params: string[] = [];\n\n // filter param can occur multiple times, for example\n // /api/catalog/entities?filter=metadata.name=wayback-search,kind=component&filter=metadata.name=www-artist,kind=component'\n // the \"outer array\" defined by `filter` occurrences corresponds to \"anyOf\" filters\n // the \"inner array\" defined within a `filter` param corresponds to \"allOf\" filters\n for (const filterItem of filterItems) {\n const filterParts: string[] = [];\n for (const [key, value] of Object.entries(filterItem)) {\n for (const v of [value].flat()) {\n if (v === CATALOG_FILTER_EXISTS) {\n filterParts.push(encodeURIComponent(key));\n } else if (typeof v === 'string') {\n filterParts.push(\n `${encodeURIComponent(key)}=${encodeURIComponent(v)}`,\n );\n }\n }\n }\n\n if (filterParts.length) {\n params.push(`filter=${filterParts.join(',')}`);\n }\n }\n\n if (fields.length) {\n params.push(`fields=${fields.map(encodeURIComponent).join(',')}`);\n }\n\n if (offset !== undefined) {\n params.push(`offset=${offset}`);\n }\n if (limit !== undefined) {\n params.push(`limit=${limit}`);\n }\n if (after !== undefined) {\n params.push(`after=${encodeURIComponent(after)}`);\n }\n\n const query = params.length ? `?${params.join('&')}` : '';\n const entities: Entity[] = await this.requestRequired(\n 'GET',\n `/entities${query}`,\n options,\n );\n\n const refCompare = (a: Entity, b: Entity) => {\n // in case field filtering is used, these fields might not be part of the response\n if (\n a.metadata?.name === undefined ||\n a.kind === undefined ||\n b.metadata?.name === undefined ||\n b.kind === undefined\n ) {\n return 0;\n }\n\n const aRef = stringifyEntityRef(a);\n const bRef = stringifyEntityRef(b);\n if (aRef < bRef) {\n return -1;\n }\n if (aRef > bRef) {\n return 1;\n }\n return 0;\n };\n\n return { items: entities.sort(refCompare) };\n }\n\n /**\n * Gets a given Entity based on a provided name.\n *\n * @param compoundName - A string containing the name.\n * @param options - An object with your preferred options.\n *\n * @returns An {@link catalog-model#Entity}.\n *\n * @public\n */\n async getEntityByName(\n compoundName: EntityName,\n options?: CatalogRequestOptions,\n ): Promise<Entity | undefined> {\n const { kind, namespace = 'default', name } = compoundName;\n return this.requestOptional(\n 'GET',\n `/entities/by-name/${encodeURIComponent(kind)}/${encodeURIComponent(\n namespace,\n )}/${encodeURIComponent(name)}`,\n options,\n );\n }\n\n /**\n * Refreshes an Entity.\n *\n * @param entityRef - A string containing the entityREf\n * @param options - An object with your preferred options.\n *\n * @public\n */\n async refreshEntity(entityRef: string, options?: CatalogRequestOptions) {\n const response = await this.fetchApi.fetch(\n `${await this.discoveryApi.getBaseUrl('catalog')}/refresh`,\n {\n headers: {\n 'Content-Type': 'application/json',\n ...(options?.token && { Authorization: `Bearer ${options?.token}` }),\n },\n method: 'POST',\n body: JSON.stringify({ entityRef }),\n },\n );\n\n if (response.status !== 200) {\n throw new Error(await response.text());\n }\n }\n\n /**\n * Adds a location.\n *\n * @param options - An object with your preferred options.\n * @param AddLocationRequest - A request object for adding locations.\n *\n * @returns An AddLocationResponse\n *\n * @public\n */\n async addLocation(\n { type = 'url', target, dryRun, presence }: AddLocationRequest,\n options?: CatalogRequestOptions,\n ): Promise<AddLocationResponse> {\n const response = await this.fetchApi.fetch(\n `${await this.discoveryApi.getBaseUrl('catalog')}/locations${\n dryRun ? '?dryRun=true' : ''\n }`,\n {\n headers: {\n 'Content-Type': 'application/json',\n ...(options?.token && { Authorization: `Bearer ${options?.token}` }),\n },\n method: 'POST',\n body: JSON.stringify({ type, target, presence }),\n },\n );\n\n if (response.status !== 201) {\n throw new Error(await response.text());\n }\n\n const { location, entities, exists } = await response.json();\n\n if (!location) {\n throw new Error(`Location wasn't added: ${target}`);\n }\n\n return {\n location,\n entities,\n exists,\n };\n }\n\n /**\n * Gets an origin Location By Entity.\n *\n * @param entity - An Entity\n * @param options - An object with your preferred options.\n *\n * @returns A {@link catalog-model#Location_2}.\n *\n * @public\n */\n async getOriginLocationByEntity(\n entity: Entity,\n options?: CatalogRequestOptions,\n ): Promise<Location | undefined> {\n const locationCompound =\n entity.metadata.annotations?.[ORIGIN_LOCATION_ANNOTATION];\n if (!locationCompound) {\n return undefined;\n }\n const all: { data: Location }[] = await this.requestRequired(\n 'GET',\n '/locations',\n options,\n );\n return all\n .map(r => r.data)\n .find(l => locationCompound === stringifyLocationReference(l));\n }\n\n /**\n * Gets a Location by Entity.\n *\n * @param entity - An Entity\n * @param options - An object with your preferred options.\n *\n * @returns A {@link catalog-model#Location_2}.\n *\n * @public\n */\n async getLocationByEntity(\n entity: Entity,\n options?: CatalogRequestOptions,\n ): Promise<Location | undefined> {\n const locationCompound = entity.metadata.annotations?.[LOCATION_ANNOTATION];\n if (!locationCompound) {\n return undefined;\n }\n const all: { data: Location }[] = await this.requestRequired(\n 'GET',\n '/locations',\n options,\n );\n return all\n .map(r => r.data)\n .find(l => locationCompound === stringifyLocationReference(l));\n }\n\n /**\n * Removes a location as identified by Id.\n *\n * @param id - A string containing the Id\n * @param options - An object with your preferred options.\n *\n * @public\n */\n async removeLocationById(\n id: string,\n options?: CatalogRequestOptions,\n ): Promise<void> {\n await this.requestIgnored(\n 'DELETE',\n `/locations/${encodeURIComponent(id)}`,\n options,\n );\n }\n\n /**\n * Removes an Entity as identified by Uid.\n *\n * @param uid - A string containing the Uid\n * @param options - An object with your preferred options.\n *\n * @public\n */\n async removeEntityByUid(\n uid: string,\n options?: CatalogRequestOptions,\n ): Promise<void> {\n await this.requestIgnored(\n 'DELETE',\n `/entities/by-uid/${encodeURIComponent(uid)}`,\n options,\n );\n }\n\n //\n // Private methods\n //\n\n private async requestIgnored(\n method: string,\n path: string,\n options?: CatalogRequestOptions,\n ): Promise<void> {\n const url = `${await this.discoveryApi.getBaseUrl('catalog')}${path}`;\n const headers: Record<string, string> = options?.token\n ? { Authorization: `Bearer ${options.token}` }\n : {};\n const response = await this.fetchApi.fetch(url, { method, headers });\n\n if (!response.ok) {\n throw await ResponseError.fromResponse(response);\n }\n }\n\n private async requestRequired(\n method: string,\n path: string,\n options?: CatalogRequestOptions,\n ): Promise<any> {\n const url = `${await this.discoveryApi.getBaseUrl('catalog')}${path}`;\n const headers: Record<string, string> = options?.token\n ? { Authorization: `Bearer ${options.token}` }\n : {};\n const response = await this.fetchApi.fetch(url, { method, headers });\n\n if (!response.ok) {\n throw await ResponseError.fromResponse(response);\n }\n\n return await response.json();\n }\n\n private async requestOptional(\n method: string,\n path: string,\n options?: CatalogRequestOptions,\n ): Promise<any | undefined> {\n const url = `${await this.discoveryApi.getBaseUrl('catalog')}${path}`;\n const headers: Record<string, string> = options?.token\n ? { Authorization: `Bearer ${options.token}` }\n : {};\n const response = await this.fetchApi.fetch(url, { method, headers });\n\n if (!response.ok) {\n if (response.status === 404) {\n return undefined;\n }\n throw await ResponseError.fromResponse(response);\n }\n\n return await response.json();\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\n/**\n * The entity `status.items[].type` for the status of the processing engine in\n * regards to an entity.\n *\n * @public\n */\nexport const ENTITY_STATUS_CATALOG_PROCESSING_TYPE =\n 'backstage.io/catalog-processing';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.esm.js","sources":["../src/types/api.ts","../src/CatalogClient.ts","../src/types/status.ts"],"sourcesContent":["/*\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, EntityName } from '@backstage/catalog-model';\n\n/**\n * This symbol can be used in place of a value when passed to filters in e.g.\n * {@link CatalogClient.getEntities}, to signify that you want to filter on the\n * presence of that key no matter what its value is.\n *\n * @public\n */\nexport const CATALOG_FILTER_EXISTS = Symbol.for(\n // Random UUID to ensure no collisions\n 'CATALOG_FILTER_EXISTS_0e15b590c0b343a2bae3e787e84c2111',\n);\n\n/**\n * The request type for {@link CatalogClient.getEntities}.\n *\n * @public\n */\nexport interface GetEntitiesRequest {\n /**\n * If given, return only entities that match the given patterns.\n *\n * @remarks\n *\n * If multiple filter sets are given as an array, then there is effectively an\n * OR between each filter set.\n *\n * Within one filter set, there is effectively an AND between the various\n * keys.\n *\n * Within one key, if there are more than one value, then there is effectively\n * an OR between them.\n *\n * Example: For an input of\n *\n * ```\n * [\n * { kind: ['API', 'Component'] },\n * { 'metadata.name': 'a', 'metadata.namespace': 'b' }\n * ]\n * ```\n *\n * This effectively means\n *\n * ```\n * (kind = EITHER 'API' OR 'Component')\n * OR\n * (metadata.name = 'a' AND metadata.namespace = 'b' )\n * ```\n *\n * Each key is a dot separated path in each object.\n *\n * As a value you can also pass in the symbol `CATALOG_FILTER_EXISTS`\n * (exported from this package), which means that you assert on the existence\n * of that key, no matter what its value is.\n */\n filter?:\n | Record<string, string | symbol | (string | symbol)[]>[]\n | Record<string, string | symbol | (string | symbol)[]>\n | undefined;\n /**\n * If given, return only the parts of each entity that match those dot\n * separated paths in each object.\n *\n * @remarks\n *\n * Example: For an input of `['kind', 'metadata.annotations']`, then response\n * objects will be shaped like\n *\n * ```\n * {\n * \"kind\": \"Component\",\n * \"metadata\": {\n * \"annotations\": {\n * \"foo\": \"bar\"\n * }\n * }\n * }\n * ```\n */\n fields?: string[] | undefined;\n /**\n * If given, skips over the first N items in the result set.\n */\n offset?: number;\n /**\n * If given, returns at most N items from the result set.\n */\n limit?: number;\n /**\n * If given, skips over all items before that cursor as returned by a previous\n * request.\n */\n after?: string;\n}\n\n/**\n * The response type for {@link CatalogClient.getEntities}.\n *\n * @public\n */\nexport interface GetEntitiesResponse {\n items: Entity[];\n}\n\n/**\n * The request type for {@link CatalogClient.getEntityAncestors}.\n *\n * @public\n */\nexport interface GetEntityAncestorsRequest {\n entityRef: string;\n}\n\n/**\n * The response type for {@link CatalogClient.getEntityAncestors}.\n *\n * @public\n */\nexport interface GetEntityAncestorsResponse {\n rootEntityRef: string;\n items: Array<{\n entity: Entity;\n parentEntityRefs: string[];\n }>;\n}\n\n/**\n * The request type for {@link CatalogClient.getEntityFacets}.\n *\n * @public\n */\nexport interface GetEntityFacetsRequest {\n /**\n * If given, return only entities that match the given patterns.\n *\n * @remarks\n *\n * If multiple filter sets are given as an array, then there is effectively an\n * OR between each filter set.\n *\n * Within one filter set, there is effectively an AND between the various\n * keys.\n *\n * Within one key, if there are more than one value, then there is effectively\n * an OR between them.\n *\n * Example: For an input of\n *\n * ```\n * [\n * { kind: ['API', 'Component'] },\n * { 'metadata.name': 'a', 'metadata.namespace': 'b' }\n * ]\n * ```\n *\n * This effectively means\n *\n * ```\n * (kind = EITHER 'API' OR 'Component')\n * OR\n * (metadata.name = 'a' AND metadata.namespace = 'b' )\n * ```\n *\n * Each key is a dot separated path in each object.\n *\n * As a value you can also pass in the symbol `CATALOG_FILTER_EXISTS`\n * (exported from this package), which means that you assert on the existence\n * of that key, no matter what its value is.\n */\n filter?:\n | Record<string, string | symbol | (string | symbol)[]>[]\n | Record<string, string | symbol | (string | symbol)[]>\n | undefined;\n /**\n * Dot separated paths for the facets to extract from each entity.\n *\n * @remarks\n *\n * Example: For an input of `['kind', 'metadata.annotations.backstage.io/orphan']`, then the\n * response will be shaped like\n *\n * ```\n * {\n * \"facets\": {\n * \"kind\": [\n * { \"key\": \"Component\", \"count\": 22 },\n * { \"key\": \"API\", \"count\": 13 }\n * ],\n * \"metadata.annotations.backstage.io/orphan\": [\n * { \"key\": \"true\", \"count\": 2 }\n * ]\n * }\n * }\n * ```\n */\n facets: string[];\n}\n\n/**\n * The response type for {@link CatalogClient.getEntityFacets}.\n *\n * @public\n */\nexport interface GetEntityFacetsResponse {\n /**\n * The computed facets, one entry per facet in the request.\n */\n facets: Record<string, Array<{ value: string; count: number }>>;\n}\n\n/**\n * Options you can pass into a catalog request for additional information.\n *\n * @public\n */\nexport interface CatalogRequestOptions {\n token?: string;\n}\n\n/**\n * Entity location for a specific entity.\n *\n * @public\n */\nexport type Location = {\n id: string;\n type: string;\n target: string;\n /** @deprecated This field is is ignored */\n presence?: 'optional' | 'required';\n};\n\n/**\n * The request type for {@link CatalogClient.addLocation}.\n *\n * @public\n */\nexport type AddLocationRequest = {\n type?: string;\n target: string;\n dryRun?: boolean;\n /** @deprecated This field is is ignored */\n presence?: 'optional' | 'required';\n};\n\n/**\n * The response type for {@link CatalogClient.addLocation}.\n *\n * @public\n */\nexport type AddLocationResponse = {\n location: Location;\n entities: Entity[];\n // Only set in dryRun mode.\n exists?: boolean;\n};\n\n/**\n * A client for interacting with the Backstage software catalog through its API.\n *\n * @public\n */\nexport interface CatalogApi {\n /**\n * Lists catalog entities.\n *\n * @param request - Request parameters\n * @param options - Additional options\n */\n getEntities(\n request?: GetEntitiesRequest,\n options?: CatalogRequestOptions,\n ): Promise<GetEntitiesResponse>;\n\n /**\n * Gets entity ancestor information, i.e. the hierarchy of parent entities\n * whose processing resulted in a given entity appearing in the catalog.\n *\n * @param request - Request parameters\n * @param options - Additional options\n */\n getEntityAncestors(\n request: GetEntityAncestorsRequest,\n options?: CatalogRequestOptions,\n ): Promise<GetEntityAncestorsResponse>;\n\n /**\n * Gets a single entity from the catalog by its ref (kind, namespace, name)\n * triplet.\n *\n * @param name - A complete entity ref\n * @param options - Additional options\n */\n getEntityByName(\n name: EntityName,\n options?: CatalogRequestOptions,\n ): Promise<Entity | undefined>;\n\n /**\n * Removes a single entity from the catalog by entity UID.\n *\n * @param uid - An entity UID\n * @param options - Additional options\n */\n removeEntityByUid(\n uid: string,\n options?: CatalogRequestOptions,\n ): Promise<void>;\n\n /**\n * Refreshes (marks for reprocessing) an entity in the catalog.\n *\n * @param entityRef - An entity ref on string form (e.g.\n * 'component/default:my-component')\n * @param options - Additional options\n */\n refreshEntity(\n entityRef: string,\n options?: CatalogRequestOptions,\n ): Promise<void>;\n\n /**\n * Gets a summary of field facets of entities in the catalog.\n *\n * @param request - Request parameters\n * @param options - Additional options\n */\n getEntityFacets(\n request: GetEntityFacetsRequest,\n options?: CatalogRequestOptions,\n ): Promise<GetEntityFacetsResponse>;\n\n // Locations\n\n /**\n * Gets a registered location by its ID.\n *\n * @param id - A location ID\n * @param options - Additional options\n */\n getLocationById(\n id: string,\n options?: CatalogRequestOptions,\n ): Promise<Location | undefined>;\n\n /**\n * Gets a registered location by its ref.\n *\n * @param locationRef - A location ref, e.g. \"url:https://github.com/...\"\n * @param options - Additional options\n */\n getLocationByRef(\n locationRef: string,\n options?: CatalogRequestOptions,\n ): Promise<Location | undefined>;\n\n /**\n * Registers a new location.\n *\n * @param location - Request parameters\n * @param options - Additional options\n */\n addLocation(\n location: AddLocationRequest,\n options?: CatalogRequestOptions,\n ): Promise<AddLocationResponse>;\n\n /**\n * Removes a registered Location by its ID.\n *\n * @param id - A location ID\n * @param options - Additional options\n */\n removeLocationById(\n id: string,\n options?: CatalogRequestOptions,\n ): Promise<void>;\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 ANNOTATION_LOCATION,\n ANNOTATION_ORIGIN_LOCATION,\n Entity,\n EntityName,\n parseEntityRef,\n stringifyEntityRef,\n stringifyLocationRef,\n} from '@backstage/catalog-model';\nimport { ResponseError } from '@backstage/errors';\nimport crossFetch from 'cross-fetch';\nimport {\n CATALOG_FILTER_EXISTS,\n AddLocationRequest,\n AddLocationResponse,\n CatalogApi,\n GetEntitiesRequest,\n GetEntitiesResponse,\n CatalogRequestOptions,\n GetEntityAncestorsRequest,\n GetEntityAncestorsResponse,\n Location,\n GetEntityFacetsRequest,\n GetEntityFacetsResponse,\n} from './types/api';\nimport { DiscoveryApi } from './types/discovery';\nimport { FetchApi } from './types/fetch';\n\n/**\n * A frontend and backend compatible client for communicating with the Backstage\n * software catalog.\n *\n * @public\n */\nexport class CatalogClient implements CatalogApi {\n private readonly discoveryApi: DiscoveryApi;\n private readonly fetchApi: FetchApi;\n\n constructor(options: {\n discoveryApi: { getBaseUrl(pluginId: string): Promise<string> };\n fetchApi?: { fetch: typeof fetch };\n }) {\n this.discoveryApi = options.discoveryApi;\n this.fetchApi = options.fetchApi || { fetch: crossFetch };\n }\n\n /**\n * {@inheritdoc CatalogApi.getEntityAncestors}\n */\n async getEntityAncestors(\n request: GetEntityAncestorsRequest,\n options?: CatalogRequestOptions,\n ): Promise<GetEntityAncestorsResponse> {\n const { kind, namespace, name } = parseEntityRef(request.entityRef);\n return await this.requestRequired(\n 'GET',\n `/entities/by-name/${encodeURIComponent(kind)}/${encodeURIComponent(\n namespace,\n )}/${encodeURIComponent(name)}/ancestry`,\n options,\n );\n }\n\n /**\n * {@inheritdoc CatalogApi.getLocationById}\n */\n async getLocationById(\n id: string,\n options?: CatalogRequestOptions,\n ): Promise<Location | undefined> {\n return await this.requestOptional(\n 'GET',\n `/locations/${encodeURIComponent(id)}`,\n options,\n );\n }\n\n /**\n * {@inheritdoc CatalogApi.getEntities}\n */\n async getEntities(\n request?: GetEntitiesRequest,\n options?: CatalogRequestOptions,\n ): Promise<GetEntitiesResponse> {\n const { filter = [], fields = [], offset, limit, after } = request ?? {};\n const params: string[] = [];\n\n // filter param can occur multiple times, for example\n // /api/catalog/entities?filter=metadata.name=wayback-search,kind=component&filter=metadata.name=www-artist,kind=component'\n // the \"outer array\" defined by `filter` occurrences corresponds to \"anyOf\" filters\n // the \"inner array\" defined within a `filter` param corresponds to \"allOf\" filters\n for (const filterItem of [filter].flat()) {\n const filterParts: string[] = [];\n for (const [key, value] of Object.entries(filterItem)) {\n for (const v of [value].flat()) {\n if (v === CATALOG_FILTER_EXISTS) {\n filterParts.push(encodeURIComponent(key));\n } else if (typeof v === 'string') {\n filterParts.push(\n `${encodeURIComponent(key)}=${encodeURIComponent(v)}`,\n );\n }\n }\n }\n\n if (filterParts.length) {\n params.push(`filter=${filterParts.join(',')}`);\n }\n }\n\n if (fields.length) {\n params.push(`fields=${fields.map(encodeURIComponent).join(',')}`);\n }\n\n if (offset !== undefined) {\n params.push(`offset=${offset}`);\n }\n if (limit !== undefined) {\n params.push(`limit=${limit}`);\n }\n if (after !== undefined) {\n params.push(`after=${encodeURIComponent(after)}`);\n }\n\n const query = params.length ? `?${params.join('&')}` : '';\n const entities: Entity[] = await this.requestRequired(\n 'GET',\n `/entities${query}`,\n options,\n );\n\n const refCompare = (a: Entity, b: Entity) => {\n // in case field filtering is used, these fields might not be part of the response\n if (\n a.metadata?.name === undefined ||\n a.kind === undefined ||\n b.metadata?.name === undefined ||\n b.kind === undefined\n ) {\n return 0;\n }\n\n const aRef = stringifyEntityRef(a);\n const bRef = stringifyEntityRef(b);\n if (aRef < bRef) {\n return -1;\n }\n if (aRef > bRef) {\n return 1;\n }\n return 0;\n };\n\n return { items: entities.sort(refCompare) };\n }\n\n /**\n * {@inheritdoc CatalogApi.getEntityByName}\n */\n async getEntityByName(\n compoundName: EntityName,\n options?: CatalogRequestOptions,\n ): Promise<Entity | undefined> {\n const { kind, namespace = 'default', name } = compoundName;\n return this.requestOptional(\n 'GET',\n `/entities/by-name/${encodeURIComponent(kind)}/${encodeURIComponent(\n namespace,\n )}/${encodeURIComponent(name)}`,\n options,\n );\n }\n\n /**\n * {@inheritdoc CatalogApi.refreshEntity}\n */\n async refreshEntity(entityRef: string, options?: CatalogRequestOptions) {\n const response = await this.fetchApi.fetch(\n `${await this.discoveryApi.getBaseUrl('catalog')}/refresh`,\n {\n headers: {\n 'Content-Type': 'application/json',\n ...(options?.token && { Authorization: `Bearer ${options?.token}` }),\n },\n method: 'POST',\n body: JSON.stringify({ entityRef }),\n },\n );\n\n if (response.status !== 200) {\n throw new Error(await response.text());\n }\n }\n\n /**\n * {@inheritdoc CatalogApi.getEntityFacets}\n */\n async getEntityFacets(\n request: GetEntityFacetsRequest,\n options?: CatalogRequestOptions,\n ): Promise<GetEntityFacetsResponse> {\n const { filter = [], facets } = request;\n const params: string[] = [];\n\n // filter param can occur multiple times, for example\n // /api/catalog/entities?filter=metadata.name=wayback-search,kind=component&filter=metadata.name=www-artist,kind=component'\n // the \"outer array\" defined by `filter` occurrences corresponds to \"anyOf\" filters\n // the \"inner array\" defined within a `filter` param corresponds to \"allOf\" filters\n for (const filterItem of [filter].flat()) {\n const filterParts: string[] = [];\n for (const [key, value] of Object.entries(filterItem)) {\n for (const v of [value].flat()) {\n if (v === CATALOG_FILTER_EXISTS) {\n filterParts.push(encodeURIComponent(key));\n } else if (typeof v === 'string') {\n filterParts.push(\n `${encodeURIComponent(key)}=${encodeURIComponent(v)}`,\n );\n }\n }\n }\n\n if (filterParts.length) {\n params.push(`filter=${filterParts.join(',')}`);\n }\n }\n\n for (const facet of facets) {\n params.push(`facet=${encodeURIComponent(facet)}`);\n }\n\n const query = params.length ? `?${params.join('&')}` : '';\n return await this.requestOptional('GET', `/entity-facets${query}`, options);\n }\n\n /**\n * {@inheritdoc CatalogApi.addLocation}\n */\n async addLocation(\n { type = 'url', target, dryRun, presence }: AddLocationRequest,\n options?: CatalogRequestOptions,\n ): Promise<AddLocationResponse> {\n const response = await this.fetchApi.fetch(\n `${await this.discoveryApi.getBaseUrl('catalog')}/locations${\n dryRun ? '?dryRun=true' : ''\n }`,\n {\n headers: {\n 'Content-Type': 'application/json',\n ...(options?.token && { Authorization: `Bearer ${options?.token}` }),\n },\n method: 'POST',\n body: JSON.stringify({ type, target, presence }),\n },\n );\n\n if (response.status !== 201) {\n throw new Error(await response.text());\n }\n\n const { location, entities, exists } = await response.json();\n\n if (!location) {\n throw new Error(`Location wasn't added: ${target}`);\n }\n\n return {\n location,\n entities,\n exists,\n };\n }\n\n /**\n * @deprecated please use getLocationByRef instead\n */\n async getOriginLocationByEntity(\n entity: Entity,\n options?: CatalogRequestOptions,\n ): Promise<Location | undefined> {\n const locationCompound =\n entity.metadata.annotations?.[ANNOTATION_ORIGIN_LOCATION];\n if (!locationCompound) {\n return undefined;\n }\n const all: { data: Location }[] = await this.requestRequired(\n 'GET',\n '/locations',\n options,\n );\n return all\n .map(r => r.data)\n .find(l => locationCompound === stringifyLocationRef(l));\n }\n\n /**\n * @deprecated please use getLocationByRef instead\n */\n async getLocationByEntity(\n entity: Entity,\n options?: CatalogRequestOptions,\n ): Promise<Location | undefined> {\n const locationCompound = entity.metadata.annotations?.[ANNOTATION_LOCATION];\n if (!locationCompound) {\n return undefined;\n }\n const all: { data: Location }[] = await this.requestRequired(\n 'GET',\n '/locations',\n options,\n );\n return all\n .map(r => r.data)\n .find(l => locationCompound === stringifyLocationRef(l));\n }\n\n /**\n * {@inheritdoc CatalogApi.getLocationByRef}\n */\n async getLocationByRef(\n locationRef: string,\n options?: CatalogRequestOptions,\n ): Promise<Location | undefined> {\n const all: { data: Location }[] = await this.requestRequired(\n 'GET',\n '/locations',\n options,\n );\n return all\n .map(r => r.data)\n .find(l => locationRef === stringifyLocationRef(l));\n }\n\n /**\n * {@inheritdoc CatalogApi.removeLocationById}\n */\n async removeLocationById(\n id: string,\n options?: CatalogRequestOptions,\n ): Promise<void> {\n await this.requestIgnored(\n 'DELETE',\n `/locations/${encodeURIComponent(id)}`,\n options,\n );\n }\n\n /**\n * {@inheritdoc CatalogApi.removeEntityByUid}\n */\n async removeEntityByUid(\n uid: string,\n options?: CatalogRequestOptions,\n ): Promise<void> {\n await this.requestIgnored(\n 'DELETE',\n `/entities/by-uid/${encodeURIComponent(uid)}`,\n options,\n );\n }\n\n //\n // Private methods\n //\n\n private async requestIgnored(\n method: string,\n path: string,\n options?: CatalogRequestOptions,\n ): Promise<void> {\n const url = `${await this.discoveryApi.getBaseUrl('catalog')}${path}`;\n const headers: Record<string, string> = options?.token\n ? { Authorization: `Bearer ${options.token}` }\n : {};\n const response = await this.fetchApi.fetch(url, { method, headers });\n\n if (!response.ok) {\n throw await ResponseError.fromResponse(response);\n }\n }\n\n private async requestRequired(\n method: string,\n path: string,\n options?: CatalogRequestOptions,\n ): Promise<any> {\n const url = `${await this.discoveryApi.getBaseUrl('catalog')}${path}`;\n const headers: Record<string, string> = options?.token\n ? { Authorization: `Bearer ${options.token}` }\n : {};\n const response = await this.fetchApi.fetch(url, { method, headers });\n\n if (!response.ok) {\n throw await ResponseError.fromResponse(response);\n }\n\n return await response.json();\n }\n\n private async requestOptional(\n method: string,\n path: string,\n options?: CatalogRequestOptions,\n ): Promise<any | undefined> {\n const url = `${await this.discoveryApi.getBaseUrl('catalog')}${path}`;\n const headers: Record<string, string> = options?.token\n ? { Authorization: `Bearer ${options.token}` }\n : {};\n const response = await this.fetchApi.fetch(url, { method, headers });\n\n if (!response.ok) {\n if (response.status === 404) {\n return undefined;\n }\n throw await ResponseError.fromResponse(response);\n }\n\n return await response.json();\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\n/**\n * The entity `status.items[].type` for the status of the processing engine in\n * regards to an entity.\n *\n * @public\n */\nexport const ENTITY_STATUS_CATALOG_PROCESSING_TYPE =\n 'backstage.io/catalog-processing';\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@backstage/catalog-client",
3
3
  "description": "An isomorphic client for the catalog backend",
4
- "version": "0.6.0",
4
+ "version": "0.7.2",
5
5
  "main": "dist/index.cjs.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "license": "Apache-2.0",
@@ -12,6 +12,9 @@
12
12
  "module": "dist/index.esm.js",
13
13
  "types": "dist/index.d.ts"
14
14
  },
15
+ "backstage": {
16
+ "role": "common-library"
17
+ },
15
18
  "homepage": "https://backstage.io",
16
19
  "repository": {
17
20
  "type": "git",
@@ -22,26 +25,26 @@
22
25
  "backstage"
23
26
  ],
24
27
  "scripts": {
25
- "build": "backstage-cli build",
26
- "lint": "backstage-cli lint",
27
- "test": "backstage-cli test",
28
- "prepack": "backstage-cli prepack",
29
- "postpack": "backstage-cli postpack",
30
- "clean": "backstage-cli clean"
28
+ "build": "backstage-cli package build",
29
+ "lint": "backstage-cli package lint",
30
+ "test": "backstage-cli package test",
31
+ "prepack": "backstage-cli package prepack",
32
+ "postpack": "backstage-cli package postpack",
33
+ "clean": "backstage-cli package clean"
31
34
  },
32
35
  "dependencies": {
33
- "@backstage/catalog-model": "^0.9.10",
34
- "@backstage/errors": "^0.2.0",
35
- "cross-fetch": "^3.0.6"
36
+ "@backstage/catalog-model": "^0.11.0",
37
+ "@backstage/errors": "^0.2.2",
38
+ "cross-fetch": "^3.1.5"
36
39
  },
37
40
  "devDependencies": {
38
- "@backstage/cli": "^0.13.2",
41
+ "@backstage/cli": "^0.14.1",
39
42
  "@types/jest": "^26.0.7",
40
43
  "msw": "^0.35.0"
41
44
  },
42
45
  "files": [
43
46
  "dist"
44
47
  ],
45
- "gitHead": "4f4bc77a4152d372b10a4e8d97d92f00e23f3b56",
48
+ "gitHead": "a15da6ea1e3e8adc37be98be064071c8b5279b4a",
46
49
  "module": "dist/index.esm.js"
47
50
  }