@@ -24,6 +24,7 @@ import { DateTime } from 'luxon';
|
||||
import { applyDatabaseMigrations } from './migrations';
|
||||
import { DefaultProcessingDatabase } from './DefaultProcessingDatabase';
|
||||
import {
|
||||
DbRefreshKeysRow,
|
||||
DbRefreshStateReferencesRow,
|
||||
DbRefreshStateRow,
|
||||
DbRelationsRow,
|
||||
@@ -67,6 +68,10 @@ describe('Default Processing Database', () => {
|
||||
await db<DbRefreshStateRow>('refresh_state').insert(ref);
|
||||
};
|
||||
|
||||
const insertRefreshKeysRow = async (db: Knex, ref) => {
|
||||
await db('refresh_keys').insert(ref);
|
||||
};
|
||||
|
||||
describe('updateProcessedEntity', () => {
|
||||
let id: string;
|
||||
let processedEntity: Entity;
|
||||
@@ -1397,4 +1402,55 @@ describe('Default Processing Database', () => {
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
describe('setRefreshKeys', () => {
|
||||
it.each(databases.eachSupportedId())(
|
||||
'should set keys, %p',
|
||||
async databaseId => {
|
||||
const { knex, db } = await createDatabase(databaseId);
|
||||
|
||||
await db.transaction(async tx =>
|
||||
db.setRefreshKeys(tx, {
|
||||
refreshKeys: [{ entityRef: 'location:default/root-1', key: 'foo' }],
|
||||
}),
|
||||
);
|
||||
|
||||
const rows = await knex<DbRefreshKeysRow>('refresh_keys').select();
|
||||
|
||||
expect(rows.length).toBe(1);
|
||||
expect(rows[0]).toEqual({
|
||||
entity_ref: 'location:default/root-1',
|
||||
key: 'foo',
|
||||
});
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
describe('deleteRefreshKeys', () => {
|
||||
it.each(databases.eachSupportedId())(
|
||||
'should delete keys, %p',
|
||||
async databaseId => {
|
||||
const { knex, db } = await createDatabase(databaseId);
|
||||
|
||||
await knex<DbRefreshKeysRow>('refresh_keys').insert({
|
||||
entity_ref: 'location:default/root-1',
|
||||
key: 'foo',
|
||||
});
|
||||
|
||||
let rows = await knex<DbRefreshKeysRow>('refresh_keys').select();
|
||||
|
||||
expect(rows.length).toBe(1);
|
||||
|
||||
await db.transaction(async tx =>
|
||||
db.deleteRefreshKey(tx, {
|
||||
key: 'foo',
|
||||
}),
|
||||
);
|
||||
|
||||
rows = await knex<DbRefreshKeysRow>('refresh_keys').select();
|
||||
|
||||
expect(rows.length).toBe(0);
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -562,7 +562,7 @@ export class DefaultProcessingDatabase implements ProcessingDatabase {
|
||||
const tx = txOpaque as Knex.Transaction;
|
||||
const { key } = options;
|
||||
|
||||
await tx<DbRefreshKeysRow>('refresh_keys').where({ key }).delete();
|
||||
await tx<DbRefreshKeysRow>('refresh_keys').where({ key: key }).delete();
|
||||
}
|
||||
|
||||
async transaction<T>(fn: (tx: Transaction) => Promise<T>): Promise<T> {
|
||||
|
||||
@@ -18,7 +18,7 @@ import { Entity } from '@backstage/catalog-model';
|
||||
import { JsonObject } from '@backstage/types';
|
||||
import { DateTime } from 'luxon';
|
||||
import { EntityRelationSpec } from '../api';
|
||||
import { DeferredEntity } from '../processing/types';
|
||||
import { DeferredEntity, RefreshKeyData } from '../processing/types';
|
||||
import { DbRelationsRow } from './tables';
|
||||
|
||||
/**
|
||||
@@ -82,7 +82,7 @@ export type ReplaceUnprocessedEntitiesOptions =
|
||||
};
|
||||
|
||||
export type RefreshKeyOptions = {
|
||||
refreshKeys: { key: String; entityRef: String }[];
|
||||
refreshKeys: RefreshKeyData[];
|
||||
};
|
||||
|
||||
export type RefreshByKeyOptions = {
|
||||
@@ -157,6 +157,17 @@ export interface ProcessingDatabase {
|
||||
*/
|
||||
refresh(txOpaque: Transaction, options: RefreshOptions): Promise<void>;
|
||||
|
||||
/**
|
||||
* Schedules a refresh for all the entities that have the given refreshKey
|
||||
*/
|
||||
setRefreshKeys(
|
||||
txOpaque: Transaction,
|
||||
options: RefreshKeyOptions,
|
||||
): Promise<void>;
|
||||
|
||||
/**
|
||||
* Schedules a refresh for all the entities that have the given refreshKey
|
||||
*/
|
||||
setRefreshKeys(
|
||||
txOpaque: Transaction,
|
||||
options: RefreshKeyOptions,
|
||||
|
||||
@@ -43,7 +43,10 @@ describe('FileReaderProcessor', () => {
|
||||
|
||||
expect(generated.type).toBe('entity');
|
||||
expect(generated.location).toEqual(spec);
|
||||
expect(generated.entity).toEqual({ kind: 'Component' });
|
||||
expect(generated.entity).toEqual({
|
||||
kind: 'Component',
|
||||
metadata: { name: 'component-test' },
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail load from file with error', async () => {
|
||||
@@ -77,14 +80,23 @@ describe('FileReaderProcessor', () => {
|
||||
defaultEntityDataParser,
|
||||
);
|
||||
|
||||
expect(emit).toBeCalledTimes(2);
|
||||
expect(emit.mock.calls[0][0].entity).toEqual({ kind: 'Component' });
|
||||
expect(emit).toBeCalledTimes(4);
|
||||
expect(emit.mock.calls[0][0].entity).toEqual({
|
||||
kind: 'Component',
|
||||
metadata: { name: 'component-test' },
|
||||
});
|
||||
expect(emit.mock.calls[0][0].location).toEqual({
|
||||
type: 'file',
|
||||
target: expect.stringMatching(/^[^*]*$/),
|
||||
});
|
||||
expect(emit.mock.calls[1][0].entity).toEqual({ kind: 'API' });
|
||||
expect(emit.mock.calls[1][0].location).toEqual({
|
||||
expect(emit.mock.calls[1][0].entityRef).toEqual(
|
||||
'component:default/component-test',
|
||||
);
|
||||
expect(emit.mock.calls[2][0].entity).toEqual({
|
||||
kind: 'API',
|
||||
metadata: { name: 'api-test' },
|
||||
});
|
||||
expect(emit.mock.calls[2][0].location).toEqual({
|
||||
type: 'file',
|
||||
target: expect.stringMatching(/^[^*]*$/),
|
||||
});
|
||||
|
||||
@@ -51,7 +51,7 @@ describe('PlaceholderProcessor', () => {
|
||||
integrations,
|
||||
});
|
||||
await expect(
|
||||
processor.preProcessEntity(input, { type: 't', target: 'l' }),
|
||||
processor.preProcessEntity(input, { type: 't', target: 'l' }, () => {}),
|
||||
).resolves.toBe(input);
|
||||
});
|
||||
|
||||
@@ -76,6 +76,7 @@ describe('PlaceholderProcessor', () => {
|
||||
spec: { a: [{ b: { $upper: 'text' } }] },
|
||||
},
|
||||
{ type: 'fake', target: 'http://example.com' },
|
||||
() => {},
|
||||
),
|
||||
).resolves.toEqual({
|
||||
apiVersion: 'a',
|
||||
@@ -110,7 +111,7 @@ describe('PlaceholderProcessor', () => {
|
||||
};
|
||||
|
||||
await expect(
|
||||
processor.preProcessEntity(entity, { type: 'a', target: 'b' }),
|
||||
processor.preProcessEntity(entity, { type: 'a', target: 'b' }, () => {}),
|
||||
).resolves.toEqual(entity);
|
||||
|
||||
expect(read).not.toBeCalled();
|
||||
@@ -131,7 +132,7 @@ describe('PlaceholderProcessor', () => {
|
||||
};
|
||||
|
||||
await expect(
|
||||
processor.preProcessEntity(entity, { type: 'a', target: 'b' }),
|
||||
processor.preProcessEntity(entity, { type: 'a', target: 'b' }, () => {}),
|
||||
).resolves.toEqual(entity);
|
||||
|
||||
expect(read).not.toBeCalled();
|
||||
@@ -158,6 +159,7 @@ describe('PlaceholderProcessor', () => {
|
||||
target:
|
||||
'https://github.com/backstage/backstage/a/b/catalog-info.yaml',
|
||||
},
|
||||
() => {},
|
||||
),
|
||||
).resolves.toEqual({
|
||||
apiVersion: 'a',
|
||||
@@ -194,6 +196,7 @@ describe('PlaceholderProcessor', () => {
|
||||
target:
|
||||
'https://github.com/backstage/backstage/a/b/catalog-info.yaml',
|
||||
},
|
||||
() => {},
|
||||
),
|
||||
).resolves.toEqual({
|
||||
apiVersion: 'a',
|
||||
@@ -228,6 +231,7 @@ describe('PlaceholderProcessor', () => {
|
||||
target:
|
||||
'https://github.com/backstage/backstage/a/b/catalog-info.yaml',
|
||||
},
|
||||
() => {},
|
||||
),
|
||||
).resolves.toEqual({
|
||||
apiVersion: 'a',
|
||||
@@ -266,6 +270,7 @@ describe('PlaceholderProcessor', () => {
|
||||
target:
|
||||
'https://github.com/backstage/backstage/a/b/catalog-info.yaml',
|
||||
},
|
||||
() => {},
|
||||
),
|
||||
).resolves.toEqual({
|
||||
apiVersion: 'a',
|
||||
@@ -303,6 +308,7 @@ describe('PlaceholderProcessor', () => {
|
||||
type: 'url',
|
||||
target: './a/b/catalog-info.yaml',
|
||||
},
|
||||
() => {},
|
||||
),
|
||||
).resolves.toEqual({
|
||||
apiVersion: 'a',
|
||||
@@ -343,6 +349,7 @@ describe('PlaceholderProcessor', () => {
|
||||
type: 'url',
|
||||
target: './a/b/catalog-info.yaml',
|
||||
},
|
||||
() => {},
|
||||
),
|
||||
).rejects.toThrow(
|
||||
/^Placeholder \$text could not form a URL out of \.\/a\/b\/catalog-info\.yaml and \.\.\/c\/catalog-info\.yaml, TypeError \[ERR_INVALID_URL\]/,
|
||||
|
||||
@@ -74,7 +74,7 @@ describe('UrlReaderProcessor', () => {
|
||||
mockCache,
|
||||
);
|
||||
|
||||
expect(emitted.length).toBe(1);
|
||||
expect(emitted.length).toBe(2);
|
||||
expect(emitted[0]).toEqual({
|
||||
type: 'entity',
|
||||
location: spec,
|
||||
|
||||
@@ -30,6 +30,7 @@ describe('DefaultCatalogProcessingEngine', () => {
|
||||
updateProcessedEntity: jest.fn(),
|
||||
updateEntityCache: jest.fn(),
|
||||
listParents: jest.fn(),
|
||||
setRefreshKeys: jest.fn(),
|
||||
} as unknown as jest.Mocked<DefaultProcessingDatabase>;
|
||||
const orchestrator: jest.Mocked<CatalogProcessingOrchestrator> = {
|
||||
process: jest.fn(),
|
||||
|
||||
@@ -102,6 +102,7 @@ describe('DefaultCatalogProcessingOrchestrator', () => {
|
||||
ok: true,
|
||||
completedEntity: entity,
|
||||
deferredEntities: [],
|
||||
refreshKeys: [],
|
||||
errors: [],
|
||||
relations: [],
|
||||
state: {
|
||||
@@ -119,6 +120,7 @@ describe('DefaultCatalogProcessingOrchestrator', () => {
|
||||
).resolves.toEqual({
|
||||
ok: true,
|
||||
completedEntity: entity,
|
||||
refreshKeys: [],
|
||||
deferredEntities: [
|
||||
{
|
||||
locationKey: 'url:./new-place',
|
||||
|
||||
@@ -37,7 +37,7 @@ export type EntityProcessingResult =
|
||||
completedEntity: Entity;
|
||||
deferredEntities: DeferredEntity[];
|
||||
relations: EntityRelationSpec[];
|
||||
refreshKeys: { key: String; entityRef: String }[];
|
||||
refreshKeys: RefreshKeyData[];
|
||||
errors: Error[];
|
||||
}
|
||||
| {
|
||||
@@ -45,6 +45,15 @@ export type EntityProcessingResult =
|
||||
errors: Error[];
|
||||
};
|
||||
|
||||
/**
|
||||
* A string to associate to the entity itself.
|
||||
* @public
|
||||
*/
|
||||
export type RefreshKeyData = {
|
||||
key: String;
|
||||
entityRef: String;
|
||||
};
|
||||
|
||||
/**
|
||||
* Responsible for executing the individual processing steps in order to fully process an entity.
|
||||
* @public
|
||||
|
||||
@@ -138,6 +138,7 @@ describe('Refresh integration', () => {
|
||||
errors: [],
|
||||
deferredEntities,
|
||||
state: {},
|
||||
refreshKeys: [],
|
||||
};
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user