@backstage/plugin-catalog-backend 1.26.0-next.2 → 1.26.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/plugin-catalog-backend
2
2
 
3
+ ## 1.26.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 74acf06: Add `dependencyOf` prop to catalog model for Component kind to enable building relationship graphs with both directions using `dependsOn` and `dependencyOf`.
8
+ - 78475c3: Allow offset mode paging in entity list provider
9
+ - bd35cdb: The `analyze-location` endpoint is now protected by the `catalog.location.analyze` permission.
10
+ The `validate-entity` endpoint is now protected by the `catalog.entity.validate` permission.
11
+
12
+ ### Patch Changes
13
+
14
+ - 1882cfe: Moved `getEntities` ordering to utilize database instead of having it inside catalog client
15
+
16
+ Please note that the latest version of `@backstage/catalog-client` will not order the entities in the same way as before. This is because the ordering is now done in the database query instead of in the client. If you rely on the ordering of the entities, you may need to update your backend plugin or code to handle this change.
17
+
18
+ - d425fc4: Modules, plugins, and services are now `BackendFeature`, not a function that returns a feature.
19
+ - c2b63ab: Updated dependency `supertest` to `^7.0.0`.
20
+ - 53cce86: Fixed an issue with the by-query call, where ordering by a field that does not exist on all entities led to not all results being returned
21
+ - Updated dependencies
22
+ - @backstage/backend-common@0.25.0
23
+ - @backstage/backend-plugin-api@1.0.0
24
+ - @backstage/catalog-model@1.7.0
25
+ - @backstage/catalog-client@1.7.0
26
+ - @backstage/plugin-search-backend-module-catalog@0.2.2
27
+ - @backstage/plugin-permission-node@0.8.3
28
+ - @backstage/plugin-catalog-common@1.1.0
29
+ - @backstage/plugin-catalog-node@1.13.0
30
+ - @backstage/integration@1.15.0
31
+ - @backstage/backend-openapi-utils@0.1.18
32
+ - @backstage/plugin-events-node@0.4.0
33
+ - @backstage/config@1.2.0
34
+ - @backstage/errors@1.2.4
35
+ - @backstage/types@1.1.1
36
+ - @backstage/plugin-permission-common@0.8.1
37
+
3
38
  ## 1.26.0-next.2
4
39
 
5
40
  ### Minor Changes
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backstage/plugin-catalog-backend__alpha",
3
- "version": "1.26.0-next.2",
3
+ "version": "1.26.0",
4
4
  "main": "../dist/alpha.cjs.js",
5
5
  "types": "../dist/alpha.d.ts"
6
6
  }
package/dist/alpha.cjs.js CHANGED
@@ -4,7 +4,7 @@ Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var alpha = require('@backstage/plugin-catalog-common/alpha');
6
6
  var pluginPermissionNode = require('@backstage/plugin-permission-node');
7
- var CatalogBuilder = require('./cjs/CatalogBuilder-DeLH-BIi.cjs.js');
7
+ var CatalogBuilder = require('./cjs/CatalogBuilder-CGSl8LEN.cjs.js');
8
8
  var backendPluginApi = require('@backstage/backend-plugin-api');
9
9
  var pluginEventsNode = require('@backstage/plugin-events-node');
10
10
  var alpha$1 = require('@backstage/plugin-catalog-node/alpha');
@@ -25,6 +25,7 @@ require('util');
25
25
  require('yaml');
26
26
  require('p-limit');
27
27
  require('uuid');
28
+ require('@backstage/plugin-permission-common');
28
29
  require('luxon');
29
30
  require('prom-client');
30
31
  require('@opentelemetry/api');
@@ -36,7 +37,6 @@ require('@backstage/types');
36
37
  require('@backstage/catalog-client');
37
38
  require('yn');
38
39
  require('@backstage/backend-openapi-utils');
39
- require('@backstage/plugin-permission-common');
40
40
  require('minimatch');
41
41
  require('@backstage/config');
42
42
 
@@ -17,6 +17,8 @@ var util = require('util');
17
17
  var yaml = require('yaml');
18
18
  var limiterFactory = require('p-limit');
19
19
  var uuid = require('uuid');
20
+ var alpha = require('@backstage/plugin-catalog-common/alpha');
21
+ var pluginPermissionCommon = require('@backstage/plugin-permission-common');
20
22
  var backendPluginApi = require('@backstage/backend-plugin-api');
21
23
  var luxon = require('luxon');
22
24
  var promClient = require('prom-client');
@@ -29,8 +31,6 @@ var types = require('@backstage/types');
29
31
  var catalogClient = require('@backstage/catalog-client');
30
32
  var yn = require('yn');
31
33
  var backendOpenapiUtils = require('@backstage/backend-openapi-utils');
32
- var alpha = require('@backstage/plugin-catalog-common/alpha');
33
- var pluginPermissionCommon = require('@backstage/plugin-permission-common');
34
34
  var minimatch = require('minimatch');
35
35
  var config = require('@backstage/config');
36
36
  var pluginPermissionNode = require('@backstage/plugin-permission-node');
@@ -317,6 +317,12 @@ class BuiltinKindsEntityProcessor {
317
317
  catalogModel.RELATION_DEPENDS_ON,
318
318
  catalogModel.RELATION_DEPENDENCY_OF
319
319
  );
320
+ doEmit(
321
+ component.spec.dependencyOf,
322
+ { defaultNamespace: selfRef.namespace },
323
+ catalogModel.RELATION_DEPENDENCY_OF,
324
+ catalogModel.RELATION_DEPENDS_ON
325
+ );
320
326
  doEmit(
321
327
  component.spec.system,
322
328
  { defaultKind: "System", defaultNamespace: selfRef.namespace },
@@ -1045,6 +1051,27 @@ class RepoLocationAnalyzer {
1045
1051
  }
1046
1052
  }
1047
1053
 
1054
+ class AuthorizedLocationAnalyzer {
1055
+ constructor(service, permissionApi) {
1056
+ this.service = service;
1057
+ this.permissionApi = permissionApi;
1058
+ }
1059
+ async analyzeLocation(request, credentials) {
1060
+ const authorizeDecision = (await this.permissionApi.authorize(
1061
+ [
1062
+ {
1063
+ permission: alpha.catalogLocationAnalyzePermission
1064
+ }
1065
+ ],
1066
+ { credentials }
1067
+ ))[0];
1068
+ if (authorizeDecision.result !== pluginPermissionCommon.AuthorizeResult.ALLOW) {
1069
+ throw new errors.NotAllowedError();
1070
+ }
1071
+ return this.service.analyzeLocation(request, credentials);
1072
+ }
1073
+ }
1074
+
1048
1075
  function timestampToDateTime(input) {
1049
1076
  try {
1050
1077
  if (typeof input === "object") {
@@ -3768,7 +3795,7 @@ function parseEntityFilterString(filterString) {
3768
3795
  if (!statements.length) {
3769
3796
  return void 0;
3770
3797
  }
3771
- const filtersByKey = {};
3798
+ const filtersByKey = /* @__PURE__ */ new Map();
3772
3799
  for (const statement of statements) {
3773
3800
  const equalsIndex = statement.indexOf("=");
3774
3801
  const key = equalsIndex === -1 ? statement : statement.substring(0, equalsIndex).trim();
@@ -3778,13 +3805,17 @@ function parseEntityFilterString(filterString) {
3778
3805
  `Invalid filter, '${statement}' is not a valid statement (expected a string on the form a=b or a= or a)`
3779
3806
  );
3780
3807
  }
3781
- const f = key in filtersByKey ? filtersByKey[key] : filtersByKey[key] = { key };
3808
+ let f = filtersByKey.get(key);
3809
+ if (!f) {
3810
+ f = { key };
3811
+ filtersByKey.set(key, f);
3812
+ }
3782
3813
  if (value !== void 0) {
3783
3814
  f.values = f.values || [];
3784
3815
  f.values.push(value);
3785
3816
  }
3786
3817
  }
3787
- return Object.values(filtersByKey);
3818
+ return Array.from(filtersByKey.values());
3788
3819
  }
3789
3820
 
3790
3821
  function getPathArrayAndValue(input, field) {
@@ -5465,6 +5496,27 @@ function parseEntityPaginationParams({
5465
5496
  };
5466
5497
  }
5467
5498
 
5499
+ class AuthorizedValidationService {
5500
+ constructor(service, permissionApi) {
5501
+ this.service = service;
5502
+ this.permissionApi = permissionApi;
5503
+ }
5504
+ async process(request, credentials) {
5505
+ const authorizeDecision = (await this.permissionApi.authorize(
5506
+ [
5507
+ {
5508
+ permission: alpha.catalogEntityValidatePermission
5509
+ }
5510
+ ],
5511
+ { credentials }
5512
+ ))[0];
5513
+ if (authorizeDecision.result !== pluginPermissionCommon.AuthorizeResult.ALLOW) {
5514
+ throw new errors.NotAllowedError();
5515
+ }
5516
+ return this.service.process(request);
5517
+ }
5518
+ }
5519
+
5468
5520
  async function createRouter(options) {
5469
5521
  const router = await createOpenApiRouter({
5470
5522
  validatorOptions: {
@@ -5482,6 +5534,7 @@ async function createRouter(options) {
5482
5534
  config,
5483
5535
  logger,
5484
5536
  permissionIntegrationRouter,
5537
+ permissionsService,
5485
5538
  auth,
5486
5539
  httpAuth
5487
5540
  } = options;
@@ -5649,9 +5702,13 @@ async function createRouter(options) {
5649
5702
  location: locationInput,
5650
5703
  catalogFilename: zod.z.string().optional()
5651
5704
  });
5705
+ const credentials = await httpAuth.credentials(req);
5652
5706
  const parsedBody = schema.parse(body);
5653
5707
  try {
5654
- const output = await locationAnalyzer.analyzeLocation(parsedBody);
5708
+ const output = await locationAnalyzer.analyzeLocation(
5709
+ parsedBody,
5710
+ credentials
5711
+ );
5655
5712
  res.status(200).json(output);
5656
5713
  } catch (err) {
5657
5714
  if (
@@ -5686,19 +5743,27 @@ async function createRouter(options) {
5686
5743
  errors: [errors.serializeError(err)]
5687
5744
  });
5688
5745
  }
5689
- const processingResult = await orchestrator.process({
5690
- entity: {
5691
- ...entity,
5692
- metadata: {
5693
- ...entity.metadata,
5694
- annotations: {
5695
- [catalogModel.ANNOTATION_LOCATION]: body.location,
5696
- [catalogModel.ANNOTATION_ORIGIN_LOCATION]: body.location,
5697
- ...entity.metadata.annotations
5746
+ const credentials = await httpAuth.credentials(req);
5747
+ const authorizedValidationService = new AuthorizedValidationService(
5748
+ orchestrator,
5749
+ permissionsService
5750
+ );
5751
+ const processingResult = await authorizedValidationService.process(
5752
+ {
5753
+ entity: {
5754
+ ...entity,
5755
+ metadata: {
5756
+ ...entity.metadata,
5757
+ annotations: {
5758
+ [catalogModel.ANNOTATION_LOCATION]: body.location,
5759
+ [catalogModel.ANNOTATION_ORIGIN_LOCATION]: body.location,
5760
+ ...entity.metadata.annotations
5761
+ }
5698
5762
  }
5699
5763
  }
5700
- }
5701
- });
5764
+ },
5765
+ credentials
5766
+ );
5702
5767
  if (!processingResult.ok)
5703
5768
  res.status(400).json({
5704
5769
  errors: processingResult.errors.map((e) => errors.serializeError(e))
@@ -7016,15 +7081,6 @@ class CatalogBuilder {
7016
7081
  });
7017
7082
  const integrations = integration.ScmIntegrations.fromConfig(config);
7018
7083
  const rulesEnforcer = DefaultCatalogRulesEnforcer.fromConfig(config);
7019
- const orchestrator = new DefaultCatalogProcessingOrchestrator({
7020
- processors,
7021
- integrations,
7022
- rulesEnforcer,
7023
- logger,
7024
- parser,
7025
- policy,
7026
- legacySingleProcessorValidation: this.legacySingleProcessorValidation
7027
- });
7028
7084
  const unauthorizedEntitiesCatalog = new DefaultEntitiesCatalog({
7029
7085
  database: dbClient,
7030
7086
  logger,
@@ -7039,6 +7095,15 @@ class CatalogBuilder {
7039
7095
  );
7040
7096
  permissionsService = pluginPermissionCommon.toPermissionEvaluator(permissions);
7041
7097
  }
7098
+ const orchestrator = new DefaultCatalogProcessingOrchestrator({
7099
+ processors,
7100
+ integrations,
7101
+ rulesEnforcer,
7102
+ logger,
7103
+ parser,
7104
+ policy,
7105
+ legacySingleProcessorValidation: this.legacySingleProcessorValidation
7106
+ });
7042
7107
  const entitiesCatalog = new AuthorizedEntitiesCatalog(
7043
7108
  unauthorizedEntitiesCatalog,
7044
7109
  permissionsService,
@@ -7089,7 +7154,10 @@ class CatalogBuilder {
7089
7154
  },
7090
7155
  eventBroker: this.eventBroker
7091
7156
  });
7092
- const locationAnalyzer = this.locationAnalyzer ?? new RepoLocationAnalyzer(logger, integrations, this.locationAnalyzers);
7157
+ const locationAnalyzer = this.locationAnalyzer ?? new AuthorizedLocationAnalyzer(
7158
+ new RepoLocationAnalyzer(logger, integrations, this.locationAnalyzers),
7159
+ permissionsService
7160
+ );
7093
7161
  const locationService = new AuthorizedLocationService(
7094
7162
  new DefaultLocationService(locationStore, orchestrator, {
7095
7163
  allowedLocationTypes: this.allowedLocationType
@@ -7110,7 +7178,8 @@ class CatalogBuilder {
7110
7178
  config,
7111
7179
  permissionIntegrationRouter,
7112
7180
  auth,
7113
- httpAuth
7181
+ httpAuth,
7182
+ permissionsService
7114
7183
  });
7115
7184
  await connectEntityProviders(providerDatabase, entityProviders);
7116
7185
  return {
@@ -7303,4 +7372,4 @@ exports.createCatalogPermissionRule = createCatalogPermissionRule;
7303
7372
  exports.createRandomProcessingInterval = createRandomProcessingInterval;
7304
7373
  exports.parseEntityYaml = parseEntityYaml;
7305
7374
  exports.permissionRules = permissionRules;
7306
- //# sourceMappingURL=CatalogBuilder-DeLH-BIi.cjs.js.map
7375
+ //# sourceMappingURL=CatalogBuilder-CGSl8LEN.cjs.js.map