use the non-alpha catalog service

Signed-off-by: Fredrik Adelöw <freben@gmail.com>
This commit is contained in:
Fredrik Adelöw
2025-04-15 23:25:59 +02:00
parent 5cdfe05cba
commit ff95e42895
7 changed files with 57 additions and 72 deletions
+4 -4
View File
@@ -24,7 +24,7 @@ import {
AuthProviderFactory,
authProvidersExtensionPoint,
} from '@backstage/plugin-auth-node';
import { catalogServiceRef } from '@backstage/plugin-catalog-node/alpha';
import { catalogServiceRef } from '@backstage/plugin-catalog-node';
import { createRouter } from './service/router';
/**
@@ -66,7 +66,7 @@ export const authPlugin = createBackendPlugin({
database: coreServices.database,
discovery: coreServices.discovery,
auth: coreServices.auth,
catalogApi: catalogServiceRef,
catalog: catalogServiceRef,
},
async init({
httpRouter,
@@ -75,7 +75,7 @@ export const authPlugin = createBackendPlugin({
database,
discovery,
auth,
catalogApi,
catalog,
}) {
const router = await createRouter({
logger,
@@ -83,7 +83,7 @@ export const authPlugin = createBackendPlugin({
database,
discovery,
auth,
catalogApi,
catalog,
providerFactories: Object.fromEntries(providers),
ownershipResolver,
});
@@ -20,15 +20,15 @@ import {
} from '@backstage/catalog-model';
import { catalogServiceMock } from '@backstage/plugin-catalog-node/testUtils';
import { CatalogIdentityClient } from './CatalogIdentityClient';
import { mockCredentials, mockServices } from '@backstage/backend-test-utils';
import { mockServices } from '@backstage/backend-test-utils';
describe('CatalogIdentityClient', () => {
const auth = mockServices.auth();
const auth = mockServices.auth({ pluginId: 'auth' });
afterEach(() => jest.resetAllMocks());
it('findUser passes through the correct search params', async () => {
const catalogApi = catalogServiceMock({
const catalog = catalogServiceMock({
entities: [
{
apiVersion: 'backstage.io/v1beta1',
@@ -42,16 +42,16 @@ describe('CatalogIdentityClient', () => {
},
],
});
jest.spyOn(catalogApi, 'getEntities');
jest.spyOn(catalog, 'getEntities');
const client = new CatalogIdentityClient({
catalogApi,
catalog,
auth,
});
await client.findUser({ annotations: { key: 'value' } });
expect(catalogApi.getEntities).toHaveBeenCalledWith(
expect(catalog.getEntities).toHaveBeenCalledWith(
{
filter: {
kind: 'user',
@@ -59,10 +59,7 @@ describe('CatalogIdentityClient', () => {
},
},
{
token: mockCredentials.service.token({
onBehalfOf: mockCredentials.service('plugin:test'),
targetPluginId: 'catalog',
}),
credentials: await auth.getOwnServiceCredentials(),
},
);
});
@@ -103,11 +100,11 @@ describe('CatalogIdentityClient', () => {
],
},
];
const catalogApi = catalogServiceMock({ entities: mockUsers });
jest.spyOn(catalogApi, 'getEntities');
const catalog = catalogServiceMock({ entities: mockUsers });
jest.spyOn(catalog, 'getEntities');
const client = new CatalogIdentityClient({
catalogApi,
catalog,
auth,
});
@@ -115,7 +112,7 @@ describe('CatalogIdentityClient', () => {
entityRefs: ['inigom', 'User:default/imontoya', 'User:reality/mpatinkin'],
});
expect(catalogApi.getEntities).toHaveBeenCalledWith(
expect(catalog.getEntities).toHaveBeenCalledWith(
{
filter: [
{
@@ -136,10 +133,7 @@ describe('CatalogIdentityClient', () => {
],
},
{
token: mockCredentials.service.token({
onBehalfOf: mockCredentials.service('plugin:test'),
targetPluginId: 'catalog',
}),
credentials: await auth.getOwnServiceCredentials(),
},
);
@@ -16,7 +16,7 @@
import { AuthService, LoggerService } from '@backstage/backend-plugin-api';
import { ConflictError, NotFoundError } from '@backstage/errors';
import { CatalogApi } from '@backstage/catalog-client';
import { CatalogService } from '@backstage/plugin-catalog-node';
import {
CompoundEntityRef,
parseEntityRef,
@@ -29,11 +29,11 @@ import {
* A catalog client tailored for reading out identity data from the catalog.
*/
export class CatalogIdentityClient {
private readonly catalogApi: CatalogApi;
private readonly catalog: CatalogService;
private readonly auth: AuthService;
constructor(options: { catalogApi: CatalogApi; auth: AuthService }) {
this.catalogApi = options.catalogApi;
constructor(options: { catalog: CatalogService; auth: AuthService }) {
this.catalog = options.catalog;
this.auth = options.auth;
}
@@ -52,12 +52,10 @@ export class CatalogIdentityClient {
filter[`metadata.annotations.${key}`] = value;
}
const { token } = await this.auth.getPluginRequestToken({
onBehalfOf: await this.auth.getOwnServiceCredentials(),
targetPluginId: 'catalog',
});
const { items } = await this.catalogApi.getEntities({ filter }, { token });
const { items } = await this.catalog.getEntities(
{ filter },
{ credentials: await this.auth.getOwnServiceCredentials() },
);
if (items.length !== 1) {
if (items.length > 1) {
@@ -103,13 +101,11 @@ export class CatalogIdentityClient {
'metadata.name': ref.name,
}));
const { token } = await this.auth.getPluginRequestToken({
onBehalfOf: await this.auth.getOwnServiceCredentials(),
targetPluginId: 'catalog',
});
const entities = await this.catalogApi
.getEntities({ filter }, { token })
const entities = await this.catalog
.getEntities(
{ filter },
{ credentials: await this.auth.getOwnServiceCredentials() },
)
.then(r => r.items);
if (entityRefs.length !== entities.length) {
@@ -25,15 +25,16 @@ describe('CatalogAuthResolverContext', () => {
jest.clearAllMocks();
});
const catalogApi = catalogServiceMock();
jest.spyOn(catalogApi, 'getEntities');
const catalog = catalogServiceMock();
jest.spyOn(catalog, 'getEntities');
it('adds kind to filter when missing', async () => {
const auth = mockServices.auth();
const context = CatalogAuthResolverContext.create({
logger: mockServices.logger.mock(),
catalogApi,
catalog,
tokenIssuer: {} as TokenIssuer,
auth: mockServices.auth(),
auth,
});
await expect(
@@ -41,11 +42,11 @@ describe('CatalogAuthResolverContext', () => {
filter: [{}, { kind: 'group' }, { KIND: 'USER' }],
}),
).rejects.toThrow(NotFoundError);
expect(catalogApi.getEntities).toHaveBeenCalledWith(
expect(catalog.getEntities).toHaveBeenCalledWith(
{
filter: [{ kind: 'user' }, { kind: 'group' }, { KIND: 'USER' }],
},
{ token: 'mock-service-token:{"sub":"plugin:test","target":"catalog"}' },
{ credentials: await auth.getOwnServiceCredentials() },
);
});
});
@@ -14,7 +14,6 @@
* limitations under the License.
*/
import { CatalogApi } from '@backstage/catalog-client';
import {
DEFAULT_NAMESPACE,
Entity,
@@ -24,6 +23,7 @@ import {
} from '@backstage/catalog-model';
import { ConflictError, InputError, NotFoundError } from '@backstage/errors';
import { AuthService, LoggerService } from '@backstage/backend-plugin-api';
import { CatalogService } from '@backstage/plugin-catalog-node';
import { TokenIssuer } from '../../identity/types';
import {
AuthOwnershipResolver,
@@ -47,13 +47,13 @@ function getDefaultOwnershipEntityRefs(entity: Entity) {
export class CatalogAuthResolverContext implements AuthResolverContext {
static create(options: {
logger: LoggerService;
catalogApi: CatalogApi;
catalog: CatalogService;
tokenIssuer: TokenIssuer;
auth: AuthService;
ownershipResolver?: AuthOwnershipResolver;
}): CatalogAuthResolverContext {
const catalogIdentityClient = new CatalogIdentityClient({
catalogApi: options.catalogApi,
catalog: options.catalog,
auth: options.auth,
});
@@ -61,7 +61,7 @@ export class CatalogAuthResolverContext implements AuthResolverContext {
options.logger,
options.tokenIssuer,
catalogIdentityClient,
options.catalogApi,
options.catalog,
options.auth,
options.ownershipResolver,
);
@@ -71,7 +71,7 @@ export class CatalogAuthResolverContext implements AuthResolverContext {
public readonly logger: LoggerService,
public readonly tokenIssuer: TokenIssuer,
public readonly catalogIdentityClient: CatalogIdentityClient,
private readonly catalogApi: CatalogApi,
private readonly catalog: CatalogService,
private readonly auth: AuthService,
private readonly ownershipResolver?: AuthOwnershipResolver,
) {}
@@ -83,17 +83,15 @@ export class CatalogAuthResolverContext implements AuthResolverContext {
async findCatalogUser(query: AuthResolverCatalogUserQuery) {
let result: Entity[] | Entity | undefined = undefined;
const { token } = await this.auth.getPluginRequestToken({
onBehalfOf: await this.auth.getOwnServiceCredentials(),
targetPluginId: 'catalog',
});
if ('entityRef' in query) {
const entityRef = parseEntityRef(query.entityRef, {
defaultKind: 'User',
defaultNamespace: DEFAULT_NAMESPACE,
});
result = await this.catalogApi.getEntityByRef(entityRef, { token });
result = await this.catalog.getEntityByRef(entityRef, {
credentials: await this.auth.getOwnServiceCredentials(),
});
} else if ('annotations' in query) {
const filter: Record<string, string> = {
kind: 'user',
@@ -101,7 +99,10 @@ export class CatalogAuthResolverContext implements AuthResolverContext {
for (const [key, value] of Object.entries(query.annotations)) {
filter[`metadata.annotations.${key}`] = value;
}
const res = await this.catalogApi.getEntities({ filter }, { token });
const res = await this.catalog.getEntities(
{ filter },
{ credentials: await this.auth.getOwnServiceCredentials() },
);
result = res.items;
} else if ('filter' in query) {
const filter = [query.filter].flat().map(value => {
@@ -117,9 +118,9 @@ export class CatalogAuthResolverContext implements AuthResolverContext {
}
return value;
});
const res = await this.catalogApi.getEntities(
const res = await this.catalog.getEntities(
{ filter: filter },
{ token },
{ credentials: await this.auth.getOwnServiceCredentials() },
);
result = res.items;
} else {
+5 -12
View File
@@ -14,18 +14,14 @@
* limitations under the License.
*/
import {
AuthService,
DiscoveryService,
LoggerService,
} from '@backstage/backend-plugin-api';
import { CatalogApi, CatalogClient } from '@backstage/catalog-client';
import { AuthService, LoggerService } from '@backstage/backend-plugin-api';
import { Config } from '@backstage/config';
import { assertError, NotFoundError } from '@backstage/errors';
import {
AuthOwnershipResolver,
AuthProviderFactory,
} from '@backstage/plugin-auth-node';
import { CatalogService } from '@backstage/plugin-catalog-node';
import express from 'express';
import Router from 'express-promise-router';
import { Minimatch } from 'minimatch';
@@ -42,11 +38,10 @@ export function bindProviderRouters(
baseUrl: string;
config: Config;
logger: LoggerService;
discovery: DiscoveryService;
auth: AuthService;
tokenIssuer: TokenIssuer;
ownershipResolver?: AuthOwnershipResolver;
catalogApi?: CatalogApi;
catalog: CatalogService;
},
) {
const {
@@ -55,10 +50,9 @@ export function bindProviderRouters(
baseUrl,
config,
logger,
discovery,
auth,
tokenIssuer,
catalogApi,
catalog,
ownershipResolver,
} = options;
@@ -84,8 +78,7 @@ export function bindProviderRouters(
logger,
resolverContext: CatalogAuthResolverContext.create({
logger,
catalogApi:
catalogApi ?? new CatalogClient({ discoveryApi: discovery }),
catalog,
tokenIssuer,
auth,
ownershipResolver,
+2 -2
View File
@@ -25,8 +25,8 @@ import {
RootConfigService,
} from '@backstage/backend-plugin-api';
import { AuthOwnershipResolver } from '@backstage/plugin-auth-node';
import { CatalogService } from '@backstage/plugin-catalog-node';
import { NotFoundError } from '@backstage/errors';
import { CatalogApi } from '@backstage/catalog-client';
import { bindOidcRouter } from '../identity/router';
import { KeyStores } from '../identity/KeyStores';
import { TokenFactory } from '../identity/TokenFactory';
@@ -49,7 +49,7 @@ interface RouterOptions {
auth: AuthService;
tokenFactoryAlgorithm?: string;
providerFactories?: ProviderFactories;
catalogApi?: CatalogApi;
catalog: CatalogService;
ownershipResolver?: AuthOwnershipResolver;
}