rename plugin-search-indexer to plugin-search-backend-node

Signed-off-by: Eric Peterson <ericpeterson@spotify.com>
This commit is contained in:
Anders Näsman
2021-03-17 10:12:44 +01:00
committed by Eric Peterson
parent 0057692401
commit 124b93bff4
12 changed files with 11 additions and 11 deletions
+3
View File
@@ -0,0 +1,3 @@
module.exports = {
extends: [require.resolve('@backstage/cli/config/eslint.backend')],
};
+21
View File
@@ -0,0 +1,21 @@
# search-backend-node
This plugin is part of a suite of plugins that comprise the Backstage search
platform, which is still very much under development. This plugin specifically
is responsible for:
- Allowing other backend plugins to register the fact that they have documents
that they'd like to be indexed by a search engine (known as `collators`)
- Allowing other backend plugins to register the fact that they have metadata
that they'd like to augment existing documents in the search index with
(known as `decorators`)
- A scheduler that, at configurable intervals, compiles documents to be indexed
and passes them to a search engine for indexing
- Types for all of the above
Documentation on how to develop and improve the search platform is currently
centralized in the `search` plugin README.md.
For a better overview of how the search platform is put together, check the
[Backstage Search Architecture](https://backstage.io/docs/features/search/architecture)
documentation.
+30
View File
@@ -0,0 +1,30 @@
{
"name": "@backstage/plugin-search-backend-node",
"version": "0.1.1",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
"publishConfig": {
"access": "public",
"main": "dist/index.cjs.js",
"types": "dist/index.d.ts"
},
"scripts": {
"start": "backstage-cli backend:dev",
"build": "backstage-cli backend:build",
"lint": "backstage-cli lint",
"test": "backstage-cli test",
"prepack": "backstage-cli prepack",
"postpack": "backstage-cli postpack",
"clean": "backstage-cli clean"
},
"dependencies": {
"@backstage/search-common": "^0.1.1"
},
"devDependencies": {
"@backstage/cli": "^0.6.0"
},
"files": [
"dist"
]
}
@@ -0,0 +1,121 @@
/*
* Copyright 2021 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { registerCollator, registerDecorator } from './';
import { Registry } from './registry';
describe('external api', () => {
afterEach(() => {
Registry.getInstance()._reset();
});
describe('registerCollator', () => {
it('registers a collator', async () => {
const collatorSpy = jest.fn(async () => []);
// Register a collator.
registerCollator({
type: 'anything',
defaultRefreshIntervalSeconds: 600,
collator: collatorSpy,
});
// Execute the registry and ensure the collator was invoked.
await Registry.getInstance().execute();
expect(collatorSpy).toHaveBeenCalled();
});
});
describe('registerDecorator', () => {
it('registers a decorator', async () => {
const mockCollator = jest.fn(async () => []);
const decoratorSpy = jest.fn(async docs => docs);
// Register a collator.
registerCollator({
type: 'anything',
defaultRefreshIntervalSeconds: 600,
collator: mockCollator,
});
// Register a decorator.
registerDecorator({
decorator: decoratorSpy,
});
// Execute the registry and ensure the decorator was invoked.
await Registry.getInstance().execute();
expect(decoratorSpy).toHaveBeenCalled();
});
it('registers a type-specific decorator', async () => {
const expectedType = 'an-expected-type';
const docFixture = {
title: 'Test',
text: 'Test text.',
location: '/test/location',
};
const mockCollator = jest.fn(async () => [docFixture]);
const decoratorSpy = jest.fn(async docs => docs);
// Register a collator.
registerCollator({
type: expectedType,
defaultRefreshIntervalSeconds: 600,
collator: mockCollator,
});
// Register a decorator for the same type.
registerDecorator({
types: [expectedType],
decorator: decoratorSpy,
});
// Execute the registry and ensure the decorator was invoked.
await Registry.getInstance().execute();
expect(decoratorSpy).toHaveBeenCalled();
expect(decoratorSpy).toHaveBeenCalledWith([docFixture]);
});
it('registers a type-specific decorator that should not be called', async () => {
const expectedType = 'an-expected-type';
const docFixture = {
title: 'Test',
text: 'Test text.',
location: '/test/location',
};
const mockCollator = jest.fn(async () => [docFixture]);
const decoratorSpy = jest.fn(async docs => docs);
// Register a collator.
registerCollator({
type: expectedType,
defaultRefreshIntervalSeconds: 600,
collator: mockCollator,
});
// Register a decorator for a different type.
registerDecorator({
types: ['not-the-expected-type'],
decorator: decoratorSpy,
});
// Execute the registry and ensure the decorator was not invoked.
await Registry.getInstance().execute();
expect(decoratorSpy).not.toHaveBeenCalled();
});
});
});
+32
View File
@@ -0,0 +1,32 @@
/*
* Copyright 2021 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Registry } from './registry';
import {
RegisterCollatorParameters,
RegisterDecoratorParameters,
} from './types';
const registry = Registry.getInstance();
export const registerCollator = (params: RegisterCollatorParameters) => {
registry.addCollator(params);
};
export const registerDecorator = (params: RegisterDecoratorParameters) => {
registry.addDecorator(params);
};
export const collateDocuments = () => {
registry.execute();
};
@@ -0,0 +1,99 @@
/*
* Copyright 2021 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {
IndexableDocumentCollator,
IndexableDocumentDecorator,
} from '@backstage/search-common';
import {
RegisterCollatorParameters,
RegisterDecoratorParameters,
} from './types';
interface CollatorRegistryEntry {
collate: IndexableDocumentCollator;
refreshInterval: number;
}
export class Registry {
private collators: Record<string, CollatorRegistryEntry>;
private decorators: Record<string, IndexableDocumentDecorator[]>;
private static instance: Registry;
private constructor() {
this.collators = {};
this.decorators = {};
}
static getInstance(): Registry {
if (!Registry.instance) {
Registry.instance = new Registry();
}
return Registry.instance;
}
addCollator({
type,
collator,
defaultRefreshIntervalSeconds,
}: RegisterCollatorParameters): void {
this.collators[type] = {
refreshInterval: defaultRefreshIntervalSeconds,
collate: collator,
};
}
addDecorator({
types = ['*'],
decorator,
}: RegisterDecoratorParameters): void {
types.forEach(type => {
if (this.decorators.hasOwnProperty(type)) {
this.decorators[type].push(decorator);
} else {
this.decorators[type] = [decorator];
}
});
}
// TODO: But like with coordination, timing, error handling, and what have you.
async execute() {
return Promise.all(
Object.keys(this.collators).map(async type => {
const decorators: IndexableDocumentDecorator[] = (
this.decorators['*'] || []
).concat(this.decorators[type] || []);
let documents = await this.collators[type].collate();
for (let i = 0; i < decorators.length; i++) {
documents = await decorators[i](documents);
}
// TODO: push documents to a configured search engine.
}),
);
}
/**
* Utility method for tests. Do not use otherwise.
* @private
*/
_reset() {
this.collators = {};
this.decorators = {};
}
}
@@ -0,0 +1,17 @@
/*
* Copyright 2021 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export {};
+56
View File
@@ -0,0 +1,56 @@
/*
* Copyright 2021 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {
IndexableDocumentCollator,
IndexableDocumentDecorator,
} from '@backstage/search-common';
/**
* Parameters required to register a collator.
*/
export interface RegisterCollatorParameters {
/**
* The type of document to be indexed (used to name indices, to configure refresh loop, etc).
*/
type: string;
/**
* The default interval (in seconds) that the provided collator will be called (can be overridden in config).
*/
defaultRefreshIntervalSeconds: number;
/**
* The collator function responsible for returning all documents of the given type.
*/
collator: IndexableDocumentCollator;
}
/**
* Parameters required to register a decorator
*/
export interface RegisterDecoratorParameters {
/**
* The decorator function responsible for appending or modifying documents of the given type(s).
*/
decorator: IndexableDocumentDecorator;
/**
* (Optional) An array of document types that the given decorator should apply to. If none are provided,
* the decorator will be applied to all types.
*/
types?: string[];
}