Tweak Collator/Decorator to be classes instead of methods

Signed-off-by: Eric Peterson <ericpeterson@spotify.com>
This commit is contained in:
Anders Näsman
2021-03-17 10:53:52 +01:00
committed by Eric Peterson
parent 124b93bff4
commit 51fc8de48c
6 changed files with 78 additions and 53 deletions
+2 -2
View File
@@ -19,14 +19,14 @@ import {
// registerCollator,
} from '@backstage/plugin-search-backend-node';
import { PluginEnvironment } from '../types';
// import { SearchCollatorFactory } from '@backstage/plugin-catalog-backend';
// import { DefaultCatalogCollator } from '@backstage/plugin-catalog-backend';
export default async function createPlugin({ logger }: PluginEnvironment) {
// TODO: Within this PR, update to use REST API instead of Catalog Builder.
/* registerCollator({
type: 'software-catalog',
defaultRefreshIntervalSeconds: 600,
collator: SearchCollatorFactory(entitiesCatalog),
collator: new DefaultCatalogCollator(entitiesCatalog),
});*/
// TODO: Make this a more proper refresh loop.
+10 -8
View File
@@ -62,15 +62,17 @@ export interface IndexableDocument {
}
/**
* Signature for the callback function that implementors must register to have
* their documents indexed.
* Interface that must be implemented in order to expose new documents to
* search.
*/
export type IndexableDocumentCollator = () => Promise<IndexableDocument[]>;
export interface DocumentCollator {
execute(): Promise<IndexableDocument[]>;
}
/**
* Signature for the callback function that implementors must register to
* decorate existing documents with additional metadata.
* Interface that must be implemented in order to decorate existing documents with
* additional metadata.
*/
export type IndexableDocumentDecorator = (
documents: IndexableDocument[],
) => Promise<IndexableDocument[]>;
export interface DocumentDecorator {
execute(documents: IndexableDocument[]): Promise<IndexableDocument[]>;
}
+12 -11
View File
@@ -14,21 +14,22 @@
* limitations under the License.
*/
import {
IndexableDocument,
IndexableDocumentCollator,
} from '@backstage/search-common';
import { IndexableDocument, DocumentCollator } from '@backstage/search-common';
import { EntitiesCatalog } from '../catalog';
export interface CatalogEntityDocument extends IndexableDocument {
componentType: string;
}
export const SearchCollatorFactory = (
entitiesCatalog: EntitiesCatalog,
): IndexableDocumentCollator => {
return async (): Promise<IndexableDocument[]> => {
const entities = await entitiesCatalog.entities();
export class DefaultCatalogCollator implements DocumentCollator {
protected entitiesCatalog: EntitiesCatalog;
constructor(entitiesCatalog: EntitiesCatalog) {
this.entitiesCatalog = entitiesCatalog;
}
async execute() {
const entities = await this.entitiesCatalog.entities();
return entities.map(
(entity): CatalogEntityDocument => {
return {
@@ -45,5 +46,5 @@ export const SearchCollatorFactory = (
};
},
);
};
};
}
}
+43 -15
View File
@@ -14,23 +14,48 @@
* limitations under the License.
*/
import {
DocumentCollator,
DocumentDecorator,
IndexableDocument,
} from '@backstage/search-common';
import { registerCollator, registerDecorator } from './';
import { Registry } from './registry';
describe('external api', () => {
class TestDocumentCollator implements DocumentCollator {
async execute() {
return [];
}
}
class TestDocumentDecorator implements DocumentDecorator {
async execute(documents: IndexableDocument[]) {
return documents;
}
}
describe('search indexer external api', () => {
let testCollator: DocumentCollator;
let testDecorator: DocumentDecorator;
beforeEach(() => {
testCollator = new TestDocumentCollator();
testDecorator = new TestDocumentDecorator();
});
afterEach(() => {
Registry.getInstance()._reset();
});
describe('registerCollator', () => {
it('registers a collator', async () => {
const collatorSpy = jest.fn(async () => []);
const collatorSpy = jest.spyOn(testCollator, 'execute');
// Register a collator.
registerCollator({
type: 'anything',
defaultRefreshIntervalSeconds: 600,
collator: collatorSpy,
collator: testCollator,
});
// Execute the registry and ensure the collator was invoked.
@@ -41,19 +66,18 @@ describe('external api', () => {
describe('registerDecorator', () => {
it('registers a decorator', async () => {
const mockCollator = jest.fn(async () => []);
const decoratorSpy = jest.fn(async docs => docs);
const decoratorSpy = jest.spyOn(testDecorator, 'execute');
// Register a collator.
registerCollator({
type: 'anything',
defaultRefreshIntervalSeconds: 600,
collator: mockCollator,
collator: testCollator,
});
// Register a decorator.
registerDecorator({
decorator: decoratorSpy,
decorator: testDecorator,
});
// Execute the registry and ensure the decorator was invoked.
@@ -68,20 +92,22 @@ describe('external api', () => {
text: 'Test text.',
location: '/test/location',
};
const mockCollator = jest.fn(async () => [docFixture]);
const decoratorSpy = jest.fn(async docs => docs);
jest
.spyOn(testCollator, 'execute')
.mockImplementation(async () => [docFixture]);
const decoratorSpy = jest.spyOn(testDecorator, 'execute');
// Register a collator.
registerCollator({
type: expectedType,
defaultRefreshIntervalSeconds: 600,
collator: mockCollator,
collator: testCollator,
});
// Register a decorator for the same type.
registerDecorator({
types: [expectedType],
decorator: decoratorSpy,
decorator: testDecorator,
});
// Execute the registry and ensure the decorator was invoked.
@@ -97,20 +123,22 @@ describe('external api', () => {
text: 'Test text.',
location: '/test/location',
};
const mockCollator = jest.fn(async () => [docFixture]);
const decoratorSpy = jest.fn(async docs => docs);
jest
.spyOn(testCollator, 'execute')
.mockImplementation(async () => [docFixture]);
const decoratorSpy = jest.spyOn(testDecorator, 'execute');
// Register a collator.
registerCollator({
type: expectedType,
defaultRefreshIntervalSeconds: 600,
collator: mockCollator,
collator: testCollator,
});
// Register a decorator for a different type.
registerDecorator({
types: ['not-the-expected-type'],
decorator: decoratorSpy,
decorator: testDecorator,
});
// Execute the registry and ensure the decorator was not invoked.
+6 -9
View File
@@ -14,23 +14,20 @@
* limitations under the License.
*/
import {
IndexableDocumentCollator,
IndexableDocumentDecorator,
} from '@backstage/search-common';
import { DocumentCollator, DocumentDecorator } from '@backstage/search-common';
import {
RegisterCollatorParameters,
RegisterDecoratorParameters,
} from './types';
interface CollatorRegistryEntry {
collate: IndexableDocumentCollator;
collate: DocumentCollator;
refreshInterval: number;
}
export class Registry {
private collators: Record<string, CollatorRegistryEntry>;
private decorators: Record<string, IndexableDocumentDecorator[]>;
private decorators: Record<string, DocumentDecorator[]>;
private static instance: Registry;
@@ -74,13 +71,13 @@ export class Registry {
async execute() {
return Promise.all(
Object.keys(this.collators).map(async type => {
const decorators: IndexableDocumentDecorator[] = (
const decorators: DocumentDecorator[] = (
this.decorators['*'] || []
).concat(this.decorators[type] || []);
let documents = await this.collators[type].collate();
let documents = await this.collators[type].collate.execute();
for (let i = 0; i < decorators.length; i++) {
documents = await decorators[i](documents);
documents = await decorators[i].execute(documents);
}
// TODO: push documents to a configured search engine.
+5 -8
View File
@@ -14,10 +14,7 @@
* limitations under the License.
*/
import {
IndexableDocumentCollator,
IndexableDocumentDecorator,
} from '@backstage/search-common';
import { DocumentCollator, DocumentDecorator } from '@backstage/search-common';
/**
* Parameters required to register a collator.
@@ -34,9 +31,9 @@ export interface RegisterCollatorParameters {
defaultRefreshIntervalSeconds: number;
/**
* The collator function responsible for returning all documents of the given type.
* The collator class responsible for returning all documents of the given type.
*/
collator: IndexableDocumentCollator;
collator: DocumentCollator;
}
/**
@@ -44,9 +41,9 @@ export interface RegisterCollatorParameters {
*/
export interface RegisterDecoratorParameters {
/**
* The decorator function responsible for appending or modifying documents of the given type(s).
* The decorator class responsible for appending or modifying documents of the given type(s).
*/
decorator: IndexableDocumentDecorator;
decorator: DocumentDecorator;
/**
* (Optional) An array of document types that the given decorator should apply to. If none are provided,