Skip to content

Commit 1ff0775

Browse files
committed
Extract scheduling to scorecard backend
Signed-off-by: Dominika Zemanovicova <[email protected]>
1 parent a6cb3a0 commit 1ff0775

File tree

21 files changed

+331
-634
lines changed

21 files changed

+331
-634
lines changed

workspaces/scorecard/plugins/scorecard-backend-module-github/src/metricProviders/GithubOpenPRsProvider.test.ts

Lines changed: 6 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@
1616

1717
import { ConfigReader } from '@backstage/config';
1818
import type { Entity } from '@backstage/catalog-model';
19-
import { mockServices } from '@backstage/backend-test-utils';
20-
import { catalogServiceMock } from '@backstage/plugin-catalog-node/testUtils';
2119
import { GithubOpenPRsProvider } from './GithubOpenPRsProvider';
2220
import { GithubClient } from '../github/GithubClient';
2321
import { DEFAULT_NUMBER_THRESHOLDS } from '@red-hat-developer-hub/backstage-plugin-scorecard-common';
@@ -32,58 +30,9 @@ jest.mock('@backstage/catalog-model', () => ({
3230
jest.mock('../github/GithubClient');
3331

3432
describe('GithubOpenPRsProvider', () => {
35-
const mockOptions = {
36-
auth: mockServices.auth(),
37-
logger: mockServices.logger.mock(),
38-
scheduler: mockServices.scheduler.mock(),
39-
catalog: catalogServiceMock(),
40-
};
41-
42-
describe('supportsEntity', () => {
43-
let provider: GithubOpenPRsProvider;
44-
45-
beforeEach(() => {
46-
provider = GithubOpenPRsProvider.fromConfig(
47-
new ConfigReader({}),
48-
mockOptions,
49-
);
50-
});
51-
52-
it.each([
53-
[
54-
'should return true for entity with github.com/project-slug annotation',
55-
{
56-
'github.com/project-slug': 'org/repo',
57-
},
58-
true,
59-
],
60-
[
61-
'should return false for entity without github.com/project-slug annotation',
62-
{
63-
'some.other/annotation': 'value',
64-
},
65-
false,
66-
],
67-
['should return false for entity with no annotations', undefined, false],
68-
])('%s', (_, annotations, expected) => {
69-
const entity: Entity = {
70-
apiVersion: 'backstage.io/v1alpha1',
71-
kind: 'Component',
72-
metadata: {
73-
name: 'test-component',
74-
annotations,
75-
},
76-
};
77-
expect(provider.supportsEntity(entity)).toBe(expected);
78-
});
79-
});
80-
8133
describe('fromConfig', () => {
8234
it('should create provider with default thresholds when no thresholds are configured', () => {
83-
const provider = GithubOpenPRsProvider.fromConfig(
84-
new ConfigReader({}),
85-
mockOptions,
86-
);
35+
const provider = GithubOpenPRsProvider.fromConfig(new ConfigReader({}));
8736

8837
expect(provider.getMetricThresholds()).toEqual(DEFAULT_NUMBER_THRESHOLDS);
8938
});
@@ -108,10 +57,7 @@ describe('GithubOpenPRsProvider', () => {
10857
},
10958
},
11059
});
111-
const provider = GithubOpenPRsProvider.fromConfig(
112-
configWithThresholds,
113-
mockOptions,
114-
);
60+
const provider = GithubOpenPRsProvider.fromConfig(configWithThresholds);
11561

11662
expect(provider.getMetricThresholds()).toEqual(customThresholds);
11763
});
@@ -131,9 +77,9 @@ describe('GithubOpenPRsProvider', () => {
13177
},
13278
});
13379

134-
expect(() =>
135-
GithubOpenPRsProvider.fromConfig(invalidConfig, mockOptions),
136-
).toThrow('Cannot parse "!100" as number from expression: ">!100"');
80+
expect(() => GithubOpenPRsProvider.fromConfig(invalidConfig)).toThrow(
81+
'Cannot parse "!100" as number from expression: ">!100"',
82+
);
13783
});
13884
});
13985

@@ -149,10 +95,7 @@ describe('GithubOpenPRsProvider', () => {
14995

15096
beforeEach(() => {
15197
jest.clearAllMocks();
152-
provider = GithubOpenPRsProvider.fromConfig(
153-
new ConfigReader({}),
154-
mockOptions,
155-
);
98+
provider = GithubOpenPRsProvider.fromConfig(new ConfigReader({}));
15699
});
157100

158101
it('should calculate metric', async () => {

workspaces/scorecard/plugins/scorecard-backend-module-github/src/metricProviders/GithubOpenPRsProvider.ts

Lines changed: 20 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -17,43 +17,25 @@
1717
import type { Config } from '@backstage/config';
1818
import { getEntitySourceLocation, type Entity } from '@backstage/catalog-model';
1919
import { CATALOG_FILTER_EXISTS } from '@backstage/catalog-client';
20-
import {
21-
AuthService,
22-
LoggerService,
23-
SchedulerService,
24-
SchedulerServiceTaskRunner,
25-
} from '@backstage/backend-plugin-api';
26-
import { CatalogService } from '@backstage/plugin-catalog-node';
2720
import {
2821
DEFAULT_NUMBER_THRESHOLDS,
2922
Metric,
3023
ThresholdConfig,
3124
} from '@red-hat-developer-hub/backstage-plugin-scorecard-common';
32-
import { BaseMetricProvider } from '@red-hat-developer-hub/backstage-plugin-scorecard-node';
25+
import {
26+
getThresholdsFromConfig,
27+
MetricProvider,
28+
} from '@red-hat-developer-hub/backstage-plugin-scorecard-node';
3329
import { GithubClient } from '../github/GithubClient';
3430
import { getRepositoryInformationFromEntity } from '../github/utils';
35-
import { GITHUB_PROJECT_ANNOTATION } from '../github/constants';
3631

37-
export class GithubOpenPRsProvider extends BaseMetricProvider<'number'> {
32+
export class GithubOpenPRsProvider implements MetricProvider<'number'> {
3833
private readonly githubClient: GithubClient;
34+
private readonly thresholds: ThresholdConfig;
3935

40-
private constructor(
41-
config: Config,
42-
auth: AuthService,
43-
logger: LoggerService,
44-
catalog: CatalogService,
45-
taskRunner: SchedulerServiceTaskRunner,
46-
thresholds?: ThresholdConfig,
47-
) {
48-
super(
49-
auth,
50-
logger,
51-
catalog,
52-
taskRunner,
53-
{ 'metadata.annotations.github.com/project-slug': CATALOG_FILTER_EXISTS },
54-
thresholds ?? DEFAULT_NUMBER_THRESHOLDS,
55-
);
36+
private constructor(config: Config, thresholds?: ThresholdConfig) {
5637
this.githubClient = new GithubClient(config);
38+
this.thresholds = thresholds ?? DEFAULT_NUMBER_THRESHOLDS;
5739
}
5840

5941
getProviderDatasourceId(): string {
@@ -75,40 +57,24 @@ export class GithubOpenPRsProvider extends BaseMetricProvider<'number'> {
7557
};
7658
}
7759

78-
supportsEntity(entity: Entity): boolean {
79-
return (
80-
entity.metadata.annotations?.[GITHUB_PROJECT_ANNOTATION] !== undefined
81-
);
60+
getMetricThresholds(): ThresholdConfig {
61+
return this.thresholds;
8262
}
8363

84-
static fromConfig(
85-
config: Config,
86-
options: {
87-
auth: AuthService;
88-
logger: LoggerService;
89-
scheduler: SchedulerService;
90-
catalog: CatalogService;
91-
},
92-
): GithubOpenPRsProvider {
93-
const thresholds = this.getThresholdsFromConfig(
64+
getCatalogFilter(): Record<string, string | symbol | (string | symbol)[]> {
65+
return {
66+
'metadata.annotations.github.com/project-slug': CATALOG_FILTER_EXISTS,
67+
};
68+
}
69+
70+
static fromConfig(config: Config): GithubOpenPRsProvider {
71+
const thresholds = getThresholdsFromConfig(
9472
config,
9573
'scorecard.plugins.github.open_prs.thresholds',
96-
);
97-
const schedule = this.getScheduleFromConfig(
98-
config,
99-
'scorecard.plugins.github.open_prs.schedule',
74+
'number',
10075
);
10176

102-
const taskRunner = options.scheduler.createScheduledTaskRunner(schedule);
103-
104-
return new GithubOpenPRsProvider(
105-
config,
106-
options.auth,
107-
options.logger,
108-
options.catalog,
109-
taskRunner,
110-
thresholds,
111-
);
77+
return new GithubOpenPRsProvider(config, thresholds);
11278
}
11379

11480
async calculateMetric(entity: Entity): Promise<number> {

workspaces/scorecard/plugins/scorecard-backend-module-github/src/module.ts

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import {
1717
coreServices,
1818
createBackendModule,
1919
} from '@backstage/backend-plugin-api';
20-
import { catalogServiceRef } from '@backstage/plugin-catalog-node';
2120
import { scorecardMetricsExtensionPoint } from '@red-hat-developer-hub/backstage-plugin-scorecard-node';
2221
import { GithubOpenPRsProvider } from './metricProviders/GithubOpenPRsProvider';
2322

@@ -27,22 +26,11 @@ export const scorecardModuleGithub = createBackendModule({
2726
register(reg) {
2827
reg.registerInit({
2928
deps: {
30-
auth: coreServices.auth,
3129
config: coreServices.rootConfig,
32-
logger: coreServices.logger,
33-
scheduler: coreServices.scheduler,
3430
metrics: scorecardMetricsExtensionPoint,
35-
catalog: catalogServiceRef,
3631
},
37-
async init({ auth, config, logger, scheduler, metrics, catalog }) {
38-
metrics.addMetricProvider(
39-
GithubOpenPRsProvider.fromConfig(config, {
40-
auth,
41-
logger,
42-
scheduler,
43-
catalog,
44-
}),
45-
);
32+
async init({ config, metrics }) {
33+
metrics.addMetricProvider(GithubOpenPRsProvider.fromConfig(config));
4634
},
4735
});
4836
},

workspaces/scorecard/plugins/scorecard-backend-module-jira/src/metricProviders/JiraOpenIssuesProvider.test.ts

Lines changed: 10 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@
1616

1717
import { ConfigReader } from '@backstage/config';
1818
import type { Entity } from '@backstage/catalog-model';
19-
import { mockServices } from '@backstage/backend-test-utils';
20-
import { catalogServiceMock } from '@backstage/plugin-catalog-node/testUtils';
2119
import { ThresholdConfig } from '@red-hat-developer-hub/backstage-plugin-scorecard-common';
2220
import { validateThresholds } from '@red-hat-developer-hub/backstage-plugin-scorecard-node';
2321
import { JiraOpenIssuesProvider } from './JiraOpenIssuesProvider';
@@ -63,44 +61,28 @@ const customThresholds: ThresholdConfig = {
6361
};
6462

6563
describe('JiraOpenIssuesProvider', () => {
66-
const mockOptions = {
67-
auth: mockServices.auth(),
68-
logger: mockServices.logger.mock(),
69-
scheduler: mockServices.scheduler.mock(),
70-
catalog: catalogServiceMock(),
71-
};
72-
7364
beforeEach(() => {
7465
jest.clearAllMocks();
7566
MockedJiraClientFactory.create.mockReturnValue(mockJiraClient);
7667
});
7768

7869
describe('getProviderDatasourceId', () => {
7970
it('should return "jira"', () => {
80-
const provider = JiraOpenIssuesProvider.fromConfig(
81-
new ConfigReader({}),
82-
mockOptions,
83-
);
71+
const provider = JiraOpenIssuesProvider.fromConfig(new ConfigReader({}));
8472
expect(provider.getProviderDatasourceId()).toEqual('jira');
8573
});
8674
});
8775

8876
describe('getProviderId', () => {
8977
it('should return "jira.open-issues"', () => {
90-
const provider = JiraOpenIssuesProvider.fromConfig(
91-
new ConfigReader({}),
92-
mockOptions,
93-
);
78+
const provider = JiraOpenIssuesProvider.fromConfig(new ConfigReader({}));
9479
expect(provider.getProviderId()).toEqual('jira.open-issues');
9580
});
9681
});
9782

9883
describe('getMetric', () => {
9984
it('should return correct metric metadata', () => {
100-
const provider = JiraOpenIssuesProvider.fromConfig(
101-
new ConfigReader({}),
102-
mockOptions,
103-
);
85+
const provider = JiraOpenIssuesProvider.fromConfig(new ConfigReader({}));
10486
expect(provider.getMetric()).toEqual({
10587
id: 'jira.open-issues',
10688
title: 'Jira open blocking tickets',
@@ -117,7 +99,6 @@ describe('JiraOpenIssuesProvider', () => {
11799
it('should return default config', () => {
118100
const provider = JiraOpenIssuesProvider.fromConfig(
119101
new ConfigReader({}),
120-
mockOptions,
121102
);
122103
expect(provider.getMetricThresholds()).toEqual({
123104
rules: [
@@ -142,10 +123,8 @@ describe('JiraOpenIssuesProvider', () => {
142123
},
143124
},
144125
});
145-
const provider = JiraOpenIssuesProvider.fromConfig(
146-
configWithThresholds,
147-
mockOptions,
148-
);
126+
const provider =
127+
JiraOpenIssuesProvider.fromConfig(configWithThresholds);
149128
expect(provider.getMetricThresholds()).toEqual(customThresholds);
150129
});
151130
});
@@ -156,7 +135,6 @@ describe('JiraOpenIssuesProvider', () => {
156135
it('should create provider with default config', () => {
157136
const provider = JiraOpenIssuesProvider.fromConfig(
158137
new ConfigReader({}),
159-
mockOptions,
160138
);
161139

162140
expect(MockedJiraClientFactory.create).toHaveBeenCalledWith(
@@ -187,10 +165,8 @@ describe('JiraOpenIssuesProvider', () => {
187165
},
188166
});
189167

190-
const provider = JiraOpenIssuesProvider.fromConfig(
191-
configWithThresholds,
192-
mockOptions,
193-
);
168+
const provider =
169+
JiraOpenIssuesProvider.fromConfig(configWithThresholds);
194170

195171
expect(MockedJiraClientFactory.create).toHaveBeenCalledWith(
196172
configWithThresholds,
@@ -227,9 +203,9 @@ describe('JiraOpenIssuesProvider', () => {
227203
},
228204
});
229205

230-
expect(() =>
231-
JiraOpenIssuesProvider.fromConfig(invalidConfig, mockOptions),
232-
).toThrow('Invalid thresholds');
206+
expect(() => JiraOpenIssuesProvider.fromConfig(invalidConfig)).toThrow(
207+
'Invalid thresholds',
208+
);
233209
expect(mockedValidateThresholds).toHaveBeenCalledWith(
234210
invalidThresholds,
235211
'number',
@@ -247,7 +223,6 @@ describe('JiraOpenIssuesProvider', () => {
247223
it('should return the count of open issues', async () => {
248224
const provider = JiraOpenIssuesProvider.fromConfig(
249225
new ConfigReader({}),
250-
mockOptions,
251226
);
252227

253228
const result = await provider.calculateMetric(mockEntity);
@@ -269,7 +244,6 @@ describe('JiraOpenIssuesProvider', () => {
269244
it('should propagate errors from Jira client', async () => {
270245
const provider = JiraOpenIssuesProvider.fromConfig(
271246
new ConfigReader({}),
272-
mockOptions,
273247
);
274248

275249
await expect(provider.calculateMetric(mockEntity)).rejects.toThrow(

0 commit comments

Comments
 (0)