refactor(search-backend): use credential for authorized search engines
Signed-off-by: Camila Belo <camilaibs@gmail.com>
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@backstage/plugin-search-backend-module-elasticsearch': patch
|
||||
'@backstage/plugin-search-backend-module-pg': patch
|
||||
---
|
||||
|
||||
Start importing `QueryTranslator`, `QueryRequestOptions` and `SearchEngine` from the `@backstage/plugin-search-backend-node`.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-search-common': patch
|
||||
---
|
||||
|
||||
Deprecate `QueryTranslator`, `QueryRequestOptions` and `SearchEngine` in favor of the types exported from `@backstage/plugin-search-backend-node`.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-search-backend': patch
|
||||
---
|
||||
|
||||
**BREAKING**: Update the router to use the new `auth` services. The router now requires a discovery service option to get credentials for the permission service.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-search-backend-node': patch
|
||||
---
|
||||
|
||||
Exports `QueryTranslator`, `QueryRequestOptions` and `SearchEngine` types. These new types were extracted from the `@backstage/plugin-search-common` package and the `token` property was deprecated in favor of the a new credentials one.
|
||||
@@ -72,7 +72,6 @@
|
||||
"@backstage/plugin-search-backend-module-pg": "workspace:^",
|
||||
"@backstage/plugin-search-backend-module-techdocs": "workspace:^",
|
||||
"@backstage/plugin-search-backend-node": "workspace:^",
|
||||
"@backstage/plugin-search-common": "workspace:^",
|
||||
"@backstage/plugin-signals-backend": "workspace:^",
|
||||
"@backstage/plugin-signals-node": "workspace:^",
|
||||
"@backstage/plugin-tech-insights-backend": "workspace:^",
|
||||
|
||||
@@ -23,9 +23,9 @@ import { ElasticSearchSearchEngine } from '@backstage/plugin-search-backend-modu
|
||||
import { PgSearchEngine } from '@backstage/plugin-search-backend-module-pg';
|
||||
import {
|
||||
IndexBuilder,
|
||||
SearchEngine,
|
||||
LunrSearchEngine,
|
||||
} from '@backstage/plugin-search-backend-node';
|
||||
import { SearchEngine } from '@backstage/plugin-search-common';
|
||||
import { DefaultTechDocsCollatorFactory } from '@backstage/plugin-search-backend-module-techdocs';
|
||||
import { Router } from 'express';
|
||||
import { PluginEnvironment } from '../types';
|
||||
@@ -117,6 +117,7 @@ export default async function createPlugin(
|
||||
return await createRouter({
|
||||
engine: indexBuilder.getSearchEngine(),
|
||||
types: indexBuilder.getDocumentTypes(),
|
||||
discovery: env.discovery,
|
||||
permissions: env.permissions,
|
||||
config: env.config,
|
||||
logger: env.logger,
|
||||
|
||||
@@ -60,6 +60,7 @@ export default async function createPlugin(
|
||||
engine: indexBuilder.getSearchEngine(),
|
||||
types: indexBuilder.getDocumentTypes(),
|
||||
permissions: env.permissions,
|
||||
discovery: env.discovery,
|
||||
config: env.config,
|
||||
logger: env.logger,
|
||||
});
|
||||
|
||||
@@ -17,7 +17,7 @@ import { IndexableResultSet } from '@backstage/plugin-search-common';
|
||||
import { Logger } from 'winston';
|
||||
import { LoggerService } from '@backstage/backend-plugin-api';
|
||||
import { Readable } from 'stream';
|
||||
import { SearchEngine } from '@backstage/plugin-search-common';
|
||||
import { SearchEngine } from '@backstage/plugin-search-backend-node';
|
||||
import { SearchQuery } from '@backstage/plugin-search-common';
|
||||
import { TransportRequestPromise } from '@opensearch-project/opensearch/lib/Transport';
|
||||
import { TransportRequestPromise as TransportRequestPromise_2 } from '@elastic/elasticsearch/lib/Transport';
|
||||
|
||||
+1
-1
@@ -18,9 +18,9 @@ import {
|
||||
IndexableDocument,
|
||||
IndexableResult,
|
||||
IndexableResultSet,
|
||||
SearchEngine,
|
||||
SearchQuery,
|
||||
} from '@backstage/plugin-search-common';
|
||||
import { SearchEngine } from '@backstage/plugin-search-backend-node';
|
||||
import { isEmpty, isNumber, isNaN as nan } from 'lodash';
|
||||
|
||||
import { AwsSigv4Signer } from '@opensearch-project/opensearch/aws';
|
||||
|
||||
@@ -10,7 +10,7 @@ import { IndexableResultSet } from '@backstage/plugin-search-common';
|
||||
import { Knex } from 'knex';
|
||||
import { Logger } from 'winston';
|
||||
import { PluginDatabaseManager } from '@backstage/backend-common';
|
||||
import { SearchEngine } from '@backstage/plugin-search-common';
|
||||
import { SearchEngine } from '@backstage/plugin-search-backend-node';
|
||||
import { SearchQuery } from '@backstage/plugin-search-common';
|
||||
|
||||
// @public
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
import { PluginDatabaseManager } from '@backstage/backend-common';
|
||||
import { SearchEngine } from '@backstage/plugin-search-common';
|
||||
import { SearchEngine } from '@backstage/plugin-search-backend-node';
|
||||
import {
|
||||
SearchQuery,
|
||||
IndexableResultSet,
|
||||
|
||||
@@ -3,12 +3,39 @@
|
||||
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
|
||||
|
||||
```ts
|
||||
/// <reference types="node" />
|
||||
|
||||
import { BackstageCredentials } from '@backstage/backend-plugin-api';
|
||||
import { DocumentTypeInfo } from '@backstage/plugin-search-common';
|
||||
import { ExtensionPoint } from '@backstage/backend-plugin-api';
|
||||
import { IndexableResultSet } from '@backstage/plugin-search-common';
|
||||
import { RegisterCollatorParameters } from '@backstage/plugin-search-backend-node';
|
||||
import { RegisterDecoratorParameters } from '@backstage/plugin-search-backend-node';
|
||||
import { SearchEngine } from '@backstage/plugin-search-common';
|
||||
import { SearchQuery } from '@backstage/plugin-search-common';
|
||||
import { ServiceRef } from '@backstage/backend-plugin-api';
|
||||
import { Writable } from 'stream';
|
||||
|
||||
// @public
|
||||
export type QueryRequestOptions =
|
||||
| {
|
||||
token?: string;
|
||||
}
|
||||
| {
|
||||
credentials: BackstageCredentials;
|
||||
};
|
||||
|
||||
// @public
|
||||
export type QueryTranslator = (query: SearchQuery) => unknown;
|
||||
|
||||
// @public
|
||||
export interface SearchEngine {
|
||||
getIndexer(type: string): Promise<Writable>;
|
||||
query(
|
||||
query: SearchQuery,
|
||||
options?: QueryRequestOptions,
|
||||
): Promise<IndexableResultSet>;
|
||||
setTranslator(translator: QueryTranslator): void;
|
||||
}
|
||||
|
||||
// @alpha
|
||||
export interface SearchEngineRegistryExtensionPoint {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
```ts
|
||||
/// <reference types="node" />
|
||||
|
||||
import { BackstageCredentials } from '@backstage/backend-plugin-api';
|
||||
import { Config } from '@backstage/config';
|
||||
import { DocumentCollatorFactory } from '@backstage/plugin-search-common';
|
||||
import { DocumentDecoratorFactory } from '@backstage/plugin-search-common';
|
||||
@@ -14,9 +15,7 @@ import { IndexableResultSet } from '@backstage/plugin-search-common';
|
||||
import { Logger } from 'winston';
|
||||
import { default as lunr_2 } from 'lunr';
|
||||
import { Permission } from '@backstage/plugin-permission-common';
|
||||
import { QueryTranslator } from '@backstage/plugin-search-common';
|
||||
import { Readable } from 'stream';
|
||||
import { SearchEngine } from '@backstage/plugin-search-common';
|
||||
import { SearchQuery } from '@backstage/plugin-search-common';
|
||||
import { TaskFunction } from '@backstage/backend-tasks';
|
||||
import { TaskRunner } from '@backstage/backend-tasks';
|
||||
@@ -144,6 +143,18 @@ export type NewlineDelimitedJsonCollatorFactoryOptions = {
|
||||
visibilityPermission?: Permission;
|
||||
};
|
||||
|
||||
// @public
|
||||
export type QueryRequestOptions =
|
||||
| {
|
||||
token?: string;
|
||||
}
|
||||
| {
|
||||
credentials: BackstageCredentials;
|
||||
};
|
||||
|
||||
// @public
|
||||
export type QueryTranslator = (query: SearchQuery) => unknown;
|
||||
|
||||
// @public
|
||||
export interface RegisterCollatorParameters {
|
||||
factory: DocumentCollatorFactory;
|
||||
@@ -170,6 +181,16 @@ export type ScheduleTaskParameters = {
|
||||
scheduledRunner: TaskRunner;
|
||||
};
|
||||
|
||||
// @public
|
||||
export interface SearchEngine {
|
||||
getIndexer(type: string): Promise<Writable>;
|
||||
query(
|
||||
query: SearchQuery,
|
||||
options?: QueryRequestOptions,
|
||||
): Promise<IndexableResultSet>;
|
||||
setTranslator(translator: QueryTranslator): void;
|
||||
}
|
||||
|
||||
// @public
|
||||
export class TestPipeline {
|
||||
execute(): Promise<TestPipelineResult>;
|
||||
|
||||
@@ -19,11 +19,10 @@ import { TaskInvocationDefinition, TaskRunner } from '@backstage/backend-tasks';
|
||||
import {
|
||||
DocumentCollatorFactory,
|
||||
DocumentDecoratorFactory,
|
||||
SearchEngine,
|
||||
} from '@backstage/plugin-search-common';
|
||||
import { Readable, Transform } from 'stream';
|
||||
import { IndexBuilder } from './IndexBuilder';
|
||||
import { LunrSearchEngine } from './index';
|
||||
import { LunrSearchEngine, SearchEngine } from './index';
|
||||
|
||||
class TestDocumentCollatorFactory implements DocumentCollatorFactory {
|
||||
readonly type: string = 'anything';
|
||||
|
||||
@@ -17,12 +17,12 @@
|
||||
import {
|
||||
DocumentDecoratorFactory,
|
||||
DocumentTypeInfo,
|
||||
SearchEngine,
|
||||
} from '@backstage/plugin-search-common';
|
||||
import { Transform, pipeline } from 'stream';
|
||||
import { Logger } from 'winston';
|
||||
import { Scheduler } from './Scheduler';
|
||||
import {
|
||||
SearchEngine,
|
||||
IndexBuilderOptions,
|
||||
RegisterCollatorParameters,
|
||||
RegisterDecoratorParameters,
|
||||
|
||||
@@ -22,10 +22,7 @@ import {
|
||||
coreServices,
|
||||
} from '@backstage/backend-plugin-api';
|
||||
import { loggerToWinstonLogger } from '@backstage/backend-common';
|
||||
import {
|
||||
DocumentTypeInfo,
|
||||
SearchEngine,
|
||||
} from '@backstage/plugin-search-common';
|
||||
import { DocumentTypeInfo } from '@backstage/plugin-search-common';
|
||||
import { createExtensionPoint } from '@backstage/backend-plugin-api';
|
||||
|
||||
import {
|
||||
@@ -33,8 +30,15 @@ import {
|
||||
RegisterDecoratorParameters,
|
||||
} from '@backstage/plugin-search-backend-node';
|
||||
|
||||
import { SearchEngine } from './types';
|
||||
import { IndexBuilder } from './IndexBuilder';
|
||||
|
||||
export type {
|
||||
SearchEngine,
|
||||
QueryRequestOptions,
|
||||
QueryTranslator,
|
||||
} from './types';
|
||||
|
||||
/**
|
||||
* @alpha
|
||||
* Options for build method on {@link SearchIndexService}.
|
||||
|
||||
@@ -16,10 +16,7 @@
|
||||
|
||||
import { getVoidLogger } from '@backstage/backend-common';
|
||||
import lunr from 'lunr';
|
||||
import {
|
||||
IndexableDocument,
|
||||
SearchEngine,
|
||||
} from '@backstage/plugin-search-common';
|
||||
import { IndexableDocument } from '@backstage/plugin-search-common';
|
||||
import {
|
||||
ConcreteLunrQuery,
|
||||
LunrSearchEngine,
|
||||
@@ -28,6 +25,7 @@ import {
|
||||
parseHighlightFields,
|
||||
} from './LunrSearchEngine';
|
||||
import { LunrSearchEngineIndexer } from './LunrSearchEngineIndexer';
|
||||
import { SearchEngine } from '../types';
|
||||
import { TestPipeline } from '../test-utils';
|
||||
|
||||
/**
|
||||
|
||||
@@ -18,9 +18,8 @@ import {
|
||||
IndexableDocument,
|
||||
IndexableResultSet,
|
||||
SearchQuery,
|
||||
QueryTranslator,
|
||||
SearchEngine,
|
||||
} from '@backstage/plugin-search-common';
|
||||
import { SearchEngine, QueryTranslator } from '../types';
|
||||
import { MissingIndexError } from '../errors';
|
||||
import lunr from 'lunr';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
|
||||
@@ -33,6 +33,9 @@ export type {
|
||||
IndexBuilderOptions,
|
||||
RegisterCollatorParameters,
|
||||
RegisterDecoratorParameters,
|
||||
SearchEngine,
|
||||
QueryRequestOptions,
|
||||
QueryTranslator,
|
||||
} from './types';
|
||||
export * from './errors';
|
||||
export * from './indexing';
|
||||
|
||||
@@ -14,12 +14,15 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { BackstageCredentials } from '@backstage/backend-plugin-api';
|
||||
import { TaskRunner } from '@backstage/backend-tasks';
|
||||
import {
|
||||
DocumentCollatorFactory,
|
||||
DocumentDecoratorFactory,
|
||||
SearchEngine,
|
||||
IndexableResultSet,
|
||||
SearchQuery,
|
||||
} from '@backstage/plugin-search-common';
|
||||
import { Writable } from 'stream';
|
||||
import { Logger } from 'winston';
|
||||
|
||||
/**
|
||||
@@ -57,3 +60,55 @@ export interface RegisterDecoratorParameters {
|
||||
*/
|
||||
factory: DocumentDecoratorFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* A type of function responsible for translating an abstract search query into
|
||||
* a concrete query relevant to a particular search engine.
|
||||
* @public
|
||||
*/
|
||||
export type QueryTranslator = (query: SearchQuery) => unknown;
|
||||
|
||||
/**
|
||||
* Options when querying a search engine.
|
||||
* @public
|
||||
*/
|
||||
export type QueryRequestOptions =
|
||||
| {
|
||||
/** @deprecated use the `credentials` option instead. */
|
||||
token?: string;
|
||||
}
|
||||
| {
|
||||
credentials: BackstageCredentials;
|
||||
};
|
||||
|
||||
/**
|
||||
* Interface that must be implemented by specific search engines, responsible
|
||||
* for performing indexing and querying and translating abstract queries into
|
||||
* concrete, search engine-specific queries.
|
||||
* @public
|
||||
*/
|
||||
export interface SearchEngine {
|
||||
/**
|
||||
* Override the default translator provided by the SearchEngine.
|
||||
*/
|
||||
setTranslator(translator: QueryTranslator): void;
|
||||
|
||||
/**
|
||||
* Factory method for getting a search engine indexer for a given document
|
||||
* type.
|
||||
*
|
||||
* @param type - The type or name of the document set for which an indexer
|
||||
* should be retrieved. This corresponds to the `type` property on the
|
||||
* document collator/decorator factories and will most often be used to
|
||||
* identify an index or group to which documents should be written.
|
||||
*/
|
||||
getIndexer(type: string): Promise<Writable>;
|
||||
|
||||
/**
|
||||
* Perform a search query against the SearchEngine.
|
||||
*/
|
||||
query(
|
||||
query: SearchQuery,
|
||||
options?: QueryRequestOptions,
|
||||
): Promise<IndexableResultSet>;
|
||||
}
|
||||
|
||||
@@ -3,13 +3,16 @@
|
||||
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
|
||||
|
||||
```ts
|
||||
import { AuthService } from '@backstage/backend-plugin-api';
|
||||
import { Config } from '@backstage/config';
|
||||
import { DiscoveryService } from '@backstage/backend-plugin-api';
|
||||
import { DocumentTypeInfo } from '@backstage/plugin-search-common';
|
||||
import express from 'express';
|
||||
import { HttpAuthService } from '@backstage/backend-plugin-api';
|
||||
import { Logger } from 'winston';
|
||||
import { PermissionAuthorizer } from '@backstage/plugin-permission-common';
|
||||
import { PermissionEvaluator } from '@backstage/plugin-permission-common';
|
||||
import { SearchEngine } from '@backstage/plugin-search-common';
|
||||
import { SearchEngine } from '@backstage/plugin-search-backend-node';
|
||||
|
||||
// @public (undocumented)
|
||||
export function createRouter(options: RouterOptions): Promise<express.Router>;
|
||||
@@ -18,8 +21,11 @@ export function createRouter(options: RouterOptions): Promise<express.Router>;
|
||||
export type RouterOptions = {
|
||||
engine: SearchEngine;
|
||||
types: Record<string, DocumentTypeInfo>;
|
||||
discovery: DiscoveryService;
|
||||
permissions: PermissionEvaluator | PermissionAuthorizer;
|
||||
config: Config;
|
||||
logger: Logger;
|
||||
auth?: AuthService;
|
||||
httpAuth?: HttpAuthService;
|
||||
};
|
||||
```
|
||||
|
||||
@@ -22,6 +22,7 @@ import { loggerToWinstonLogger } from '@backstage/backend-common';
|
||||
import {
|
||||
RegisterCollatorParameters,
|
||||
RegisterDecoratorParameters,
|
||||
SearchEngine,
|
||||
LunrSearchEngine,
|
||||
} from '@backstage/plugin-search-backend-node';
|
||||
import {
|
||||
@@ -33,7 +34,6 @@ import {
|
||||
} 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[] = [];
|
||||
@@ -94,11 +94,23 @@ export default createBackendPlugin({
|
||||
deps: {
|
||||
logger: coreServices.logger,
|
||||
config: coreServices.rootConfig,
|
||||
discovery: coreServices.discovery,
|
||||
permissions: coreServices.permissions,
|
||||
auth: coreServices.auth,
|
||||
http: coreServices.httpRouter,
|
||||
httpAuth: coreServices.httpAuth,
|
||||
searchIndexService: searchIndexServiceRef,
|
||||
},
|
||||
async init({ config, logger, permissions, http, searchIndexService }) {
|
||||
async init({
|
||||
config,
|
||||
logger,
|
||||
discovery,
|
||||
permissions,
|
||||
auth,
|
||||
http,
|
||||
httpAuth,
|
||||
searchIndexService,
|
||||
}) {
|
||||
let searchEngine = searchEngineRegistry.getSearchEngine();
|
||||
if (!searchEngine) {
|
||||
searchEngine = new LunrSearchEngine({
|
||||
@@ -117,7 +129,10 @@ export default createBackendPlugin({
|
||||
|
||||
const router = await createRouter({
|
||||
config,
|
||||
discovery,
|
||||
permissions,
|
||||
auth,
|
||||
httpAuth,
|
||||
logger: loggerToWinstonLogger(logger),
|
||||
engine: searchEngine,
|
||||
types: searchIndexService.getDocumentTypes(),
|
||||
|
||||
@@ -25,8 +25,8 @@ import {
|
||||
import {
|
||||
DocumentTypeInfo,
|
||||
IndexableDocument,
|
||||
SearchEngine,
|
||||
} from '@backstage/plugin-search-common';
|
||||
import { SearchEngine } from '@backstage/plugin-search-backend-node';
|
||||
import {
|
||||
encodePageCursor,
|
||||
decodePageCursor,
|
||||
|
||||
@@ -23,21 +23,23 @@ import {
|
||||
EvaluatePermissionRequest,
|
||||
EvaluatePermissionResponse,
|
||||
isResourcePermission,
|
||||
PermissionEvaluator,
|
||||
QueryPermissionRequest,
|
||||
} from '@backstage/plugin-permission-common';
|
||||
import {
|
||||
DocumentTypeInfo,
|
||||
IndexableResult,
|
||||
IndexableResultSet,
|
||||
SearchQuery,
|
||||
} from '@backstage/plugin-search-common';
|
||||
import {
|
||||
QueryRequestOptions,
|
||||
QueryTranslator,
|
||||
SearchEngine,
|
||||
SearchQuery,
|
||||
} from '@backstage/plugin-search-common';
|
||||
} from '@backstage/plugin-search-backend-node';
|
||||
import { Config } from '@backstage/config';
|
||||
import { InputError } from '@backstage/errors';
|
||||
import { Writable } from 'stream';
|
||||
import { PermissionsService } from '@backstage/backend-plugin-api';
|
||||
|
||||
export function decodePageCursor(pageCursor?: string): { page: number } {
|
||||
if (!pageCursor) {
|
||||
@@ -68,7 +70,7 @@ export class AuthorizedSearchEngine implements SearchEngine {
|
||||
constructor(
|
||||
private readonly searchEngine: SearchEngine,
|
||||
private readonly types: Record<string, DocumentTypeInfo>,
|
||||
private readonly permissions: PermissionEvaluator,
|
||||
private readonly permissions: PermissionsService,
|
||||
config: Config,
|
||||
) {
|
||||
this.queryLatencyBudgetMs =
|
||||
|
||||
@@ -14,16 +14,22 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { getVoidLogger } from '@backstage/backend-common';
|
||||
import {
|
||||
PluginEndpointDiscovery,
|
||||
getVoidLogger,
|
||||
} from '@backstage/backend-common';
|
||||
import { ConfigReader } from '@backstage/config';
|
||||
import { PermissionEvaluator } from '@backstage/plugin-permission-common';
|
||||
import { IndexBuilder } from '@backstage/plugin-search-backend-node';
|
||||
import { SearchEngine } from '@backstage/plugin-search-common';
|
||||
import {
|
||||
IndexBuilder,
|
||||
SearchEngine,
|
||||
} from '@backstage/plugin-search-backend-node';
|
||||
import express from 'express';
|
||||
import request from 'supertest';
|
||||
import { createRouter } from './router';
|
||||
import { wrapInOpenApiTestServer } from '@backstage/backend-openapi-utils';
|
||||
import { Server } from 'http';
|
||||
import { mockCredentials, mockServices } from '@backstage/backend-test-utils';
|
||||
|
||||
const mockPermissionEvaluator: PermissionEvaluator = {
|
||||
authorize: () => {
|
||||
@@ -38,6 +44,16 @@ describe('createRouter', () => {
|
||||
let app: express.Express | Server;
|
||||
let mockSearchEngine: jest.Mocked<SearchEngine>;
|
||||
|
||||
const mockBaseUrl = 'http://backstage:9191/api/proxy';
|
||||
const discovery: PluginEndpointDiscovery = {
|
||||
async getBaseUrl() {
|
||||
return mockBaseUrl;
|
||||
},
|
||||
async getExternalBaseUrl() {
|
||||
return mockBaseUrl;
|
||||
},
|
||||
};
|
||||
|
||||
beforeAll(async () => {
|
||||
const logger = getVoidLogger();
|
||||
mockSearchEngine = {
|
||||
@@ -65,7 +81,10 @@ describe('createRouter', () => {
|
||||
search: { maxPageLimit: 200, maxTermLength: 20 },
|
||||
}),
|
||||
permissions: mockPermissionEvaluator,
|
||||
discovery,
|
||||
logger,
|
||||
auth: mockServices.auth(),
|
||||
httpAuth: mockServices.httpAuth(),
|
||||
});
|
||||
app = wrapInOpenApiTestServer(express().use(router));
|
||||
});
|
||||
@@ -227,7 +246,11 @@ describe('createRouter', () => {
|
||||
unknownKey2: 'unknownValue1',
|
||||
};
|
||||
const secondArg = {
|
||||
token: undefined,
|
||||
credentials: mockCredentials.user(),
|
||||
token: mockCredentials.service.token({
|
||||
onBehalfOf: mockCredentials.user(),
|
||||
targetPluginId: 'search',
|
||||
}),
|
||||
};
|
||||
expect(response.status).toEqual(200);
|
||||
expect(mockSearchEngine.query).toHaveBeenCalledWith(firstArg, secondArg);
|
||||
@@ -251,6 +274,7 @@ describe('createRouter', () => {
|
||||
types: indexBuilder.getDocumentTypes(),
|
||||
config: new ConfigReader({ permissions: { enabled: false } }),
|
||||
permissions: mockPermissionEvaluator,
|
||||
discovery,
|
||||
logger,
|
||||
});
|
||||
app = express().use(router);
|
||||
|
||||
@@ -17,11 +17,13 @@
|
||||
import express from 'express';
|
||||
import { Logger } from 'winston';
|
||||
import { z } from 'zod';
|
||||
import { errorHandler } from '@backstage/backend-common';
|
||||
import {
|
||||
createLegacyAuthAdapters,
|
||||
errorHandler,
|
||||
} from '@backstage/backend-common';
|
||||
import { InputError } from '@backstage/errors';
|
||||
import { Config } from '@backstage/config';
|
||||
import { JsonObject, JsonValue } from '@backstage/types';
|
||||
import { getBearerTokenFromAuthorizationHeader } from '@backstage/plugin-auth-node';
|
||||
import {
|
||||
PermissionAuthorizer,
|
||||
PermissionEvaluator,
|
||||
@@ -32,9 +34,14 @@ import {
|
||||
IndexableResultSet,
|
||||
SearchResultSet,
|
||||
} from '@backstage/plugin-search-common';
|
||||
import { SearchEngine } from '@backstage/plugin-search-common';
|
||||
import { SearchEngine } from '@backstage/plugin-search-backend-node';
|
||||
import { AuthorizedSearchEngine } from './AuthorizedSearchEngine';
|
||||
import { createOpenApiRouter } from '../schema/openapi.generated';
|
||||
import {
|
||||
AuthService,
|
||||
DiscoveryService,
|
||||
HttpAuthService,
|
||||
} from '@backstage/backend-plugin-api';
|
||||
|
||||
const jsonObjectSchema: z.ZodSchema<JsonObject> = z.lazy(() => {
|
||||
const jsonValueSchema: z.ZodSchema<JsonValue> = z.lazy(() =>
|
||||
@@ -57,9 +64,12 @@ const jsonObjectSchema: z.ZodSchema<JsonObject> = z.lazy(() => {
|
||||
export type RouterOptions = {
|
||||
engine: SearchEngine;
|
||||
types: Record<string, DocumentTypeInfo>;
|
||||
discovery: DiscoveryService;
|
||||
permissions: PermissionEvaluator | PermissionAuthorizer;
|
||||
config: Config;
|
||||
logger: Logger;
|
||||
auth?: AuthService;
|
||||
httpAuth?: HttpAuthService;
|
||||
};
|
||||
|
||||
const defaultMaxPageLimit = 100;
|
||||
@@ -75,6 +85,8 @@ export async function createRouter(
|
||||
const router = await createOpenApiRouter();
|
||||
const { engine: inputEngine, types, permissions, config, logger } = options;
|
||||
|
||||
const { auth, httpAuth } = createLegacyAuthAdapters(options);
|
||||
|
||||
const maxPageLimit =
|
||||
config.getOptionalNumber('search.maxPageLimit') ?? defaultMaxPageLimit;
|
||||
|
||||
@@ -169,12 +181,16 @@ export async function createRouter(
|
||||
}`,
|
||||
);
|
||||
|
||||
const token = getBearerTokenFromAuthorizationHeader(
|
||||
req.header('authorization'),
|
||||
);
|
||||
|
||||
try {
|
||||
const resultSet = await engine?.query(query, { token });
|
||||
const credentials = await httpAuth.credentials(req);
|
||||
const { token } = await auth.getPluginRequestToken({
|
||||
onBehalfOf: credentials,
|
||||
targetPluginId: 'search',
|
||||
});
|
||||
const resultSet = await engine?.query(query, {
|
||||
token,
|
||||
credentials,
|
||||
});
|
||||
|
||||
res.json(filterResultSet(toSearchResults(resultSet)));
|
||||
} catch (error) {
|
||||
|
||||
@@ -57,6 +57,7 @@ export async function startStandaloneServer(
|
||||
const router = await createRouter({
|
||||
engine: indexBuilder.getSearchEngine(),
|
||||
types: indexBuilder.getDocumentTypes(),
|
||||
discovery,
|
||||
permissions,
|
||||
config,
|
||||
logger,
|
||||
|
||||
@@ -42,12 +42,12 @@ export type IndexableResult = Result<IndexableDocument>;
|
||||
// @public (undocumented)
|
||||
export type IndexableResultSet = ResultSet<IndexableDocument>;
|
||||
|
||||
// @public
|
||||
// @public @deprecated
|
||||
export type QueryRequestOptions = {
|
||||
token?: string;
|
||||
};
|
||||
|
||||
// @public
|
||||
// @public @deprecated
|
||||
export type QueryTranslator = (query: SearchQuery) => unknown;
|
||||
|
||||
// @public (undocumented)
|
||||
@@ -87,7 +87,7 @@ export interface SearchDocument {
|
||||
title: string;
|
||||
}
|
||||
|
||||
// @public
|
||||
// @public @deprecated
|
||||
export interface SearchEngine {
|
||||
getIndexer(type: string): Promise<Writable>;
|
||||
query(
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright 2024 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 { Writable } from 'stream';
|
||||
import { SearchQuery, IndexableResultSet } from './types';
|
||||
|
||||
/**
|
||||
* A type of function responsible for translating an abstract search query into
|
||||
* a concrete query relevant to a particular search engine.
|
||||
* @public
|
||||
* @deprecated Import from `@backstage/plugin-search-backend-node` instead
|
||||
*/
|
||||
export type QueryTranslator = (query: SearchQuery) => unknown;
|
||||
|
||||
/**
|
||||
* Options when querying a search engine.
|
||||
* @public
|
||||
* @deprecated Import from `@backstage/plugin-search-backend-node` instead
|
||||
*/
|
||||
export type QueryRequestOptions = {
|
||||
token?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Interface that must be implemented by specific search engines, responsible
|
||||
* for performing indexing and querying and translating abstract queries into
|
||||
* concrete, search engine-specific queries.
|
||||
* @public
|
||||
* @deprecated Import from `@backstage/plugin-search-backend-node` instead
|
||||
*/
|
||||
export interface SearchEngine {
|
||||
/**
|
||||
* Override the default translator provided by the SearchEngine.
|
||||
*/
|
||||
setTranslator(translator: QueryTranslator): void;
|
||||
|
||||
/**
|
||||
* Factory method for getting a search engine indexer for a given document
|
||||
* type.
|
||||
*
|
||||
* @param type - The type or name of the document set for which an indexer
|
||||
* should be retrieved. This corresponds to the `type` property on the
|
||||
* document collator/decorator factories and will most often be used to
|
||||
* identify an index or group to which documents should be written.
|
||||
*/
|
||||
getIndexer(type: string): Promise<Writable>;
|
||||
|
||||
/**
|
||||
* Perform a search query against the SearchEngine.
|
||||
*/
|
||||
query(
|
||||
query: SearchQuery,
|
||||
options?: QueryRequestOptions,
|
||||
): Promise<IndexableResultSet>;
|
||||
}
|
||||
@@ -21,3 +21,4 @@
|
||||
*/
|
||||
|
||||
export * from './types';
|
||||
export * from './deprecated';
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
import { Permission } from '@backstage/plugin-permission-common';
|
||||
import { JsonObject } from '@backstage/types';
|
||||
import { Readable, Transform, Writable } from 'stream';
|
||||
import { Readable, Transform } from 'stream';
|
||||
|
||||
/**
|
||||
* @public
|
||||
@@ -206,50 +206,3 @@ export interface DocumentDecoratorFactory {
|
||||
*/
|
||||
getDecorator(): Promise<Transform>;
|
||||
}
|
||||
|
||||
/**
|
||||
* A type of function responsible for translating an abstract search query into
|
||||
* a concrete query relevant to a particular search engine.
|
||||
* @public
|
||||
*/
|
||||
export type QueryTranslator = (query: SearchQuery) => unknown;
|
||||
|
||||
/**
|
||||
* Options when querying a search engine.
|
||||
* @public
|
||||
*/
|
||||
export type QueryRequestOptions = {
|
||||
token?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Interface that must be implemented by specific search engines, responsible
|
||||
* for performing indexing and querying and translating abstract queries into
|
||||
* concrete, search engine-specific queries.
|
||||
* @public
|
||||
*/
|
||||
export interface SearchEngine {
|
||||
/**
|
||||
* Override the default translator provided by the SearchEngine.
|
||||
*/
|
||||
setTranslator(translator: QueryTranslator): void;
|
||||
|
||||
/**
|
||||
* Factory method for getting a search engine indexer for a given document
|
||||
* type.
|
||||
*
|
||||
* @param type - The type or name of the document set for which an indexer
|
||||
* should be retrieved. This corresponds to the `type` property on the
|
||||
* document collator/decorator factories and will most often be used to
|
||||
* identify an index or group to which documents should be written.
|
||||
*/
|
||||
getIndexer(type: string): Promise<Writable>;
|
||||
|
||||
/**
|
||||
* Perform a search query against the SearchEngine.
|
||||
*/
|
||||
query(
|
||||
query: SearchQuery,
|
||||
options?: QueryRequestOptions,
|
||||
): Promise<IndexableResultSet>;
|
||||
}
|
||||
|
||||
@@ -27469,7 +27469,6 @@ __metadata:
|
||||
"@backstage/plugin-search-backend-module-pg": "workspace:^"
|
||||
"@backstage/plugin-search-backend-module-techdocs": "workspace:^"
|
||||
"@backstage/plugin-search-backend-node": "workspace:^"
|
||||
"@backstage/plugin-search-common": "workspace:^"
|
||||
"@backstage/plugin-signals-backend": "workspace:^"
|
||||
"@backstage/plugin-signals-node": "workspace:^"
|
||||
"@backstage/plugin-tech-insights-backend": "workspace:^"
|
||||
|
||||
Reference in New Issue
Block a user