Migrate techdocs plugins from alpha catalogServiceRef to stable
Migrated `@backstage/plugin-techdocs-backend` and `@backstage/plugin-search-backend-module-techdocs` to use the stable `catalogServiceRef` from `@backstage/plugin-catalog-node` instead of the deprecated one from `@backstage/plugin-catalog-node/alpha`. This also updates `CachedEntityLoader`, `DefaultTechDocsCollatorFactory`, and the TechDocs router to use `CatalogService` (credentials-based) instead of `CatalogApi` (token-based). Signed-off-by: Fredrik Adelöw <freben@spotify.com> Made-with: Cursor
This commit is contained in:
@@ -34,7 +34,7 @@ import {
|
||||
techdocsPreparerExtensionPoint,
|
||||
techdocsPublisherExtensionPoint,
|
||||
} from '@backstage/plugin-techdocs-node';
|
||||
import { catalogServiceRef } from '@backstage/plugin-catalog-node/alpha';
|
||||
import { catalogServiceRef } from '@backstage/plugin-catalog-node';
|
||||
import * as winston from 'winston';
|
||||
import { createRouter } from './service/router';
|
||||
|
||||
|
||||
@@ -39,8 +39,6 @@ describe('CachedEntityLoader', () => {
|
||||
},
|
||||
};
|
||||
|
||||
const token = 'test-token';
|
||||
|
||||
const userCredentials: BackstageCredentials = {
|
||||
$$type: '@backstage/BackstageCredentials',
|
||||
principal: {
|
||||
@@ -67,7 +65,7 @@ describe('CachedEntityLoader', () => {
|
||||
auth.isPrincipal.mockReturnValue(true);
|
||||
|
||||
const loader = new CachedEntityLoader({ auth, catalog, cache });
|
||||
const result = await loader.load(userCredentials, entityName, token);
|
||||
const result = await loader.load(userCredentials, entityName);
|
||||
|
||||
expect(result).toEqual(entity);
|
||||
expect(cache.set).toHaveBeenCalledWith(
|
||||
@@ -84,7 +82,7 @@ describe('CachedEntityLoader', () => {
|
||||
auth.isPrincipal.mockReturnValue(true);
|
||||
|
||||
const loader = new CachedEntityLoader({ auth, catalog, cache });
|
||||
const result = await loader.load(userCredentials, entityName, token);
|
||||
const result = await loader.load(userCredentials, entityName);
|
||||
|
||||
expect(result).toEqual(entity);
|
||||
expect(catalog.getEntityByRef).not.toHaveBeenCalled();
|
||||
@@ -96,7 +94,7 @@ describe('CachedEntityLoader', () => {
|
||||
auth.isPrincipal.mockReturnValue(true);
|
||||
|
||||
const loader = new CachedEntityLoader({ auth, catalog, cache });
|
||||
const result = await loader.load(userCredentials, entityName, token);
|
||||
const result = await loader.load(userCredentials, entityName);
|
||||
|
||||
expect(result).toBeUndefined();
|
||||
expect(cache.set).not.toHaveBeenCalled();
|
||||
@@ -108,7 +106,7 @@ describe('CachedEntityLoader', () => {
|
||||
auth.isPrincipal.mockReturnValueOnce(false).mockReturnValueOnce(true);
|
||||
|
||||
const loader = new CachedEntityLoader({ auth, catalog, cache });
|
||||
const result = await loader.load(pluginCredentials, entityName, undefined);
|
||||
const result = await loader.load(pluginCredentials, entityName);
|
||||
|
||||
expect(result).toEqual(entity);
|
||||
expect(cache.set).toHaveBeenCalledWith(
|
||||
@@ -131,7 +129,7 @@ describe('CachedEntityLoader', () => {
|
||||
auth.isPrincipal.mockReturnValue(true);
|
||||
|
||||
const loader = new CachedEntityLoader({ auth, catalog, cache });
|
||||
const result = await loader.load(userCredentials, entityName, token);
|
||||
const result = await loader.load(userCredentials, entityName);
|
||||
|
||||
expect(result).toEqual(entity);
|
||||
});
|
||||
@@ -151,8 +149,8 @@ describe('CachedEntityLoader', () => {
|
||||
},
|
||||
};
|
||||
|
||||
await loader.load(userCredentials, entityName, token);
|
||||
await loader.load(anotherUserCredentials, entityName, token);
|
||||
await loader.load(userCredentials, entityName);
|
||||
await loader.load(anotherUserCredentials, entityName);
|
||||
|
||||
expect(cache.set).toHaveBeenCalledWith(
|
||||
'catalog:component:default/test:user:default/test-user',
|
||||
@@ -172,7 +170,7 @@ describe('CachedEntityLoader', () => {
|
||||
auth.isPrincipal.mockReturnValueOnce(false).mockReturnValueOnce(true);
|
||||
|
||||
const loader = new CachedEntityLoader({ auth, catalog, cache });
|
||||
const result = await loader.load(pluginCredentials, entityName, token);
|
||||
const result = await loader.load(pluginCredentials, entityName);
|
||||
|
||||
expect(result).toEqual(entity);
|
||||
expect(cache.set).toHaveBeenCalledWith(
|
||||
@@ -197,7 +195,7 @@ describe('CachedEntityLoader', () => {
|
||||
};
|
||||
|
||||
const loader = new CachedEntityLoader({ auth, catalog, cache });
|
||||
const result = await loader.load(unknownCredentials, entityName, token);
|
||||
const result = await loader.load(unknownCredentials, entityName);
|
||||
|
||||
expect(result).toEqual(entity);
|
||||
expect(cache.set).toHaveBeenCalledWith(
|
||||
|
||||
@@ -19,22 +19,22 @@ import {
|
||||
BackstageCredentials,
|
||||
CacheService,
|
||||
} from '@backstage/backend-plugin-api';
|
||||
import { CatalogApi } from '@backstage/catalog-client';
|
||||
import {
|
||||
Entity,
|
||||
CompoundEntityRef,
|
||||
stringifyEntityRef,
|
||||
} from '@backstage/catalog-model';
|
||||
import { CatalogService } from '@backstage/plugin-catalog-node';
|
||||
|
||||
export type CachedEntityLoaderOptions = {
|
||||
auth: AuthService;
|
||||
catalog: CatalogApi;
|
||||
catalog: CatalogService;
|
||||
cache: CacheService;
|
||||
};
|
||||
|
||||
export class CachedEntityLoader {
|
||||
private readonly auth: AuthService;
|
||||
private readonly catalog: CatalogApi;
|
||||
private readonly catalog: CatalogService;
|
||||
private readonly cache: CacheService;
|
||||
private readonly readTimeout = 1000;
|
||||
|
||||
@@ -47,7 +47,6 @@ export class CachedEntityLoader {
|
||||
async load(
|
||||
credentials: BackstageCredentials,
|
||||
entityRef: CompoundEntityRef,
|
||||
token: string | undefined,
|
||||
): Promise<Entity | undefined> {
|
||||
const cacheKey = this.getCacheKey(entityRef, credentials);
|
||||
let result = await this.getFromCache(cacheKey);
|
||||
@@ -56,7 +55,7 @@ export class CachedEntityLoader {
|
||||
return result;
|
||||
}
|
||||
|
||||
result = await this.catalog.getEntityByRef(entityRef, { token });
|
||||
result = await this.catalog.getEntityByRef(entityRef, { credentials });
|
||||
|
||||
if (result) {
|
||||
this.cache.set(cacheKey, result, { ttl: 5000 });
|
||||
|
||||
@@ -28,8 +28,8 @@ import { CachedEntityLoader } from './CachedEntityLoader';
|
||||
import { createEventStream, createRouter, RouterOptions } from './router';
|
||||
import { TechDocsCache } from '../cache';
|
||||
import { mockErrorHandler, mockServices } from '@backstage/backend-test-utils';
|
||||
import { catalogServiceMock } from '@backstage/plugin-catalog-node/testUtils';
|
||||
|
||||
jest.mock('@backstage/catalog-client');
|
||||
jest.mock('./CachedEntityLoader');
|
||||
jest.mock('./DocsSynchronizer');
|
||||
jest.mock('../cache/TechDocsCache');
|
||||
@@ -107,6 +107,7 @@ describe('createRouter', () => {
|
||||
const docsBuildStrategy: jest.Mocked<DocsBuildStrategy> = {
|
||||
shouldBuild: jest.fn(),
|
||||
};
|
||||
const mockCatalogService = catalogServiceMock();
|
||||
const outOfTheBoxOptions = {
|
||||
preparers,
|
||||
generators,
|
||||
@@ -124,6 +125,7 @@ describe('createRouter', () => {
|
||||
docsBuildStrategy,
|
||||
auth: mockServices.auth(),
|
||||
httpAuth: mockServices.httpAuth(),
|
||||
catalogClient: mockCatalogService,
|
||||
};
|
||||
const recommendedOptions = {
|
||||
publisher,
|
||||
@@ -134,6 +136,7 @@ describe('createRouter', () => {
|
||||
docsBuildStrategy,
|
||||
auth: mockServices.auth(),
|
||||
httpAuth: mockServices.httpAuth(),
|
||||
catalogClient: mockCatalogService,
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { CatalogApi, CatalogClient } from '@backstage/catalog-client';
|
||||
import { stringifyEntityRef } from '@backstage/catalog-model';
|
||||
import { Config, readDurationFromConfig } from '@backstage/config';
|
||||
import { NotFoundError } from '@backstage/errors';
|
||||
@@ -41,6 +40,7 @@ import {
|
||||
HttpAuthService,
|
||||
LoggerService,
|
||||
} from '@backstage/backend-plugin-api';
|
||||
import { CatalogService } from '@backstage/plugin-catalog-node';
|
||||
import { durationToMilliseconds } from '@backstage/types';
|
||||
|
||||
/**
|
||||
@@ -60,7 +60,7 @@ export type OutOfTheBoxDeploymentOptions = {
|
||||
cache: CacheService;
|
||||
docsBuildStrategy?: DocsBuildStrategy;
|
||||
buildLogTransport?: winston.transport;
|
||||
catalogClient?: CatalogApi;
|
||||
catalogClient?: CatalogService;
|
||||
httpAuth: HttpAuthService;
|
||||
auth: AuthService;
|
||||
};
|
||||
@@ -79,7 +79,7 @@ export type RecommendedDeploymentOptions = {
|
||||
cache: CacheService;
|
||||
docsBuildStrategy?: DocsBuildStrategy;
|
||||
buildLogTransport?: winston.transport;
|
||||
catalogClient?: CatalogApi;
|
||||
catalogClient?: CatalogService;
|
||||
httpAuth: HttpAuthService;
|
||||
auth: AuthService;
|
||||
};
|
||||
@@ -116,8 +116,10 @@ export async function createRouter(
|
||||
const router = Router();
|
||||
const { publisher, config, logger, discovery, httpAuth, auth } = options;
|
||||
|
||||
const catalogClient =
|
||||
options.catalogClient ?? new CatalogClient({ discoveryApi: discovery });
|
||||
if (!options.catalogClient) {
|
||||
throw new Error('catalogClient is required');
|
||||
}
|
||||
const catalogClient = options.catalogClient;
|
||||
const docsBuildStrategy =
|
||||
options.docsBuildStrategy ?? DefaultDocsBuildStrategy.fromConfig(config);
|
||||
const buildLogTransport = options.buildLogTransport;
|
||||
@@ -163,13 +165,8 @@ export async function createRouter(
|
||||
|
||||
const credentials = await httpAuth.credentials(req);
|
||||
|
||||
const { token } = await auth.getPluginRequestToken({
|
||||
onBehalfOf: credentials,
|
||||
targetPluginId: 'catalog',
|
||||
});
|
||||
|
||||
// Verify that the related entity exists and the current user has permission to view it.
|
||||
const entity = await entityLoader.load(credentials, entityName, token);
|
||||
const entity = await entityLoader.load(credentials, entityName);
|
||||
|
||||
if (!entity) {
|
||||
throw new NotFoundError(
|
||||
@@ -202,12 +199,7 @@ export async function createRouter(
|
||||
|
||||
const credentials = await httpAuth.credentials(req);
|
||||
|
||||
const { token } = await auth.getPluginRequestToken({
|
||||
onBehalfOf: credentials,
|
||||
targetPluginId: 'catalog',
|
||||
});
|
||||
|
||||
const entity = await entityLoader.load(credentials, entityName, token);
|
||||
const entity = await entityLoader.load(credentials, entityName);
|
||||
|
||||
if (!entity) {
|
||||
throw new NotFoundError(
|
||||
@@ -240,17 +232,12 @@ export async function createRouter(
|
||||
|
||||
const credentials = await httpAuth.credentials(req);
|
||||
|
||||
const { token } = await auth.getPluginRequestToken({
|
||||
onBehalfOf: credentials,
|
||||
targetPluginId: 'catalog',
|
||||
const entity = await entityLoader.load(credentials, {
|
||||
kind,
|
||||
namespace,
|
||||
name,
|
||||
});
|
||||
|
||||
const entity = await entityLoader.load(
|
||||
credentials,
|
||||
{ kind, namespace, name },
|
||||
token,
|
||||
);
|
||||
|
||||
if (!entity?.metadata?.uid) {
|
||||
throw new NotFoundError('Entity metadata UID missing');
|
||||
}
|
||||
@@ -315,12 +302,7 @@ export async function createRouter(
|
||||
allowLimitedAccess: true,
|
||||
});
|
||||
|
||||
const { token } = await auth.getPluginRequestToken({
|
||||
onBehalfOf: credentials,
|
||||
targetPluginId: 'catalog',
|
||||
});
|
||||
|
||||
const entity = await entityLoader.load(credentials, entityName, token);
|
||||
const entity = await entityLoader.load(credentials, entityName);
|
||||
|
||||
if (!entity) {
|
||||
throw new NotFoundError(
|
||||
|
||||
Reference in New Issue
Block a user