diff --git a/.changeset/funny-pots-hide.md b/.changeset/funny-pots-hide.md new file mode 100644 index 0000000000..32388a93a5 --- /dev/null +++ b/.changeset/funny-pots-hide.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-search-backend': minor +--- + +Exports search plugin that can be used with the new backend system. For documentation on how to migrate, check out the [how to migrate to the new backend system guide](../docs/features/search/how-to-guides.md#how-to-migrate-to-use-search-together-with-the-new-backend-system). diff --git a/.changeset/giant-hairs-serve.md b/.changeset/giant-hairs-serve.md new file mode 100644 index 0000000000..0f77a6fe34 --- /dev/null +++ b/.changeset/giant-hairs-serve.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-search-backend-module-elasticsearch': minor +'@backstage/plugin-search-backend-module-pg': patch +--- + +Search backend modules migrated to the new backend system. For documentation on how to migrate, check out the [how to migrate to the new backend system guide](../docs/features/search/how-to-guides.md#how-to-migrate-to-use-search-together-with-the-new-backend-system). diff --git a/.changeset/tall-chairs-explain.md b/.changeset/tall-chairs-explain.md new file mode 100644 index 0000000000..9709f225b4 --- /dev/null +++ b/.changeset/tall-chairs-explain.md @@ -0,0 +1,7 @@ +--- +'@backstage/plugin-techdocs-backend': patch +'@backstage/plugin-catalog-backend': patch +'@backstage/plugin-explore-backend': patch +--- + +Collator factories instantiated in new backend system modules and now marked as deprecated. Will be continued to be exported publicly until the new backend system is fully rolled out. diff --git a/.changeset/warm-buses-switch.md b/.changeset/warm-buses-switch.md new file mode 100644 index 0000000000..5564f33d67 --- /dev/null +++ b/.changeset/warm-buses-switch.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-search-backend-node': minor +--- + +Exports services and extension points that can be used with the new backend system. For documentation on how to migrate, check out the [how to migrate to the new backend system guide](../docs/features/search/how-to-guides.md#how-to-migrate-to-use-search-together-with-the-new-backend-system). diff --git a/.changeset/wet-lamps-happen.md b/.changeset/wet-lamps-happen.md new file mode 100644 index 0000000000..066b34b23c --- /dev/null +++ b/.changeset/wet-lamps-happen.md @@ -0,0 +1,7 @@ +--- +'@backstage/plugin-search-backend-module-techdocs': minor +'@backstage/plugin-search-backend-module-catalog': minor +'@backstage/plugin-search-backend-module-explore': minor +--- + +Package introduced to export search backend modules that can be used with the new backend system to extend search with plugin specific functionality, such as collators. For documentation on how to migrate, check out the [how to migrate to the new backend system guide](../docs/features/search/how-to-guides.md#how-to-migrate-to-use-search-together-with-the-new-backend-system). diff --git a/docs/features/search/how-to-guides.md b/docs/features/search/how-to-guides.md index 5b28e259b5..abb29ab4eb 100644 --- a/docs/features/search/how-to-guides.md +++ b/docs/features/search/how-to-guides.md @@ -51,7 +51,7 @@ The TechDocs plugin has supported integrations to Search, meaning that it provides a default collator factory ready to be used. The purpose of this guide is to walk you through how to register the -[DefaultTechDocsCollatorFactory](https://github.com/backstage/backstage/blob/master/plugins/techdocs-backend/src/search/DefaultTechDocsCollatorFactory.ts) +[DefaultTechDocsCollatorFactory](https://github.com/backstage/backstage/blob/de294ce5c410c9eb56da6870a1fab795268f60e3/plugins/techdocs-backend/src/search/DefaultTechDocsCollatorFactory.ts) in your App, so that you can get TechDocs documents indexed. If you have been through the @@ -369,3 +369,31 @@ export const SearchModal = ({ toggleModal }: { toggleModal: () => void }) => ( ``` There are other more specific search results layout components that also accept result item extensions, check their documentation: [SearchResultList](https://backstage.io/storybook/?path=/story/plugins-search-searchresultlist--with-result-item-extensions) and [SearchResultGroup](https://backstage.io/storybook/?path=/story/plugins-search-searchresultgroup--with-result-item-extensions). + +## How to migrate your backend installation to use Search together with the new backend system + +> DISCLAIMER: The new backend system is in alpha, and so are the search backend support for the new backend system. We don't recommend you to migrate your backend installations to the new system yet. But if you want to experiment, this is the guide for you! + +Recently, the Backstage maintainers [announced the new Backend System](https://backstage.io/blog/2023/02/15/backend-system-alpha). The search plugins are now migrated to support the new backend system. In this guide you will learn how to update your backend set up. + +In packages/backend-next/index.ts + +```ts +import { searchPlugin } from '@backstage/plugin-search-backend/alpha'; +import { searchModuleElasticsearchEngine } from '@backstage/plugin-search-backend-module-elasticsearch/alpha'; +import { searchModuleCatalogCollator } from '@backstage/plugin-search-backend-module-catalog/alpha'; +import { searchModuleTechDocsCollator } from '@backstage/plugin-search-backend-module-techdocs/alpha'; +import { searchModuleExploreCollator } from '@backstage/plugin-search-backend-module-explore/alpha'; + +const backend = createBackend(); +// adding the search plugin to the backend +backend.add(searchPlugin()); +// (optional) the default search engine is Lunr, if you want to extend the search backend with another search engine. +backend.add(searchModuleElasticsearchEngine()); +// extending search with collator modules to start index documents, take in optional schedule parameters. +backend.add(searchModuleCatalogCollator()); +backend.add(searchModuleTechDocsCollator()); +backend.add(searchModuleExploreCollator()); + +backend.start(); +``` diff --git a/docs/plugins/integrating-search-into-plugins.md b/docs/plugins/integrating-search-into-plugins.md index 4e829b6ae9..7fb8e40b20 100644 --- a/docs/plugins/integrating-search-into-plugins.md +++ b/docs/plugins/integrating-search-into-plugins.md @@ -142,7 +142,7 @@ export class FaqCollatorFactory implements DocumentCollatorFactory { To verify your implementation works as expected make sure to add tests for it. For your convenience, there is the [`TestPipeline`](https://backstage.io/docs/reference/plugin-search-backend-node.testpipeline) utility that emulates a pipeline into which you can integrate your custom collator. -Look at [DefaultTechDocsCollatorFactory test](https://github.com/backstage/backstage/blob/master/plugins/techdocs-backend/src/search/DefaultTechDocsCollatorFactory.test.ts), for an example. +Look at [DefaultTechDocsCollatorFactory test](https://github.com/backstage/backstage/blob/de294ce5c410c9eb56da6870a1fab795268f60e3/plugins/techdocs-backend/src/search/DefaultTechDocsCollatorFactory.test.ts), for an example. #### 6. Make your plugins collator discoverable for others diff --git a/packages/backend-next/package.json b/packages/backend-next/package.json index 8c40565737..bed4f10975 100644 --- a/packages/backend-next/package.json +++ b/packages/backend-next/package.json @@ -29,6 +29,11 @@ "@backstage/plugin-app-backend": "workspace:^", "@backstage/plugin-catalog-backend": "workspace:^", "@backstage/plugin-scaffolder-backend": "workspace:^", + "@backstage/plugin-search-backend": "workspace:^", + "@backstage/plugin-search-backend-module-catalog": "workspace:^", + "@backstage/plugin-search-backend-module-explore": "workspace:^", + "@backstage/plugin-search-backend-module-techdocs": "workspace:^", + "@backstage/plugin-search-backend-node": "workspace:^", "@backstage/plugin-techdocs-backend": "workspace:^", "@backstage/plugin-todo-backend": "workspace:^" }, diff --git a/packages/backend-next/src/index.ts b/packages/backend-next/src/index.ts index 8f8984b192..e3cbbd53b8 100644 --- a/packages/backend-next/src/index.ts +++ b/packages/backend-next/src/index.ts @@ -14,18 +14,35 @@ * limitations under the License. */ -import { catalogPlugin } from '@backstage/plugin-catalog-backend/alpha'; -import { catalogModuleTemplateKind } from '@backstage/plugin-scaffolder-backend/alpha'; import { createBackend } from '@backstage/backend-defaults'; import { appPlugin } from '@backstage/plugin-app-backend/alpha'; import { todoPlugin } from '@backstage/plugin-todo-backend'; import { techdocsPlugin } from '@backstage/plugin-techdocs-backend/alpha'; +import { catalogPlugin } from '@backstage/plugin-catalog-backend/alpha'; +import { catalogModuleTemplateKind } from '@backstage/plugin-scaffolder-backend/alpha'; +import { searchPlugin } from '@backstage/plugin-search-backend/alpha'; +import { searchModuleCatalogCollator } from '@backstage/plugin-search-backend-module-catalog/alpha'; +import { searchModuleTechDocsCollator } from '@backstage/plugin-search-backend-module-techdocs/alpha'; +import { searchModuleExploreCollator } from '@backstage/plugin-search-backend-module-explore/alpha'; const backend = createBackend(); +backend.add(appPlugin({ appPackageName: 'example-app' })); + +// Todo +backend.add(todoPlugin()); + +// Techdocs +backend.add(techdocsPlugin()); + +// Catalog backend.add(catalogPlugin()); backend.add(catalogModuleTemplateKind()); -backend.add(appPlugin({ appPackageName: 'example-app' })); -backend.add(todoPlugin()); -backend.add(techdocsPlugin()); + +// Search +backend.add(searchPlugin()); +backend.add(searchModuleCatalogCollator()); +backend.add(searchModuleTechDocsCollator()); +backend.add(searchModuleExploreCollator()); + backend.start(); diff --git a/plugins/catalog-backend/api-report.md b/plugins/catalog-backend/api-report.md index 5e7b2dee02..d491f391d5 100644 --- a/plugins/catalog-backend/api-report.md +++ b/plugins/catalog-backend/api-report.md @@ -11,6 +11,7 @@ import { AnalyzeLocationGenerateEntity as AnalyzeLocationGenerateEntity_2 } from import { AnalyzeLocationRequest as AnalyzeLocationRequest_2 } from '@backstage/plugin-catalog-common'; import { AnalyzeLocationResponse as AnalyzeLocationResponse_2 } from '@backstage/plugin-catalog-common'; import { CatalogApi } from '@backstage/catalog-client'; +import type { CatalogCollatorEntityTransformer as CatalogCollatorEntityTransformer_2 } from '@backstage/plugin-search-backend-module-catalog'; import { CatalogEntityDocument } from '@backstage/plugin-catalog-common'; import { CatalogProcessor as CatalogProcessor_2 } from '@backstage/plugin-catalog-node'; import { CatalogProcessorCache as CatalogProcessorCache_2 } from '@backstage/plugin-catalog-node'; @@ -23,8 +24,9 @@ import { CatalogProcessorRefreshKeysResult as CatalogProcessorRefreshKeysResult_ import { CatalogProcessorRelationResult as CatalogProcessorRelationResult_2 } from '@backstage/plugin-catalog-node'; import { CatalogProcessorResult as CatalogProcessorResult_2 } from '@backstage/plugin-catalog-node'; import { Config } from '@backstage/config'; +import { DefaultCatalogCollatorFactory as DefaultCatalogCollatorFactory_2 } from '@backstage/plugin-search-backend-module-catalog'; +import type { DefaultCatalogCollatorFactoryOptions as DefaultCatalogCollatorFactoryOptions_2 } from '@backstage/plugin-search-backend-module-catalog'; import { DeferredEntity as DeferredEntity_2 } from '@backstage/plugin-catalog-node'; -import { DocumentCollatorFactory } from '@backstage/plugin-search-common'; import { Entity } from '@backstage/catalog-model'; import { EntityPolicy } from '@backstage/catalog-model'; import { EntityProvider as EntityProvider_2 } from '@backstage/plugin-catalog-node'; @@ -44,7 +46,6 @@ import { PermissionRule } from '@backstage/plugin-permission-node'; import { PermissionRuleParams } from '@backstage/plugin-permission-common'; import { PluginDatabaseManager } from '@backstage/backend-common'; import { PluginEndpointDiscovery } from '@backstage/backend-common'; -import { Readable } from 'stream'; import { Router } from 'express'; import { ScmIntegrationRegistry } from '@backstage/integration'; import { TokenManager } from '@backstage/backend-common'; @@ -160,10 +161,9 @@ export class CatalogBuilder { useLegacySingleProcessorValidation(): this; } -// @public (undocumented) -export type CatalogCollatorEntityTransformer = ( - entity: Entity, -) => Omit; +// @public @deprecated (undocumented) +export type CatalogCollatorEntityTransformer = + CatalogCollatorEntityTransformer_2; // @public (undocumented) export type CatalogEnvironment = { @@ -286,34 +286,15 @@ export class DefaultCatalogCollator { readonly visibilityPermission: Permission; } -// @public (undocumented) -export const defaultCatalogCollatorEntityTransformer: CatalogCollatorEntityTransformer; +// @public @deprecated (undocumented) +export const defaultCatalogCollatorEntityTransformer: CatalogCollatorEntityTransformer_2; -// @public (undocumented) -export class DefaultCatalogCollatorFactory implements DocumentCollatorFactory { - // (undocumented) - static fromConfig( - _config: Config, - options: DefaultCatalogCollatorFactoryOptions, - ): DefaultCatalogCollatorFactory; - // (undocumented) - getCollator(): Promise; - // (undocumented) - readonly type = 'software-catalog'; - // (undocumented) - readonly visibilityPermission: Permission; -} +// @public @deprecated (undocumented) +export const DefaultCatalogCollatorFactory: typeof DefaultCatalogCollatorFactory_2; -// @public (undocumented) -export type DefaultCatalogCollatorFactoryOptions = { - discovery: PluginEndpointDiscovery; - tokenManager: TokenManager; - locationTemplate?: string; - filter?: GetEntitiesRequest['filter']; - batchSize?: number; - catalogClient?: CatalogApi; - entityTransformer?: CatalogCollatorEntityTransformer; -}; +// @public @deprecated (undocumented) +export type DefaultCatalogCollatorFactoryOptions = + DefaultCatalogCollatorFactoryOptions_2; // @public @deprecated (undocumented) export type DeferredEntity = DeferredEntity_2; diff --git a/plugins/catalog-backend/package.json b/plugins/catalog-backend/package.json index b60f03e4cd..023d709d81 100644 --- a/plugins/catalog-backend/package.json +++ b/plugins/catalog-backend/package.json @@ -57,6 +57,7 @@ "@backstage/plugin-permission-common": "workspace:^", "@backstage/plugin-permission-node": "workspace:^", "@backstage/plugin-scaffolder-common": "workspace:^", + "@backstage/plugin-search-backend-module-catalog": "workspace:^", "@backstage/plugin-search-common": "workspace:^", "@backstage/types": "workspace:^", "@opentelemetry/api": "^1.3.0", diff --git a/plugins/catalog-backend/src/index.ts b/plugins/catalog-backend/src/index.ts index b3d2bc95c7..eca155ce46 100644 --- a/plugins/catalog-backend/src/index.ts +++ b/plugins/catalog-backend/src/index.ts @@ -27,3 +27,40 @@ export * from './processing'; export * from './search'; export * from './service'; export * from './deprecated'; + +import { + DefaultCatalogCollatorFactory as _DefaultCatalogCollatorFactory, + defaultCatalogCollatorEntityTransformer as _defaultCatalogCollatorEntityTransformer, +} from '@backstage/plugin-search-backend-module-catalog'; + +/** + * @public + * @deprecated import from `@backstage/search-backend-module-catalog` instead + */ +export const DefaultCatalogCollatorFactory = _DefaultCatalogCollatorFactory; + +/** + * @public + * @deprecated import from `@backstage/search-backend-module-catalog` instead + */ +export const defaultCatalogCollatorEntityTransformer = + _defaultCatalogCollatorEntityTransformer; + +import type { + DefaultCatalogCollatorFactoryOptions as _DefaultCatalogCollatorFactoryOptions, + CatalogCollatorEntityTransformer as _CatalogCollatorEntityTransformer, +} from '@backstage/plugin-search-backend-module-catalog'; + +/** + * @public + * @deprecated import from `@backstage/search-backend-module-catalog` instead + */ +export type DefaultCatalogCollatorFactoryOptions = + _DefaultCatalogCollatorFactoryOptions; + +/** + * @public + * @deprecated import from `@backstage/search-backend-module-catalog` instead + */ +export type CatalogCollatorEntityTransformer = + _CatalogCollatorEntityTransformer; diff --git a/plugins/catalog-backend/src/search/index.ts b/plugins/catalog-backend/src/search/index.ts index 3603ab2e60..50966e4c5b 100644 --- a/plugins/catalog-backend/src/search/index.ts +++ b/plugins/catalog-backend/src/search/index.ts @@ -14,11 +14,6 @@ * limitations under the License. */ -export { DefaultCatalogCollatorFactory } from './DefaultCatalogCollatorFactory'; -export type { DefaultCatalogCollatorFactoryOptions } from './DefaultCatalogCollatorFactory'; -export type { CatalogCollatorEntityTransformer } from './CatalogCollatorEntityTransformer'; -export { defaultCatalogCollatorEntityTransformer } from './defaultCatalogCollatorEntityTransformer'; - /** * todo(backstage/techdocs-core): stop exporting this in a future release. */ diff --git a/plugins/explore-backend/api-report.md b/plugins/explore-backend/api-report.md index cc112ed3cc..e3933e350b 100644 --- a/plugins/explore-backend/api-report.md +++ b/plugins/explore-backend/api-report.md @@ -3,18 +3,14 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -/// - -import { Config } from '@backstage/config'; -import { DocumentCollatorFactory } from '@backstage/plugin-search-common'; import { ExploreTool } from '@backstage/plugin-explore-common'; import express from 'express'; import { GetExploreToolsRequest } from '@backstage/plugin-explore-common'; import { GetExploreToolsResponse } from '@backstage/plugin-explore-common'; -import { IndexableDocument } from '@backstage/plugin-search-common'; import { Logger } from 'winston'; -import { PluginEndpointDiscovery } from '@backstage/backend-common'; -import { Readable } from 'stream'; +import type { ToolDocument as ToolDocument_2 } from '@backstage/plugin-search-backend-module-explore'; +import { ToolDocumentCollatorFactory as ToolDocumentCollatorFactory_2 } from '@backstage/plugin-search-backend-module-explore'; +import type { ToolDocumentCollatorFactoryOptions as ToolDocumentCollatorFactoryOptions_2 } from '@backstage/plugin-search-backend-module-explore'; // @public (undocumented) export function createRouter(options: RouterOptions): Promise; @@ -40,27 +36,13 @@ export class StaticExploreToolProvider implements ExploreToolProvider { getTools(request: GetExploreToolsRequest): Promise; } -// @public -export interface ToolDocument extends IndexableDocument, ExploreTool {} +// @public @deprecated (undocumented) +export type ToolDocument = ToolDocument_2; -// @public -export class ToolDocumentCollatorFactory implements DocumentCollatorFactory { - // (undocumented) - execute(): AsyncGenerator; - // (undocumented) - static fromConfig( - _config: Config, - options: ToolDocumentCollatorFactoryOptions, - ): ToolDocumentCollatorFactory; - // (undocumented) - getCollator(): Promise; - // (undocumented) - readonly type: string; -} +// @public @deprecated (undocumented) +export const ToolDocumentCollatorFactory: typeof ToolDocumentCollatorFactory_2; -// @public -export type ToolDocumentCollatorFactoryOptions = { - discovery: PluginEndpointDiscovery; - logger: Logger; -}; +// @public @deprecated (undocumented) +export type ToolDocumentCollatorFactoryOptions = + ToolDocumentCollatorFactoryOptions_2; ``` diff --git a/plugins/explore-backend/package.json b/plugins/explore-backend/package.json index 2302b6dd0f..30ab27bf07 100644 --- a/plugins/explore-backend/package.json +++ b/plugins/explore-backend/package.json @@ -25,6 +25,7 @@ "@backstage/backend-common": "workspace:^", "@backstage/config": "workspace:^", "@backstage/plugin-explore-common": "workspace:^", + "@backstage/plugin-search-backend-module-explore": "workspace:^", "@backstage/plugin-search-common": "workspace:^", "@types/express": "*", "express": "^4.18.1", diff --git a/plugins/explore-backend/src/index.ts b/plugins/explore-backend/src/index.ts index a0e7216621..ca501db96b 100644 --- a/plugins/explore-backend/src/index.ts +++ b/plugins/explore-backend/src/index.ts @@ -20,7 +20,6 @@ * @packageDocumentation */ -export * from './search'; export * from './service'; export * from './tools'; @@ -28,3 +27,28 @@ export * from './tools'; * @internal Example only - do not use in production */ export { exampleTools } from './example/exampleTools'; + +import { ToolDocumentCollatorFactory as _ToolDocumentCollatorFactory } from '@backstage/plugin-search-backend-module-explore'; +import type { + ToolDocument as _ToolDocument, + ToolDocumentCollatorFactoryOptions as _ToolDocumentCollatorFactoryOptions, +} from '@backstage/plugin-search-backend-module-explore'; + +/** + * @public + * @deprecated import from `@backstage/search-backend-module-explore` instead + */ +export const ToolDocumentCollatorFactory = _ToolDocumentCollatorFactory; + +/** + * @public + * @deprecated import from `@backstage/search-backend-module-explore` instead + */ +export type ToolDocument = _ToolDocument; + +/** + * @public + * @deprecated import from `@backstage/search-backend-module-explore` instead + */ +export type ToolDocumentCollatorFactoryOptions = + _ToolDocumentCollatorFactoryOptions; diff --git a/plugins/search-backend-module-catalog/.eslintrc.js b/plugins/search-backend-module-catalog/.eslintrc.js new file mode 100644 index 0000000000..e2a53a6ad2 --- /dev/null +++ b/plugins/search-backend-module-catalog/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/plugins/search-backend-module-catalog/README.md b/plugins/search-backend-module-catalog/README.md new file mode 100644 index 0000000000..b5960e2789 --- /dev/null +++ b/plugins/search-backend-module-catalog/README.md @@ -0,0 +1,41 @@ +# search-backend-module-catalog + +> DISCLAIMER: The new backend system is in alpha, and so are the search backend module support for the new backend system. We don't recommend you to migrate your backend installations to the new system yet. But if you want to experiment, you can find getting started guides below. + +This package exports catalog backend modules responsible for extending search. + +## Example + +### Use default schedule + +```tsx +// packages/backend-next/src/index.ts +import { createBackend } from '@backstage/backend-defaults'; +import { searchPlugin } from '@backstage/plugin-search-backend/alpha'; +import { searchModuleCatalogCollator } from '@backstage/plugin-search-backend-module-catalog/alpha'; + +const backend = createBackend(); +backend.add(searchPlugin()); +backend.add(searchModuleCatalogCollator()); +backend.start(); +``` + +### Use custom schedule + +```tsx +// packages/backend-next/src/index.ts +import { createBackend } from '@backstage/backend-defaults'; +import { searchPlugin } from '@backstage/plugin-search-backend/alpha'; +import { searchModuleCatalogCollator } from '@backstage/plugin-search-backend-module-catalog/alpha'; + +const schedule = { + frequency: { minutes: 10 }, + timeout: { minutes: 15 }, + initialDelay: { seconds: 3 }, +}; + +const backend = createBackend(); +backend.add(searchPlugin()); +backend.add(searchModuleCatalogCollator({ schedule })); +backend.start(); +``` diff --git a/plugins/search-backend-module-catalog/alpha-api-report.md b/plugins/search-backend-module-catalog/alpha-api-report.md new file mode 100644 index 0000000000..70fae9ef93 --- /dev/null +++ b/plugins/search-backend-module-catalog/alpha-api-report.md @@ -0,0 +1,24 @@ +## API Report File for "@backstage/plugin-search-backend-module-catalog" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +import { BackendFeature } from '@backstage/backend-plugin-api'; +import { DefaultCatalogCollatorFactoryOptions } from '@backstage/plugin-search-backend-module-catalog'; +import { TaskScheduleDefinition } from '@backstage/backend-tasks'; + +// @alpha +export const searchModuleCatalogCollator: ( + options?: SearchModuleCatalogCollatorOptions | undefined, +) => BackendFeature; + +// @alpha +export type SearchModuleCatalogCollatorOptions = Omit< + DefaultCatalogCollatorFactoryOptions, + 'discovery' | 'tokenManager' +> & { + schedule?: TaskScheduleDefinition; +}; + +// (No @packageDocumentation comment for this package) +``` diff --git a/plugins/search-backend-module-catalog/api-report.md b/plugins/search-backend-module-catalog/api-report.md new file mode 100644 index 0000000000..b773860fe8 --- /dev/null +++ b/plugins/search-backend-module-catalog/api-report.md @@ -0,0 +1,52 @@ +## API Report File for "@backstage/plugin-search-backend-module-catalog" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +/// + +import { CatalogApi } from '@backstage/catalog-client'; +import { CatalogEntityDocument } from '@backstage/plugin-catalog-common'; +import { Config } from '@backstage/config'; +import { DocumentCollatorFactory } from '@backstage/plugin-search-common'; +import { Entity } from '@backstage/catalog-model'; +import { GetEntitiesRequest } from '@backstage/catalog-client'; +import { Permission } from '@backstage/plugin-permission-common'; +import { PluginEndpointDiscovery } from '@backstage/backend-common'; +import { Readable } from 'stream'; +import { TokenManager } from '@backstage/backend-common'; + +// @public (undocumented) +export type CatalogCollatorEntityTransformer = ( + entity: Entity, +) => Omit; + +// @public (undocumented) +export const defaultCatalogCollatorEntityTransformer: CatalogCollatorEntityTransformer; + +// @public (undocumented) +export class DefaultCatalogCollatorFactory implements DocumentCollatorFactory { + // (undocumented) + static fromConfig( + _config: Config, + options: DefaultCatalogCollatorFactoryOptions, + ): DefaultCatalogCollatorFactory; + // (undocumented) + getCollator(): Promise; + // (undocumented) + readonly type = 'software-catalog'; + // (undocumented) + readonly visibilityPermission: Permission; +} + +// @public (undocumented) +export type DefaultCatalogCollatorFactoryOptions = { + discovery: PluginEndpointDiscovery; + tokenManager: TokenManager; + locationTemplate?: string; + filter?: GetEntitiesRequest['filter']; + batchSize?: number; + catalogClient?: CatalogApi; + entityTransformer?: CatalogCollatorEntityTransformer; +}; +``` diff --git a/plugins/search-backend-module-catalog/package.json b/plugins/search-backend-module-catalog/package.json new file mode 100644 index 0000000000..4c323ae7fd --- /dev/null +++ b/plugins/search-backend-module-catalog/package.json @@ -0,0 +1,59 @@ +{ + "name": "@backstage/plugin-search-backend-module-catalog", + "description": "A module for the search backend that exports catalog modules", + "version": "0.0.0", + "main": "src/index.ts", + "types": "src/index.ts", + "license": "Apache-2.0", + "publishConfig": { + "access": "public" + }, + "exports": { + ".": "./src/index.ts", + "./alpha": "./src/alpha.ts", + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "alpha": [ + "src/alpha.ts" + ], + "package.json": [ + "package.json" + ] + } + }, + "backstage": { + "role": "backend-plugin-module" + }, + "scripts": { + "start": "backstage-cli package start", + "build": "backstage-cli package build", + "lint": "backstage-cli package lint", + "test": "backstage-cli package test", + "prepack": "backstage-cli package prepack", + "postpack": "backstage-cli package postpack", + "clean": "backstage-cli package clean" + }, + "dependencies": { + "@backstage/backend-common": "workspace:^", + "@backstage/backend-plugin-api": "workspace:^", + "@backstage/backend-tasks": "workspace:^", + "@backstage/catalog-client": "workspace:^", + "@backstage/catalog-model": "workspace:^", + "@backstage/config": "workspace:^", + "@backstage/plugin-catalog-common": "workspace:^", + "@backstage/plugin-permission-common": "workspace:^", + "@backstage/plugin-search-backend-node": "workspace:^", + "@backstage/plugin-search-common": "workspace:^" + }, + "devDependencies": { + "@backstage/backend-common": "workspace:^", + "@backstage/backend-test-utils": "workspace:^", + "@backstage/cli": "workspace:^", + "msw": "^1.0.0" + }, + "files": [ + "dist" + ] +} diff --git a/plugins/search-backend-module-catalog/src/alpha.test.ts b/plugins/search-backend-module-catalog/src/alpha.test.ts new file mode 100644 index 0000000000..8a3082c6aa --- /dev/null +++ b/plugins/search-backend-module-catalog/src/alpha.test.ts @@ -0,0 +1,46 @@ +/* + * Copyright 2023 The Backstage Authors + * + * 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 { startTestBackend } from '@backstage/backend-test-utils'; +import { searchIndexRegistryExtensionPoint } from '@backstage/plugin-search-backend-node/alpha'; +import { searchModuleCatalogCollator } from './alpha'; + +describe('searchModuleCatalogCollator', () => { + const schedule = { + frequency: { minutes: 10 }, + timeout: { minutes: 15 }, + initialDelay: { seconds: 3 }, + }; + + it('should register the catalog collator to the search index registry extension point with factory and schedule', async () => { + const extensionPointMock = { + addCollator: jest.fn(), + }; + + await startTestBackend({ + extensionPoints: [ + [searchIndexRegistryExtensionPoint, extensionPointMock], + ], + features: [searchModuleCatalogCollator({ schedule })], + }); + + expect(extensionPointMock.addCollator).toHaveBeenCalledTimes(1); + expect(extensionPointMock.addCollator).toHaveBeenCalledWith({ + factory: expect.objectContaining({ type: 'software-catalog' }), + schedule: expect.objectContaining({ run: expect.any(Function) }), + }); + }); +}); diff --git a/plugins/search-backend-module-catalog/src/alpha.ts b/plugins/search-backend-module-catalog/src/alpha.ts new file mode 100644 index 0000000000..c633dc71b2 --- /dev/null +++ b/plugins/search-backend-module-catalog/src/alpha.ts @@ -0,0 +1,89 @@ +/* + * Copyright 2023 The Backstage Authors + * + * 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. + */ + +/** + * @packageDocumentation + * A module for the search backend that exports Catalog modules. + */ + +import { + coreServices, + createBackendModule, +} from '@backstage/backend-plugin-api'; +import { TaskScheduleDefinition } from '@backstage/backend-tasks'; +import { searchIndexRegistryExtensionPoint } from '@backstage/plugin-search-backend-node/alpha'; + +import { + DefaultCatalogCollatorFactory, + DefaultCatalogCollatorFactoryOptions, +} from '@backstage/plugin-search-backend-module-catalog'; + +/** + * @alpha + * Options for {@link searchModuleCatalogCollator}. + */ +export type SearchModuleCatalogCollatorOptions = Omit< + DefaultCatalogCollatorFactoryOptions, + 'discovery' | 'tokenManager' +> & { + schedule?: TaskScheduleDefinition; +}; + +/** + * @alpha + * Search backend module for the Catalog index. + */ +export const searchModuleCatalogCollator = createBackendModule( + (options?: SearchModuleCatalogCollatorOptions) => ({ + moduleId: 'catalogCollator', + pluginId: 'search', + register(env) { + env.registerInit({ + deps: { + config: coreServices.config, + discovery: coreServices.discovery, + tokenManager: coreServices.tokenManager, + scheduler: coreServices.scheduler, + indexRegistry: searchIndexRegistryExtensionPoint, + }, + async init({ + config, + discovery, + tokenManager, + scheduler, + indexRegistry, + }) { + const defaultSchedule = { + frequency: { minutes: 10 }, + timeout: { minutes: 15 }, + initialDelay: { seconds: 3 }, + }; + + indexRegistry.addCollator({ + schedule: scheduler.createScheduledTaskRunner( + options?.schedule ?? defaultSchedule, + ), + factory: DefaultCatalogCollatorFactory.fromConfig(config, { + ...options, + discovery, + tokenManager, + }), + }); + }, + }); + }, + }), +); diff --git a/plugins/catalog-backend/src/search/CatalogCollatorEntityTransformer.ts b/plugins/search-backend-module-catalog/src/collators/CatalogCollatorEntityTransformer.ts similarity index 100% rename from plugins/catalog-backend/src/search/CatalogCollatorEntityTransformer.ts rename to plugins/search-backend-module-catalog/src/collators/CatalogCollatorEntityTransformer.ts diff --git a/plugins/catalog-backend/src/search/DefaultCatalogCollatorFactory.test.ts b/plugins/search-backend-module-catalog/src/collators/DefaultCatalogCollatorFactory.test.ts similarity index 100% rename from plugins/catalog-backend/src/search/DefaultCatalogCollatorFactory.test.ts rename to plugins/search-backend-module-catalog/src/collators/DefaultCatalogCollatorFactory.test.ts diff --git a/plugins/catalog-backend/src/search/DefaultCatalogCollatorFactory.ts b/plugins/search-backend-module-catalog/src/collators/DefaultCatalogCollatorFactory.ts similarity index 100% rename from plugins/catalog-backend/src/search/DefaultCatalogCollatorFactory.ts rename to plugins/search-backend-module-catalog/src/collators/DefaultCatalogCollatorFactory.ts diff --git a/plugins/catalog-backend/src/search/defaultCatalogCollatorEntityTransformer.test.ts b/plugins/search-backend-module-catalog/src/collators/defaultCatalogCollatorEntityTransformer.test.ts similarity index 100% rename from plugins/catalog-backend/src/search/defaultCatalogCollatorEntityTransformer.test.ts rename to plugins/search-backend-module-catalog/src/collators/defaultCatalogCollatorEntityTransformer.test.ts diff --git a/plugins/catalog-backend/src/search/defaultCatalogCollatorEntityTransformer.ts b/plugins/search-backend-module-catalog/src/collators/defaultCatalogCollatorEntityTransformer.ts similarity index 100% rename from plugins/catalog-backend/src/search/defaultCatalogCollatorEntityTransformer.ts rename to plugins/search-backend-module-catalog/src/collators/defaultCatalogCollatorEntityTransformer.ts diff --git a/plugins/search-backend-module-catalog/src/collators/index.ts b/plugins/search-backend-module-catalog/src/collators/index.ts new file mode 100644 index 0000000000..5876e71a02 --- /dev/null +++ b/plugins/search-backend-module-catalog/src/collators/index.ts @@ -0,0 +1,21 @@ +/* + * Copyright 2023 The Backstage Authors + * + * 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 { DefaultCatalogCollatorFactory } from './DefaultCatalogCollatorFactory'; +export type { DefaultCatalogCollatorFactoryOptions } from './DefaultCatalogCollatorFactory'; + +export { defaultCatalogCollatorEntityTransformer } from './defaultCatalogCollatorEntityTransformer'; +export type { CatalogCollatorEntityTransformer } from './CatalogCollatorEntityTransformer'; diff --git a/plugins/search-backend-module-catalog/src/index.ts b/plugins/search-backend-module-catalog/src/index.ts new file mode 100644 index 0000000000..7998562311 --- /dev/null +++ b/plugins/search-backend-module-catalog/src/index.ts @@ -0,0 +1,22 @@ +/* + * Copyright 2023 The Backstage Authors + * + * 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. + */ + +/** + * @packageDocumentation + * A module for the search backend that exports Catalog modules. + */ + +export * from './collators'; diff --git a/plugins/search-backend-module-elasticsearch/alpha-api-report.md b/plugins/search-backend-module-elasticsearch/alpha-api-report.md new file mode 100644 index 0000000000..91007c20de --- /dev/null +++ b/plugins/search-backend-module-elasticsearch/alpha-api-report.md @@ -0,0 +1,22 @@ +## API Report File for "@backstage/plugin-search-backend-module-elasticsearch" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +import { BackendFeature } from '@backstage/backend-plugin-api'; +import { ElasticSearchCustomIndexTemplate } from '@backstage/plugin-search-backend-module-elasticsearch'; +import { ElasticSearchQueryTranslator } from '@backstage/plugin-search-backend-module-elasticsearch'; + +// @alpha +export const searchModuleElasticsearchEngine: ( + options?: SearchModuleElasticsearchEngineOptions | undefined, +) => BackendFeature; + +// @alpha +export type SearchModuleElasticsearchEngineOptions = { + translator?: ElasticSearchQueryTranslator; + indexTemplate?: ElasticSearchCustomIndexTemplate; +}; + +// (No @packageDocumentation comment for this package) +``` diff --git a/plugins/search-backend-module-elasticsearch/package.json b/plugins/search-backend-module-elasticsearch/package.json index 044d52e3fd..285ab4e2fe 100644 --- a/plugins/search-backend-module-elasticsearch/package.json +++ b/plugins/search-backend-module-elasticsearch/package.json @@ -6,9 +6,22 @@ "types": "src/index.ts", "license": "Apache-2.0", "publishConfig": { - "access": "public", - "main": "dist/index.cjs.js", - "types": "dist/index.d.ts" + "access": "public" + }, + "exports": { + ".": "./src/index.ts", + "./alpha": "./src/alpha.ts", + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "alpha": [ + "src/alpha.ts" + ], + "package.json": [ + "package.json" + ] + } }, "backstage": { "role": "backend-plugin-module" @@ -23,6 +36,8 @@ "clean": "backstage-cli package clean" }, "dependencies": { + "@backstage/backend-common": "workspace:^", + "@backstage/backend-plugin-api": "workspace:^", "@backstage/config": "workspace:^", "@backstage/plugin-search-backend-node": "workspace:^", "@backstage/plugin-search-common": "workspace:^", diff --git a/plugins/search-backend-module-elasticsearch/src/alpha.ts b/plugins/search-backend-module-elasticsearch/src/alpha.ts new file mode 100644 index 0000000000..538124f004 --- /dev/null +++ b/plugins/search-backend-module-elasticsearch/src/alpha.ts @@ -0,0 +1,74 @@ +/* + * Copyright 2023 The Backstage Authors + * + * 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 { + coreServices, + createBackendModule, +} from '@backstage/backend-plugin-api'; +import { loggerToWinstonLogger } from '@backstage/backend-common'; +import { searchEngineRegistryExtensionPoint } from '@backstage/plugin-search-backend-node/alpha'; + +import { + ElasticSearchCustomIndexTemplate, + ElasticSearchQueryTranslator, + ElasticSearchSearchEngine, +} from '@backstage/plugin-search-backend-module-elasticsearch'; + +/** + * @alpha + * Options for {@link searchModuleElasticsearchEngine}. + */ +export type SearchModuleElasticsearchEngineOptions = { + translator?: ElasticSearchQueryTranslator; + indexTemplate?: ElasticSearchCustomIndexTemplate; +}; + +/** + * @alpha + * Search backend module for the Elasticsearch engine. + */ +export const searchModuleElasticsearchEngine = createBackendModule( + (options?: SearchModuleElasticsearchEngineOptions) => ({ + moduleId: 'elasticsearchEngine', + pluginId: 'search', + register(env) { + env.registerInit({ + deps: { + searchEngineRegistry: searchEngineRegistryExtensionPoint, + logger: coreServices.logger, + config: coreServices.config, + }, + async init({ searchEngineRegistry, logger, config }) { + const searchEngine = await ElasticSearchSearchEngine.fromConfig({ + logger: loggerToWinstonLogger(logger), + config: config, + }); + + // set custom translator if available + if (options?.translator) { + searchEngine.setTranslator(options.translator); + } + + // set custom index template if available + if (options?.indexTemplate) { + searchEngine.setIndexTemplate(options.indexTemplate); + } + + searchEngineRegistry.setSearchEngine(searchEngine); + }, + }); + }, + }), +); diff --git a/plugins/search-backend-module-explore/.eslintrc.js b/plugins/search-backend-module-explore/.eslintrc.js new file mode 100644 index 0000000000..e2a53a6ad2 --- /dev/null +++ b/plugins/search-backend-module-explore/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/plugins/search-backend-module-explore/README.md b/plugins/search-backend-module-explore/README.md new file mode 100644 index 0000000000..a87f2d78f9 --- /dev/null +++ b/plugins/search-backend-module-explore/README.md @@ -0,0 +1,41 @@ +# search-backend-module-explore + +> DISCLAIMER: The new backend system is in alpha, and so are the search backend module support for the new backend system. We don't recommend you to migrate your backend installations to the new system yet. But if you want to experiment, you can find getting started guides below. + +This package exports explore backend modules responsible for extending search. + +## Example + +### Use default schedule + +```tsx +// packages/backend-next/src/index.ts +import { createBackend } from '@backstage/backend-defaults'; +import { searchPlugin } from '@backstage/plugin-search-backend/alpha'; +import { searchModuleExploreCollator } from '@backstage/plugin-search-backend-module-explore/alpha'; + +const backend = createBackend(); +backend.add(searchPlugin()); +backend.add(searchModuleExploreCollator()); +backend.start(); +``` + +### Use custom schedule + +```tsx +// packages/backend-next/src/index.ts +import { createBackend } from '@backstage/backend-defaults'; +import { searchPlugin } from '@backstage/plugin-search-backend/alpha'; +import { searchModuleExploreCollator } from '@backstage/plugin-search-backend-module-explore/alpha'; + +const schedule = { + frequency: { minutes: 10 }, + timeout: { minutes: 15 }, + initialDelay: { seconds: 3 }, +}; + +const backend = createBackend(); +backend.add(searchPlugin()); +backend.add(searchModuleExploreCollator({ schedule })); +backend.start(); +``` diff --git a/plugins/search-backend-module-explore/alpha-api-report.md b/plugins/search-backend-module-explore/alpha-api-report.md new file mode 100644 index 0000000000..cf32b59dd3 --- /dev/null +++ b/plugins/search-backend-module-explore/alpha-api-report.md @@ -0,0 +1,24 @@ +## API Report File for "@backstage/plugin-search-backend-module-explore" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +import { BackendFeature } from '@backstage/backend-plugin-api'; +import { TaskScheduleDefinition } from '@backstage/backend-tasks'; +import { ToolDocumentCollatorFactoryOptions } from '@backstage/plugin-search-backend-module-explore'; + +// @alpha +export const searchModuleExploreCollator: ( + options?: SearchModuleExploreCollatorOptions | undefined, +) => BackendFeature; + +// @alpha +export type SearchModuleExploreCollatorOptions = Omit< + ToolDocumentCollatorFactoryOptions, + 'logger' | 'discovery' +> & { + schedule?: TaskScheduleDefinition; +}; + +// (No @packageDocumentation comment for this package) +``` diff --git a/plugins/search-backend-module-explore/api-report.md b/plugins/search-backend-module-explore/api-report.md new file mode 100644 index 0000000000..80e8c0c06b --- /dev/null +++ b/plugins/search-backend-module-explore/api-report.md @@ -0,0 +1,39 @@ +## API Report File for "@backstage/plugin-search-backend-module-explore" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +/// + +import { Config } from '@backstage/config'; +import { DocumentCollatorFactory } from '@backstage/plugin-search-common'; +import { ExploreTool } from '@backstage/plugin-explore-common'; +import { IndexableDocument } from '@backstage/plugin-search-common'; +import { Logger } from 'winston'; +import { PluginEndpointDiscovery } from '@backstage/backend-common'; +import { Readable } from 'stream'; + +// @public +export interface ToolDocument extends IndexableDocument, ExploreTool {} + +// @public +export class ToolDocumentCollatorFactory implements DocumentCollatorFactory { + // (undocumented) + execute(): AsyncGenerator; + // (undocumented) + static fromConfig( + _config: Config, + options: ToolDocumentCollatorFactoryOptions, + ): ToolDocumentCollatorFactory; + // (undocumented) + getCollator(): Promise; + // (undocumented) + readonly type: string; +} + +// @public +export type ToolDocumentCollatorFactoryOptions = { + discovery: PluginEndpointDiscovery; + logger: Logger; +}; +``` diff --git a/plugins/search-backend-module-explore/package.json b/plugins/search-backend-module-explore/package.json new file mode 100644 index 0000000000..e4c71448ff --- /dev/null +++ b/plugins/search-backend-module-explore/package.json @@ -0,0 +1,56 @@ +{ + "name": "@backstage/plugin-search-backend-module-explore", + "description": "A module for the search backend that exports explore modules", + "version": "0.0.0", + "main": "src/index.ts", + "types": "src/index.ts", + "license": "Apache-2.0", + "publishConfig": { + "access": "public" + }, + "exports": { + ".": "./src/index.ts", + "./alpha": "./src/alpha.ts", + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "alpha": [ + "src/alpha.ts" + ], + "package.json": [ + "package.json" + ] + } + }, + "backstage": { + "role": "backend-plugin-module" + }, + "scripts": { + "start": "backstage-cli package start", + "build": "backstage-cli package build", + "lint": "backstage-cli package lint", + "test": "backstage-cli package test", + "prepack": "backstage-cli package prepack", + "postpack": "backstage-cli package postpack", + "clean": "backstage-cli package clean" + }, + "dependencies": { + "@backstage/backend-common": "workspace:^", + "@backstage/backend-plugin-api": "workspace:^", + "@backstage/backend-tasks": "workspace:^", + "@backstage/config": "workspace:^", + "@backstage/plugin-explore-common": "workspace:^", + "@backstage/plugin-search-backend-node": "workspace:^", + "@backstage/plugin-search-common": "workspace:^", + "node-fetch": "^2.6.7", + "winston": "^3.2.1" + }, + "devDependencies": { + "@backstage/backend-test-utils": "workspace:^", + "@backstage/cli": "workspace:^" + }, + "files": [ + "dist" + ] +} diff --git a/plugins/search-backend-module-explore/src/alpha.test.ts b/plugins/search-backend-module-explore/src/alpha.test.ts new file mode 100644 index 0000000000..9b133f0fd5 --- /dev/null +++ b/plugins/search-backend-module-explore/src/alpha.test.ts @@ -0,0 +1,46 @@ +/* + * Copyright 2023 The Backstage Authors + * + * 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 { startTestBackend } from '@backstage/backend-test-utils'; +import { searchIndexRegistryExtensionPoint } from '@backstage/plugin-search-backend-node/alpha'; +import { searchModuleExploreCollator } from './alpha'; + +describe('searchModuleExploreCollator', () => { + const schedule = { + frequency: { minutes: 10 }, + timeout: { minutes: 15 }, + initialDelay: { seconds: 3 }, + }; + + it('should register the explore collator to the search index registry extension point with factory and schedule', async () => { + const extensionPointMock = { + addCollator: jest.fn(), + }; + + await startTestBackend({ + extensionPoints: [ + [searchIndexRegistryExtensionPoint, extensionPointMock], + ], + features: [searchModuleExploreCollator({ schedule })], + }); + + expect(extensionPointMock.addCollator).toHaveBeenCalledTimes(1); + expect(extensionPointMock.addCollator).toHaveBeenCalledWith({ + factory: expect.objectContaining({ type: 'tools' }), + schedule: expect.objectContaining({ run: expect.any(Function) }), + }); + }); +}); diff --git a/plugins/search-backend-module-explore/src/alpha.ts b/plugins/search-backend-module-explore/src/alpha.ts new file mode 100644 index 0000000000..f13c3c2406 --- /dev/null +++ b/plugins/search-backend-module-explore/src/alpha.ts @@ -0,0 +1,84 @@ +/* + * Copyright 2023 The Backstage Authors + * + * 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. + */ + +/** + * @packageDocumentation + * A module for the search backend that exports Explore modules. + */ + +import { + coreServices, + createBackendModule, +} from '@backstage/backend-plugin-api'; +import { TaskScheduleDefinition } from '@backstage/backend-tasks'; +import { loggerToWinstonLogger } from '@backstage/backend-common'; +import { searchIndexRegistryExtensionPoint } from '@backstage/plugin-search-backend-node/alpha'; + +import { + ToolDocumentCollatorFactory, + ToolDocumentCollatorFactoryOptions, +} from '@backstage/plugin-search-backend-module-explore'; + +/** + * @alpha + * Options for {@link searchModuleExploreCollator}. + */ +export type SearchModuleExploreCollatorOptions = Omit< + ToolDocumentCollatorFactoryOptions, + 'logger' | 'discovery' +> & { + schedule?: TaskScheduleDefinition; +}; + +/** + * @alpha + * Search backend module for the Explore index. + */ +export const searchModuleExploreCollator = createBackendModule( + (options?: SearchModuleExploreCollatorOptions) => ({ + moduleId: 'exploreCollator', + pluginId: 'search', + register(env) { + env.registerInit({ + deps: { + config: coreServices.config, + logger: coreServices.logger, + discovery: coreServices.discovery, + scheduler: coreServices.scheduler, + indexRegistry: searchIndexRegistryExtensionPoint, + }, + async init({ config, logger, discovery, scheduler, indexRegistry }) { + const defaultSchedule = { + frequency: { minutes: 10 }, + timeout: { minutes: 15 }, + initialDelay: { seconds: 3 }, + }; + + indexRegistry.addCollator({ + schedule: scheduler.createScheduledTaskRunner( + options?.schedule ?? defaultSchedule, + ), + factory: ToolDocumentCollatorFactory.fromConfig(config, { + ...options, + discovery, + logger: loggerToWinstonLogger(logger), + }), + }); + }, + }); + }, + }), +); diff --git a/plugins/explore-backend/src/search/ToolDocumentCollatorFactory.ts b/plugins/search-backend-module-explore/src/collators/ToolDocumentCollatorFactory.ts similarity index 100% rename from plugins/explore-backend/src/search/ToolDocumentCollatorFactory.ts rename to plugins/search-backend-module-explore/src/collators/ToolDocumentCollatorFactory.ts diff --git a/plugins/explore-backend/src/search/index.ts b/plugins/search-backend-module-explore/src/collators/index.ts similarity index 99% rename from plugins/explore-backend/src/search/index.ts rename to plugins/search-backend-module-explore/src/collators/index.ts index 70e9414652..45f0c933e2 100644 --- a/plugins/explore-backend/src/search/index.ts +++ b/plugins/search-backend-module-explore/src/collators/index.ts @@ -15,6 +15,7 @@ */ export { ToolDocumentCollatorFactory } from './ToolDocumentCollatorFactory'; + export type { ToolDocument, ToolDocumentCollatorFactoryOptions, diff --git a/plugins/search-backend-module-explore/src/index.ts b/plugins/search-backend-module-explore/src/index.ts new file mode 100644 index 0000000000..ea94071984 --- /dev/null +++ b/plugins/search-backend-module-explore/src/index.ts @@ -0,0 +1,22 @@ +/* + * Copyright 2023 The Backstage Authors + * + * 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. + */ + +/** + * @packageDocumentation + * A module for the search backend that exports Explore modules. + */ + +export * from './collators'; diff --git a/plugins/search-backend-module-pg/alpha-api-report.md b/plugins/search-backend-module-pg/alpha-api-report.md new file mode 100644 index 0000000000..eaee78e944 --- /dev/null +++ b/plugins/search-backend-module-pg/alpha-api-report.md @@ -0,0 +1,12 @@ +## API Report File for "@backstage/plugin-search-backend-module-pg" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +import { BackendFeature } from '@backstage/backend-plugin-api'; + +// @alpha +export const searchModulePostgresEngine: () => BackendFeature; + +// (No @packageDocumentation comment for this package) +``` diff --git a/plugins/search-backend-module-pg/package.json b/plugins/search-backend-module-pg/package.json index 7c49e90000..28432f2025 100644 --- a/plugins/search-backend-module-pg/package.json +++ b/plugins/search-backend-module-pg/package.json @@ -6,9 +6,22 @@ "types": "src/index.ts", "license": "Apache-2.0", "publishConfig": { - "access": "public", - "main": "dist/index.cjs.js", - "types": "dist/index.d.ts" + "access": "public" + }, + "exports": { + ".": "./src/index.ts", + "./alpha": "./src/alpha.ts", + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "alpha": [ + "src/alpha.ts" + ], + "package.json": [ + "package.json" + ] + } }, "backstage": { "role": "backend-plugin-module" @@ -24,6 +37,7 @@ }, "dependencies": { "@backstage/backend-common": "workspace:^", + "@backstage/backend-plugin-api": "workspace:^", "@backstage/config": "workspace:^", "@backstage/plugin-search-backend-node": "workspace:^", "@backstage/plugin-search-common": "workspace:^", diff --git a/plugins/search-backend-module-pg/src/alpha.ts b/plugins/search-backend-module-pg/src/alpha.ts new file mode 100644 index 0000000000..e3e0087957 --- /dev/null +++ b/plugins/search-backend-module-pg/src/alpha.ts @@ -0,0 +1,46 @@ +/* + * Copyright 2023 The Backstage Authors + * + * 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 { + coreServices, + createBackendModule, +} from '@backstage/backend-plugin-api'; +import { searchEngineRegistryExtensionPoint } from '@backstage/plugin-search-backend-node/alpha'; +import { PgSearchEngine } from './PgSearchEngine'; + +/** + * @alpha + * Search backend module for the Postgres engine. + */ +export const searchModulePostgresEngine = createBackendModule({ + moduleId: 'postgresEngine', + pluginId: 'search', + register(env) { + env.registerInit({ + deps: { + searchEngineRegistry: searchEngineRegistryExtensionPoint, + database: coreServices.database, + config: coreServices.config, + }, + async init({ searchEngineRegistry, database, config }) { + searchEngineRegistry.setSearchEngine( + await PgSearchEngine.fromConfig(config, { + database: database, + }), + ); + }, + }); + }, +}); diff --git a/plugins/search-backend-module-techdocs/.eslintrc.js b/plugins/search-backend-module-techdocs/.eslintrc.js new file mode 100644 index 0000000000..e2a53a6ad2 --- /dev/null +++ b/plugins/search-backend-module-techdocs/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/plugins/search-backend-module-techdocs/README.md b/plugins/search-backend-module-techdocs/README.md new file mode 100644 index 0000000000..5de310466b --- /dev/null +++ b/plugins/search-backend-module-techdocs/README.md @@ -0,0 +1,41 @@ +# search-backend-module-techdocs + +> DISCLAIMER: The new backend system is in alpha, and so are the search backend module support for the new backend system. We don't recommend you to migrate your backend installations to the new system yet. But if you want to experiment, you can find getting started guides below. + +This package exports techdocs backend modules responsible for extending search. + +## Example + +### Use default schedule + +```tsx +// packages/backend-next/src/index.ts +import { createBackend } from '@backstage/backend-defaults'; +import { searchPlugin } from '@backstage/plugin-search-backend/alpha'; +import { searchModuleTechDocsCollator } from '@backstage/plugin-search-backend-module-techdocs/alpha'; + +const backend = createBackend(); +backend.add(searchPlugin()); +backend.add(searchModuleTechDocsCollator()); +backend.start(); +``` + +### Use custom schedule + +```tsx +// packages/backend-next/src/index.ts +import { createBackend } from '@backstage/backend-defaults'; +import { searchPlugin } from '@backstage/plugin-search-backend/alpha'; +import { searchModuleTechDocsCollator } from '@backstage/plugin-search-backend-module-techdocs/alpha'; + +const schedule = { + frequency: { minutes: 10 }, + timeout: { minutes: 15 }, + initialDelay: { seconds: 3 }, +}; + +const backend = createBackend(); +backend.add(searchPlugin()); +backend.add(searchModuleTechDocsCollator({ schedule })); +backend.start(); +``` diff --git a/plugins/search-backend-module-techdocs/alpha-api-report.md b/plugins/search-backend-module-techdocs/alpha-api-report.md new file mode 100644 index 0000000000..71ce09b744 --- /dev/null +++ b/plugins/search-backend-module-techdocs/alpha-api-report.md @@ -0,0 +1,24 @@ +## API Report File for "@backstage/plugin-search-backend-module-techdocs" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +import { BackendFeature } from '@backstage/backend-plugin-api'; +import { TaskScheduleDefinition } from '@backstage/backend-tasks'; +import { TechDocsCollatorFactoryOptions } from '@backstage/plugin-search-backend-module-techdocs'; + +// @alpha +export const searchModuleTechDocsCollator: ( + options?: SearchModuleTechDocsCollatorOptions | undefined, +) => BackendFeature; + +// @alpha +export type SearchModuleTechDocsCollatorOptions = Omit< + TechDocsCollatorFactoryOptions, + 'logger' | 'discovery' | 'tokenManager' +> & { + schedule?: TaskScheduleDefinition; +}; + +// (No @packageDocumentation comment for this package) +``` diff --git a/plugins/search-backend-module-techdocs/api-report.md b/plugins/search-backend-module-techdocs/api-report.md new file mode 100644 index 0000000000..9f72b1c487 --- /dev/null +++ b/plugins/search-backend-module-techdocs/api-report.md @@ -0,0 +1,42 @@ +## API Report File for "@backstage/plugin-search-backend-module-techdocs" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +/// + +import { CatalogApi } from '@backstage/catalog-client'; +import { Config } from '@backstage/config'; +import { DocumentCollatorFactory } from '@backstage/plugin-search-common'; +import { Logger } from 'winston'; +import { Permission } from '@backstage/plugin-permission-common'; +import { PluginEndpointDiscovery } from '@backstage/backend-common'; +import { Readable } from 'stream'; +import { TokenManager } from '@backstage/backend-common'; + +// @public +export class DefaultTechDocsCollatorFactory implements DocumentCollatorFactory { + // (undocumented) + static fromConfig( + config: Config, + options: TechDocsCollatorFactoryOptions, + ): DefaultTechDocsCollatorFactory; + // (undocumented) + getCollator(): Promise; + // (undocumented) + readonly type: string; + // (undocumented) + readonly visibilityPermission: Permission; +} + +// @public +export type TechDocsCollatorFactoryOptions = { + discovery: PluginEndpointDiscovery; + logger: Logger; + tokenManager: TokenManager; + locationTemplate?: string; + catalogClient?: CatalogApi; + parallelismLimit?: number; + legacyPathCasing?: boolean; +}; +``` diff --git a/plugins/search-backend-module-techdocs/package.json b/plugins/search-backend-module-techdocs/package.json new file mode 100644 index 0000000000..d08193be29 --- /dev/null +++ b/plugins/search-backend-module-techdocs/package.json @@ -0,0 +1,63 @@ +{ + "name": "@backstage/plugin-search-backend-module-techdocs", + "description": "A module for the search backend that exports techdocs modules", + "version": "0.0.0", + "main": "src/index.ts", + "types": "src/index.ts", + "license": "Apache-2.0", + "publishConfig": { + "access": "public" + }, + "exports": { + ".": "./src/index.ts", + "./alpha": "./src/alpha.ts", + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "alpha": [ + "src/alpha.ts" + ], + "package.json": [ + "package.json" + ] + } + }, + "backstage": { + "role": "backend-plugin-module" + }, + "scripts": { + "start": "backstage-cli package start", + "build": "backstage-cli package build", + "lint": "backstage-cli package lint", + "test": "backstage-cli package test", + "prepack": "backstage-cli package prepack", + "postpack": "backstage-cli package postpack", + "clean": "backstage-cli package clean" + }, + "dependencies": { + "@backstage/backend-common": "workspace:^", + "@backstage/backend-plugin-api": "workspace:^", + "@backstage/backend-tasks": "workspace:^", + "@backstage/catalog-client": "workspace:^", + "@backstage/catalog-model": "workspace:^", + "@backstage/config": "workspace:^", + "@backstage/plugin-catalog-common": "workspace:^", + "@backstage/plugin-permission-common": "workspace:^", + "@backstage/plugin-search-backend-node": "workspace:^", + "@backstage/plugin-search-common": "workspace:^", + "@backstage/plugin-techdocs-node": "workspace:^", + "lodash": "^4.17.21", + "node-fetch": "^2.6.7", + "p-limit": "^3.1.0", + "winston": "^3.2.1" + }, + "devDependencies": { + "@backstage/backend-test-utils": "workspace:^", + "@backstage/cli": "workspace:^", + "msw": "^1.0.0" + }, + "files": [ + "dist" + ] +} diff --git a/plugins/search-backend-module-techdocs/src/alpha.test.ts b/plugins/search-backend-module-techdocs/src/alpha.test.ts new file mode 100644 index 0000000000..011b00b12c --- /dev/null +++ b/plugins/search-backend-module-techdocs/src/alpha.test.ts @@ -0,0 +1,46 @@ +/* + * Copyright 2023 The Backstage Authors + * + * 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 { startTestBackend } from '@backstage/backend-test-utils'; +import { searchIndexRegistryExtensionPoint } from '@backstage/plugin-search-backend-node/alpha'; +import { searchModuleTechDocsCollator } from './alpha'; + +describe('searchModuleTechDocsCollator', () => { + const schedule = { + frequency: { minutes: 10 }, + timeout: { minutes: 15 }, + initialDelay: { seconds: 3 }, + }; + + it('should register the techdocs collator to the search index registry extension point with factory and schedule', async () => { + const extensionPointMock = { + addCollator: jest.fn(), + }; + + await startTestBackend({ + extensionPoints: [ + [searchIndexRegistryExtensionPoint, extensionPointMock], + ], + features: [searchModuleTechDocsCollator({ schedule })], + }); + + expect(extensionPointMock.addCollator).toHaveBeenCalledTimes(1); + expect(extensionPointMock.addCollator).toHaveBeenCalledWith({ + factory: expect.objectContaining({ type: 'techdocs' }), + schedule: expect.objectContaining({ run: expect.any(Function) }), + }); + }); +}); diff --git a/plugins/search-backend-module-techdocs/src/alpha.ts b/plugins/search-backend-module-techdocs/src/alpha.ts new file mode 100644 index 0000000000..1432cc88fb --- /dev/null +++ b/plugins/search-backend-module-techdocs/src/alpha.ts @@ -0,0 +1,93 @@ +/* + * Copyright 2023 The Backstage Authors + * + * 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. + */ + +/** + * @packageDocumentation + * A module for the search backend that exports TechDocs modules. + */ + +import { + coreServices, + createBackendModule, +} from '@backstage/backend-plugin-api'; +import { TaskScheduleDefinition } from '@backstage/backend-tasks'; +import { loggerToWinstonLogger } from '@backstage/backend-common'; +import { searchIndexRegistryExtensionPoint } from '@backstage/plugin-search-backend-node/alpha'; + +import { + DefaultTechDocsCollatorFactory, + TechDocsCollatorFactoryOptions, +} from '@backstage/plugin-search-backend-module-techdocs'; + +/** + * @alpha + * Options for {@link searchModuleTechDocsCollator}. + */ +export type SearchModuleTechDocsCollatorOptions = Omit< + TechDocsCollatorFactoryOptions, + 'logger' | 'discovery' | 'tokenManager' +> & { + schedule?: TaskScheduleDefinition; +}; + +/** + * @alpha + * Search backend module for the TechDocs index. + */ +export const searchModuleTechDocsCollator = createBackendModule( + (options?: SearchModuleTechDocsCollatorOptions) => ({ + moduleId: 'techDocsCollator', + pluginId: 'search', + register(env) { + env.registerInit({ + deps: { + config: coreServices.config, + logger: coreServices.logger, + discovery: coreServices.discovery, + tokenManager: coreServices.tokenManager, + scheduler: coreServices.scheduler, + indexRegistry: searchIndexRegistryExtensionPoint, + }, + async init({ + config, + logger, + discovery, + tokenManager, + scheduler, + indexRegistry, + }) { + const defaultSchedule = { + frequency: { minutes: 10 }, + timeout: { minutes: 15 }, + initialDelay: { seconds: 3 }, + }; + + indexRegistry.addCollator({ + schedule: scheduler.createScheduledTaskRunner( + options?.schedule ?? defaultSchedule, + ), + factory: DefaultTechDocsCollatorFactory.fromConfig(config, { + ...options, + discovery, + tokenManager, + logger: loggerToWinstonLogger(logger), + }), + }); + }, + }); + }, + }), +); diff --git a/plugins/techdocs-backend/src/search/DefaultTechDocsCollatorFactory.test.ts b/plugins/search-backend-module-techdocs/src/collators/DefaultTechDocsCollatorFactory.test.ts similarity index 100% rename from plugins/techdocs-backend/src/search/DefaultTechDocsCollatorFactory.test.ts rename to plugins/search-backend-module-techdocs/src/collators/DefaultTechDocsCollatorFactory.test.ts diff --git a/plugins/techdocs-backend/src/search/DefaultTechDocsCollatorFactory.ts b/plugins/search-backend-module-techdocs/src/collators/DefaultTechDocsCollatorFactory.ts similarity index 100% rename from plugins/techdocs-backend/src/search/DefaultTechDocsCollatorFactory.ts rename to plugins/search-backend-module-techdocs/src/collators/DefaultTechDocsCollatorFactory.ts diff --git a/plugins/search-backend-module-techdocs/src/collators/index.ts b/plugins/search-backend-module-techdocs/src/collators/index.ts new file mode 100644 index 0000000000..8c5fd122f1 --- /dev/null +++ b/plugins/search-backend-module-techdocs/src/collators/index.ts @@ -0,0 +1,19 @@ +/* + * Copyright 2023 The Backstage Authors + * + * 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 { DefaultTechDocsCollatorFactory } from './DefaultTechDocsCollatorFactory'; + +export type { TechDocsCollatorFactoryOptions } from './DefaultTechDocsCollatorFactory'; diff --git a/plugins/search-backend-module-techdocs/src/index.ts b/plugins/search-backend-module-techdocs/src/index.ts new file mode 100644 index 0000000000..c28175fd61 --- /dev/null +++ b/plugins/search-backend-module-techdocs/src/index.ts @@ -0,0 +1,22 @@ +/* + * Copyright 2023 The Backstage Authors + * + * 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. + */ + +/** + * @packageDocumentation + * A module for the search backend that exports TechDocs modules. + */ + +export * from './collators'; diff --git a/plugins/search-backend-node/alpha-api-report.md b/plugins/search-backend-node/alpha-api-report.md new file mode 100644 index 0000000000..74b7a4b4bc --- /dev/null +++ b/plugins/search-backend-node/alpha-api-report.md @@ -0,0 +1,50 @@ +## API Report File for "@backstage/plugin-search-backend-node" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +import { DocumentTypeInfo } from '@backstage/plugin-search-common'; +import { ExtensionPoint } from '@backstage/backend-plugin-api'; +import { RegisterCollatorParameters } from '@backstage/plugin-search-backend-node'; +import { RegisterDecoratorParameters } from '@backstage/plugin-search-backend-node'; +import { SearchEngine } from '@backstage/plugin-search-common'; +import { ServiceRef } from '@backstage/backend-plugin-api'; + +// @alpha +export interface SearchEngineRegistryExtensionPoint { + // (undocumented) + setSearchEngine(searchEngine: SearchEngine): void; +} + +// @alpha +export const searchEngineRegistryExtensionPoint: ExtensionPoint; + +// @alpha +export interface SearchIndexRegistryExtensionPoint { + // (undocumented) + addCollator(options: RegisterCollatorParameters): void; + // (undocumented) + addDecorator(options: RegisterDecoratorParameters): void; +} + +// @alpha +export const searchIndexRegistryExtensionPoint: ExtensionPoint; + +// @alpha +export interface SearchIndexService { + getDocumentTypes(): Record; + start(options: SearchIndexServiceStartOptions): Promise; +} + +// @alpha +export const searchIndexServiceRef: ServiceRef; + +// @alpha +export type SearchIndexServiceStartOptions = { + searchEngine: SearchEngine; + collators: RegisterCollatorParameters[]; + decorators: RegisterDecoratorParameters[]; +}; + +// (No @packageDocumentation comment for this package) +``` diff --git a/plugins/search-backend-node/package.json b/plugins/search-backend-node/package.json index 7a95341fb8..a49b6088ab 100644 --- a/plugins/search-backend-node/package.json +++ b/plugins/search-backend-node/package.json @@ -6,9 +6,22 @@ "types": "src/index.ts", "license": "Apache-2.0", "publishConfig": { - "access": "public", - "main": "dist/index.cjs.js", - "types": "dist/index.d.ts" + "access": "public" + }, + "exports": { + ".": "./src/index.ts", + "./alpha": "./src/alpha.ts", + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "alpha": [ + "src/alpha.ts" + ], + "package.json": [ + "package.json" + ] + } }, "backstage": { "role": "node-library" @@ -24,6 +37,7 @@ }, "dependencies": { "@backstage/backend-common": "workspace:^", + "@backstage/backend-plugin-api": "workspace:^", "@backstage/backend-tasks": "workspace:^", "@backstage/config": "workspace:^", "@backstage/errors": "workspace:^", diff --git a/plugins/search-backend-node/src/alpha.ts b/plugins/search-backend-node/src/alpha.ts new file mode 100644 index 0000000000..943e11b580 --- /dev/null +++ b/plugins/search-backend-node/src/alpha.ts @@ -0,0 +1,159 @@ +/* + * Copyright 2023 The Backstage Authors + * + * 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 { Logger } from 'winston'; + +import { + createServiceRef, + createServiceFactory, + coreServices, +} from '@backstage/backend-plugin-api'; +import { loggerToWinstonLogger } from '@backstage/backend-common'; +import { + DocumentTypeInfo, + SearchEngine, +} from '@backstage/plugin-search-common'; +import { createExtensionPoint } from '@backstage/backend-plugin-api'; + +import { + RegisterCollatorParameters, + RegisterDecoratorParameters, +} from '@backstage/plugin-search-backend-node'; + +import { IndexBuilder } from './IndexBuilder'; + +/** + * @alpha + * Options for build method on {@link SearchIndexService}. + */ +export type SearchIndexServiceStartOptions = { + searchEngine: SearchEngine; + collators: RegisterCollatorParameters[]; + decorators: RegisterDecoratorParameters[]; +}; + +/** + * @alpha + * Interface for implementation of index service. + */ +export interface SearchIndexService { + /** + * Starts indexing process + */ + start(options: SearchIndexServiceStartOptions): Promise; + /** + * Returns an index types list. + */ + getDocumentTypes(): Record; +} + +/** + * @alpha + * Interface for search index registry extension point. + */ +export interface SearchIndexRegistryExtensionPoint { + addCollator(options: RegisterCollatorParameters): void; + addDecorator(options: RegisterDecoratorParameters): void; +} + +/** + * @alpha + * Interface for search engine registry extension point. + */ +export interface SearchEngineRegistryExtensionPoint { + setSearchEngine(searchEngine: SearchEngine): void; +} + +type DefaultSearchIndexServiceOptions = { + logger: Logger; +}; + +/** + * @alpha + * Reponsible for register the indexing task and start the schedule. + */ +class DefaultSearchIndexService implements SearchIndexService { + private logger: Logger; + private indexBuilder: IndexBuilder | null = null; + + private constructor(options: DefaultSearchIndexServiceOptions) { + this.logger = options.logger; + } + + static fromConfig(options: DefaultSearchIndexServiceOptions) { + return new DefaultSearchIndexService(options); + } + + async start(options: SearchIndexServiceStartOptions): Promise { + this.indexBuilder = new IndexBuilder({ + logger: this.logger, + searchEngine: options.searchEngine, + }); + + options.collators.forEach(collator => + this.indexBuilder?.addCollator(collator), + ); + + options.decorators.forEach(decorator => + this.indexBuilder?.addDecorator(decorator), + ); + + const { scheduler } = await this.indexBuilder?.build(); + scheduler.start(); + } + + getDocumentTypes(): Record { + return this.indexBuilder?.getDocumentTypes() ?? {}; + } +} + +/** + * @alpha + * Service that builds a search index. + */ +export const searchIndexServiceRef = createServiceRef({ + id: 'search.index.service', + defaultFactory: async service => + createServiceFactory({ + service, + deps: { + logger: coreServices.logger, + }, + factory({ logger }) { + return DefaultSearchIndexService.fromConfig({ + logger: loggerToWinstonLogger(logger), + }); + }, + }), +}); + +/** + * @alpha + * Extension point for register a search engine. + */ +export const searchEngineRegistryExtensionPoint = + createExtensionPoint({ + id: 'search.engine.registry', + }); + +/** + * @alpha + * Extension point for registering collators and decorators + */ +export const searchIndexRegistryExtensionPoint = + createExtensionPoint({ + id: 'search.index.registry', + }); diff --git a/plugins/search-backend/alpha-api-report.md b/plugins/search-backend/alpha-api-report.md new file mode 100644 index 0000000000..6ee94a0e29 --- /dev/null +++ b/plugins/search-backend/alpha-api-report.md @@ -0,0 +1,12 @@ +## API Report File for "@backstage/plugin-search-backend" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +import { BackendFeature } from '@backstage/backend-plugin-api'; + +// @alpha +export const searchPlugin: () => BackendFeature; + +// (No @packageDocumentation comment for this package) +``` diff --git a/plugins/search-backend/package.json b/plugins/search-backend/package.json index 19543dc7ce..3091e0e8b4 100644 --- a/plugins/search-backend/package.json +++ b/plugins/search-backend/package.json @@ -6,9 +6,22 @@ "types": "src/index.ts", "license": "Apache-2.0", "publishConfig": { - "access": "public", - "main": "dist/index.cjs.js", - "types": "dist/index.d.ts" + "access": "public" + }, + "exports": { + ".": "./src/index.ts", + "./alpha": "./src/alpha.ts", + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "alpha": [ + "src/alpha.ts" + ], + "package.json": [ + "package.json" + ] + } }, "backstage": { "role": "backend-plugin" @@ -24,6 +37,7 @@ }, "dependencies": { "@backstage/backend-common": "workspace:^", + "@backstage/backend-plugin-api": "workspace:^", "@backstage/config": "workspace:^", "@backstage/errors": "workspace:^", "@backstage/plugin-auth-node": "workspace:^", @@ -43,6 +57,7 @@ "zod": "~3.18.0" }, "devDependencies": { + "@backstage/backend-test-utils": "workspace:^", "@backstage/cli": "workspace:^", "@types/supertest": "^2.0.8", "supertest": "^6.1.3" diff --git a/plugins/search-backend/src/alpha.test.ts b/plugins/search-backend/src/alpha.test.ts new file mode 100644 index 0000000000..527f099236 --- /dev/null +++ b/plugins/search-backend/src/alpha.test.ts @@ -0,0 +1,31 @@ +/* + * Copyright 2023 The Backstage Authors + * + * 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 { startTestBackend } from '@backstage/backend-test-utils'; +import request from 'supertest'; +import { searchPlugin } from './alpha'; + +describe('searchPlugin', () => { + it('should serve search results on query endpoint', async () => { + const { server } = await startTestBackend({ + features: [searchPlugin()], + }); + + const response = await request(server).get('/api/search/query'); + expect(response.status).toBe(200); + expect(response.body).toEqual({ numberOfResults: 0, results: [] }); + }); +}); diff --git a/plugins/search-backend/src/alpha.ts b/plugins/search-backend/src/alpha.ts new file mode 100644 index 0000000000..0c14852875 --- /dev/null +++ b/plugins/search-backend/src/alpha.ts @@ -0,0 +1,130 @@ +/* + * Copyright 2023 The Backstage Authors + * + * 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 { + coreServices, + createBackendPlugin, +} from '@backstage/backend-plugin-api'; +import { loggerToWinstonLogger } from '@backstage/backend-common'; +import { + RegisterCollatorParameters, + RegisterDecoratorParameters, + LunrSearchEngine, +} from '@backstage/plugin-search-backend-node'; +import { + searchIndexServiceRef, + searchIndexRegistryExtensionPoint, + SearchIndexRegistryExtensionPoint, + SearchEngineRegistryExtensionPoint, + searchEngineRegistryExtensionPoint, +} from '@backstage/plugin-search-backend-node/alpha'; + +import { createRouter } from './service/router'; +import { SearchEngine } from '@backstage/plugin-search-common'; + +class SearchIndexRegistry implements SearchIndexRegistryExtensionPoint { + private collators: RegisterCollatorParameters[] = []; + private decorators: RegisterDecoratorParameters[] = []; + + public addCollator(options: RegisterCollatorParameters): void { + this.collators.push(options); + } + + public addDecorator(options: RegisterDecoratorParameters): void { + this.decorators.push(options); + } + + public getCollators(): RegisterCollatorParameters[] { + return this.collators; + } + + public getDecorators(): RegisterDecoratorParameters[] { + return this.decorators; + } +} + +class SearchEngineRegistry implements SearchEngineRegistryExtensionPoint { + private searchEngine: SearchEngine | null = null; + + public setSearchEngine(searchEngine: SearchEngine): void { + if (this.searchEngine) { + throw new Error('Multiple Search engines is not supported at this time'); + } + this.searchEngine = searchEngine; + } + + public getSearchEngine(): SearchEngine | null { + return this.searchEngine; + } +} + +/** + * The Search plugin is responsible for starting search indexing processes and return search results. + * @alpha + */ +export const searchPlugin = createBackendPlugin({ + pluginId: 'search', + register(env) { + const searchIndexRegistry = new SearchIndexRegistry(); + env.registerExtensionPoint( + searchIndexRegistryExtensionPoint, + searchIndexRegistry, + ); + + const searchEngineRegistry = new SearchEngineRegistry(); + env.registerExtensionPoint( + searchEngineRegistryExtensionPoint, + searchEngineRegistry, + ); + + env.registerInit({ + deps: { + logger: coreServices.logger, + config: coreServices.config, + permissions: coreServices.permissions, + http: coreServices.httpRouter, + searchIndexService: searchIndexServiceRef, + }, + async init({ config, logger, permissions, http, searchIndexService }) { + let searchEngine = searchEngineRegistry.getSearchEngine(); + if (!searchEngine) { + searchEngine = new LunrSearchEngine({ + logger: loggerToWinstonLogger(logger), + }); + } + + const collators = searchIndexRegistry.getCollators(); + const decorators = searchIndexRegistry.getDecorators(); + + await searchIndexService.start({ + searchEngine, + collators, + decorators, + }); + + const router = await createRouter({ + config, + permissions, + logger: loggerToWinstonLogger(logger), + engine: searchEngine, + types: searchIndexService.getDocumentTypes(), + }); + + http.use(router); + }, + }); + }, +}); diff --git a/plugins/techdocs-backend/api-report.md b/plugins/techdocs-backend/api-report.md index 1f6aa1d66a..b8974e6d4f 100644 --- a/plugins/techdocs-backend/api-report.md +++ b/plugins/techdocs-backend/api-report.md @@ -3,12 +3,10 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -/// - import { CatalogApi } from '@backstage/catalog-client'; import { CatalogClient } from '@backstage/catalog-client'; import { Config } from '@backstage/config'; -import { DocumentCollatorFactory } from '@backstage/plugin-search-common'; +import { DefaultTechDocsCollatorFactory as DefaultTechDocsCollatorFactory_2 } from '@backstage/plugin-search-backend-module-techdocs'; import { Entity } from '@backstage/catalog-model'; import express from 'express'; import { GeneratorBuilder } from '@backstage/plugin-techdocs-node'; @@ -19,7 +17,7 @@ import { PluginCacheManager } from '@backstage/backend-common'; import { PluginEndpointDiscovery } from '@backstage/backend-common'; import { PreparerBuilder } from '@backstage/plugin-techdocs-node'; import { PublisherBase } from '@backstage/plugin-techdocs-node'; -import { Readable } from 'stream'; +import type { TechDocsCollatorFactoryOptions as TechDocsCollatorFactoryOptions_2 } from '@backstage/plugin-search-backend-module-techdocs'; import { TechDocsDocument } from '@backstage/plugin-techdocs-node'; import { TokenManager } from '@backstage/backend-common'; import * as winston from 'winston'; @@ -47,20 +45,8 @@ export class DefaultTechDocsCollator { readonly visibilityPermission: Permission; } -// @public -export class DefaultTechDocsCollatorFactory implements DocumentCollatorFactory { - // (undocumented) - static fromConfig( - config: Config, - options: TechDocsCollatorFactoryOptions, - ): DefaultTechDocsCollatorFactory; - // (undocumented) - getCollator(): Promise; - // (undocumented) - readonly type: string; - // (undocumented) - readonly visibilityPermission: Permission; -} +// @public @deprecated (undocumented) +export const DefaultTechDocsCollatorFactory: typeof DefaultTechDocsCollatorFactory_2; // @public export interface DocsBuildStrategy { @@ -105,16 +91,8 @@ export type ShouldBuildParameters = { entity: Entity; }; -// @public -export type TechDocsCollatorFactoryOptions = { - discovery: PluginEndpointDiscovery; - logger: Logger; - tokenManager: TokenManager; - locationTemplate?: string; - catalogClient?: CatalogApi; - parallelismLimit?: number; - legacyPathCasing?: boolean; -}; +// @public @deprecated (undocumented) +export type TechDocsCollatorFactoryOptions = TechDocsCollatorFactoryOptions_2; // @public export type TechDocsCollatorOptions = { diff --git a/plugins/techdocs-backend/package.json b/plugins/techdocs-backend/package.json index 0a4183ccb0..74af0e1010 100644 --- a/plugins/techdocs-backend/package.json +++ b/plugins/techdocs-backend/package.json @@ -57,6 +57,7 @@ "@backstage/integration": "workspace:^", "@backstage/plugin-catalog-common": "workspace:^", "@backstage/plugin-permission-common": "workspace:^", + "@backstage/plugin-search-backend-module-techdocs": "workspace:^", "@backstage/plugin-search-common": "workspace:^", "@backstage/plugin-techdocs-node": "workspace:^", "@types/express": "^4.17.6", diff --git a/plugins/techdocs-backend/src/search/index.ts b/plugins/techdocs-backend/src/search/index.ts index 68e3b4edc7..fc2b48375e 100644 --- a/plugins/techdocs-backend/src/search/index.ts +++ b/plugins/techdocs-backend/src/search/index.ts @@ -14,11 +14,23 @@ * limitations under the License. */ -export { DefaultTechDocsCollatorFactory } from './DefaultTechDocsCollatorFactory'; -export type { TechDocsCollatorFactoryOptions } from './DefaultTechDocsCollatorFactory'; - /** * todo(backstage/techdocs-core): stop exporting these in a future release. */ export { DefaultTechDocsCollator } from './DefaultTechDocsCollator'; export type { TechDocsCollatorOptions } from './DefaultTechDocsCollator'; + +import { DefaultTechDocsCollatorFactory as _DefaultTechDocsCollatorFactory } from '@backstage/plugin-search-backend-module-techdocs'; +import type { TechDocsCollatorFactoryOptions as _TechDocsCollatorFactoryOptions } from '@backstage/plugin-search-backend-module-techdocs'; + +/** + * @public + * @deprecated import from `@backstage/search-backend-module-techdocs` instead + */ +export type TechDocsCollatorFactoryOptions = _TechDocsCollatorFactoryOptions; + +/** + * @public + * @deprecated import from `@backstage/search-backend-module-techdocs` instead + */ +export const DefaultTechDocsCollatorFactory = _DefaultTechDocsCollatorFactory; diff --git a/yarn.lock b/yarn.lock index 6efefe1810..1f8f7fa330 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5276,6 +5276,7 @@ __metadata: "@backstage/plugin-permission-common": "workspace:^" "@backstage/plugin-permission-node": "workspace:^" "@backstage/plugin-scaffolder-common": "workspace:^" + "@backstage/plugin-search-backend-module-catalog": "workspace:^" "@backstage/plugin-search-backend-node": "workspace:^" "@backstage/plugin-search-common": "workspace:^" "@backstage/types": "workspace:^" @@ -6152,6 +6153,7 @@ __metadata: "@backstage/cli": "workspace:^" "@backstage/config": "workspace:^" "@backstage/plugin-explore-common": "workspace:^" + "@backstage/plugin-search-backend-module-explore": "workspace:^" "@backstage/plugin-search-common": "workspace:^" "@types/express": "*" "@types/supertest": ^2.0.8 @@ -7914,11 +7916,32 @@ __metadata: languageName: unknown linkType: soft +"@backstage/plugin-search-backend-module-catalog@workspace:^, @backstage/plugin-search-backend-module-catalog@workspace:plugins/search-backend-module-catalog": + version: 0.0.0-use.local + resolution: "@backstage/plugin-search-backend-module-catalog@workspace:plugins/search-backend-module-catalog" + dependencies: + "@backstage/backend-common": "workspace:^" + "@backstage/backend-plugin-api": "workspace:^" + "@backstage/backend-tasks": "workspace:^" + "@backstage/backend-test-utils": "workspace:^" + "@backstage/catalog-client": "workspace:^" + "@backstage/catalog-model": "workspace:^" + "@backstage/cli": "workspace:^" + "@backstage/config": "workspace:^" + "@backstage/plugin-catalog-common": "workspace:^" + "@backstage/plugin-permission-common": "workspace:^" + "@backstage/plugin-search-backend-node": "workspace:^" + "@backstage/plugin-search-common": "workspace:^" + msw: ^1.0.0 + languageName: unknown + linkType: soft + "@backstage/plugin-search-backend-module-elasticsearch@workspace:^, @backstage/plugin-search-backend-module-elasticsearch@workspace:plugins/search-backend-module-elasticsearch": version: 0.0.0-use.local resolution: "@backstage/plugin-search-backend-module-elasticsearch@workspace:plugins/search-backend-module-elasticsearch" dependencies: "@backstage/backend-common": "workspace:^" + "@backstage/backend-plugin-api": "workspace:^" "@backstage/cli": "workspace:^" "@backstage/config": "workspace:^" "@backstage/plugin-search-backend-node": "workspace:^" @@ -7936,11 +7959,30 @@ __metadata: languageName: unknown linkType: soft +"@backstage/plugin-search-backend-module-explore@workspace:^, @backstage/plugin-search-backend-module-explore@workspace:plugins/search-backend-module-explore": + version: 0.0.0-use.local + resolution: "@backstage/plugin-search-backend-module-explore@workspace:plugins/search-backend-module-explore" + dependencies: + "@backstage/backend-common": "workspace:^" + "@backstage/backend-plugin-api": "workspace:^" + "@backstage/backend-tasks": "workspace:^" + "@backstage/backend-test-utils": "workspace:^" + "@backstage/cli": "workspace:^" + "@backstage/config": "workspace:^" + "@backstage/plugin-explore-common": "workspace:^" + "@backstage/plugin-search-backend-node": "workspace:^" + "@backstage/plugin-search-common": "workspace:^" + node-fetch: ^2.6.7 + winston: ^3.2.1 + languageName: unknown + linkType: soft + "@backstage/plugin-search-backend-module-pg@workspace:^, @backstage/plugin-search-backend-module-pg@workspace:plugins/search-backend-module-pg": version: 0.0.0-use.local resolution: "@backstage/plugin-search-backend-module-pg@workspace:plugins/search-backend-module-pg" dependencies: "@backstage/backend-common": "workspace:^" + "@backstage/backend-plugin-api": "workspace:^" "@backstage/backend-test-utils": "workspace:^" "@backstage/cli": "workspace:^" "@backstage/config": "workspace:^" @@ -7953,11 +7995,37 @@ __metadata: languageName: unknown linkType: soft +"@backstage/plugin-search-backend-module-techdocs@workspace:^, @backstage/plugin-search-backend-module-techdocs@workspace:plugins/search-backend-module-techdocs": + version: 0.0.0-use.local + resolution: "@backstage/plugin-search-backend-module-techdocs@workspace:plugins/search-backend-module-techdocs" + dependencies: + "@backstage/backend-common": "workspace:^" + "@backstage/backend-plugin-api": "workspace:^" + "@backstage/backend-tasks": "workspace:^" + "@backstage/backend-test-utils": "workspace:^" + "@backstage/catalog-client": "workspace:^" + "@backstage/catalog-model": "workspace:^" + "@backstage/cli": "workspace:^" + "@backstage/config": "workspace:^" + "@backstage/plugin-catalog-common": "workspace:^" + "@backstage/plugin-permission-common": "workspace:^" + "@backstage/plugin-search-backend-node": "workspace:^" + "@backstage/plugin-search-common": "workspace:^" + "@backstage/plugin-techdocs-node": "workspace:^" + lodash: ^4.17.21 + msw: ^1.0.0 + node-fetch: ^2.6.7 + p-limit: ^3.1.0 + winston: ^3.2.1 + languageName: unknown + linkType: soft + "@backstage/plugin-search-backend-node@workspace:^, @backstage/plugin-search-backend-node@workspace:plugins/search-backend-node": version: 0.0.0-use.local resolution: "@backstage/plugin-search-backend-node@workspace:plugins/search-backend-node" dependencies: "@backstage/backend-common": "workspace:^" + "@backstage/backend-plugin-api": "workspace:^" "@backstage/backend-tasks": "workspace:^" "@backstage/cli": "workspace:^" "@backstage/config": "workspace:^" @@ -7979,6 +8047,8 @@ __metadata: resolution: "@backstage/plugin-search-backend@workspace:plugins/search-backend" dependencies: "@backstage/backend-common": "workspace:^" + "@backstage/backend-plugin-api": "workspace:^" + "@backstage/backend-test-utils": "workspace:^" "@backstage/cli": "workspace:^" "@backstage/config": "workspace:^" "@backstage/errors": "workspace:^" @@ -8512,6 +8582,7 @@ __metadata: "@backstage/integration": "workspace:^" "@backstage/plugin-catalog-common": "workspace:^" "@backstage/plugin-permission-common": "workspace:^" + "@backstage/plugin-search-backend-module-techdocs": "workspace:^" "@backstage/plugin-search-backend-node": "workspace:^" "@backstage/plugin-search-common": "workspace:^" "@backstage/plugin-techdocs-node": "workspace:^" @@ -23023,6 +23094,11 @@ __metadata: "@backstage/plugin-app-backend": "workspace:^" "@backstage/plugin-catalog-backend": "workspace:^" "@backstage/plugin-scaffolder-backend": "workspace:^" + "@backstage/plugin-search-backend": "workspace:^" + "@backstage/plugin-search-backend-module-catalog": "workspace:^" + "@backstage/plugin-search-backend-module-explore": "workspace:^" + "@backstage/plugin-search-backend-module-techdocs": "workspace:^" + "@backstage/plugin-search-backend-node": "workspace:^" "@backstage/plugin-techdocs-backend": "workspace:^" "@backstage/plugin-todo-backend": "workspace:^" languageName: unknown