@backstage/plugin-catalog-backend 1.6.0-next.3 → 1.7.0-next.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,97 @@
|
|
|
1
1
|
# @backstage/plugin-catalog-backend
|
|
2
2
|
|
|
3
|
+
## 1.7.0-next.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- f75bf76330: Implemented server side ordering in the entities endpoint
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- d136793ff0: Fixed an issue where internal references in the catalog would stick around for longer than expected, causing entities to not be deleted or orphaned as expected.
|
|
12
|
+
- Updated dependencies
|
|
13
|
+
- @backstage/catalog-model@1.1.5-next.0
|
|
14
|
+
- @backstage/plugin-scaffolder-common@1.2.4-next.0
|
|
15
|
+
- @backstage/catalog-client@1.3.0-next.0
|
|
16
|
+
- @backstage/backend-common@0.17.0
|
|
17
|
+
- @backstage/backend-plugin-api@0.2.0
|
|
18
|
+
- @backstage/config@1.0.5
|
|
19
|
+
- @backstage/errors@1.1.4
|
|
20
|
+
- @backstage/integration@1.4.1
|
|
21
|
+
- @backstage/types@1.0.2
|
|
22
|
+
- @backstage/plugin-catalog-common@1.0.10-next.0
|
|
23
|
+
- @backstage/plugin-catalog-node@1.3.1-next.0
|
|
24
|
+
- @backstage/plugin-permission-common@0.7.2
|
|
25
|
+
- @backstage/plugin-permission-node@0.7.2
|
|
26
|
+
- @backstage/plugin-search-common@1.2.0
|
|
27
|
+
|
|
28
|
+
## 1.6.0
|
|
29
|
+
|
|
30
|
+
### Minor Changes
|
|
31
|
+
|
|
32
|
+
- 16891a212c: Added new `POST /entities/by-refs` endpoint, which allows you to efficiently
|
|
33
|
+
batch-fetch entities by their entity ref. This can be useful e.g. in graphql
|
|
34
|
+
resolvers or similar contexts where you need to fetch many entities at the same
|
|
35
|
+
time.
|
|
36
|
+
- 273ba3a77f: Deprecated Prometheus metrics in favour of OpenTelemtry metrics.
|
|
37
|
+
- c395abb5b2: The catalog no longer stops after the first processor `validateEntityKind`
|
|
38
|
+
method returns `true` when validating entity kind shapes. Instead, it continues
|
|
39
|
+
through all registered processors that have this method, and requires that _at
|
|
40
|
+
least one_ of them returned true.
|
|
41
|
+
|
|
42
|
+
The old behavior of stopping early made it harder to extend existing core kinds
|
|
43
|
+
with additional fields, since the `BuiltinKindsEntityProcessor` is always
|
|
44
|
+
present at the top of the processing chain and ensures that your additional
|
|
45
|
+
validation code would never be run.
|
|
46
|
+
|
|
47
|
+
This is technically a breaking change, although it should not affect anybody
|
|
48
|
+
under normal circumstances, except if you had problematic validation code that
|
|
49
|
+
you were unaware that it was not being run. That code may now start to exhibit
|
|
50
|
+
those problems.
|
|
51
|
+
|
|
52
|
+
If you need to disable this new behavior, `CatalogBuilder` as used in your
|
|
53
|
+
`packages/backend/src/plugins/catalog.ts` file now has a
|
|
54
|
+
`useLegacySingleProcessorValidation()` method to go back to the old behavior.
|
|
55
|
+
|
|
56
|
+
```diff
|
|
57
|
+
const builder = await CatalogBuilder.create(env);
|
|
58
|
+
+builder.useLegacySingleProcessorValidation();
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
- 3072ebfdd7: The search table also holds the original entity value now and the facets endpoint fetches the filtered entity data from the search table.
|
|
62
|
+
|
|
63
|
+
### Patch Changes
|
|
64
|
+
|
|
65
|
+
- ba13ff663c: Added a new `catalog.rules[].location` configuration that makes it possible to configure catalog rules to only apply to specific locations, either via exact match or a glob pattern.
|
|
66
|
+
- d8593ce0e6: Do not use deprecated `LocationSpec` from the `@backstage/plugin-catalog-node` package
|
|
67
|
+
- c507aee8a2: Ensured typescript type checks in migration files.
|
|
68
|
+
- 2a8e3cc0b5: Optimize `Stitcher` process to be more memory efficient
|
|
69
|
+
- 884d749b14: Refactored to use `coreServices` from `@backstage/backend-plugin-api`.
|
|
70
|
+
- eacc8e2b55: Make it possible for entity providers to supply only entity refs, instead of full entities, in `delta` mutation deletions.
|
|
71
|
+
- b05dcd5530: Move the `zod` dependency to a version that does not collide with other libraries
|
|
72
|
+
- 5b3e2afa45: Fixed deprecated use of `substr` into `substring`.
|
|
73
|
+
- 71147d5c16: Internal code reorganization.
|
|
74
|
+
- 93870e4df1: Track the last time the final entity changed with new timestamp "last updated at" data in final entities database, which gets updated with the time when final entity is updated.
|
|
75
|
+
- 20a5161f04: Adds MySQL support for the catalog-backend
|
|
76
|
+
- 3280711113: Updated dependency `msw` to `^0.49.0`.
|
|
77
|
+
- e982f77fe3: Registered shutdown hook in experimental catalog plugin.
|
|
78
|
+
- b3fac9c107: Ignore attempts at emitting the current entity as a child of itself.
|
|
79
|
+
- Updated dependencies
|
|
80
|
+
- @backstage/catalog-client@1.2.0
|
|
81
|
+
- @backstage/backend-common@0.17.0
|
|
82
|
+
- @backstage/plugin-catalog-node@1.3.0
|
|
83
|
+
- @backstage/plugin-permission-common@0.7.2
|
|
84
|
+
- @backstage/plugin-permission-node@0.7.2
|
|
85
|
+
- @backstage/errors@1.1.4
|
|
86
|
+
- @backstage/backend-plugin-api@0.2.0
|
|
87
|
+
- @backstage/integration@1.4.1
|
|
88
|
+
- @backstage/types@1.0.2
|
|
89
|
+
- @backstage/plugin-search-common@1.2.0
|
|
90
|
+
- @backstage/catalog-model@1.1.4
|
|
91
|
+
- @backstage/config@1.0.5
|
|
92
|
+
- @backstage/plugin-catalog-common@1.0.9
|
|
93
|
+
- @backstage/plugin-scaffolder-common@1.2.3
|
|
94
|
+
|
|
3
95
|
## 1.6.0-next.3
|
|
4
96
|
|
|
5
97
|
### Patch Changes
|
package/alpha/package.json
CHANGED
package/dist/index.cjs.js
CHANGED
|
@@ -28,6 +28,7 @@ var backendCommon = require('@backstage/backend-common');
|
|
|
28
28
|
var luxon = require('luxon');
|
|
29
29
|
var promClient = require('prom-client');
|
|
30
30
|
var stableStringify = require('fast-json-stable-stringify');
|
|
31
|
+
var api = require('@opentelemetry/api');
|
|
31
32
|
var express = require('express');
|
|
32
33
|
var Router = require('express-promise-router');
|
|
33
34
|
var yn = require('yn');
|
|
@@ -1724,7 +1725,6 @@ class DefaultProcessingDatabase {
|
|
|
1724
1725
|
async addUnprocessedEntities(txOpaque, options) {
|
|
1725
1726
|
const tx = txOpaque;
|
|
1726
1727
|
const stateReferences = new Array();
|
|
1727
|
-
const conflictingStateReferences = new Array();
|
|
1728
1728
|
for (const { entity, locationKey } of options.entities) {
|
|
1729
1729
|
const entityRef = catalogModel.stringifyEntityRef(entity);
|
|
1730
1730
|
const hash = generateStableHash$1(entity);
|
|
@@ -1758,10 +1758,9 @@ class DefaultProcessingDatabase {
|
|
|
1758
1758
|
this.options.logger.warn(
|
|
1759
1759
|
`Detected conflicting entityRef ${entityRef} already referenced by ${conflictingKey} and now also ${locationKey}`
|
|
1760
1760
|
);
|
|
1761
|
-
conflictingStateReferences.push(entityRef);
|
|
1762
1761
|
}
|
|
1763
1762
|
}
|
|
1764
|
-
await tx("refresh_state_references").
|
|
1763
|
+
await tx("refresh_state_references").andWhere({ source_entity_ref: options.sourceEntityRef }).delete();
|
|
1765
1764
|
await tx.batchInsert(
|
|
1766
1765
|
"refresh_state_references",
|
|
1767
1766
|
stateReferences.map((entityRef) => ({
|
|
@@ -2008,54 +2007,104 @@ class DefaultCatalogProcessingEngine {
|
|
|
2008
2007
|
}
|
|
2009
2008
|
}
|
|
2010
2009
|
function progressTracker() {
|
|
2011
|
-
const
|
|
2010
|
+
const promStitchedEntities = createCounterMetric({
|
|
2012
2011
|
name: "catalog_stitched_entities_count",
|
|
2013
|
-
help: "Amount of entities stitched"
|
|
2012
|
+
help: "Amount of entities stitched. DEPRECATED, use OpenTelemetry metrics instead"
|
|
2014
2013
|
});
|
|
2015
|
-
const
|
|
2014
|
+
const promProcessedEntities = createCounterMetric({
|
|
2016
2015
|
name: "catalog_processed_entities_count",
|
|
2017
|
-
help: "Amount of entities processed",
|
|
2016
|
+
help: "Amount of entities processed, DEPRECATED, use OpenTelemetry metrics instead",
|
|
2018
2017
|
labelNames: ["result"]
|
|
2019
2018
|
});
|
|
2020
|
-
const
|
|
2019
|
+
const promProcessingDuration = createSummaryMetric({
|
|
2021
2020
|
name: "catalog_processing_duration_seconds",
|
|
2022
|
-
help: "Time spent executing the full processing flow",
|
|
2021
|
+
help: "Time spent executing the full processing flow, DEPRECATED, use OpenTelemetry metrics instead",
|
|
2023
2022
|
labelNames: ["result"]
|
|
2024
2023
|
});
|
|
2025
|
-
const
|
|
2024
|
+
const promProcessorsDuration = createSummaryMetric({
|
|
2026
2025
|
name: "catalog_processors_duration_seconds",
|
|
2027
|
-
help: "Time spent executing catalog processors",
|
|
2026
|
+
help: "Time spent executing catalog processors, DEPRECATED, use OpenTelemetry metrics instead",
|
|
2028
2027
|
labelNames: ["result"]
|
|
2029
2028
|
});
|
|
2030
|
-
const
|
|
2029
|
+
const promProcessingQueueDelay = createSummaryMetric({
|
|
2031
2030
|
name: "catalog_processing_queue_delay_seconds",
|
|
2032
|
-
help: "The amount of delay between being scheduled for processing, and the start of actually being processed"
|
|
2031
|
+
help: "The amount of delay between being scheduled for processing, and the start of actually being processed, DEPRECATED, use OpenTelemetry metrics instead"
|
|
2033
2032
|
});
|
|
2033
|
+
const meter = api.metrics.getMeter("default");
|
|
2034
|
+
const stitchedEntities = meter.createCounter(
|
|
2035
|
+
"catalog.stitched.entities.count",
|
|
2036
|
+
{
|
|
2037
|
+
description: "Amount of entities stitched"
|
|
2038
|
+
}
|
|
2039
|
+
);
|
|
2040
|
+
const processedEntities = meter.createCounter(
|
|
2041
|
+
"catalog.processed.entities.count",
|
|
2042
|
+
{ description: "Amount of entities processed" }
|
|
2043
|
+
);
|
|
2044
|
+
const processingDuration = meter.createHistogram(
|
|
2045
|
+
"catalog.processing.duration",
|
|
2046
|
+
{
|
|
2047
|
+
description: "Time spent executing the full processing flow",
|
|
2048
|
+
unit: "seconds"
|
|
2049
|
+
}
|
|
2050
|
+
);
|
|
2051
|
+
const processorsDuration = meter.createHistogram(
|
|
2052
|
+
"catalog.processors.duration",
|
|
2053
|
+
{
|
|
2054
|
+
description: "Time spent executing catalog processors",
|
|
2055
|
+
unit: "seconds"
|
|
2056
|
+
}
|
|
2057
|
+
);
|
|
2058
|
+
const processingQueueDelay = meter.createHistogram(
|
|
2059
|
+
"catalog.processing.queue.delay",
|
|
2060
|
+
{
|
|
2061
|
+
description: "The amount of delay between being scheduled for processing, and the start of actually being processed",
|
|
2062
|
+
unit: "seconds"
|
|
2063
|
+
}
|
|
2064
|
+
);
|
|
2034
2065
|
function processStart(item, logger) {
|
|
2066
|
+
const startTime = process.hrtime();
|
|
2067
|
+
const endOverallTimer = promProcessingDuration.startTimer();
|
|
2068
|
+
const endProcessorsTimer = promProcessorsDuration.startTimer();
|
|
2035
2069
|
logger.debug(`Processing ${item.entityRef}`);
|
|
2036
2070
|
if (item.nextUpdateAt) {
|
|
2037
|
-
|
|
2071
|
+
const seconds = -item.nextUpdateAt.diffNow().as("seconds");
|
|
2072
|
+
promProcessingQueueDelay.observe(seconds);
|
|
2073
|
+
processingQueueDelay.record(seconds);
|
|
2074
|
+
}
|
|
2075
|
+
function endTime() {
|
|
2076
|
+
const delta = process.hrtime(startTime);
|
|
2077
|
+
return delta[0] + delta[1] / 1e9;
|
|
2038
2078
|
}
|
|
2039
|
-
const endOverallTimer = processingDuration.startTimer();
|
|
2040
|
-
const endProcessorsTimer = processorsDuration.startTimer();
|
|
2041
2079
|
function markProcessorsCompleted(result) {
|
|
2042
2080
|
endProcessorsTimer({ result: result.ok ? "ok" : "failed" });
|
|
2081
|
+
processorsDuration.record(endTime(), {
|
|
2082
|
+
result: result.ok ? "ok" : "failed"
|
|
2083
|
+
});
|
|
2043
2084
|
}
|
|
2044
2085
|
function markSuccessfulWithNoChanges() {
|
|
2045
2086
|
endOverallTimer({ result: "unchanged" });
|
|
2046
|
-
|
|
2087
|
+
promProcessedEntities.inc({ result: "unchanged" }, 1);
|
|
2088
|
+
processingDuration.record(endTime(), { result: "unchanged" });
|
|
2089
|
+
processedEntities.add(1, { result: "unchanged" });
|
|
2047
2090
|
}
|
|
2048
2091
|
function markSuccessfulWithErrors() {
|
|
2049
2092
|
endOverallTimer({ result: "errors" });
|
|
2050
|
-
|
|
2093
|
+
promProcessedEntities.inc({ result: "errors" }, 1);
|
|
2094
|
+
processingDuration.record(endTime(), { result: "errors" });
|
|
2095
|
+
processedEntities.add(1, { result: "errors" });
|
|
2051
2096
|
}
|
|
2052
2097
|
function markSuccessfulWithChanges(stitchedCount) {
|
|
2053
2098
|
endOverallTimer({ result: "changed" });
|
|
2054
|
-
|
|
2055
|
-
|
|
2099
|
+
promStitchedEntities.inc(stitchedCount);
|
|
2100
|
+
promProcessedEntities.inc({ result: "changed" }, 1);
|
|
2101
|
+
processingDuration.record(endTime(), { result: "changed" });
|
|
2102
|
+
stitchedEntities.add(stitchedCount);
|
|
2103
|
+
processedEntities.add(1, { result: "changed" });
|
|
2056
2104
|
}
|
|
2057
2105
|
function markFailed(error) {
|
|
2058
|
-
|
|
2106
|
+
promProcessedEntities.inc({ result: "failed" }, 1);
|
|
2107
|
+
processedEntities.add(1, { result: "failed" });
|
|
2059
2108
|
logger.warn(`Processing of ${item.entityRef} failed`, error);
|
|
2060
2109
|
}
|
|
2061
2110
|
return {
|
|
@@ -2198,7 +2247,7 @@ function stringifyPagination(input) {
|
|
|
2198
2247
|
return base64;
|
|
2199
2248
|
}
|
|
2200
2249
|
function addCondition(queryBuilder, db, filter, negate = false, entityIdField = "entity_id") {
|
|
2201
|
-
const matchQuery = db("search").select(
|
|
2250
|
+
const matchQuery = db("search").select("search.entity_id").where({ key: filter.key.toLowerCase() }).andWhere(function keyFilter() {
|
|
2202
2251
|
if (filter.values) {
|
|
2203
2252
|
if (filter.values.length === 1) {
|
|
2204
2253
|
this.where({ value: filter.values[0].toLowerCase() });
|
|
@@ -2254,12 +2303,41 @@ class DefaultEntitiesCatalog {
|
|
|
2254
2303
|
this.stitcher = stitcher;
|
|
2255
2304
|
}
|
|
2256
2305
|
async entities(request) {
|
|
2306
|
+
var _a, _b;
|
|
2257
2307
|
const db = this.database;
|
|
2258
2308
|
let entitiesQuery = db("final_entities").select("final_entities.*");
|
|
2309
|
+
(_a = request == null ? void 0 : request.order) == null ? void 0 : _a.forEach(({ field }, index) => {
|
|
2310
|
+
const alias = `order_${index}`;
|
|
2311
|
+
entitiesQuery = entitiesQuery.leftOuterJoin(
|
|
2312
|
+
{ [alias]: "search" },
|
|
2313
|
+
function search(inner) {
|
|
2314
|
+
inner.on(`${alias}.entity_id`, "final_entities.entity_id").andOn(`${alias}.key`, db.raw("?", [field]));
|
|
2315
|
+
}
|
|
2316
|
+
);
|
|
2317
|
+
});
|
|
2318
|
+
entitiesQuery = entitiesQuery.whereNotNull("final_entities.final_entity");
|
|
2259
2319
|
if (request == null ? void 0 : request.filter) {
|
|
2260
|
-
entitiesQuery = parseFilter(
|
|
2320
|
+
entitiesQuery = parseFilter(
|
|
2321
|
+
request.filter,
|
|
2322
|
+
entitiesQuery,
|
|
2323
|
+
db,
|
|
2324
|
+
false,
|
|
2325
|
+
"final_entities.entity_id"
|
|
2326
|
+
);
|
|
2261
2327
|
}
|
|
2262
|
-
|
|
2328
|
+
(_b = request == null ? void 0 : request.order) == null ? void 0 : _b.forEach(({ order }, index) => {
|
|
2329
|
+
if (db.client.config.client === "pg") {
|
|
2330
|
+
entitiesQuery = entitiesQuery.orderBy([
|
|
2331
|
+
{ column: `order_${index}.value`, order, nulls: "last" }
|
|
2332
|
+
]);
|
|
2333
|
+
} else {
|
|
2334
|
+
entitiesQuery = entitiesQuery.orderBy([
|
|
2335
|
+
{ column: `order_${index}.value`, order: void 0, nulls: "last" },
|
|
2336
|
+
{ column: `order_${index}.value`, order }
|
|
2337
|
+
]);
|
|
2338
|
+
}
|
|
2339
|
+
});
|
|
2340
|
+
entitiesQuery = entitiesQuery.orderBy("final_entities.entity_id", "asc");
|
|
2263
2341
|
const { limit, offset } = parsePagination(request == null ? void 0 : request.pagination);
|
|
2264
2342
|
if (limit !== void 0) {
|
|
2265
2343
|
entitiesQuery = entitiesQuery.limit(limit + 1);
|
|
@@ -2439,8 +2517,14 @@ class ProcessorOutputCollector {
|
|
|
2439
2517
|
this.refreshKeys = new Array();
|
|
2440
2518
|
this.done = false;
|
|
2441
2519
|
}
|
|
2442
|
-
|
|
2443
|
-
return (i) => this.receive(i);
|
|
2520
|
+
generic() {
|
|
2521
|
+
return (i) => this.receive(this.logger, i);
|
|
2522
|
+
}
|
|
2523
|
+
forProcessor(processor) {
|
|
2524
|
+
const logger = this.logger.child({
|
|
2525
|
+
processor: processor.getProcessorName()
|
|
2526
|
+
});
|
|
2527
|
+
return (i) => this.receive(logger, i);
|
|
2444
2528
|
}
|
|
2445
2529
|
results() {
|
|
2446
2530
|
this.done = true;
|
|
@@ -2451,9 +2535,9 @@ class ProcessorOutputCollector {
|
|
|
2451
2535
|
deferredEntities: this.deferredEntities
|
|
2452
2536
|
};
|
|
2453
2537
|
}
|
|
2454
|
-
receive(i) {
|
|
2538
|
+
receive(logger, i) {
|
|
2455
2539
|
if (this.done) {
|
|
2456
|
-
|
|
2540
|
+
logger.warn(
|
|
2457
2541
|
`Item of type "${i.type}" was emitted after processing had completed. Stack trace: ${new Error().stack}`
|
|
2458
2542
|
);
|
|
2459
2543
|
return;
|
|
@@ -2465,10 +2549,17 @@ class ProcessorOutputCollector {
|
|
|
2465
2549
|
entity = validateEntityEnvelope(i.entity);
|
|
2466
2550
|
} catch (e) {
|
|
2467
2551
|
errors.assertError(e);
|
|
2468
|
-
|
|
2552
|
+
logger.debug(`Envelope validation failed at ${location}, ${e}`);
|
|
2469
2553
|
this.errors.push(e);
|
|
2470
2554
|
return;
|
|
2471
2555
|
}
|
|
2556
|
+
const entityRef = catalogModel.stringifyEntityRef(entity);
|
|
2557
|
+
if (entityRef === catalogModel.stringifyEntityRef(this.parentEntity)) {
|
|
2558
|
+
logger.warn(
|
|
2559
|
+
`Ignored emitted entity ${entityRef} whose ref was identical to the one being processed. This commonly indicates mistakenly emitting the input entity instead of returning it.`
|
|
2560
|
+
);
|
|
2561
|
+
return;
|
|
2562
|
+
}
|
|
2472
2563
|
const annotations = entity.metadata.annotations || {};
|
|
2473
2564
|
if (typeof annotations === "object" && !Array.isArray(annotations)) {
|
|
2474
2565
|
const originLocation = getEntityOriginLocationRef(this.parentEntity);
|
|
@@ -2666,7 +2757,7 @@ class DefaultCatalogProcessingOrchestrator {
|
|
|
2666
2757
|
res = await processor.preProcessEntity(
|
|
2667
2758
|
res,
|
|
2668
2759
|
context.location,
|
|
2669
|
-
context.collector.
|
|
2760
|
+
context.collector.forProcessor(processor),
|
|
2670
2761
|
context.originLocation,
|
|
2671
2762
|
context.cache.forProcessor(processor)
|
|
2672
2763
|
);
|
|
@@ -2749,7 +2840,7 @@ class DefaultCatalogProcessingOrchestrator {
|
|
|
2749
2840
|
}
|
|
2750
2841
|
for (const maybeRelativeTarget of targets) {
|
|
2751
2842
|
if (type === "file" && maybeRelativeTarget.endsWith(path__default["default"].sep)) {
|
|
2752
|
-
context.collector.
|
|
2843
|
+
context.collector.generic()(
|
|
2753
2844
|
pluginCatalogNode.processingResult.inputError(
|
|
2754
2845
|
context.location,
|
|
2755
2846
|
`LocationEntityProcessor cannot handle ${type} type location with target ${context.location.target} that ends with a path separator`
|
|
@@ -2774,7 +2865,7 @@ class DefaultCatalogProcessingOrchestrator {
|
|
|
2774
2865
|
presence
|
|
2775
2866
|
},
|
|
2776
2867
|
presence === "optional",
|
|
2777
|
-
context.collector.
|
|
2868
|
+
context.collector.forProcessor(processor),
|
|
2778
2869
|
this.options.parser,
|
|
2779
2870
|
context.cache.forProcessor(processor, target)
|
|
2780
2871
|
);
|
|
@@ -2805,7 +2896,7 @@ class DefaultCatalogProcessingOrchestrator {
|
|
|
2805
2896
|
res = await processor.postProcessEntity(
|
|
2806
2897
|
res,
|
|
2807
2898
|
context.location,
|
|
2808
|
-
context.collector.
|
|
2899
|
+
context.collector.forProcessor(processor),
|
|
2809
2900
|
context.cache.forProcessor(processor)
|
|
2810
2901
|
);
|
|
2811
2902
|
} catch (e) {
|
|
@@ -3041,8 +3132,9 @@ class Stitcher {
|
|
|
3041
3132
|
"final_entities"
|
|
3042
3133
|
).update({
|
|
3043
3134
|
final_entity: JSON.stringify(entity),
|
|
3044
|
-
hash
|
|
3045
|
-
|
|
3135
|
+
hash,
|
|
3136
|
+
last_updated_at: this.database.fn.now()
|
|
3137
|
+
}).where("entity_id", entityId).where("stitch_ticket", ticket).onConflict("entity_id").merge(["final_entity", "hash", "last_updated_at"]);
|
|
3046
3138
|
if (amountOfRowsChanged === 0) {
|
|
3047
3139
|
this.logger.debug(
|
|
3048
3140
|
`Entity ${entityRef} is already processed, skipping write.`
|
|
@@ -3217,6 +3309,22 @@ function parseEntityFacetParams(params) {
|
|
|
3217
3309
|
throw new errors.InputError("Missing facet parameter");
|
|
3218
3310
|
}
|
|
3219
3311
|
|
|
3312
|
+
function parseEntityOrderParams(params) {
|
|
3313
|
+
var _a;
|
|
3314
|
+
return (_a = parseStringsParam(params.order, "order")) == null ? void 0 : _a.map((item) => {
|
|
3315
|
+
const match = item.match(/^(asc|desc):(.+)$/);
|
|
3316
|
+
if (!match) {
|
|
3317
|
+
throw new errors.InputError(
|
|
3318
|
+
`Invalid order parameter "${item}", expected "<asc or desc>:<field name>"`
|
|
3319
|
+
);
|
|
3320
|
+
}
|
|
3321
|
+
return {
|
|
3322
|
+
order: match[1],
|
|
3323
|
+
field: match[2]
|
|
3324
|
+
};
|
|
3325
|
+
});
|
|
3326
|
+
}
|
|
3327
|
+
|
|
3220
3328
|
async function requireRequestBody(req) {
|
|
3221
3329
|
const contentType = req.header("content-type");
|
|
3222
3330
|
if (!contentType) {
|
|
@@ -3288,6 +3396,7 @@ async function createRouter(options) {
|
|
|
3288
3396
|
const { entities, pageInfo } = await entitiesCatalog.entities({
|
|
3289
3397
|
filter: parseEntityFilterParams(req.query),
|
|
3290
3398
|
fields: parseEntityTransformParams(req.query),
|
|
3399
|
+
order: parseEntityOrderParams(req.query),
|
|
3291
3400
|
pagination: parseEntityPaginationParams(req.query),
|
|
3292
3401
|
authorizationToken: getBearerToken(req.header("authorization"))
|
|
3293
3402
|
});
|
|
@@ -4024,6 +4133,7 @@ class DefaultProviderDatabase {
|
|
|
4024
4133
|
logger: this.options.logger
|
|
4025
4134
|
});
|
|
4026
4135
|
}
|
|
4136
|
+
await tx("refresh_state_references").where("target_entity_ref", entityRef).andWhere({ source_key: options.sourceKey }).delete();
|
|
4027
4137
|
if (ok) {
|
|
4028
4138
|
await tx(
|
|
4029
4139
|
"refresh_state_references"
|