@backstage/plugin-catalog-backend 1.6.0-next.0 → 1.6.0-next.2
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 +76 -0
- package/alpha/package.json +1 -1
- package/dist/index.alpha.d.ts +7 -1
- package/dist/index.beta.d.ts +6 -0
- package/dist/index.cjs.js +137 -91
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +6 -0
- package/migrations/20200511113813_init.js +2 -2
- package/migrations/20200702153613_entities.js +2 -1
- package/migrations/20200807120600_entitySearch.js +1 -1
- package/migrations/20200923104503_case_insensitivity.js +2 -2
- package/migrations/20201005122705_add_entity_full_name.js +4 -3
- package/migrations/20201006130744_entity_data_column.js +2 -1
- package/migrations/20201123205611_relations_table_uniq.js +2 -2
- package/migrations/20210302150147_refresh_state.js +35 -59
- package/migrations/20210622104022_refresh_state_location_key.js +1 -3
- package/migrations/20210925102509_add_refresh_state_input_hash.js +1 -1
- package/migrations/20220616202842_refresh_keys.js +4 -2
- package/migrations/20221109192547_search_add_original_value_column.js +51 -0
- package/package.json +19 -19
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,81 @@
|
|
|
1
1
|
# @backstage/plugin-catalog-backend
|
|
2
2
|
|
|
3
|
+
## 1.6.0-next.2
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 3072ebfdd7: The search table also holds the original entity value now and the facets endpoint fetches the filtered entity data from the search table.
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- c507aee8a2: Ensured typescript type checks in migration files.
|
|
12
|
+
- 884d749b14: Refactored to use `coreServices` from `@backstage/backend-plugin-api`.
|
|
13
|
+
- eacc8e2b55: Make it possible for entity providers to supply only entity refs, instead of full entities, in `delta` mutation deletions.
|
|
14
|
+
- 20a5161f04: Adds MySQL support for the catalog-backend
|
|
15
|
+
- Updated dependencies
|
|
16
|
+
- @backstage/plugin-catalog-node@1.3.0-next.2
|
|
17
|
+
- @backstage/backend-common@0.17.0-next.2
|
|
18
|
+
- @backstage/backend-plugin-api@0.2.0-next.2
|
|
19
|
+
- @backstage/plugin-search-common@1.2.0-next.2
|
|
20
|
+
- @backstage/plugin-permission-node@0.7.2-next.2
|
|
21
|
+
- @backstage/catalog-client@1.2.0-next.1
|
|
22
|
+
- @backstage/catalog-model@1.1.4-next.1
|
|
23
|
+
- @backstage/config@1.0.5-next.1
|
|
24
|
+
- @backstage/errors@1.1.4-next.1
|
|
25
|
+
- @backstage/integration@1.4.1-next.1
|
|
26
|
+
- @backstage/types@1.0.2-next.1
|
|
27
|
+
- @backstage/plugin-catalog-common@1.0.9-next.2
|
|
28
|
+
- @backstage/plugin-permission-common@0.7.2-next.1
|
|
29
|
+
- @backstage/plugin-scaffolder-common@1.2.3-next.1
|
|
30
|
+
|
|
31
|
+
## 1.6.0-next.1
|
|
32
|
+
|
|
33
|
+
### Minor Changes
|
|
34
|
+
|
|
35
|
+
- c395abb5b2: The catalog no longer stops after the first processor `validateEntityKind`
|
|
36
|
+
method returns `true` when validating entity kind shapes. Instead, it continues
|
|
37
|
+
through all registered processors that have this method, and requires that _at
|
|
38
|
+
least one_ of them returned true.
|
|
39
|
+
|
|
40
|
+
The old behavior of stopping early made it harder to extend existing core kinds
|
|
41
|
+
with additional fields, since the `BuiltinKindsEntityProcessor` is always
|
|
42
|
+
present at the top of the processing chain and ensures that your additional
|
|
43
|
+
validation code would never be run.
|
|
44
|
+
|
|
45
|
+
This is technically a breaking change, although it should not affect anybody
|
|
46
|
+
under normal circumstances, except if you had problematic validation code that
|
|
47
|
+
you were unaware that it was not being run. That code may now start to exhibit
|
|
48
|
+
those problems.
|
|
49
|
+
|
|
50
|
+
If you need to disable this new behavior, `CatalogBuilder` as used in your
|
|
51
|
+
`packages/backend/src/plugins/catalog.ts` file now has a
|
|
52
|
+
`useLegacySingleProcessorValidation()` method to go back to the old behavior.
|
|
53
|
+
|
|
54
|
+
```diff
|
|
55
|
+
const builder = await CatalogBuilder.create(env);
|
|
56
|
+
+builder.useLegacySingleProcessorValidation();
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Patch Changes
|
|
60
|
+
|
|
61
|
+
- 2a8e3cc0b5: Optimize `Stitcher` process to be more memory efficient
|
|
62
|
+
- 5b3e2afa45: Fixed deprecated use of `substr` into `substring`.
|
|
63
|
+
- Updated dependencies
|
|
64
|
+
- @backstage/backend-common@0.17.0-next.1
|
|
65
|
+
- @backstage/types@1.0.2-next.1
|
|
66
|
+
- @backstage/backend-plugin-api@0.1.5-next.1
|
|
67
|
+
- @backstage/plugin-catalog-node@1.2.2-next.1
|
|
68
|
+
- @backstage/plugin-permission-node@0.7.2-next.1
|
|
69
|
+
- @backstage/config@1.0.5-next.1
|
|
70
|
+
- @backstage/integration@1.4.1-next.1
|
|
71
|
+
- @backstage/catalog-client@1.2.0-next.1
|
|
72
|
+
- @backstage/catalog-model@1.1.4-next.1
|
|
73
|
+
- @backstage/errors@1.1.4-next.1
|
|
74
|
+
- @backstage/plugin-catalog-common@1.0.9-next.1
|
|
75
|
+
- @backstage/plugin-permission-common@0.7.2-next.1
|
|
76
|
+
- @backstage/plugin-scaffolder-common@1.2.3-next.1
|
|
77
|
+
- @backstage/plugin-search-common@1.1.2-next.1
|
|
78
|
+
|
|
3
79
|
## 1.6.0-next.0
|
|
4
80
|
|
|
5
81
|
### Minor Changes
|
package/alpha/package.json
CHANGED
package/dist/index.alpha.d.ts
CHANGED
|
@@ -177,6 +177,7 @@ export declare class CatalogBuilder {
|
|
|
177
177
|
private locationAnalyzer;
|
|
178
178
|
private readonly permissionRules;
|
|
179
179
|
private allowedLocationType;
|
|
180
|
+
private legacySingleProcessorValidation;
|
|
180
181
|
/**
|
|
181
182
|
* Creates a catalog builder.
|
|
182
183
|
*/
|
|
@@ -306,13 +307,18 @@ export declare class CatalogBuilder {
|
|
|
306
307
|
* @param permissionRules - Additional permission rules
|
|
307
308
|
* @alpha
|
|
308
309
|
*/
|
|
309
|
-
addPermissionRules(...permissionRules: Array<CatalogPermissionRule | Array<CatalogPermissionRule>>):
|
|
310
|
+
addPermissionRules(...permissionRules: Array<CatalogPermissionRule | Array<CatalogPermissionRule>>): this;
|
|
310
311
|
/**
|
|
311
312
|
* Sets up the allowed location types from being registered via the location service.
|
|
312
313
|
*
|
|
313
314
|
* @param allowedLocationTypes - the allowed location types
|
|
314
315
|
*/
|
|
315
316
|
setAllowedLocationTypes(allowedLocationTypes: string[]): CatalogBuilder;
|
|
317
|
+
/**
|
|
318
|
+
* Enables the legacy behaviour of canceling validation early whenever only a
|
|
319
|
+
* single processor declares an entity kind to be valid.
|
|
320
|
+
*/
|
|
321
|
+
useLegacySingleProcessorValidation(): this;
|
|
316
322
|
/**
|
|
317
323
|
* Wires up and returns all of the component parts of the catalog
|
|
318
324
|
*/
|
package/dist/index.beta.d.ts
CHANGED
|
@@ -177,6 +177,7 @@ export declare class CatalogBuilder {
|
|
|
177
177
|
private locationAnalyzer;
|
|
178
178
|
private readonly permissionRules;
|
|
179
179
|
private allowedLocationType;
|
|
180
|
+
private legacySingleProcessorValidation;
|
|
180
181
|
/**
|
|
181
182
|
* Creates a catalog builder.
|
|
182
183
|
*/
|
|
@@ -305,6 +306,11 @@ export declare class CatalogBuilder {
|
|
|
305
306
|
* @param allowedLocationTypes - the allowed location types
|
|
306
307
|
*/
|
|
307
308
|
setAllowedLocationTypes(allowedLocationTypes: string[]): CatalogBuilder;
|
|
309
|
+
/**
|
|
310
|
+
* Enables the legacy behaviour of canceling validation early whenever only a
|
|
311
|
+
* single processor declares an entity kind to be valid.
|
|
312
|
+
*/
|
|
313
|
+
useLegacySingleProcessorValidation(): this;
|
|
308
314
|
/**
|
|
309
315
|
* Wires up and returns all of the component parts of the catalog
|
|
310
316
|
*/
|
package/dist/index.cjs.js
CHANGED
|
@@ -601,7 +601,7 @@ class PlaceholderProcessor {
|
|
|
601
601
|
} else if (keys.length !== 1) {
|
|
602
602
|
return [data, false];
|
|
603
603
|
}
|
|
604
|
-
const resolverKey = keys[0].
|
|
604
|
+
const resolverKey = keys[0].substring(1);
|
|
605
605
|
const resolverValue = data[keys[0]];
|
|
606
606
|
const resolver = this.options.resolvers[resolverKey];
|
|
607
607
|
if (!resolver) {
|
|
@@ -1517,6 +1517,7 @@ class DefaultProcessingDatabase {
|
|
|
1517
1517
|
refreshKeys,
|
|
1518
1518
|
locationKey
|
|
1519
1519
|
} = options;
|
|
1520
|
+
const configClient = tx.client.config.client;
|
|
1520
1521
|
const refreshResult = await tx("refresh_state").update({
|
|
1521
1522
|
processed_entity: JSON.stringify(processedEntity),
|
|
1522
1523
|
result_hash: resultHash,
|
|
@@ -1539,7 +1540,7 @@ class DefaultProcessingDatabase {
|
|
|
1539
1540
|
sourceEntityRef
|
|
1540
1541
|
});
|
|
1541
1542
|
let previousRelationRows;
|
|
1542
|
-
if (
|
|
1543
|
+
if (configClient.includes("sqlite3") || configClient.includes("mysql")) {
|
|
1543
1544
|
previousRelationRows = await tx("relations").select("*").where({ originating_entity_id: id });
|
|
1544
1545
|
await tx("relations").where({ originating_entity_id: id }).delete();
|
|
1545
1546
|
} else {
|
|
@@ -1868,16 +1869,20 @@ class DefaultProcessingDatabase {
|
|
|
1868
1869
|
next_update_at: tx.fn.now(),
|
|
1869
1870
|
last_discovery_at: tx.fn.now()
|
|
1870
1871
|
});
|
|
1871
|
-
if (
|
|
1872
|
+
if (tx.client.config.client.includes("pg")) {
|
|
1872
1873
|
query = query.onConflict("entity_ref").ignore();
|
|
1873
1874
|
}
|
|
1874
1875
|
const result = await query;
|
|
1875
1876
|
return result.rowCount === 1 || result.length === 1;
|
|
1876
1877
|
} catch (error) {
|
|
1877
|
-
if (
|
|
1878
|
+
if (!backendCommon.isDatabaseConflictError(error)) {
|
|
1879
|
+
throw error;
|
|
1880
|
+
} else {
|
|
1881
|
+
this.options.logger.debug(
|
|
1882
|
+
`Unable to insert a new refresh state row, ${error}`
|
|
1883
|
+
);
|
|
1878
1884
|
return false;
|
|
1879
1885
|
}
|
|
1880
|
-
throw error;
|
|
1881
1886
|
}
|
|
1882
1887
|
}
|
|
1883
1888
|
async checkLocationKeyConflict(tx, entityRef, locationKey) {
|
|
@@ -1906,7 +1911,7 @@ class DefaultProcessingDatabase {
|
|
|
1906
1911
|
deferred: e,
|
|
1907
1912
|
hash: generateStableHash$1(e.entity)
|
|
1908
1913
|
})),
|
|
1909
|
-
toRemove: options.removed.map((e) =>
|
|
1914
|
+
toRemove: options.removed.map((e) => e.entityRef)
|
|
1910
1915
|
};
|
|
1911
1916
|
}
|
|
1912
1917
|
const oldRefs = await tx(
|
|
@@ -2425,8 +2430,8 @@ function stringifyPagination(input) {
|
|
|
2425
2430
|
const base64 = Buffer.from(json, "utf8").toString("base64");
|
|
2426
2431
|
return base64;
|
|
2427
2432
|
}
|
|
2428
|
-
function addCondition(queryBuilder, db, filter, negate = false) {
|
|
2429
|
-
const matchQuery = db("search").select(
|
|
2433
|
+
function addCondition(queryBuilder, db, filter, negate = false, entityIdField = "entity_id") {
|
|
2434
|
+
const matchQuery = db("search").select(entityIdField).where({ key: filter.key.toLowerCase() }).andWhere(function keyFilter() {
|
|
2430
2435
|
if (filter.values) {
|
|
2431
2436
|
if (filter.values.length === 1) {
|
|
2432
2437
|
this.where({ value: filter.values[0].toLowerCase() });
|
|
@@ -2439,7 +2444,7 @@ function addCondition(queryBuilder, db, filter, negate = false) {
|
|
|
2439
2444
|
}
|
|
2440
2445
|
}
|
|
2441
2446
|
});
|
|
2442
|
-
queryBuilder.andWhere(
|
|
2447
|
+
queryBuilder.andWhere(entityIdField, negate ? "not in" : "in", matchQuery);
|
|
2443
2448
|
}
|
|
2444
2449
|
function isEntitiesSearchFilter(filter) {
|
|
2445
2450
|
return filter.hasOwnProperty("key");
|
|
@@ -2450,24 +2455,28 @@ function isOrEntityFilter(filter) {
|
|
|
2450
2455
|
function isNegationEntityFilter(filter) {
|
|
2451
2456
|
return filter.hasOwnProperty("not");
|
|
2452
2457
|
}
|
|
2453
|
-
function parseFilter(filter, query, db, negate = false) {
|
|
2458
|
+
function parseFilter(filter, query, db, negate = false, entityIdField = "entity_id") {
|
|
2454
2459
|
if (isEntitiesSearchFilter(filter)) {
|
|
2455
2460
|
return query.andWhere(function filterFunction() {
|
|
2456
|
-
addCondition(this, db, filter, negate);
|
|
2461
|
+
addCondition(this, db, filter, negate, entityIdField);
|
|
2457
2462
|
});
|
|
2458
2463
|
}
|
|
2459
2464
|
if (isNegationEntityFilter(filter)) {
|
|
2460
|
-
return parseFilter(filter.not, query, db, !negate);
|
|
2465
|
+
return parseFilter(filter.not, query, db, !negate, entityIdField);
|
|
2461
2466
|
}
|
|
2462
2467
|
return query[negate ? "andWhereNot" : "andWhere"](function filterFunction() {
|
|
2463
2468
|
var _a, _b;
|
|
2464
2469
|
if (isOrEntityFilter(filter)) {
|
|
2465
2470
|
for (const subFilter of (_a = filter.anyOf) != null ? _a : []) {
|
|
2466
|
-
this.orWhere(
|
|
2471
|
+
this.orWhere(
|
|
2472
|
+
(subQuery) => parseFilter(subFilter, subQuery, db, false, entityIdField)
|
|
2473
|
+
);
|
|
2467
2474
|
}
|
|
2468
2475
|
} else {
|
|
2469
2476
|
for (const subFilter of (_b = filter.allOf) != null ? _b : []) {
|
|
2470
|
-
this.andWhere(
|
|
2477
|
+
this.andWhere(
|
|
2478
|
+
(subQuery) => parseFilter(subFilter, subQuery, db, false, entityIdField)
|
|
2479
|
+
);
|
|
2471
2480
|
}
|
|
2472
2481
|
}
|
|
2473
2482
|
});
|
|
@@ -2551,14 +2560,36 @@ class DefaultEntitiesCatalog {
|
|
|
2551
2560
|
return { items };
|
|
2552
2561
|
}
|
|
2553
2562
|
async removeEntityByUid(uid) {
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2563
|
+
const dbConfig = this.database.client.config;
|
|
2564
|
+
if (dbConfig.client.includes("mysql")) {
|
|
2565
|
+
const results = await this.database("refresh_state").select("entity_id").whereIn("entity_ref", function parents(builder) {
|
|
2566
|
+
return builder.from("refresh_state").innerJoin(
|
|
2567
|
+
"refresh_state_references",
|
|
2568
|
+
{
|
|
2569
|
+
"refresh_state_references.target_entity_ref": "refresh_state.entity_ref"
|
|
2570
|
+
}
|
|
2571
|
+
).where("refresh_state.entity_id", "=", uid).select("refresh_state_references.source_entity_ref");
|
|
2572
|
+
});
|
|
2573
|
+
await this.database("refresh_state").update({
|
|
2574
|
+
result_hash: "child-was-deleted",
|
|
2575
|
+
next_update_at: this.database.fn.now()
|
|
2576
|
+
}).whereIn(
|
|
2577
|
+
"entity_id",
|
|
2578
|
+
results.map((key) => key.entity_id)
|
|
2579
|
+
);
|
|
2580
|
+
} else {
|
|
2581
|
+
await this.database("refresh_state").update({
|
|
2582
|
+
result_hash: "child-was-deleted",
|
|
2583
|
+
next_update_at: this.database.fn.now()
|
|
2584
|
+
}).whereIn("entity_ref", function parents(builder) {
|
|
2585
|
+
return builder.from("refresh_state").innerJoin(
|
|
2586
|
+
"refresh_state_references",
|
|
2587
|
+
{
|
|
2588
|
+
"refresh_state_references.target_entity_ref": "refresh_state.entity_ref"
|
|
2589
|
+
}
|
|
2590
|
+
).where("refresh_state.entity_id", "=", uid).select("refresh_state_references.source_entity_ref");
|
|
2591
|
+
});
|
|
2592
|
+
}
|
|
2562
2593
|
const relationPeers = await this.database.from("relations").innerJoin("refresh_state", {
|
|
2563
2594
|
"refresh_state.entity_ref": "relations.target_entity_ref"
|
|
2564
2595
|
}).where("relations.originating_entity_id", "=", uid).andWhere("refresh_state.entity_id", "!=", uid).select({ ref: "relations.target_entity_ref" }).union(
|
|
@@ -2614,32 +2645,17 @@ class DefaultEntitiesCatalog {
|
|
|
2614
2645
|
};
|
|
2615
2646
|
}
|
|
2616
2647
|
async facets(request) {
|
|
2617
|
-
const { entities } = await this.entities({
|
|
2618
|
-
filter: request.filter,
|
|
2619
|
-
authorizationToken: request.authorizationToken
|
|
2620
|
-
});
|
|
2621
2648
|
const facets = {};
|
|
2649
|
+
const db = this.database;
|
|
2622
2650
|
for (const facet of request.facets) {
|
|
2623
|
-
const
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
}).flatMap((field) => {
|
|
2632
|
-
if (typeof field === "string") {
|
|
2633
|
-
return [field];
|
|
2634
|
-
} else if (Array.isArray(field)) {
|
|
2635
|
-
return field.filter((i) => typeof i === "string");
|
|
2636
|
-
}
|
|
2637
|
-
return [];
|
|
2638
|
-
}).sort();
|
|
2639
|
-
const counts = lodash__default["default"].countBy(values, lodash__default["default"].identity);
|
|
2640
|
-
facets[facet] = Object.entries(counts).map(([value, count]) => ({
|
|
2641
|
-
value,
|
|
2642
|
-
count
|
|
2651
|
+
const dbQuery = db("search").join("final_entities", "search.entity_id", "final_entities.entity_id").where("search.key", facet.toLocaleLowerCase("en-US")).count("search.entity_id as count").select({ value: "search.original_value" }).groupBy("search.original_value");
|
|
2652
|
+
if (request == null ? void 0 : request.filter) {
|
|
2653
|
+
parseFilter(request.filter, dbQuery, db, false, "search.entity_id");
|
|
2654
|
+
}
|
|
2655
|
+
const result = await dbQuery;
|
|
2656
|
+
facets[facet] = result.map((data) => ({
|
|
2657
|
+
value: String(data.value),
|
|
2658
|
+
count: Number(data.count)
|
|
2643
2659
|
}));
|
|
2644
2660
|
}
|
|
2645
2661
|
return { facets };
|
|
@@ -2930,13 +2946,16 @@ class DefaultCatalogProcessingOrchestrator {
|
|
|
2930
2946
|
e
|
|
2931
2947
|
);
|
|
2932
2948
|
}
|
|
2933
|
-
let
|
|
2949
|
+
let valid = false;
|
|
2934
2950
|
for (const processor of this.options.processors) {
|
|
2935
2951
|
if (processor.validateEntityKind) {
|
|
2936
2952
|
try {
|
|
2937
|
-
|
|
2938
|
-
if (
|
|
2939
|
-
|
|
2953
|
+
const thisValid = await processor.validateEntityKind(entity);
|
|
2954
|
+
if (thisValid) {
|
|
2955
|
+
valid = true;
|
|
2956
|
+
if (this.options.legacySingleProcessorValidation) {
|
|
2957
|
+
break;
|
|
2958
|
+
}
|
|
2940
2959
|
}
|
|
2941
2960
|
} catch (e) {
|
|
2942
2961
|
throw new errors.InputError(
|
|
@@ -2946,7 +2965,7 @@ class DefaultCatalogProcessingOrchestrator {
|
|
|
2946
2965
|
}
|
|
2947
2966
|
}
|
|
2948
2967
|
}
|
|
2949
|
-
if (!
|
|
2968
|
+
if (!valid) {
|
|
2950
2969
|
throw new errors.InputError(
|
|
2951
2970
|
`No processor recognized the entity ${context.entityRef} as valid, possibly caused by a foreign kind or apiVersion`
|
|
2952
2971
|
);
|
|
@@ -3079,15 +3098,30 @@ function mapToRows(input, entityId) {
|
|
|
3079
3098
|
for (const { key: rawKey, value: rawValue } of input) {
|
|
3080
3099
|
const key = rawKey.toLocaleLowerCase("en-US");
|
|
3081
3100
|
if (rawValue === void 0 || rawValue === null) {
|
|
3082
|
-
result.push({
|
|
3101
|
+
result.push({
|
|
3102
|
+
entity_id: entityId,
|
|
3103
|
+
key,
|
|
3104
|
+
original_value: null,
|
|
3105
|
+
value: null
|
|
3106
|
+
});
|
|
3083
3107
|
} else {
|
|
3084
3108
|
const value = String(rawValue).toLocaleLowerCase("en-US");
|
|
3085
3109
|
if (key.length <= MAX_KEY_LENGTH) {
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3110
|
+
if (value.length <= MAX_VALUE_LENGTH) {
|
|
3111
|
+
result.push({
|
|
3112
|
+
entity_id: entityId,
|
|
3113
|
+
key,
|
|
3114
|
+
original_value: String(rawValue),
|
|
3115
|
+
value
|
|
3116
|
+
});
|
|
3117
|
+
} else {
|
|
3118
|
+
result.push({
|
|
3119
|
+
entity_id: entityId,
|
|
3120
|
+
key,
|
|
3121
|
+
original_value: null,
|
|
3122
|
+
value: null
|
|
3123
|
+
});
|
|
3124
|
+
}
|
|
3091
3125
|
}
|
|
3092
3126
|
}
|
|
3093
3127
|
}
|
|
@@ -3159,22 +3193,24 @@ class Stitcher {
|
|
|
3159
3193
|
hash: "",
|
|
3160
3194
|
stitch_ticket: ticket
|
|
3161
3195
|
}).onConflict("entity_id").merge(["stitch_ticket"]);
|
|
3162
|
-
const
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
|
|
3173
|
-
|
|
3174
|
-
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
|
|
3196
|
+
const [processedResult, relationsResult] = await Promise.all([
|
|
3197
|
+
this.database.with("incoming_references", function incomingReferences(builder) {
|
|
3198
|
+
return builder.from("refresh_state_references").where({ target_entity_ref: entityRef }).count({ count: "*" });
|
|
3199
|
+
}).select({
|
|
3200
|
+
entityId: "refresh_state.entity_id",
|
|
3201
|
+
processedEntity: "refresh_state.processed_entity",
|
|
3202
|
+
errors: "refresh_state.errors",
|
|
3203
|
+
incomingReferenceCount: "incoming_references.count",
|
|
3204
|
+
previousHash: "final_entities.hash"
|
|
3205
|
+
}).from("refresh_state").where({ "refresh_state.entity_ref": entityRef }).crossJoin(this.database.raw("incoming_references")).leftOuterJoin("final_entities", {
|
|
3206
|
+
"final_entities.entity_id": "refresh_state.entity_id"
|
|
3207
|
+
}),
|
|
3208
|
+
this.database.distinct({
|
|
3209
|
+
relationType: "type",
|
|
3210
|
+
relationTarget: "target_entity_ref"
|
|
3211
|
+
}).from("relations").where({ source_entity_ref: entityRef }).orderBy("relationType", "asc").orderBy("relationTarget", "asc")
|
|
3212
|
+
]);
|
|
3213
|
+
if (!processedResult.length) {
|
|
3178
3214
|
this.logger.error(
|
|
3179
3215
|
`Unable to stitch ${entityRef}, item does not exist in refresh state table`
|
|
3180
3216
|
);
|
|
@@ -3186,7 +3222,7 @@ class Stitcher {
|
|
|
3186
3222
|
errors,
|
|
3187
3223
|
incomingReferenceCount,
|
|
3188
3224
|
previousHash
|
|
3189
|
-
} =
|
|
3225
|
+
} = processedResult[0];
|
|
3190
3226
|
if (!processedEntity) {
|
|
3191
3227
|
this.logger.debug(
|
|
3192
3228
|
`Unable to stitch ${entityRef}, the entity has not yet been processed`
|
|
@@ -3214,11 +3250,7 @@ class Stitcher {
|
|
|
3214
3250
|
}));
|
|
3215
3251
|
}
|
|
3216
3252
|
}
|
|
3217
|
-
|
|
3218
|
-
result,
|
|
3219
|
-
(r) => `${r.relationType}:${r.relationTarget}`
|
|
3220
|
-
);
|
|
3221
|
-
entity.relations = uniqueRelationRows.filter((row) => row.relationType).map((row) => ({
|
|
3253
|
+
entity.relations = relationsResult.filter((row) => row.relationType).map((row) => ({
|
|
3222
3254
|
type: row.relationType,
|
|
3223
3255
|
targetRef: row.relationTarget
|
|
3224
3256
|
}));
|
|
@@ -3330,8 +3362,8 @@ function parseEntityFilterString(filterString) {
|
|
|
3330
3362
|
const filtersByKey = {};
|
|
3331
3363
|
for (const statement of statements) {
|
|
3332
3364
|
const equalsIndex = statement.indexOf("=");
|
|
3333
|
-
const key = equalsIndex === -1 ? statement : statement.
|
|
3334
|
-
const value = equalsIndex === -1 ? void 0 : statement.
|
|
3365
|
+
const key = equalsIndex === -1 ? statement : statement.substring(0, equalsIndex).trim();
|
|
3366
|
+
const value = equalsIndex === -1 ? void 0 : statement.substring(equalsIndex + 1).trim();
|
|
3335
3367
|
if (!key) {
|
|
3336
3368
|
throw new errors.InputError(
|
|
3337
3369
|
`Invalid filter, '${statement}' is not a valid statement (expected a string on the form a=b or a= or a)`
|
|
@@ -3807,13 +3839,20 @@ class Connection {
|
|
|
3807
3839
|
});
|
|
3808
3840
|
} else if (mutation.type === "delta") {
|
|
3809
3841
|
this.check(mutation.added.map((e) => e.entity));
|
|
3810
|
-
this.check(
|
|
3842
|
+
this.check(
|
|
3843
|
+
mutation.removed.map((e) => "entity" in e ? e.entity : void 0).filter((e) => Boolean(e))
|
|
3844
|
+
);
|
|
3811
3845
|
await db.transaction(async (tx) => {
|
|
3812
3846
|
await db.replaceUnprocessedEntities(tx, {
|
|
3813
3847
|
sourceKey: this.config.id,
|
|
3814
3848
|
type: "delta",
|
|
3815
3849
|
added: mutation.added,
|
|
3816
|
-
removed: mutation.removed
|
|
3850
|
+
removed: mutation.removed.map(
|
|
3851
|
+
(r) => "entityRef" in r ? r : {
|
|
3852
|
+
entityRef: catalogModel.stringifyEntityRef(r.entity),
|
|
3853
|
+
locationKey: r.locationKey
|
|
3854
|
+
}
|
|
3855
|
+
)
|
|
3817
3856
|
});
|
|
3818
3857
|
});
|
|
3819
3858
|
}
|
|
@@ -4057,6 +4096,7 @@ class CatalogBuilder {
|
|
|
4057
4096
|
maxSeconds: 150
|
|
4058
4097
|
});
|
|
4059
4098
|
this.locationAnalyzer = void 0;
|
|
4099
|
+
this.legacySingleProcessorValidation = false;
|
|
4060
4100
|
this.env = env;
|
|
4061
4101
|
this.entityPolicies = [];
|
|
4062
4102
|
this.entityPoliciesReplace = false;
|
|
@@ -4138,11 +4178,16 @@ class CatalogBuilder {
|
|
|
4138
4178
|
}
|
|
4139
4179
|
addPermissionRules(...permissionRules) {
|
|
4140
4180
|
this.permissionRules.push(...permissionRules.flat());
|
|
4181
|
+
return this;
|
|
4141
4182
|
}
|
|
4142
4183
|
setAllowedLocationTypes(allowedLocationTypes) {
|
|
4143
4184
|
this.allowedLocationType = allowedLocationTypes;
|
|
4144
4185
|
return this;
|
|
4145
4186
|
}
|
|
4187
|
+
useLegacySingleProcessorValidation() {
|
|
4188
|
+
this.legacySingleProcessorValidation = true;
|
|
4189
|
+
return this;
|
|
4190
|
+
}
|
|
4146
4191
|
async build() {
|
|
4147
4192
|
var _a, _b;
|
|
4148
4193
|
const { config, database, logger, permissions } = this.env;
|
|
@@ -4167,7 +4212,8 @@ class CatalogBuilder {
|
|
|
4167
4212
|
rulesEnforcer,
|
|
4168
4213
|
logger,
|
|
4169
4214
|
parser,
|
|
4170
|
-
policy
|
|
4215
|
+
policy,
|
|
4216
|
+
legacySingleProcessorValidation: this.legacySingleProcessorValidation
|
|
4171
4217
|
});
|
|
4172
4218
|
const stitcher = new Stitcher(dbClient, logger);
|
|
4173
4219
|
const unauthorizedEntitiesCatalog = new DefaultEntitiesCatalog(
|
|
@@ -4436,13 +4482,13 @@ const catalogPlugin = backendPluginApi.createBackendPlugin({
|
|
|
4436
4482
|
);
|
|
4437
4483
|
env.registerInit({
|
|
4438
4484
|
deps: {
|
|
4439
|
-
logger: backendPluginApi.
|
|
4440
|
-
config: backendPluginApi.
|
|
4441
|
-
reader: backendPluginApi.
|
|
4442
|
-
permissions: backendPluginApi.
|
|
4443
|
-
database: backendPluginApi.
|
|
4444
|
-
httpRouter: backendPluginApi.
|
|
4445
|
-
lifecycle: backendPluginApi.
|
|
4485
|
+
logger: backendPluginApi.coreServices.logger,
|
|
4486
|
+
config: backendPluginApi.coreServices.config,
|
|
4487
|
+
reader: backendPluginApi.coreServices.urlReader,
|
|
4488
|
+
permissions: backendPluginApi.coreServices.permissions,
|
|
4489
|
+
database: backendPluginApi.coreServices.database,
|
|
4490
|
+
httpRouter: backendPluginApi.coreServices.httpRouter,
|
|
4491
|
+
lifecycle: backendPluginApi.coreServices.lifecycle
|
|
4446
4492
|
},
|
|
4447
4493
|
async init({
|
|
4448
4494
|
logger,
|