Merge branch 'master' of https://github.com/backstage/backstage into migrate-google-pubsub-alpha-metrics
This commit is contained in:
@@ -63,6 +63,7 @@
|
||||
"@octokit/auth-callback": "^5.0.0",
|
||||
"@octokit/core": "^5.2.0",
|
||||
"@octokit/graphql": "^7.0.2",
|
||||
"@octokit/plugin-retry": "^6.0.0",
|
||||
"@octokit/plugin-throttling": "^8.1.3",
|
||||
"@octokit/rest": "^19.0.3",
|
||||
"@octokit/webhooks-types": "^7.6.1",
|
||||
|
||||
@@ -41,6 +41,7 @@ import {
|
||||
} from './github';
|
||||
import { Octokit } from '@octokit/core';
|
||||
import { throttling } from '@octokit/plugin-throttling';
|
||||
import { retry } from '@octokit/plugin-retry';
|
||||
|
||||
jest.mock('@octokit/core', () => ({
|
||||
...jest.requireActual('@octokit/core'),
|
||||
@@ -1009,9 +1010,9 @@ describe('github', () => {
|
||||
baseUrl,
|
||||
logger,
|
||||
});
|
||||
it('should return a graphql client with throttling', async () => {
|
||||
it('should return a graphql client with throttling and retry', async () => {
|
||||
expect(client).toBeDefined();
|
||||
expect(Octokit.plugin).toHaveBeenCalledWith(throttling);
|
||||
expect(Octokit.plugin).toHaveBeenCalledWith(throttling, retry);
|
||||
});
|
||||
|
||||
it('should return a graphql client with the correct options', async () => {
|
||||
|
||||
@@ -30,6 +30,7 @@ import { DeferredEntity } from '@backstage/plugin-catalog-node';
|
||||
import { Octokit } from '@octokit/core';
|
||||
import { LoggerService } from '@backstage/backend-plugin-api';
|
||||
import { throttling } from '@octokit/plugin-throttling';
|
||||
import { retry } from '@octokit/plugin-retry';
|
||||
|
||||
/**
|
||||
* Configuration for GitHub GraphQL API page sizes.
|
||||
@@ -874,7 +875,7 @@ export const createReplaceEntitiesOperation =
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a GraphQL Client with Throttling
|
||||
* Creates a GraphQL Client with Throttling and Retries
|
||||
*/
|
||||
export const createGraphqlClient = (args: {
|
||||
headers:
|
||||
@@ -886,7 +887,7 @@ export const createGraphqlClient = (args: {
|
||||
logger: LoggerService;
|
||||
}): typeof graphql => {
|
||||
const { headers, baseUrl, logger } = args;
|
||||
const ThrottledOctokit = Octokit.plugin(throttling);
|
||||
const ThrottledOctokit = Octokit.plugin(throttling, retry);
|
||||
const octokit = new ThrottledOctokit({
|
||||
throttle: {
|
||||
onRateLimit: (retryAfter, rateLimitData, _, retryCount) => {
|
||||
|
||||
@@ -47,6 +47,7 @@ type PartialDeep<T> = T extends (...args: unknown[]) => unknown
|
||||
jest.mock('../lib/github', () => {
|
||||
return {
|
||||
getOrganizationRepositories: jest.fn(),
|
||||
createGraphqlClient: jest.fn().mockReturnValue(jest.fn()),
|
||||
};
|
||||
});
|
||||
class PersistingTaskRunner implements SchedulerServiceTaskRunner {
|
||||
|
||||
@@ -32,13 +32,13 @@ import {
|
||||
|
||||
import { LocationSpec } from '@backstage/plugin-catalog-common';
|
||||
|
||||
import { graphql } from '@octokit/graphql';
|
||||
import * as uuid from 'uuid';
|
||||
import {
|
||||
GithubEntityProviderConfig,
|
||||
readProviderConfigs,
|
||||
} from './GithubEntityProviderConfig';
|
||||
import {
|
||||
createGraphqlClient,
|
||||
getOrganizationRepositories,
|
||||
getOrganizationRepository,
|
||||
RepositoryResponse,
|
||||
@@ -249,9 +249,10 @@ export class GithubEntityProvider implements EntityProvider, EventSubscriber {
|
||||
url: orgUrl,
|
||||
});
|
||||
|
||||
return graphql.defaults({
|
||||
baseUrl: this.integration.apiBaseUrl,
|
||||
return createGraphqlClient({
|
||||
headers,
|
||||
baseUrl: this.integration.apiBaseUrl!,
|
||||
logger: this.logger,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -43,10 +43,7 @@ import {
|
||||
UrlReaderService,
|
||||
} from '@backstage/backend-plugin-api';
|
||||
import { Config, readDurationFromConfig } from '@backstage/config';
|
||||
import {
|
||||
catalogPermissions,
|
||||
RESOURCE_TYPE_CATALOG_ENTITY,
|
||||
} from '@backstage/plugin-catalog-common/alpha';
|
||||
import { catalogPermissions } from '@backstage/plugin-catalog-common/alpha';
|
||||
import {
|
||||
CatalogProcessor,
|
||||
CatalogProcessorParser,
|
||||
@@ -55,15 +52,7 @@ import {
|
||||
ScmLocationAnalyzer,
|
||||
} from '@backstage/plugin-catalog-node';
|
||||
import { EventsService } from '@backstage/plugin-events-node';
|
||||
import {
|
||||
Permission,
|
||||
PermissionAuthorizer,
|
||||
toPermissionEvaluator,
|
||||
} from '@backstage/plugin-permission-common';
|
||||
import {
|
||||
createConditionTransformer,
|
||||
createPermissionIntegrationRouter,
|
||||
} from '@backstage/plugin-permission-node';
|
||||
import { createConditionTransformer } from '@backstage/plugin-permission-node';
|
||||
import { durationToMilliseconds } from '@backstage/types';
|
||||
import { DefaultCatalogDatabase } from '../database/DefaultCatalogDatabase';
|
||||
import { DefaultProcessingDatabase } from '../database/DefaultProcessingDatabase';
|
||||
@@ -111,7 +100,6 @@ import { DefaultRefreshService } from './DefaultRefreshService';
|
||||
import { entitiesResponseToObjects } from './response';
|
||||
import {
|
||||
catalogEntityPermissionResourceRef,
|
||||
CatalogPermissionRuleInput,
|
||||
CatalogScmEventsService,
|
||||
} from '@backstage/plugin-catalog-node/alpha';
|
||||
import { filterAndSortProcessors, filterProviders } from './util';
|
||||
@@ -124,8 +112,8 @@ export type CatalogEnvironment = {
|
||||
database: DatabaseService;
|
||||
config: RootConfigService;
|
||||
reader: UrlReaderService;
|
||||
permissions: PermissionsService | PermissionAuthorizer;
|
||||
permissionsRegistry?: PermissionsRegistryService;
|
||||
permissions: PermissionsService;
|
||||
permissionsRegistry: PermissionsRegistryService;
|
||||
scheduler: SchedulerService;
|
||||
auth: AuthService;
|
||||
httpAuth: HttpAuthService;
|
||||
@@ -177,8 +165,6 @@ export class CatalogBuilder {
|
||||
}) => Promise<void> | void;
|
||||
private processingInterval: ProcessingIntervalFunction;
|
||||
private locationAnalyzer: LocationAnalyzer | undefined = undefined;
|
||||
private readonly permissions: Permission[];
|
||||
private readonly permissionRules: CatalogPermissionRuleInput[];
|
||||
private allowedLocationType: string[];
|
||||
|
||||
/**
|
||||
@@ -199,8 +185,6 @@ export class CatalogBuilder {
|
||||
this.locationAnalyzers = [];
|
||||
this.processorsReplace = false;
|
||||
this.parser = undefined;
|
||||
this.permissions = [...catalogPermissions];
|
||||
this.permissionRules = Object.values(catalogPermissionRules);
|
||||
this.allowedLocationType = ['url'];
|
||||
|
||||
this.processingInterval = CatalogBuilder.getDefaultProcessingInterval(
|
||||
@@ -375,33 +359,6 @@ export class CatalogBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds additional permissions. See
|
||||
* {@link @backstage/plugin-permission-node#Permission}.
|
||||
*
|
||||
* @param permissions - Additional permissions
|
||||
*/
|
||||
addPermissions(...permissions: Array<Permission | Array<Permission>>) {
|
||||
this.permissions.push(...permissions.flat());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds additional permission rules. Permission rules are used to evaluate
|
||||
* catalog resources against queries. See
|
||||
* {@link @backstage/plugin-permission-node#PermissionRule}.
|
||||
*
|
||||
* @param permissionRules - Additional permission rules
|
||||
*/
|
||||
addPermissionRules(
|
||||
...permissionRules: Array<
|
||||
CatalogPermissionRuleInput | Array<CatalogPermissionRuleInput>
|
||||
>
|
||||
) {
|
||||
this.permissionRules.push(...permissionRules.flat());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the allowed location types from being registered via the location service.
|
||||
*
|
||||
@@ -479,16 +436,6 @@ export class CatalogBuilder {
|
||||
enableRelationsCompatibility,
|
||||
});
|
||||
|
||||
let permissionsService: PermissionsService;
|
||||
if ('authorizeConditional' in permissions) {
|
||||
permissionsService = permissions as PermissionsService;
|
||||
} else {
|
||||
logger.warn(
|
||||
'PermissionAuthorizer is deprecated. Please use an instance of PermissionEvaluator instead of PermissionAuthorizer in PluginEnvironment#permissions',
|
||||
);
|
||||
permissionsService = toPermissionEvaluator(permissions);
|
||||
}
|
||||
|
||||
const orchestrator = new DefaultCatalogProcessingOrchestrator({
|
||||
processors,
|
||||
integrations,
|
||||
@@ -500,14 +447,12 @@ export class CatalogBuilder {
|
||||
|
||||
const entitiesCatalog = new AuthorizedEntitiesCatalog(
|
||||
unauthorizedEntitiesCatalog,
|
||||
permissionsService,
|
||||
permissionsRegistry
|
||||
? createConditionTransformer(
|
||||
permissionsRegistry.getPermissionRuleset(
|
||||
catalogEntityPermissionResourceRef,
|
||||
),
|
||||
)
|
||||
: createConditionTransformer(this.permissionRules),
|
||||
permissions,
|
||||
createConditionTransformer(
|
||||
permissionsRegistry.getPermissionRuleset(
|
||||
catalogEntityPermissionResourceRef,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
const getResources = async (resourceRefs: string[]) => {
|
||||
@@ -519,24 +464,12 @@ export class CatalogBuilder {
|
||||
return entitiesResponseToObjects(items).map(e => e || undefined);
|
||||
};
|
||||
|
||||
let permissionIntegrationRouter:
|
||||
| ReturnType<typeof createPermissionIntegrationRouter>
|
||||
| undefined;
|
||||
if (permissionsRegistry) {
|
||||
permissionsRegistry.addResourceType({
|
||||
resourceRef: catalogEntityPermissionResourceRef,
|
||||
getResources,
|
||||
permissions: this.permissions,
|
||||
rules: this.permissionRules,
|
||||
});
|
||||
} else {
|
||||
permissionIntegrationRouter = createPermissionIntegrationRouter({
|
||||
resourceType: RESOURCE_TYPE_CATALOG_ENTITY,
|
||||
getResources,
|
||||
permissions: this.permissions,
|
||||
rules: this.permissionRules,
|
||||
});
|
||||
}
|
||||
permissionsRegistry.addResourceType({
|
||||
resourceRef: catalogEntityPermissionResourceRef,
|
||||
getResources,
|
||||
permissions: [...catalogPermissions],
|
||||
rules: Object.values(catalogPermissionRules),
|
||||
});
|
||||
|
||||
const scmEventHandlingConfig = readScmEventHandlingConfig(config);
|
||||
const locationStore = new DefaultLocationStore(
|
||||
@@ -589,7 +522,7 @@ export class CatalogBuilder {
|
||||
this.locationAnalyzer ??
|
||||
new AuthorizedLocationAnalyzer(
|
||||
new RepoLocationAnalyzer(logger, integrations, this.locationAnalyzers),
|
||||
permissionsService,
|
||||
permissions,
|
||||
);
|
||||
const locationService = new AuthorizedLocationService(
|
||||
new DefaultLocationService(locationStore, orchestrator, {
|
||||
@@ -599,11 +532,11 @@ export class CatalogBuilder {
|
||||
'catalog.defaultLocationConflictStrategy',
|
||||
) as 'refresh' | 'reject') || 'reject',
|
||||
}),
|
||||
permissionsService,
|
||||
permissions,
|
||||
);
|
||||
const refreshService = new AuthorizedRefreshService(
|
||||
new DefaultRefreshService({ database: catalogDatabase }),
|
||||
permissionsService,
|
||||
permissions,
|
||||
);
|
||||
|
||||
const router = await createRouter({
|
||||
@@ -614,10 +547,9 @@ export class CatalogBuilder {
|
||||
refreshService,
|
||||
logger,
|
||||
config,
|
||||
permissionIntegrationRouter,
|
||||
auth,
|
||||
httpAuth,
|
||||
permissionsService,
|
||||
permissionsService: permissions,
|
||||
auditor,
|
||||
enableRelationsCompatibility,
|
||||
});
|
||||
|
||||
@@ -36,13 +36,9 @@ import {
|
||||
import {
|
||||
CatalogModelExtensionPoint,
|
||||
catalogModelExtensionPoint,
|
||||
CatalogPermissionExtensionPoint,
|
||||
catalogPermissionExtensionPoint,
|
||||
CatalogPermissionRuleInput,
|
||||
catalogScmEventsServiceRef,
|
||||
} from '@backstage/plugin-catalog-node/alpha';
|
||||
import { eventsServiceRef } from '@backstage/plugin-events-node';
|
||||
import { Permission } from '@backstage/plugin-permission-common';
|
||||
import { merge } from 'lodash';
|
||||
import { CatalogBuilder } from './CatalogBuilder';
|
||||
import {
|
||||
@@ -66,33 +62,6 @@ class CatalogLocationsExtensionPointImpl
|
||||
}
|
||||
}
|
||||
|
||||
class CatalogPermissionExtensionPointImpl
|
||||
implements CatalogPermissionExtensionPoint
|
||||
{
|
||||
#permissions = new Array<Permission>();
|
||||
#permissionRules = new Array<CatalogPermissionRuleInput>();
|
||||
|
||||
addPermissions(...permission: Array<Permission | Array<Permission>>): void {
|
||||
this.#permissions.push(...permission.flat());
|
||||
}
|
||||
|
||||
addPermissionRules(
|
||||
...rules: Array<
|
||||
CatalogPermissionRuleInput | Array<CatalogPermissionRuleInput>
|
||||
>
|
||||
): void {
|
||||
this.#permissionRules.push(...rules.flat());
|
||||
}
|
||||
|
||||
get permissions() {
|
||||
return this.#permissions;
|
||||
}
|
||||
|
||||
get permissionRules() {
|
||||
return this.#permissionRules;
|
||||
}
|
||||
}
|
||||
|
||||
class CatalogModelExtensionPointImpl implements CatalogModelExtensionPoint {
|
||||
#fieldValidators: Partial<Validators> = {};
|
||||
|
||||
@@ -189,12 +158,6 @@ export const catalogPlugin = createBackendPlugin({
|
||||
},
|
||||
});
|
||||
|
||||
const permissionExtensions = new CatalogPermissionExtensionPointImpl();
|
||||
env.registerExtensionPoint(
|
||||
catalogPermissionExtensionPoint,
|
||||
permissionExtensions,
|
||||
);
|
||||
|
||||
const modelExtensions = new CatalogModelExtensionPointImpl();
|
||||
env.registerExtensionPoint(catalogModelExtensionPoint, modelExtensions);
|
||||
|
||||
@@ -282,8 +245,6 @@ export const catalogPlugin = createBackendPlugin({
|
||||
} else {
|
||||
builder.addLocationAnalyzers(...scmLocationAnalyzers);
|
||||
}
|
||||
builder.addPermissions(...permissionExtensions.permissions);
|
||||
builder.addPermissionRules(...permissionExtensions.permissionRules);
|
||||
builder.setFieldFormatValidators(modelExtensions.fieldValidators);
|
||||
|
||||
if (locationTypeExtensions.allowedLocationTypes) {
|
||||
|
||||
@@ -31,17 +31,10 @@ import {
|
||||
} from '@backstage/catalog-model';
|
||||
import { ConfigReader } from '@backstage/config';
|
||||
import { NotFoundError } from '@backstage/errors';
|
||||
import { RESOURCE_TYPE_CATALOG_ENTITY } from '@backstage/plugin-catalog-common/alpha';
|
||||
import { LocationAnalyzer } from '@backstage/plugin-catalog-node';
|
||||
import { AuthorizeResult } from '@backstage/plugin-permission-common';
|
||||
import {
|
||||
createPermissionIntegrationRouter,
|
||||
createPermissionRule,
|
||||
} from '@backstage/plugin-permission-node';
|
||||
import express from 'express';
|
||||
import { Server } from 'node:http';
|
||||
import request from 'supertest';
|
||||
import { z } from 'zod/v3';
|
||||
import { Cursor, EntitiesCatalog } from '../catalog/types';
|
||||
import { applyDatabaseMigrations } from '../database/migrations';
|
||||
import { DbLocationsRow } from '../database/tables';
|
||||
@@ -98,7 +91,6 @@ describe('createRouter readonly disabled', () => {
|
||||
logger: mockServices.logger.mock(),
|
||||
refreshService,
|
||||
config: new ConfigReader(undefined),
|
||||
permissionIntegrationRouter: express.Router(),
|
||||
auth: mockServices.auth(),
|
||||
httpAuth: mockServices.httpAuth(),
|
||||
locationAnalyzer,
|
||||
@@ -1386,7 +1378,6 @@ describe('createRouter readonly and raw json enabled', () => {
|
||||
readonly: true,
|
||||
},
|
||||
}),
|
||||
permissionIntegrationRouter: express.Router(),
|
||||
auth: mockServices.auth(),
|
||||
httpAuth: mockServices.httpAuth(),
|
||||
orchestrator: { process: jest.fn() },
|
||||
@@ -1558,110 +1549,6 @@ describe('createRouter readonly and raw json enabled', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('NextRouter permissioning', () => {
|
||||
let entitiesCatalog: jest.Mocked<EntitiesCatalog>;
|
||||
let locationService: jest.Mocked<LocationService>;
|
||||
let app: express.Express;
|
||||
let refreshService: RefreshService;
|
||||
const permissionsService = mockServices.permissions();
|
||||
|
||||
const fakeRule = createPermissionRule({
|
||||
name: 'FAKE_RULE',
|
||||
description: 'fake rule',
|
||||
resourceType: RESOURCE_TYPE_CATALOG_ENTITY,
|
||||
paramsSchema: z.object({
|
||||
foo: z.string(),
|
||||
}),
|
||||
apply: () => true,
|
||||
toQuery: () => ({ key: '', values: [] }),
|
||||
});
|
||||
|
||||
beforeAll(async () => {
|
||||
entitiesCatalog = {
|
||||
entities: jest.fn(),
|
||||
entitiesBatch: jest.fn(),
|
||||
removeEntityByUid: jest.fn(),
|
||||
entityAncestry: jest.fn(),
|
||||
facets: jest.fn(),
|
||||
queryEntities: jest.fn(),
|
||||
};
|
||||
locationService = {
|
||||
getLocation: jest.fn(),
|
||||
createLocation: jest.fn(),
|
||||
queryLocations: jest.fn(),
|
||||
listLocations: jest.fn(),
|
||||
deleteLocation: jest.fn(),
|
||||
getLocationByEntity: jest.fn(),
|
||||
};
|
||||
refreshService = { refresh: jest.fn() };
|
||||
const router = await createRouter({
|
||||
entitiesCatalog,
|
||||
locationService,
|
||||
logger: mockServices.logger.mock(),
|
||||
refreshService,
|
||||
config: new ConfigReader(undefined),
|
||||
permissionIntegrationRouter: createPermissionIntegrationRouter({
|
||||
resourceType: RESOURCE_TYPE_CATALOG_ENTITY,
|
||||
rules: [fakeRule],
|
||||
getResources: jest.fn((resourceRefs: string[]) =>
|
||||
Promise.resolve(
|
||||
resourceRefs.map(resourceRef => ({ id: resourceRef })),
|
||||
),
|
||||
),
|
||||
}),
|
||||
auth: mockServices.auth(),
|
||||
httpAuth: mockServices.httpAuth(),
|
||||
orchestrator: { process: jest.fn() },
|
||||
permissionsService,
|
||||
auditor: mockServices.auditor.mock(),
|
||||
});
|
||||
app = express().use(router);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('accepts and evaluates conditions at the apply-conditions endpoint', async () => {
|
||||
const spideySense: Entity = {
|
||||
apiVersion: 'a',
|
||||
kind: 'component',
|
||||
metadata: {
|
||||
name: 'spidey-sense',
|
||||
},
|
||||
};
|
||||
entitiesCatalog.entities.mockResolvedValueOnce({
|
||||
entities: { type: 'object', entities: [spideySense] },
|
||||
pageInfo: { hasNextPage: false },
|
||||
});
|
||||
|
||||
const requestBody = {
|
||||
items: [
|
||||
{
|
||||
id: '123',
|
||||
resourceType: 'catalog-entity',
|
||||
resourceRef: 'component:default/spidey-sense',
|
||||
conditions: {
|
||||
rule: 'FAKE_RULE',
|
||||
resourceType: 'catalog-entity',
|
||||
params: {
|
||||
foo: 'user:default/spiderman',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
const response = await request(app)
|
||||
.post('/.well-known/backstage/permissions/apply-conditions')
|
||||
.send(requestBody);
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body).toEqual({
|
||||
items: [{ id: '123', result: AuthorizeResult.ALLOW }],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('POST /locations/by-query works end to end', () => {
|
||||
const databases = TestDatabases.create();
|
||||
|
||||
|
||||
@@ -79,7 +79,6 @@ export interface RouterOptions {
|
||||
refreshService?: RefreshService;
|
||||
logger: LoggerService;
|
||||
config: Config;
|
||||
permissionIntegrationRouter?: express.Router;
|
||||
auth: AuthService;
|
||||
httpAuth: HttpAuthService;
|
||||
permissionsService: PermissionsService;
|
||||
@@ -108,7 +107,6 @@ export async function createRouter(
|
||||
refreshService,
|
||||
config,
|
||||
logger,
|
||||
permissionIntegrationRouter,
|
||||
permissionsService,
|
||||
auth,
|
||||
httpAuth,
|
||||
@@ -156,10 +154,6 @@ export async function createRouter(
|
||||
});
|
||||
}
|
||||
|
||||
if (permissionIntegrationRouter) {
|
||||
router.use(permissionIntegrationRouter);
|
||||
}
|
||||
|
||||
if (entitiesCatalog) {
|
||||
router
|
||||
.get('/entities', async (req, res) => {
|
||||
|
||||
@@ -165,6 +165,7 @@ See below the complete list of available configs:
|
||||
| `maxDepth` | A maximum number of levels of relations to display in the graph. | `number` | yes | `1` |
|
||||
| `unidirectional` | Shows only relations that are from the source to the target entity. | `boolean` | yes | `true` |
|
||||
| `mergeRelations` | Merge the relations line into a single one. | `boolean` | yes | `true` |
|
||||
| `showArrowHeads` | Show arrowheads on the relation lines | `boolean` | yes | `false` |
|
||||
| `direction` | Render direction of the graph. | `TB` \| `BT` \| `LR` \| `RL` | yes | `'LR'` |
|
||||
| `relationPairs` | A list of [pairs of entity relations](https://backstage.io/docs/features/software-catalog/well-known-relations#relations), used to define which relations are merged together and which the primary relation is. | `[string[], string[]]` | yes | Show all entity [relations](https://backstage.io/docs/features/software-catalog/well-known-relations#relations). |
|
||||
| `zoom` | Controls zoom behavior of graph. | `enabled` \| `disabled` \| `enable-on-click` | yes | `'enabled'` |
|
||||
@@ -265,6 +266,7 @@ See below the complete list of available configs:
|
||||
| `maxDepth` | A maximum number of levels of relations to display in the graph. | `number` | yes | `1` |
|
||||
| `unidirectional` | Shows only relations that are from the source to the target entity. | `boolean` | yes | `true` |
|
||||
| `mergeRelations` | Merge the relations line into a single one. | `boolean` | yes | `true` |
|
||||
| `showArrowHeads` | Show arrowheads on the relation lines | `boolean` | yes | `false` |
|
||||
| `direction` | Render direction of the graph. | `TB` \| `BT` \| `LR` \| `RL` | yes | `'LR'` |
|
||||
| `relationPairs` | A list of [pairs of entity relations](https://backstage.io/docs/features/software-catalog/well-known-relations#relations), used to define which relations are merged together and which the primary relation is. | `[string[], string[]]` | yes | Show all entity [relations](https://backstage.io/docs/features/software-catalog/well-known-relations#relations). |
|
||||
| `zoom` | Controls zoom behavior of graph. | `enabled` \| `disabled` \| `enable-on-click` | yes | `'enabled'` |
|
||||
|
||||
@@ -88,6 +88,7 @@ const _default: OverridableFrontendPlugin<
|
||||
maxDepth: number | undefined;
|
||||
unidirectional: boolean | undefined;
|
||||
mergeRelations: boolean | undefined;
|
||||
showArrowHeads: boolean | undefined;
|
||||
direction: 'TB' | 'BT' | 'LR' | 'RL' | undefined;
|
||||
relationPairs: [string, string][] | undefined;
|
||||
zoom: 'disabled' | 'enabled' | 'enable-on-click' | undefined;
|
||||
@@ -103,6 +104,7 @@ const _default: OverridableFrontendPlugin<
|
||||
direction?: 'TB' | 'BT' | 'LR' | 'RL' | undefined;
|
||||
zoom?: 'disabled' | 'enabled' | 'enable-on-click' | undefined;
|
||||
title?: string | undefined;
|
||||
showArrowHeads?: boolean | undefined;
|
||||
relations?: string[] | undefined;
|
||||
maxDepth?: number | undefined;
|
||||
kinds?: string[] | undefined;
|
||||
@@ -152,6 +154,7 @@ const _default: OverridableFrontendPlugin<
|
||||
maxDepth: number | undefined;
|
||||
unidirectional: boolean | undefined;
|
||||
mergeRelations: boolean | undefined;
|
||||
showArrowHeads: boolean | undefined;
|
||||
direction: 'TB' | 'BT' | 'LR' | 'RL' | undefined;
|
||||
showFilters: boolean | undefined;
|
||||
curve: 'curveStepBefore' | 'curveMonotoneX' | undefined;
|
||||
@@ -166,6 +169,7 @@ const _default: OverridableFrontendPlugin<
|
||||
curve?: 'curveStepBefore' | 'curveMonotoneX' | undefined;
|
||||
direction?: 'TB' | 'BT' | 'LR' | 'RL' | undefined;
|
||||
zoom?: 'disabled' | 'enabled' | 'enable-on-click' | undefined;
|
||||
showArrowHeads?: boolean | undefined;
|
||||
relations?: string[] | undefined;
|
||||
maxDepth?: number | undefined;
|
||||
rootEntityRefs?: string[] | undefined;
|
||||
|
||||
@@ -37,6 +37,7 @@ const CatalogGraphEntityCard = EntityCardBlueprint.makeWithOverrides({
|
||||
maxDepth: z => z.number().optional(),
|
||||
unidirectional: z => z.boolean().optional(),
|
||||
mergeRelations: z => z.boolean().optional(),
|
||||
showArrowHeads: z => z.boolean().optional(),
|
||||
direction: z => z.nativeEnum(Direction).optional(),
|
||||
relationPairs: z => z.array(z.tuple([z.string(), z.string()])).optional(),
|
||||
zoom: z => z.enum(['enabled', 'disabled', 'enable-on-click']).optional(),
|
||||
@@ -66,6 +67,7 @@ const CatalogGraphPage = PageBlueprint.makeWithOverrides({
|
||||
maxDepth: z => z.number().optional(),
|
||||
unidirectional: z => z.boolean().optional(),
|
||||
mergeRelations: z => z.boolean().optional(),
|
||||
showArrowHeads: z => z.boolean().optional(),
|
||||
direction: z => z.nativeEnum(Direction).optional(),
|
||||
showFilters: z => z.boolean().optional(),
|
||||
curve: z => z.enum(['curveStepBefore', 'curveMonotoneX']).optional(),
|
||||
|
||||
@@ -68,6 +68,7 @@ export const CatalogGraphCard = (
|
||||
maxDepth = 1,
|
||||
unidirectional = true,
|
||||
mergeRelations = true,
|
||||
showArrowHeads,
|
||||
direction = Direction.LEFT_RIGHT,
|
||||
kinds,
|
||||
relations,
|
||||
@@ -147,6 +148,7 @@ export const CatalogGraphCard = (
|
||||
relationPairs={relationPairs}
|
||||
entityFilter={entityFilter}
|
||||
zoom={zoom}
|
||||
showArrowHeads={showArrowHeads}
|
||||
/>
|
||||
</EntityInfoCard>
|
||||
);
|
||||
|
||||
@@ -131,7 +131,7 @@ export const CatalogGraphPage = (
|
||||
};
|
||||
} & Partial<EntityRelationsGraphProps>,
|
||||
) => {
|
||||
const { relationPairs, initialState, entityFilter } = props;
|
||||
const { relationPairs, initialState, entityFilter, showArrowHeads } = props;
|
||||
const { t } = useTranslationRef(catalogGraphTranslationRef);
|
||||
const navigate = useNavigate();
|
||||
const classes = useStyles();
|
||||
@@ -260,6 +260,7 @@ export const CatalogGraphPage = (
|
||||
}
|
||||
mergeRelations={mergeRelations}
|
||||
unidirectional={unidirectional}
|
||||
showArrowHeads={showArrowHeads}
|
||||
onNodeClick={onNodeClick}
|
||||
direction={direction}
|
||||
relationPairs={relationPairs}
|
||||
|
||||
@@ -11,10 +11,7 @@ import { CatalogProcessorParser } from '@backstage/plugin-catalog-node';
|
||||
import { EntitiesSearchFilter } from '@backstage/plugin-catalog-node';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { ExtensionPoint } from '@backstage/backend-plugin-api';
|
||||
import { Permission } from '@backstage/plugin-permission-common';
|
||||
import { PermissionResourceRef } from '@backstage/plugin-permission-node';
|
||||
import { PermissionRule } from '@backstage/plugin-permission-node';
|
||||
import { PermissionRuleParams } from '@backstage/plugin-permission-common';
|
||||
import { ServiceRef } from '@backstage/backend-plugin-api';
|
||||
import { Validators } from '@backstage/catalog-model';
|
||||
|
||||
@@ -47,26 +44,6 @@ export interface CatalogModelExtensionPoint {
|
||||
// @alpha (undocumented)
|
||||
export const catalogModelExtensionPoint: ExtensionPoint<CatalogModelExtensionPoint>;
|
||||
|
||||
// @alpha @deprecated (undocumented)
|
||||
export interface CatalogPermissionExtensionPoint {
|
||||
// (undocumented)
|
||||
addPermissionRules(
|
||||
...rules: Array<
|
||||
CatalogPermissionRuleInput | Array<CatalogPermissionRuleInput>
|
||||
>
|
||||
): void;
|
||||
// (undocumented)
|
||||
addPermissions(...permissions: Array<Permission | Array<Permission>>): void;
|
||||
}
|
||||
|
||||
// @alpha @deprecated (undocumented)
|
||||
export const catalogPermissionExtensionPoint: ExtensionPoint<CatalogPermissionExtensionPoint>;
|
||||
|
||||
// @alpha @deprecated (undocumented)
|
||||
export type CatalogPermissionRuleInput<
|
||||
TParams extends PermissionRuleParams = PermissionRuleParams,
|
||||
> = PermissionRule<Entity, EntitiesSearchFilter, 'catalog-entity', TParams>;
|
||||
|
||||
// @alpha @deprecated (undocumented)
|
||||
export type CatalogProcessingExtensionPoint = CatalogProcessingExtensionPoint_2;
|
||||
|
||||
|
||||
@@ -100,8 +100,5 @@ export const catalogAnalysisExtensionPoint = _catalogAnalysisExtensionPoint;
|
||||
|
||||
export type { CatalogModelExtensionPoint } from './extensions';
|
||||
export { catalogModelExtensionPoint } from './extensions';
|
||||
export type { CatalogPermissionRuleInput } from './extensions';
|
||||
export type { CatalogPermissionExtensionPoint } from './extensions';
|
||||
export { catalogPermissionExtensionPoint } from './extensions';
|
||||
|
||||
export * from './scmEvents';
|
||||
|
||||
@@ -19,17 +19,11 @@ import { Entity, Validators } from '@backstage/catalog-model';
|
||||
import {
|
||||
CatalogProcessor,
|
||||
CatalogProcessorParser,
|
||||
EntitiesSearchFilter,
|
||||
EntityProvider,
|
||||
PlaceholderResolver,
|
||||
LocationAnalyzer,
|
||||
ScmLocationAnalyzer,
|
||||
} from '@backstage/plugin-catalog-node';
|
||||
import {
|
||||
Permission,
|
||||
PermissionRuleParams,
|
||||
} from '@backstage/plugin-permission-common';
|
||||
import { PermissionRule } from '@backstage/plugin-permission-node';
|
||||
|
||||
/**
|
||||
* @public
|
||||
@@ -163,33 +157,3 @@ export const catalogModelExtensionPoint =
|
||||
createExtensionPoint<CatalogModelExtensionPoint>({
|
||||
id: 'catalog.model',
|
||||
});
|
||||
|
||||
/**
|
||||
* @alpha
|
||||
* @deprecated Use the `coreServices.permissionsRegistry` instead.
|
||||
*/
|
||||
export type CatalogPermissionRuleInput<
|
||||
TParams extends PermissionRuleParams = PermissionRuleParams,
|
||||
> = PermissionRule<Entity, EntitiesSearchFilter, 'catalog-entity', TParams>;
|
||||
|
||||
/**
|
||||
* @alpha
|
||||
* @deprecated Use the `coreServices.permissionsRegistry` instead.
|
||||
*/
|
||||
export interface CatalogPermissionExtensionPoint {
|
||||
addPermissions(...permissions: Array<Permission | Array<Permission>>): void;
|
||||
addPermissionRules(
|
||||
...rules: Array<
|
||||
CatalogPermissionRuleInput | Array<CatalogPermissionRuleInput>
|
||||
>
|
||||
): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* @alpha
|
||||
* @deprecated Use the `coreServices.permissionsRegistry` instead.
|
||||
*/
|
||||
export const catalogPermissionExtensionPoint =
|
||||
createExtensionPoint<CatalogPermissionExtensionPoint>({
|
||||
id: 'catalog.permission',
|
||||
});
|
||||
|
||||
@@ -5651,7 +5651,7 @@
|
||||
- `step`: The name of the step that was run
|
||||
- `result`: A string describing whether the task ran successfully, failed, or was skipped
|
||||
|
||||
You can find a guide for running Prometheus metrics here: https://github.com/backstage/backstage/blob/master/contrib/docs/tutorials/prometheus-metrics.md
|
||||
You can find a guide for running Prometheus metrics here: https://github.com/backstage/backstage/blob/384b7bac2e/contrib/docs/tutorials/prometheus-metrics.md
|
||||
|
||||
- 5921b5ce49: - The GitLab Project ID for the `publish:gitlab:merge-request` action is now passed through the query parameter `project` in the `repoUrl`. It still allows people to not use the `projectid` and use the `repoUrl` with the `owner` and `repo` query parameters instead. This makes it easier to publish to repositories instead of writing the full path to the project.
|
||||
- 5025d2e8b6: Adds the ability to pass (an optional) array of strings that will be applied to the newly scaffolded repository as topic labels.
|
||||
@@ -5744,7 +5744,7 @@
|
||||
- `step`: The name of the step that was run
|
||||
- `result`: A string describing whether the task ran successfully, failed, or was skipped
|
||||
|
||||
You can find a guide for running Prometheus metrics here: https://github.com/backstage/backstage/blob/master/contrib/docs/tutorials/prometheus-metrics.md
|
||||
You can find a guide for running Prometheus metrics here: https://github.com/backstage/backstage/blob/384b7bac2e/contrib/docs/tutorials/prometheus-metrics.md
|
||||
|
||||
### Patch Changes
|
||||
|
||||
|
||||
@@ -78,7 +78,6 @@
|
||||
"@backstage/plugin-scaffolder-common": "workspace:^",
|
||||
"@backstage/plugin-scaffolder-node": "workspace:^",
|
||||
"@backstage/types": "workspace:^",
|
||||
"@opentelemetry/api": "^1.9.0",
|
||||
"@types/luxon": "^3.0.0",
|
||||
"express": "^4.22.0",
|
||||
"fs-extra": "^11.2.0",
|
||||
|
||||
@@ -63,6 +63,7 @@ import {
|
||||
import {
|
||||
actionsServiceRef,
|
||||
actionsRegistryServiceRef,
|
||||
metricsServiceRef,
|
||||
} from '@backstage/backend-plugin-api/alpha';
|
||||
import { createScaffolderActions } from './actions';
|
||||
|
||||
@@ -151,6 +152,7 @@ export const scaffolderPlugin = createBackendPlugin({
|
||||
actionsRegistry: actionsServiceRef,
|
||||
actionsRegistryService: actionsRegistryServiceRef,
|
||||
scaffolderService: scaffolderServiceRef,
|
||||
metrics: metricsServiceRef,
|
||||
},
|
||||
async init({
|
||||
logger,
|
||||
@@ -168,6 +170,7 @@ export const scaffolderPlugin = createBackendPlugin({
|
||||
actionsRegistry,
|
||||
actionsRegistryService,
|
||||
scaffolderService,
|
||||
metrics,
|
||||
}) {
|
||||
const log = loggerToWinstonLogger(logger);
|
||||
const integrations = ScmIntegrations.fromConfig(config);
|
||||
@@ -244,6 +247,7 @@ export const scaffolderPlugin = createBackendPlugin({
|
||||
events,
|
||||
auditor,
|
||||
actionsRegistry,
|
||||
metrics,
|
||||
});
|
||||
httpRouter.use(router);
|
||||
},
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
BackstageCredentials,
|
||||
LoggerService,
|
||||
} from '@backstage/backend-plugin-api';
|
||||
import type { MetricsService } from '@backstage/backend-plugin-api/alpha';
|
||||
import type { UserEntity } from '@backstage/catalog-model';
|
||||
import { Config } from '@backstage/config';
|
||||
import { ScmIntegrations } from '@backstage/integration';
|
||||
@@ -81,6 +82,7 @@ export type TemplateTesterCreateOptions = {
|
||||
additionalTemplateGlobals?: Record<string, TemplateGlobal>;
|
||||
permissions?: PermissionEvaluator;
|
||||
config?: Config;
|
||||
metrics: MetricsService;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -39,7 +39,10 @@ import {
|
||||
mockCredentials,
|
||||
mockServices,
|
||||
} from '@backstage/backend-test-utils';
|
||||
import { actionsRegistryServiceMock } from '@backstage/backend-test-utils/alpha';
|
||||
import {
|
||||
actionsRegistryServiceMock,
|
||||
metricsServiceMock,
|
||||
} from '@backstage/backend-test-utils/alpha';
|
||||
|
||||
describe('NunjucksWorkflowRunner', () => {
|
||||
let actionRegistry: TemplateActionRegistry;
|
||||
@@ -249,6 +252,7 @@ describe('NunjucksWorkflowRunner', () => {
|
||||
logger,
|
||||
permissions: mockedPermissionApi,
|
||||
config,
|
||||
metrics: metricsServiceMock.mock(),
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -23,7 +23,6 @@ import {
|
||||
TaskStep,
|
||||
} from '@backstage/plugin-scaffolder-common';
|
||||
import { JsonArray, JsonObject, JsonValue } from '@backstage/types';
|
||||
import { metrics } from '@opentelemetry/api';
|
||||
import fs from 'fs-extra';
|
||||
import { validate as validateJsonSchema } from 'jsonschema';
|
||||
import nunjucks from 'nunjucks';
|
||||
@@ -42,6 +41,7 @@ import type {
|
||||
LoggerService,
|
||||
PermissionsService,
|
||||
} from '@backstage/backend-plugin-api';
|
||||
import type { MetricsService } from '@backstage/backend-plugin-api/alpha';
|
||||
import { UserEntity } from '@backstage/catalog-model';
|
||||
import {
|
||||
AuthorizeResult,
|
||||
@@ -78,6 +78,7 @@ type NunjucksWorkflowRunnerOptions = {
|
||||
additionalTemplateGlobals?: Record<string, TemplateGlobal>;
|
||||
permissions?: PermissionsService;
|
||||
config?: Config;
|
||||
metrics: MetricsService;
|
||||
};
|
||||
|
||||
type TemplateContext = {
|
||||
@@ -188,6 +189,8 @@ export class NunjucksWorkflowRunner implements WorkflowRunner {
|
||||
secrets?: Record<string, string>;
|
||||
} = { parameters: {}, secrets: {} };
|
||||
|
||||
private readonly tracker: ReturnType<typeof scaffoldingTracker>;
|
||||
|
||||
constructor(options: NunjucksWorkflowRunnerOptions) {
|
||||
this.options = options;
|
||||
this.defaultTemplateFilters = convertFiltersToRecord(
|
||||
@@ -195,10 +198,9 @@ export class NunjucksWorkflowRunner implements WorkflowRunner {
|
||||
integrations: this.options.integrations,
|
||||
}),
|
||||
);
|
||||
this.tracker = scaffoldingTracker(options.metrics);
|
||||
}
|
||||
|
||||
private readonly tracker = scaffoldingTracker();
|
||||
|
||||
async getEnvironmentConfig(): Promise<{
|
||||
parameters: JsonObject;
|
||||
secrets?: TaskSecrets;
|
||||
@@ -700,7 +702,7 @@ export class NunjucksWorkflowRunner implements WorkflowRunner {
|
||||
}
|
||||
}
|
||||
|
||||
function scaffoldingTracker() {
|
||||
function scaffoldingTracker(metrics: MetricsService) {
|
||||
// prom-client metrics are deprecated in favour of OpenTelemetry metrics.
|
||||
const promTaskCount = createCounterMetric({
|
||||
name: 'scaffolder_task_count',
|
||||
@@ -723,23 +725,22 @@ function scaffoldingTracker() {
|
||||
labelNames: ['template', 'step', 'result'],
|
||||
});
|
||||
|
||||
const meter = metrics.getMeter('default');
|
||||
const taskCount = meter.createCounter('scaffolder.task.count', {
|
||||
description: 'Count of task runs',
|
||||
const taskCount = metrics.createCounter('scaffolder.task.count', {
|
||||
description: 'Total number of scaffolder tasks executed',
|
||||
});
|
||||
|
||||
const taskDuration = meter.createHistogram('scaffolder.task.duration', {
|
||||
description: 'Duration of a task run',
|
||||
unit: 'seconds',
|
||||
const taskDuration = metrics.createHistogram('scaffolder.task.duration', {
|
||||
description: 'Time taken to complete a scaffolder task end-to-end',
|
||||
unit: 's',
|
||||
});
|
||||
|
||||
const stepCount = meter.createCounter('scaffolder.step.count', {
|
||||
description: 'Count of step runs',
|
||||
const stepCount = metrics.createCounter('scaffolder.step.count', {
|
||||
description: 'Total number of individual scaffolder action steps executed',
|
||||
});
|
||||
|
||||
const stepDuration = meter.createHistogram('scaffolder.step.duration', {
|
||||
description: 'Duration of a step runs',
|
||||
unit: 'seconds',
|
||||
const stepDuration = metrics.createHistogram('scaffolder.step.duration', {
|
||||
description: 'Time taken to complete a single scaffolder action step',
|
||||
unit: 's',
|
||||
});
|
||||
|
||||
async function taskStart(task: TaskContext) {
|
||||
|
||||
@@ -36,6 +36,7 @@ import { WorkflowRunner } from './types';
|
||||
import ObservableImpl from 'zen-observable';
|
||||
import waitForExpect from 'wait-for-expect';
|
||||
import { mockServices } from '@backstage/backend-test-utils';
|
||||
import { metricsServiceMock } from '@backstage/backend-test-utils/alpha';
|
||||
import { loggerToWinstonLogger } from '../../util/loggerToWinstonLogger';
|
||||
|
||||
jest.mock('./NunjucksWorkflowRunner');
|
||||
@@ -93,6 +94,7 @@ describe('TaskWorker', () => {
|
||||
integrations,
|
||||
taskBroker: broker,
|
||||
actionRegistry,
|
||||
metrics: metricsServiceMock.mock(),
|
||||
});
|
||||
|
||||
await broker.dispatch({
|
||||
@@ -124,6 +126,7 @@ describe('TaskWorker', () => {
|
||||
integrations,
|
||||
taskBroker: broker,
|
||||
actionRegistry,
|
||||
metrics: metricsServiceMock.mock(),
|
||||
});
|
||||
|
||||
const { taskId } = await broker.dispatch({
|
||||
@@ -174,6 +177,7 @@ describe('TaskWorker', () => {
|
||||
},
|
||||
},
|
||||
}),
|
||||
metrics: metricsServiceMock.mock(),
|
||||
});
|
||||
|
||||
await taskWorker.runOneTask({
|
||||
@@ -261,6 +265,7 @@ describe('Concurrent TaskWorker', () => {
|
||||
taskBroker: broker,
|
||||
actionRegistry,
|
||||
concurrentTasksLimit: expectedConcurrentTasks,
|
||||
metrics: metricsServiceMock.mock(),
|
||||
});
|
||||
|
||||
taskWorker.start();
|
||||
@@ -307,6 +312,7 @@ describe('Cancellable TaskWorker', () => {
|
||||
integrations,
|
||||
taskBroker,
|
||||
actionRegistry,
|
||||
metrics: metricsServiceMock.mock(),
|
||||
});
|
||||
|
||||
const steps = [...Array(10)].map(n => ({
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
|
||||
import { AuditorService, LoggerService } from '@backstage/backend-plugin-api';
|
||||
import type { MetricsService } from '@backstage/backend-plugin-api/alpha';
|
||||
import { assertError, InputError, stringifyError } from '@backstage/errors';
|
||||
import { ScmIntegrations } from '@backstage/integration';
|
||||
import { PermissionEvaluator } from '@backstage/plugin-permission-common';
|
||||
@@ -78,6 +79,7 @@ export type CreateWorkerOptions = {
|
||||
additionalTemplateGlobals?: Record<string, TemplateGlobal>;
|
||||
permissions?: PermissionEvaluator;
|
||||
gracefulShutdown?: boolean;
|
||||
metrics: MetricsService;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -123,6 +125,7 @@ export class TaskWorker {
|
||||
additionalTemplateGlobals,
|
||||
permissions,
|
||||
gracefulShutdown,
|
||||
metrics,
|
||||
} = options;
|
||||
|
||||
const workflowRunner = new NunjucksWorkflowRunner({
|
||||
@@ -135,6 +138,7 @@ export class TaskWorker {
|
||||
additionalTemplateGlobals,
|
||||
permissions,
|
||||
config,
|
||||
metrics,
|
||||
});
|
||||
|
||||
return new TaskWorker({
|
||||
|
||||
@@ -58,7 +58,10 @@ import {
|
||||
import { createDefaultFilters } from '../lib/templating/filters/createDefaultFilters';
|
||||
import { createRouter } from './router';
|
||||
import { DatabaseTaskStore } from '../scaffolder/tasks/DatabaseTaskStore';
|
||||
import { actionsRegistryServiceMock } from '@backstage/backend-test-utils/alpha';
|
||||
import {
|
||||
actionsRegistryServiceMock,
|
||||
metricsServiceMock,
|
||||
} from '@backstage/backend-test-utils/alpha';
|
||||
import { ActionsService } from '@backstage/backend-plugin-api/alpha';
|
||||
|
||||
function createDatabase(): DatabaseService {
|
||||
@@ -229,6 +232,7 @@ const createTestRouter = async (
|
||||
createDebugLogAction(),
|
||||
],
|
||||
actionsRegistry: overrides.actionsRegistry ?? actionsRegistryServiceMock(),
|
||||
metrics: metricsServiceMock.mock(),
|
||||
});
|
||||
|
||||
router.use(mockErrorHandler());
|
||||
|
||||
@@ -131,7 +131,10 @@ import {
|
||||
scaffolderTaskRules,
|
||||
scaffolderTemplateRules,
|
||||
} from './rules';
|
||||
import { ActionsService } from '@backstage/backend-plugin-api/alpha';
|
||||
import {
|
||||
ActionsService,
|
||||
MetricsService,
|
||||
} from '@backstage/backend-plugin-api/alpha';
|
||||
|
||||
/**
|
||||
* RouterOptions
|
||||
@@ -165,6 +168,7 @@ export interface RouterOptions {
|
||||
auditor?: AuditorService;
|
||||
autocompleteHandlers?: Record<string, AutocompleteHandler>;
|
||||
actionsRegistry: ActionsService;
|
||||
metrics: MetricsService;
|
||||
}
|
||||
|
||||
function isSupportedTemplate(entity: TemplateEntityV1beta3) {
|
||||
@@ -256,6 +260,7 @@ export async function createRouter(
|
||||
httpAuth,
|
||||
auditor,
|
||||
actionsRegistry,
|
||||
metrics,
|
||||
} = options;
|
||||
|
||||
const concurrentTasksLimit =
|
||||
@@ -344,6 +349,7 @@ export async function createRouter(
|
||||
concurrentTasksLimit,
|
||||
permissions,
|
||||
gracefulShutdown,
|
||||
metrics,
|
||||
...templateExtensions,
|
||||
});
|
||||
|
||||
@@ -375,6 +381,7 @@ export async function createRouter(
|
||||
workingDirectory,
|
||||
permissions,
|
||||
config,
|
||||
metrics,
|
||||
...templateExtensions,
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user