@backstage/plugin-catalog-backend 1.9.0-next.3 → 1.9.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 +69 -0
- package/alpha/package.json +1 -1
- package/config.d.ts +7 -1
- package/dist/alpha.cjs.js +7 -3
- package/dist/alpha.cjs.js.map +1 -1
- package/dist/cjs/{CatalogBuilder-c295413f.cjs.js → CatalogBuilder-ecfd2db6.cjs.js} +120 -24
- package/dist/cjs/CatalogBuilder-ecfd2db6.cjs.js.map +1 -0
- package/dist/index.cjs.js +2 -1
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/package.json +19 -18
- package/dist/cjs/CatalogBuilder-c295413f.cjs.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,74 @@
|
|
|
1
1
|
# @backstage/plugin-catalog-backend
|
|
2
2
|
|
|
3
|
+
## 1.9.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 329b63f4dab: The catalog now has a new, optional `catalog.orphanStrategy` app-config parameter, which can have the string values `'keep'` (default) or `'delete'`.
|
|
8
|
+
|
|
9
|
+
If set to `'keep'` or left unset, the old behavior is maintained of keeping orphaned entities around until manually deleted.
|
|
10
|
+
|
|
11
|
+
If set to `'delete'`, the catalog will attempt to automatically clean out orphaned entities without manual intervention. Note that there are no guarantees that this process is instantaneous, so there may be some delay before orphaned items disappear.
|
|
12
|
+
|
|
13
|
+
For context, the [Life of an Entity](https://backstage.io/docs/features/software-catalog/life-of-an-entity/#orphaning) article goes into some more details on how the nature of orphaning works.
|
|
14
|
+
|
|
15
|
+
To enable the new behavior, you will need to pass the plugin task scheduler to your catalog backend builder. If your code already looks like this, you don't need to change it:
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
// in packages/backend/src/plugins/catalog.ts
|
|
19
|
+
export default async function createPlugin(
|
|
20
|
+
env: PluginEnvironment,
|
|
21
|
+
): Promise<Router> {
|
|
22
|
+
const builder = await CatalogBuilder.create(env);
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
But if you pass things into the catalog builder one by one, you'll need to add the new field:
|
|
26
|
+
|
|
27
|
+
```diff
|
|
28
|
+
// in packages/backend/src/plugins/catalog.ts
|
|
29
|
+
const builder = await CatalogBuilder.create({
|
|
30
|
+
// ... other dependencies
|
|
31
|
+
+ scheduler: env.scheduler,
|
|
32
|
+
});
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Finally adjust your app-config:
|
|
36
|
+
|
|
37
|
+
```yaml
|
|
38
|
+
catalog:
|
|
39
|
+
orphanStrategy: delete
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
- 92a4590fc3a: Add monorepo support to CodeOwnersProccesor.
|
|
43
|
+
|
|
44
|
+
### Patch Changes
|
|
45
|
+
|
|
46
|
+
- 62a725e3a94: Use the `LocationSpec` type from the `catalog-common` package in place of the deprecated `LocationSpec` from the `catalog-node` package.
|
|
47
|
+
- be5aca50114: Updates and moves OpenAPI spec to `src/schema/openapi.yaml` and uses `ApiRouter` type from `@backstage/backend-openapi-utils` to handle automatic types from the OpenAPI spec file.
|
|
48
|
+
- c9a0fdcd2c8: Fix deprecated types.
|
|
49
|
+
- 899ebfd8e02: Add full text search support to the `by-query` endpoint.
|
|
50
|
+
- 1e4f5e91b8e: Bump `zod` and `zod-to-json-schema` dependencies.
|
|
51
|
+
- c4b846359c0: Allow replacement of the BuiltinKindsEntityProcessor which enables customization of schema validation and connections emitted.
|
|
52
|
+
- c36b89f2af3: Fixed bug in the `DefaultCatalogProcessingEngine` where entities that contained multiple different types of relations for the same source entity would not properly trigger stitching for that source entity.
|
|
53
|
+
- 01ae205352e: Collator factories instantiated in new backend system modules and now marked as deprecated. Will be continued to be exported publicly until the new backend system is fully rolled out.
|
|
54
|
+
- Updated dependencies
|
|
55
|
+
- @backstage/backend-common@0.18.4
|
|
56
|
+
- @backstage/plugin-scaffolder-common@1.2.7
|
|
57
|
+
- @backstage/catalog-client@1.4.1
|
|
58
|
+
- @backstage/plugin-permission-node@0.7.7
|
|
59
|
+
- @backstage/plugin-permission-common@0.7.5
|
|
60
|
+
- @backstage/backend-tasks@0.5.1
|
|
61
|
+
- @backstage/catalog-model@1.3.0
|
|
62
|
+
- @backstage/plugin-search-backend-module-catalog@0.1.0
|
|
63
|
+
- @backstage/integration@1.4.4
|
|
64
|
+
- @backstage/plugin-catalog-node@1.3.5
|
|
65
|
+
- @backstage/backend-plugin-api@0.5.1
|
|
66
|
+
- @backstage/config@1.0.7
|
|
67
|
+
- @backstage/errors@1.1.5
|
|
68
|
+
- @backstage/types@1.0.2
|
|
69
|
+
- @backstage/plugin-catalog-common@1.0.13
|
|
70
|
+
- @backstage/plugin-search-common@1.2.3
|
|
71
|
+
|
|
3
72
|
## 1.9.0-next.3
|
|
4
73
|
|
|
5
74
|
### Minor Changes
|
package/alpha/package.json
CHANGED
package/config.d.ts
CHANGED
|
@@ -81,7 +81,6 @@ export interface Config {
|
|
|
81
81
|
* be used in combination with static locations to only serve operator
|
|
82
82
|
* provided locations. Effectively this removes the ability to register new
|
|
83
83
|
* components to a running backstage instance.
|
|
84
|
-
*
|
|
85
84
|
*/
|
|
86
85
|
readonly?: boolean;
|
|
87
86
|
|
|
@@ -136,5 +135,12 @@ export interface Config {
|
|
|
136
135
|
allow: Array<string>;
|
|
137
136
|
}>;
|
|
138
137
|
}>;
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* The strategy to use for entities that are orphaned, i.e. no longer have
|
|
141
|
+
* any other entities or providers referencing them. The default value is
|
|
142
|
+
* "keep".
|
|
143
|
+
*/
|
|
144
|
+
orphanStrategy?: 'keep' | 'delete';
|
|
139
145
|
};
|
|
140
146
|
}
|
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-
|
|
7
|
+
var CatalogBuilder = require('./cjs/CatalogBuilder-ecfd2db6.cjs.js');
|
|
8
8
|
var backendPluginApi = require('@backstage/backend-plugin-api');
|
|
9
9
|
var alpha$1 = require('@backstage/plugin-catalog-node/alpha');
|
|
10
10
|
var backendCommon = require('@backstage/backend-common');
|
|
@@ -26,6 +26,7 @@ require('p-limit');
|
|
|
26
26
|
require('uuid');
|
|
27
27
|
require('luxon');
|
|
28
28
|
require('prom-client');
|
|
29
|
+
require('lodash/uniq');
|
|
29
30
|
require('fast-json-stable-stringify');
|
|
30
31
|
require('@opentelemetry/api');
|
|
31
32
|
require('zod');
|
|
@@ -94,7 +95,8 @@ const catalogPlugin = backendPluginApi.createBackendPlugin({
|
|
|
94
95
|
permissions: backendPluginApi.coreServices.permissions,
|
|
95
96
|
database: backendPluginApi.coreServices.database,
|
|
96
97
|
httpRouter: backendPluginApi.coreServices.httpRouter,
|
|
97
|
-
lifecycle: backendPluginApi.coreServices.lifecycle
|
|
98
|
+
lifecycle: backendPluginApi.coreServices.lifecycle,
|
|
99
|
+
scheduler: backendPluginApi.coreServices.scheduler
|
|
98
100
|
},
|
|
99
101
|
async init({
|
|
100
102
|
logger,
|
|
@@ -103,7 +105,8 @@ const catalogPlugin = backendPluginApi.createBackendPlugin({
|
|
|
103
105
|
database,
|
|
104
106
|
permissions,
|
|
105
107
|
httpRouter,
|
|
106
|
-
lifecycle
|
|
108
|
+
lifecycle,
|
|
109
|
+
scheduler
|
|
107
110
|
}) {
|
|
108
111
|
const winstonLogger = backendCommon.loggerToWinstonLogger(logger);
|
|
109
112
|
const builder = await CatalogBuilder.CatalogBuilder.create({
|
|
@@ -111,6 +114,7 @@ const catalogPlugin = backendPluginApi.createBackendPlugin({
|
|
|
111
114
|
reader,
|
|
112
115
|
permissions,
|
|
113
116
|
database,
|
|
117
|
+
scheduler,
|
|
114
118
|
logger: winstonLogger
|
|
115
119
|
});
|
|
116
120
|
builder.addProcessor(...processingExtensions.processors);
|
package/dist/alpha.cjs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"alpha.cjs.js","sources":["../src/permissions/conditionExports.ts","../src/service/CatalogPlugin.ts"],"sourcesContent":["/*\n * Copyright 2022 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 { RESOURCE_TYPE_CATALOG_ENTITY } from '@backstage/plugin-catalog-common/alpha';\nimport { createConditionExports } from '@backstage/plugin-permission-node';\nimport { permissionRules } from './rules';\n\nconst { conditions, createConditionalDecision } = createConditionExports({\n pluginId: 'catalog',\n resourceType: RESOURCE_TYPE_CATALOG_ENTITY,\n rules: permissionRules,\n});\n\n/**\n * These conditions are used when creating conditional decisions for catalog\n * entities that are returned by authorization policies.\n *\n * @alpha\n */\nexport const catalogConditions = conditions;\n\n/**\n * `createCatalogConditionalDecision` can be used when authoring policies to\n * create conditional decisions. It requires a permission of type\n * `ResourcePermission<'catalog-entity'>` to be passed as the first parameter.\n * It's recommended that you use the provided `isResourcePermission` and\n * `isPermission` helper methods to narrow the type of the permission passed to\n * the handle method as shown below.\n *\n * ```\n * // MyAuthorizationPolicy.ts\n * ...\n * import { createCatalogPolicyDecision } from '@backstage/plugin-catalog-backend';\n * import { RESOURCE_TYPE_CATALOG_ENTITY } from '@backstage/plugin-catalog-common';\n *\n * class MyAuthorizationPolicy implements PermissionPolicy {\n * async handle(request, user) {\n * ...\n *\n * if (isResourcePermission(request.permission, RESOURCE_TYPE_CATALOG_ENTITY)) {\n * return createCatalogConditionalDecision(\n * request.permission,\n * { anyOf: [...insert conditions here...] }\n * );\n * }\n *\n * ...\n * }\n * ```\n *\n * @alpha\n */\nexport const createCatalogConditionalDecision = createConditionalDecision;\n","/*\n * Copyright 2022 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 */\nimport {\n createBackendPlugin,\n coreServices,\n} from '@backstage/backend-plugin-api';\nimport { CatalogBuilder } from './CatalogBuilder';\nimport {\n CatalogProcessingExtensionPoint,\n catalogProcessingExtensionPoint,\n} from '@backstage/plugin-catalog-node/alpha';\nimport {\n CatalogProcessor,\n EntityProvider,\n} from '@backstage/plugin-catalog-node';\nimport { loggerToWinstonLogger } from '@backstage/backend-common';\n\nclass CatalogExtensionPointImpl implements CatalogProcessingExtensionPoint {\n #processors = new Array<CatalogProcessor>();\n #entityProviders = new Array<EntityProvider>();\n\n addProcessor(\n ...processors: Array<CatalogProcessor | Array<CatalogProcessor>>\n ): void {\n this.#processors.push(...processors.flat());\n }\n\n addEntityProvider(\n ...providers: Array<EntityProvider | Array<EntityProvider>>\n ): void {\n this.#entityProviders.push(...providers.flat());\n }\n\n get processors() {\n return this.#processors;\n }\n\n get entityProviders() {\n return this.#entityProviders;\n }\n}\n\n/**\n * Catalog plugin\n * @alpha\n */\nexport const catalogPlugin = createBackendPlugin({\n pluginId: 'catalog',\n register(env) {\n const processingExtensions = new CatalogExtensionPointImpl();\n // plugins depending on this API will be initialized before this plugins init method is executed.\n env.registerExtensionPoint(\n catalogProcessingExtensionPoint,\n processingExtensions,\n );\n\n env.registerInit({\n deps: {\n logger: coreServices.logger,\n config: coreServices.config,\n reader: coreServices.urlReader,\n permissions: coreServices.permissions,\n database: coreServices.database,\n httpRouter: coreServices.httpRouter,\n lifecycle: coreServices.lifecycle,\n },\n async init({\n logger,\n config,\n reader,\n database,\n permissions,\n httpRouter,\n lifecycle,\n }) {\n const winstonLogger = loggerToWinstonLogger(logger);\n const builder = await CatalogBuilder.create({\n config,\n reader,\n permissions,\n database,\n logger: winstonLogger,\n });\n builder.addProcessor(...processingExtensions.processors);\n builder.addEntityProvider(...processingExtensions.entityProviders);\n const { processingEngine, router } = await builder.build();\n\n await processingEngine.start();\n lifecycle.addShutdownHook(() => processingEngine.stop());\n httpRouter.use(router);\n },\n });\n },\n});\n"],"names":["createConditionExports","RESOURCE_TYPE_CATALOG_ENTITY","permissionRules","createBackendPlugin","catalogProcessingExtensionPoint","coreServices","loggerToWinstonLogger","CatalogBuilder"],"mappings":"
|
|
1
|
+
{"version":3,"file":"alpha.cjs.js","sources":["../src/permissions/conditionExports.ts","../src/service/CatalogPlugin.ts"],"sourcesContent":["/*\n * Copyright 2022 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 { RESOURCE_TYPE_CATALOG_ENTITY } from '@backstage/plugin-catalog-common/alpha';\nimport { createConditionExports } from '@backstage/plugin-permission-node';\nimport { permissionRules } from './rules';\n\nconst { conditions, createConditionalDecision } = createConditionExports({\n pluginId: 'catalog',\n resourceType: RESOURCE_TYPE_CATALOG_ENTITY,\n rules: permissionRules,\n});\n\n/**\n * These conditions are used when creating conditional decisions for catalog\n * entities that are returned by authorization policies.\n *\n * @alpha\n */\nexport const catalogConditions = conditions;\n\n/**\n * `createCatalogConditionalDecision` can be used when authoring policies to\n * create conditional decisions. It requires a permission of type\n * `ResourcePermission<'catalog-entity'>` to be passed as the first parameter.\n * It's recommended that you use the provided `isResourcePermission` and\n * `isPermission` helper methods to narrow the type of the permission passed to\n * the handle method as shown below.\n *\n * ```\n * // MyAuthorizationPolicy.ts\n * ...\n * import { createCatalogPolicyDecision } from '@backstage/plugin-catalog-backend';\n * import { RESOURCE_TYPE_CATALOG_ENTITY } from '@backstage/plugin-catalog-common';\n *\n * class MyAuthorizationPolicy implements PermissionPolicy {\n * async handle(request, user) {\n * ...\n *\n * if (isResourcePermission(request.permission, RESOURCE_TYPE_CATALOG_ENTITY)) {\n * return createCatalogConditionalDecision(\n * request.permission,\n * { anyOf: [...insert conditions here...] }\n * );\n * }\n *\n * ...\n * }\n * ```\n *\n * @alpha\n */\nexport const createCatalogConditionalDecision = createConditionalDecision;\n","/*\n * Copyright 2022 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 */\nimport {\n createBackendPlugin,\n coreServices,\n} from '@backstage/backend-plugin-api';\nimport { CatalogBuilder } from './CatalogBuilder';\nimport {\n CatalogProcessingExtensionPoint,\n catalogProcessingExtensionPoint,\n} from '@backstage/plugin-catalog-node/alpha';\nimport {\n CatalogProcessor,\n EntityProvider,\n} from '@backstage/plugin-catalog-node';\nimport { loggerToWinstonLogger } from '@backstage/backend-common';\n\nclass CatalogExtensionPointImpl implements CatalogProcessingExtensionPoint {\n #processors = new Array<CatalogProcessor>();\n #entityProviders = new Array<EntityProvider>();\n\n addProcessor(\n ...processors: Array<CatalogProcessor | Array<CatalogProcessor>>\n ): void {\n this.#processors.push(...processors.flat());\n }\n\n addEntityProvider(\n ...providers: Array<EntityProvider | Array<EntityProvider>>\n ): void {\n this.#entityProviders.push(...providers.flat());\n }\n\n get processors() {\n return this.#processors;\n }\n\n get entityProviders() {\n return this.#entityProviders;\n }\n}\n\n/**\n * Catalog plugin\n * @alpha\n */\nexport const catalogPlugin = createBackendPlugin({\n pluginId: 'catalog',\n register(env) {\n const processingExtensions = new CatalogExtensionPointImpl();\n // plugins depending on this API will be initialized before this plugins init method is executed.\n env.registerExtensionPoint(\n catalogProcessingExtensionPoint,\n processingExtensions,\n );\n\n env.registerInit({\n deps: {\n logger: coreServices.logger,\n config: coreServices.config,\n reader: coreServices.urlReader,\n permissions: coreServices.permissions,\n database: coreServices.database,\n httpRouter: coreServices.httpRouter,\n lifecycle: coreServices.lifecycle,\n scheduler: coreServices.scheduler,\n },\n async init({\n logger,\n config,\n reader,\n database,\n permissions,\n httpRouter,\n lifecycle,\n scheduler,\n }) {\n const winstonLogger = loggerToWinstonLogger(logger);\n const builder = await CatalogBuilder.create({\n config,\n reader,\n permissions,\n database,\n scheduler,\n logger: winstonLogger,\n });\n builder.addProcessor(...processingExtensions.processors);\n builder.addEntityProvider(...processingExtensions.entityProviders);\n const { processingEngine, router } = await builder.build();\n\n await processingEngine.start();\n lifecycle.addShutdownHook(() => processingEngine.stop());\n httpRouter.use(router);\n },\n });\n },\n});\n"],"names":["createConditionExports","RESOURCE_TYPE_CATALOG_ENTITY","permissionRules","createBackendPlugin","catalogProcessingExtensionPoint","coreServices","loggerToWinstonLogger","CatalogBuilder"],"mappings":"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"}
|
|
@@ -19,6 +19,7 @@ var uuid = require('uuid');
|
|
|
19
19
|
var backendCommon = require('@backstage/backend-common');
|
|
20
20
|
var luxon = require('luxon');
|
|
21
21
|
var promClient = require('prom-client');
|
|
22
|
+
var uniq = require('lodash/uniq');
|
|
22
23
|
var stableStringify = require('fast-json-stable-stringify');
|
|
23
24
|
var api = require('@opentelemetry/api');
|
|
24
25
|
var zod = require('zod');
|
|
@@ -59,6 +60,7 @@ var g__default = /*#__PURE__*/_interopDefaultLegacy(g);
|
|
|
59
60
|
var path__default = /*#__PURE__*/_interopDefaultLegacy(path);
|
|
60
61
|
var yaml__default = /*#__PURE__*/_interopDefaultLegacy(yaml);
|
|
61
62
|
var limiterFactory__default = /*#__PURE__*/_interopDefaultLegacy(limiterFactory);
|
|
63
|
+
var uniq__default = /*#__PURE__*/_interopDefaultLegacy(uniq);
|
|
62
64
|
var stableStringify__default = /*#__PURE__*/_interopDefaultLegacy(stableStringify);
|
|
63
65
|
var express__default = /*#__PURE__*/_interopDefaultLegacy(express);
|
|
64
66
|
var Router__default = /*#__PURE__*/_interopDefaultLegacy(Router);
|
|
@@ -1169,6 +1171,48 @@ async function updateUnprocessedEntity(options) {
|
|
|
1169
1171
|
return refreshResult === 1;
|
|
1170
1172
|
}
|
|
1171
1173
|
|
|
1174
|
+
async function deleteOrphanedEntities(options) {
|
|
1175
|
+
const { tx } = options;
|
|
1176
|
+
let total = 0;
|
|
1177
|
+
for (let i = 0; i < 100; ++i) {
|
|
1178
|
+
const candidates = await tx.with(
|
|
1179
|
+
"orphans",
|
|
1180
|
+
(orphans) => orphans.from("refresh_state").select("entity_id", "entity_ref").whereNotIn(
|
|
1181
|
+
"entity_ref",
|
|
1182
|
+
(keep) => keep.distinct("target_entity_ref").from("refresh_state_references")
|
|
1183
|
+
)
|
|
1184
|
+
).select({
|
|
1185
|
+
entityId: "orphans.entity_id",
|
|
1186
|
+
relationSourceId: "refresh_state.entity_id"
|
|
1187
|
+
}).from("orphans").leftOuterJoin(
|
|
1188
|
+
"relations",
|
|
1189
|
+
"relations.target_entity_ref",
|
|
1190
|
+
"orphans.entity_ref"
|
|
1191
|
+
).leftOuterJoin(
|
|
1192
|
+
"refresh_state",
|
|
1193
|
+
"refresh_state.entity_ref",
|
|
1194
|
+
"relations.source_entity_ref"
|
|
1195
|
+
);
|
|
1196
|
+
if (!candidates.length) {
|
|
1197
|
+
break;
|
|
1198
|
+
}
|
|
1199
|
+
const orphanIds = uniq__default["default"](candidates.map((r) => r.entityId));
|
|
1200
|
+
const orphanRelationIds = uniq__default["default"](
|
|
1201
|
+
candidates.map((r) => r.relationSourceId).filter(Boolean)
|
|
1202
|
+
);
|
|
1203
|
+
total += orphanIds.length;
|
|
1204
|
+
await tx.table("refresh_state").delete().whereIn("entity_id", orphanIds);
|
|
1205
|
+
await tx.table("final_entities").update({
|
|
1206
|
+
hash: "orphan-relation-deleted"
|
|
1207
|
+
}).whereIn("entity_id", orphanRelationIds);
|
|
1208
|
+
await tx.table("refresh_state").update({
|
|
1209
|
+
result_hash: "orphan-relation-deleted",
|
|
1210
|
+
next_update_at: tx.fn.now()
|
|
1211
|
+
}).whereIn("entity_id", orphanRelationIds);
|
|
1212
|
+
}
|
|
1213
|
+
return total;
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1172
1216
|
function generateStableHash$1(entity) {
|
|
1173
1217
|
return crypto.createHash("sha1").update(stableStringify__default["default"]({ ...entity })).digest("hex");
|
|
1174
1218
|
}
|
|
@@ -1309,6 +1353,10 @@ class DefaultProcessingDatabase {
|
|
|
1309
1353
|
const entityRefs = rows.map((r) => r.source_entity_ref).filter(Boolean);
|
|
1310
1354
|
return { entityRefs };
|
|
1311
1355
|
}
|
|
1356
|
+
async deleteOrphanedEntities(txOpaque) {
|
|
1357
|
+
const tx = txOpaque;
|
|
1358
|
+
return await deleteOrphanedEntities({ tx });
|
|
1359
|
+
}
|
|
1312
1360
|
async transaction(fn) {
|
|
1313
1361
|
try {
|
|
1314
1362
|
let result = void 0;
|
|
@@ -1445,21 +1493,40 @@ function startTaskPipeline(options) {
|
|
|
1445
1493
|
|
|
1446
1494
|
const CACHE_TTL = 5;
|
|
1447
1495
|
class DefaultCatalogProcessingEngine {
|
|
1448
|
-
constructor(
|
|
1449
|
-
|
|
1450
|
-
this.
|
|
1451
|
-
this.
|
|
1452
|
-
this.
|
|
1453
|
-
this.
|
|
1454
|
-
this.
|
|
1455
|
-
this.
|
|
1456
|
-
this.
|
|
1496
|
+
constructor(options) {
|
|
1497
|
+
var _a, _b, _c;
|
|
1498
|
+
this.config = options.config;
|
|
1499
|
+
this.scheduler = options.scheduler;
|
|
1500
|
+
this.logger = options.logger;
|
|
1501
|
+
this.processingDatabase = options.processingDatabase;
|
|
1502
|
+
this.orchestrator = options.orchestrator;
|
|
1503
|
+
this.stitcher = options.stitcher;
|
|
1504
|
+
this.createHash = options.createHash;
|
|
1505
|
+
this.pollingIntervalMs = (_a = options.pollingIntervalMs) != null ? _a : 1e3;
|
|
1506
|
+
this.orphanCleanupIntervalMs = (_b = options.orphanCleanupIntervalMs) != null ? _b : 3e4;
|
|
1507
|
+
this.onProcessingError = options.onProcessingError;
|
|
1508
|
+
this.tracker = (_c = options.tracker) != null ? _c : progressTracker();
|
|
1509
|
+
this.stopFunc = void 0;
|
|
1457
1510
|
}
|
|
1458
1511
|
async start() {
|
|
1459
1512
|
if (this.stopFunc) {
|
|
1460
1513
|
throw new Error("Processing engine is already started");
|
|
1461
1514
|
}
|
|
1462
|
-
|
|
1515
|
+
const stopPipeline = this.startPipeline();
|
|
1516
|
+
const stopCleanup = this.startOrphanCleanup();
|
|
1517
|
+
this.stopFunc = () => {
|
|
1518
|
+
stopPipeline();
|
|
1519
|
+
stopCleanup();
|
|
1520
|
+
};
|
|
1521
|
+
}
|
|
1522
|
+
async stop() {
|
|
1523
|
+
if (this.stopFunc) {
|
|
1524
|
+
this.stopFunc();
|
|
1525
|
+
this.stopFunc = void 0;
|
|
1526
|
+
}
|
|
1527
|
+
}
|
|
1528
|
+
startPipeline() {
|
|
1529
|
+
return startTaskPipeline({
|
|
1463
1530
|
lowWatermark: 5,
|
|
1464
1531
|
highWatermark: 10,
|
|
1465
1532
|
pollingIntervalMs: this.pollingIntervalMs,
|
|
@@ -1618,11 +1685,40 @@ class DefaultCatalogProcessingEngine {
|
|
|
1618
1685
|
}
|
|
1619
1686
|
});
|
|
1620
1687
|
}
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1688
|
+
startOrphanCleanup() {
|
|
1689
|
+
var _a;
|
|
1690
|
+
const strategy = (_a = this.config.getOptionalString("catalog.orphanStrategy")) != null ? _a : "keep";
|
|
1691
|
+
if (strategy !== "delete") {
|
|
1692
|
+
return () => {
|
|
1693
|
+
};
|
|
1694
|
+
}
|
|
1695
|
+
const runOnce = async () => {
|
|
1696
|
+
try {
|
|
1697
|
+
await this.processingDatabase.transaction(async (tx) => {
|
|
1698
|
+
const n = await this.processingDatabase.deleteOrphanedEntities(tx);
|
|
1699
|
+
this.logger.info(`Deleted ${n} orphaned entities`);
|
|
1700
|
+
});
|
|
1701
|
+
} catch (error) {
|
|
1702
|
+
this.logger.warn(`Failed to delete orphaned entities`, error);
|
|
1703
|
+
}
|
|
1704
|
+
};
|
|
1705
|
+
if (this.scheduler) {
|
|
1706
|
+
const abortController = new AbortController();
|
|
1707
|
+
this.scheduler.scheduleTask({
|
|
1708
|
+
id: "catalog_orphan_cleanup",
|
|
1709
|
+
frequency: { milliseconds: this.orphanCleanupIntervalMs },
|
|
1710
|
+
timeout: { milliseconds: this.orphanCleanupIntervalMs * 0.8 },
|
|
1711
|
+
fn: runOnce,
|
|
1712
|
+
signal: abortController.signal
|
|
1713
|
+
});
|
|
1714
|
+
return () => {
|
|
1715
|
+
abortController.abort();
|
|
1716
|
+
};
|
|
1625
1717
|
}
|
|
1718
|
+
const intervalKey = setInterval(runOnce, this.orphanCleanupIntervalMs);
|
|
1719
|
+
return () => {
|
|
1720
|
+
clearInterval(intervalKey);
|
|
1721
|
+
};
|
|
1626
1722
|
}
|
|
1627
1723
|
}
|
|
1628
1724
|
function progressTracker() {
|
|
@@ -1652,9 +1748,7 @@ function progressTracker() {
|
|
|
1652
1748
|
const meter = api.metrics.getMeter("default");
|
|
1653
1749
|
const stitchedEntities = meter.createCounter(
|
|
1654
1750
|
"catalog.stitched.entities.count",
|
|
1655
|
-
{
|
|
1656
|
-
description: "Amount of entities stitched"
|
|
1657
|
-
}
|
|
1751
|
+
{ description: "Amount of entities stitched" }
|
|
1658
1752
|
);
|
|
1659
1753
|
const processedEntities = meter.createCounter(
|
|
1660
1754
|
"catalog.processed.entities.count",
|
|
@@ -4684,7 +4778,7 @@ class CatalogBuilder {
|
|
|
4684
4778
|
*/
|
|
4685
4779
|
async build() {
|
|
4686
4780
|
var _a, _b;
|
|
4687
|
-
const { config, database, logger, permissions } = this.env;
|
|
4781
|
+
const { config, database, logger, permissions, scheduler } = this.env;
|
|
4688
4782
|
const policy = this.buildEntityPolicy();
|
|
4689
4783
|
const processors = this.buildProcessors();
|
|
4690
4784
|
const parser = this.parser || defaultEntityDataParser;
|
|
@@ -4766,18 +4860,20 @@ class CatalogBuilder {
|
|
|
4766
4860
|
[...this.entityProviders, locationStore, configLocationProvider],
|
|
4767
4861
|
(provider) => provider.getProviderName()
|
|
4768
4862
|
);
|
|
4769
|
-
const processingEngine = new DefaultCatalogProcessingEngine(
|
|
4863
|
+
const processingEngine = new DefaultCatalogProcessingEngine({
|
|
4864
|
+
config,
|
|
4865
|
+
scheduler,
|
|
4770
4866
|
logger,
|
|
4771
4867
|
processingDatabase,
|
|
4772
4868
|
orchestrator,
|
|
4773
4869
|
stitcher,
|
|
4774
|
-
() => crypto.createHash("sha1"),
|
|
4775
|
-
1e3,
|
|
4776
|
-
(event) => {
|
|
4870
|
+
createHash: () => crypto.createHash("sha1"),
|
|
4871
|
+
pollingIntervalMs: 1e3,
|
|
4872
|
+
onProcessingError: (event) => {
|
|
4777
4873
|
var _a2;
|
|
4778
4874
|
(_a2 = this.onProcessingError) == null ? void 0 : _a2.call(this, event);
|
|
4779
4875
|
}
|
|
4780
|
-
);
|
|
4876
|
+
});
|
|
4781
4877
|
const locationAnalyzer = (_b = this.locationAnalyzer) != null ? _b : new RepoLocationAnalyzer(logger, integrations, this.locationAnalyzers);
|
|
4782
4878
|
const locationService = new AuthorizedLocationService(
|
|
4783
4879
|
new DefaultLocationService(locationStore, orchestrator, {
|
|
@@ -4960,4 +5056,4 @@ exports.createCatalogPermissionRule = createCatalogPermissionRule;
|
|
|
4960
5056
|
exports.createRandomProcessingInterval = createRandomProcessingInterval;
|
|
4961
5057
|
exports.parseEntityYaml = parseEntityYaml;
|
|
4962
5058
|
exports.permissionRules = permissionRules;
|
|
4963
|
-
//# sourceMappingURL=CatalogBuilder-
|
|
5059
|
+
//# sourceMappingURL=CatalogBuilder-ecfd2db6.cjs.js.map
|