@backstage/plugin-catalog-backend 1.5.1 → 1.6.0-next.1
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 +78 -0
- package/alpha/package.json +1 -1
- package/dist/index.alpha.d.ts +31 -13
- package/dist/index.beta.d.ts +30 -12
- package/dist/index.cjs.js +128 -45
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +30 -12
- package/package.json +20 -20
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,83 @@
|
|
|
1
1
|
# @backstage/plugin-catalog-backend
|
|
2
2
|
|
|
3
|
+
## 1.6.0-next.1
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- c395abb5b2: The catalog no longer stops after the first processor `validateEntityKind`
|
|
8
|
+
method returns `true` when validating entity kind shapes. Instead, it continues
|
|
9
|
+
through all registered processors that have this method, and requires that _at
|
|
10
|
+
least one_ of them returned true.
|
|
11
|
+
|
|
12
|
+
The old behavior of stopping early made it harder to extend existing core kinds
|
|
13
|
+
with additional fields, since the `BuiltinKindsEntityProcessor` is always
|
|
14
|
+
present at the top of the processing chain and ensures that your additional
|
|
15
|
+
validation code would never be run.
|
|
16
|
+
|
|
17
|
+
This is technically a breaking change, although it should not affect anybody
|
|
18
|
+
under normal circumstances, except if you had problematic validation code that
|
|
19
|
+
you were unaware that it was not being run. That code may now start to exhibit
|
|
20
|
+
those problems.
|
|
21
|
+
|
|
22
|
+
If you need to disable this new behavior, `CatalogBuilder` as used in your
|
|
23
|
+
`packages/backend/src/plugins/catalog.ts` file now has a
|
|
24
|
+
`useLegacySingleProcessorValidation()` method to go back to the old behavior.
|
|
25
|
+
|
|
26
|
+
```diff
|
|
27
|
+
const builder = await CatalogBuilder.create(env);
|
|
28
|
+
+builder.useLegacySingleProcessorValidation();
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Patch Changes
|
|
32
|
+
|
|
33
|
+
- 2a8e3cc0b5: Optimize `Stitcher` process to be more memory efficient
|
|
34
|
+
- 5b3e2afa45: Fixed deprecated use of `substr` into `substring`.
|
|
35
|
+
- Updated dependencies
|
|
36
|
+
- @backstage/backend-common@0.17.0-next.1
|
|
37
|
+
- @backstage/types@1.0.2-next.1
|
|
38
|
+
- @backstage/backend-plugin-api@0.1.5-next.1
|
|
39
|
+
- @backstage/plugin-catalog-node@1.2.2-next.1
|
|
40
|
+
- @backstage/plugin-permission-node@0.7.2-next.1
|
|
41
|
+
- @backstage/config@1.0.5-next.1
|
|
42
|
+
- @backstage/integration@1.4.1-next.1
|
|
43
|
+
- @backstage/catalog-client@1.2.0-next.1
|
|
44
|
+
- @backstage/catalog-model@1.1.4-next.1
|
|
45
|
+
- @backstage/errors@1.1.4-next.1
|
|
46
|
+
- @backstage/plugin-catalog-common@1.0.9-next.1
|
|
47
|
+
- @backstage/plugin-permission-common@0.7.2-next.1
|
|
48
|
+
- @backstage/plugin-scaffolder-common@1.2.3-next.1
|
|
49
|
+
- @backstage/plugin-search-common@1.1.2-next.1
|
|
50
|
+
|
|
51
|
+
## 1.6.0-next.0
|
|
52
|
+
|
|
53
|
+
### Minor Changes
|
|
54
|
+
|
|
55
|
+
- 16891a212c: Added new `POST /entities/by-refs` endpoint, which allows you to efficiently
|
|
56
|
+
batch-fetch entities by their entity ref. This can be useful e.g. in graphql
|
|
57
|
+
resolvers or similar contexts where you need to fetch many entities at the same
|
|
58
|
+
time.
|
|
59
|
+
|
|
60
|
+
### Patch Changes
|
|
61
|
+
|
|
62
|
+
- d8593ce0e6: Do not use deprecated `LocationSpec` from the `@backstage/plugin-catalog-node` package
|
|
63
|
+
- 3280711113: Updated dependency `msw` to `^0.49.0`.
|
|
64
|
+
- e982f77fe3: Registered shutdown hook in experimental catalog plugin.
|
|
65
|
+
- Updated dependencies
|
|
66
|
+
- @backstage/catalog-client@1.2.0-next.0
|
|
67
|
+
- @backstage/backend-common@0.16.1-next.0
|
|
68
|
+
- @backstage/integration@1.4.1-next.0
|
|
69
|
+
- @backstage/plugin-permission-common@0.7.2-next.0
|
|
70
|
+
- @backstage/plugin-permission-node@0.7.2-next.0
|
|
71
|
+
- @backstage/types@1.0.2-next.0
|
|
72
|
+
- @backstage/backend-plugin-api@0.1.5-next.0
|
|
73
|
+
- @backstage/plugin-catalog-node@1.2.2-next.0
|
|
74
|
+
- @backstage/catalog-model@1.1.4-next.0
|
|
75
|
+
- @backstage/config@1.0.5-next.0
|
|
76
|
+
- @backstage/errors@1.1.4-next.0
|
|
77
|
+
- @backstage/plugin-catalog-common@1.0.9-next.0
|
|
78
|
+
- @backstage/plugin-scaffolder-common@1.2.3-next.0
|
|
79
|
+
- @backstage/plugin-search-common@1.1.2-next.0
|
|
80
|
+
|
|
3
81
|
## 1.5.1
|
|
4
82
|
|
|
5
83
|
### Patch Changes
|
package/alpha/package.json
CHANGED
package/dist/index.alpha.d.ts
CHANGED
|
@@ -38,7 +38,7 @@ import { EntityRelationSpec } from '@backstage/plugin-catalog-node';
|
|
|
38
38
|
import { GetEntitiesRequest } from '@backstage/catalog-client';
|
|
39
39
|
import { JsonValue } from '@backstage/types';
|
|
40
40
|
import { LocationEntityV1alpha1 } from '@backstage/catalog-model';
|
|
41
|
-
import { LocationSpec } from '@backstage/plugin-catalog-
|
|
41
|
+
import { LocationSpec as LocationSpec_2 } from '@backstage/plugin-catalog-common';
|
|
42
42
|
import { Logger } from 'winston';
|
|
43
43
|
import { Permission } from '@backstage/plugin-permission-common';
|
|
44
44
|
import { PermissionAuthorizer } from '@backstage/plugin-permission-common';
|
|
@@ -114,7 +114,7 @@ export declare class AnnotateLocationEntityProcessor implements CatalogProcessor
|
|
|
114
114
|
integrations: ScmIntegrationRegistry;
|
|
115
115
|
});
|
|
116
116
|
getProcessorName(): string;
|
|
117
|
-
preProcessEntity(entity: Entity, location:
|
|
117
|
+
preProcessEntity(entity: Entity, location: LocationSpec_2, _: CatalogProcessorEmit, originLocation: LocationSpec_2): Promise<Entity>;
|
|
118
118
|
}
|
|
119
119
|
|
|
120
120
|
/** @public */
|
|
@@ -125,7 +125,7 @@ export declare class AnnotateScmSlugEntityProcessor implements CatalogProcessor
|
|
|
125
125
|
});
|
|
126
126
|
getProcessorName(): string;
|
|
127
127
|
static fromConfig(config: Config): AnnotateScmSlugEntityProcessor;
|
|
128
|
-
preProcessEntity(entity: Entity, location:
|
|
128
|
+
preProcessEntity(entity: Entity, location: LocationSpec_2): Promise<Entity>;
|
|
129
129
|
}
|
|
130
130
|
|
|
131
131
|
/** @public */
|
|
@@ -133,7 +133,7 @@ export declare class BuiltinKindsEntityProcessor implements CatalogProcessor {
|
|
|
133
133
|
private readonly validators;
|
|
134
134
|
getProcessorName(): string;
|
|
135
135
|
validateEntityKind(entity: Entity): Promise<boolean>;
|
|
136
|
-
postProcessEntity(entity: Entity, _location:
|
|
136
|
+
postProcessEntity(entity: Entity, _location: LocationSpec_2, emit: CatalogProcessorEmit): Promise<Entity>;
|
|
137
137
|
}
|
|
138
138
|
|
|
139
139
|
/**
|
|
@@ -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
|
*/
|
|
@@ -427,7 +433,7 @@ export declare class CodeOwnersProcessor implements CatalogProcessor {
|
|
|
427
433
|
reader: UrlReader;
|
|
428
434
|
});
|
|
429
435
|
getProcessorName(): string;
|
|
430
|
-
preProcessEntity(entity: Entity, location:
|
|
436
|
+
preProcessEntity(entity: Entity, location: LocationSpec_2): Promise<Entity>;
|
|
431
437
|
}
|
|
432
438
|
|
|
433
439
|
/**
|
|
@@ -586,7 +592,7 @@ export { EntityRelationSpec }
|
|
|
586
592
|
/** @public */
|
|
587
593
|
export declare class FileReaderProcessor implements CatalogProcessor {
|
|
588
594
|
getProcessorName(): string;
|
|
589
|
-
readLocation(location:
|
|
595
|
+
readLocation(location: LocationSpec_2, optional: boolean, emit: CatalogProcessorEmit, parser: CatalogProcessorParser): Promise<boolean>;
|
|
590
596
|
}
|
|
591
597
|
|
|
592
598
|
/** @public */
|
|
@@ -605,7 +611,7 @@ export declare class LocationEntityProcessor implements CatalogProcessor {
|
|
|
605
611
|
private readonly options;
|
|
606
612
|
constructor(options: LocationEntityProcessorOptions);
|
|
607
613
|
getProcessorName(): string;
|
|
608
|
-
postProcessEntity(entity: Entity, location:
|
|
614
|
+
postProcessEntity(entity: Entity, location: LocationSpec_2, emit: CatalogProcessorEmit): Promise<Entity>;
|
|
609
615
|
}
|
|
610
616
|
|
|
611
617
|
/** @public */
|
|
@@ -613,16 +619,28 @@ export declare type LocationEntityProcessorOptions = {
|
|
|
613
619
|
integrations: ScmIntegrationRegistry;
|
|
614
620
|
};
|
|
615
621
|
|
|
616
|
-
|
|
622
|
+
/**
|
|
623
|
+
* Holds the entity location information.
|
|
624
|
+
*
|
|
625
|
+
* @remarks
|
|
626
|
+
*
|
|
627
|
+
* `presence` flag: when using repo importer plugin, location is being created before the component yaml file is merged to the main branch.
|
|
628
|
+
* This flag is then set to indicate that the file can be not present.
|
|
629
|
+
* default value: 'required'.
|
|
630
|
+
*
|
|
631
|
+
* @public
|
|
632
|
+
* @deprecated use the same type from `@backstage/plugin-catalog-common` instead
|
|
633
|
+
*/
|
|
634
|
+
export declare type LocationSpec = LocationSpec_2;
|
|
617
635
|
|
|
618
636
|
/** @public */
|
|
619
637
|
export declare function locationSpecToLocationEntity(opts: {
|
|
620
|
-
location:
|
|
638
|
+
location: LocationSpec_2;
|
|
621
639
|
parentEntity?: Entity;
|
|
622
640
|
}): LocationEntityV1alpha1;
|
|
623
641
|
|
|
624
642
|
/** @public */
|
|
625
|
-
export declare function parseEntityYaml(data: Buffer, location:
|
|
643
|
+
export declare function parseEntityYaml(data: Buffer, location: LocationSpec_2): Iterable<CatalogProcessorResult>;
|
|
626
644
|
|
|
627
645
|
/**
|
|
628
646
|
* These permission rules can be used to conditionally filter catalog entities
|
|
@@ -663,7 +681,7 @@ export declare class PlaceholderProcessor implements CatalogProcessor {
|
|
|
663
681
|
private readonly options;
|
|
664
682
|
constructor(options: PlaceholderProcessorOptions);
|
|
665
683
|
getProcessorName(): string;
|
|
666
|
-
preProcessEntity(entity: Entity, location:
|
|
684
|
+
preProcessEntity(entity: Entity, location: LocationSpec_2, emit: CatalogProcessorEmit): Promise<Entity>;
|
|
667
685
|
}
|
|
668
686
|
|
|
669
687
|
/** @public */
|
|
@@ -719,7 +737,7 @@ export declare class UrlReaderProcessor implements CatalogProcessor {
|
|
|
719
737
|
logger: Logger;
|
|
720
738
|
});
|
|
721
739
|
getProcessorName(): string;
|
|
722
|
-
readLocation(location:
|
|
740
|
+
readLocation(location: LocationSpec_2, optional: boolean, emit: CatalogProcessorEmit, parser: CatalogProcessorParser, cache: CatalogProcessorCache): Promise<boolean>;
|
|
723
741
|
private doRead;
|
|
724
742
|
}
|
|
725
743
|
|
package/dist/index.beta.d.ts
CHANGED
|
@@ -38,7 +38,7 @@ import { EntityRelationSpec } from '@backstage/plugin-catalog-node';
|
|
|
38
38
|
import { GetEntitiesRequest } from '@backstage/catalog-client';
|
|
39
39
|
import { JsonValue } from '@backstage/types';
|
|
40
40
|
import { LocationEntityV1alpha1 } from '@backstage/catalog-model';
|
|
41
|
-
import { LocationSpec } from '@backstage/plugin-catalog-
|
|
41
|
+
import { LocationSpec as LocationSpec_2 } from '@backstage/plugin-catalog-common';
|
|
42
42
|
import { Logger } from 'winston';
|
|
43
43
|
import { Permission } from '@backstage/plugin-permission-common';
|
|
44
44
|
import { PermissionAuthorizer } from '@backstage/plugin-permission-common';
|
|
@@ -114,7 +114,7 @@ export declare class AnnotateLocationEntityProcessor implements CatalogProcessor
|
|
|
114
114
|
integrations: ScmIntegrationRegistry;
|
|
115
115
|
});
|
|
116
116
|
getProcessorName(): string;
|
|
117
|
-
preProcessEntity(entity: Entity, location:
|
|
117
|
+
preProcessEntity(entity: Entity, location: LocationSpec_2, _: CatalogProcessorEmit, originLocation: LocationSpec_2): Promise<Entity>;
|
|
118
118
|
}
|
|
119
119
|
|
|
120
120
|
/** @public */
|
|
@@ -125,7 +125,7 @@ export declare class AnnotateScmSlugEntityProcessor implements CatalogProcessor
|
|
|
125
125
|
});
|
|
126
126
|
getProcessorName(): string;
|
|
127
127
|
static fromConfig(config: Config): AnnotateScmSlugEntityProcessor;
|
|
128
|
-
preProcessEntity(entity: Entity, location:
|
|
128
|
+
preProcessEntity(entity: Entity, location: LocationSpec_2): Promise<Entity>;
|
|
129
129
|
}
|
|
130
130
|
|
|
131
131
|
/** @public */
|
|
@@ -133,7 +133,7 @@ export declare class BuiltinKindsEntityProcessor implements CatalogProcessor {
|
|
|
133
133
|
private readonly validators;
|
|
134
134
|
getProcessorName(): string;
|
|
135
135
|
validateEntityKind(entity: Entity): Promise<boolean>;
|
|
136
|
-
postProcessEntity(entity: Entity, _location:
|
|
136
|
+
postProcessEntity(entity: Entity, _location: LocationSpec_2, emit: CatalogProcessorEmit): Promise<Entity>;
|
|
137
137
|
}
|
|
138
138
|
|
|
139
139
|
/**
|
|
@@ -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
|
*/
|
|
@@ -380,7 +386,7 @@ export declare class CodeOwnersProcessor implements CatalogProcessor {
|
|
|
380
386
|
reader: UrlReader;
|
|
381
387
|
});
|
|
382
388
|
getProcessorName(): string;
|
|
383
|
-
preProcessEntity(entity: Entity, location:
|
|
389
|
+
preProcessEntity(entity: Entity, location: LocationSpec_2): Promise<Entity>;
|
|
384
390
|
}
|
|
385
391
|
|
|
386
392
|
/* Excluded from this release type: createCatalogConditionalDecision */
|
|
@@ -501,7 +507,7 @@ export { EntityRelationSpec }
|
|
|
501
507
|
/** @public */
|
|
502
508
|
export declare class FileReaderProcessor implements CatalogProcessor {
|
|
503
509
|
getProcessorName(): string;
|
|
504
|
-
readLocation(location:
|
|
510
|
+
readLocation(location: LocationSpec_2, optional: boolean, emit: CatalogProcessorEmit, parser: CatalogProcessorParser): Promise<boolean>;
|
|
505
511
|
}
|
|
506
512
|
|
|
507
513
|
/** @public */
|
|
@@ -520,7 +526,7 @@ export declare class LocationEntityProcessor implements CatalogProcessor {
|
|
|
520
526
|
private readonly options;
|
|
521
527
|
constructor(options: LocationEntityProcessorOptions);
|
|
522
528
|
getProcessorName(): string;
|
|
523
|
-
postProcessEntity(entity: Entity, location:
|
|
529
|
+
postProcessEntity(entity: Entity, location: LocationSpec_2, emit: CatalogProcessorEmit): Promise<Entity>;
|
|
524
530
|
}
|
|
525
531
|
|
|
526
532
|
/** @public */
|
|
@@ -528,16 +534,28 @@ export declare type LocationEntityProcessorOptions = {
|
|
|
528
534
|
integrations: ScmIntegrationRegistry;
|
|
529
535
|
};
|
|
530
536
|
|
|
531
|
-
|
|
537
|
+
/**
|
|
538
|
+
* Holds the entity location information.
|
|
539
|
+
*
|
|
540
|
+
* @remarks
|
|
541
|
+
*
|
|
542
|
+
* `presence` flag: when using repo importer plugin, location is being created before the component yaml file is merged to the main branch.
|
|
543
|
+
* This flag is then set to indicate that the file can be not present.
|
|
544
|
+
* default value: 'required'.
|
|
545
|
+
*
|
|
546
|
+
* @public
|
|
547
|
+
* @deprecated use the same type from `@backstage/plugin-catalog-common` instead
|
|
548
|
+
*/
|
|
549
|
+
export declare type LocationSpec = LocationSpec_2;
|
|
532
550
|
|
|
533
551
|
/** @public */
|
|
534
552
|
export declare function locationSpecToLocationEntity(opts: {
|
|
535
|
-
location:
|
|
553
|
+
location: LocationSpec_2;
|
|
536
554
|
parentEntity?: Entity;
|
|
537
555
|
}): LocationEntityV1alpha1;
|
|
538
556
|
|
|
539
557
|
/** @public */
|
|
540
|
-
export declare function parseEntityYaml(data: Buffer, location:
|
|
558
|
+
export declare function parseEntityYaml(data: Buffer, location: LocationSpec_2): Iterable<CatalogProcessorResult>;
|
|
541
559
|
|
|
542
560
|
/* Excluded from this release type: permissionRules */
|
|
543
561
|
|
|
@@ -550,7 +568,7 @@ export declare class PlaceholderProcessor implements CatalogProcessor {
|
|
|
550
568
|
private readonly options;
|
|
551
569
|
constructor(options: PlaceholderProcessorOptions);
|
|
552
570
|
getProcessorName(): string;
|
|
553
|
-
preProcessEntity(entity: Entity, location:
|
|
571
|
+
preProcessEntity(entity: Entity, location: LocationSpec_2, emit: CatalogProcessorEmit): Promise<Entity>;
|
|
554
572
|
}
|
|
555
573
|
|
|
556
574
|
/** @public */
|
|
@@ -606,7 +624,7 @@ export declare class UrlReaderProcessor implements CatalogProcessor {
|
|
|
606
624
|
logger: Logger;
|
|
607
625
|
});
|
|
608
626
|
getProcessorName(): string;
|
|
609
|
-
readLocation(location:
|
|
627
|
+
readLocation(location: LocationSpec_2, optional: boolean, emit: CatalogProcessorEmit, parser: CatalogProcessorParser, cache: CatalogProcessorCache): Promise<boolean>;
|
|
610
628
|
private doRead;
|
|
611
629
|
}
|
|
612
630
|
|
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) {
|
|
@@ -2525,6 +2525,31 @@ class DefaultEntitiesCatalog {
|
|
|
2525
2525
|
pageInfo
|
|
2526
2526
|
};
|
|
2527
2527
|
}
|
|
2528
|
+
async entitiesBatch(request) {
|
|
2529
|
+
const lookup = /* @__PURE__ */ new Map();
|
|
2530
|
+
for (const chunk of lodash__default["default"].chunk(request.entityRefs, 200)) {
|
|
2531
|
+
let query = this.database("final_entities").innerJoin("refresh_state", {
|
|
2532
|
+
"refresh_state.entity_id": "final_entities.entity_id"
|
|
2533
|
+
}).select({
|
|
2534
|
+
entityRef: "refresh_state.entity_ref",
|
|
2535
|
+
entity: "final_entities.final_entity"
|
|
2536
|
+
}).whereIn("refresh_state.entity_ref", chunk);
|
|
2537
|
+
if (request == null ? void 0 : request.filter) {
|
|
2538
|
+
query = parseFilter(request.filter, query, this.database);
|
|
2539
|
+
}
|
|
2540
|
+
for (const row of await query) {
|
|
2541
|
+
lookup.set(row.entityRef, row.entity ? JSON.parse(row.entity) : null);
|
|
2542
|
+
}
|
|
2543
|
+
}
|
|
2544
|
+
let items = request.entityRefs.map((ref) => {
|
|
2545
|
+
var _a;
|
|
2546
|
+
return (_a = lookup.get(ref)) != null ? _a : null;
|
|
2547
|
+
});
|
|
2548
|
+
if (request.fields) {
|
|
2549
|
+
items = items.map((e) => e && request.fields(e));
|
|
2550
|
+
}
|
|
2551
|
+
return { items };
|
|
2552
|
+
}
|
|
2528
2553
|
async removeEntityByUid(uid) {
|
|
2529
2554
|
await this.database("refresh_state").update({
|
|
2530
2555
|
result_hash: "child-was-deleted",
|
|
@@ -2905,13 +2930,16 @@ class DefaultCatalogProcessingOrchestrator {
|
|
|
2905
2930
|
e
|
|
2906
2931
|
);
|
|
2907
2932
|
}
|
|
2908
|
-
let
|
|
2933
|
+
let valid = false;
|
|
2909
2934
|
for (const processor of this.options.processors) {
|
|
2910
2935
|
if (processor.validateEntityKind) {
|
|
2911
2936
|
try {
|
|
2912
|
-
|
|
2913
|
-
if (
|
|
2914
|
-
|
|
2937
|
+
const thisValid = await processor.validateEntityKind(entity);
|
|
2938
|
+
if (thisValid) {
|
|
2939
|
+
valid = true;
|
|
2940
|
+
if (this.options.legacySingleProcessorValidation) {
|
|
2941
|
+
break;
|
|
2942
|
+
}
|
|
2915
2943
|
}
|
|
2916
2944
|
} catch (e) {
|
|
2917
2945
|
throw new errors.InputError(
|
|
@@ -2921,7 +2949,7 @@ class DefaultCatalogProcessingOrchestrator {
|
|
|
2921
2949
|
}
|
|
2922
2950
|
}
|
|
2923
2951
|
}
|
|
2924
|
-
if (!
|
|
2952
|
+
if (!valid) {
|
|
2925
2953
|
throw new errors.InputError(
|
|
2926
2954
|
`No processor recognized the entity ${context.entityRef} as valid, possibly caused by a foreign kind or apiVersion`
|
|
2927
2955
|
);
|
|
@@ -3134,22 +3162,24 @@ class Stitcher {
|
|
|
3134
3162
|
hash: "",
|
|
3135
3163
|
stitch_ticket: ticket
|
|
3136
3164
|
}).onConflict("entity_id").merge(["stitch_ticket"]);
|
|
3137
|
-
const
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3165
|
+
const [processedResult, relationsResult] = await Promise.all([
|
|
3166
|
+
this.database.with("incoming_references", function incomingReferences(builder) {
|
|
3167
|
+
return builder.from("refresh_state_references").where({ target_entity_ref: entityRef }).count({ count: "*" });
|
|
3168
|
+
}).select({
|
|
3169
|
+
entityId: "refresh_state.entity_id",
|
|
3170
|
+
processedEntity: "refresh_state.processed_entity",
|
|
3171
|
+
errors: "refresh_state.errors",
|
|
3172
|
+
incomingReferenceCount: "incoming_references.count",
|
|
3173
|
+
previousHash: "final_entities.hash"
|
|
3174
|
+
}).from("refresh_state").where({ "refresh_state.entity_ref": entityRef }).crossJoin(this.database.raw("incoming_references")).leftOuterJoin("final_entities", {
|
|
3175
|
+
"final_entities.entity_id": "refresh_state.entity_id"
|
|
3176
|
+
}),
|
|
3177
|
+
this.database.distinct({
|
|
3178
|
+
relationType: "type",
|
|
3179
|
+
relationTarget: "target_entity_ref"
|
|
3180
|
+
}).from("relations").where({ source_entity_ref: entityRef }).orderBy("relationType", "asc").orderBy("relationTarget", "asc")
|
|
3181
|
+
]);
|
|
3182
|
+
if (!processedResult.length) {
|
|
3153
3183
|
this.logger.error(
|
|
3154
3184
|
`Unable to stitch ${entityRef}, item does not exist in refresh state table`
|
|
3155
3185
|
);
|
|
@@ -3161,7 +3191,7 @@ class Stitcher {
|
|
|
3161
3191
|
errors,
|
|
3162
3192
|
incomingReferenceCount,
|
|
3163
3193
|
previousHash
|
|
3164
|
-
} =
|
|
3194
|
+
} = processedResult[0];
|
|
3165
3195
|
if (!processedEntity) {
|
|
3166
3196
|
this.logger.debug(
|
|
3167
3197
|
`Unable to stitch ${entityRef}, the entity has not yet been processed`
|
|
@@ -3189,11 +3219,7 @@ class Stitcher {
|
|
|
3189
3219
|
}));
|
|
3190
3220
|
}
|
|
3191
3221
|
}
|
|
3192
|
-
|
|
3193
|
-
result,
|
|
3194
|
-
(r) => `${r.relationType}:${r.relationTarget}`
|
|
3195
|
-
);
|
|
3196
|
-
entity.relations = uniqueRelationRows.filter((row) => row.relationType).map((row) => ({
|
|
3222
|
+
entity.relations = relationsResult.filter((row) => row.relationType).map((row) => ({
|
|
3197
3223
|
type: row.relationType,
|
|
3198
3224
|
targetRef: row.relationTarget
|
|
3199
3225
|
}));
|
|
@@ -3230,6 +3256,19 @@ class Stitcher {
|
|
|
3230
3256
|
}
|
|
3231
3257
|
}
|
|
3232
3258
|
|
|
3259
|
+
const schema = zod.z.object({
|
|
3260
|
+
entityRefs: zod.z.array(zod.z.string())
|
|
3261
|
+
});
|
|
3262
|
+
function entitiesBatchRequest(req) {
|
|
3263
|
+
try {
|
|
3264
|
+
return schema.parse(req.body);
|
|
3265
|
+
} catch (error) {
|
|
3266
|
+
throw new errors.InputError(
|
|
3267
|
+
`Malformed request body (did you remember to specify an application/json content type?), ${error.message}`
|
|
3268
|
+
);
|
|
3269
|
+
}
|
|
3270
|
+
}
|
|
3271
|
+
|
|
3233
3272
|
function basicEntityFilter(items) {
|
|
3234
3273
|
const filtersByKey = {};
|
|
3235
3274
|
for (const [key, value] of Object.entries(items)) {
|
|
@@ -3292,8 +3331,8 @@ function parseEntityFilterString(filterString) {
|
|
|
3292
3331
|
const filtersByKey = {};
|
|
3293
3332
|
for (const statement of statements) {
|
|
3294
3333
|
const equalsIndex = statement.indexOf("=");
|
|
3295
|
-
const key = equalsIndex === -1 ? statement : statement.
|
|
3296
|
-
const value = equalsIndex === -1 ? void 0 : statement.
|
|
3334
|
+
const key = equalsIndex === -1 ? statement : statement.substring(0, equalsIndex).trim();
|
|
3335
|
+
const value = equalsIndex === -1 ? void 0 : statement.substring(equalsIndex + 1).trim();
|
|
3297
3336
|
if (!key) {
|
|
3298
3337
|
throw new errors.InputError(
|
|
3299
3338
|
`Invalid filter, '${statement}' is not a valid statement (expected a string on the form a=b or a= or a)`
|
|
@@ -3369,6 +3408,17 @@ function parseEntityTransformParams(params) {
|
|
|
3369
3408
|
};
|
|
3370
3409
|
}
|
|
3371
3410
|
|
|
3411
|
+
function parseEntityFacetParams(params) {
|
|
3412
|
+
const facetStrings = parseStringsParam(params.facet, "facet");
|
|
3413
|
+
if (facetStrings) {
|
|
3414
|
+
const filtered = facetStrings.filter(Boolean);
|
|
3415
|
+
if (filtered.length) {
|
|
3416
|
+
return filtered;
|
|
3417
|
+
}
|
|
3418
|
+
}
|
|
3419
|
+
throw new errors.InputError("Missing facet parameter");
|
|
3420
|
+
}
|
|
3421
|
+
|
|
3372
3422
|
async function requireRequestBody(req) {
|
|
3373
3423
|
const contentType = req.header("content-type");
|
|
3374
3424
|
if (!contentType) {
|
|
@@ -3405,17 +3455,6 @@ function disallowReadonlyMode(readonly) {
|
|
|
3405
3455
|
}
|
|
3406
3456
|
}
|
|
3407
3457
|
|
|
3408
|
-
function parseEntityFacetParams(params) {
|
|
3409
|
-
const facetStrings = parseStringsParam(params.facet, "facet");
|
|
3410
|
-
if (facetStrings) {
|
|
3411
|
-
const filtered = facetStrings.filter(Boolean);
|
|
3412
|
-
if (filtered.length) {
|
|
3413
|
-
return filtered;
|
|
3414
|
-
}
|
|
3415
|
-
}
|
|
3416
|
-
throw new errors.InputError("Missing facet parameter");
|
|
3417
|
-
}
|
|
3418
|
-
|
|
3419
3458
|
async function createRouter(options) {
|
|
3420
3459
|
const {
|
|
3421
3460
|
entitiesCatalog,
|
|
@@ -3503,7 +3542,16 @@ async function createRouter(options) {
|
|
|
3503
3542
|
});
|
|
3504
3543
|
res.status(200).json(response);
|
|
3505
3544
|
}
|
|
3506
|
-
).
|
|
3545
|
+
).post("/entities/by-refs", async (req, res) => {
|
|
3546
|
+
const request = entitiesBatchRequest(req);
|
|
3547
|
+
const token = getBearerToken(req.header("authorization"));
|
|
3548
|
+
const response = await entitiesCatalog.entitiesBatch({
|
|
3549
|
+
entityRefs: request.entityRefs,
|
|
3550
|
+
fields: parseEntityTransformParams(req.query),
|
|
3551
|
+
authorizationToken: token
|
|
3552
|
+
});
|
|
3553
|
+
res.status(200).json(response);
|
|
3554
|
+
}).get("/entity-facets", async (req, res) => {
|
|
3507
3555
|
const response = await entitiesCatalog.facets({
|
|
3508
3556
|
filter: parseEntityFilterParams(req.query),
|
|
3509
3557
|
facets: parseEntityFacetParams(req.query),
|
|
@@ -3829,6 +3877,27 @@ class AuthorizedEntitiesCatalog {
|
|
|
3829
3877
|
}
|
|
3830
3878
|
return this.entitiesCatalog.entities(request);
|
|
3831
3879
|
}
|
|
3880
|
+
async entitiesBatch(request) {
|
|
3881
|
+
const authorizeDecision = (await this.permissionApi.authorizeConditional(
|
|
3882
|
+
[{ permission: pluginCatalogCommon.catalogEntityReadPermission }],
|
|
3883
|
+
{ token: request == null ? void 0 : request.authorizationToken }
|
|
3884
|
+
))[0];
|
|
3885
|
+
if (authorizeDecision.result === pluginPermissionCommon.AuthorizeResult.DENY) {
|
|
3886
|
+
return {
|
|
3887
|
+
items: new Array(request.entityRefs.length).fill(null)
|
|
3888
|
+
};
|
|
3889
|
+
}
|
|
3890
|
+
if (authorizeDecision.result === pluginPermissionCommon.AuthorizeResult.CONDITIONAL) {
|
|
3891
|
+
const permissionFilter = this.transformConditions(
|
|
3892
|
+
authorizeDecision.conditions
|
|
3893
|
+
);
|
|
3894
|
+
return this.entitiesCatalog.entitiesBatch({
|
|
3895
|
+
...request,
|
|
3896
|
+
filter: (request == null ? void 0 : request.filter) ? { allOf: [permissionFilter, request.filter] } : permissionFilter
|
|
3897
|
+
});
|
|
3898
|
+
}
|
|
3899
|
+
return this.entitiesCatalog.entitiesBatch(request);
|
|
3900
|
+
}
|
|
3832
3901
|
async removeEntityByUid(uid, options) {
|
|
3833
3902
|
const authorizeResponse = (await this.permissionApi.authorizeConditional(
|
|
3834
3903
|
[{ permission: pluginCatalogCommon.catalogEntityDeletePermission }],
|
|
@@ -3989,6 +4058,7 @@ class CatalogBuilder {
|
|
|
3989
4058
|
maxSeconds: 150
|
|
3990
4059
|
});
|
|
3991
4060
|
this.locationAnalyzer = void 0;
|
|
4061
|
+
this.legacySingleProcessorValidation = false;
|
|
3992
4062
|
this.env = env;
|
|
3993
4063
|
this.entityPolicies = [];
|
|
3994
4064
|
this.entityPoliciesReplace = false;
|
|
@@ -4070,11 +4140,16 @@ class CatalogBuilder {
|
|
|
4070
4140
|
}
|
|
4071
4141
|
addPermissionRules(...permissionRules) {
|
|
4072
4142
|
this.permissionRules.push(...permissionRules.flat());
|
|
4143
|
+
return this;
|
|
4073
4144
|
}
|
|
4074
4145
|
setAllowedLocationTypes(allowedLocationTypes) {
|
|
4075
4146
|
this.allowedLocationType = allowedLocationTypes;
|
|
4076
4147
|
return this;
|
|
4077
4148
|
}
|
|
4149
|
+
useLegacySingleProcessorValidation() {
|
|
4150
|
+
this.legacySingleProcessorValidation = true;
|
|
4151
|
+
return this;
|
|
4152
|
+
}
|
|
4078
4153
|
async build() {
|
|
4079
4154
|
var _a, _b;
|
|
4080
4155
|
const { config, database, logger, permissions } = this.env;
|
|
@@ -4099,7 +4174,8 @@ class CatalogBuilder {
|
|
|
4099
4174
|
rulesEnforcer,
|
|
4100
4175
|
logger,
|
|
4101
4176
|
parser,
|
|
4102
|
-
policy
|
|
4177
|
+
policy,
|
|
4178
|
+
legacySingleProcessorValidation: this.legacySingleProcessorValidation
|
|
4103
4179
|
});
|
|
4104
4180
|
const stitcher = new Stitcher(dbClient, logger);
|
|
4105
4181
|
const unauthorizedEntitiesCatalog = new DefaultEntitiesCatalog(
|
|
@@ -4373,7 +4449,8 @@ const catalogPlugin = backendPluginApi.createBackendPlugin({
|
|
|
4373
4449
|
reader: backendPluginApi.urlReaderServiceRef,
|
|
4374
4450
|
permissions: backendPluginApi.permissionsServiceRef,
|
|
4375
4451
|
database: backendPluginApi.databaseServiceRef,
|
|
4376
|
-
httpRouter: backendPluginApi.httpRouterServiceRef
|
|
4452
|
+
httpRouter: backendPluginApi.httpRouterServiceRef,
|
|
4453
|
+
lifecycle: backendPluginApi.lifecycleServiceRef
|
|
4377
4454
|
},
|
|
4378
4455
|
async init({
|
|
4379
4456
|
logger,
|
|
@@ -4381,7 +4458,8 @@ const catalogPlugin = backendPluginApi.createBackendPlugin({
|
|
|
4381
4458
|
reader,
|
|
4382
4459
|
database,
|
|
4383
4460
|
permissions,
|
|
4384
|
-
httpRouter
|
|
4461
|
+
httpRouter,
|
|
4462
|
+
lifecycle
|
|
4385
4463
|
}) {
|
|
4386
4464
|
const winstonLogger = backendPluginApi.loggerToWinstonLogger(logger);
|
|
4387
4465
|
const builder = await CatalogBuilder.create({
|
|
@@ -4395,6 +4473,11 @@ const catalogPlugin = backendPluginApi.createBackendPlugin({
|
|
|
4395
4473
|
builder.addEntityProvider(...processingExtensions.entityProviders);
|
|
4396
4474
|
const { processingEngine, router } = await builder.build();
|
|
4397
4475
|
await processingEngine.start();
|
|
4476
|
+
lifecycle.addShutdownHook({
|
|
4477
|
+
fn: async () => {
|
|
4478
|
+
await processingEngine.stop();
|
|
4479
|
+
}
|
|
4480
|
+
});
|
|
4398
4481
|
httpRouter.use(router);
|
|
4399
4482
|
}
|
|
4400
4483
|
});
|