@backstage/plugin-catalog-backend 1.3.1-next.2 → 1.4.0-next.1

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,57 @@
1
1
  # @backstage/plugin-catalog-backend
2
2
 
3
+ ## 1.4.0-next.1
4
+
5
+ ### Minor Changes
6
+
7
+ - dd395335bc: Allow unknown typed location from being registered via the location service by configuration settings
8
+ - 651c9d6800: The search index now does retain fields that have a very long value, but in the form of just a null. This makes it possible to at least filter for their existence.
9
+
10
+ ### Patch Changes
11
+
12
+ - ce77e78c93: Fixes a bug to be able to utilize refresh keys after the entity is loaded from cache
13
+ - 679f7c5e95: Include entity ref into error message when catalog policies fail
14
+ - Updated dependencies
15
+ - @backstage/plugin-permission-node@0.6.5-next.1
16
+ - @backstage/backend-common@0.15.1-next.1
17
+
18
+ ## 1.3.2-next.0
19
+
20
+ ### Patch Changes
21
+
22
+ - 243533ecdc: Added support to mysql on some raw queries
23
+ - bf5e9030eb: Updated dependency `msw` to `^0.45.0`.
24
+ - 62788b2ee8: The experimental `CatalogProcessingExtensionPoint` now accepts multiple providers and processors at once.
25
+ - Updated dependencies
26
+ - @backstage/backend-common@0.15.1-next.0
27
+ - @backstage/backend-plugin-api@0.1.2-next.0
28
+ - @backstage/catalog-client@1.0.5-next.0
29
+ - @backstage/integration@1.3.1-next.0
30
+ - @backstage/plugin-permission-common@0.6.4-next.0
31
+ - @backstage/plugin-permission-node@0.6.5-next.0
32
+ - @backstage/plugin-scaffolder-common@1.2.0-next.0
33
+ - @backstage/plugin-catalog-node@1.0.2-next.0
34
+ - @backstage/plugin-catalog-common@1.0.6-next.0
35
+ - @backstage/plugin-search-common@1.0.1-next.0
36
+
37
+ ## 1.3.1
38
+
39
+ ### Patch Changes
40
+
41
+ - 56e1b4b89c: Fixed typos in alpha types.
42
+ - e3d3018531: Fix issue for conditional decisions based on properties stored as arrays, like tags.
43
+
44
+ Before this change, having a permission policy returning conditional decisions based on metadata like tags, such like `createCatalogConditionalDecision(permission, catalogConditions.hasMetadata('tags', 'java'),)`, was producing wrong results. The issue occurred when authorizing entities already loaded from the database, for example when authorizing `catalogEntityDeletePermission`.
45
+
46
+ - 059ae348b4: Use the non-deprecated form of table.unique in knex
47
+ - Updated dependencies
48
+ - @backstage/backend-common@0.15.0
49
+ - @backstage/backend-plugin-api@0.1.1
50
+ - @backstage/plugin-catalog-node@1.0.1
51
+ - @backstage/integration@1.3.0
52
+ - @backstage/plugin-catalog-common@1.0.5
53
+ - @backstage/plugin-permission-node@0.6.4
54
+
3
55
  ## 1.3.1-next.2
4
56
 
5
57
  ### Patch Changes
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backstage/plugin-catalog-backend",
3
- "version": "1.3.1-next.2",
3
+ "version": "1.4.0-next.1",
4
4
  "main": "../dist/index.cjs.js",
5
5
  "types": "../dist/index.alpha.d.ts"
6
6
  }
@@ -6,7 +6,7 @@
6
6
 
7
7
  /// <reference types="node" />
8
8
 
9
- import { BackendRegistrable } from '@backstage/backend-plugin-api';
9
+ import { BackendFeature } from '@backstage/backend-plugin-api';
10
10
  import { CatalogApi } from '@backstage/catalog-client';
11
11
  import { CatalogEntityDocument } from '@backstage/plugin-catalog-common';
12
12
  import { CatalogProcessor } from '@backstage/plugin-catalog-node';
@@ -171,7 +171,8 @@ export declare class CatalogBuilder {
171
171
  private onProcessingError?;
172
172
  private processingInterval;
173
173
  private locationAnalyzer;
174
- private permissionRules;
174
+ private readonly permissionRules;
175
+ private allowedLocationType;
175
176
  /**
176
177
  * Creates a catalog builder.
177
178
  */
@@ -293,6 +294,12 @@ export declare class CatalogBuilder {
293
294
  * @alpha
294
295
  */
295
296
  addPermissionRules(...permissionRules: Array<CatalogPermissionRule | Array<CatalogPermissionRule>>): void;
297
+ /**
298
+ * Sets up the allowed location types from being registered via the location service.
299
+ *
300
+ * @param allowedLocationTypes - the allowed location types
301
+ */
302
+ setAllowedLocationTypes(allowedLocationTypes: string[]): CatalogBuilder;
296
303
  /**
297
304
  * Wires up and returns all of the component parts of the catalog
298
305
  */
@@ -349,7 +356,7 @@ export declare type CatalogPermissionRule<TParams extends unknown[] = unknown[]>
349
356
  * Catalog plugin
350
357
  * @alpha
351
358
  */
352
- export declare const catalogPlugin: (option: unknown) => BackendRegistrable;
359
+ export declare const catalogPlugin: (options?: unknown) => BackendFeature;
353
360
 
354
361
  /** @public */
355
362
  export declare interface CatalogProcessingEngine {
@@ -6,7 +6,7 @@
6
6
 
7
7
  /// <reference types="node" />
8
8
 
9
- import { BackendRegistrable } from '@backstage/backend-plugin-api';
9
+ import { BackendFeature } from '@backstage/backend-plugin-api';
10
10
  import { CatalogApi } from '@backstage/catalog-client';
11
11
  import { CatalogEntityDocument } from '@backstage/plugin-catalog-common';
12
12
  import { CatalogProcessor } from '@backstage/plugin-catalog-node';
@@ -171,7 +171,8 @@ export declare class CatalogBuilder {
171
171
  private onProcessingError?;
172
172
  private processingInterval;
173
173
  private locationAnalyzer;
174
- private permissionRules;
174
+ private readonly permissionRules;
175
+ private allowedLocationType;
175
176
  /**
176
177
  * Creates a catalog builder.
177
178
  */
@@ -285,6 +286,12 @@ export declare class CatalogBuilder {
285
286
  */
286
287
  setEntityDataParser(parser: CatalogProcessorParser): CatalogBuilder;
287
288
  /* Excluded from this release type: addPermissionRules */
289
+ /**
290
+ * Sets up the allowed location types from being registered via the location service.
291
+ *
292
+ * @param allowedLocationTypes - the allowed location types
293
+ */
294
+ setAllowedLocationTypes(allowedLocationTypes: string[]): CatalogBuilder;
288
295
  /**
289
296
  * Wires up and returns all of the component parts of the catalog
290
297
  */
package/dist/index.cjs.js CHANGED
@@ -754,6 +754,7 @@ class UrlReaderProcessor {
754
754
  for (const parseResult of cacheItem.value) {
755
755
  emit(parseResult);
756
756
  }
757
+ emit(pluginCatalogNode.processingResult.refresh(`${location.type}:${location.target}`));
757
758
  } else if (error.name === "NotFoundError") {
758
759
  if (!optional) {
759
760
  emit(pluginCatalogNode.processingResult.notFoundError(location, message));
@@ -1506,7 +1507,7 @@ class DefaultProcessingDatabase {
1506
1507
  sourceEntityRef
1507
1508
  });
1508
1509
  let previousRelationRows;
1509
- if (tx.client.config.client.includes("sqlite3")) {
1510
+ if (tx.client.config.client.includes("sqlite3") || tx.client.config.client.includes("mysql")) {
1510
1511
  previousRelationRows = await tx("relations").select("*").where({ originating_entity_id: id });
1511
1512
  await tx("relations").where({ originating_entity_id: id }).delete();
1512
1513
  } else {
@@ -1558,6 +1559,12 @@ class DefaultProcessingDatabase {
1558
1559
  const { toAdd, toUpsert, toRemove } = await this.createDelta(tx, options);
1559
1560
  if (toRemove.length) {
1560
1561
  let removedCount = 0;
1562
+ const rootId = () => {
1563
+ if (tx.client.config.client.includes("mysql")) {
1564
+ return tx.raw("CAST(NULL as UNSIGNED INT)", []);
1565
+ }
1566
+ return tx.raw("CAST(NULL as INT)", []);
1567
+ };
1561
1568
  for (const refs of lodash__default["default"].chunk(toRemove, 1e3)) {
1562
1569
  removedCount += await tx("refresh_state").whereIn("entity_ref", function orphanedEntityRefs(orphans) {
1563
1570
  return orphans.withRecursive("descendants", function descendants(outer) {
@@ -1571,7 +1578,7 @@ class DefaultProcessingDatabase {
1571
1578
  });
1572
1579
  }).withRecursive("ancestors", function ancestors(outer) {
1573
1580
  return outer.select({
1574
- root_id: tx.raw("CAST(NULL as INT)", []),
1581
+ root_id: rootId(),
1575
1582
  via_entity_ref: "entity_ref",
1576
1583
  to_entity_ref: "entity_ref"
1577
1584
  }).from("descendants").union(function recursive(inner) {
@@ -1695,11 +1702,20 @@ class DefaultProcessingDatabase {
1695
1702
  }
1696
1703
  const items = await itemsQuery.where("next_update_at", "<=", tx.fn.now()).limit(request.processBatchSize).orderBy("next_update_at", "asc");
1697
1704
  const interval = this.options.refreshInterval();
1705
+ const nextUpdateAt = (refreshInterval) => {
1706
+ if (tx.client.config.client.includes("sqlite3")) {
1707
+ return tx.raw(`datetime('now', ?)`, [`${refreshInterval} seconds`]);
1708
+ }
1709
+ if (tx.client.config.client.includes("mysql")) {
1710
+ return tx.raw(`now() + interval ${refreshInterval} second`);
1711
+ }
1712
+ return tx.raw(`now() + interval '${refreshInterval} seconds'`);
1713
+ };
1698
1714
  await tx("refresh_state").whereIn(
1699
1715
  "entity_ref",
1700
1716
  items.map((i) => i.entity_ref)
1701
1717
  ).update({
1702
- next_update_at: tx.client.config.client.includes("sqlite3") ? tx.raw(`datetime('now', ?)`, [`${interval} seconds`]) : tx.raw(`now() + interval '${interval} seconds'`)
1718
+ next_update_at: nextUpdateAt(interval)
1703
1719
  });
1704
1720
  return {
1705
1721
  items: items.map(
@@ -2246,13 +2262,20 @@ function progressTracker() {
2246
2262
  }
2247
2263
 
2248
2264
  class DefaultLocationService {
2249
- constructor(store, orchestrator) {
2265
+ constructor(store, orchestrator, options = {
2266
+ allowedLocationTypes: ["url"]
2267
+ }) {
2250
2268
  this.store = store;
2251
2269
  this.orchestrator = orchestrator;
2270
+ this.options = options;
2252
2271
  }
2253
2272
  async createLocation(input, dryRun) {
2254
- if (input.type !== "url") {
2255
- throw new errors.InputError(`Registered locations must be of type 'url'`);
2273
+ if (!this.options.allowedLocationTypes.includes(input.type)) {
2274
+ throw new errors.InputError(
2275
+ `Registered locations must be of an allowed type ${JSON.stringify(
2276
+ this.options.allowedLocationTypes
2277
+ )}`
2278
+ );
2256
2279
  }
2257
2280
  if (dryRun) {
2258
2281
  return this.dryRunCreateLocation(input);
@@ -2808,10 +2831,17 @@ class DefaultCatalogProcessingOrchestrator {
2808
2831
  try {
2809
2832
  policyEnforcedEntity = await this.options.policy.enforce(entity);
2810
2833
  } catch (e) {
2811
- throw new errors.InputError("Policy check failed", e);
2834
+ throw new errors.InputError(
2835
+ `Policy check failed for ${catalogModel.stringifyEntityRef(entity)}`,
2836
+ e
2837
+ );
2812
2838
  }
2813
2839
  if (!policyEnforcedEntity) {
2814
- throw new Error("Policy unexpectedly returned no data");
2840
+ throw new Error(
2841
+ `Policy unexpectedly returned no data for ${catalogModel.stringifyEntityRef(
2842
+ entity
2843
+ )}`
2844
+ );
2815
2845
  }
2816
2846
  return policyEnforcedEntity;
2817
2847
  }
@@ -2981,8 +3011,12 @@ function mapToRows(input, entityId) {
2981
3011
  result.push({ entity_id: entityId, key, value: null });
2982
3012
  } else {
2983
3013
  const value = String(rawValue).toLocaleLowerCase("en-US");
2984
- if (key.length <= MAX_KEY_LENGTH && value.length <= MAX_VALUE_LENGTH) {
2985
- result.push({ entity_id: entityId, key, value });
3014
+ if (key.length <= MAX_KEY_LENGTH) {
3015
+ result.push({
3016
+ entity_id: entityId,
3017
+ key,
3018
+ value: value.length <= MAX_VALUE_LENGTH ? value : null
3019
+ });
2986
3020
  }
2987
3021
  }
2988
3022
  }
@@ -3905,6 +3939,7 @@ class CatalogBuilder {
3905
3939
  this.processorsReplace = false;
3906
3940
  this.parser = void 0;
3907
3941
  this.permissionRules = Object.values(permissionRules);
3942
+ this.allowedLocationType = ["url"];
3908
3943
  }
3909
3944
  static create(env) {
3910
3945
  return new CatalogBuilder(env);
@@ -3971,6 +4006,10 @@ class CatalogBuilder {
3971
4006
  addPermissionRules(...permissionRules) {
3972
4007
  this.permissionRules.push(...permissionRules.flat());
3973
4008
  }
4009
+ setAllowedLocationTypes(allowedLocationTypes) {
4010
+ this.allowedLocationType = allowedLocationTypes;
4011
+ return this;
4012
+ }
3974
4013
  async build() {
3975
4014
  var _a, _b;
3976
4015
  const { config, database, logger, permissions } = this.env;
@@ -4056,7 +4095,9 @@ class CatalogBuilder {
4056
4095
  );
4057
4096
  const locationAnalyzer = (_b = this.locationAnalyzer) != null ? _b : new RepoLocationAnalyzer(logger, integrations);
4058
4097
  const locationService = new AuthorizedLocationService(
4059
- new DefaultLocationService(locationStore, orchestrator),
4098
+ new DefaultLocationService(locationStore, orchestrator, {
4099
+ allowedLocationTypes: this.allowedLocationType
4100
+ }),
4060
4101
  permissionEvaluator
4061
4102
  );
4062
4103
  const refreshService = new AuthorizedRefreshService(
@@ -4234,11 +4275,11 @@ class CatalogExtensionPointImpl {
4234
4275
  __privateAdd(this, _processors, new Array());
4235
4276
  __privateAdd(this, _entityProviders, new Array());
4236
4277
  }
4237
- addProcessor(processor) {
4238
- __privateGet(this, _processors).push(processor);
4278
+ addProcessor(...processors) {
4279
+ __privateGet(this, _processors).push(...processors.flat());
4239
4280
  }
4240
- addEntityProvider(provider) {
4241
- __privateGet(this, _entityProviders).push(provider);
4281
+ addEntityProvider(...providers) {
4282
+ __privateGet(this, _entityProviders).push(...providers.flat());
4242
4283
  }
4243
4284
  get processors() {
4244
4285
  return __privateGet(this, _processors);
@@ -4254,7 +4295,7 @@ const catalogPlugin = backendPluginApi.createBackendPlugin({
4254
4295
  register(env) {
4255
4296
  const processingExtensions = new CatalogExtensionPointImpl();
4256
4297
  env.registerExtensionPoint(
4257
- pluginCatalogNode.catalogProcessingExtentionPoint,
4298
+ pluginCatalogNode.catalogProcessingExtensionPoint,
4258
4299
  processingExtensions
4259
4300
  );
4260
4301
  env.registerInit({