catalog-node: refactor catalog service to always use credentials

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2024-10-22 13:09:46 +02:00
parent 33b1892633
commit 3fb2f7c690
4 changed files with 124 additions and 144 deletions
+3 -1
View File
@@ -2,4 +2,6 @@
'@backstage/plugin-catalog-node': minor
---
The `catalogServiceRef` now have its own accompanying `CatalogService` interface, which also supports passing Backstage `credentials` objects in addition to tokens. In addition, it has been promoted from the `/alpha` export and is now available from the main `@backstage/plugin-catalog-node` entry point.
The `catalogServiceRef` now has its own accompanying `CatalogService` interface that requires Backstage `credentials` objects to be passed. This new version of the `catalogServiceRef` has been promoted and is now available via the main `@backstage/plugin-catalog-node` entry point.
The old `catalogServiceRef` with the old `CatalogApi` type is still available from the `/alpha` entry point.
+19 -21
View File
@@ -11,8 +11,6 @@ import { AnalyzeLocationExistingEntity } from '@backstage/plugin-catalog-common'
import { AnalyzeLocationRequest } from '@backstage/plugin-catalog-common';
import { AnalyzeLocationResponse } from '@backstage/plugin-catalog-common';
import { BackstageCredentials } from '@backstage/backend-plugin-api';
import { CatalogApi } from '@backstage/catalog-client';
import { CatalogRequestOptions } from '@backstage/catalog-client';
import { CompoundEntityRef } from '@backstage/catalog-model';
import { Entity } from '@backstage/catalog-model';
import { GetEntitiesByRefsRequest } from '@backstage/catalog-client';
@@ -120,77 +118,77 @@ export type CatalogProcessorResult =
| CatalogProcessorRefreshKeysResult;
// @public
export interface CatalogService extends CatalogApi {
export interface CatalogService {
// (undocumented)
addLocation(
location: AddLocationRequest,
options?: CatalogServiceRequestOptions,
options: CatalogServiceRequestOptions,
): Promise<AddLocationResponse>;
// (undocumented)
getEntities(
request?: GetEntitiesRequest,
options?: CatalogServiceRequestOptions,
request: GetEntitiesRequest | undefined,
options: CatalogServiceRequestOptions,
): Promise<GetEntitiesResponse>;
// (undocumented)
getEntitiesByRefs(
request: GetEntitiesByRefsRequest,
options?: CatalogServiceRequestOptions,
options: CatalogServiceRequestOptions,
): Promise<GetEntitiesByRefsResponse>;
// (undocumented)
getEntityAncestors(
request: GetEntityAncestorsRequest,
options?: CatalogServiceRequestOptions,
options: CatalogServiceRequestOptions,
): Promise<GetEntityAncestorsResponse>;
// (undocumented)
getEntityByRef(
entityRef: string | CompoundEntityRef,
options?: CatalogServiceRequestOptions,
options: CatalogServiceRequestOptions,
): Promise<Entity | undefined>;
// (undocumented)
getEntityFacets(
request: GetEntityFacetsRequest,
options?: CatalogServiceRequestOptions,
options: CatalogServiceRequestOptions,
): Promise<GetEntityFacetsResponse>;
// (undocumented)
getLocationByEntity(
entityRef: string | CompoundEntityRef,
options?: CatalogServiceRequestOptions,
options: CatalogServiceRequestOptions,
): Promise<Location_2 | undefined>;
// (undocumented)
getLocationById(
id: string,
options?: CatalogServiceRequestOptions,
options: CatalogServiceRequestOptions,
): Promise<Location_2 | undefined>;
// (undocumented)
getLocationByRef(
locationRef: string,
options?: CatalogServiceRequestOptions,
options: CatalogServiceRequestOptions,
): Promise<Location_2 | undefined>;
// (undocumented)
queryEntities(
request?: QueryEntitiesRequest,
options?: CatalogServiceRequestOptions,
request: QueryEntitiesRequest | undefined,
options: CatalogServiceRequestOptions,
): Promise<QueryEntitiesResponse>;
// (undocumented)
refreshEntity(
entityRef: string,
options?: CatalogServiceRequestOptions,
options: CatalogServiceRequestOptions,
): Promise<void>;
// (undocumented)
removeEntityByUid(
uid: string,
options?: CatalogServiceRequestOptions,
options: CatalogServiceRequestOptions,
): Promise<void>;
// (undocumented)
removeLocationById(
id: string,
options?: CatalogServiceRequestOptions,
options: CatalogServiceRequestOptions,
): Promise<void>;
// (undocumented)
validateEntity(
entity: Entity,
locationRef: string,
options?: CatalogServiceRequestOptions,
options: CatalogServiceRequestOptions,
): Promise<ValidateEntityResponse>;
}
@@ -202,9 +200,9 @@ export const catalogServiceRef: ServiceRef<
>;
// @public (undocumented)
export interface CatalogServiceRequestOptions extends CatalogRequestOptions {
export interface CatalogServiceRequestOptions {
// (undocumented)
credentials?: BackstageCredentials;
credentials: BackstageCredentials;
}
// @public
@@ -118,57 +118,4 @@ describe('catalogServiceRef', () => {
{ credentials: mockCredentials.service() },
);
});
it('should call with token', async () => {
expect.assertions(1);
server.use(
rest.get('http://localhost/api/catalog/entities', (req, res, ctx) => {
expect(req.headers.get('authorization')).toBe(
mockCredentials.user.header(),
);
return res(ctx.json({}));
}),
);
const tester = ServiceFactoryTester.from(
createServiceFactory({
service: createServiceRef<void>({ id: 'unused-dummy' }),
deps: {},
factory() {},
}),
{ dependencies: [mockServices.discovery.factory()] },
);
const catalogService = await tester.getService(catalogServiceRef);
await catalogService.getEntities(
{},
{
token: mockCredentials.user.token(),
},
);
});
it('should call without credentials', async () => {
expect.assertions(1);
server.use(
rest.get('http://localhost/api/catalog/entities', (req, res, ctx) => {
expect(req.headers.get('authorization')).toBeFalsy();
return res(ctx.json({}));
}),
);
const tester = ServiceFactoryTester.from(
createServiceFactory({
service: createServiceRef<void>({ id: 'unused-dummy' }),
deps: {},
factory() {},
}),
{ dependencies: [mockServices.discovery.factory()] },
);
const catalogService = await tester.getService(catalogServiceRef);
await catalogService.getEntities();
});
});
+102 -69
View File
@@ -19,7 +19,6 @@ import {
createServiceRef,
coreServices,
BackstageCredentials,
DiscoveryService,
AuthService,
} from '@backstage/backend-plugin-api';
import {
@@ -46,192 +45,229 @@ import { CompoundEntityRef, Entity } from '@backstage/catalog-model';
/**
* @public
*/
export interface CatalogServiceRequestOptions extends CatalogRequestOptions {
credentials?: BackstageCredentials;
export interface CatalogServiceRequestOptions {
credentials: BackstageCredentials;
}
/**
* A version of the {@link @backstage/catalog-client#CatalogApi | CatalogApi} that
* accepts backend credentials in addition to a token.
* requires backend credentials to be passed instead of a token.
*
* @public
*/
export interface CatalogService extends CatalogApi {
export interface CatalogService {
getEntities(
request?: GetEntitiesRequest,
options?: CatalogServiceRequestOptions,
request: GetEntitiesRequest | undefined,
options: CatalogServiceRequestOptions,
): Promise<GetEntitiesResponse>;
getEntitiesByRefs(
request: GetEntitiesByRefsRequest,
options?: CatalogServiceRequestOptions,
options: CatalogServiceRequestOptions,
): Promise<GetEntitiesByRefsResponse>;
queryEntities(
request?: QueryEntitiesRequest,
options?: CatalogServiceRequestOptions,
request: QueryEntitiesRequest | undefined,
options: CatalogServiceRequestOptions,
): Promise<QueryEntitiesResponse>;
getEntityAncestors(
request: GetEntityAncestorsRequest,
options?: CatalogServiceRequestOptions,
options: CatalogServiceRequestOptions,
): Promise<GetEntityAncestorsResponse>;
getEntityByRef(
entityRef: string | CompoundEntityRef,
options?: CatalogServiceRequestOptions,
options: CatalogServiceRequestOptions,
): Promise<Entity | undefined>;
removeEntityByUid(
uid: string,
options?: CatalogServiceRequestOptions,
options: CatalogServiceRequestOptions,
): Promise<void>;
refreshEntity(
entityRef: string,
options?: CatalogServiceRequestOptions,
options: CatalogServiceRequestOptions,
): Promise<void>;
getEntityFacets(
request: GetEntityFacetsRequest,
options?: CatalogServiceRequestOptions,
options: CatalogServiceRequestOptions,
): Promise<GetEntityFacetsResponse>;
getLocationById(
id: string,
options?: CatalogServiceRequestOptions,
options: CatalogServiceRequestOptions,
): Promise<Location | undefined>;
getLocationByRef(
locationRef: string,
options?: CatalogServiceRequestOptions,
options: CatalogServiceRequestOptions,
): Promise<Location | undefined>;
addLocation(
location: AddLocationRequest,
options?: CatalogServiceRequestOptions,
options: CatalogServiceRequestOptions,
): Promise<AddLocationResponse>;
removeLocationById(
id: string,
options?: CatalogServiceRequestOptions,
options: CatalogServiceRequestOptions,
): Promise<void>;
getLocationByEntity(
entityRef: string | CompoundEntityRef,
options?: CatalogServiceRequestOptions,
options: CatalogServiceRequestOptions,
): Promise<Location | undefined>;
validateEntity(
entity: Entity,
locationRef: string,
options?: CatalogServiceRequestOptions,
options: CatalogServiceRequestOptions,
): Promise<ValidateEntityResponse>;
}
class DefaultCatalogService extends CatalogClient {
class DefaultCatalogService implements CatalogService {
readonly #auth: AuthService;
readonly #catalogApi: CatalogApi;
constructor({
discoveryApi,
catalogApi,
auth,
}: {
discoveryApi: DiscoveryService;
catalogApi: CatalogApi;
auth: AuthService;
}) {
super({ discoveryApi });
this.#catalogApi = catalogApi;
this.#auth = auth;
}
async getEntities(
request?: GetEntitiesRequest,
options?: CatalogServiceRequestOptions,
request: GetEntitiesRequest | undefined,
options: CatalogServiceRequestOptions,
): Promise<GetEntitiesResponse> {
return super.getEntities(request, await this.#getOptions(options));
return this.#catalogApi.getEntities(
request,
await this.#getOptions(options),
);
}
async getEntitiesByRefs(
request: GetEntitiesByRefsRequest,
options?: CatalogServiceRequestOptions,
options: CatalogServiceRequestOptions,
): Promise<GetEntitiesByRefsResponse> {
return super.getEntitiesByRefs(request, await this.#getOptions(options));
return this.#catalogApi.getEntitiesByRefs(
request,
await this.#getOptions(options),
);
}
async queryEntities(
request?: QueryEntitiesRequest,
options?: CatalogServiceRequestOptions,
request: QueryEntitiesRequest | undefined,
options: CatalogServiceRequestOptions,
): Promise<QueryEntitiesResponse> {
return super.queryEntities(request, await this.#getOptions(options));
return this.#catalogApi.queryEntities(
request,
await this.#getOptions(options),
);
}
async getEntityAncestors(
request: GetEntityAncestorsRequest,
options?: CatalogServiceRequestOptions,
options: CatalogServiceRequestOptions,
): Promise<GetEntityAncestorsResponse> {
return super.getEntityAncestors(request, await this.#getOptions(options));
return this.#catalogApi.getEntityAncestors(
request,
await this.#getOptions(options),
);
}
async getEntityByRef(
entityRef: string | CompoundEntityRef,
options?: CatalogServiceRequestOptions,
options: CatalogServiceRequestOptions,
): Promise<Entity | undefined> {
return super.getEntityByRef(entityRef, await this.#getOptions(options));
return this.#catalogApi.getEntityByRef(
entityRef,
await this.#getOptions(options),
);
}
async removeEntityByUid(
uid: string,
options?: CatalogServiceRequestOptions,
options: CatalogServiceRequestOptions,
): Promise<void> {
return super.removeEntityByUid(uid, await this.#getOptions(options));
return this.#catalogApi.removeEntityByUid(
uid,
await this.#getOptions(options),
);
}
async refreshEntity(
entityRef: string,
options?: CatalogServiceRequestOptions,
options: CatalogServiceRequestOptions,
): Promise<void> {
return super.refreshEntity(entityRef, await this.#getOptions(options));
return this.#catalogApi.refreshEntity(
entityRef,
await this.#getOptions(options),
);
}
async getEntityFacets(
request: GetEntityFacetsRequest,
options?: CatalogServiceRequestOptions,
options: CatalogServiceRequestOptions,
): Promise<GetEntityFacetsResponse> {
return super.getEntityFacets(request, await this.#getOptions(options));
return this.#catalogApi.getEntityFacets(
request,
await this.#getOptions(options),
);
}
async getLocationById(
id: string,
options?: CatalogServiceRequestOptions,
options: CatalogServiceRequestOptions,
): Promise<Location | undefined> {
return super.getLocationById(id, await this.#getOptions(options));
return this.#catalogApi.getLocationById(
id,
await this.#getOptions(options),
);
}
async getLocationByRef(
locationRef: string,
options?: CatalogServiceRequestOptions,
options: CatalogServiceRequestOptions,
): Promise<Location | undefined> {
return super.getLocationByRef(locationRef, await this.#getOptions(options));
return this.#catalogApi.getLocationByRef(
locationRef,
await this.#getOptions(options),
);
}
async addLocation(
location: AddLocationRequest,
options?: CatalogServiceRequestOptions,
options: CatalogServiceRequestOptions,
): Promise<AddLocationResponse> {
return super.addLocation(location, await this.#getOptions(options));
return this.#catalogApi.addLocation(
location,
await this.#getOptions(options),
);
}
async removeLocationById(
id: string,
options?: CatalogServiceRequestOptions,
options: CatalogServiceRequestOptions,
): Promise<void> {
return super.removeLocationById(id, await this.#getOptions(options));
return this.#catalogApi.removeLocationById(
id,
await this.#getOptions(options),
);
}
async getLocationByEntity(
entityRef: string | CompoundEntityRef,
options?: CatalogServiceRequestOptions,
options: CatalogServiceRequestOptions,
): Promise<Location | undefined> {
return super.getLocationByEntity(
return this.#catalogApi.getLocationByEntity(
entityRef,
await this.#getOptions(options),
);
@@ -240,9 +276,9 @@ class DefaultCatalogService extends CatalogClient {
async validateEntity(
entity: Entity,
locationRef: string,
options?: CatalogServiceRequestOptions,
options: CatalogServiceRequestOptions,
): Promise<ValidateEntityResponse> {
return super.validateEntity(
return this.#catalogApi.validateEntity(
entity,
locationRef,
await this.#getOptions(options),
@@ -250,18 +286,12 @@ class DefaultCatalogService extends CatalogClient {
}
async #getOptions(
options?: CatalogServiceRequestOptions,
): Promise<CatalogRequestOptions | undefined> {
if (options?.token) {
return options;
}
if (options?.credentials) {
return this.#auth.getPluginRequestToken({
onBehalfOf: options.credentials,
targetPluginId: 'catalog',
});
}
return options;
options: CatalogServiceRequestOptions,
): Promise<CatalogRequestOptions> {
return this.#auth.getPluginRequestToken({
onBehalfOf: options.credentials,
targetPluginId: 'catalog',
});
}
}
@@ -279,8 +309,11 @@ export const catalogServiceRef = createServiceRef<CatalogService>({
auth: coreServices.auth,
discoveryApi: coreServices.discovery,
},
async factory(deps) {
return new DefaultCatalogService(deps);
async factory({ auth, discoveryApi }) {
return new DefaultCatalogService({
auth,
catalogApi: new CatalogClient({ discoveryApi }),
});
},
}),
});