update mockServices

Signed-off-by: Paul Schultz <pschultz@pobox.com>
This commit is contained in:
Paul Schultz
2024-12-16 16:45:26 -06:00
parent 3db23e512b
commit ba53aa668e
4 changed files with 114 additions and 189 deletions
+3 -5
View File
@@ -3,12 +3,9 @@
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
```ts
/// <reference types="express" />
/// <reference types="jest" />
/// <reference types="node" />
/// <reference types="qs" />
import type { AuditorOptions } from '@backstage/backend-defaults/auditor';
import { AuditorService } from '@backstage/backend-plugin-api';
import { AuthService } from '@backstage/backend-plugin-api';
import { Backend } from '@backstage/backend-app-api';
@@ -37,6 +34,7 @@ import { ParamsDictionary } from 'express-serve-static-core';
import { ParsedQs } from 'qs';
import { PermissionsRegistryService } from '@backstage/backend-plugin-api';
import { PermissionsService } from '@backstage/backend-plugin-api';
import type { RootAuditorOptions } from '@backstage/backend-defaults/auditor';
import { RootConfigService } from '@backstage/backend-plugin-api';
import { RootHealthService } from '@backstage/backend-plugin-api';
import { RootHttpRouterService } from '@backstage/backend-plugin-api';
@@ -156,14 +154,14 @@ export function mockErrorHandler(): ErrorRequestHandler<
// @public
export namespace mockServices {
export function auditor(
options?: Omit<auditor.Options, 'plugin'> & {
options?: auditor.Options & {
pluginId?: string;
},
): AuditorService;
// (undocumented)
export namespace auditor {
// (undocumented)
export type Options = Omit<AuditorOptions, 'format' | 'transports'>;
export type Options = RootAuditorOptions;
const // (undocumented)
factory: (
options?: auditor.Options,
@@ -15,7 +15,7 @@
*/
import type { ErrorLike } from '@backstage/errors';
import { MockAuditorService } from './MockAuditorService';
import { MockRootAuditorService } from './MockAuditorService';
import { MockAuthService } from './MockAuthService';
import { MockHttpAuthService } from './MockHttpAuthService';
import { mockCredentials } from './mockCredentials';
@@ -25,81 +25,21 @@ describe('MockAuditorService', () => {
jest.resetAllMocks();
});
it('should error without plugin service', async () => {
const auditor = MockAuditorService.create();
await expect(
auditor.createEvent({
eventId: 'test-event',
}),
).rejects.toThrow(
`The core service 'plugin' was not provided during the auditor's instantiation`,
);
});
it('should error without auth service', async () => {
const pluginId = 'test-plugin';
const auditor = MockAuditorService.create().child(
{},
{
plugin: {
getId: () => pluginId,
},
},
);
await expect(
auditor.createEvent({
eventId: 'test-event',
}),
).rejects.toThrow(
`The core service 'auth' was not provided during the auditor's instantiation`,
);
});
it('should error without httpAuth service', async () => {
const pluginId = 'test-plugin';
const auditor = MockAuditorService.create().child(
{},
{
plugin: {
getId: () => pluginId,
},
auth: new MockAuthService({
pluginId,
disableDefaultAuthPolicy: false,
}),
},
);
await expect(
auditor.createEvent({
eventId: 'test-event',
}),
).rejects.toThrow(
`The core service 'httpAuth' was not provided during the auditor's instantiation`,
);
});
it('should log', async () => {
jest.spyOn(console, 'log').mockImplementation(() => {});
const pluginId = 'test-plugin';
const auditor = MockAuditorService.create().child(
{},
{
plugin: {
getId: () => pluginId,
},
auth: new MockAuthService({
pluginId,
disableDefaultAuthPolicy: false,
}),
httpAuth: new MockHttpAuthService(pluginId, mockCredentials.user()),
const auditor = MockRootAuditorService.create().forPlugin({
plugin: {
getId: () => pluginId,
},
);
auth: new MockAuthService({
pluginId,
disableDefaultAuthPolicy: false,
}),
httpAuth: new MockHttpAuthService(pluginId, mockCredentials.user()),
});
await auditor.createEvent({
eventId: 'test-event',
@@ -113,19 +53,16 @@ describe('MockAuditorService', () => {
const pluginId = 'test-plugin';
const auditor = MockAuditorService.create().child(
{},
{
plugin: {
getId: () => pluginId,
},
auth: new MockAuthService({
pluginId,
disableDefaultAuthPolicy: false,
}),
httpAuth: new MockHttpAuthService(pluginId, mockCredentials.user()),
const auditor = MockRootAuditorService.create().forPlugin({
plugin: {
getId: () => pluginId,
},
);
auth: new MockAuthService({
pluginId,
disableDefaultAuthPolicy: false,
}),
httpAuth: new MockHttpAuthService(pluginId, mockCredentials.user()),
});
await auditor.createEvent({
eventId: 'test-event',
@@ -139,19 +76,16 @@ describe('MockAuditorService', () => {
const pluginId = 'test-plugin';
const auditor = MockAuditorService.create().child(
{},
{
plugin: {
getId: () => pluginId,
},
auth: new MockAuthService({
pluginId,
disableDefaultAuthPolicy: false,
}),
httpAuth: new MockHttpAuthService(pluginId, mockCredentials.user()),
const auditor = MockRootAuditorService.create().forPlugin({
plugin: {
getId: () => pluginId,
},
);
auth: new MockAuthService({
pluginId,
disableDefaultAuthPolicy: false,
}),
httpAuth: new MockHttpAuthService(pluginId, mockCredentials.user()),
});
const auditorEvent = await auditor.createEvent({
eventId: 'test-event',
@@ -167,19 +101,16 @@ describe('MockAuditorService', () => {
const pluginId = 'test-plugin';
const auditor = MockAuditorService.create().child(
{},
{
plugin: {
getId: () => pluginId,
},
auth: new MockAuthService({
pluginId,
disableDefaultAuthPolicy: false,
}),
httpAuth: new MockHttpAuthService(pluginId, mockCredentials.user()),
const auditor = MockRootAuditorService.create().forPlugin({
plugin: {
getId: () => pluginId,
},
);
auth: new MockAuthService({
pluginId,
disableDefaultAuthPolicy: false,
}),
httpAuth: new MockHttpAuthService(pluginId, mockCredentials.user()),
});
const auditorEvent = await auditor.createEvent({
eventId: 'test-event',
@@ -26,27 +26,47 @@ import type {
HttpAuthService,
PluginMetadataService,
} from '@backstage/backend-plugin-api';
import { ForwardedError, ServiceUnavailableError } from '@backstage/errors';
import { ForwardedError } from '@backstage/errors';
import type { JsonObject } from '@backstage/types';
import type { Request } from 'express';
import type { mockServices } from './mockServices';
export class MockAuditorService implements AuditorService {
readonly #options: mockServices.auditor.Options & {
auth?: AuthService;
httpAuth?: HttpAuthService;
plugin?: PluginMetadataService;
};
private readonly impl: MockRootAuditorService;
private readonly auth: AuthService;
private readonly httpAuth: HttpAuthService;
private readonly plugin: PluginMetadataService;
static create(options?: mockServices.auditor.Options): MockAuditorService {
return new MockAuditorService(options ?? {});
private constructor(
impl: MockRootAuditorService,
deps: {
auth: AuthService;
httpAuth: HttpAuthService;
plugin: PluginMetadataService;
},
) {
this.impl = impl;
this.auth = deps.auth;
this.httpAuth = deps.httpAuth;
this.plugin = deps.plugin;
}
static create(
impl: MockRootAuditorService,
deps: {
auth: AuthService;
httpAuth: HttpAuthService;
plugin: PluginMetadataService;
},
): AuditorService {
return new MockAuditorService(impl, deps);
}
private async log<TMeta extends JsonObject>(
options: AuditorEventOptions<TMeta>,
): Promise<void> {
const auditEvent = await this.reshapeAuditorEvent(options);
this.#log(...auditEvent);
this.impl.log(auditEvent);
}
async createEvent<TMeta extends JsonObject>(
@@ -89,57 +109,25 @@ export class MockAuditorService implements AuditorService {
};
}
child(
meta: JsonObject,
deps?: {
auth?: AuthService;
httpAuth?: HttpAuthService;
plugin?: PluginMetadataService;
},
): AuditorService {
return new MockAuditorService({
...this.#options,
auth: deps?.auth,
httpAuth: deps?.httpAuth,
plugin: deps?.plugin,
meta: {
...this.#options.meta,
...meta,
},
});
}
private async getActorId(
request?: Request<any, any, any, any, any>,
): Promise<string | undefined> {
if (!this.#options.auth) {
throw new ServiceUnavailableError(
`The core service 'auth' was not provided during the auditor's instantiation`,
);
}
if (!this.#options.httpAuth) {
throw new ServiceUnavailableError(
`The core service 'httpAuth' was not provided during the auditor's instantiation`,
);
}
let credentials: BackstageCredentials =
await this.#options.auth.getOwnServiceCredentials();
await this.auth.getOwnServiceCredentials();
if (request) {
try {
credentials = await this.#options.httpAuth.credentials(request);
credentials = await this.httpAuth.credentials(request);
} catch (error) {
throw new ForwardedError('Could not resolve credentials', error);
}
}
if (this.#options.auth.isPrincipal(credentials, 'user')) {
if (this.auth.isPrincipal(credentials, 'user')) {
return credentials.principal.userEntityRef;
}
if (this.#options.auth.isPrincipal(credentials, 'service')) {
if (this.auth.isPrincipal(credentials, 'service')) {
return credentials.principal.subject;
}
@@ -151,14 +139,8 @@ export class MockAuditorService implements AuditorService {
): Promise<AuditorEvent> {
const { eventId, severityLevel = 'low', request, ...rest } = options;
if (!this.#options.plugin) {
throw new ServiceUnavailableError(
`The core service 'plugin' was not provided during the auditor's instantiation`,
);
}
const auditEvent: AuditorEvent = [
`${this.#options.plugin.getId()}.${eventId}`,
`${this.plugin.getId()}.${eventId}`,
{
severityLevel,
actor: {
@@ -179,15 +161,32 @@ export class MockAuditorService implements AuditorService {
return auditEvent;
}
}
private constructor(
options: mockServices.auditor.Options & {
auth?: AuthService;
httpAuth?: HttpAuthService;
plugin?: PluginMetadataService;
},
) {
this.#options = options;
export class MockRootAuditorService {
private readonly options: mockServices.auditor.Options;
private constructor(options?: mockServices.auditor.Options) {
this.options = options ?? {};
}
static create(
options?: mockServices.auditor.Options,
): MockRootAuditorService {
return new MockRootAuditorService(options);
}
async log(auditEvent: AuditorEvent): Promise<void> {
this.#log(...auditEvent);
}
forPlugin(deps: {
auth: AuthService;
httpAuth: HttpAuthService;
plugin: PluginMetadataService;
}): AuditorService {
const impl = new MockRootAuditorService(this.options);
return MockAuditorService.create(impl, deps);
}
#log(message: string, meta?: AuditorEvent[1]) {
@@ -51,7 +51,7 @@ import {
} from '@backstage/plugin-events-node';
import { JsonObject } from '@backstage/types';
import { Knex } from 'knex';
import { MockAuditorService } from './MockAuditorService';
import { MockRootAuditorService } from './MockAuditorService';
import { MockAuthService } from './MockAuthService';
import { MockHttpAuthService } from './MockHttpAuthService';
import { MockRootLoggerService } from './MockRootLoggerService';
@@ -228,7 +228,7 @@ export namespace mockServices {
* Creates a mock implementation of the `AuditorService`.
*/
export function auditor(
options?: Omit<auditor.Options, 'plugin'> & {
options?: auditor.Options & {
pluginId?: string;
},
): AuditorService {
@@ -247,18 +247,19 @@ export namespace mockServices {
getId: () => pluginId,
};
const auditorMock = MockAuditorService.create({
const auditorMock = MockRootAuditorService.create({
meta: options?.meta ? { ...options.meta, service } : { service },
});
return auditorMock.child(
{ pluginId },
{ auth: mockAuth, httpAuth: mockHttpAuth, plugin: mockPlugin },
);
return auditorMock.forPlugin({
auth: mockAuth,
httpAuth: mockHttpAuth,
plugin: mockPlugin,
});
}
export namespace auditor {
export type Options = Omit<RootAuditorOptions, 'format' | 'transports'>;
export type Options = RootAuditorOptions;
export const factory = (options?: auditor.Options) =>
createServiceFactory({
@@ -271,23 +272,19 @@ export namespace mockServices {
createRootContext() {
const service = 'backstage';
return MockAuditorService.create({
return MockRootAuditorService.create({
meta: options?.meta ? { ...options.meta, service } : { service },
});
},
factory(
{
plugin,
{ auth: mockAuth, httpAuth: mockHttpAuth, plugin: mockPlugin },
rootAuditor,
) {
return rootAuditor.forPlugin({
auth: mockAuth,
httpAuth: mockHttpAuth,
plugin: mockPlugin,
},
rootAuditor,
) {
return rootAuditor.child(
{ plugin: plugin.getId() },
{ auth: mockAuth, httpAuth: mockHttpAuth, plugin: mockPlugin },
);
});
},
});