@backstage/catalog-client 0.3.16 → 0.4.0

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,40 @@
1
1
  # @backstage/catalog-client
2
2
 
3
+ ## 0.4.0
4
+
5
+ ### Minor Changes
6
+
7
+ - dbcaa6387a: Extends the `CatalogClient` interface with a `refreshEntity` method.
8
+
9
+ ### Patch Changes
10
+
11
+ - 9ef2987a83: Update `AddLocationResponse` to optionally return `exists` to signal that the location already exists, this is only returned when calling `addLocation` in dryRun.
12
+ - Updated dependencies
13
+ - @backstage/catalog-model@0.9.3
14
+ - @backstage/config@0.1.10
15
+
16
+ ## 0.3.19
17
+
18
+ ### Patch Changes
19
+
20
+ - d1da88a19: Properly export all used types.
21
+ - Updated dependencies
22
+ - @backstage/catalog-model@0.9.2
23
+ - @backstage/errors@0.1.2
24
+ - @backstage/config@0.1.9
25
+
26
+ ## 0.3.18
27
+
28
+ ### Patch Changes
29
+
30
+ - 11c370af2: Support filtering entities via property existence
31
+
32
+ ## 0.3.17
33
+
34
+ ### Patch Changes
35
+
36
+ - 71c936eb6: Export `CatalogRequestOptions` type
37
+
3
38
  ## 0.3.16
4
39
 
5
40
  ### Patch Changes
package/dist/index.cjs.js CHANGED
@@ -10,6 +10,8 @@ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'defau
10
10
 
11
11
  var fetch__default = /*#__PURE__*/_interopDefaultLegacy(fetch);
12
12
 
13
+ const CATALOG_FILTER_EXISTS = Symbol("CATALOG_FILTER_EXISTS");
14
+
13
15
  class CatalogClient {
14
16
  constructor(options) {
15
17
  this.discoveryApi = options.discoveryApi;
@@ -25,7 +27,11 @@ class CatalogClient {
25
27
  const filterParts = [];
26
28
  for (const [key, value] of Object.entries(filterItem)) {
27
29
  for (const v of [value].flat()) {
28
- filterParts.push(`${encodeURIComponent(key)}=${encodeURIComponent(v)}`);
30
+ if (v === CATALOG_FILTER_EXISTS) {
31
+ filterParts.push(encodeURIComponent(key));
32
+ } else if (typeof v === "string") {
33
+ filterParts.push(`${encodeURIComponent(key)}=${encodeURIComponent(v)}`);
34
+ }
29
35
  }
30
36
  }
31
37
  if (filterParts.length) {
@@ -58,6 +64,19 @@ class CatalogClient {
58
64
  const {kind, namespace = "default", name} = compoundName;
59
65
  return this.requestOptional("GET", `/entities/by-name/${encodeURIComponent(kind)}/${encodeURIComponent(namespace)}/${encodeURIComponent(name)}`, options);
60
66
  }
67
+ async refreshEntity(entityRef, options) {
68
+ const response = await fetch__default['default'](`${await this.discoveryApi.getBaseUrl("catalog")}/refresh`, {
69
+ headers: {
70
+ "Content-Type": "application/json",
71
+ ...(options == null ? void 0 : options.token) && {Authorization: `Bearer ${options == null ? void 0 : options.token}`}
72
+ },
73
+ method: "POST",
74
+ body: JSON.stringify({entityRef})
75
+ });
76
+ if (response.status !== 200) {
77
+ throw new Error(await response.text());
78
+ }
79
+ }
61
80
  async addLocation({type = "url", target, dryRun, presence}, options) {
62
81
  const response = await fetch__default['default'](`${await this.discoveryApi.getBaseUrl("catalog")}/locations${dryRun ? "?dryRun=true" : ""}`, {
63
82
  headers: {
@@ -70,13 +89,14 @@ class CatalogClient {
70
89
  if (response.status !== 201) {
71
90
  throw new Error(await response.text());
72
91
  }
73
- const {location, entities} = await response.json();
92
+ const {location, entities, exists} = await response.json();
74
93
  if (!location) {
75
94
  throw new Error(`Location wasn't added: ${target}`);
76
95
  }
77
96
  return {
78
97
  location,
79
- entities
98
+ entities,
99
+ exists
80
100
  };
81
101
  }
82
102
  async getOriginLocationByEntity(entity, options) {
@@ -136,6 +156,7 @@ class CatalogClient {
136
156
 
137
157
  const ENTITY_STATUS_CATALOG_PROCESSING_TYPE = "backstage.io/catalog-processing";
138
158
 
159
+ exports.CATALOG_FILTER_EXISTS = CATALOG_FILTER_EXISTS;
139
160
  exports.CatalogClient = CatalogClient;
140
161
  exports.ENTITY_STATUS_CATALOG_PROCESSING_TYPE = ENTITY_STATUS_CATALOG_PROCESSING_TYPE;
141
162
  //# sourceMappingURL=index.cjs.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs.js","sources":["../src/CatalogClient.ts","../src/types/status.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n Entity,\n EntityName,\n Location,\n LOCATION_ANNOTATION,\n ORIGIN_LOCATION_ANNOTATION,\n stringifyEntityRef,\n stringifyLocationReference,\n} from '@backstage/catalog-model';\nimport { ResponseError } from '@backstage/errors';\nimport fetch from 'cross-fetch';\nimport {\n AddLocationRequest,\n AddLocationResponse,\n CatalogApi,\n CatalogEntitiesRequest,\n CatalogListResponse,\n CatalogRequestOptions,\n} from './types/api';\nimport { DiscoveryApi } from './types/discovery';\n\nexport class CatalogClient implements CatalogApi {\n private readonly discoveryApi: DiscoveryApi;\n\n constructor(options: { discoveryApi: DiscoveryApi }) {\n this.discoveryApi = options.discoveryApi;\n }\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 async getEntities(\n request?: CatalogEntitiesRequest,\n options?: CatalogRequestOptions,\n ): Promise<CatalogListResponse<Entity>> {\n const { filter = [], fields = [] } = 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 filterParts.push(\n `${encodeURIComponent(key)}=${encodeURIComponent(v)}`,\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 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 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 async addLocation(\n { type = 'url', target, dryRun, presence }: AddLocationRequest,\n options?: CatalogRequestOptions,\n ): Promise<AddLocationResponse> {\n const response = await 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 } = 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 };\n }\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 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 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 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 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 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 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 */\nexport const ENTITY_STATUS_CATALOG_PROCESSING_TYPE =\n 'backstage.io/catalog-processing';\n"],"names":["stringifyEntityRef","fetch","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, Location } from '@backstage/catalog-model';\n\n/** @public */\nexport const CATALOG_FILTER_EXISTS = Symbol('CATALOG_FILTER_EXISTS');\n\n/** @public */\nexport type CatalogEntitiesRequest = {\n filter?:\n | Record<string, string | symbol | (string | symbol)[]>[]\n | Record<string, string | symbol | (string | symbol)[]>\n | undefined;\n fields?: string[] | undefined;\n};\n\n/** @public */\nexport type CatalogListResponse<T> = {\n items: T[];\n};\n\n/** @public */\nexport type CatalogRequestOptions = {\n token?: string;\n};\n\n/** @public */\nexport interface CatalogApi {\n // Entities\n getEntities(\n request?: CatalogEntitiesRequest,\n options?: CatalogRequestOptions,\n ): Promise<CatalogListResponse<Entity>>;\n getEntityByName(\n name: EntityName,\n options?: CatalogRequestOptions,\n ): Promise<Entity | undefined>;\n removeEntityByUid(\n uid: string,\n options?: CatalogRequestOptions,\n ): Promise<void>;\n refreshEntity(\n entityRef: string,\n options?: CatalogRequestOptions,\n ): Promise<void>;\n\n // Locations\n getLocationById(\n id: string,\n options?: CatalogRequestOptions,\n ): Promise<Location | undefined>;\n getOriginLocationByEntity(\n entity: Entity,\n options?: CatalogRequestOptions,\n ): Promise<Location | undefined>;\n getLocationByEntity(\n entity: Entity,\n options?: CatalogRequestOptions,\n ): Promise<Location | undefined>;\n addLocation(\n location: AddLocationRequest,\n options?: CatalogRequestOptions,\n ): Promise<AddLocationResponse>;\n removeLocationById(\n id: string,\n options?: CatalogRequestOptions,\n ): Promise<void>;\n}\n\n/** @public */\nexport type AddLocationRequest = {\n type?: string;\n target: string;\n dryRun?: boolean;\n presence?: 'optional' | 'required';\n};\n\n/** @public */\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 stringifyEntityRef,\n stringifyLocationReference,\n} from '@backstage/catalog-model';\nimport { ResponseError } from '@backstage/errors';\nimport fetch from 'cross-fetch';\nimport {\n CATALOG_FILTER_EXISTS,\n AddLocationRequest,\n AddLocationResponse,\n CatalogApi,\n CatalogEntitiesRequest,\n CatalogListResponse,\n CatalogRequestOptions,\n} from './types/api';\nimport { DiscoveryApi } from './types/discovery';\n\n/** @public */\nexport class CatalogClient implements CatalogApi {\n private readonly discoveryApi: DiscoveryApi;\n\n constructor(options: { discoveryApi: DiscoveryApi }) {\n this.discoveryApi = options.discoveryApi;\n }\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 async getEntities(\n request?: CatalogEntitiesRequest,\n options?: CatalogRequestOptions,\n ): Promise<CatalogListResponse<Entity>> {\n const { filter = [], fields = [] } = 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 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 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 async refreshEntity(entityRef: string, options?: CatalogRequestOptions) {\n const response = await 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 async addLocation(\n { type = 'url', target, dryRun, presence }: AddLocationRequest,\n options?: CatalogRequestOptions,\n ): Promise<AddLocationResponse> {\n const response = await 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 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 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 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 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 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 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 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":["stringifyEntityRef","fetch","ORIGIN_LOCATION_ANNOTATION","stringifyLocationReference","LOCATION_ANNOTATION","ResponseError"],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,43 +1,56 @@
1
1
  import { Entity, EntityName, Location } from '@backstage/catalog-model';
2
2
 
3
+ /** @public */
4
+ declare const CATALOG_FILTER_EXISTS: unique symbol;
5
+ /** @public */
3
6
  declare type CatalogEntitiesRequest = {
4
- filter?: Record<string, string | string[]>[] | Record<string, string | string[]> | undefined;
7
+ filter?: Record<string, string | symbol | (string | symbol)[]>[] | Record<string, string | symbol | (string | symbol)[]> | undefined;
5
8
  fields?: string[] | undefined;
6
9
  };
10
+ /** @public */
7
11
  declare type CatalogListResponse<T> = {
8
12
  items: T[];
9
13
  };
14
+ /** @public */
10
15
  declare type CatalogRequestOptions = {
11
16
  token?: string;
12
17
  };
18
+ /** @public */
13
19
  interface CatalogApi {
14
20
  getEntities(request?: CatalogEntitiesRequest, options?: CatalogRequestOptions): Promise<CatalogListResponse<Entity>>;
15
21
  getEntityByName(name: EntityName, options?: CatalogRequestOptions): Promise<Entity | undefined>;
16
22
  removeEntityByUid(uid: string, options?: CatalogRequestOptions): Promise<void>;
23
+ refreshEntity(entityRef: string, options?: CatalogRequestOptions): Promise<void>;
17
24
  getLocationById(id: string, options?: CatalogRequestOptions): Promise<Location | undefined>;
18
25
  getOriginLocationByEntity(entity: Entity, options?: CatalogRequestOptions): Promise<Location | undefined>;
19
26
  getLocationByEntity(entity: Entity, options?: CatalogRequestOptions): Promise<Location | undefined>;
20
27
  addLocation(location: AddLocationRequest, options?: CatalogRequestOptions): Promise<AddLocationResponse>;
21
28
  removeLocationById(id: string, options?: CatalogRequestOptions): Promise<void>;
22
29
  }
30
+ /** @public */
23
31
  declare type AddLocationRequest = {
24
32
  type?: string;
25
33
  target: string;
26
34
  dryRun?: boolean;
27
35
  presence?: 'optional' | 'required';
28
36
  };
37
+ /** @public */
29
38
  declare type AddLocationResponse = {
30
39
  location: Location;
31
40
  entities: Entity[];
41
+ exists?: boolean;
32
42
  };
33
43
 
34
44
  /**
35
45
  * This is a copy of the core DiscoveryApi, to avoid importing core.
46
+ *
47
+ * @public
36
48
  */
37
49
  declare type DiscoveryApi = {
38
50
  getBaseUrl(pluginId: string): Promise<string>;
39
51
  };
40
52
 
53
+ /** @public */
41
54
  declare class CatalogClient implements CatalogApi {
42
55
  private readonly discoveryApi;
43
56
  constructor(options: {
@@ -46,6 +59,7 @@ declare class CatalogClient implements CatalogApi {
46
59
  getLocationById(id: string, options?: CatalogRequestOptions): Promise<Location | undefined>;
47
60
  getEntities(request?: CatalogEntitiesRequest, options?: CatalogRequestOptions): Promise<CatalogListResponse<Entity>>;
48
61
  getEntityByName(compoundName: EntityName, options?: CatalogRequestOptions): Promise<Entity | undefined>;
62
+ refreshEntity(entityRef: string, options?: CatalogRequestOptions): Promise<void>;
49
63
  addLocation({ type, target, dryRun, presence }: AddLocationRequest, options?: CatalogRequestOptions): Promise<AddLocationResponse>;
50
64
  getOriginLocationByEntity(entity: Entity, options?: CatalogRequestOptions): Promise<Location | undefined>;
51
65
  getLocationByEntity(entity: Entity, options?: CatalogRequestOptions): Promise<Location | undefined>;
@@ -59,7 +73,9 @@ declare class CatalogClient implements CatalogApi {
59
73
  /**
60
74
  * The entity `status.items[].type` for the status of the processing engine in
61
75
  * regards to an entity.
76
+ *
77
+ * @public
62
78
  */
63
79
  declare const ENTITY_STATUS_CATALOG_PROCESSING_TYPE = "backstage.io/catalog-processing";
64
80
 
65
- export { AddLocationRequest, AddLocationResponse, CatalogApi, CatalogClient, CatalogEntitiesRequest, CatalogListResponse, ENTITY_STATUS_CATALOG_PROCESSING_TYPE };
81
+ export { AddLocationRequest, AddLocationResponse, CATALOG_FILTER_EXISTS, CatalogApi, CatalogClient, CatalogEntitiesRequest, CatalogListResponse, CatalogRequestOptions, DiscoveryApi, ENTITY_STATUS_CATALOG_PROCESSING_TYPE };
package/dist/index.esm.js CHANGED
@@ -2,6 +2,8 @@ import { ORIGIN_LOCATION_ANNOTATION, stringifyLocationReference, LOCATION_ANNOTA
2
2
  import { ResponseError } from '@backstage/errors';
3
3
  import fetch from 'cross-fetch';
4
4
 
5
+ const CATALOG_FILTER_EXISTS = Symbol("CATALOG_FILTER_EXISTS");
6
+
5
7
  class CatalogClient {
6
8
  constructor(options) {
7
9
  this.discoveryApi = options.discoveryApi;
@@ -17,7 +19,11 @@ class CatalogClient {
17
19
  const filterParts = [];
18
20
  for (const [key, value] of Object.entries(filterItem)) {
19
21
  for (const v of [value].flat()) {
20
- filterParts.push(`${encodeURIComponent(key)}=${encodeURIComponent(v)}`);
22
+ if (v === CATALOG_FILTER_EXISTS) {
23
+ filterParts.push(encodeURIComponent(key));
24
+ } else if (typeof v === "string") {
25
+ filterParts.push(`${encodeURIComponent(key)}=${encodeURIComponent(v)}`);
26
+ }
21
27
  }
22
28
  }
23
29
  if (filterParts.length) {
@@ -50,6 +56,19 @@ class CatalogClient {
50
56
  const {kind, namespace = "default", name} = compoundName;
51
57
  return this.requestOptional("GET", `/entities/by-name/${encodeURIComponent(kind)}/${encodeURIComponent(namespace)}/${encodeURIComponent(name)}`, options);
52
58
  }
59
+ async refreshEntity(entityRef, options) {
60
+ const response = await fetch(`${await this.discoveryApi.getBaseUrl("catalog")}/refresh`, {
61
+ headers: {
62
+ "Content-Type": "application/json",
63
+ ...(options == null ? void 0 : options.token) && {Authorization: `Bearer ${options == null ? void 0 : options.token}`}
64
+ },
65
+ method: "POST",
66
+ body: JSON.stringify({entityRef})
67
+ });
68
+ if (response.status !== 200) {
69
+ throw new Error(await response.text());
70
+ }
71
+ }
53
72
  async addLocation({type = "url", target, dryRun, presence}, options) {
54
73
  const response = await fetch(`${await this.discoveryApi.getBaseUrl("catalog")}/locations${dryRun ? "?dryRun=true" : ""}`, {
55
74
  headers: {
@@ -62,13 +81,14 @@ class CatalogClient {
62
81
  if (response.status !== 201) {
63
82
  throw new Error(await response.text());
64
83
  }
65
- const {location, entities} = await response.json();
84
+ const {location, entities, exists} = await response.json();
66
85
  if (!location) {
67
86
  throw new Error(`Location wasn't added: ${target}`);
68
87
  }
69
88
  return {
70
89
  location,
71
- entities
90
+ entities,
91
+ exists
72
92
  };
73
93
  }
74
94
  async getOriginLocationByEntity(entity, options) {
@@ -128,5 +148,5 @@ class CatalogClient {
128
148
 
129
149
  const ENTITY_STATUS_CATALOG_PROCESSING_TYPE = "backstage.io/catalog-processing";
130
150
 
131
- export { CatalogClient, ENTITY_STATUS_CATALOG_PROCESSING_TYPE };
151
+ export { CATALOG_FILTER_EXISTS, CatalogClient, ENTITY_STATUS_CATALOG_PROCESSING_TYPE };
132
152
  //# sourceMappingURL=index.esm.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.esm.js","sources":["../src/CatalogClient.ts","../src/types/status.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n Entity,\n EntityName,\n Location,\n LOCATION_ANNOTATION,\n ORIGIN_LOCATION_ANNOTATION,\n stringifyEntityRef,\n stringifyLocationReference,\n} from '@backstage/catalog-model';\nimport { ResponseError } from '@backstage/errors';\nimport fetch from 'cross-fetch';\nimport {\n AddLocationRequest,\n AddLocationResponse,\n CatalogApi,\n CatalogEntitiesRequest,\n CatalogListResponse,\n CatalogRequestOptions,\n} from './types/api';\nimport { DiscoveryApi } from './types/discovery';\n\nexport class CatalogClient implements CatalogApi {\n private readonly discoveryApi: DiscoveryApi;\n\n constructor(options: { discoveryApi: DiscoveryApi }) {\n this.discoveryApi = options.discoveryApi;\n }\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 async getEntities(\n request?: CatalogEntitiesRequest,\n options?: CatalogRequestOptions,\n ): Promise<CatalogListResponse<Entity>> {\n const { filter = [], fields = [] } = 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 filterParts.push(\n `${encodeURIComponent(key)}=${encodeURIComponent(v)}`,\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 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 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 async addLocation(\n { type = 'url', target, dryRun, presence }: AddLocationRequest,\n options?: CatalogRequestOptions,\n ): Promise<AddLocationResponse> {\n const response = await 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 } = 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 };\n }\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 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 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 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 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 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 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 */\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, Location } from '@backstage/catalog-model';\n\n/** @public */\nexport const CATALOG_FILTER_EXISTS = Symbol('CATALOG_FILTER_EXISTS');\n\n/** @public */\nexport type CatalogEntitiesRequest = {\n filter?:\n | Record<string, string | symbol | (string | symbol)[]>[]\n | Record<string, string | symbol | (string | symbol)[]>\n | undefined;\n fields?: string[] | undefined;\n};\n\n/** @public */\nexport type CatalogListResponse<T> = {\n items: T[];\n};\n\n/** @public */\nexport type CatalogRequestOptions = {\n token?: string;\n};\n\n/** @public */\nexport interface CatalogApi {\n // Entities\n getEntities(\n request?: CatalogEntitiesRequest,\n options?: CatalogRequestOptions,\n ): Promise<CatalogListResponse<Entity>>;\n getEntityByName(\n name: EntityName,\n options?: CatalogRequestOptions,\n ): Promise<Entity | undefined>;\n removeEntityByUid(\n uid: string,\n options?: CatalogRequestOptions,\n ): Promise<void>;\n refreshEntity(\n entityRef: string,\n options?: CatalogRequestOptions,\n ): Promise<void>;\n\n // Locations\n getLocationById(\n id: string,\n options?: CatalogRequestOptions,\n ): Promise<Location | undefined>;\n getOriginLocationByEntity(\n entity: Entity,\n options?: CatalogRequestOptions,\n ): Promise<Location | undefined>;\n getLocationByEntity(\n entity: Entity,\n options?: CatalogRequestOptions,\n ): Promise<Location | undefined>;\n addLocation(\n location: AddLocationRequest,\n options?: CatalogRequestOptions,\n ): Promise<AddLocationResponse>;\n removeLocationById(\n id: string,\n options?: CatalogRequestOptions,\n ): Promise<void>;\n}\n\n/** @public */\nexport type AddLocationRequest = {\n type?: string;\n target: string;\n dryRun?: boolean;\n presence?: 'optional' | 'required';\n};\n\n/** @public */\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 stringifyEntityRef,\n stringifyLocationReference,\n} from '@backstage/catalog-model';\nimport { ResponseError } from '@backstage/errors';\nimport fetch from 'cross-fetch';\nimport {\n CATALOG_FILTER_EXISTS,\n AddLocationRequest,\n AddLocationResponse,\n CatalogApi,\n CatalogEntitiesRequest,\n CatalogListResponse,\n CatalogRequestOptions,\n} from './types/api';\nimport { DiscoveryApi } from './types/discovery';\n\n/** @public */\nexport class CatalogClient implements CatalogApi {\n private readonly discoveryApi: DiscoveryApi;\n\n constructor(options: { discoveryApi: DiscoveryApi }) {\n this.discoveryApi = options.discoveryApi;\n }\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 async getEntities(\n request?: CatalogEntitiesRequest,\n options?: CatalogRequestOptions,\n ): Promise<CatalogListResponse<Entity>> {\n const { filter = [], fields = [] } = 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 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 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 async refreshEntity(entityRef: string, options?: CatalogRequestOptions) {\n const response = await 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 async addLocation(\n { type = 'url', target, dryRun, presence }: AddLocationRequest,\n options?: CatalogRequestOptions,\n ): Promise<AddLocationResponse> {\n const response = await 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 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 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 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 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 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 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 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,6 +1,7 @@
1
1
  {
2
2
  "name": "@backstage/catalog-client",
3
- "version": "0.3.16",
3
+ "description": "An isomorphic client for the catalog backend",
4
+ "version": "0.4.0",
4
5
  "main": "dist/index.cjs.js",
5
6
  "types": "dist/index.d.ts",
6
7
  "license": "Apache-2.0",
@@ -29,19 +30,19 @@
29
30
  "clean": "backstage-cli clean"
30
31
  },
31
32
  "dependencies": {
32
- "@backstage/catalog-model": "^0.9.0",
33
- "@backstage/config": "^0.1.5",
34
- "@backstage/errors": "^0.1.1",
33
+ "@backstage/catalog-model": "^0.9.3",
34
+ "@backstage/config": "^0.1.10",
35
+ "@backstage/errors": "^0.1.2",
35
36
  "cross-fetch": "^3.0.6"
36
37
  },
37
38
  "devDependencies": {
38
- "@backstage/cli": "^0.7.3",
39
+ "@backstage/cli": "^0.7.13",
39
40
  "@types/jest": "^26.0.7",
40
41
  "msw": "^0.29.0"
41
42
  },
42
43
  "files": [
43
44
  "dist"
44
45
  ],
45
- "gitHead": "6cebb9d587224c055516d1ab2958f34bd3659c43",
46
+ "gitHead": "2fd2dbc5d2fad9da5f623126920958970aea469b",
46
47
  "module": "dist/index.esm.js"
47
48
  }