Merge pull request #31219 from backstage/freben/oldies
🧹 catalog: make some inputs mandatory, that are always provided in the new backend system
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-backend': patch
|
||||
---
|
||||
|
||||
Internal refactor to remove remnants of the old backend system
|
||||
@@ -58,6 +58,7 @@ describe('DefaultProcessingDatabase', () => {
|
||||
minSeconds: 100,
|
||||
maxSeconds: 150,
|
||||
}),
|
||||
events: mockServices.events.mock(),
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -42,11 +42,7 @@ import { checkLocationKeyConflict } from './operations/refreshState/checkLocatio
|
||||
import { insertUnprocessedEntity } from './operations/refreshState/insertUnprocessedEntity';
|
||||
import { updateUnprocessedEntity } from './operations/refreshState/updateUnprocessedEntity';
|
||||
import { generateStableHash, generateTargetKey } from './util';
|
||||
import {
|
||||
EventBroker,
|
||||
EventParams,
|
||||
EventsService,
|
||||
} from '@backstage/plugin-events-node';
|
||||
import { EventParams, EventsService } from '@backstage/plugin-events-node';
|
||||
import { DateTime } from 'luxon';
|
||||
import { CATALOG_CONFLICTS_TOPIC } from '../constants';
|
||||
import { CatalogConflictEventPayload } from '../catalog/types';
|
||||
@@ -64,7 +60,7 @@ export class DefaultProcessingDatabase implements ProcessingDatabase {
|
||||
database: Knex;
|
||||
logger: LoggerService;
|
||||
refreshInterval: ProcessingIntervalFunction;
|
||||
eventBroker?: EventBroker | EventsService;
|
||||
events: EventsService;
|
||||
},
|
||||
) {
|
||||
initDatabaseMetrics(options.database);
|
||||
@@ -367,7 +363,7 @@ export class DefaultProcessingDatabase implements ProcessingDatabase {
|
||||
this.options.logger.warn(
|
||||
`Detected conflicting entityRef ${entityRef} already referenced by ${conflictingKey} and now also ${locationKey}`,
|
||||
);
|
||||
if (this.options.eventBroker && locationKey) {
|
||||
if (locationKey) {
|
||||
const eventParams: EventParams<CatalogConflictEventPayload> = {
|
||||
topic: CATALOG_CONFLICTS_TOPIC,
|
||||
eventPayload: {
|
||||
@@ -378,7 +374,7 @@ export class DefaultProcessingDatabase implements ProcessingDatabase {
|
||||
lastConflictAt: DateTime.now().toISO()!,
|
||||
},
|
||||
};
|
||||
await this.options.eventBroker?.publish(eventParams);
|
||||
await this.options.events.publish(eventParams);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,6 +70,8 @@ describe('DefaultCatalogProcessingEngine', () => {
|
||||
orchestrator: orchestrator,
|
||||
stitcher: stitcher,
|
||||
createHash: () => hash,
|
||||
scheduler: mockServices.scheduler(),
|
||||
events: mockServices.events.mock(),
|
||||
});
|
||||
|
||||
db.transaction.mockImplementation(cb => cb((() => {}) as any));
|
||||
@@ -136,7 +138,9 @@ describe('DefaultCatalogProcessingEngine', () => {
|
||||
knex: {} as any,
|
||||
orchestrator: orchestrator,
|
||||
stitcher: stitcher,
|
||||
scheduler: mockServices.scheduler(),
|
||||
createHash: () => hash,
|
||||
events: mockServices.events.mock(),
|
||||
});
|
||||
|
||||
db.transaction.mockImplementation(cb => cb((() => {}) as any));
|
||||
@@ -219,7 +223,9 @@ describe('DefaultCatalogProcessingEngine', () => {
|
||||
knex: {} as any,
|
||||
orchestrator: orchestrator,
|
||||
stitcher: stitcher,
|
||||
scheduler: mockServices.scheduler(),
|
||||
createHash: () => hash,
|
||||
events: mockServices.events.mock(),
|
||||
});
|
||||
|
||||
db.transaction.mockImplementation(cb => cb((() => {}) as any));
|
||||
@@ -296,7 +302,9 @@ describe('DefaultCatalogProcessingEngine', () => {
|
||||
knex: {} as any,
|
||||
orchestrator: orchestrator,
|
||||
stitcher: stitcher,
|
||||
scheduler: mockServices.scheduler(),
|
||||
createHash: () => hash,
|
||||
events: mockServices.events.mock(),
|
||||
});
|
||||
|
||||
db.transaction.mockImplementation(cb => cb((() => {}) as any));
|
||||
@@ -355,8 +363,10 @@ describe('DefaultCatalogProcessingEngine', () => {
|
||||
knex: {} as any,
|
||||
orchestrator: orchestrator,
|
||||
stitcher: stitcher,
|
||||
scheduler: mockServices.scheduler(),
|
||||
createHash: () => hash,
|
||||
pollingIntervalMs: 100,
|
||||
events: mockServices.events.mock(),
|
||||
});
|
||||
|
||||
db.transaction.mockImplementation(cb => cb((() => {}) as any));
|
||||
@@ -470,8 +480,10 @@ describe('DefaultCatalogProcessingEngine', () => {
|
||||
knex: {} as any,
|
||||
orchestrator: orchestrator,
|
||||
stitcher: stitcher,
|
||||
scheduler: mockServices.scheduler(),
|
||||
createHash: () => hash,
|
||||
pollingIntervalMs: 100,
|
||||
events: mockServices.events.mock(),
|
||||
});
|
||||
|
||||
db.transaction.mockImplementation(cb => cb((() => {}) as any));
|
||||
@@ -575,8 +587,10 @@ describe('DefaultCatalogProcessingEngine', () => {
|
||||
knex: {} as any,
|
||||
orchestrator: orchestrator,
|
||||
stitcher: stitcher,
|
||||
scheduler: mockServices.scheduler(),
|
||||
createHash: () => hash,
|
||||
pollingIntervalMs: 100,
|
||||
events: mockServices.events.mock(),
|
||||
});
|
||||
|
||||
db.transaction.mockImplementation(cb => cb((() => {}) as any));
|
||||
@@ -658,8 +672,10 @@ describe('DefaultCatalogProcessingEngine', () => {
|
||||
knex: {} as any,
|
||||
orchestrator: orchestrator,
|
||||
stitcher: stitcher,
|
||||
scheduler: mockServices.scheduler(),
|
||||
createHash: () => hash,
|
||||
pollingIntervalMs: 100,
|
||||
events: mockServices.events.mock(),
|
||||
});
|
||||
|
||||
db.transaction.mockImplementation(cb => cb((() => {}) as any));
|
||||
@@ -746,8 +762,10 @@ describe('DefaultCatalogProcessingEngine', () => {
|
||||
knex: {} as any,
|
||||
orchestrator: orchestrator,
|
||||
stitcher: stitcher,
|
||||
scheduler: mockServices.scheduler(),
|
||||
createHash: () => hash,
|
||||
pollingIntervalMs: 100,
|
||||
events: mockServices.events.mock(),
|
||||
});
|
||||
|
||||
db.transaction.mockImplementation(cb => cb((() => {}) as any));
|
||||
|
||||
@@ -36,7 +36,7 @@ import {
|
||||
withActiveSpan,
|
||||
} from '../util/opentelemetry';
|
||||
import { deleteOrphanedEntities } from '../database/operations/util/deleteOrphanedEntities';
|
||||
import { EventBroker, EventsService } from '@backstage/plugin-events-node';
|
||||
import { EventsService } from '@backstage/plugin-events-node';
|
||||
import { CATALOG_ERRORS_TOPIC } from '../constants';
|
||||
import { LoggerService, SchedulerService } from '@backstage/backend-plugin-api';
|
||||
|
||||
@@ -59,7 +59,7 @@ const stableStringifyArray = (arr: any[]) => {
|
||||
// is just one.
|
||||
export class DefaultCatalogProcessingEngine {
|
||||
private readonly config: Config;
|
||||
private readonly scheduler?: SchedulerService;
|
||||
private readonly scheduler: SchedulerService;
|
||||
private readonly logger: LoggerService;
|
||||
private readonly knex: Knex;
|
||||
private readonly processingDatabase: ProcessingDatabase;
|
||||
@@ -73,13 +73,13 @@ export class DefaultCatalogProcessingEngine {
|
||||
errors: Error[];
|
||||
}) => Promise<void> | void;
|
||||
private readonly tracker: ProgressTracker;
|
||||
private readonly eventBroker?: EventBroker | EventsService;
|
||||
private readonly events: EventsService;
|
||||
|
||||
private stopFunc?: () => void;
|
||||
|
||||
constructor(options: {
|
||||
config: Config;
|
||||
scheduler?: SchedulerService;
|
||||
scheduler: SchedulerService;
|
||||
logger: LoggerService;
|
||||
knex: Knex;
|
||||
processingDatabase: ProcessingDatabase;
|
||||
@@ -93,7 +93,7 @@ export class DefaultCatalogProcessingEngine {
|
||||
errors: Error[];
|
||||
}) => Promise<void> | void;
|
||||
tracker?: ProgressTracker;
|
||||
eventBroker?: EventBroker | EventsService;
|
||||
events: EventsService;
|
||||
}) {
|
||||
this.config = options.config;
|
||||
this.scheduler = options.scheduler;
|
||||
@@ -107,7 +107,7 @@ export class DefaultCatalogProcessingEngine {
|
||||
this.orphanCleanupIntervalMs = options.orphanCleanupIntervalMs ?? 30_000;
|
||||
this.onProcessingError = options.onProcessingError;
|
||||
this.tracker = options.tracker ?? progressTracker();
|
||||
this.eventBroker = options.eventBroker;
|
||||
this.events = options.events;
|
||||
|
||||
this.stopFunc = undefined;
|
||||
}
|
||||
@@ -201,7 +201,7 @@ export class DefaultCatalogProcessingEngine {
|
||||
const location =
|
||||
unprocessedEntity?.metadata?.annotations?.[ANNOTATION_LOCATION];
|
||||
if (result.errors.length) {
|
||||
this.eventBroker?.publish({
|
||||
this.events.publish({
|
||||
topic: CATALOG_ERRORS_TOPIC,
|
||||
eventPayload: {
|
||||
entity: entityRef,
|
||||
@@ -370,25 +370,17 @@ export class DefaultCatalogProcessingEngine {
|
||||
}
|
||||
};
|
||||
|
||||
if (this.scheduler) {
|
||||
const abortController = new AbortController();
|
||||
const abortController = new AbortController();
|
||||
this.scheduler.scheduleTask({
|
||||
id: 'catalog_orphan_cleanup',
|
||||
frequency: { milliseconds: this.orphanCleanupIntervalMs },
|
||||
timeout: { milliseconds: this.orphanCleanupIntervalMs * 0.8 },
|
||||
fn: runOnce,
|
||||
signal: abortController.signal,
|
||||
});
|
||||
|
||||
this.scheduler.scheduleTask({
|
||||
id: 'catalog_orphan_cleanup',
|
||||
frequency: { milliseconds: this.orphanCleanupIntervalMs },
|
||||
timeout: { milliseconds: this.orphanCleanupIntervalMs * 0.8 },
|
||||
fn: runOnce,
|
||||
signal: abortController.signal,
|
||||
});
|
||||
|
||||
return () => {
|
||||
abortController.abort();
|
||||
};
|
||||
}
|
||||
|
||||
const intervalKey = setInterval(runOnce, this.orphanCleanupIntervalMs);
|
||||
return () => {
|
||||
clearInterval(intervalKey);
|
||||
abortController.abort();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,7 +97,6 @@ describe('DefaultCatalogProcessingOrchestrator', () => {
|
||||
parser: defaultEntityDataParser,
|
||||
policy: EntityPolicies.allOf([]),
|
||||
rulesEnforcer: { isAllowed: () => true },
|
||||
legacySingleProcessorValidation: false,
|
||||
});
|
||||
|
||||
it('runs a minimal processing', async () => {
|
||||
@@ -192,7 +191,7 @@ describe('DefaultCatalogProcessingOrchestrator', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('runs all processor validations when asked to', async () => {
|
||||
it('runs all processor validations', async () => {
|
||||
const validate = jest.fn(async () => true);
|
||||
const processor1: CatalogProcessor = {
|
||||
getProcessorName: () => 'processor1',
|
||||
@@ -213,7 +212,6 @@ describe('DefaultCatalogProcessingOrchestrator', () => {
|
||||
parser: defaultEntityDataParser,
|
||||
policy: EntityPolicies.allOf([]),
|
||||
rulesEnforcer: { isAllowed: () => true },
|
||||
legacySingleProcessorValidation: true,
|
||||
});
|
||||
|
||||
const modern = new DefaultCatalogProcessingOrchestrator({
|
||||
@@ -226,13 +224,12 @@ describe('DefaultCatalogProcessingOrchestrator', () => {
|
||||
parser: defaultEntityDataParser,
|
||||
policy: EntityPolicies.allOf([]),
|
||||
rulesEnforcer: { isAllowed: () => true },
|
||||
legacySingleProcessorValidation: false,
|
||||
});
|
||||
|
||||
await expect(legacy.process({ entity })).resolves.toMatchObject({
|
||||
ok: true,
|
||||
});
|
||||
expect(validate).toHaveBeenCalledTimes(1);
|
||||
expect(validate).toHaveBeenCalledTimes(2);
|
||||
|
||||
validate.mockClear();
|
||||
|
||||
@@ -291,7 +288,6 @@ describe('DefaultCatalogProcessingOrchestrator', () => {
|
||||
parser,
|
||||
policy: EntityPolicies.allOf([]),
|
||||
rulesEnforcer,
|
||||
legacySingleProcessorValidation: false,
|
||||
});
|
||||
|
||||
rulesEnforcer.isAllowed.mockReturnValueOnce(true);
|
||||
@@ -333,7 +329,6 @@ describe('DefaultCatalogProcessingOrchestrator', () => {
|
||||
parser,
|
||||
policy: EntityPolicies.allOf([new FailingEntityPolicy()]),
|
||||
rulesEnforcer,
|
||||
legacySingleProcessorValidation: false,
|
||||
});
|
||||
|
||||
await expect(
|
||||
|
||||
@@ -95,7 +95,6 @@ export class DefaultCatalogProcessingOrchestrator
|
||||
parser: CatalogProcessorParser;
|
||||
policy: EntityPolicy;
|
||||
rulesEnforcer: CatalogRulesEnforcer;
|
||||
legacySingleProcessorValidation: boolean;
|
||||
},
|
||||
) {}
|
||||
|
||||
@@ -309,9 +308,6 @@ export class DefaultCatalogProcessingOrchestrator
|
||||
);
|
||||
if (thisValid) {
|
||||
valid = true;
|
||||
if (this.options.legacySingleProcessorValidation) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
throw new InputError(
|
||||
|
||||
@@ -55,7 +55,7 @@ import {
|
||||
PlaceholderResolver,
|
||||
ScmLocationAnalyzer,
|
||||
} from '@backstage/plugin-catalog-node';
|
||||
import { EventBroker, EventsService } from '@backstage/plugin-events-node';
|
||||
import { EventsService } from '@backstage/plugin-events-node';
|
||||
import {
|
||||
Permission,
|
||||
PermissionAuthorizer,
|
||||
@@ -119,10 +119,11 @@ export type CatalogEnvironment = {
|
||||
reader: UrlReaderService;
|
||||
permissions: PermissionsService | PermissionAuthorizer;
|
||||
permissionsRegistry?: PermissionsRegistryService;
|
||||
scheduler?: SchedulerService;
|
||||
scheduler: SchedulerService;
|
||||
auth: AuthService;
|
||||
httpAuth: HttpAuthService;
|
||||
auditor?: AuditorService;
|
||||
auditor: AuditorService;
|
||||
events: EventsService;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -168,8 +169,6 @@ export class CatalogBuilder {
|
||||
private readonly permissions: Permission[];
|
||||
private readonly permissionRules: CatalogPermissionRuleInput[];
|
||||
private allowedLocationType: string[];
|
||||
private legacySingleProcessorValidation = false;
|
||||
private eventBroker?: EventBroker | EventsService;
|
||||
|
||||
/**
|
||||
* Creates a catalog builder.
|
||||
@@ -216,31 +215,6 @@ export class CatalogBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processing interval determines how often entities should be processed.
|
||||
* Seconds provided will be multiplied by 1.5
|
||||
* The default processing interval is 100-150 seconds.
|
||||
* setting this too low will potentially deplete request quotas to upstream services.
|
||||
*/
|
||||
setProcessingIntervalSeconds(seconds: number): CatalogBuilder {
|
||||
this.processingInterval = createRandomProcessingInterval({
|
||||
minSeconds: seconds,
|
||||
maxSeconds: seconds * 1.5,
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overwrites the default processing interval function used to spread
|
||||
* entity updates in the catalog.
|
||||
*/
|
||||
setProcessingInterval(
|
||||
processingInterval: ProcessingIntervalFunction,
|
||||
): CatalogBuilder {
|
||||
this.processingInterval = processingInterval;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overwrites the default location analyzer.
|
||||
*/
|
||||
@@ -427,23 +401,6 @@ export class CatalogBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables the legacy behaviour of canceling validation early whenever only a
|
||||
* single processor declares an entity kind to be valid.
|
||||
*/
|
||||
useLegacySingleProcessorValidation(): this {
|
||||
this.legacySingleProcessorValidation = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables the publishing of events for conflicts in the DefaultProcessingDatabase
|
||||
*/
|
||||
setEventBroker(broker: EventBroker | EventsService): CatalogBuilder {
|
||||
this.eventBroker = broker;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wires up and returns all of the component parts of the catalog
|
||||
*/
|
||||
@@ -461,6 +418,7 @@ export class CatalogBuilder {
|
||||
auditor,
|
||||
auth,
|
||||
httpAuth,
|
||||
events,
|
||||
} = this.env;
|
||||
|
||||
const enableRelationsCompatibility = Boolean(
|
||||
@@ -485,8 +443,8 @@ export class CatalogBuilder {
|
||||
const processingDatabase = new DefaultProcessingDatabase({
|
||||
database: dbClient,
|
||||
logger,
|
||||
events,
|
||||
refreshInterval: this.processingInterval,
|
||||
eventBroker: this.eventBroker,
|
||||
});
|
||||
const providerDatabase = new DefaultProviderDatabase({
|
||||
database: dbClient,
|
||||
@@ -523,7 +481,6 @@ export class CatalogBuilder {
|
||||
logger,
|
||||
parser,
|
||||
policy,
|
||||
legacySingleProcessorValidation: this.legacySingleProcessorValidation,
|
||||
});
|
||||
|
||||
const entitiesCatalog = new AuthorizedEntitiesCatalog(
|
||||
@@ -588,7 +545,7 @@ export class CatalogBuilder {
|
||||
onProcessingError: event => {
|
||||
this.onProcessingError?.(event);
|
||||
},
|
||||
eventBroker: this.eventBroker,
|
||||
events,
|
||||
});
|
||||
|
||||
const locationAnalyzer =
|
||||
|
||||
@@ -271,10 +271,9 @@ export const catalogPlugin = createBackendPlugin({
|
||||
auth,
|
||||
httpAuth,
|
||||
auditor,
|
||||
events,
|
||||
});
|
||||
|
||||
builder.setEventBroker(events);
|
||||
|
||||
if (processingExtensions.onProcessingErrorHandler) {
|
||||
builder.subscribe({
|
||||
onProcessingError: processingExtensions.onProcessingErrorHandler,
|
||||
|
||||
@@ -57,6 +57,7 @@ describe('DefaultRefreshService', () => {
|
||||
database: knex,
|
||||
logger,
|
||||
refreshInterval: () => 100,
|
||||
events: mockServices.events.mock(),
|
||||
}),
|
||||
catalogDb: new DefaultCatalogDatabase({
|
||||
database: knex,
|
||||
@@ -121,6 +122,7 @@ describe('DefaultRefreshService', () => {
|
||||
processingDatabase: db,
|
||||
knex: knex,
|
||||
stitcher: stitcher,
|
||||
scheduler: mockServices.scheduler(),
|
||||
orchestrator: {
|
||||
async process(request: EntityProcessingRequest) {
|
||||
const entityRef = stringifyEntityRef(request.entity);
|
||||
@@ -161,6 +163,7 @@ describe('DefaultRefreshService', () => {
|
||||
},
|
||||
createHash: () => createHash('sha1'),
|
||||
pollingIntervalMs: 50,
|
||||
events: mockServices.events.mock(),
|
||||
});
|
||||
|
||||
return engine;
|
||||
|
||||
@@ -156,12 +156,12 @@ describe('createRouter readonly disabled', () => {
|
||||
logger: mockServices.logger.mock(),
|
||||
refreshService,
|
||||
config: new ConfigReader(undefined),
|
||||
permissionIntegrationRouter: express.Router(),
|
||||
auth: mockServices.auth(),
|
||||
httpAuth: mockServices.httpAuth(),
|
||||
locationAnalyzer,
|
||||
permissionsService,
|
||||
enableRelationsCompatibility: true, // added
|
||||
auditor: mockServices.auditor.mock(),
|
||||
});
|
||||
|
||||
app = await wrapServer(express().use(router));
|
||||
@@ -218,12 +218,12 @@ describe('createRouter readonly disabled', () => {
|
||||
logger: mockServices.logger.mock(),
|
||||
refreshService,
|
||||
config: new ConfigReader(undefined),
|
||||
permissionIntegrationRouter: express.Router(),
|
||||
auth: mockServices.auth(),
|
||||
httpAuth: mockServices.httpAuth(),
|
||||
locationAnalyzer,
|
||||
permissionsService,
|
||||
enableRelationsCompatibility: true,
|
||||
auditor: mockServices.auditor.mock(),
|
||||
});
|
||||
app = await wrapServer(express().use(router));
|
||||
entitiesCatalog.entities.mockResolvedValueOnce({
|
||||
@@ -951,6 +951,7 @@ describe('createRouter readonly and raw json enabled', () => {
|
||||
permissionIntegrationRouter: express.Router(),
|
||||
auth: mockServices.auth(),
|
||||
httpAuth: mockServices.httpAuth(),
|
||||
orchestrator: { process: jest.fn() },
|
||||
permissionsService,
|
||||
auditor: mockServices.auditor.mock(),
|
||||
});
|
||||
@@ -1171,6 +1172,7 @@ describe('NextRouter permissioning', () => {
|
||||
}),
|
||||
auth: mockServices.auth(),
|
||||
httpAuth: mockServices.httpAuth(),
|
||||
orchestrator: { process: jest.fn() },
|
||||
permissionsService,
|
||||
auditor: mockServices.auditor.mock(),
|
||||
});
|
||||
|
||||
@@ -20,7 +20,6 @@ import {
|
||||
HttpAuthService,
|
||||
LoggerService,
|
||||
PermissionsService,
|
||||
SchedulerService,
|
||||
} from '@backstage/backend-plugin-api';
|
||||
import {
|
||||
ANNOTATION_LOCATION,
|
||||
@@ -70,17 +69,15 @@ export interface RouterOptions {
|
||||
entitiesCatalog?: EntitiesCatalog;
|
||||
locationAnalyzer?: LocationAnalyzer;
|
||||
locationService: LocationService;
|
||||
orchestrator?: CatalogProcessingOrchestrator;
|
||||
orchestrator: CatalogProcessingOrchestrator;
|
||||
refreshService?: RefreshService;
|
||||
scheduler?: SchedulerService;
|
||||
logger: LoggerService;
|
||||
config: Config;
|
||||
permissionIntegrationRouter?: express.Router;
|
||||
auth: AuthService;
|
||||
httpAuth: HttpAuthService;
|
||||
permissionsService: PermissionsService;
|
||||
// TODO: Require AuditorService once `backend-legacy` is removed
|
||||
auditor?: AuditorService;
|
||||
auditor: AuditorService;
|
||||
enableRelationsCompatibility?: boolean;
|
||||
}
|
||||
|
||||
@@ -124,7 +121,7 @@ export async function createRouter(
|
||||
router.post('/refresh', async (req, res) => {
|
||||
const { authorizationToken, ...restBody } = req.body;
|
||||
|
||||
const auditorEvent = await auditor?.createEvent({
|
||||
const auditorEvent = await auditor.createEvent({
|
||||
eventId: 'entity-mutate',
|
||||
severityLevel: 'medium',
|
||||
meta: {
|
||||
@@ -160,7 +157,7 @@ export async function createRouter(
|
||||
if (entitiesCatalog) {
|
||||
router
|
||||
.get('/entities', async (req, res) => {
|
||||
const auditorEvent = await auditor?.createEvent({
|
||||
const auditorEvent = await auditor.createEvent({
|
||||
eventId: 'entity-fetch',
|
||||
request: req,
|
||||
meta: {
|
||||
@@ -258,7 +255,7 @@ export async function createRouter(
|
||||
}
|
||||
})
|
||||
.get('/entities/by-query', async (req, res) => {
|
||||
const auditorEvent = await auditor?.createEvent({
|
||||
const auditorEvent = await auditor.createEvent({
|
||||
eventId: 'entity-fetch',
|
||||
request: req,
|
||||
meta: {
|
||||
@@ -311,7 +308,7 @@ export async function createRouter(
|
||||
.get('/entities/by-uid/:uid', async (req, res) => {
|
||||
const { uid } = req.params;
|
||||
|
||||
const auditorEvent = await auditor?.createEvent({
|
||||
const auditorEvent = await auditor.createEvent({
|
||||
eventId: 'entity-fetch',
|
||||
request: req,
|
||||
meta: {
|
||||
@@ -356,7 +353,7 @@ export async function createRouter(
|
||||
.delete('/entities/by-uid/:uid', async (req, res) => {
|
||||
const { uid } = req.params;
|
||||
|
||||
const auditorEvent = await auditor?.createEvent({
|
||||
const auditorEvent = await auditor.createEvent({
|
||||
eventId: 'entity-mutate',
|
||||
severityLevel: 'medium',
|
||||
request: req,
|
||||
@@ -385,7 +382,7 @@ export async function createRouter(
|
||||
const { kind, namespace, name } = req.params;
|
||||
const entityRef = stringifyEntityRef({ kind, namespace, name });
|
||||
|
||||
const auditorEvent = await auditor?.createEvent({
|
||||
const auditorEvent = await auditor.createEvent({
|
||||
eventId: 'entity-fetch',
|
||||
request: req,
|
||||
meta: {
|
||||
@@ -420,7 +417,7 @@ export async function createRouter(
|
||||
const { kind, namespace, name } = req.params;
|
||||
const entityRef = stringifyEntityRef({ kind, namespace, name });
|
||||
|
||||
const auditorEvent = await auditor?.createEvent({
|
||||
const auditorEvent = await auditor.createEvent({
|
||||
eventId: 'entity-fetch',
|
||||
request: req,
|
||||
meta: {
|
||||
@@ -456,7 +453,7 @@ export async function createRouter(
|
||||
},
|
||||
)
|
||||
.post('/entities/by-refs', async (req, res) => {
|
||||
const auditorEvent = await auditor?.createEvent({
|
||||
const auditorEvent = await auditor.createEvent({
|
||||
eventId: 'entity-fetch',
|
||||
request: req,
|
||||
meta: {
|
||||
@@ -495,7 +492,7 @@ export async function createRouter(
|
||||
}
|
||||
})
|
||||
.get('/entity-facets', async (req, res) => {
|
||||
const auditorEvent = await auditor?.createEvent({
|
||||
const auditorEvent = await auditor.createEvent({
|
||||
eventId: 'entity-facets',
|
||||
request: req,
|
||||
});
|
||||
@@ -525,7 +522,7 @@ export async function createRouter(
|
||||
const location = await validateRequestBody(req, locationInput);
|
||||
const dryRun = yn(req.query.dryRun, { default: false });
|
||||
|
||||
const auditorEvent = await auditor?.createEvent({
|
||||
const auditorEvent = await auditor.createEvent({
|
||||
eventId: 'location-mutate',
|
||||
severityLevel: dryRun ? 'low' : 'medium',
|
||||
request: req,
|
||||
@@ -570,7 +567,7 @@ export async function createRouter(
|
||||
}
|
||||
})
|
||||
.get('/locations', async (req, res) => {
|
||||
const auditorEvent = await auditor?.createEvent({
|
||||
const auditorEvent = await auditor.createEvent({
|
||||
eventId: 'location-fetch',
|
||||
request: req,
|
||||
meta: {
|
||||
@@ -597,7 +594,7 @@ export async function createRouter(
|
||||
.get('/locations/:id', async (req, res) => {
|
||||
const { id } = req.params;
|
||||
|
||||
const auditorEvent = await auditor?.createEvent({
|
||||
const auditorEvent = await auditor.createEvent({
|
||||
eventId: 'location-fetch',
|
||||
request: req,
|
||||
meta: {
|
||||
@@ -628,7 +625,7 @@ export async function createRouter(
|
||||
.delete('/locations/:id', async (req, res) => {
|
||||
const { id } = req.params;
|
||||
|
||||
const auditorEvent = await auditor?.createEvent({
|
||||
const auditorEvent = await auditor.createEvent({
|
||||
eventId: 'location-mutate',
|
||||
severityLevel: 'medium',
|
||||
request: req,
|
||||
@@ -659,7 +656,7 @@ export async function createRouter(
|
||||
const { kind, namespace, name } = req.params;
|
||||
const locationRef = `${kind}:${namespace}/${name}`;
|
||||
|
||||
const auditorEvent = await auditor?.createEvent({
|
||||
const auditorEvent = await auditor.createEvent({
|
||||
eventId: 'location-fetch',
|
||||
request: req,
|
||||
meta: {
|
||||
@@ -692,7 +689,7 @@ export async function createRouter(
|
||||
|
||||
if (locationAnalyzer) {
|
||||
router.post('/analyze-location', async (req, res) => {
|
||||
const auditorEvent = await auditor?.createEvent({
|
||||
const auditorEvent = await auditor.createEvent({
|
||||
eventId: 'location-analyze',
|
||||
request: req,
|
||||
});
|
||||
@@ -743,86 +740,84 @@ export async function createRouter(
|
||||
});
|
||||
}
|
||||
|
||||
if (orchestrator) {
|
||||
router.post('/validate-entity', async (req, res) => {
|
||||
const auditorEvent = await auditor?.createEvent({
|
||||
eventId: 'entity-validate',
|
||||
request: req,
|
||||
router.post('/validate-entity', async (req, res) => {
|
||||
const auditorEvent = await auditor.createEvent({
|
||||
eventId: 'entity-validate',
|
||||
request: req,
|
||||
});
|
||||
|
||||
try {
|
||||
const bodySchema = z.object({
|
||||
entity: z.unknown(),
|
||||
location: z.string(),
|
||||
});
|
||||
|
||||
let body: z.infer<typeof bodySchema>;
|
||||
let entity: Entity;
|
||||
let location: { type: string; target: string };
|
||||
try {
|
||||
const bodySchema = z.object({
|
||||
entity: z.unknown(),
|
||||
location: z.string(),
|
||||
});
|
||||
|
||||
let body: z.infer<typeof bodySchema>;
|
||||
let entity: Entity;
|
||||
let location: { type: string; target: string };
|
||||
try {
|
||||
body = await validateRequestBody(req, bodySchema);
|
||||
entity = validateEntityEnvelope(body.entity);
|
||||
location = parseLocationRef(body.location);
|
||||
if (location.type !== 'url')
|
||||
throw new TypeError(
|
||||
`Invalid location ref ${body.location}, only 'url:<target>' is supported, e.g. url:https://host/path`,
|
||||
);
|
||||
} catch (err) {
|
||||
await auditorEvent?.fail({
|
||||
error: err,
|
||||
});
|
||||
|
||||
return res.status(400).json({
|
||||
errors: [serializeError(err)],
|
||||
});
|
||||
}
|
||||
|
||||
const credentials = await httpAuth.credentials(req);
|
||||
const authorizedValidationService = new AuthorizedValidationService(
|
||||
orchestrator,
|
||||
permissionsService,
|
||||
);
|
||||
const processingResult = await authorizedValidationService.process(
|
||||
{
|
||||
entity: {
|
||||
...entity,
|
||||
metadata: {
|
||||
...entity.metadata,
|
||||
annotations: {
|
||||
[ANNOTATION_LOCATION]: body.location,
|
||||
[ANNOTATION_ORIGIN_LOCATION]: body.location,
|
||||
...entity.metadata.annotations,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
credentials,
|
||||
);
|
||||
|
||||
if (!processingResult.ok) {
|
||||
const errors = processingResult.errors.map(e => serializeError(e));
|
||||
|
||||
await auditorEvent?.fail({
|
||||
// TODO(Rugvip): Seems like there aren't proper types for AggregateError yet
|
||||
error: (AggregateError as any)(errors, 'Could not validate entity'),
|
||||
});
|
||||
|
||||
res.status(400).json({
|
||||
errors,
|
||||
});
|
||||
}
|
||||
|
||||
await auditorEvent?.success();
|
||||
|
||||
return res.status(200).end();
|
||||
body = await validateRequestBody(req, bodySchema);
|
||||
entity = validateEntityEnvelope(body.entity);
|
||||
location = parseLocationRef(body.location);
|
||||
if (location.type !== 'url')
|
||||
throw new TypeError(
|
||||
`Invalid location ref ${body.location}, only 'url:<target>' is supported, e.g. url:https://host/path`,
|
||||
);
|
||||
} catch (err) {
|
||||
await auditorEvent?.fail({
|
||||
error: err,
|
||||
});
|
||||
throw err;
|
||||
|
||||
return res.status(400).json({
|
||||
errors: [serializeError(err)],
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const credentials = await httpAuth.credentials(req);
|
||||
const authorizedValidationService = new AuthorizedValidationService(
|
||||
orchestrator,
|
||||
permissionsService,
|
||||
);
|
||||
const processingResult = await authorizedValidationService.process(
|
||||
{
|
||||
entity: {
|
||||
...entity,
|
||||
metadata: {
|
||||
...entity.metadata,
|
||||
annotations: {
|
||||
[ANNOTATION_LOCATION]: body.location,
|
||||
[ANNOTATION_ORIGIN_LOCATION]: body.location,
|
||||
...entity.metadata.annotations,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
credentials,
|
||||
);
|
||||
|
||||
if (!processingResult.ok) {
|
||||
const errors = processingResult.errors.map(e => serializeError(e));
|
||||
|
||||
await auditorEvent?.fail({
|
||||
// TODO(Rugvip): Seems like there aren't proper types for AggregateError yet
|
||||
error: (AggregateError as any)(errors, 'Could not validate entity'),
|
||||
});
|
||||
|
||||
res.status(400).json({
|
||||
errors,
|
||||
});
|
||||
}
|
||||
|
||||
await auditorEvent?.success();
|
||||
|
||||
return res.status(200).end();
|
||||
} catch (err) {
|
||||
await auditorEvent?.fail({
|
||||
error: err,
|
||||
});
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
|
||||
return router;
|
||||
}
|
||||
|
||||
@@ -244,6 +244,7 @@ class TestHarness {
|
||||
const processingDatabase = new DefaultProcessingDatabase({
|
||||
database: options.db,
|
||||
logger,
|
||||
events: mockServices.events.mock(),
|
||||
refreshInterval: () => 0.05,
|
||||
});
|
||||
|
||||
@@ -273,7 +274,6 @@ class TestHarness {
|
||||
logger,
|
||||
parser: defaultEntityDataParser,
|
||||
policy: EntityPolicies.allOf([]),
|
||||
legacySingleProcessorValidation: false,
|
||||
});
|
||||
const stitcher = DefaultStitcher.fromConfig(config, {
|
||||
knex: options.db,
|
||||
@@ -296,12 +296,14 @@ class TestHarness {
|
||||
knex: options.db,
|
||||
orchestrator,
|
||||
stitcher,
|
||||
scheduler: mockServices.scheduler(),
|
||||
createHash: () => createHash('sha1'),
|
||||
pollingIntervalMs: 50,
|
||||
onProcessingError: event => {
|
||||
proxyProgressTracker.reportError(event.unprocessedEntity, event.errors);
|
||||
},
|
||||
tracker: proxyProgressTracker,
|
||||
events: mockServices.events.mock(),
|
||||
});
|
||||
|
||||
const refresh = new DefaultRefreshService({ database: catalogDatabase });
|
||||
|
||||
+1
@@ -79,6 +79,7 @@ describePerformanceTest('getProcessableEntities', () => {
|
||||
const sut = new DefaultProcessingDatabase({
|
||||
database: knex,
|
||||
logger: mockServices.logger.mock(),
|
||||
events: mockServices.events.mock(),
|
||||
refreshInterval: () => 0,
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user