diff --git a/packages/backend/src/plugins/search.ts b/packages/backend/src/plugins/search.ts index d1268e4f59..bde67360c7 100644 --- a/packages/backend/src/plugins/search.ts +++ b/packages/backend/src/plugins/search.ts @@ -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. diff --git a/packages/search-common/src/types.ts b/packages/search-common/src/types.ts index 3a47187945..ad8dcf9fc6 100644 --- a/packages/search-common/src/types.ts +++ b/packages/search-common/src/types.ts @@ -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; +export interface DocumentCollator { + execute(): Promise; +} /** - * 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; +export interface DocumentDecorator { + execute(documents: IndexableDocument[]): Promise; +} diff --git a/plugins/catalog-backend/src/search/Collator.ts b/plugins/catalog-backend/src/search/Collator.ts index 1513060384..625cc09a6b 100644 --- a/plugins/catalog-backend/src/search/Collator.ts +++ b/plugins/catalog-backend/src/search/Collator.ts @@ -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 => { - 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 = ( }; }, ); - }; -}; + } +} diff --git a/plugins/search-backend-node/src/index.test.ts b/plugins/search-backend-node/src/index.test.ts index 21db1657b3..01a4d9a486 100644 --- a/plugins/search-backend-node/src/index.test.ts +++ b/plugins/search-backend-node/src/index.test.ts @@ -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. diff --git a/plugins/search-backend-node/src/registry.ts b/plugins/search-backend-node/src/registry.ts index 6125e8f6a3..3c17ae5569 100644 --- a/plugins/search-backend-node/src/registry.ts +++ b/plugins/search-backend-node/src/registry.ts @@ -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; - private decorators: Record; + private decorators: Record; 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. diff --git a/plugins/search-backend-node/src/types.ts b/plugins/search-backend-node/src/types.ts index 08ba475df8..e55faccfb4 100644 --- a/plugins/search-backend-node/src/types.ts +++ b/plugins/search-backend-node/src/types.ts @@ -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,