Tweak Collator/Decorator to be classes instead of methods
Signed-off-by: Eric Peterson <ericpeterson@spotify.com>
This commit is contained in:
committed by
Eric Peterson
parent
124b93bff4
commit
51fc8de48c
@@ -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.
|
||||
|
||||
@@ -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[]>;
|
||||
}
|
||||
|
||||
@@ -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 = (
|
||||
};
|
||||
},
|
||||
);
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user