@red-hat-developer-hub/backstage-plugin-scorecard-backend 2.1.0 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/CHANGELOG.md +88 -0
  2. package/README.md +105 -4
  3. package/dist/database/DatabaseMetricValues.cjs.js +37 -0
  4. package/dist/database/DatabaseMetricValues.cjs.js.map +1 -1
  5. package/dist/permissions/permissionUtils.cjs.js +1 -1
  6. package/dist/permissions/permissionUtils.cjs.js.map +1 -1
  7. package/dist/plugin.cjs.js +3 -2
  8. package/dist/plugin.cjs.js.map +1 -1
  9. package/dist/providers/MetricProvidersRegistry.cjs.js +9 -3
  10. package/dist/providers/MetricProvidersRegistry.cjs.js.map +1 -1
  11. package/dist/scheduler/index.cjs.js +4 -1
  12. package/dist/scheduler/index.cjs.js.map +1 -1
  13. package/dist/scheduler/tasks/PullMetricsByProviderTask.cjs.js +17 -3
  14. package/dist/scheduler/tasks/PullMetricsByProviderTask.cjs.js.map +1 -1
  15. package/dist/service/CatalogMetricService.cjs.js +79 -102
  16. package/dist/service/CatalogMetricService.cjs.js.map +1 -1
  17. package/dist/service/router.cjs.js +57 -28
  18. package/dist/service/router.cjs.js.map +1 -1
  19. package/dist/utils/getEntitiesOwnedByUser.cjs.js +49 -0
  20. package/dist/utils/getEntitiesOwnedByUser.cjs.js.map +1 -0
  21. package/dist/utils/mergeEntityAndProviderThresholds.cjs.js +59 -0
  22. package/dist/utils/mergeEntityAndProviderThresholds.cjs.js.map +1 -0
  23. package/dist/utils/parseCommaSeparatedString.cjs.js +8 -0
  24. package/dist/utils/parseCommaSeparatedString.cjs.js.map +1 -0
  25. package/dist/validation/validateCatalogMetricsSchema.cjs.js +18 -0
  26. package/dist/validation/validateCatalogMetricsSchema.cjs.js.map +1 -0
  27. package/dist/validation/validateMetricsSchema.cjs.js +19 -0
  28. package/dist/validation/validateMetricsSchema.cjs.js.map +1 -0
  29. package/migrations/20251117131443_add_status_column.js +30 -0
  30. package/package.json +13 -13
package/CHANGELOG.md CHANGED
@@ -1,5 +1,93 @@
1
1
  # @red-hat-developer-hub/backstage-plugin-scorecard-backend
2
2
 
3
+ ## 2.3.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 23e21ad: Added `metricIds` query parameter to the `/metrics` endpoint to filter metrics by metric IDs.
8
+
9
+ Scorecard read permission are no longer needed to get available metrics for the `/metrics` endpoint.
10
+
11
+ - 4e360d5: Implemented endpoint to aggregate metrics for scorecard metrics
12
+
13
+ **BREAKING** Update attribute `value` in the `MetricResult` type and update validation to support `null` instead `undefined` for the updated attribute
14
+
15
+ ```diff
16
+ export type MetricResult = {
17
+ id: string;
18
+ status: 'success' | 'error';
19
+ metadata: {
20
+ title: string;
21
+ description: string;
22
+ type: MetricType;
23
+ history?: boolean;
24
+ };
25
+ result: {
26
+ - value?: MetricValue;
27
+ + value: MetricValue | null;
28
+ timestamp: string;
29
+ thresholdResult: ThresholdResult;
30
+ };
31
+ error?: string;
32
+ };
33
+ ```
34
+
35
+ **BREAKING** Update attribute `evaluation` in the `ThresholdResult` type and update validation to support `null` instead `undefined` for the updated attribute
36
+
37
+ ```diff
38
+ export type ThresholdResult = {
39
+ status: 'success' | 'error';
40
+ - definition: ThresholdConfig | undefined;
41
+ + definition: ThresholdConfig | null;
42
+ evaluation: string | undefined; // threshold key the expression evaluated to
43
+ error?: string;
44
+ };
45
+ ```
46
+
47
+ ### Patch Changes
48
+
49
+ - Updated dependencies [52b60ee]
50
+ - Updated dependencies [4e360d5]
51
+ - @red-hat-developer-hub/backstage-plugin-scorecard-common@2.3.0
52
+ - @red-hat-developer-hub/backstage-plugin-scorecard-node@2.3.0
53
+
54
+ ## 2.2.0
55
+
56
+ ### Minor Changes
57
+
58
+ - f8fb8e4: Implemented saving metric `status` to the database. Added logic for saving `status` in the metric puller scheduler.
59
+
60
+ **BREAKING**: Added method `getMetricType` to the `MetricProvider` interface and updated the `getMetric` method to use `getMetricType()` instead of hardcoded `type` values.
61
+
62
+ ```diff
63
+ export class MyMetricProvider implements MetricProvider {
64
+ + getMetricType(): 'number' {
65
+ + return 'number';
66
+ + }
67
+
68
+ getMetric(): Metric<'number'> {
69
+ return {
70
+ id: this.getProviderId(),
71
+ title: 'GitHub open PRs',
72
+ description:
73
+ 'Current count of open Pull Requests for a given GitHub repository.',
74
+ - type: 'number',
75
+ + type: this.getMetricType(),
76
+ history: true,
77
+ };
78
+ }
79
+ }
80
+ ```
81
+
82
+ - 4c2261f: Backstage version bump to v1.45.2
83
+
84
+ ### Patch Changes
85
+
86
+ - Updated dependencies [f8fb8e4]
87
+ - Updated dependencies [4c2261f]
88
+ - @red-hat-developer-hub/backstage-plugin-scorecard-node@2.2.0
89
+ - @red-hat-developer-hub/backstage-plugin-scorecard-common@2.2.0
90
+
3
91
  ## 2.1.0
4
92
 
5
93
  ### Minor Changes
package/README.md CHANGED
@@ -75,15 +75,17 @@ The Scorecard plugin collects metrics from third-party data sources using metric
75
75
 
76
76
  The following metric providers are available:
77
77
 
78
- | Provider | Metric ID | Title | Description | Type |
79
- | ---------- | ------------------ | ---------------- | ------------------------------------- | ------ |
80
- | **GitHub** | `github.open_prs` | GitHub open PRs | Count of open Pull Requests in GitHub | number |
81
- | **Jira** | `jira.open_issues` | Jira open issues | The number of opened issues in Jira | number |
78
+ | Provider | Metric ID | Title | Description | Type |
79
+ | ----------- | ------------------ | --------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | ------ |
80
+ | **GitHub** | `github.open_prs` | GitHub open PRs | Count of open Pull Requests in GitHub | number |
81
+ | **Jira** | `jira.open_issues` | Jira open issues | The number of opened issues in Jira | number |
82
+ | **OpenSSF** | `openssf.*` | OpenSSF Security Scorecards | 18 security metrics from OpenSSF Scorecards (e.g., `openssf.code_review`, `openssf.maintained`). Each returns a score from 0-10. | number |
82
83
 
83
84
  To use these providers, install the corresponding backend modules:
84
85
 
85
86
  - GitHub: [`@red-hat-developer-hub/backstage-plugin-scorecard-backend-module-github`](../scorecard-backend-module-github/README.md)
86
87
  - Jira: [`@red-hat-developer-hub/backstage-plugin-scorecard-backend-module-jira`](../scorecard-backend-module-jira/README.md)
88
+ - OpenSSF: [`@red-hat-developer-hub/backstage-plugin-scorecard-backend-module-openssf`](../scorecard-backend-module-openssf/README.md)
87
89
 
88
90
  ## Thresholds
89
91
 
@@ -97,6 +99,105 @@ Thresholds are evaluated in order, and the first matching rule determines the ca
97
99
 
98
100
  For comprehensive threshold configuration guide, examples, and best practices, see [thresholds.md](./docs/thresholds.md).
99
101
 
102
+ ## API Endpoints
103
+
104
+ ### `GET /metrics`
105
+
106
+ Returns a list of available metrics. Supports filtering by metric IDs or datasource.
107
+
108
+ #### Query Parameters
109
+
110
+ | Parameter | Type | Required | Description |
111
+ | ------------ | ------ | -------- | -------------------------------------------------------------------------------------------- |
112
+ | `metricIds` | string | No | Comma-separated list of metric IDs to filter by (e.g., `github.open_prs,github.open_issues`) |
113
+ | `datasource` | string | No | Filter metrics by datasource ID (e.g., `github`, `jira`, `sonar`) |
114
+
115
+ #### Behavior
116
+
117
+ - If `metricIds` is provided, returns only the specified metrics
118
+ - If `datasource` is provided (and `metricIds` is not), returns all metrics from that datasource
119
+ - If neither parameter is provided, returns all available metrics
120
+ - **Note**: Providing both `metricIds` and `datasource` will result in a `400 Bad Request` error
121
+
122
+ #### Example Requests
123
+
124
+ ```bash
125
+ # Get all metrics
126
+ curl -X GET "{{url}}/api/scorecard/metrics" \
127
+ -H "Authorization: Bearer <token>"
128
+
129
+ # Get specific metrics by IDs
130
+ curl -X GET "{{url}}/api/scorecard/metrics?metricIds=github.open_prs,github.open_issues" \
131
+ -H "Authorization: Bearer <token>"
132
+
133
+ # Get all metrics from a specific datasource
134
+ curl -X GET "{{url}}/api/scorecard/metrics?datasource=github" \
135
+ -H "Authorization: Bearer <token>"
136
+ ```
137
+
138
+ ### `GET /metrics/catalog/:kind/:namespace/:name`
139
+
140
+ Returns the latest metric values for a specific catalog entity.
141
+
142
+ #### Path Parameters
143
+
144
+ | Parameter | Type | Required | Description |
145
+ | ----------- | ------ | -------- | ---------------------------------- |
146
+ | `kind` | string | Yes | Entity kind (e.g., `component`) |
147
+ | `namespace` | string | Yes | Entity namespace (e.g., `default`) |
148
+ | `name` | string | Yes | Entity name |
149
+
150
+ #### Query Parameters
151
+
152
+ | Parameter | Type | Required | Description |
153
+ | ----------- | ------ | -------- | -------------------------------------------------------------------------------------------- |
154
+ | `metricIds` | string | No | Comma-separated list of metric IDs to filter by (e.g., `github.open_prs,github.open_issues`) |
155
+
156
+ #### Permissions
157
+
158
+ Requires `scorecard.metric.read` permission and `catalog.entity.read` permission for the specific entity.
159
+
160
+ #### Example Request
161
+
162
+ ```bash
163
+ curl -X GET "{{url}}/api/scorecard/metrics/catalog/component/default/my-service?metricIds=github.open_prs" \
164
+ -H "Authorization: Bearer <token>"
165
+ ```
166
+
167
+ ### `GET /metrics/:metricId/catalog/aggregations`
168
+
169
+ Returns aggregated metrics for a specific metric across all entities owned by the authenticated user. This endpoint aggregates metrics from:
170
+
171
+ - Entities directly owned by the user
172
+ - Entities owned by groups the user is a direct member of (only direct parent groups are considered)
173
+
174
+ #### Path Parameters
175
+
176
+ | Parameter | Type | Required | Description |
177
+ | ---------- | ------ | -------- | --------------------------------- |
178
+ | `metricId` | string | Yes | The ID of the metric to aggregate |
179
+
180
+ #### Authentication
181
+
182
+ Requires user authentication. The endpoint uses the authenticated user's entity reference to determine which entities to aggregate.
183
+
184
+ #### Permissions
185
+
186
+ Requires `scorecard.metric.read` permission. Additionally:
187
+
188
+ - The user must have access to the specific metric (returns `403 Forbidden` if access is denied)
189
+ - The user must have `catalog.entity.read` permission for each entity that will be included in the aggregation
190
+
191
+ #### Example Request
192
+
193
+ ```bash
194
+ # Get aggregated metrics for a specific metric
195
+ curl -X GET "{{url}}/api/scorecard/metrics/github.open_prs/catalog/aggregations" \
196
+ -H "Authorization: Bearer <token>"
197
+ ```
198
+
199
+ For comprehensive documentation on how entity aggregation works, including details on transitive parent groups, error handling, and best practices, see [aggregation.md](./docs/aggregation.md).
200
+
100
201
  ## Configuration cleanup Job
101
202
 
102
203
  The plugin has a predefined job that runs every day to check and clean old metrics. By default, metrics are saved for **365 days**, however, this period can be changed in the `app-config.yaml` file. Here is an example of how to do that:
@@ -26,6 +26,43 @@ class DatabaseMetricValues {
26
26
  async cleanupExpiredMetrics(olderThan) {
27
27
  return await this.dbClient(this.tableName).where("timestamp", "<", olderThan).del();
28
28
  }
29
+ /**
30
+ * Get aggregated metrics by status for multiple entities and metrics.
31
+ */
32
+ async readAggregatedMetricsByEntityRefs(catalog_entity_refs, metric_ids) {
33
+ const latestIdsSubquery = this.dbClient(this.tableName).max("id").whereIn("metric_id", metric_ids).whereIn("catalog_entity_ref", catalog_entity_refs).groupBy("metric_id", "catalog_entity_ref");
34
+ const results = await this.dbClient(this.tableName).select("metric_id").count("* as total").max("timestamp as max_timestamp").select(
35
+ this.dbClient.raw(
36
+ "SUM(CASE WHEN status = 'success' AND value IS NOT NULL THEN 1 ELSE 0 END) as success"
37
+ )
38
+ ).select(
39
+ this.dbClient.raw(
40
+ "SUM(CASE WHEN status = 'warning' AND value IS NOT NULL THEN 1 ELSE 0 END) as warning"
41
+ )
42
+ ).select(
43
+ this.dbClient.raw(
44
+ "SUM(CASE WHEN status = 'error' AND value IS NOT NULL THEN 1 ELSE 0 END) as error"
45
+ )
46
+ ).whereIn("id", latestIdsSubquery).whereNotNull("status").whereNotNull("value").groupBy("metric_id");
47
+ return results.map((row) => {
48
+ let maxTimestamp;
49
+ if (row.max_timestamp instanceof Date) {
50
+ maxTimestamp = row.max_timestamp;
51
+ } else if (typeof row.max_timestamp === "number" || typeof row.max_timestamp === "string") {
52
+ maxTimestamp = new Date(row.max_timestamp);
53
+ } else {
54
+ maxTimestamp = /* @__PURE__ */ new Date();
55
+ }
56
+ return {
57
+ metric_id: row.metric_id,
58
+ total: Number(row.total),
59
+ max_timestamp: maxTimestamp,
60
+ success: Number(row.success),
61
+ warning: Number(row.warning),
62
+ error: Number(row.error)
63
+ };
64
+ });
65
+ }
29
66
  }
30
67
 
31
68
  exports.DatabaseMetricValues = DatabaseMetricValues;
@@ -1 +1 @@
1
- {"version":3,"file":"DatabaseMetricValues.cjs.js","sources":["../../src/database/DatabaseMetricValues.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Knex } from 'knex';\nimport { DbMetricValue } from './types';\n\nexport class DatabaseMetricValues {\n private readonly tableName = 'metric_values';\n\n constructor(private readonly dbClient: Knex<any, any[]>) {}\n\n /**\n * Insert multiple metric values\n */\n async createMetricValues(\n metricValues: Omit<DbMetricValue, 'id'>[],\n ): Promise<void> {\n await this.dbClient(this.tableName).insert(metricValues);\n }\n\n /**\n * Get the latest metric values for a specific entity and metrics\n */\n async readLatestEntityMetricValues(\n catalog_entity_ref: string,\n metric_ids: string[],\n ): Promise<DbMetricValue[]> {\n return await this.dbClient(this.tableName)\n .select('*')\n .whereIn(\n 'id',\n this.dbClient(this.tableName)\n .max('id')\n .whereIn('metric_id', metric_ids)\n .where('catalog_entity_ref', catalog_entity_ref)\n .groupBy('metric_id'),\n );\n }\n\n /**\n * Delete metric values that are older than the given date\n */\n async cleanupExpiredMetrics(olderThan: Date): Promise<number> {\n return await this.dbClient(this.tableName)\n .where('timestamp', '<', olderThan)\n .del();\n }\n}\n"],"names":[],"mappings":";;AAmBO,MAAM,oBAAqB,CAAA;AAAA,EAGhC,YAA6B,QAA4B,EAAA;AAA5B,IAAA,IAAA,CAAA,QAAA,GAAA,QAAA;AAAA;AAA6B,EAFzC,SAAY,GAAA,eAAA;AAAA;AAAA;AAAA;AAAA,EAO7B,MAAM,mBACJ,YACe,EAAA;AACf,IAAA,MAAM,KAAK,QAAS,CAAA,IAAA,CAAK,SAAS,CAAA,CAAE,OAAO,YAAY,CAAA;AAAA;AACzD;AAAA;AAAA;AAAA,EAKA,MAAM,4BACJ,CAAA,kBAAA,EACA,UAC0B,EAAA;AAC1B,IAAO,OAAA,MAAM,KAAK,QAAS,CAAA,IAAA,CAAK,SAAS,CACtC,CAAA,MAAA,CAAO,GAAG,CACV,CAAA,OAAA;AAAA,MACC,IAAA;AAAA,MACA,KAAK,QAAS,CAAA,IAAA,CAAK,SAAS,CAAA,CACzB,IAAI,IAAI,CAAA,CACR,OAAQ,CAAA,WAAA,EAAa,UAAU,CAC/B,CAAA,KAAA,CAAM,sBAAsB,kBAAkB,CAAA,CAC9C,QAAQ,WAAW;AAAA,KACxB;AAAA;AACJ;AAAA;AAAA;AAAA,EAKA,MAAM,sBAAsB,SAAkC,EAAA;AAC5D,IAAO,OAAA,MAAM,IAAK,CAAA,QAAA,CAAS,IAAK,CAAA,SAAS,CACtC,CAAA,KAAA,CAAM,WAAa,EAAA,GAAA,EAAK,SAAS,CAAA,CACjC,GAAI,EAAA;AAAA;AAEX;;;;"}
1
+ {"version":3,"file":"DatabaseMetricValues.cjs.js","sources":["../../src/database/DatabaseMetricValues.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Knex } from 'knex';\nimport {\n DbMetricValueCreate,\n DbMetricValue,\n DbAggregatedMetric,\n} from './types';\n\nexport class DatabaseMetricValues {\n private readonly tableName = 'metric_values';\n\n constructor(private readonly dbClient: Knex<any, any[]>) {}\n\n /**\n * Insert multiple metric values\n */\n async createMetricValues(metricValues: DbMetricValueCreate[]): Promise<void> {\n await this.dbClient(this.tableName).insert(metricValues);\n }\n\n /**\n * Get the latest metric values for a specific entity and metrics\n */\n async readLatestEntityMetricValues(\n catalog_entity_ref: string,\n metric_ids: string[],\n ): Promise<DbMetricValue[]> {\n return await this.dbClient(this.tableName)\n .select('*')\n .whereIn(\n 'id',\n this.dbClient(this.tableName)\n .max('id')\n .whereIn('metric_id', metric_ids)\n .where('catalog_entity_ref', catalog_entity_ref)\n .groupBy('metric_id'),\n );\n }\n\n /**\n * Delete metric values that are older than the given date\n */\n async cleanupExpiredMetrics(olderThan: Date): Promise<number> {\n return await this.dbClient(this.tableName)\n .where('timestamp', '<', olderThan)\n .del();\n }\n\n /**\n * Get aggregated metrics by status for multiple entities and metrics.\n */\n async readAggregatedMetricsByEntityRefs(\n catalog_entity_refs: string[],\n metric_ids: string[],\n ): Promise<DbAggregatedMetric[]> {\n const latestIdsSubquery = this.dbClient(this.tableName)\n .max('id')\n .whereIn('metric_id', metric_ids)\n .whereIn('catalog_entity_ref', catalog_entity_refs)\n .groupBy('metric_id', 'catalog_entity_ref');\n\n const results = await this.dbClient(this.tableName)\n .select('metric_id')\n .count('* as total')\n .max('timestamp as max_timestamp')\n .select(\n this.dbClient.raw(\n \"SUM(CASE WHEN status = 'success' AND value IS NOT NULL THEN 1 ELSE 0 END) as success\",\n ),\n )\n .select(\n this.dbClient.raw(\n \"SUM(CASE WHEN status = 'warning' AND value IS NOT NULL THEN 1 ELSE 0 END) as warning\",\n ),\n )\n .select(\n this.dbClient.raw(\n \"SUM(CASE WHEN status = 'error' AND value IS NOT NULL THEN 1 ELSE 0 END) as error\",\n ),\n )\n .whereIn('id', latestIdsSubquery)\n .whereNotNull('status')\n .whereNotNull('value')\n .groupBy('metric_id');\n\n // Normalize types for cross-database compatibility\n // PostgreSQL returns COUNT/SUM as strings, SQLite returns numbers\n // PostgreSQL returns MAX(timestamp) as Date, SQLite returns number (milliseconds)\n return results.map(row => {\n let maxTimestamp: Date;\n if (row.max_timestamp instanceof Date) {\n maxTimestamp = row.max_timestamp;\n } else if (\n typeof row.max_timestamp === 'number' ||\n typeof row.max_timestamp === 'string'\n ) {\n maxTimestamp = new Date(row.max_timestamp as string | number);\n } else {\n maxTimestamp = new Date();\n }\n\n return {\n metric_id: row.metric_id,\n total: Number(row.total),\n max_timestamp: maxTimestamp,\n success: Number(row.success),\n warning: Number(row.warning),\n error: Number(row.error),\n };\n });\n }\n}\n"],"names":[],"mappings":"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"}
@@ -11,7 +11,7 @@ const checkEntityAccess = async (entityRef, req, permissions, httpAuth) => {
11
11
  { credentials: await httpAuth.credentials(req) }
12
12
  );
13
13
  if (entityAccessDecision[0].result !== pluginPermissionCommon.AuthorizeResult.ALLOW) {
14
- throw new errors.NotAllowedError("Access to entity metrics denied");
14
+ throw new errors.NotAllowedError(`Access to "${entityRef}" entity metrics denied`);
15
15
  }
16
16
  };
17
17
  const matches = (metric, filters) => {
@@ -1 +1 @@
1
- {"version":3,"file":"permissionUtils.cjs.js","sources":["../../src/permissions/permissionUtils.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n PermissionCondition,\n PermissionCriteria,\n PermissionRuleParams,\n AuthorizeResult,\n} from '@backstage/plugin-permission-common';\nimport { Request } from 'express';\nimport { NotAllowedError } from '@backstage/errors';\nimport { catalogEntityReadPermission } from '@backstage/plugin-catalog-common/alpha';\nimport type {\n HttpAuthService,\n PermissionsService,\n} from '@backstage/backend-plugin-api';\n\nimport { Metric } from '@red-hat-developer-hub/backstage-plugin-scorecard-common';\n\nimport { rules as scorecardRules } from './rules';\n\nexport const checkEntityAccess = async (\n entityRef: string,\n req: Request,\n permissions: PermissionsService,\n httpAuth: HttpAuthService,\n): Promise<void> => {\n const entityAccessDecision = await permissions.authorize(\n [{ permission: catalogEntityReadPermission, resourceRef: entityRef }],\n { credentials: await httpAuth.credentials(req) },\n );\n\n if (entityAccessDecision[0].result !== AuthorizeResult.ALLOW) {\n throw new NotAllowedError('Access to entity metrics denied');\n }\n};\n\nexport const matches = (\n metric: Metric,\n filters?: PermissionCriteria<\n PermissionCondition<string, PermissionRuleParams>\n >,\n): boolean => {\n if (!filters) {\n return true;\n }\n\n if ('allOf' in filters) {\n return filters.allOf.every(filter => matches(metric, filter));\n }\n\n if ('anyOf' in filters) {\n return filters.anyOf.some(filter => matches(metric, filter));\n }\n\n if ('not' in filters) {\n return !matches(metric, filters.not);\n }\n return (\n Object.values(scorecardRules)\n .find(r => r.name === filters.rule)\n ?.apply(metric, filters.params ?? {}) ?? false\n );\n};\n\nexport const filterAuthorizedMetrics = (\n metrics: Metric[],\n filter?: PermissionCriteria<\n PermissionCondition<string, PermissionRuleParams>\n >,\n) => {\n if (!filter) {\n return metrics;\n }\n\n return metrics.filter(metric => matches(metric, filter));\n};\n"],"names":["catalogEntityReadPermission","AuthorizeResult","NotAllowedError","scorecardRules"],"mappings":";;;;;;;AAkCO,MAAM,iBAAoB,GAAA,OAC/B,SACA,EAAA,GAAA,EACA,aACA,QACkB,KAAA;AAClB,EAAM,MAAA,oBAAA,GAAuB,MAAM,WAAY,CAAA,SAAA;AAAA,IAC7C,CAAC,EAAE,UAAA,EAAYA,iCAA6B,EAAA,WAAA,EAAa,WAAW,CAAA;AAAA,IACpE,EAAE,WAAa,EAAA,MAAM,QAAS,CAAA,WAAA,CAAY,GAAG,CAAE;AAAA,GACjD;AAEA,EAAA,IAAI,oBAAqB,CAAA,CAAC,CAAE,CAAA,MAAA,KAAWC,uCAAgB,KAAO,EAAA;AAC5D,IAAM,MAAA,IAAIC,uBAAgB,iCAAiC,CAAA;AAAA;AAE/D;AAEa,MAAA,OAAA,GAAU,CACrB,MAAA,EACA,OAGY,KAAA;AACZ,EAAA,IAAI,CAAC,OAAS,EAAA;AACZ,IAAO,OAAA,IAAA;AAAA;AAGT,EAAA,IAAI,WAAW,OAAS,EAAA;AACtB,IAAA,OAAO,QAAQ,KAAM,CAAA,KAAA,CAAM,YAAU,OAAQ,CAAA,MAAA,EAAQ,MAAM,CAAC,CAAA;AAAA;AAG9D,EAAA,IAAI,WAAW,OAAS,EAAA;AACtB,IAAA,OAAO,QAAQ,KAAM,CAAA,IAAA,CAAK,YAAU,OAAQ,CAAA,MAAA,EAAQ,MAAM,CAAC,CAAA;AAAA;AAG7D,EAAA,IAAI,SAAS,OAAS,EAAA;AACpB,IAAA,OAAO,CAAC,OAAA,CAAQ,MAAQ,EAAA,OAAA,CAAQ,GAAG,CAAA;AAAA;AAErC,EAAA,OACE,OAAO,MAAO,CAAAC,WAAc,CACzB,CAAA,IAAA,CAAK,OAAK,CAAE,CAAA,IAAA,KAAS,OAAQ,CAAA,IAAI,GAChC,KAAM,CAAA,MAAA,EAAQ,QAAQ,MAAU,IAAA,EAAE,CAAK,IAAA,KAAA;AAE/C;AAEa,MAAA,uBAAA,GAA0B,CACrC,OAAA,EACA,MAGG,KAAA;AACH,EAAA,IAAI,CAAC,MAAQ,EAAA;AACX,IAAO,OAAA,OAAA;AAAA;AAGT,EAAA,OAAO,QAAQ,MAAO,CAAA,CAAA,MAAA,KAAU,OAAQ,CAAA,MAAA,EAAQ,MAAM,CAAC,CAAA;AACzD;;;;;;"}
1
+ {"version":3,"file":"permissionUtils.cjs.js","sources":["../../src/permissions/permissionUtils.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n PermissionCondition,\n PermissionCriteria,\n PermissionRuleParams,\n AuthorizeResult,\n} from '@backstage/plugin-permission-common';\nimport { Request } from 'express';\nimport { NotAllowedError } from '@backstage/errors';\nimport { catalogEntityReadPermission } from '@backstage/plugin-catalog-common/alpha';\nimport type {\n HttpAuthService,\n PermissionsService,\n} from '@backstage/backend-plugin-api';\n\nimport { Metric } from '@red-hat-developer-hub/backstage-plugin-scorecard-common';\n\nimport { rules as scorecardRules } from './rules';\n\nexport const checkEntityAccess = async (\n entityRef: string,\n req: Request,\n permissions: PermissionsService,\n httpAuth: HttpAuthService,\n): Promise<void> => {\n const entityAccessDecision = await permissions.authorize(\n [{ permission: catalogEntityReadPermission, resourceRef: entityRef }],\n { credentials: await httpAuth.credentials(req) },\n );\n\n if (entityAccessDecision[0].result !== AuthorizeResult.ALLOW) {\n throw new NotAllowedError(`Access to \"${entityRef}\" entity metrics denied`);\n }\n};\n\nexport const matches = (\n metric: Metric,\n filters?: PermissionCriteria<\n PermissionCondition<string, PermissionRuleParams>\n >,\n): boolean => {\n if (!filters) {\n return true;\n }\n\n if ('allOf' in filters) {\n return filters.allOf.every(filter => matches(metric, filter));\n }\n\n if ('anyOf' in filters) {\n return filters.anyOf.some(filter => matches(metric, filter));\n }\n\n if ('not' in filters) {\n return !matches(metric, filters.not);\n }\n return (\n Object.values(scorecardRules)\n .find(r => r.name === filters.rule)\n ?.apply(metric, filters.params ?? {}) ?? false\n );\n};\n\nexport const filterAuthorizedMetrics = (\n metrics: Metric[],\n filter?: PermissionCriteria<\n PermissionCondition<string, PermissionRuleParams>\n >,\n) => {\n if (!filter) {\n return metrics;\n }\n\n return metrics.filter(metric => matches(metric, filter));\n};\n"],"names":["catalogEntityReadPermission","AuthorizeResult","NotAllowedError","scorecardRules"],"mappings":";;;;;;;AAkCO,MAAM,iBAAoB,GAAA,OAC/B,SACA,EAAA,GAAA,EACA,aACA,QACkB,KAAA;AAClB,EAAM,MAAA,oBAAA,GAAuB,MAAM,WAAY,CAAA,SAAA;AAAA,IAC7C,CAAC,EAAE,UAAA,EAAYA,iCAA6B,EAAA,WAAA,EAAa,WAAW,CAAA;AAAA,IACpE,EAAE,WAAa,EAAA,MAAM,QAAS,CAAA,WAAA,CAAY,GAAG,CAAE;AAAA,GACjD;AAEA,EAAA,IAAI,oBAAqB,CAAA,CAAC,CAAE,CAAA,MAAA,KAAWC,uCAAgB,KAAO,EAAA;AAC5D,IAAA,MAAM,IAAIC,sBAAA,CAAgB,CAAc,WAAA,EAAA,SAAS,CAAyB,uBAAA,CAAA,CAAA;AAAA;AAE9E;AAEa,MAAA,OAAA,GAAU,CACrB,MAAA,EACA,OAGY,KAAA;AACZ,EAAA,IAAI,CAAC,OAAS,EAAA;AACZ,IAAO,OAAA,IAAA;AAAA;AAGT,EAAA,IAAI,WAAW,OAAS,EAAA;AACtB,IAAA,OAAO,QAAQ,KAAM,CAAA,KAAA,CAAM,YAAU,OAAQ,CAAA,MAAA,EAAQ,MAAM,CAAC,CAAA;AAAA;AAG9D,EAAA,IAAI,WAAW,OAAS,EAAA;AACtB,IAAA,OAAO,QAAQ,KAAM,CAAA,IAAA,CAAK,YAAU,OAAQ,CAAA,MAAA,EAAQ,MAAM,CAAC,CAAA;AAAA;AAG7D,EAAA,IAAI,SAAS,OAAS,EAAA;AACpB,IAAA,OAAO,CAAC,OAAA,CAAQ,MAAQ,EAAA,OAAA,CAAQ,GAAG,CAAA;AAAA;AAErC,EAAA,OACE,OAAO,MAAO,CAAAC,WAAc,CACzB,CAAA,IAAA,CAAK,OAAK,CAAE,CAAA,IAAA,KAAS,OAAQ,CAAA,IAAI,GAChC,KAAM,CAAA,MAAA,EAAQ,QAAQ,MAAU,IAAA,EAAE,CAAK,IAAA,KAAA;AAE/C;AAEa,MAAA,uBAAA,GAA0B,CACrC,OAAA,EACA,MAGG,KAAA;AACH,EAAA,IAAI,CAAC,MAAQ,EAAA;AACX,IAAO,OAAA,OAAA;AAAA;AAGT,EAAA,OAAO,QAAQ,MAAO,CAAA,CAAA,MAAA,KAAU,OAAQ,CAAA,MAAA,EAAQ,MAAM,CAAC,CAAA;AACzD;;;;;;"}
@@ -64,7 +64,6 @@ const scorecardPlugin = backendPluginApi.createBackendPlugin({
64
64
  catalog,
65
65
  auth,
66
66
  registry: metricProvidersRegistry,
67
- thresholdEvaluator: new ThresholdEvaluator.ThresholdEvaluator(),
68
67
  database: dbMetricValues
69
68
  });
70
69
  index.Scheduler.create({
@@ -74,12 +73,14 @@ const scorecardPlugin = backendPluginApi.createBackendPlugin({
74
73
  logger,
75
74
  scheduler,
76
75
  database: dbMetricValues,
77
- metricProvidersRegistry
76
+ metricProvidersRegistry,
77
+ thresholdEvaluator: new ThresholdEvaluator.ThresholdEvaluator()
78
78
  }).start();
79
79
  httpRouter.use(
80
80
  await router.createRouter({
81
81
  metricProvidersRegistry,
82
82
  catalogMetricService,
83
+ catalog,
83
84
  httpAuth,
84
85
  permissions
85
86
  })
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.cjs.js","sources":["../src/plugin.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {\n coreServices,\n createBackendPlugin,\n} from '@backstage/backend-plugin-api';\nimport { createRouter } from './service/router';\nimport { catalogServiceRef } from '@backstage/plugin-catalog-node';\nimport {\n MetricProvider,\n scorecardMetricsExtensionPoint,\n} from '@red-hat-developer-hub/backstage-plugin-scorecard-node';\nimport { MetricProvidersRegistry } from './providers/MetricProvidersRegistry';\nimport { CatalogMetricService } from './service/CatalogMetricService';\nimport { ThresholdEvaluator } from './threshold/ThresholdEvaluator';\nimport { scorecardPermissions } from '@red-hat-developer-hub/backstage-plugin-scorecard-common';\nimport {\n scorecardMetricPermissionResourceRef,\n rules as scorecardRules,\n} from './permissions/rules';\nimport { migrate } from './database/migration';\nimport { DatabaseMetricValues } from './database/DatabaseMetricValues';\nimport { Scheduler } from './scheduler';\n\n/**\n * scorecardPlugin backend plugin\n *\n * @public\n */\nexport const scorecardPlugin = createBackendPlugin({\n pluginId: 'scorecard',\n register(env) {\n const metricProvidersRegistry = new MetricProvidersRegistry();\n\n env.registerExtensionPoint(scorecardMetricsExtensionPoint, {\n addMetricProvider(...newMetricProviders: MetricProvider[]) {\n newMetricProviders.forEach(metricProvider => {\n metricProvidersRegistry.register(metricProvider);\n });\n },\n });\n\n env.registerInit({\n deps: {\n auth: coreServices.auth,\n catalog: catalogServiceRef,\n config: coreServices.rootConfig,\n database: coreServices.database,\n httpRouter: coreServices.httpRouter,\n httpAuth: coreServices.httpAuth,\n logger: coreServices.logger,\n permissions: coreServices.permissions,\n permissionsRegistry: coreServices.permissionsRegistry,\n scheduler: coreServices.scheduler,\n },\n async init({\n auth,\n catalog,\n config,\n database,\n httpRouter,\n httpAuth,\n logger,\n permissions,\n permissionsRegistry,\n scheduler,\n }) {\n permissionsRegistry.addResourceType({\n resourceRef: scorecardMetricPermissionResourceRef,\n getResources: async (resourceRefs: string[]) => {\n return metricProvidersRegistry.listMetrics(resourceRefs);\n },\n permissions: scorecardPermissions,\n rules: Object.values(scorecardRules),\n });\n\n // Run database migrations\n await migrate(database);\n\n const client = await database.getClient();\n const dbMetricValues = new DatabaseMetricValues(client);\n\n const catalogMetricService = new CatalogMetricService({\n catalog,\n auth,\n registry: metricProvidersRegistry,\n thresholdEvaluator: new ThresholdEvaluator(),\n database: dbMetricValues,\n });\n\n Scheduler.create({\n auth,\n catalog,\n config,\n logger,\n scheduler,\n database: dbMetricValues,\n metricProvidersRegistry,\n }).start();\n\n httpRouter.use(\n await createRouter({\n metricProvidersRegistry,\n catalogMetricService,\n httpAuth,\n permissions,\n }),\n );\n },\n });\n },\n});\n"],"names":["createBackendPlugin","MetricProvidersRegistry","scorecardMetricsExtensionPoint","coreServices","catalogServiceRef","scorecardMetricPermissionResourceRef","scorecardPermissions","scorecardRules","migrate","DatabaseMetricValues","CatalogMetricService","ThresholdEvaluator","Scheduler","createRouter"],"mappings":";;;;;;;;;;;;;;;AA0CO,MAAM,kBAAkBA,oCAAoB,CAAA;AAAA,EACjD,QAAU,EAAA,WAAA;AAAA,EACV,SAAS,GAAK,EAAA;AACZ,IAAM,MAAA,uBAAA,GAA0B,IAAIC,+CAAwB,EAAA;AAE5D,IAAA,GAAA,CAAI,uBAAuBC,2DAAgC,EAAA;AAAA,MACzD,qBAAqB,kBAAsC,EAAA;AACzD,QAAA,kBAAA,CAAmB,QAAQ,CAAkB,cAAA,KAAA;AAC3C,UAAA,uBAAA,CAAwB,SAAS,cAAc,CAAA;AAAA,SAChD,CAAA;AAAA;AACH,KACD,CAAA;AAED,IAAA,GAAA,CAAI,YAAa,CAAA;AAAA,MACf,IAAM,EAAA;AAAA,QACJ,MAAMC,6BAAa,CAAA,IAAA;AAAA,QACnB,OAAS,EAAAC,mCAAA;AAAA,QACT,QAAQD,6BAAa,CAAA,UAAA;AAAA,QACrB,UAAUA,6BAAa,CAAA,QAAA;AAAA,QACvB,YAAYA,6BAAa,CAAA,UAAA;AAAA,QACzB,UAAUA,6BAAa,CAAA,QAAA;AAAA,QACvB,QAAQA,6BAAa,CAAA,MAAA;AAAA,QACrB,aAAaA,6BAAa,CAAA,WAAA;AAAA,QAC1B,qBAAqBA,6BAAa,CAAA,mBAAA;AAAA,QAClC,WAAWA,6BAAa,CAAA;AAAA,OAC1B;AAAA,MACA,MAAM,IAAK,CAAA;AAAA,QACT,IAAA;AAAA,QACA,OAAA;AAAA,QACA,MAAA;AAAA,QACA,QAAA;AAAA,QACA,UAAA;AAAA,QACA,QAAA;AAAA,QACA,MAAA;AAAA,QACA,WAAA;AAAA,QACA,mBAAA;AAAA,QACA;AAAA,OACC,EAAA;AACD,QAAA,mBAAA,CAAoB,eAAgB,CAAA;AAAA,UAClC,WAAa,EAAAE,0CAAA;AAAA,UACb,YAAA,EAAc,OAAO,YAA2B,KAAA;AAC9C,YAAO,OAAA,uBAAA,CAAwB,YAAY,YAAY,CAAA;AAAA,WACzD;AAAA,UACA,WAAa,EAAAC,mDAAA;AAAA,UACb,KAAA,EAAO,MAAO,CAAA,MAAA,CAAOC,WAAc;AAAA,SACpC,CAAA;AAGD,QAAA,MAAMC,kBAAQ,QAAQ,CAAA;AAEtB,QAAM,MAAA,MAAA,GAAS,MAAM,QAAA,CAAS,SAAU,EAAA;AACxC,QAAM,MAAA,cAAA,GAAiB,IAAIC,yCAAA,CAAqB,MAAM,CAAA;AAEtD,QAAM,MAAA,oBAAA,GAAuB,IAAIC,yCAAqB,CAAA;AAAA,UACpD,OAAA;AAAA,UACA,IAAA;AAAA,UACA,QAAU,EAAA,uBAAA;AAAA,UACV,kBAAA,EAAoB,IAAIC,qCAAmB,EAAA;AAAA,UAC3C,QAAU,EAAA;AAAA,SACX,CAAA;AAED,QAAAC,eAAA,CAAU,MAAO,CAAA;AAAA,UACf,IAAA;AAAA,UACA,OAAA;AAAA,UACA,MAAA;AAAA,UACA,MAAA;AAAA,UACA,SAAA;AAAA,UACA,QAAU,EAAA,cAAA;AAAA,UACV;AAAA,SACD,EAAE,KAAM,EAAA;AAET,QAAW,UAAA,CAAA,GAAA;AAAA,UACT,MAAMC,mBAAa,CAAA;AAAA,YACjB,uBAAA;AAAA,YACA,oBAAA;AAAA,YACA,QAAA;AAAA,YACA;AAAA,WACD;AAAA,SACH;AAAA;AACF,KACD,CAAA;AAAA;AAEL,CAAC;;;;"}
1
+ {"version":3,"file":"plugin.cjs.js","sources":["../src/plugin.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {\n coreServices,\n createBackendPlugin,\n} from '@backstage/backend-plugin-api';\nimport { createRouter } from './service/router';\nimport { catalogServiceRef } from '@backstage/plugin-catalog-node';\nimport {\n MetricProvider,\n scorecardMetricsExtensionPoint,\n} from '@red-hat-developer-hub/backstage-plugin-scorecard-node';\nimport { MetricProvidersRegistry } from './providers/MetricProvidersRegistry';\nimport { CatalogMetricService } from './service/CatalogMetricService';\nimport { ThresholdEvaluator } from './threshold/ThresholdEvaluator';\nimport { scorecardPermissions } from '@red-hat-developer-hub/backstage-plugin-scorecard-common';\nimport {\n scorecardMetricPermissionResourceRef,\n rules as scorecardRules,\n} from './permissions/rules';\nimport { migrate } from './database/migration';\nimport { DatabaseMetricValues } from './database/DatabaseMetricValues';\nimport { Scheduler } from './scheduler';\n\n/**\n * scorecardPlugin backend plugin\n *\n * @public\n */\nexport const scorecardPlugin = createBackendPlugin({\n pluginId: 'scorecard',\n register(env) {\n const metricProvidersRegistry = new MetricProvidersRegistry();\n\n env.registerExtensionPoint(scorecardMetricsExtensionPoint, {\n addMetricProvider(...newMetricProviders: MetricProvider[]) {\n newMetricProviders.forEach(metricProvider => {\n metricProvidersRegistry.register(metricProvider);\n });\n },\n });\n\n env.registerInit({\n deps: {\n auth: coreServices.auth,\n catalog: catalogServiceRef,\n config: coreServices.rootConfig,\n database: coreServices.database,\n httpRouter: coreServices.httpRouter,\n httpAuth: coreServices.httpAuth,\n logger: coreServices.logger,\n permissions: coreServices.permissions,\n permissionsRegistry: coreServices.permissionsRegistry,\n scheduler: coreServices.scheduler,\n },\n async init({\n auth,\n catalog,\n config,\n database,\n httpRouter,\n httpAuth,\n logger,\n permissions,\n permissionsRegistry,\n scheduler,\n }) {\n permissionsRegistry.addResourceType({\n resourceRef: scorecardMetricPermissionResourceRef,\n getResources: async (resourceRefs: string[]) => {\n return metricProvidersRegistry.listMetrics(resourceRefs);\n },\n permissions: scorecardPermissions,\n rules: Object.values(scorecardRules),\n });\n\n // Run database migrations\n await migrate(database);\n\n const client = await database.getClient();\n const dbMetricValues = new DatabaseMetricValues(client);\n\n const catalogMetricService = new CatalogMetricService({\n catalog,\n auth,\n registry: metricProvidersRegistry,\n database: dbMetricValues,\n });\n\n Scheduler.create({\n auth,\n catalog,\n config,\n logger,\n scheduler,\n database: dbMetricValues,\n metricProvidersRegistry,\n thresholdEvaluator: new ThresholdEvaluator(),\n }).start();\n\n httpRouter.use(\n await createRouter({\n metricProvidersRegistry,\n catalogMetricService,\n catalog,\n httpAuth,\n permissions,\n }),\n );\n },\n });\n },\n});\n"],"names":["createBackendPlugin","MetricProvidersRegistry","scorecardMetricsExtensionPoint","coreServices","catalogServiceRef","scorecardMetricPermissionResourceRef","scorecardPermissions","scorecardRules","migrate","DatabaseMetricValues","CatalogMetricService","Scheduler","ThresholdEvaluator","createRouter"],"mappings":";;;;;;;;;;;;;;;AA0CO,MAAM,kBAAkBA,oCAAoB,CAAA;AAAA,EACjD,QAAU,EAAA,WAAA;AAAA,EACV,SAAS,GAAK,EAAA;AACZ,IAAM,MAAA,uBAAA,GAA0B,IAAIC,+CAAwB,EAAA;AAE5D,IAAA,GAAA,CAAI,uBAAuBC,2DAAgC,EAAA;AAAA,MACzD,qBAAqB,kBAAsC,EAAA;AACzD,QAAA,kBAAA,CAAmB,QAAQ,CAAkB,cAAA,KAAA;AAC3C,UAAA,uBAAA,CAAwB,SAAS,cAAc,CAAA;AAAA,SAChD,CAAA;AAAA;AACH,KACD,CAAA;AAED,IAAA,GAAA,CAAI,YAAa,CAAA;AAAA,MACf,IAAM,EAAA;AAAA,QACJ,MAAMC,6BAAa,CAAA,IAAA;AAAA,QACnB,OAAS,EAAAC,mCAAA;AAAA,QACT,QAAQD,6BAAa,CAAA,UAAA;AAAA,QACrB,UAAUA,6BAAa,CAAA,QAAA;AAAA,QACvB,YAAYA,6BAAa,CAAA,UAAA;AAAA,QACzB,UAAUA,6BAAa,CAAA,QAAA;AAAA,QACvB,QAAQA,6BAAa,CAAA,MAAA;AAAA,QACrB,aAAaA,6BAAa,CAAA,WAAA;AAAA,QAC1B,qBAAqBA,6BAAa,CAAA,mBAAA;AAAA,QAClC,WAAWA,6BAAa,CAAA;AAAA,OAC1B;AAAA,MACA,MAAM,IAAK,CAAA;AAAA,QACT,IAAA;AAAA,QACA,OAAA;AAAA,QACA,MAAA;AAAA,QACA,QAAA;AAAA,QACA,UAAA;AAAA,QACA,QAAA;AAAA,QACA,MAAA;AAAA,QACA,WAAA;AAAA,QACA,mBAAA;AAAA,QACA;AAAA,OACC,EAAA;AACD,QAAA,mBAAA,CAAoB,eAAgB,CAAA;AAAA,UAClC,WAAa,EAAAE,0CAAA;AAAA,UACb,YAAA,EAAc,OAAO,YAA2B,KAAA;AAC9C,YAAO,OAAA,uBAAA,CAAwB,YAAY,YAAY,CAAA;AAAA,WACzD;AAAA,UACA,WAAa,EAAAC,mDAAA;AAAA,UACb,KAAA,EAAO,MAAO,CAAA,MAAA,CAAOC,WAAc;AAAA,SACpC,CAAA;AAGD,QAAA,MAAMC,kBAAQ,QAAQ,CAAA;AAEtB,QAAM,MAAA,MAAA,GAAS,MAAM,QAAA,CAAS,SAAU,EAAA;AACxC,QAAM,MAAA,cAAA,GAAiB,IAAIC,yCAAA,CAAqB,MAAM,CAAA;AAEtD,QAAM,MAAA,oBAAA,GAAuB,IAAIC,yCAAqB,CAAA;AAAA,UACpD,OAAA;AAAA,UACA,IAAA;AAAA,UACA,QAAU,EAAA,uBAAA;AAAA,UACV,QAAU,EAAA;AAAA,SACX,CAAA;AAED,QAAAC,eAAA,CAAU,MAAO,CAAA;AAAA,UACf,IAAA;AAAA,UACA,OAAA;AAAA,UACA,MAAA;AAAA,UACA,MAAA;AAAA,UACA,SAAA;AAAA,UACA,QAAU,EAAA,cAAA;AAAA,UACV,uBAAA;AAAA,UACA,kBAAA,EAAoB,IAAIC,qCAAmB;AAAA,SAC5C,EAAE,KAAM,EAAA;AAET,QAAW,UAAA,CAAA,GAAA;AAAA,UACT,MAAMC,mBAAa,CAAA;AAAA,YACjB,uBAAA;AAAA,YACA,oBAAA;AAAA,YACA,OAAA;AAAA,YACA,QAAA;AAAA,YACA;AAAA,WACD;AAAA,SACH;AAAA;AACF,KACD,CAAA;AAAA;AAEL,CAAC;;;;"}
@@ -8,10 +8,16 @@ class MetricProvidersRegistry {
8
8
  register(metricProvider) {
9
9
  const providerId = metricProvider.getProviderId();
10
10
  const providerDatasource = metricProvider.getProviderDatasourceId();
11
- const metricId = metricProvider.getMetric().id;
12
- if (providerId !== metricId) {
11
+ const metric = metricProvider.getMetric();
12
+ const metricType = metricProvider.getMetricType();
13
+ if (providerId !== metric.id) {
13
14
  throw new Error(
14
- `Invalid metric provider with ID ${providerId}, provider ID must match metric ID '${metricId}'`
15
+ `Invalid metric provider with ID ${providerId}, provider ID must match metric ID '${metric.id}'`
16
+ );
17
+ }
18
+ if (metricType !== metric.type) {
19
+ throw new Error(
20
+ `Invalid metric provider with ID ${providerId}, getMetricType() must match getMetric().type. Expected '${metricType}', but got '${metric.type}'`
15
21
  );
16
22
  }
17
23
  const expectedPrefix = `${providerDatasource}.`;
@@ -1 +1 @@
1
- {"version":3,"file":"MetricProvidersRegistry.cjs.js","sources":["../../src/providers/MetricProvidersRegistry.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { ConflictError, NotFoundError } from '@backstage/errors';\nimport {\n Metric,\n MetricValue,\n} from '@red-hat-developer-hub/backstage-plugin-scorecard-common';\nimport type { Entity } from '@backstage/catalog-model';\nimport { MetricProvider } from '@red-hat-developer-hub/backstage-plugin-scorecard-node';\n\n/**\n * Registry of all registered metric providers.\n */\nexport class MetricProvidersRegistry {\n private readonly metricProviders = new Map<string, MetricProvider>();\n private readonly datasourceIndex = new Map<string, Set<string>>();\n\n register(metricProvider: MetricProvider): void {\n const providerId = metricProvider.getProviderId();\n const providerDatasource = metricProvider.getProviderDatasourceId();\n const metricId = metricProvider.getMetric().id;\n\n if (providerId !== metricId) {\n throw new Error(\n `Invalid metric provider with ID ${providerId}, provider ID must match metric ID '${metricId}'`,\n );\n }\n\n const expectedPrefix = `${providerDatasource}.`;\n if (\n !providerId.startsWith(expectedPrefix) ||\n providerId === expectedPrefix\n ) {\n throw new Error(\n `Invalid metric provider with ID ${providerId}, must have format '${providerDatasource}.<metric_name>' where metric name is not empty`,\n );\n }\n\n if (this.metricProviders.has(providerId)) {\n throw new ConflictError(\n `Metric provider with ID '${providerId}' has already been registered`,\n );\n }\n\n this.metricProviders.set(providerId, metricProvider);\n let datasourceProviders = this.datasourceIndex.get(providerDatasource);\n if (!datasourceProviders) {\n datasourceProviders = new Set();\n this.datasourceIndex.set(providerDatasource, datasourceProviders);\n }\n datasourceProviders.add(providerId);\n }\n\n getProvider(providerId: string): MetricProvider {\n const metricProvider = this.metricProviders.get(providerId);\n if (!metricProvider) {\n throw new NotFoundError(\n `Metric provider with ID '${providerId}' is not registered.`,\n );\n }\n return metricProvider;\n }\n\n getMetric(providerId: string): Metric {\n return this.getProvider(providerId).getMetric();\n }\n\n async calculateMetric(\n providerId: string,\n entity: Entity,\n ): Promise<MetricValue> {\n return this.getProvider(providerId).calculateMetric(entity);\n }\n\n async calculateMetrics(\n providerIds: string[],\n entity: Entity,\n ): Promise<{ providerId: string; value?: MetricValue; error?: Error }[]> {\n const results = await Promise.allSettled(\n providerIds.map(providerId => this.calculateMetric(providerId, entity)),\n );\n\n return results.map((result, index) => {\n const providerId = providerIds[index];\n if (result.status === 'fulfilled') {\n return { providerId, value: result.value };\n }\n return { providerId, error: result.reason as Error };\n });\n }\n\n listProviders(): MetricProvider[] {\n return Array.from(this.metricProviders.values());\n }\n\n listMetrics(providerIds?: string[]): Metric[] {\n if (providerIds && providerIds.length !== 0) {\n return providerIds\n .map(providerId => this.metricProviders.get(providerId)?.getMetric())\n .filter((m): m is Metric => m !== undefined);\n }\n return [...this.metricProviders.values()].map(provider =>\n provider.getMetric(),\n );\n }\n\n listMetricsByDatasource(datasourceId: string): Metric[] {\n const providerIdsOfDatasource = this.datasourceIndex.get(datasourceId);\n if (!providerIdsOfDatasource) {\n return [];\n }\n\n return Array.from(providerIdsOfDatasource)\n .map(providerId => this.metricProviders.get(providerId))\n .filter((provider): provider is MetricProvider => provider !== undefined)\n .map(provider => provider.getMetric());\n }\n}\n"],"names":["ConflictError","NotFoundError"],"mappings":"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"}
1
+ {"version":3,"file":"MetricProvidersRegistry.cjs.js","sources":["../../src/providers/MetricProvidersRegistry.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { ConflictError, NotFoundError } from '@backstage/errors';\nimport {\n Metric,\n MetricValue,\n} from '@red-hat-developer-hub/backstage-plugin-scorecard-common';\nimport type { Entity } from '@backstage/catalog-model';\nimport { MetricProvider } from '@red-hat-developer-hub/backstage-plugin-scorecard-node';\n\n/**\n * Registry of all registered metric providers.\n */\nexport class MetricProvidersRegistry {\n private readonly metricProviders = new Map<string, MetricProvider>();\n private readonly datasourceIndex = new Map<string, Set<string>>();\n\n register(metricProvider: MetricProvider): void {\n const providerId = metricProvider.getProviderId();\n const providerDatasource = metricProvider.getProviderDatasourceId();\n const metric = metricProvider.getMetric();\n const metricType = metricProvider.getMetricType();\n\n if (providerId !== metric.id) {\n throw new Error(\n `Invalid metric provider with ID ${providerId}, provider ID must match metric ID '${metric.id}'`,\n );\n }\n\n if (metricType !== metric.type) {\n throw new Error(\n `Invalid metric provider with ID ${providerId}, getMetricType() must match getMetric().type. Expected '${metricType}', but got '${metric.type}'`,\n );\n }\n\n const expectedPrefix = `${providerDatasource}.`;\n if (\n !providerId.startsWith(expectedPrefix) ||\n providerId === expectedPrefix\n ) {\n throw new Error(\n `Invalid metric provider with ID ${providerId}, must have format '${providerDatasource}.<metric_name>' where metric name is not empty`,\n );\n }\n\n if (this.metricProviders.has(providerId)) {\n throw new ConflictError(\n `Metric provider with ID '${providerId}' has already been registered`,\n );\n }\n\n this.metricProviders.set(providerId, metricProvider);\n\n let datasourceProviders = this.datasourceIndex.get(providerDatasource);\n if (!datasourceProviders) {\n datasourceProviders = new Set();\n this.datasourceIndex.set(providerDatasource, datasourceProviders);\n }\n datasourceProviders.add(providerId);\n }\n\n getProvider(providerId: string): MetricProvider {\n const metricProvider = this.metricProviders.get(providerId);\n if (!metricProvider) {\n throw new NotFoundError(\n `Metric provider with ID '${providerId}' is not registered.`,\n );\n }\n return metricProvider;\n }\n\n getMetric(providerId: string): Metric {\n return this.getProvider(providerId).getMetric();\n }\n\n async calculateMetric(\n providerId: string,\n entity: Entity,\n ): Promise<MetricValue> {\n return this.getProvider(providerId).calculateMetric(entity);\n }\n\n async calculateMetrics(\n providerIds: string[],\n entity: Entity,\n ): Promise<{ providerId: string; value?: MetricValue; error?: Error }[]> {\n const results = await Promise.allSettled(\n providerIds.map(providerId => this.calculateMetric(providerId, entity)),\n );\n\n return results.map((result, index) => {\n const providerId = providerIds[index];\n if (result.status === 'fulfilled') {\n return { providerId, value: result.value };\n }\n return { providerId, error: result.reason as Error };\n });\n }\n\n listProviders(): MetricProvider[] {\n return Array.from(this.metricProviders.values());\n }\n\n listMetrics(providerIds?: string[]): Metric[] {\n if (providerIds && providerIds.length !== 0) {\n return providerIds\n .map(providerId => this.metricProviders.get(providerId)?.getMetric())\n .filter((m): m is Metric => m !== undefined);\n }\n return [...this.metricProviders.values()].map(provider =>\n provider.getMetric(),\n );\n }\n\n listMetricsByDatasource(datasourceId: string): Metric[] {\n const providerIdsOfDatasource = this.datasourceIndex.get(datasourceId);\n\n if (!providerIdsOfDatasource) {\n return [];\n }\n\n return Array.from(providerIdsOfDatasource)\n .map(providerId => this.metricProviders.get(providerId))\n .filter((provider): provider is MetricProvider => provider !== undefined)\n .map(provider => provider.getMetric());\n }\n}\n"],"names":["ConflictError","NotFoundError"],"mappings":"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"}
@@ -12,6 +12,7 @@ class Scheduler {
12
12
  scheduler;
13
13
  database;
14
14
  metricProvidersRegistry;
15
+ thresholdEvaluator;
15
16
  tasks = [];
16
17
  constructor(options) {
17
18
  this.auth = options.auth;
@@ -21,6 +22,7 @@ class Scheduler {
21
22
  this.scheduler = options.scheduler;
22
23
  this.database = options.database;
23
24
  this.metricProvidersRegistry = options.metricProvidersRegistry;
25
+ this.thresholdEvaluator = options.thresholdEvaluator;
24
26
  }
25
27
  static create(options) {
26
28
  return new Scheduler(options);
@@ -68,7 +70,8 @@ class Scheduler {
68
70
  database: this.database,
69
71
  config: this.config,
70
72
  catalog: this.catalog,
71
- auth: this.auth
73
+ auth: this.auth,
74
+ thresholdEvaluator: this.thresholdEvaluator
72
75
  },
73
76
  provider
74
77
  )
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs.js","sources":["../../src/scheduler/index.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n AuthService,\n LoggerService,\n SchedulerService,\n} from '@backstage/backend-plugin-api';\nimport { CatalogService } from '@backstage/plugin-catalog-node';\nimport { MetricProvidersRegistry } from '../providers/MetricProvidersRegistry';\nimport type { Config } from '@backstage/config';\nimport { CLEANUP_EXPIRED_METRICS_ID } from './constants';\nimport { CleanupExpiredMetricsTask } from './tasks/CleanupExpiredMetricsTask';\nimport { PullMetricsByProviderTask } from './tasks/PullMetricsByProviderTask';\nimport { SchedulerOptions, SchedulerTask } from './types';\nimport { DatabaseMetricValues } from '../database/DatabaseMetricValues';\n\nexport class Scheduler {\n private readonly auth: AuthService;\n private readonly catalog: CatalogService;\n private readonly config: Config;\n private readonly logger: LoggerService;\n private readonly scheduler: SchedulerService;\n private readonly database: DatabaseMetricValues;\n private readonly metricProvidersRegistry: MetricProvidersRegistry;\n\n private tasks: Array<{ name: string; task: SchedulerTask }> = [];\n\n private constructor(options: SchedulerOptions) {\n this.auth = options.auth;\n this.catalog = options.catalog;\n this.config = options.config;\n this.logger = options.logger;\n this.scheduler = options.scheduler;\n this.database = options.database;\n this.metricProvidersRegistry = options.metricProvidersRegistry;\n }\n\n static create(options: SchedulerOptions): Scheduler {\n return new Scheduler(options);\n }\n\n async start(): Promise<void> {\n this.initializeTasks();\n this.initializeTasksByProviders();\n\n const results = await Promise.allSettled(\n this.tasks.map(({ name, task }) => this.startTask(name, task)),\n );\n\n const successCount = results.filter(r => r.status === 'fulfilled').length;\n\n let index = 0;\n\n for (const result of results) {\n if (result.status === 'rejected') {\n this.logger.warn(\n `Failed to start task '${this.tasks[index].name}': ${result.reason}`,\n );\n }\n index++;\n }\n\n this.logger.info(`Scheduled: ${successCount}/${this.tasks.length} tasks`);\n }\n\n private initializeTasks(): void {\n this.tasks = [\n {\n name: CLEANUP_EXPIRED_METRICS_ID,\n task: new CleanupExpiredMetricsTask({\n scheduler: this.scheduler,\n logger: this.logger,\n database: this.database,\n config: this.config,\n }),\n },\n ];\n }\n\n private initializeTasksByProviders(): void {\n const providers = this.metricProvidersRegistry.listProviders();\n\n for (const provider of providers) {\n this.tasks.push({\n name: provider.getProviderId(),\n task: new PullMetricsByProviderTask(\n {\n scheduler: this.scheduler,\n logger: this.logger,\n database: this.database,\n config: this.config,\n catalog: this.catalog,\n auth: this.auth,\n },\n provider,\n ),\n });\n }\n }\n\n private async startTask(name: string, task: SchedulerTask): Promise<void> {\n try {\n await task.start();\n } catch (error) {\n this.logger.error(`Failed to start task '${name}': ${error}`, error);\n throw error;\n }\n }\n}\n"],"names":["CLEANUP_EXPIRED_METRICS_ID","CleanupExpiredMetricsTask","PullMetricsByProviderTask"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs.js","sources":["../../src/scheduler/index.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n AuthService,\n LoggerService,\n SchedulerService,\n} from '@backstage/backend-plugin-api';\nimport { CatalogService } from '@backstage/plugin-catalog-node';\nimport { MetricProvidersRegistry } from '../providers/MetricProvidersRegistry';\nimport type { Config } from '@backstage/config';\nimport { CLEANUP_EXPIRED_METRICS_ID } from './constants';\nimport { CleanupExpiredMetricsTask } from './tasks/CleanupExpiredMetricsTask';\nimport { PullMetricsByProviderTask } from './tasks/PullMetricsByProviderTask';\nimport { SchedulerOptions, SchedulerTask } from './types';\nimport { DatabaseMetricValues } from '../database/DatabaseMetricValues';\nimport { ThresholdEvaluator } from '../threshold/ThresholdEvaluator';\n\nexport class Scheduler {\n private readonly auth: AuthService;\n private readonly catalog: CatalogService;\n private readonly config: Config;\n private readonly logger: LoggerService;\n private readonly scheduler: SchedulerService;\n private readonly database: DatabaseMetricValues;\n private readonly metricProvidersRegistry: MetricProvidersRegistry;\n private readonly thresholdEvaluator: ThresholdEvaluator;\n\n private tasks: Array<{ name: string; task: SchedulerTask }> = [];\n\n private constructor(options: SchedulerOptions) {\n this.auth = options.auth;\n this.catalog = options.catalog;\n this.config = options.config;\n this.logger = options.logger;\n this.scheduler = options.scheduler;\n this.database = options.database;\n this.metricProvidersRegistry = options.metricProvidersRegistry;\n this.thresholdEvaluator = options.thresholdEvaluator;\n }\n\n static create(options: SchedulerOptions): Scheduler {\n return new Scheduler(options);\n }\n\n async start(): Promise<void> {\n this.initializeTasks();\n this.initializeTasksByProviders();\n\n const results = await Promise.allSettled(\n this.tasks.map(({ name, task }) => this.startTask(name, task)),\n );\n\n const successCount = results.filter(r => r.status === 'fulfilled').length;\n\n let index = 0;\n\n for (const result of results) {\n if (result.status === 'rejected') {\n this.logger.warn(\n `Failed to start task '${this.tasks[index].name}': ${result.reason}`,\n );\n }\n index++;\n }\n\n this.logger.info(`Scheduled: ${successCount}/${this.tasks.length} tasks`);\n }\n\n private initializeTasks(): void {\n this.tasks = [\n {\n name: CLEANUP_EXPIRED_METRICS_ID,\n task: new CleanupExpiredMetricsTask({\n scheduler: this.scheduler,\n logger: this.logger,\n database: this.database,\n config: this.config,\n }),\n },\n ];\n }\n\n private initializeTasksByProviders(): void {\n const providers = this.metricProvidersRegistry.listProviders();\n\n for (const provider of providers) {\n this.tasks.push({\n name: provider.getProviderId(),\n task: new PullMetricsByProviderTask(\n {\n scheduler: this.scheduler,\n logger: this.logger,\n database: this.database,\n config: this.config,\n catalog: this.catalog,\n auth: this.auth,\n thresholdEvaluator: this.thresholdEvaluator,\n },\n provider,\n ),\n });\n }\n }\n\n private async startTask(name: string, task: SchedulerTask): Promise<void> {\n try {\n await task.start();\n } catch (error) {\n this.logger.error(`Failed to start task '${name}': ${error}`, error);\n throw error;\n }\n }\n}\n"],"names":["CLEANUP_EXPIRED_METRICS_ID","CleanupExpiredMetricsTask","PullMetricsByProviderTask"],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var backendPluginApi = require('@backstage/backend-plugin-api');
4
+ var mergeEntityAndProviderThresholds = require('../../utils/mergeEntityAndProviderThresholds.cjs.js');
4
5
  var uuid = require('uuid');
5
6
  var catalogModel = require('@backstage/catalog-model');
6
7
 
@@ -13,6 +14,7 @@ class PullMetricsByProviderTask {
13
14
  provider;
14
15
  scheduler;
15
16
  database;
17
+ thresholdEvaluator;
16
18
  static CATALOG_BATCH_SIZE = 50;
17
19
  static DEFAULT_SCHEDULE = {
18
20
  frequency: { hours: 1 },
@@ -28,6 +30,7 @@ class PullMetricsByProviderTask {
28
30
  this.provider = provider;
29
31
  this.scheduler = options.scheduler;
30
32
  this.database = options.database;
33
+ this.thresholdEvaluator = options.thresholdEvaluator;
31
34
  }
32
35
  async start() {
33
36
  const scheduleConfigPath = `scorecard.plugins.${this.providerId}.schedule`;
@@ -61,6 +64,7 @@ class PullMetricsByProviderTask {
61
64
  logger.info(`Pulling metrics for ${this.providerId}`);
62
65
  let totalProcessed = 0;
63
66
  let cursor = void 0;
67
+ const metricType = provider.getMetricType();
64
68
  try {
65
69
  do {
66
70
  const entitiesResponse = await this.catalog.queryEntities(
@@ -74,20 +78,30 @@ class PullMetricsByProviderTask {
74
78
  cursor = entitiesResponse.pageInfo.nextCursor;
75
79
  const batchResults = await Promise.allSettled(
76
80
  entitiesResponse.items.map(async (entity) => {
81
+ let value;
77
82
  try {
78
- const value = await provider.calculateMetric(entity);
83
+ value = await provider.calculateMetric(entity);
84
+ const thresholds = mergeEntityAndProviderThresholds.mergeEntityAndProviderThresholds(
85
+ entity,
86
+ provider
87
+ );
88
+ const status = this.thresholdEvaluator.getFirstMatchingThreshold(
89
+ value,
90
+ metricType,
91
+ thresholds
92
+ );
79
93
  return {
80
94
  catalog_entity_ref: catalogModel.stringifyEntityRef(entity),
81
95
  metric_id: this.providerId,
82
96
  value,
83
97
  timestamp: /* @__PURE__ */ new Date(),
84
- error_message: void 0
98
+ status
85
99
  };
86
100
  } catch (error) {
87
101
  return {
88
102
  catalog_entity_ref: catalogModel.stringifyEntityRef(entity),
89
103
  metric_id: this.providerId,
90
- value: void 0,
104
+ value,
91
105
  timestamp: /* @__PURE__ */ new Date(),
92
106
  error_message: error instanceof Error ? error.message : String(error)
93
107
  };
@@ -1 +1 @@
1
- {"version":3,"file":"PullMetricsByProviderTask.cjs.js","sources":["../../../src/scheduler/tasks/PullMetricsByProviderTask.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { DatabaseMetricValues } from '../../database/DatabaseMetricValues';\nimport {\n AuthService,\n readSchedulerServiceTaskScheduleDefinitionFromConfig,\n SchedulerService,\n SchedulerServiceTaskScheduleDefinition,\n LoggerService,\n} from '@backstage/backend-plugin-api';\nimport type { Config } from '@backstage/config';\nimport { CatalogService } from '@backstage/plugin-catalog-node';\nimport { MetricProvider } from '@red-hat-developer-hub/backstage-plugin-scorecard-node';\nimport { v4 as uuid } from 'uuid';\nimport { stringifyEntityRef } from '@backstage/catalog-model';\nimport { DbMetricValue } from '../../database/types';\nimport { SchedulerOptions, SchedulerTask } from '../types';\n\ntype Options = Pick<\n SchedulerOptions,\n 'scheduler' | 'logger' | 'database' | 'config' | 'catalog' | 'auth'\n>;\n\nexport class PullMetricsByProviderTask implements SchedulerTask {\n private readonly config: Config;\n private readonly auth: AuthService;\n private readonly providerId: string;\n private readonly logger: LoggerService;\n private readonly catalog: CatalogService;\n private readonly provider: MetricProvider;\n private readonly scheduler: SchedulerService;\n private readonly database: DatabaseMetricValues;\n\n private static readonly CATALOG_BATCH_SIZE = 50;\n\n private static readonly DEFAULT_SCHEDULE: SchedulerServiceTaskScheduleDefinition =\n {\n frequency: { hours: 1 },\n timeout: { minutes: 15 },\n initialDelay: { seconds: 3 },\n };\n\n constructor(options: Options, provider: MetricProvider) {\n this.config = options.config;\n this.auth = options.auth;\n this.providerId = provider.getProviderId();\n this.logger = options.logger;\n this.catalog = options.catalog;\n this.provider = provider;\n this.scheduler = options.scheduler;\n this.database = options.database;\n }\n\n async start(): Promise<void> {\n const scheduleConfigPath = `scorecard.plugins.${this.providerId}.schedule`;\n const schedule = this.getScheduleFromConfig(scheduleConfigPath);\n\n const taskRunner = this.scheduler.createScheduledTaskRunner(schedule);\n\n await taskRunner.run({\n id: this.providerId,\n fn: async () => {\n const logger = this.logger.child({\n class: this.constructor.name,\n taskId: this.providerId,\n taskInstanceId: uuid(),\n });\n\n try {\n await this.pullProviderMetrics(this.provider, logger);\n } catch (error) {\n logger.error(\n `${this.providerId} pulling metrics failed, ${error}`,\n error,\n );\n }\n },\n });\n }\n\n private getScheduleFromConfig(\n schedulePath: string,\n ): SchedulerServiceTaskScheduleDefinition {\n return this.config.has(schedulePath)\n ? readSchedulerServiceTaskScheduleDefinitionFromConfig(\n this.config.getConfig(schedulePath),\n )\n : PullMetricsByProviderTask.DEFAULT_SCHEDULE;\n }\n\n private async pullProviderMetrics(\n provider: MetricProvider,\n logger: LoggerService,\n ): Promise<void> {\n logger.info(`Pulling metrics for ${this.providerId}`);\n\n let totalProcessed = 0;\n let cursor: string | undefined = undefined;\n\n try {\n do {\n const entitiesResponse = await this.catalog.queryEntities(\n {\n filter: provider.getCatalogFilter(),\n limit: PullMetricsByProviderTask.CATALOG_BATCH_SIZE,\n ...(cursor ? { cursor } : {}),\n },\n { credentials: await this.auth.getOwnServiceCredentials() },\n );\n\n cursor = entitiesResponse.pageInfo.nextCursor;\n\n const batchResults = await Promise.allSettled(\n entitiesResponse.items.map(async entity => {\n try {\n const value = await provider.calculateMetric(entity);\n return {\n catalog_entity_ref: stringifyEntityRef(entity),\n metric_id: this.providerId,\n value,\n timestamp: new Date(),\n error_message: undefined,\n };\n } catch (error) {\n return {\n catalog_entity_ref: stringifyEntityRef(entity),\n metric_id: this.providerId,\n value: undefined,\n timestamp: new Date(),\n error_message:\n error instanceof Error ? error.message : String(error),\n };\n }\n }),\n ).then(promises =>\n promises.reduce((acc, curr) => {\n if (curr.status === 'fulfilled') {\n return [...acc, curr.value];\n }\n return acc;\n }, [] as Omit<DbMetricValue, 'id'>[]),\n );\n\n await this.database.createMetricValues(batchResults);\n totalProcessed += entitiesResponse.items.length;\n } while (cursor !== undefined);\n\n logger.info(\n `Completed metric pull for ${this.providerId}: processed ${totalProcessed} entities`,\n );\n } catch (error) {\n logger.error(`Failed to pull metrics for ${this.providerId}: ${error}`);\n\n throw error;\n }\n }\n}\n"],"names":["uuid","readSchedulerServiceTaskScheduleDefinitionFromConfig","stringifyEntityRef"],"mappings":"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"}
1
+ {"version":3,"file":"PullMetricsByProviderTask.cjs.js","sources":["../../../src/scheduler/tasks/PullMetricsByProviderTask.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { DatabaseMetricValues } from '../../database/DatabaseMetricValues';\nimport {\n AuthService,\n readSchedulerServiceTaskScheduleDefinitionFromConfig,\n SchedulerService,\n SchedulerServiceTaskScheduleDefinition,\n LoggerService,\n} from '@backstage/backend-plugin-api';\nimport type { Config } from '@backstage/config';\nimport { CatalogService } from '@backstage/plugin-catalog-node';\nimport { MetricProvider } from '@red-hat-developer-hub/backstage-plugin-scorecard-node';\nimport { mergeEntityAndProviderThresholds } from '../../utils/mergeEntityAndProviderThresholds';\nimport { v4 as uuid } from 'uuid';\nimport { stringifyEntityRef } from '@backstage/catalog-model';\nimport { DbMetricValueCreate } from '../../database/types';\nimport { SchedulerOptions, SchedulerTask } from '../types';\nimport { ThresholdEvaluator } from '../../threshold/ThresholdEvaluator';\nimport { MetricValue } from '@red-hat-developer-hub/backstage-plugin-scorecard-common';\n\ntype Options = Pick<\n SchedulerOptions,\n | 'scheduler'\n | 'logger'\n | 'database'\n | 'config'\n | 'catalog'\n | 'auth'\n | 'thresholdEvaluator'\n>;\n\nexport class PullMetricsByProviderTask implements SchedulerTask {\n private readonly config: Config;\n private readonly auth: AuthService;\n private readonly providerId: string;\n private readonly logger: LoggerService;\n private readonly catalog: CatalogService;\n private readonly provider: MetricProvider;\n private readonly scheduler: SchedulerService;\n private readonly database: DatabaseMetricValues;\n private readonly thresholdEvaluator: ThresholdEvaluator;\n\n private static readonly CATALOG_BATCH_SIZE = 50;\n\n private static readonly DEFAULT_SCHEDULE: SchedulerServiceTaskScheduleDefinition =\n {\n frequency: { hours: 1 },\n timeout: { minutes: 15 },\n initialDelay: { seconds: 3 },\n };\n\n constructor(options: Options, provider: MetricProvider) {\n this.config = options.config;\n this.auth = options.auth;\n this.providerId = provider.getProviderId();\n this.logger = options.logger;\n this.catalog = options.catalog;\n this.provider = provider;\n this.scheduler = options.scheduler;\n this.database = options.database;\n this.thresholdEvaluator = options.thresholdEvaluator;\n }\n\n async start(): Promise<void> {\n const scheduleConfigPath = `scorecard.plugins.${this.providerId}.schedule`;\n const schedule = this.getScheduleFromConfig(scheduleConfigPath);\n\n const taskRunner = this.scheduler.createScheduledTaskRunner(schedule);\n\n await taskRunner.run({\n id: this.providerId,\n fn: async () => {\n const logger = this.logger.child({\n class: this.constructor.name,\n taskId: this.providerId,\n taskInstanceId: uuid(),\n });\n\n try {\n await this.pullProviderMetrics(this.provider, logger);\n } catch (error) {\n logger.error(\n `${this.providerId} pulling metrics failed, ${error}`,\n error,\n );\n }\n },\n });\n }\n\n private getScheduleFromConfig(\n schedulePath: string,\n ): SchedulerServiceTaskScheduleDefinition {\n return this.config.has(schedulePath)\n ? readSchedulerServiceTaskScheduleDefinitionFromConfig(\n this.config.getConfig(schedulePath),\n )\n : PullMetricsByProviderTask.DEFAULT_SCHEDULE;\n }\n\n private async pullProviderMetrics(\n provider: MetricProvider,\n logger: LoggerService,\n ): Promise<void> {\n logger.info(`Pulling metrics for ${this.providerId}`);\n\n let totalProcessed = 0;\n let cursor: string | undefined = undefined;\n\n const metricType = provider.getMetricType();\n\n try {\n do {\n const entitiesResponse = await this.catalog.queryEntities(\n {\n filter: provider.getCatalogFilter(),\n limit: PullMetricsByProviderTask.CATALOG_BATCH_SIZE,\n ...(cursor ? { cursor } : {}),\n },\n { credentials: await this.auth.getOwnServiceCredentials() },\n );\n\n cursor = entitiesResponse.pageInfo.nextCursor;\n\n const batchResults = await Promise.allSettled(\n entitiesResponse.items.map(async entity => {\n let value: MetricValue | undefined;\n\n try {\n value = await provider.calculateMetric(entity);\n\n const thresholds = mergeEntityAndProviderThresholds(\n entity,\n provider,\n );\n\n const status = this.thresholdEvaluator.getFirstMatchingThreshold(\n value,\n metricType,\n thresholds,\n );\n\n return {\n catalog_entity_ref: stringifyEntityRef(entity),\n metric_id: this.providerId,\n value,\n timestamp: new Date(),\n status,\n } as DbMetricValueCreate;\n } catch (error) {\n return {\n catalog_entity_ref: stringifyEntityRef(entity),\n metric_id: this.providerId,\n value,\n timestamp: new Date(),\n error_message:\n error instanceof Error ? error.message : String(error),\n } as DbMetricValueCreate;\n }\n }),\n ).then(promises =>\n promises.reduce((acc, curr) => {\n if (curr.status === 'fulfilled') {\n return [...acc, curr.value];\n }\n return acc;\n }, [] as DbMetricValueCreate[]),\n );\n\n await this.database.createMetricValues(batchResults);\n totalProcessed += entitiesResponse.items.length;\n } while (cursor !== undefined);\n\n logger.info(\n `Completed metric pull for ${this.providerId}: processed ${totalProcessed} entities`,\n );\n } catch (error) {\n logger.error(`Failed to pull metrics for ${this.providerId}: ${error}`);\n\n throw error;\n }\n }\n}\n"],"names":["uuid","readSchedulerServiceTaskScheduleDefinitionFromConfig","mergeEntityAndProviderThresholds","stringifyEntityRef"],"mappings":"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"}