Merge pull request #4515 from backstage/iameap/search-indexer
[Search] Foundation: plugins and types
This commit is contained in:
@@ -11,6 +11,7 @@
|
||||
/plugins/cost-insights @backstage/silver-lining
|
||||
/plugins/cloudbuild @trivago/ebarrios
|
||||
/plugins/search @backstage/techdocs-core
|
||||
/plugins/search-* @backstage/techdocs-core
|
||||
/plugins/techdocs @backstage/techdocs-core
|
||||
/plugins/techdocs-backend @backstage/techdocs-core
|
||||
/packages/techdocs-common @backstage/techdocs-core
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<svg host="65bd71144e" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="789px" height="766px" viewBox="-0.5 -0.5 789 766" content="<mxfile host="be2f1433-9da5-431b-92db-92882262d7d1" modified="2021-02-10T13:37:29.724Z" agent="5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Code/1.53.0 Chrome/87.0.4280.141 Electron/11.2.1 Safari/537.36" etag="EtMH9Eo_7GPdazvr9TBW" version="14.2.4" type="embed"><diagram id="AOZgdlUmH_6GT6Gt5u4e" name="Page-1">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</diagram></mxfile>" style="background-color: rgb(255, 255, 255);">
|
||||
<svg host="65bd71144e" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="789px" height="766px" viewBox="-0.5 -0.5 789 766" content="<mxfile><diagram id="AOZgdlUmH_6GT6Gt5u4e" name="Page-1">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</diagram></mxfile>" style="background-color: rgb(255, 255, 255);">
|
||||
<defs/>
|
||||
<g>
|
||||
<rect x="560" y="484" width="140" height="140" fill="#21c0a5" stroke="#006658" pointer-events="all"/>
|
||||
@@ -268,7 +268,7 @@
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 220px; margin-left: 350px;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 220px; margin-left: 351px;">
|
||||
<div style="box-sizing: border-box; font-size: 0; text-align: center; ">
|
||||
<div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #5C5C5C; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">
|
||||
Pass Search
|
||||
@@ -282,7 +282,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="350" y="223" fill="#5C5C5C" font-family="Helvetica" font-size="11px" text-anchor="middle">
|
||||
<text x="351" y="223" fill="#5C5C5C" font-family="Helvetica" font-size="11px" text-anchor="middle">
|
||||
Pass Search...
|
||||
</text>
|
||||
</switch>
|
||||
@@ -368,7 +368,7 @@
|
||||
</g>
|
||||
<path d="M 444.17 349.5 C 444.17 341.22 453.31 334.5 464.59 334.5 C 470.01 334.5 475.2 336.08 479.03 338.89 C 482.86 341.71 485.01 345.52 485.01 349.5 L 485.01 379 C 485.01 387.28 475.87 394 464.59 394 C 453.31 394 444.17 387.28 444.17 379 Z" fill="#21c0a5" stroke="#006658" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<path d="M 485.01 349.5 C 485.01 357.78 475.87 364.5 464.59 364.5 C 453.31 364.5 444.17 357.78 444.17 349.5" fill="none" stroke="#006658" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<path d="M 550 364.25 L 517.5 364.3 L 485.01 364.25" fill="none" stroke="#006658" stroke-miterlimit="10" pointer-events="stroke"/>
|
||||
<path d="M 550 364.25 L 517.5 364.3 L 485.01 364.3" fill="none" stroke="#006658" stroke-miterlimit="10" pointer-events="stroke"/>
|
||||
<rect x="550" y="324.25" width="80" height="80" fill="#21c0a5" stroke="#006658" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
@@ -529,7 +529,7 @@
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<rect x="565" y="80" width="130" height="30" fill="none" stroke="none" pointer-events="all"/>
|
||||
<rect x="565" y="80" width="170" height="30" fill="none" stroke="none" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
|
||||
@@ -538,7 +538,7 @@
|
||||
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #5C5C5C; line-height: 1.2; pointer-events: all; white-space: nowrap; ">
|
||||
@backstage/
|
||||
<br/>
|
||||
plugin-search-indexer
|
||||
plugin-search-backend-node
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 46 KiB |
@@ -39,7 +39,7 @@ import { GraphiQLPage } from '@backstage/plugin-graphiql';
|
||||
import { LighthousePage } from '@backstage/plugin-lighthouse';
|
||||
import { NewRelicPage } from '@backstage/plugin-newrelic';
|
||||
import { ScaffolderPage, scaffolderPlugin } from '@backstage/plugin-scaffolder';
|
||||
import { SearchPage } from '@backstage/plugin-search';
|
||||
import { SearchPage, SearchPageNext } from '@backstage/plugin-search';
|
||||
import { TechRadarPage } from '@backstage/plugin-tech-radar';
|
||||
import { TechdocsPage } from '@backstage/plugin-techdocs';
|
||||
import { UserSettingsPage } from '@backstage/plugin-user-settings';
|
||||
@@ -112,6 +112,10 @@ const routes = (
|
||||
<Route path="/gcp-projects" element={<GcpProjectsPage />} />
|
||||
<Route path="/newrelic" element={<NewRelicPage />} />
|
||||
<Route path="/search" element={<SearchPage />} />
|
||||
<Route
|
||||
path="/search-next"
|
||||
element={<SearchPageNext /* TODO: illustrate customization */ />}
|
||||
/>
|
||||
<Route path="/cost-insights" element={<CostInsightsPage />} />
|
||||
<Route
|
||||
path="/cost-insights/investigating-growth"
|
||||
|
||||
@@ -41,6 +41,8 @@
|
||||
"@backstage/plugin-proxy-backend": "^0.2.6",
|
||||
"@backstage/plugin-rollbar-backend": "^0.1.8",
|
||||
"@backstage/plugin-scaffolder-backend": "^0.9.2",
|
||||
"@backstage/plugin-search-backend": "^0.1.1",
|
||||
"@backstage/plugin-search-backend-node": "^0.1.1",
|
||||
"@backstage/plugin-techdocs-backend": "^0.6.5",
|
||||
"@backstage/plugin-todo-backend": "^0.1.1",
|
||||
"@gitbeaker/node": "^28.0.2",
|
||||
|
||||
@@ -42,6 +42,7 @@ import kafka from './plugins/kafka';
|
||||
import rollbar from './plugins/rollbar';
|
||||
import scaffolder from './plugins/scaffolder';
|
||||
import proxy from './plugins/proxy';
|
||||
import search from './plugins/search';
|
||||
import techdocs from './plugins/techdocs';
|
||||
import todo from './plugins/todo';
|
||||
import graphql from './plugins/graphql';
|
||||
@@ -78,6 +79,7 @@ async function main() {
|
||||
const authEnv = useHotMemoize(module, () => createEnv('auth'));
|
||||
const proxyEnv = useHotMemoize(module, () => createEnv('proxy'));
|
||||
const rollbarEnv = useHotMemoize(module, () => createEnv('rollbar'));
|
||||
const searchEnv = useHotMemoize(module, () => createEnv('search'));
|
||||
const techdocsEnv = useHotMemoize(module, () => createEnv('techdocs'));
|
||||
const todoEnv = useHotMemoize(module, () => createEnv('todo'));
|
||||
const kubernetesEnv = useHotMemoize(module, () => createEnv('kubernetes'));
|
||||
@@ -91,6 +93,7 @@ async function main() {
|
||||
apiRouter.use('/rollbar', await rollbar(rollbarEnv));
|
||||
apiRouter.use('/scaffolder', await scaffolder(scaffolderEnv));
|
||||
apiRouter.use('/auth', await auth(authEnv));
|
||||
apiRouter.use('/search', await search(searchEnv));
|
||||
apiRouter.use('/techdocs', await techdocs(techdocsEnv));
|
||||
apiRouter.use('/todo', await todo(todoEnv));
|
||||
apiRouter.use('/kubernetes', await kubernetes(kubernetesEnv));
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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 { useHotCleanup } from '@backstage/backend-common';
|
||||
import { createRouter } from '@backstage/plugin-search-backend';
|
||||
import { IndexBuilder } from '@backstage/plugin-search-backend-node';
|
||||
import { PluginEnvironment } from '../types';
|
||||
import { DefaultCatalogCollator } from '@backstage/plugin-catalog-backend';
|
||||
|
||||
export default async function createPlugin({
|
||||
logger,
|
||||
discovery,
|
||||
}: PluginEnvironment) {
|
||||
const indexBuilder = new IndexBuilder({ logger });
|
||||
|
||||
indexBuilder.addCollator({
|
||||
type: 'software-catalog',
|
||||
defaultRefreshIntervalSeconds: 600,
|
||||
collator: new DefaultCatalogCollator(discovery),
|
||||
});
|
||||
|
||||
// TODO: Move refresh loop logic into the builder.
|
||||
const timerId = setInterval(() => {
|
||||
indexBuilder.build();
|
||||
}, 60000);
|
||||
useHotCleanup(module, () => clearInterval(timerId));
|
||||
|
||||
return await createRouter({
|
||||
logger,
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
extends: [require.resolve('@backstage/cli/config/eslint.backend')],
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
# @backstage/search-common
|
||||
|
||||
Common functionalities for Search, to be shared between various search-enabled plugins.
|
||||
@@ -0,0 +1,49 @@
|
||||
{
|
||||
"name": "@backstage/search-common",
|
||||
"description": "Common functionalities for Search, to be shared between various search-enabled plugins",
|
||||
"version": "0.1.1",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"private": false,
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"main": "dist/index.cjs.js",
|
||||
"types": "dist/index.d.ts"
|
||||
},
|
||||
"homepage": "https://backstage.io",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/backstage/backstage",
|
||||
"directory": "packages/search-common"
|
||||
},
|
||||
"keywords": [
|
||||
"backstage",
|
||||
"search"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "backstage-cli build --outputs cjs,types",
|
||||
"lint": "backstage-cli lint",
|
||||
"test": "backstage-cli test",
|
||||
"prepack": "backstage-cli prepack",
|
||||
"postpack": "backstage-cli postpack",
|
||||
"clean": "backstage-cli clean"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/backstage/backstage/issues"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/config": "^0.1.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.6.2"
|
||||
},
|
||||
"jest": {
|
||||
"roots": [
|
||||
".."
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* 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 * as anything from './';
|
||||
|
||||
describe('search-common', () => {
|
||||
// TODO: Test real things once they exist.
|
||||
it('should exist', () => {
|
||||
expect(anything).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* 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 * from './types';
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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 { JsonObject } from '@backstage/config';
|
||||
|
||||
export interface SearchQuery {
|
||||
term: string;
|
||||
filters?: JsonObject;
|
||||
pageCursor: string;
|
||||
}
|
||||
|
||||
export interface SearchResult {
|
||||
document: IndexableDocument;
|
||||
}
|
||||
|
||||
export interface SearchResultSet {
|
||||
results: SearchResult[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Base properties that all indexed documents must include, as well as some
|
||||
* common properties that documents are encouraged to use where appropriate.
|
||||
*/
|
||||
export interface IndexableDocument {
|
||||
/**
|
||||
* The primary name of the document (e.g. name, title, identifier, etc).
|
||||
*/
|
||||
title: string;
|
||||
|
||||
/**
|
||||
* Free-form text of the document (e.g. description, content, etc).
|
||||
*/
|
||||
text: string;
|
||||
|
||||
/**
|
||||
* The relative or absolute URL of the document (target when a search result
|
||||
* is clicked).
|
||||
*/
|
||||
location: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface that must be implemented in order to expose new documents to
|
||||
* search.
|
||||
*/
|
||||
export interface DocumentCollator {
|
||||
execute(): Promise<IndexableDocument[]>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface that must be implemented in order to decorate existing documents with
|
||||
* additional metadata.
|
||||
*/
|
||||
export interface DocumentDecorator {
|
||||
execute(documents: IndexableDocument[]): Promise<IndexableDocument[]>;
|
||||
}
|
||||
@@ -35,6 +35,8 @@
|
||||
"@backstage/config": "^0.1.4",
|
||||
"@backstage/errors": "^0.1.1",
|
||||
"@backstage/integration": "^0.5.1",
|
||||
"@backstage/plugin-search-backend-node": "^0.1.1",
|
||||
"@backstage/search-common": "^0.1.1",
|
||||
"@octokit/graphql": "^4.5.8",
|
||||
"@types/express": "^4.17.6",
|
||||
"@types/ldapjs": "^1.0.9",
|
||||
|
||||
@@ -17,5 +17,6 @@
|
||||
export * from './catalog';
|
||||
export * from './database';
|
||||
export * from './ingestion';
|
||||
export * from './search';
|
||||
export * from './service';
|
||||
export * from './util';
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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 { PluginEndpointDiscovery } from '@backstage/backend-common';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { IndexableDocument, DocumentCollator } from '@backstage/search-common';
|
||||
import fetch from 'cross-fetch';
|
||||
|
||||
export interface CatalogEntityDocument extends IndexableDocument {
|
||||
componentType: string;
|
||||
namespace: string;
|
||||
kind: string;
|
||||
}
|
||||
|
||||
export class DefaultCatalogCollator implements DocumentCollator {
|
||||
protected discovery: PluginEndpointDiscovery;
|
||||
|
||||
constructor(discovery: PluginEndpointDiscovery) {
|
||||
this.discovery = discovery;
|
||||
}
|
||||
|
||||
async execute() {
|
||||
const baseUrl = await this.discovery.getBaseUrl('catalog');
|
||||
const res = await fetch(`${baseUrl}/entities`);
|
||||
const entities: Entity[] = await res.json();
|
||||
return entities.map(
|
||||
(entity): CatalogEntityDocument => {
|
||||
return {
|
||||
title: entity.metadata.name,
|
||||
// TODO: Use a config-based template approach for entity location.
|
||||
location: `/catalog/${
|
||||
entity.metadata.namespace || 'default'
|
||||
}/component/${entity.metadata.name}`,
|
||||
text: entity.metadata.description || '',
|
||||
componentType: entity.spec?.type?.toString() || 'other',
|
||||
namespace: entity.metadata.namespace || 'default',
|
||||
kind: entity.kind,
|
||||
};
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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 * from './DefaultCatalogCollator';
|
||||
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
extends: [require.resolve('@backstage/cli/config/eslint.backend')],
|
||||
};
|
||||
@@ -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.
|
||||
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"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",
|
||||
"winston": "^3.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/backend-common": "0.5.6",
|
||||
"@backstage/cli": "^0.6.0"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* 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 { DocumentCollator, DocumentDecorator } from '@backstage/search-common';
|
||||
import { Logger } from 'winston';
|
||||
import {
|
||||
RegisterCollatorParameters,
|
||||
RegisterDecoratorParameters,
|
||||
} from './types';
|
||||
|
||||
interface CollatorEnvelope {
|
||||
collate: DocumentCollator;
|
||||
refreshInterval: number;
|
||||
}
|
||||
|
||||
type IndexBuilderOptions = {
|
||||
logger: Logger;
|
||||
};
|
||||
|
||||
export class IndexBuilder {
|
||||
private collators: Record<string, CollatorEnvelope>;
|
||||
private decorators: Record<string, DocumentDecorator[]>;
|
||||
private logger: Logger;
|
||||
|
||||
constructor({ logger }: IndexBuilderOptions) {
|
||||
this.collators = {};
|
||||
this.decorators = {};
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the index builder aware of a collator that should be executed at the
|
||||
* given refresh interval.
|
||||
*/
|
||||
addCollator({
|
||||
type,
|
||||
collator,
|
||||
defaultRefreshIntervalSeconds,
|
||||
}: RegisterCollatorParameters): void {
|
||||
this.logger.info(
|
||||
`Added ${collator.constructor.name} collator for type ${type}`,
|
||||
);
|
||||
this.collators[type] = {
|
||||
refreshInterval: defaultRefreshIntervalSeconds,
|
||||
collate: collator,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the index builder aware of a decorator. If no types are provided, it
|
||||
* will be applied to documents from all known collators, otherwise it will
|
||||
* only be applied to documents of the given types.
|
||||
*/
|
||||
addDecorator({
|
||||
types = ['*'],
|
||||
decorator,
|
||||
}: RegisterDecoratorParameters): void {
|
||||
this.logger.info(
|
||||
`Added decorator ${decorator.constructor.name} to types ${types.join(
|
||||
', ',
|
||||
)}`,
|
||||
);
|
||||
types.forEach(type => {
|
||||
if (this.decorators.hasOwnProperty(type)) {
|
||||
this.decorators[type].push(decorator);
|
||||
} else {
|
||||
this.decorators[type] = [decorator];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the process of executing collators and decorators and building the
|
||||
* search index.
|
||||
*
|
||||
* TODO: But like with coordination, timing, error handling, and what have you.
|
||||
*/
|
||||
async build() {
|
||||
return Promise.all(
|
||||
Object.keys(this.collators).map(async type => {
|
||||
const decorators: DocumentDecorator[] = (
|
||||
this.decorators['*'] || []
|
||||
).concat(this.decorators[type] || []);
|
||||
|
||||
this.logger.info(
|
||||
`Collating documents for ${type} via ${this.collators[type].collate.constructor.name}`,
|
||||
);
|
||||
let documents = await this.collators[type].collate.execute();
|
||||
for (let i = 0; i < decorators.length; i++) {
|
||||
this.logger.info(
|
||||
`Decorating ${type} documents via ${decorators[i].constructor.name}`,
|
||||
);
|
||||
documents = await decorators[i].execute(documents);
|
||||
}
|
||||
|
||||
// TODO: push documents to a configured search engine.
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* 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 { getVoidLogger } from '@backstage/backend-common';
|
||||
import {
|
||||
DocumentCollator,
|
||||
DocumentDecorator,
|
||||
IndexableDocument,
|
||||
} from '@backstage/search-common';
|
||||
import { IndexBuilder } from './IndexBuilder';
|
||||
|
||||
class TestDocumentCollator implements DocumentCollator {
|
||||
async execute() {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
class TestDocumentDecorator implements DocumentDecorator {
|
||||
async execute(documents: IndexableDocument[]) {
|
||||
return documents;
|
||||
}
|
||||
}
|
||||
|
||||
describe('IndexBuilder', () => {
|
||||
let testIndexBuilder: IndexBuilder;
|
||||
let testCollator: DocumentCollator;
|
||||
let testDecorator: DocumentDecorator;
|
||||
|
||||
beforeEach(() => {
|
||||
testIndexBuilder = new IndexBuilder({ logger: getVoidLogger() });
|
||||
testCollator = new TestDocumentCollator();
|
||||
testDecorator = new TestDocumentDecorator();
|
||||
});
|
||||
|
||||
describe('addCollator', () => {
|
||||
it('adds a collator', async () => {
|
||||
const collatorSpy = jest.spyOn(testCollator, 'execute');
|
||||
|
||||
// Add a collator.
|
||||
testIndexBuilder.addCollator({
|
||||
type: 'anything',
|
||||
defaultRefreshIntervalSeconds: 600,
|
||||
collator: testCollator,
|
||||
});
|
||||
|
||||
// Build the index and ensure the collator was invoked.
|
||||
await testIndexBuilder.build();
|
||||
expect(collatorSpy).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('addDecorator', () => {
|
||||
it('adds a decorator', async () => {
|
||||
const decoratorSpy = jest.spyOn(testDecorator, 'execute');
|
||||
|
||||
// Add a collator.
|
||||
testIndexBuilder.addCollator({
|
||||
type: 'anything',
|
||||
defaultRefreshIntervalSeconds: 600,
|
||||
collator: testCollator,
|
||||
});
|
||||
|
||||
// Add a decorator.
|
||||
testIndexBuilder.addDecorator({
|
||||
decorator: testDecorator,
|
||||
});
|
||||
|
||||
// Build the index and ensure the decorator was invoked.
|
||||
await testIndexBuilder.build();
|
||||
expect(decoratorSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('adds a type-specific decorator', async () => {
|
||||
const expectedType = 'an-expected-type';
|
||||
const docFixture = {
|
||||
title: 'Test',
|
||||
text: 'Test text.',
|
||||
location: '/test/location',
|
||||
};
|
||||
jest
|
||||
.spyOn(testCollator, 'execute')
|
||||
.mockImplementation(async () => [docFixture]);
|
||||
const decoratorSpy = jest.spyOn(testDecorator, 'execute');
|
||||
|
||||
// Add a collator.
|
||||
testIndexBuilder.addCollator({
|
||||
type: expectedType,
|
||||
defaultRefreshIntervalSeconds: 600,
|
||||
collator: testCollator,
|
||||
});
|
||||
|
||||
// Add a decorator for the same type.
|
||||
testIndexBuilder.addDecorator({
|
||||
types: [expectedType],
|
||||
decorator: testDecorator,
|
||||
});
|
||||
|
||||
// Build the index and ensure the decorator was invoked.
|
||||
await testIndexBuilder.build();
|
||||
expect(decoratorSpy).toHaveBeenCalled();
|
||||
expect(decoratorSpy).toHaveBeenCalledWith([docFixture]);
|
||||
});
|
||||
|
||||
it('adds 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',
|
||||
};
|
||||
jest
|
||||
.spyOn(testCollator, 'execute')
|
||||
.mockImplementation(async () => [docFixture]);
|
||||
const decoratorSpy = jest.spyOn(testDecorator, 'execute');
|
||||
|
||||
// Add a collator.
|
||||
testIndexBuilder.addCollator({
|
||||
type: expectedType,
|
||||
defaultRefreshIntervalSeconds: 600,
|
||||
collator: testCollator,
|
||||
});
|
||||
|
||||
// Add a decorator for a different type.
|
||||
testIndexBuilder.addDecorator({
|
||||
types: ['not-the-expected-type'],
|
||||
decorator: testDecorator,
|
||||
});
|
||||
|
||||
// Build the index and ensure the decorator was not invoked.
|
||||
await testIndexBuilder.build();
|
||||
expect(decoratorSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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 { IndexBuilder } from './IndexBuilder';
|
||||
@@ -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 {};
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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 { DocumentCollator, DocumentDecorator } 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 class responsible for returning all documents of the given type.
|
||||
*/
|
||||
collator: DocumentCollator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parameters required to register a decorator
|
||||
*/
|
||||
export interface RegisterDecoratorParameters {
|
||||
/**
|
||||
* The decorator class responsible for appending or modifying documents of the given type(s).
|
||||
*/
|
||||
decorator: DocumentDecorator;
|
||||
|
||||
/**
|
||||
* (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[];
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
extends: [require.resolve('@backstage/cli/config/eslint.backend')],
|
||||
};
|
||||
@@ -0,0 +1,15 @@
|
||||
# search-backend
|
||||
|
||||
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:
|
||||
|
||||
- Exposing a JSON API for querying a search engine
|
||||
- Types related to interacting with that API
|
||||
|
||||
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.
|
||||
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"name": "@backstage/plugin-search-backend",
|
||||
"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/backend-common": "^0.5.3",
|
||||
"@backstage/search-common": "^0.1.1",
|
||||
"@types/express": "^4.17.6",
|
||||
"express": "^4.17.1",
|
||||
"express-promise-router": "^3.0.3",
|
||||
"winston": "^3.2.1",
|
||||
"yn": "^4.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.6.0",
|
||||
"@types/supertest": "^2.0.8",
|
||||
"supertest": "^4.0.2"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
]
|
||||
}
|
||||
@@ -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 * from './service/router';
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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 { getRootLogger } from '@backstage/backend-common';
|
||||
import yn from 'yn';
|
||||
import { startStandaloneServer } from './service/standaloneServer';
|
||||
|
||||
const port = process.env.PLUGIN_PORT ? Number(process.env.PLUGIN_PORT) : 7000;
|
||||
const enableCors = yn(process.env.PLUGIN_CORS, { default: false });
|
||||
const logger = getRootLogger();
|
||||
|
||||
startStandaloneServer({ port, enableCors, logger }).catch(err => {
|
||||
logger.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
process.on('SIGINT', () => {
|
||||
logger.info('CTRL+C pressed; exiting.');
|
||||
process.exit(0);
|
||||
});
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2020 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 { getVoidLogger } from '@backstage/backend-common';
|
||||
import express from 'express';
|
||||
import request from 'supertest';
|
||||
|
||||
import { createRouter } from './router';
|
||||
|
||||
describe('createRouter', () => {
|
||||
let app: express.Express;
|
||||
|
||||
beforeAll(async () => {
|
||||
const router = await createRouter({
|
||||
logger: getVoidLogger(),
|
||||
});
|
||||
app = express().use(router);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
describe('GET /query', () => {
|
||||
it('returns empty results array', async () => {
|
||||
const response = await request(app).get('/query');
|
||||
|
||||
expect(response.status).toEqual(200);
|
||||
expect(response.body).toMatchObject({ results: [] });
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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 express from 'express';
|
||||
import Router from 'express-promise-router';
|
||||
import { Logger } from 'winston';
|
||||
import { SearchQuery, SearchResultSet } from '@backstage/search-common';
|
||||
|
||||
type RouterOptions = {
|
||||
logger: Logger;
|
||||
};
|
||||
|
||||
export async function createRouter({
|
||||
logger,
|
||||
}: RouterOptions): Promise<express.Router> {
|
||||
const router = Router();
|
||||
|
||||
router.get(
|
||||
'/query',
|
||||
async (
|
||||
req: express.Request<any, unknown, unknown, SearchQuery>,
|
||||
res: express.Response<SearchResultSet>,
|
||||
) => {
|
||||
// TODO: Actually transform req.params into search engine specific query.
|
||||
const { term, filters = {}, pageCursor = '' } = req.query;
|
||||
logger.info(
|
||||
`Search request received: ${term}, ${JSON.stringify(
|
||||
filters,
|
||||
)}, ${pageCursor}`,
|
||||
);
|
||||
|
||||
try {
|
||||
// TODO: Actually query search engine.
|
||||
// TODO: And actually transform results into frontend-readable result
|
||||
res.send({
|
||||
results: [],
|
||||
});
|
||||
} catch (err) {
|
||||
throw new Error(`There was a problem performing the search query.`);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
return router;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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 { createServiceBuilder } from '@backstage/backend-common';
|
||||
import { Server } from 'http';
|
||||
import { Logger } from 'winston';
|
||||
import { createRouter } from './router';
|
||||
|
||||
export interface ServerOptions {
|
||||
port: number;
|
||||
enableCors: boolean;
|
||||
logger: Logger;
|
||||
}
|
||||
|
||||
export async function startStandaloneServer(
|
||||
options: ServerOptions,
|
||||
): Promise<Server> {
|
||||
const logger = options.logger.child({ service: 'search-backend' });
|
||||
logger.debug('Starting application server...');
|
||||
const router = await createRouter({
|
||||
logger,
|
||||
});
|
||||
|
||||
const service = createServiceBuilder(module)
|
||||
.enableCors({ origin: 'http://localhost:3000' })
|
||||
.addRouter('/search', router);
|
||||
|
||||
return await service.start().catch(err => {
|
||||
logger.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
|
||||
module.hot?.accept();
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2020 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 {};
|
||||
@@ -2,8 +2,22 @@
|
||||
|
||||
**This plugin is still under development.**
|
||||
|
||||
You can follow the progress under the Global search in Backstage [milestone](https://github.com/backstage/backstage/milestone/21) or reach out to us in the #search Discord channel.
|
||||
You can follow the progress and contribute at the Backstage [Search Project Board](https://github.com/backstage/backstage/projects/6) or reach out to us in the [`#search` Discord channel](https://discord.com/channels/687207715902193673/770283289327566848).
|
||||
|
||||
## Getting started
|
||||
|
||||
Run `yarn start` in the root directory, and then navigate to [/search](http://localhost:3000/search)to check out the plugin.
|
||||
Run `yarn start` in the root directory, and then navigate to [/search](http://localhost:3000/search) to check out the plugin.
|
||||
|
||||
### Working on the search platform
|
||||
|
||||
The above search experience is 100% client-side and only looks at the Software Catalog. We are actively developing [a more complete search platform](https://backstage.io/docs/features/search/search-overview), which will replace the current experience when ready.
|
||||
|
||||
In order to work on this new search platform, you will need to be aware of the following:
|
||||
|
||||
- **In-development app search route**: The new search platform will move the primary search page to the App-level, out of the search plugin. For now, to ensure the old experience is still easy to test, you can work on the new platform at `/search-next` instead of `/search`.
|
||||
- **App SearchPage Component**: You'll find a new search page under `/packages/app/src/components/search`. When ready, we'll add an equivalent page to the `@backstage/create-app` template.
|
||||
- **Backend**: Don't forget, a lot of functionality will be made available in backend plugins:
|
||||
- `@backstage/plugin-search-backend-node`, which is responsible for the search index management
|
||||
- `@backstage/plugin-search-backend`, which is responsible for query processing
|
||||
|
||||
As you work, be sure not to break the existing, frontend-only search page.
|
||||
|
||||
@@ -32,6 +32,8 @@
|
||||
"@backstage/core": "^0.7.2",
|
||||
"@backstage/catalog-model": "^0.7.3",
|
||||
"@backstage/plugin-catalog-react": "^0.1.2",
|
||||
"@backstage/plugin-search-backend": "^0.1.1",
|
||||
"@backstage/search-common": "^0.1.1",
|
||||
"@backstage/theme": "^0.2.4",
|
||||
"@material-ui/core": "^4.11.0",
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
|
||||
@@ -14,8 +14,17 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { createApiRef, DiscoveryApi } from '@backstage/core';
|
||||
import { Entity, ENTITY_DEFAULT_NAMESPACE } from '@backstage/catalog-model';
|
||||
|
||||
import { CatalogApi } from '@backstage/plugin-catalog-react';
|
||||
import { SearchQuery, SearchResultSet } from '@backstage/search-common';
|
||||
import qs from 'qs';
|
||||
|
||||
export const searchApiRef = createApiRef<SearchApi>({
|
||||
id: 'plugin.search.queryservice',
|
||||
description: 'Used to make requests against the search API',
|
||||
});
|
||||
|
||||
export type Result = {
|
||||
name: string;
|
||||
@@ -28,11 +37,18 @@ export type Result = {
|
||||
|
||||
export type SearchResults = Array<Result>;
|
||||
|
||||
class SearchApi {
|
||||
private catalogApi: CatalogApi;
|
||||
export interface SearchApi {
|
||||
getSearchResult(): Promise<SearchResults>;
|
||||
_alphaPerformSearch(query: SearchQuery): Promise<SearchResultSet>;
|
||||
}
|
||||
|
||||
constructor(catalogApi: CatalogApi) {
|
||||
this.catalogApi = catalogApi;
|
||||
export class SearchClient implements SearchApi {
|
||||
private readonly catalogApi: CatalogApi;
|
||||
private readonly discoveryApi: DiscoveryApi;
|
||||
|
||||
constructor(options: { catalogApi: CatalogApi; discoveryApi: DiscoveryApi }) {
|
||||
this.catalogApi = options.catalogApi;
|
||||
this.discoveryApi = options.discoveryApi;
|
||||
}
|
||||
|
||||
private async entities() {
|
||||
@@ -53,9 +69,17 @@ class SearchApi {
|
||||
}));
|
||||
}
|
||||
|
||||
public getSearchResult(): Promise<SearchResults> {
|
||||
getSearchResult(): Promise<SearchResults> {
|
||||
return this.entities();
|
||||
}
|
||||
}
|
||||
|
||||
export default SearchApi;
|
||||
// TODO: Productionalize as we implement search milestones.
|
||||
async _alphaPerformSearch(query: SearchQuery): Promise<SearchResultSet> {
|
||||
const queryString = qs.stringify(query);
|
||||
const url = `${await this.discoveryApi.getBaseUrl(
|
||||
'search/query',
|
||||
)}?${queryString}`;
|
||||
const response = await fetch(url);
|
||||
return response.json();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright 2020 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 {
|
||||
Content,
|
||||
Header,
|
||||
Lifecycle,
|
||||
Page,
|
||||
useQueryParamState,
|
||||
} from '@backstage/core';
|
||||
import { Grid } from '@material-ui/core';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useDebounce } from 'react-use';
|
||||
import { SearchBar } from '../SearchBar';
|
||||
import { SearchResult } from '../SearchResult';
|
||||
|
||||
export const SearchPageNext = () => {
|
||||
const [queryString, setQueryString] = useQueryParamState<string>('query');
|
||||
const [searchQuery, setSearchQuery] = useState(queryString ?? '');
|
||||
|
||||
const handleSearch = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
event.preventDefault();
|
||||
setSearchQuery(event.target.value);
|
||||
};
|
||||
|
||||
useEffect(() => setSearchQuery(queryString ?? ''), [queryString]);
|
||||
|
||||
useDebounce(
|
||||
() => {
|
||||
setQueryString(searchQuery);
|
||||
},
|
||||
200,
|
||||
[searchQuery],
|
||||
);
|
||||
|
||||
const handleClearSearchBar = () => {
|
||||
setSearchQuery('');
|
||||
};
|
||||
|
||||
return (
|
||||
<Page themeId="home">
|
||||
<Header title="Search" subtitle={<Lifecycle alpha />} />
|
||||
<Content>
|
||||
<Grid container direction="row">
|
||||
<Grid item xs={12}>
|
||||
<SearchBar
|
||||
handleSearch={handleSearch}
|
||||
handleClearSearchBar={handleClearSearchBar}
|
||||
searchQuery={searchQuery}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<SearchResult searchQuery={(queryString ?? '').toLowerCase()} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Content>
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2020 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 { SearchPageNext } from './SearchPageNext';
|
||||
@@ -21,12 +21,12 @@ import {
|
||||
TableColumn,
|
||||
useApi,
|
||||
} from '@backstage/core';
|
||||
import { catalogApiRef } from '@backstage/plugin-catalog-react';
|
||||
import { Divider, Grid, makeStyles, Typography } from '@material-ui/core';
|
||||
import { Alert } from '@material-ui/lab';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useAsync } from 'react-use';
|
||||
import SearchApi, { Result, SearchResults } from '../../apis';
|
||||
import { Result, SearchResults, searchApiRef } from '../../apis';
|
||||
|
||||
import { Filters, FiltersButton, FiltersState } from '../Filters';
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
@@ -116,7 +116,7 @@ const TableHeader = ({
|
||||
};
|
||||
|
||||
export const SearchResult = ({ searchQuery }: SearchResultProps) => {
|
||||
const catalogApi = useApi(catalogApiRef);
|
||||
const searchApi = useApi(searchApiRef);
|
||||
|
||||
const [showFilters, toggleFilters] = useState(false);
|
||||
const [selectedFilters, setSelectedFilters] = useState<FiltersState>({
|
||||
@@ -126,8 +126,6 @@ export const SearchResult = ({ searchQuery }: SearchResultProps) => {
|
||||
|
||||
const [filteredResults, setFilteredResults] = useState<SearchResults>([]);
|
||||
|
||||
const searchApi = new SearchApi(catalogApi);
|
||||
|
||||
const { loading, error, value: results } = useAsync(() => {
|
||||
return searchApi.getSearchResult();
|
||||
}, []);
|
||||
|
||||
@@ -17,5 +17,6 @@
|
||||
export * from './Filters';
|
||||
export * from './SearchBar';
|
||||
export * from './SearchPage';
|
||||
export * from './SearchPageNext';
|
||||
export * from './SearchResult';
|
||||
export * from './SidebarSearch';
|
||||
|
||||
@@ -13,7 +13,14 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
export { searchPlugin, searchPlugin as plugin, SearchPage } from './plugin';
|
||||
|
||||
// TODO: export searchApiRef from ./apis once interface is stable and settled.
|
||||
export {
|
||||
searchPlugin,
|
||||
searchPlugin as plugin,
|
||||
SearchPage,
|
||||
SearchPageNext,
|
||||
} from './plugin';
|
||||
export {
|
||||
Filters,
|
||||
FiltersButton,
|
||||
|
||||
@@ -14,24 +14,45 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import {
|
||||
createApiFactory,
|
||||
createPlugin,
|
||||
createRouteRef,
|
||||
createRoutableExtension,
|
||||
discoveryApiRef,
|
||||
} from '@backstage/core';
|
||||
import { SearchClient, searchApiRef } from './apis';
|
||||
import { catalogApiRef } from '@backstage/plugin-catalog-react';
|
||||
import { SearchPage as SearchPageComponent } from './components/SearchPage';
|
||||
import { SearchPageNext as SearchPageNextComponent } from './components/SearchPageNext';
|
||||
|
||||
export const rootRouteRef = createRouteRef({
|
||||
path: '/search',
|
||||
title: 'search',
|
||||
});
|
||||
|
||||
export const rootNextRouteRef = createRouteRef({
|
||||
path: '/search-next',
|
||||
title: 'search',
|
||||
});
|
||||
|
||||
export const searchPlugin = createPlugin({
|
||||
id: 'search',
|
||||
apis: [
|
||||
createApiFactory({
|
||||
api: searchApiRef,
|
||||
deps: { catalogApi: catalogApiRef, discoveryApi: discoveryApiRef },
|
||||
factory: ({ catalogApi, discoveryApi }) => {
|
||||
return new SearchClient({ catalogApi, discoveryApi });
|
||||
},
|
||||
}),
|
||||
],
|
||||
register({ router }) {
|
||||
router.addRoute(rootRouteRef, SearchPageComponent);
|
||||
router.addRoute(rootNextRouteRef, SearchPageNextComponent);
|
||||
},
|
||||
routes: {
|
||||
root: rootRouteRef,
|
||||
nextRoot: rootNextRouteRef,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -41,3 +62,11 @@ export const SearchPage = searchPlugin.provide(
|
||||
mountPoint: rootRouteRef,
|
||||
}),
|
||||
);
|
||||
|
||||
export const SearchPageNext = searchPlugin.provide(
|
||||
createRoutableExtension({
|
||||
component: () =>
|
||||
import('./components/SearchPageNext').then(m => m.SearchPageNext),
|
||||
mountPoint: rootNextRouteRef,
|
||||
}),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user