@backstage/plugin-catalog-backend 1.25.3-next.1 → 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,66 @@
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
+
38
+ ## 1.26.0-next.2
39
+
40
+ ### Minor Changes
41
+
42
+ - 78475c3: Allow offset mode paging in entity list provider
43
+
44
+ ### Patch Changes
45
+
46
+ - c2b63ab: Updated dependency `supertest` to `^7.0.0`.
47
+ - Updated dependencies
48
+ - @backstage/backend-common@0.25.0-next.2
49
+ - @backstage/backend-plugin-api@1.0.0-next.2
50
+ - @backstage/catalog-client@1.7.0-next.1
51
+ - @backstage/integration@1.15.0-next.0
52
+ - @backstage/backend-openapi-utils@0.1.18-next.2
53
+ - @backstage/plugin-permission-node@0.8.3-next.2
54
+ - @backstage/catalog-model@1.6.0
55
+ - @backstage/config@1.2.0
56
+ - @backstage/errors@1.2.4
57
+ - @backstage/types@1.1.1
58
+ - @backstage/plugin-catalog-common@1.0.26
59
+ - @backstage/plugin-catalog-node@1.12.7-next.2
60
+ - @backstage/plugin-events-node@0.4.0-next.2
61
+ - @backstage/plugin-permission-common@0.8.1
62
+ - @backstage/plugin-search-backend-module-catalog@0.2.2-next.2
63
+
3
64
  ## 1.25.3-next.1
4
65
 
5
66
  ### Patch Changes
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backstage/plugin-catalog-backend__alpha",
3
- "version": "1.25.3-next.1",
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-BQ7aDMTH.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") {
@@ -2579,6 +2606,9 @@ class DefaultEntitiesCatalog {
2579
2606
  }
2580
2607
  ]);
2581
2608
  }
2609
+ if (isQueryEntitiesInitialRequest(request) && request.offset !== void 0) {
2610
+ dbQuery.offset(request.offset);
2611
+ }
2582
2612
  dbQuery.limit(isFetchingBackwards ? limit : limit + 1);
2583
2613
  countQuery.count("final_entities.entity_id", { as: "count" });
2584
2614
  const [rows, [{ count }]] = await Promise.all([
@@ -3765,7 +3795,7 @@ function parseEntityFilterString(filterString) {
3765
3795
  if (!statements.length) {
3766
3796
  return void 0;
3767
3797
  }
3768
- const filtersByKey = {};
3798
+ const filtersByKey = /* @__PURE__ */ new Map();
3769
3799
  for (const statement of statements) {
3770
3800
  const equalsIndex = statement.indexOf("=");
3771
3801
  const key = equalsIndex === -1 ? statement : statement.substring(0, equalsIndex).trim();
@@ -3775,13 +3805,17 @@ function parseEntityFilterString(filterString) {
3775
3805
  `Invalid filter, '${statement}' is not a valid statement (expected a string on the form a=b or a= or a)`
3776
3806
  );
3777
3807
  }
3778
- 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
+ }
3779
3813
  if (value !== void 0) {
3780
3814
  f.values = f.values || [];
3781
3815
  f.values.push(value);
3782
3816
  }
3783
3817
  }
3784
- return Object.values(filtersByKey);
3818
+ return Array.from(filtersByKey.values());
3785
3819
  }
3786
3820
 
3787
3821
  function getPathArrayAndValue(input, field) {
@@ -4986,6 +5020,9 @@ const spec = {
4986
5020
  {
4987
5021
  $ref: "#/components/parameters/limit"
4988
5022
  },
5023
+ {
5024
+ $ref: "#/components/parameters/offset"
5025
+ },
4989
5026
  {
4990
5027
  $ref: "#/components/parameters/orderField"
4991
5028
  },
@@ -5459,6 +5496,27 @@ function parseEntityPaginationParams({
5459
5496
  };
5460
5497
  }
5461
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
+
5462
5520
  async function createRouter(options) {
5463
5521
  const router = await createOpenApiRouter({
5464
5522
  validatorOptions: {
@@ -5476,6 +5534,7 @@ async function createRouter(options) {
5476
5534
  config,
5477
5535
  logger,
5478
5536
  permissionIntegrationRouter,
5537
+ permissionsService,
5479
5538
  auth,
5480
5539
  httpAuth
5481
5540
  } = options;
@@ -5516,6 +5575,7 @@ async function createRouter(options) {
5516
5575
  }).get("/entities/by-query", async (req, res) => {
5517
5576
  const { items, pageInfo, totalItems } = await entitiesCatalog.queryEntities({
5518
5577
  limit: req.query.limit,
5578
+ offset: req.query.offset,
5519
5579
  ...parseQueryEntitiesParams(req.query),
5520
5580
  credentials: await httpAuth.credentials(req)
5521
5581
  });
@@ -5642,9 +5702,13 @@ async function createRouter(options) {
5642
5702
  location: locationInput,
5643
5703
  catalogFilename: zod.z.string().optional()
5644
5704
  });
5705
+ const credentials = await httpAuth.credentials(req);
5645
5706
  const parsedBody = schema.parse(body);
5646
5707
  try {
5647
- const output = await locationAnalyzer.analyzeLocation(parsedBody);
5708
+ const output = await locationAnalyzer.analyzeLocation(
5709
+ parsedBody,
5710
+ credentials
5711
+ );
5648
5712
  res.status(200).json(output);
5649
5713
  } catch (err) {
5650
5714
  if (
@@ -5679,19 +5743,27 @@ async function createRouter(options) {
5679
5743
  errors: [errors.serializeError(err)]
5680
5744
  });
5681
5745
  }
5682
- const processingResult = await orchestrator.process({
5683
- entity: {
5684
- ...entity,
5685
- metadata: {
5686
- ...entity.metadata,
5687
- annotations: {
5688
- [catalogModel.ANNOTATION_LOCATION]: body.location,
5689
- [catalogModel.ANNOTATION_ORIGIN_LOCATION]: body.location,
5690
- ...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
+ }
5691
5762
  }
5692
5763
  }
5693
- }
5694
- });
5764
+ },
5765
+ credentials
5766
+ );
5695
5767
  if (!processingResult.ok)
5696
5768
  res.status(400).json({
5697
5769
  errors: processingResult.errors.map((e) => errors.serializeError(e))
@@ -7009,15 +7081,6 @@ class CatalogBuilder {
7009
7081
  });
7010
7082
  const integrations = integration.ScmIntegrations.fromConfig(config);
7011
7083
  const rulesEnforcer = DefaultCatalogRulesEnforcer.fromConfig(config);
7012
- const orchestrator = new DefaultCatalogProcessingOrchestrator({
7013
- processors,
7014
- integrations,
7015
- rulesEnforcer,
7016
- logger,
7017
- parser,
7018
- policy,
7019
- legacySingleProcessorValidation: this.legacySingleProcessorValidation
7020
- });
7021
7084
  const unauthorizedEntitiesCatalog = new DefaultEntitiesCatalog({
7022
7085
  database: dbClient,
7023
7086
  logger,
@@ -7032,6 +7095,15 @@ class CatalogBuilder {
7032
7095
  );
7033
7096
  permissionsService = pluginPermissionCommon.toPermissionEvaluator(permissions);
7034
7097
  }
7098
+ const orchestrator = new DefaultCatalogProcessingOrchestrator({
7099
+ processors,
7100
+ integrations,
7101
+ rulesEnforcer,
7102
+ logger,
7103
+ parser,
7104
+ policy,
7105
+ legacySingleProcessorValidation: this.legacySingleProcessorValidation
7106
+ });
7035
7107
  const entitiesCatalog = new AuthorizedEntitiesCatalog(
7036
7108
  unauthorizedEntitiesCatalog,
7037
7109
  permissionsService,
@@ -7082,7 +7154,10 @@ class CatalogBuilder {
7082
7154
  },
7083
7155
  eventBroker: this.eventBroker
7084
7156
  });
7085
- 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
+ );
7086
7161
  const locationService = new AuthorizedLocationService(
7087
7162
  new DefaultLocationService(locationStore, orchestrator, {
7088
7163
  allowedLocationTypes: this.allowedLocationType
@@ -7103,7 +7178,8 @@ class CatalogBuilder {
7103
7178
  config,
7104
7179
  permissionIntegrationRouter,
7105
7180
  auth,
7106
- httpAuth
7181
+ httpAuth,
7182
+ permissionsService
7107
7183
  });
7108
7184
  await connectEntityProviders(providerDatabase, entityProviders);
7109
7185
  return {
@@ -7296,4 +7372,4 @@ exports.createCatalogPermissionRule = createCatalogPermissionRule;
7296
7372
  exports.createRandomProcessingInterval = createRandomProcessingInterval;
7297
7373
  exports.parseEntityYaml = parseEntityYaml;
7298
7374
  exports.permissionRules = permissionRules;
7299
- //# sourceMappingURL=CatalogBuilder-BQ7aDMTH.cjs.js.map
7375
+ //# sourceMappingURL=CatalogBuilder-CGSl8LEN.cjs.js.map