@checkstack/healthcheck-jenkins-backend 0.2.13 → 0.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.
- package/CHANGELOG.md +24 -0
- package/package.json +1 -1
- package/src/collectors/build-history.test.ts +2 -2
- package/src/collectors/build-history.ts +21 -35
- package/src/collectors/job-status.test.ts +3 -3
- package/src/collectors/job-status.ts +24 -45
- package/src/collectors/node-health.test.ts +3 -3
- package/src/collectors/node-health.ts +27 -44
- package/src/collectors/queue-info.test.ts +3 -3
- package/src/collectors/queue-info.ts +24 -44
- package/src/collectors/server-info.test.ts +4 -4
- package/src/collectors/server-info.ts +18 -33
- package/src/strategy.test.ts +6 -6
- package/src/strategy.ts +27 -49
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
# @checkstack/healthcheck-jenkins-backend
|
|
2
2
|
|
|
3
|
+
## 0.3.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 3dd1914: Migrate health check strategies to VersionedAggregated with \_type discriminator
|
|
8
|
+
|
|
9
|
+
All 13 health check strategies now use `VersionedAggregated` for their `aggregatedResult` property, enabling automatic bucket merging with 100% mathematical fidelity.
|
|
10
|
+
|
|
11
|
+
**Key changes:**
|
|
12
|
+
|
|
13
|
+
- **`_type` discriminator**: All aggregated state objects now include a required `_type` field (`"average"`, `"rate"`, `"counter"`, `"minmax"`) for reliable type detection
|
|
14
|
+
- The `HealthCheckStrategy` interface now requires `aggregatedResult` to be a `VersionedAggregated<AggregatedResultShape>`
|
|
15
|
+
- Strategy/collector `mergeResult` methods return state objects with `_type` (e.g., `{ _type: "average", _sum, _count, avg }`)
|
|
16
|
+
- `mergeAggregatedBucketResults`, `combineBuckets`, and `reaggregateBuckets` now require `registry` and `strategyId` parameters
|
|
17
|
+
- `HealthCheckService` constructor now requires both `registry` and `collectorRegistry` parameters
|
|
18
|
+
- Frontend `extractComputedValue` now uses `_type` discriminator for robust type detection
|
|
19
|
+
|
|
20
|
+
**Breaking Change**: State objects now require `_type`. Merge functions automatically add `_type` to output. The bucket merging functions and `HealthCheckService` now require additional required parameters.
|
|
21
|
+
|
|
22
|
+
### Patch Changes
|
|
23
|
+
|
|
24
|
+
- Updated dependencies [3dd1914]
|
|
25
|
+
- @checkstack/backend-api@0.7.0
|
|
26
|
+
|
|
3
27
|
## 0.2.13
|
|
4
28
|
|
|
5
29
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -101,7 +101,7 @@ describe("BuildHistoryCollector", () => {
|
|
|
101
101
|
let aggregated = collector.mergeResult(undefined, runs[0]);
|
|
102
102
|
aggregated = collector.mergeResult(aggregated, runs[1]);
|
|
103
103
|
|
|
104
|
-
expect(aggregated.avgSuccessRate).toBe(70);
|
|
105
|
-
expect(aggregated.avgBuildDuration).toBe(70000);
|
|
104
|
+
expect(aggregated.avgSuccessRate.avg).toBe(70);
|
|
105
|
+
expect(aggregated.avgBuildDuration.avg).toBe(70000);
|
|
106
106
|
});
|
|
107
107
|
});
|
|
@@ -5,8 +5,9 @@ import {
|
|
|
5
5
|
type CollectorResult,
|
|
6
6
|
type CollectorStrategy,
|
|
7
7
|
mergeAverage,
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
VersionedAggregated,
|
|
9
|
+
aggregatedAverage,
|
|
10
|
+
type InferAggregatedResult,
|
|
10
11
|
} from "@checkstack/backend-api";
|
|
11
12
|
import { healthResultNumber } from "@checkstack/healthcheck-common";
|
|
12
13
|
import { pluginMetadata } from "../plugin-metadata";
|
|
@@ -89,32 +90,23 @@ const buildHistoryResultSchema = z.object({
|
|
|
89
90
|
|
|
90
91
|
export type BuildHistoryResult = z.infer<typeof buildHistoryResultSchema>;
|
|
91
92
|
|
|
92
|
-
|
|
93
|
-
|
|
93
|
+
// Aggregated result fields definition
|
|
94
|
+
const buildHistoryAggregatedFields = {
|
|
95
|
+
avgSuccessRate: aggregatedAverage({
|
|
94
96
|
"x-chart-type": "gauge",
|
|
95
97
|
"x-chart-label": "Avg Success Rate",
|
|
96
98
|
"x-chart-unit": "%",
|
|
97
99
|
}),
|
|
98
|
-
avgBuildDuration:
|
|
100
|
+
avgBuildDuration: aggregatedAverage({
|
|
99
101
|
"x-chart-type": "line",
|
|
100
102
|
"x-chart-label": "Avg Build Duration",
|
|
101
103
|
"x-chart-unit": "ms",
|
|
102
104
|
}),
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
const buildHistoryAggregatedInternalSchema = z.object({
|
|
106
|
-
_successRate: averageStateSchema
|
|
107
|
-
.optional(),
|
|
108
|
-
_duration: averageStateSchema
|
|
109
|
-
.optional(),
|
|
110
|
-
});
|
|
105
|
+
};
|
|
111
106
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
export type BuildHistoryAggregatedResult = z.infer<
|
|
117
|
-
typeof buildHistoryAggregatedSchema
|
|
107
|
+
// Type inferred from field definitions
|
|
108
|
+
export type BuildHistoryAggregatedResult = InferAggregatedResult<
|
|
109
|
+
typeof buildHistoryAggregatedFields
|
|
118
110
|
>;
|
|
119
111
|
|
|
120
112
|
// ============================================================================
|
|
@@ -140,9 +132,9 @@ export class BuildHistoryCollector implements CollectorStrategy<
|
|
|
140
132
|
|
|
141
133
|
config = new Versioned({ version: 1, schema: buildHistoryConfigSchema });
|
|
142
134
|
result = new Versioned({ version: 1, schema: buildHistoryResultSchema });
|
|
143
|
-
aggregatedResult = new
|
|
135
|
+
aggregatedResult = new VersionedAggregated({
|
|
144
136
|
version: 1,
|
|
145
|
-
|
|
137
|
+
fields: buildHistoryAggregatedFields,
|
|
146
138
|
});
|
|
147
139
|
|
|
148
140
|
async execute({
|
|
@@ -270,21 +262,15 @@ export class BuildHistoryCollector implements CollectorStrategy<
|
|
|
270
262
|
): BuildHistoryAggregatedResult {
|
|
271
263
|
const metadata = run.metadata;
|
|
272
264
|
|
|
273
|
-
const successRateState = mergeAverage(
|
|
274
|
-
existing?._successRate as AverageState | undefined,
|
|
275
|
-
metadata?.successRate,
|
|
276
|
-
);
|
|
277
|
-
|
|
278
|
-
const durationState = mergeAverage(
|
|
279
|
-
existing?._duration as AverageState | undefined,
|
|
280
|
-
metadata?.avgDurationMs,
|
|
281
|
-
);
|
|
282
|
-
|
|
283
265
|
return {
|
|
284
|
-
avgSuccessRate:
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
266
|
+
avgSuccessRate: mergeAverage(
|
|
267
|
+
existing?.avgSuccessRate,
|
|
268
|
+
metadata?.successRate,
|
|
269
|
+
),
|
|
270
|
+
avgBuildDuration: mergeAverage(
|
|
271
|
+
existing?.avgBuildDuration,
|
|
272
|
+
metadata?.avgDurationMs,
|
|
273
|
+
),
|
|
288
274
|
};
|
|
289
275
|
}
|
|
290
276
|
}
|
|
@@ -141,8 +141,8 @@ describe("JobStatusCollector", () => {
|
|
|
141
141
|
aggregated = collector.mergeResult(aggregated, runs[1]);
|
|
142
142
|
aggregated = collector.mergeResult(aggregated, runs[2]);
|
|
143
143
|
|
|
144
|
-
expect(aggregated.successRate).toBe(67); // 2/3
|
|
145
|
-
expect(aggregated.avgBuildDurationMs).toBe(60000); // (60000+80000+40000)/3
|
|
146
|
-
expect(aggregated.buildableRate).toBe(100);
|
|
144
|
+
expect(aggregated.successRate.rate).toBe(67); // 2/3
|
|
145
|
+
expect(aggregated.avgBuildDurationMs.avg).toBe(60000); // (60000+80000+40000)/3
|
|
146
|
+
expect(aggregated.buildableRate.rate).toBe(100);
|
|
147
147
|
});
|
|
148
148
|
});
|
|
@@ -6,10 +6,10 @@ import {
|
|
|
6
6
|
type CollectorStrategy,
|
|
7
7
|
mergeAverage,
|
|
8
8
|
mergeRate,
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
type
|
|
9
|
+
VersionedAggregated,
|
|
10
|
+
aggregatedAverage,
|
|
11
|
+
aggregatedRate,
|
|
12
|
+
type InferAggregatedResult,
|
|
13
13
|
} from "@checkstack/backend-api";
|
|
14
14
|
import {
|
|
15
15
|
healthResultNumber,
|
|
@@ -83,36 +83,28 @@ const jobStatusResultSchema = z.object({
|
|
|
83
83
|
|
|
84
84
|
export type JobStatusResult = z.infer<typeof jobStatusResultSchema>;
|
|
85
85
|
|
|
86
|
-
|
|
87
|
-
|
|
86
|
+
// Aggregated result fields definition
|
|
87
|
+
const jobStatusAggregatedFields = {
|
|
88
|
+
avgBuildDurationMs: aggregatedAverage({
|
|
88
89
|
"x-chart-type": "line",
|
|
89
90
|
"x-chart-label": "Avg Build Duration",
|
|
90
91
|
"x-chart-unit": "ms",
|
|
91
92
|
}),
|
|
92
|
-
successRate:
|
|
93
|
+
successRate: aggregatedRate({
|
|
93
94
|
"x-chart-type": "gauge",
|
|
94
95
|
"x-chart-label": "Success Rate",
|
|
95
96
|
"x-chart-unit": "%",
|
|
96
97
|
}),
|
|
97
|
-
buildableRate:
|
|
98
|
+
buildableRate: aggregatedRate({
|
|
98
99
|
"x-chart-type": "gauge",
|
|
99
100
|
"x-chart-label": "Enabled Rate",
|
|
100
101
|
"x-chart-unit": "%",
|
|
101
102
|
}),
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
const jobStatusAggregatedInternalSchema = z.object({
|
|
105
|
-
_duration: averageStateSchema.optional(),
|
|
106
|
-
_success: rateStateSchema.optional(),
|
|
107
|
-
_buildable: rateStateSchema.optional(),
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
const jobStatusAggregatedSchema = jobStatusAggregatedDisplaySchema.merge(
|
|
111
|
-
jobStatusAggregatedInternalSchema,
|
|
112
|
-
);
|
|
103
|
+
};
|
|
113
104
|
|
|
114
|
-
|
|
115
|
-
|
|
105
|
+
// Type inferred from field definitions
|
|
106
|
+
export type JobStatusAggregatedResult = InferAggregatedResult<
|
|
107
|
+
typeof jobStatusAggregatedFields
|
|
116
108
|
>;
|
|
117
109
|
|
|
118
110
|
// ============================================================================
|
|
@@ -138,9 +130,9 @@ export class JobStatusCollector implements CollectorStrategy<
|
|
|
138
130
|
|
|
139
131
|
config = new Versioned({ version: 1, schema: jobStatusConfigSchema });
|
|
140
132
|
result = new Versioned({ version: 1, schema: jobStatusResultSchema });
|
|
141
|
-
aggregatedResult = new
|
|
133
|
+
aggregatedResult = new VersionedAggregated({
|
|
142
134
|
version: 1,
|
|
143
|
-
|
|
135
|
+
fields: jobStatusAggregatedFields,
|
|
144
136
|
});
|
|
145
137
|
|
|
146
138
|
async execute({
|
|
@@ -224,29 +216,16 @@ export class JobStatusCollector implements CollectorStrategy<
|
|
|
224
216
|
): JobStatusAggregatedResult {
|
|
225
217
|
const metadata = run.metadata;
|
|
226
218
|
|
|
227
|
-
const durationState = mergeAverage(
|
|
228
|
-
existing?._duration as AverageState | undefined,
|
|
229
|
-
metadata?.lastBuildDurationMs,
|
|
230
|
-
);
|
|
231
|
-
|
|
232
|
-
// Success is when lastBuildResult === "SUCCESS"
|
|
233
|
-
const successState = mergeRate(
|
|
234
|
-
existing?._success as RateState | undefined,
|
|
235
|
-
metadata?.lastBuildResult === "SUCCESS",
|
|
236
|
-
);
|
|
237
|
-
|
|
238
|
-
const buildableState = mergeRate(
|
|
239
|
-
existing?._buildable as RateState | undefined,
|
|
240
|
-
metadata?.buildable,
|
|
241
|
-
);
|
|
242
|
-
|
|
243
219
|
return {
|
|
244
|
-
avgBuildDurationMs:
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
220
|
+
avgBuildDurationMs: mergeAverage(
|
|
221
|
+
existing?.avgBuildDurationMs,
|
|
222
|
+
metadata?.lastBuildDurationMs,
|
|
223
|
+
),
|
|
224
|
+
successRate: mergeRate(
|
|
225
|
+
existing?.successRate,
|
|
226
|
+
metadata?.lastBuildResult === "SUCCESS",
|
|
227
|
+
),
|
|
228
|
+
buildableRate: mergeRate(existing?.buildableRate, metadata?.buildable),
|
|
250
229
|
};
|
|
251
230
|
}
|
|
252
231
|
}
|
|
@@ -143,8 +143,8 @@ describe("NodeHealthCollector", () => {
|
|
|
143
143
|
let aggregated = collector.mergeResult(undefined, runs[0]);
|
|
144
144
|
aggregated = collector.mergeResult(aggregated, runs[1]);
|
|
145
145
|
|
|
146
|
-
expect(aggregated.avgOnlineNodes).toBe(4);
|
|
147
|
-
expect(aggregated.avgUtilization).toBe(60);
|
|
148
|
-
expect(aggregated.minOnlineNodes).toBe(3);
|
|
146
|
+
expect(aggregated.avgOnlineNodes.avg).toBe(4);
|
|
147
|
+
expect(aggregated.avgUtilization.avg).toBe(60);
|
|
148
|
+
expect(aggregated.minOnlineNodes.min).toBe(3);
|
|
149
149
|
});
|
|
150
150
|
});
|
|
@@ -6,10 +6,10 @@ import {
|
|
|
6
6
|
type CollectorStrategy,
|
|
7
7
|
mergeAverage,
|
|
8
8
|
mergeMinMax,
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
type
|
|
9
|
+
VersionedAggregated,
|
|
10
|
+
aggregatedAverage,
|
|
11
|
+
aggregatedMinMax,
|
|
12
|
+
type InferAggregatedResult,
|
|
13
13
|
} from "@checkstack/backend-api";
|
|
14
14
|
import {
|
|
15
15
|
healthResultBoolean,
|
|
@@ -83,34 +83,26 @@ const nodeHealthResultSchema = z.object({
|
|
|
83
83
|
|
|
84
84
|
export type NodeHealthResult = z.infer<typeof nodeHealthResultSchema>;
|
|
85
85
|
|
|
86
|
-
|
|
87
|
-
|
|
86
|
+
// Aggregated result fields definition
|
|
87
|
+
const nodeHealthAggregatedFields = {
|
|
88
|
+
avgOnlineNodes: aggregatedAverage({
|
|
88
89
|
"x-chart-type": "line",
|
|
89
90
|
"x-chart-label": "Avg Online Nodes",
|
|
90
91
|
}),
|
|
91
|
-
avgUtilization:
|
|
92
|
+
avgUtilization: aggregatedAverage({
|
|
92
93
|
"x-chart-type": "gauge",
|
|
93
94
|
"x-chart-label": "Avg Utilization",
|
|
94
95
|
"x-chart-unit": "%",
|
|
95
96
|
}),
|
|
96
|
-
minOnlineNodes:
|
|
97
|
+
minOnlineNodes: aggregatedMinMax({
|
|
97
98
|
"x-chart-type": "line",
|
|
98
99
|
"x-chart-label": "Min Online Nodes",
|
|
99
100
|
}),
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
const nodeHealthAggregatedInternalSchema = z.object({
|
|
103
|
-
_onlineNodes: averageStateSchema.optional(),
|
|
104
|
-
_utilization: averageStateSchema.optional(),
|
|
105
|
-
_minOnlineNodes: minMaxStateSchema.optional(),
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
const nodeHealthAggregatedSchema = nodeHealthAggregatedDisplaySchema.merge(
|
|
109
|
-
nodeHealthAggregatedInternalSchema,
|
|
110
|
-
);
|
|
101
|
+
};
|
|
111
102
|
|
|
112
|
-
|
|
113
|
-
|
|
103
|
+
// Type inferred from field definitions
|
|
104
|
+
export type NodeHealthAggregatedResult = InferAggregatedResult<
|
|
105
|
+
typeof nodeHealthAggregatedFields
|
|
114
106
|
>;
|
|
115
107
|
|
|
116
108
|
// ============================================================================
|
|
@@ -136,9 +128,9 @@ export class NodeHealthCollector implements CollectorStrategy<
|
|
|
136
128
|
|
|
137
129
|
config = new Versioned({ version: 1, schema: nodeHealthConfigSchema });
|
|
138
130
|
result = new Versioned({ version: 1, schema: nodeHealthResultSchema });
|
|
139
|
-
aggregatedResult = new
|
|
131
|
+
aggregatedResult = new VersionedAggregated({
|
|
140
132
|
version: 1,
|
|
141
|
-
|
|
133
|
+
fields: nodeHealthAggregatedFields,
|
|
142
134
|
});
|
|
143
135
|
|
|
144
136
|
async execute({
|
|
@@ -294,28 +286,19 @@ export class NodeHealthCollector implements CollectorStrategy<
|
|
|
294
286
|
): NodeHealthAggregatedResult {
|
|
295
287
|
const metadata = run.metadata;
|
|
296
288
|
|
|
297
|
-
const onlineNodesState = mergeAverage(
|
|
298
|
-
existing?._onlineNodes as AverageState | undefined,
|
|
299
|
-
metadata?.onlineNodes,
|
|
300
|
-
);
|
|
301
|
-
|
|
302
|
-
const utilizationState = mergeAverage(
|
|
303
|
-
existing?._utilization as AverageState | undefined,
|
|
304
|
-
metadata?.executorUtilization,
|
|
305
|
-
);
|
|
306
|
-
|
|
307
|
-
const minOnlineNodesState = mergeMinMax(
|
|
308
|
-
existing?._minOnlineNodes as MinMaxState | undefined,
|
|
309
|
-
metadata?.onlineNodes,
|
|
310
|
-
);
|
|
311
|
-
|
|
312
289
|
return {
|
|
313
|
-
avgOnlineNodes:
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
290
|
+
avgOnlineNodes: mergeAverage(
|
|
291
|
+
existing?.avgOnlineNodes,
|
|
292
|
+
metadata?.onlineNodes,
|
|
293
|
+
),
|
|
294
|
+
avgUtilization: mergeAverage(
|
|
295
|
+
existing?.avgUtilization,
|
|
296
|
+
metadata?.executorUtilization,
|
|
297
|
+
),
|
|
298
|
+
minOnlineNodes: mergeMinMax(
|
|
299
|
+
existing?.minOnlineNodes,
|
|
300
|
+
metadata?.onlineNodes,
|
|
301
|
+
),
|
|
319
302
|
};
|
|
320
303
|
}
|
|
321
304
|
}
|
|
@@ -107,8 +107,8 @@ describe("QueueInfoCollector", () => {
|
|
|
107
107
|
let aggregated = collector.mergeResult(undefined, runs[0]);
|
|
108
108
|
aggregated = collector.mergeResult(aggregated, runs[1]);
|
|
109
109
|
|
|
110
|
-
expect(aggregated.avgQueueLength).toBe(4);
|
|
111
|
-
expect(aggregated.maxQueueLength).toBe(5);
|
|
112
|
-
expect(aggregated.avgWaitTime).toBe(15000);
|
|
110
|
+
expect(aggregated.avgQueueLength.avg).toBe(4);
|
|
111
|
+
expect(aggregated.maxQueueLength.max).toBe(5);
|
|
112
|
+
expect(aggregated.avgWaitTime.avg).toBe(15000);
|
|
113
113
|
});
|
|
114
114
|
});
|
|
@@ -6,10 +6,10 @@ import {
|
|
|
6
6
|
type CollectorStrategy,
|
|
7
7
|
mergeAverage,
|
|
8
8
|
mergeMinMax,
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
type
|
|
9
|
+
VersionedAggregated,
|
|
10
|
+
aggregatedAverage,
|
|
11
|
+
aggregatedMinMax,
|
|
12
|
+
type InferAggregatedResult,
|
|
13
13
|
} from "@checkstack/backend-api";
|
|
14
14
|
import { healthResultNumber } from "@checkstack/healthcheck-common";
|
|
15
15
|
import { pluginMetadata } from "../plugin-metadata";
|
|
@@ -58,34 +58,26 @@ const queueInfoResultSchema = z.object({
|
|
|
58
58
|
|
|
59
59
|
export type QueueInfoResult = z.infer<typeof queueInfoResultSchema>;
|
|
60
60
|
|
|
61
|
-
|
|
62
|
-
|
|
61
|
+
// Aggregated result fields definition
|
|
62
|
+
const queueInfoAggregatedFields = {
|
|
63
|
+
avgQueueLength: aggregatedAverage({
|
|
63
64
|
"x-chart-type": "line",
|
|
64
65
|
"x-chart-label": "Avg Queue Length",
|
|
65
66
|
}),
|
|
66
|
-
maxQueueLength:
|
|
67
|
+
maxQueueLength: aggregatedMinMax({
|
|
67
68
|
"x-chart-type": "line",
|
|
68
69
|
"x-chart-label": "Max Queue Length",
|
|
69
70
|
}),
|
|
70
|
-
avgWaitTime:
|
|
71
|
+
avgWaitTime: aggregatedAverage({
|
|
71
72
|
"x-chart-type": "line",
|
|
72
73
|
"x-chart-label": "Avg Wait Time",
|
|
73
74
|
"x-chart-unit": "ms",
|
|
74
75
|
}),
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const queueInfoAggregatedInternalSchema = z.object({
|
|
78
|
-
_queueLength: averageStateSchema.optional(),
|
|
79
|
-
_maxQueueLength: minMaxStateSchema.optional(),
|
|
80
|
-
_waitTime: averageStateSchema.optional(),
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
const queueInfoAggregatedSchema = queueInfoAggregatedDisplaySchema.merge(
|
|
84
|
-
queueInfoAggregatedInternalSchema,
|
|
85
|
-
);
|
|
76
|
+
};
|
|
86
77
|
|
|
87
|
-
|
|
88
|
-
|
|
78
|
+
// Type inferred from field definitions
|
|
79
|
+
export type QueueInfoAggregatedResult = InferAggregatedResult<
|
|
80
|
+
typeof queueInfoAggregatedFields
|
|
89
81
|
>;
|
|
90
82
|
|
|
91
83
|
// ============================================================================
|
|
@@ -110,9 +102,9 @@ export class QueueInfoCollector implements CollectorStrategy<
|
|
|
110
102
|
|
|
111
103
|
config = new Versioned({ version: 1, schema: queueInfoConfigSchema });
|
|
112
104
|
result = new Versioned({ version: 1, schema: queueInfoResultSchema });
|
|
113
|
-
aggregatedResult = new
|
|
105
|
+
aggregatedResult = new VersionedAggregated({
|
|
114
106
|
version: 1,
|
|
115
|
-
|
|
107
|
+
fields: queueInfoAggregatedFields,
|
|
116
108
|
});
|
|
117
109
|
|
|
118
110
|
async execute({
|
|
@@ -206,28 +198,16 @@ export class QueueInfoCollector implements CollectorStrategy<
|
|
|
206
198
|
): QueueInfoAggregatedResult {
|
|
207
199
|
const metadata = run.metadata;
|
|
208
200
|
|
|
209
|
-
const queueLengthState = mergeAverage(
|
|
210
|
-
existing?._queueLength as AverageState | undefined,
|
|
211
|
-
metadata?.queueLength,
|
|
212
|
-
);
|
|
213
|
-
|
|
214
|
-
const maxQueueLengthState = mergeMinMax(
|
|
215
|
-
existing?._maxQueueLength as MinMaxState | undefined,
|
|
216
|
-
metadata?.queueLength,
|
|
217
|
-
);
|
|
218
|
-
|
|
219
|
-
const waitTimeState = mergeAverage(
|
|
220
|
-
existing?._waitTime as AverageState | undefined,
|
|
221
|
-
metadata?.avgWaitingMs,
|
|
222
|
-
);
|
|
223
|
-
|
|
224
201
|
return {
|
|
225
|
-
avgQueueLength:
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
202
|
+
avgQueueLength: mergeAverage(
|
|
203
|
+
existing?.avgQueueLength,
|
|
204
|
+
metadata?.queueLength,
|
|
205
|
+
),
|
|
206
|
+
maxQueueLength: mergeMinMax(
|
|
207
|
+
existing?.maxQueueLength,
|
|
208
|
+
metadata?.queueLength,
|
|
209
|
+
),
|
|
210
|
+
avgWaitTime: mergeAverage(existing?.avgWaitTime, metadata?.avgWaitingMs),
|
|
231
211
|
};
|
|
232
212
|
}
|
|
233
213
|
}
|
|
@@ -9,7 +9,7 @@ describe("ServerInfoCollector", () => {
|
|
|
9
9
|
const collector = new ServerInfoCollector();
|
|
10
10
|
|
|
11
11
|
const createMockClient = (
|
|
12
|
-
response: JenkinsResponse
|
|
12
|
+
response: JenkinsResponse,
|
|
13
13
|
): JenkinsTransportClient => ({
|
|
14
14
|
exec: async () => response,
|
|
15
15
|
});
|
|
@@ -83,9 +83,9 @@ describe("ServerInfoCollector", () => {
|
|
|
83
83
|
];
|
|
84
84
|
|
|
85
85
|
let aggregated = collector.mergeResult(undefined, runs[0]);
|
|
86
|
-
|
|
86
|
+
aggregated = collector.mergeResult(aggregated, runs[1]);
|
|
87
87
|
|
|
88
|
-
expect(aggregated.avgExecutors).toBe(5);
|
|
89
|
-
expect(aggregated.avgTotalJobs).toBe(11);
|
|
88
|
+
expect(aggregated.avgExecutors.avg).toBe(5);
|
|
89
|
+
expect(aggregated.avgTotalJobs.avg).toBe(11);
|
|
90
90
|
});
|
|
91
91
|
});
|
|
@@ -5,8 +5,9 @@ import {
|
|
|
5
5
|
type CollectorResult,
|
|
6
6
|
type CollectorStrategy,
|
|
7
7
|
mergeAverage,
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
VersionedAggregated,
|
|
9
|
+
aggregatedAverage,
|
|
10
|
+
type InferAggregatedResult,
|
|
10
11
|
} from "@checkstack/backend-api";
|
|
11
12
|
import {
|
|
12
13
|
healthResultNumber,
|
|
@@ -57,28 +58,21 @@ const serverInfoResultSchema = z.object({
|
|
|
57
58
|
|
|
58
59
|
export type ServerInfoResult = z.infer<typeof serverInfoResultSchema>;
|
|
59
60
|
|
|
60
|
-
|
|
61
|
-
|
|
61
|
+
// Aggregated result fields definition
|
|
62
|
+
const serverInfoAggregatedFields = {
|
|
63
|
+
avgExecutors: aggregatedAverage({
|
|
62
64
|
"x-chart-type": "line",
|
|
63
65
|
"x-chart-label": "Avg Executors",
|
|
64
66
|
}),
|
|
65
|
-
avgTotalJobs:
|
|
67
|
+
avgTotalJobs: aggregatedAverage({
|
|
66
68
|
"x-chart-type": "line",
|
|
67
69
|
"x-chart-label": "Avg Jobs",
|
|
68
70
|
}),
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const serverInfoAggregatedInternalSchema = z.object({
|
|
72
|
-
_executors: averageStateSchema.optional(),
|
|
73
|
-
_jobs: averageStateSchema.optional(),
|
|
74
|
-
});
|
|
71
|
+
};
|
|
75
72
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
export type ServerInfoAggregatedResult = z.infer<
|
|
81
|
-
typeof serverInfoAggregatedSchema
|
|
73
|
+
// Type inferred from field definitions
|
|
74
|
+
export type ServerInfoAggregatedResult = InferAggregatedResult<
|
|
75
|
+
typeof serverInfoAggregatedFields
|
|
82
76
|
>;
|
|
83
77
|
|
|
84
78
|
// ============================================================================
|
|
@@ -103,9 +97,9 @@ export class ServerInfoCollector implements CollectorStrategy<
|
|
|
103
97
|
|
|
104
98
|
config = new Versioned({ version: 1, schema: serverInfoConfigSchema });
|
|
105
99
|
result = new Versioned({ version: 1, schema: serverInfoResultSchema });
|
|
106
|
-
aggregatedResult = new
|
|
100
|
+
aggregatedResult = new VersionedAggregated({
|
|
107
101
|
version: 1,
|
|
108
|
-
|
|
102
|
+
fields: serverInfoAggregatedFields,
|
|
109
103
|
});
|
|
110
104
|
|
|
111
105
|
async execute({
|
|
@@ -159,21 +153,12 @@ export class ServerInfoCollector implements CollectorStrategy<
|
|
|
159
153
|
): ServerInfoAggregatedResult {
|
|
160
154
|
const metadata = run.metadata;
|
|
161
155
|
|
|
162
|
-
const executorsState = mergeAverage(
|
|
163
|
-
existing?._executors as AverageState | undefined,
|
|
164
|
-
metadata?.numExecutors,
|
|
165
|
-
);
|
|
166
|
-
|
|
167
|
-
const jobsState = mergeAverage(
|
|
168
|
-
existing?._jobs as AverageState | undefined,
|
|
169
|
-
metadata?.totalJobs,
|
|
170
|
-
);
|
|
171
|
-
|
|
172
156
|
return {
|
|
173
|
-
avgExecutors:
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
157
|
+
avgExecutors: mergeAverage(
|
|
158
|
+
existing?.avgExecutors,
|
|
159
|
+
metadata?.numExecutors,
|
|
160
|
+
),
|
|
161
|
+
avgTotalJobs: mergeAverage(existing?.avgTotalJobs, metadata?.totalJobs),
|
|
177
162
|
};
|
|
178
163
|
}
|
|
179
164
|
}
|
package/src/strategy.test.ts
CHANGED
|
@@ -184,9 +184,9 @@ describe("JenkinsHealthCheckStrategy", () => {
|
|
|
184
184
|
aggregated = strategy.mergeResult(aggregated, runs[1]);
|
|
185
185
|
aggregated = strategy.mergeResult(aggregated, runs[2]);
|
|
186
186
|
|
|
187
|
-
expect(aggregated.successRate).toBe(67); // 2/3
|
|
188
|
-
expect(aggregated.avgResponseTimeMs).toBe(175); // (150+200)/2
|
|
189
|
-
expect(aggregated.errorCount).toBe(1);
|
|
187
|
+
expect(aggregated.successRate.rate).toBe(67); // 2/3
|
|
188
|
+
expect(aggregated.avgResponseTimeMs.avg).toBe(175); // (150+200)/2
|
|
189
|
+
expect(aggregated.errorCount.count).toBe(1);
|
|
190
190
|
});
|
|
191
191
|
|
|
192
192
|
it("should handle single run", () => {
|
|
@@ -197,9 +197,9 @@ describe("JenkinsHealthCheckStrategy", () => {
|
|
|
197
197
|
};
|
|
198
198
|
const aggregated = strategy.mergeResult(undefined, run);
|
|
199
199
|
|
|
200
|
-
expect(aggregated.successRate).toBe(100);
|
|
201
|
-
expect(aggregated.avgResponseTimeMs).toBe(150);
|
|
202
|
-
expect(aggregated.errorCount).toBe(0);
|
|
200
|
+
expect(aggregated.successRate.rate).toBe(100);
|
|
201
|
+
expect(aggregated.avgResponseTimeMs.avg).toBe(150);
|
|
202
|
+
expect(aggregated.errorCount.count).toBe(0);
|
|
203
203
|
});
|
|
204
204
|
});
|
|
205
205
|
});
|
package/src/strategy.ts
CHANGED
|
@@ -2,19 +2,18 @@ import {
|
|
|
2
2
|
HealthCheckStrategy,
|
|
3
3
|
HealthCheckRunForAggregation,
|
|
4
4
|
Versioned,
|
|
5
|
+
VersionedAggregated,
|
|
6
|
+
aggregatedAverage,
|
|
7
|
+
aggregatedRate,
|
|
8
|
+
aggregatedCounter,
|
|
9
|
+
mergeAverage,
|
|
10
|
+
mergeRate,
|
|
11
|
+
mergeCounter,
|
|
5
12
|
z,
|
|
6
13
|
configString,
|
|
7
14
|
configNumber,
|
|
8
15
|
type ConnectedClient,
|
|
9
|
-
|
|
10
|
-
averageStateSchema,
|
|
11
|
-
mergeRate,
|
|
12
|
-
rateStateSchema,
|
|
13
|
-
mergeCounter,
|
|
14
|
-
counterStateSchema,
|
|
15
|
-
type AverageState,
|
|
16
|
-
type RateState,
|
|
17
|
-
type CounterState,
|
|
16
|
+
type InferAggregatedResult,
|
|
18
17
|
} from "@checkstack/backend-api";
|
|
19
18
|
import {
|
|
20
19
|
healthResultNumber,
|
|
@@ -75,37 +74,27 @@ const jenkinsResultSchema = healthResultSchema({
|
|
|
75
74
|
|
|
76
75
|
type JenkinsResult = z.infer<typeof jenkinsResultSchema>;
|
|
77
76
|
|
|
78
|
-
/** Aggregated
|
|
79
|
-
const
|
|
80
|
-
successRate:
|
|
77
|
+
/** Aggregated field definitions for bucket merging */
|
|
78
|
+
const jenkinsAggregatedFields = {
|
|
79
|
+
successRate: aggregatedRate({
|
|
81
80
|
"x-chart-type": "gauge",
|
|
82
81
|
"x-chart-label": "Success Rate",
|
|
83
82
|
"x-chart-unit": "%",
|
|
84
83
|
}),
|
|
85
|
-
avgResponseTimeMs:
|
|
84
|
+
avgResponseTimeMs: aggregatedAverage({
|
|
86
85
|
"x-chart-type": "line",
|
|
87
86
|
"x-chart-label": "Avg Response Time",
|
|
88
87
|
"x-chart-unit": "ms",
|
|
89
88
|
}),
|
|
90
|
-
errorCount:
|
|
89
|
+
errorCount: aggregatedCounter({
|
|
91
90
|
"x-chart-type": "counter",
|
|
92
91
|
"x-chart-label": "Errors",
|
|
93
92
|
}),
|
|
94
|
-
}
|
|
93
|
+
};
|
|
95
94
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
_success: rateStateSchema
|
|
100
|
-
.optional(),
|
|
101
|
-
_errors: counterStateSchema.optional(),
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
const jenkinsAggregatedSchema = jenkinsAggregatedDisplaySchema.merge(
|
|
105
|
-
jenkinsAggregatedInternalSchema,
|
|
106
|
-
);
|
|
107
|
-
|
|
108
|
-
type JenkinsAggregatedResult = z.infer<typeof jenkinsAggregatedSchema>;
|
|
95
|
+
type JenkinsAggregatedResult = InferAggregatedResult<
|
|
96
|
+
typeof jenkinsAggregatedFields
|
|
97
|
+
>;
|
|
109
98
|
|
|
110
99
|
// ============================================================================
|
|
111
100
|
// STRATEGY
|
|
@@ -115,7 +104,7 @@ export class JenkinsHealthCheckStrategy implements HealthCheckStrategy<
|
|
|
115
104
|
JenkinsConfig,
|
|
116
105
|
JenkinsTransportClient,
|
|
117
106
|
JenkinsResult,
|
|
118
|
-
|
|
107
|
+
typeof jenkinsAggregatedFields
|
|
119
108
|
> {
|
|
120
109
|
id = "jenkins";
|
|
121
110
|
displayName = "Jenkins Health Check";
|
|
@@ -131,9 +120,9 @@ export class JenkinsHealthCheckStrategy implements HealthCheckStrategy<
|
|
|
131
120
|
schema: jenkinsResultSchema,
|
|
132
121
|
});
|
|
133
122
|
|
|
134
|
-
aggregatedResult
|
|
123
|
+
aggregatedResult = new VersionedAggregated({
|
|
135
124
|
version: 1,
|
|
136
|
-
|
|
125
|
+
fields: jenkinsAggregatedFields,
|
|
137
126
|
});
|
|
138
127
|
|
|
139
128
|
/**
|
|
@@ -224,28 +213,17 @@ export class JenkinsHealthCheckStrategy implements HealthCheckStrategy<
|
|
|
224
213
|
): JenkinsAggregatedResult {
|
|
225
214
|
const metadata = run.metadata;
|
|
226
215
|
|
|
227
|
-
const
|
|
228
|
-
existing?.
|
|
216
|
+
const avgResponseTimeMs = mergeAverage(
|
|
217
|
+
existing?.avgResponseTimeMs,
|
|
229
218
|
metadata?.responseTimeMs,
|
|
230
219
|
);
|
|
231
220
|
|
|
232
|
-
const
|
|
233
|
-
|
|
234
|
-
metadata?.connected,
|
|
235
|
-
);
|
|
221
|
+
const isSuccess = metadata?.connected ?? false;
|
|
222
|
+
const successRate = mergeRate(existing?.successRate, isSuccess);
|
|
236
223
|
|
|
237
|
-
const
|
|
238
|
-
|
|
239
|
-
metadata?.error !== undefined,
|
|
240
|
-
);
|
|
224
|
+
const hasError = metadata?.error !== undefined;
|
|
225
|
+
const errorCount = mergeCounter(existing?.errorCount, hasError);
|
|
241
226
|
|
|
242
|
-
return {
|
|
243
|
-
successRate: successState.rate,
|
|
244
|
-
avgResponseTimeMs: responseTimeState.avg,
|
|
245
|
-
errorCount: errorState.count,
|
|
246
|
-
_responseTime: responseTimeState,
|
|
247
|
-
_success: successState,
|
|
248
|
-
_errors: errorState,
|
|
249
|
-
};
|
|
227
|
+
return { successRate, avgResponseTimeMs, errorCount };
|
|
250
228
|
}
|
|
251
229
|
}
|