@pipeline-builder/pipeline-data 0.0.1
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/LICENSE +202 -0
- package/README.md +61 -0
- package/drizzle.config.ts +17 -0
- package/lib/api/access-control-builder.d.ts +148 -0
- package/lib/api/access-control-builder.js +199 -0
- package/lib/api/crud-service.d.ts +343 -0
- package/lib/api/crud-service.js +849 -0
- package/lib/api/query-builders.d.ts +82 -0
- package/lib/api/query-builders.js +460 -0
- package/lib/api/reporting-service.d.ts +685 -0
- package/lib/api/reporting-service.js +1564 -0
- package/lib/api/soft-delete-sweep.d.ts +65 -0
- package/lib/api/soft-delete-sweep.js +124 -0
- package/lib/api/viewer-context.d.ts +29 -0
- package/lib/api/viewer-context.js +63 -0
- package/lib/core/query-filters.d.ts +263 -0
- package/lib/core/query-filters.js +4 -0
- package/lib/database/drizzle-schema.d.ts +7357 -0
- package/lib/database/drizzle-schema.js +66 -0
- package/lib/database/index.d.ts +5 -0
- package/lib/database/index.js +8 -0
- package/lib/database/migrator.d.ts +20 -0
- package/lib/database/migrator.js +80 -0
- package/lib/database/postgres-connection.d.ts +201 -0
- package/lib/database/postgres-connection.js +413 -0
- package/lib/database/retry-strategy.d.ts +68 -0
- package/lib/database/retry-strategy.js +122 -0
- package/lib/database/schema/alert.d.ts +588 -0
- package/lib/database/schema/alert.js +130 -0
- package/lib/database/schema/compliance.d.ts +3319 -0
- package/lib/database/schema/compliance.js +377 -0
- package/lib/database/schema/dashboard.d.ts +474 -0
- package/lib/database/schema/dashboard.js +129 -0
- package/lib/database/schema/message-attachment.d.ts +183 -0
- package/lib/database/schema/message-attachment.js +40 -0
- package/lib/database/schema/message.d.ts +411 -0
- package/lib/database/schema/message.js +114 -0
- package/lib/database/schema/pipeline-template.d.ts +466 -0
- package/lib/database/schema/pipeline-template.js +80 -0
- package/lib/database/schema/pipeline.d.ts +1613 -0
- package/lib/database/schema/pipeline.js +332 -0
- package/lib/database/schema/plugin.d.ts +749 -0
- package/lib/database/schema/plugin.js +166 -0
- package/lib/database/tenancy.d.ts +103 -0
- package/lib/database/tenancy.js +167 -0
- package/lib/index.d.ts +32 -0
- package/lib/index.js +38 -0
- package/package.json +161 -0
|
@@ -0,0 +1,685 @@
|
|
|
1
|
+
interface ExecutionCount {
|
|
2
|
+
id: string;
|
|
3
|
+
project: string;
|
|
4
|
+
organization: string;
|
|
5
|
+
pipelineName: string | null;
|
|
6
|
+
total: number;
|
|
7
|
+
succeeded: number;
|
|
8
|
+
failed: number;
|
|
9
|
+
canceled: number;
|
|
10
|
+
firstExecution: string | null;
|
|
11
|
+
lastExecution: string | null;
|
|
12
|
+
}
|
|
13
|
+
interface TimeSeriesEntry {
|
|
14
|
+
period: string;
|
|
15
|
+
succeeded: number;
|
|
16
|
+
failed: number;
|
|
17
|
+
canceled: number;
|
|
18
|
+
successPct: number;
|
|
19
|
+
}
|
|
20
|
+
interface DurationStats {
|
|
21
|
+
id: string;
|
|
22
|
+
project: string;
|
|
23
|
+
pipelineName: string | null;
|
|
24
|
+
avgMs: number;
|
|
25
|
+
minMs: number;
|
|
26
|
+
maxMs: number;
|
|
27
|
+
p95Ms: number;
|
|
28
|
+
executions: number;
|
|
29
|
+
}
|
|
30
|
+
interface PipelineExecution {
|
|
31
|
+
executionId: string;
|
|
32
|
+
/** Rolled-up terminal status: succeeded | failed | canceled | in-progress. */
|
|
33
|
+
status: string;
|
|
34
|
+
startedAt: string | null;
|
|
35
|
+
endedAt: string | null;
|
|
36
|
+
durationMs: number | null;
|
|
37
|
+
/** First failing stage/action for the execution (null when it didn't fail). */
|
|
38
|
+
failingStage: string | null;
|
|
39
|
+
failingAction: string | null;
|
|
40
|
+
}
|
|
41
|
+
interface StageFailure {
|
|
42
|
+
stageName: string;
|
|
43
|
+
failures: number;
|
|
44
|
+
total: number;
|
|
45
|
+
failurePct: number;
|
|
46
|
+
}
|
|
47
|
+
interface StageBottleneck {
|
|
48
|
+
id: string;
|
|
49
|
+
pipelineName: string | null;
|
|
50
|
+
stageName: string;
|
|
51
|
+
avgMs: number;
|
|
52
|
+
maxMs: number;
|
|
53
|
+
}
|
|
54
|
+
interface ActionFailure {
|
|
55
|
+
actionName: string;
|
|
56
|
+
failures: number;
|
|
57
|
+
total: number;
|
|
58
|
+
failurePct: number;
|
|
59
|
+
}
|
|
60
|
+
interface ErrorEntry {
|
|
61
|
+
errorPattern: string;
|
|
62
|
+
occurrences: number;
|
|
63
|
+
affectedPipelines: number;
|
|
64
|
+
lastSeen: string;
|
|
65
|
+
}
|
|
66
|
+
interface PluginSummary {
|
|
67
|
+
total: number;
|
|
68
|
+
active: number;
|
|
69
|
+
inactive: number;
|
|
70
|
+
/** Counts per sharing rung — one bucket per `visibility` value. */
|
|
71
|
+
public: number;
|
|
72
|
+
org: number;
|
|
73
|
+
private: number;
|
|
74
|
+
uniqueNames: number;
|
|
75
|
+
}
|
|
76
|
+
interface TypeComputeDistribution {
|
|
77
|
+
pluginType: string;
|
|
78
|
+
computeType: string;
|
|
79
|
+
count: number;
|
|
80
|
+
}
|
|
81
|
+
interface VersionCount {
|
|
82
|
+
name: string;
|
|
83
|
+
versionCount: number;
|
|
84
|
+
latestVersion: string;
|
|
85
|
+
hasDefault: boolean;
|
|
86
|
+
}
|
|
87
|
+
interface BuildTimeSeriesEntry {
|
|
88
|
+
period: string;
|
|
89
|
+
succeeded: number;
|
|
90
|
+
failed: number;
|
|
91
|
+
successPct: number;
|
|
92
|
+
}
|
|
93
|
+
interface BuildDuration {
|
|
94
|
+
pluginName: string;
|
|
95
|
+
avgMs: number;
|
|
96
|
+
maxMs: number;
|
|
97
|
+
builds: number;
|
|
98
|
+
}
|
|
99
|
+
interface BuildFailure {
|
|
100
|
+
pluginName: string;
|
|
101
|
+
errorMessage: string;
|
|
102
|
+
occurrences: number;
|
|
103
|
+
lastSeen: string;
|
|
104
|
+
}
|
|
105
|
+
/** Effective standard-event retention (days): a valid per-org override wins,
|
|
106
|
+
* else the global env default. Shared by the sweep + the settings surface so
|
|
107
|
+
* they can't drift. The `-1` unlimited sentinel (Phase 8) passes straight
|
|
108
|
+
* through — the sweep reads it as "keep forever, skip this org's standard
|
|
109
|
+
* events". A positive override clamps to [1, RETENTION_MAX_DAYS]; a `null`, a
|
|
110
|
+
* non-integer, or any other out-of-range value (e.g. 0, -2) falls back to the
|
|
111
|
+
* env default. */
|
|
112
|
+
export declare function resolveEventRetentionDays(override?: number | null): number;
|
|
113
|
+
/** Effective DORA-source retention (days): a valid per-org override wins, else
|
|
114
|
+
* the global env default. The `-1` unlimited sentinel (Phase 8) passes straight
|
|
115
|
+
* through — the sweep reads it as "keep forever, skip this org's DORA-source
|
|
116
|
+
* windows". A positive override clamps to [1, RETENTION_MAX_DAYS]; a `null`, a
|
|
117
|
+
* non-integer, or any other out-of-range value (e.g. 0, -2) falls back to the
|
|
118
|
+
* env default. */
|
|
119
|
+
export declare function resolveDoraRetentionDays(override?: number | null): number;
|
|
120
|
+
/** The cutoff instant for a retention window: rows with `created_at` strictly
|
|
121
|
+
* before this (`< cutoff`) are expired. Deterministic for a fixed `now`. */
|
|
122
|
+
export declare function retentionCutoff(now: Date, days: number): Date;
|
|
123
|
+
/** Per-org retention override row (Phase 7). Both nullable ⇒ global defaults. */
|
|
124
|
+
export interface ReportingRetentionSettings {
|
|
125
|
+
eventRetentionDays: number | null;
|
|
126
|
+
doraRetentionDays: number | null;
|
|
127
|
+
defaultEventRetentionDays: number;
|
|
128
|
+
defaultDoraRetentionDays: number;
|
|
129
|
+
}
|
|
130
|
+
/** Options for the reporting retention sweep. */
|
|
131
|
+
export interface ReportingRetentionOptions {
|
|
132
|
+
/** Rows deleted per statement (default 1000). */
|
|
133
|
+
batchSize?: number;
|
|
134
|
+
/** Max batches per table per org per tick (default 50); the rest defers to
|
|
135
|
+
* the next tick so a huge backlog can't hold locks too long. */
|
|
136
|
+
maxBatchesPerTable?: number;
|
|
137
|
+
/** Fixed "now" for the whole sweep (default `new Date()`). */
|
|
138
|
+
now?: Date;
|
|
139
|
+
}
|
|
140
|
+
/** Per-run purge tallies (also logged). */
|
|
141
|
+
export interface ReportingRetentionCounts {
|
|
142
|
+
/** Orgs swept this run. */
|
|
143
|
+
orgs: number;
|
|
144
|
+
/** Standard `pipeline_events` (environment IS NULL) rows purged. */
|
|
145
|
+
standardEvents: number;
|
|
146
|
+
/** DORA-source `pipeline_events` (environment IS NOT NULL) rows purged. */
|
|
147
|
+
doraEvents: number;
|
|
148
|
+
/** `deployment_outcomes` rows purged. */
|
|
149
|
+
deploymentOutcomes: number;
|
|
150
|
+
/** `incidents` rows purged. */
|
|
151
|
+
incidents: number;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* DORA performance band. `null` = insufficient data to classify (no
|
|
155
|
+
* deployments/failures/lead-time sample in the window), so the UI shows a
|
|
156
|
+
* neutral state rather than mislabeling an empty window as "low".
|
|
157
|
+
*/
|
|
158
|
+
export type DoraLevel = 'elite' | 'high' | 'medium' | 'low' | null;
|
|
159
|
+
/** Optional scoping for {@link ReportingService.getDoraMetrics}. */
|
|
160
|
+
export interface DoraOptions {
|
|
161
|
+
/** Restrict to a single pipeline (per-pipeline DORA). */
|
|
162
|
+
pipelineId?: string;
|
|
163
|
+
/** Restrict to a single deploy environment (else all deploy environments). */
|
|
164
|
+
environment?: string;
|
|
165
|
+
/**
|
|
166
|
+
* Per-org incident→deploy correlation window override (hours). When set (a
|
|
167
|
+
* positive finite number), the incident correlation uses it instead of the
|
|
168
|
+
* global `DORA_INCIDENT_WINDOW_HOURS`. The route resolves this from the org's
|
|
169
|
+
* `dora_settings` row (see {@link ReportingService.getIncidentSettings}) and
|
|
170
|
+
* passes it in; unset falls back to the env default.
|
|
171
|
+
*/
|
|
172
|
+
incidentWindowHours?: number;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Per-org reporting settings surfaced to the org-admin panel. `incidentWindowHours`
|
|
176
|
+
* (Phase 5b) is the incident→deploy correlation window; `eventRetentionDays` /
|
|
177
|
+
* `doraRetentionDays` (Phase 7) are the two retention-sweep windows. Each override
|
|
178
|
+
* is `null` when unset (the paired `default*` field shows the env fallback applied).
|
|
179
|
+
*/
|
|
180
|
+
export interface IncidentSettings {
|
|
181
|
+
/** The org's stored correlation-window override in hours, or `null` when unset. */
|
|
182
|
+
incidentWindowHours: number | null;
|
|
183
|
+
/** The global env default applied when no override is stored. */
|
|
184
|
+
defaultWindowHours: number;
|
|
185
|
+
/** Standard-event retention override in days, or `null` when unset (Phase 7). */
|
|
186
|
+
eventRetentionDays: number | null;
|
|
187
|
+
/** DORA-source retention override in days, or `null` when unset (Phase 7). */
|
|
188
|
+
doraRetentionDays: number | null;
|
|
189
|
+
/** Global standard-event retention default applied when unset (days). */
|
|
190
|
+
defaultEventRetentionDays: number;
|
|
191
|
+
/** Global DORA-source retention default applied when unset (days). */
|
|
192
|
+
defaultDoraRetentionDays: number;
|
|
193
|
+
}
|
|
194
|
+
/** A partial reporting-settings write (Phase 5b + 7). Only provided fields are
|
|
195
|
+
* upserted; omitted fields are left unchanged (an omitted retention field keeps
|
|
196
|
+
* its stored override / the global default). */
|
|
197
|
+
export interface ReportingSettingsPatch {
|
|
198
|
+
incidentWindowHours?: number;
|
|
199
|
+
eventRetentionDays?: number;
|
|
200
|
+
doraRetentionDays?: number;
|
|
201
|
+
}
|
|
202
|
+
/** One row of the org-admin incidents list (recent incidents + deploy correlation). */
|
|
203
|
+
export interface IncidentListItem {
|
|
204
|
+
incidentId: string;
|
|
205
|
+
environment: string;
|
|
206
|
+
severity: string;
|
|
207
|
+
openedAt: string | null;
|
|
208
|
+
resolvedAt: string | null;
|
|
209
|
+
createdAt: string | null;
|
|
210
|
+
/** True once the incident has a `resolved_at`. */
|
|
211
|
+
resolved: boolean;
|
|
212
|
+
/** The correlated deploy execution id (most recent successful deploy in-window), or null. */
|
|
213
|
+
correlatedExecutionId: string | null;
|
|
214
|
+
/** That deploy's completion instant, or null when uncorrelated. */
|
|
215
|
+
deployCompletedAt: string | null;
|
|
216
|
+
}
|
|
217
|
+
/** Result of the wiring-test dry-run correlation ({@link ReportingService.testIncidentCorrelation}). */
|
|
218
|
+
export interface IncidentTestResult {
|
|
219
|
+
environment: string;
|
|
220
|
+
/** The synthetic incident's openedAt (now, ISO). */
|
|
221
|
+
openedAt: string;
|
|
222
|
+
/** The effective correlation window used. */
|
|
223
|
+
windowHours: number;
|
|
224
|
+
/** Whether a recent successful deploy to `environment` correlated. */
|
|
225
|
+
correlated: boolean;
|
|
226
|
+
/** The correlated deploy execution id, or null. */
|
|
227
|
+
executionId: string | null;
|
|
228
|
+
/** That deploy's completion instant, or null. */
|
|
229
|
+
deployCompletedAt: string | null;
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* DORA metrics over a [from,to] window, org-scoped (single-org or rollup subtree).
|
|
233
|
+
*
|
|
234
|
+
* DEPLOY-BASIS — every metric derives from real DEPLOY-STAGE executions, i.e.
|
|
235
|
+
* `pipeline_events` rows with `event_type='STAGE'` and a non-null `environment`
|
|
236
|
+
* (the forwarder sets `environment` only for the stages a user declared in
|
|
237
|
+
* `pb.deploys`). A CI-only build/test pipeline with no deploy stage produces NO
|
|
238
|
+
* DORA data — there is no run-based fallback. The panel is empty until deployed
|
|
239
|
+
* pipelines re-synth with the new deploy tags and start emitting deploy events.
|
|
240
|
+
*
|
|
241
|
+
* `production` is the headline environment; MTTR is measured production-only.
|
|
242
|
+
*/
|
|
243
|
+
export interface DoraMetrics {
|
|
244
|
+
/** The [from,to] window echoed back (deploy `completed_at` range). */
|
|
245
|
+
window: {
|
|
246
|
+
from: string;
|
|
247
|
+
to: string;
|
|
248
|
+
};
|
|
249
|
+
/** The scoping applied (echoed for the UI); `null` when unscoped. */
|
|
250
|
+
filters: {
|
|
251
|
+
pipelineId: string | null;
|
|
252
|
+
environment: string | null;
|
|
253
|
+
};
|
|
254
|
+
/** The headline environment name (`production`). */
|
|
255
|
+
headline: string;
|
|
256
|
+
/** Per-environment DF / CFR / lead time. Sorted headline-first, then A→Z. */
|
|
257
|
+
environments: DoraEnvMetrics[];
|
|
258
|
+
/**
|
|
259
|
+
* Mean Time To Restore — PRODUCTION-ONLY, from `deployment_outcomes`.
|
|
260
|
+
* `median(restored.at − deployed.completed_at)` over restored production
|
|
261
|
+
* incidents (deltas clamped ≥0). `incidents` counts production deploys marked
|
|
262
|
+
* failed; `restored` counts those that recovered; `medianSeconds` is null when
|
|
263
|
+
* no restored incident has a resolvable deploy time.
|
|
264
|
+
*/
|
|
265
|
+
meanTimeToRestore: {
|
|
266
|
+
incidents: number;
|
|
267
|
+
restored: number;
|
|
268
|
+
medianSeconds: number | null;
|
|
269
|
+
level: DoraLevel;
|
|
270
|
+
};
|
|
271
|
+
/**
|
|
272
|
+
* Coverage reconciliation — registered pipelines with no observed deploy in
|
|
273
|
+
* the window. A high `withoutDeploys` count means DORA is blind to most of the
|
|
274
|
+
* fleet (pipelines that haven't re-synthed with deploy tags, or don't deploy).
|
|
275
|
+
*/
|
|
276
|
+
coverage: {
|
|
277
|
+
/** Pipelines in the registry (org-scoped). */
|
|
278
|
+
registered: number;
|
|
279
|
+
/** Registered pipelines with ≥1 deploy-stage execution in the window. */
|
|
280
|
+
deploying: number;
|
|
281
|
+
/** registered − deploying (clamped ≥0). */
|
|
282
|
+
withoutDeploys: number;
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
/** Per-environment DORA metrics (deployment frequency, CFR, lead time). */
|
|
286
|
+
export interface DoraEnvMetrics {
|
|
287
|
+
environment: string;
|
|
288
|
+
/** Deployment Frequency — successful deploy-stage executions for this env. */
|
|
289
|
+
deploymentFrequency: {
|
|
290
|
+
deployments: number;
|
|
291
|
+
perDay: number;
|
|
292
|
+
level: DoraLevel;
|
|
293
|
+
};
|
|
294
|
+
/**
|
|
295
|
+
* Change Failure Rate — two-class:
|
|
296
|
+
* `(deployTimeFailures + postDeployFailures) / attempts`.
|
|
297
|
+
* - `deployTimeFailures` — deploy stage `result=failed` (from events).
|
|
298
|
+
* - `postDeployFailures` — a successful deploy later marked failed in prod
|
|
299
|
+
* (from `deployment_outcomes`).
|
|
300
|
+
* - `attempts` — all terminal deploy-stage attempts (succeeded + failed).
|
|
301
|
+
*/
|
|
302
|
+
changeFailureRate: {
|
|
303
|
+
rate: number;
|
|
304
|
+
deployTimeFailures: number;
|
|
305
|
+
postDeployFailures: number;
|
|
306
|
+
attempts: number;
|
|
307
|
+
level: DoraLevel;
|
|
308
|
+
};
|
|
309
|
+
/**
|
|
310
|
+
* Lead Time — MEASURED: `median(deploy_completed − oldest_commit_time)` over
|
|
311
|
+
* successful deploys with a resolvable commit timestamp (deltas clamped ≥0).
|
|
312
|
+
* `medianSeconds` is `null` (= unknown) when no successful deploy in this env
|
|
313
|
+
* carried a `commit_timestamp`. The old median-run-duration proxy is removed.
|
|
314
|
+
*/
|
|
315
|
+
leadTime: {
|
|
316
|
+
/** Successful deploys with a resolvable commit time (the median sample). */
|
|
317
|
+
deployments: number;
|
|
318
|
+
medianSeconds: number | null;
|
|
319
|
+
level: DoraLevel;
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
/** One interval bucket of the DORA trend (deploy frequency + change failure). */
|
|
323
|
+
export interface DoraTrendPoint {
|
|
324
|
+
/** Bucket start (DATE_TRUNC of the deploy `completed_at`), ISO text. */
|
|
325
|
+
period: string;
|
|
326
|
+
/** Successful deploy-stage executions in the bucket. */
|
|
327
|
+
deployments: number;
|
|
328
|
+
/** Failed deploy-stage executions in the bucket. */
|
|
329
|
+
failed: number;
|
|
330
|
+
/** succeeded + failed in the bucket (deploy attempts). */
|
|
331
|
+
total: number;
|
|
332
|
+
/** failed/total as a percent (0 when total is 0). Deploy-time CFR only. */
|
|
333
|
+
changeFailurePct: number;
|
|
334
|
+
}
|
|
335
|
+
/** Per-stage build-health metrics (Phase 6) for one pipeline over a window. */
|
|
336
|
+
export interface BuildHealthStage {
|
|
337
|
+
stage: string;
|
|
338
|
+
/** Terminal stage runs (succeeded + failed) in the window. */
|
|
339
|
+
runs: number;
|
|
340
|
+
successes: number;
|
|
341
|
+
failures: number;
|
|
342
|
+
/** successes / runs as a percent (0 when runs is 0). */
|
|
343
|
+
successRate: number;
|
|
344
|
+
/** Duration percentiles over the stage's terminal runs; null when no durations. */
|
|
345
|
+
p50Ms: number | null;
|
|
346
|
+
p90Ms: number | null;
|
|
347
|
+
p99Ms: number | null;
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Per-pipeline build-health breakdown (Phase 6). Standard reporting (NOT
|
|
351
|
+
* `advanced_reporting`-gated) — per-stage success rate + timing percentiles, with
|
|
352
|
+
* totals summed across stages.
|
|
353
|
+
*/
|
|
354
|
+
export interface BuildHealth {
|
|
355
|
+
stages: BuildHealthStage[];
|
|
356
|
+
totals: {
|
|
357
|
+
runs: number;
|
|
358
|
+
failures: number;
|
|
359
|
+
failureRate: number;
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
/** Post-deploy incident marker (Phase 5) accepted by `ReportingService.recordIncident`. */
|
|
363
|
+
export interface IncidentInput {
|
|
364
|
+
incidentId: string;
|
|
365
|
+
environment: string;
|
|
366
|
+
openedAt: string;
|
|
367
|
+
resolvedAt?: string;
|
|
368
|
+
severity: string;
|
|
369
|
+
}
|
|
370
|
+
/** Event payload accepted by `ReportingService.ingestEvents`. Mirrors the route's Zod shape. */
|
|
371
|
+
export interface IngestEvent {
|
|
372
|
+
/** Stable pipeline id the events Lambda read from the `pb.pipeline-id`
|
|
373
|
+
* tag (= the platform pipelineId). The registry join key. */
|
|
374
|
+
pipelineId: string;
|
|
375
|
+
eventSource: 'codepipeline' | 'codebuild' | 'plugin-build';
|
|
376
|
+
eventType: 'PIPELINE' | 'STAGE' | 'ACTION' | 'BUILD';
|
|
377
|
+
status: string;
|
|
378
|
+
executionId?: string;
|
|
379
|
+
stageName?: string;
|
|
380
|
+
actionName?: string;
|
|
381
|
+
/** Human-readable failure reason (Action events). */
|
|
382
|
+
errorMessage?: string;
|
|
383
|
+
startedAt?: string;
|
|
384
|
+
completedAt?: string;
|
|
385
|
+
durationMs?: number;
|
|
386
|
+
/** Source commit id of the change being shipped (DORA deploy attribution). */
|
|
387
|
+
commitSha?: string;
|
|
388
|
+
/** Source ref/branch (DORA deploy attribution). */
|
|
389
|
+
commitRef?: string;
|
|
390
|
+
/** Deploy target (e.g. "production"). Its presence on a STAGE/ACTION event
|
|
391
|
+
* marks a real deployment (derived server-side — there is no `isDeploy`). */
|
|
392
|
+
environment?: string;
|
|
393
|
+
/** Oldest unshipped commit timestamp (ISO 8601) for measured lead time. */
|
|
394
|
+
commitTimestamp?: string;
|
|
395
|
+
/** Number of commits shipped in this change (≥1). */
|
|
396
|
+
commitCount?: number;
|
|
397
|
+
detail?: Record<string, unknown>;
|
|
398
|
+
}
|
|
399
|
+
/**
|
|
400
|
+
* Per-event metric descriptor emitted (via the {@link ReportingService.ingestEvents}
|
|
401
|
+
* `onMetric` hook) for each REGISTERED terminal deploy/stage event, so the route
|
|
402
|
+
* layer can fan them into Prometheus counters without pipeline-data importing
|
|
403
|
+
* api-server's metrics registry. `result` is the terminal outcome; `environment`
|
|
404
|
+
* is non-null only for a deploy-stage event.
|
|
405
|
+
*/
|
|
406
|
+
export interface IngestMetric {
|
|
407
|
+
pipelineId: string;
|
|
408
|
+
orgId: string;
|
|
409
|
+
stage: string;
|
|
410
|
+
environment: string | null;
|
|
411
|
+
result: 'succeeded' | 'failed';
|
|
412
|
+
}
|
|
413
|
+
/** Counts + the (possibly truncated) list of unregistered pipeline ids the caller can log. */
|
|
414
|
+
export interface IngestResult {
|
|
415
|
+
inserted: number;
|
|
416
|
+
skipped: number;
|
|
417
|
+
unregisteredPipelineIds: string[];
|
|
418
|
+
}
|
|
419
|
+
/**
|
|
420
|
+
* Read-only reporting service for pipeline execution and plugin inventory aggregations.
|
|
421
|
+
* Does not extend CrudService — reports are aggregate queries, not entity CRUD.
|
|
422
|
+
*
|
|
423
|
+
* All queries are cached in-memory to avoid repeated expensive SQL aggregations:
|
|
424
|
+
* - Inventory queries (plugin summary/distribution/versions): 5 min TTL
|
|
425
|
+
* - Timeseries queries (execution/build metrics with date ranges): 2 min TTL
|
|
426
|
+
*/
|
|
427
|
+
export declare class ReportingService {
|
|
428
|
+
#private;
|
|
429
|
+
/** Invalidate all cached reports for an org (call after event ingest). */
|
|
430
|
+
invalidateOrg(orgId: string): Promise<void>;
|
|
431
|
+
/**
|
|
432
|
+
* Resolve incoming events against the pipeline registry, batch-insert the
|
|
433
|
+
* matched ones, and invalidate reporting caches for affected orgs.
|
|
434
|
+
* Events for unregistered pipeline ids are dropped (and logged at WARN
|
|
435
|
+
* with sample ids so an operator can see when EventBridge is delivering
|
|
436
|
+
* events for pipelines that haven't called POST /pipelines/registry yet).
|
|
437
|
+
*
|
|
438
|
+
* Returns counts + a sample of unregistered pipeline ids for observability.
|
|
439
|
+
*/
|
|
440
|
+
ingestEvents(events: IngestEvent[], onMetric?: (m: IngestMetric) => void): Promise<IngestResult>;
|
|
441
|
+
/**
|
|
442
|
+
* Build the org-scope predicate for a report query. With `orgIds` (the
|
|
443
|
+
* org → team rollup — a parent's `[self, ...descendants]`) it becomes an
|
|
444
|
+
* `IN (...)` over the subtree; otherwise the single-org `= $org`. Returns a
|
|
445
|
+
* `multi` flag so callers can run multi-org reads under sysadmin context
|
|
446
|
+
* (the subtree spans orgs outside the request's RLS scope) and salt the
|
|
447
|
+
* cache key.
|
|
448
|
+
*/
|
|
449
|
+
private orgScope;
|
|
450
|
+
/**
|
|
451
|
+
* Run a report read. Single-org reads use the per-org cache (invalidated on
|
|
452
|
+
* that org's event ingest). Rollup (multi-org) reads **bypass the cache** and
|
|
453
|
+
* run under sysadmin context: they're admin-only and lower-frequency, and a
|
|
454
|
+
* child org's event ingest can't invalidate a parent's rollup entry (reporting
|
|
455
|
+
* has no org tree), so caching them would serve stale aggregates. Always fresh.
|
|
456
|
+
*/
|
|
457
|
+
private runReport;
|
|
458
|
+
/** 1.1 Execution count per pipeline with status breakdown. */
|
|
459
|
+
getExecutionCount(orgId: string, orgIds?: string[], range?: {
|
|
460
|
+
from?: string;
|
|
461
|
+
to?: string;
|
|
462
|
+
}): Promise<ExecutionCount[]>;
|
|
463
|
+
/**
|
|
464
|
+
* 1.1b Per-pipeline execution history — DISTINCT executions for one pipeline,
|
|
465
|
+
* newest first. Groups all events by `execution_id` in a single scan and
|
|
466
|
+
* rolls each execution up to one row:
|
|
467
|
+
* - status: derived from the PIPELINE-type events. FAILED wins, then
|
|
468
|
+
* SUCCEEDED, then CANCELED; an execution with no terminal PIPELINE event
|
|
469
|
+
* is still `in-progress`.
|
|
470
|
+
* - startedAt/endedAt/durationMs: from the PIPELINE lifecycle events.
|
|
471
|
+
* - failingStage/failingAction: the first FAILED STAGE/ACTION event (cheap —
|
|
472
|
+
* same scan, no extra query).
|
|
473
|
+
*
|
|
474
|
+
* ORG-SCOPING: identical to the sibling execution reports — joins the pipeline
|
|
475
|
+
* registry table (`pipeline`) and gates on `p.org_id ${pred}`, where `pred` is
|
|
476
|
+
* the single-org `= $org` or (with a rollup) an `IN (...)` over the org→team
|
|
477
|
+
* subtree. A pipelineId belonging to another org yields zero rows.
|
|
478
|
+
*/
|
|
479
|
+
listPipelineExecutions(orgId: string, pipelineId: string, orgIds?: string[], range?: {
|
|
480
|
+
from?: string;
|
|
481
|
+
to?: string;
|
|
482
|
+
}, limit?: number): Promise<PipelineExecution[]>;
|
|
483
|
+
/** 1.2 Success rate over time for an org. */
|
|
484
|
+
getSuccessRate(orgId: string, interval: string, from: string, to: string, orgIds?: string[]): Promise<TimeSeriesEntry[]>;
|
|
485
|
+
/** 1.3 Average duration per pipeline. */
|
|
486
|
+
getAverageDuration(orgId: string, from: string, to: string, orgIds?: string[]): Promise<DurationStats[]>;
|
|
487
|
+
/** 1.5 Stage failure heatmap — which stages fail most. */
|
|
488
|
+
getStageFailures(orgId: string, from: string, to: string, orgIds?: string[]): Promise<StageFailure[]>;
|
|
489
|
+
/**
|
|
490
|
+
* Distinct deploy `environment` values observed in the window — powers the
|
|
491
|
+
* DORA environment-scope datalist. Org-scoped + rollup-aware like the sibling
|
|
492
|
+
* reports; only non-null environments (i.e. deploy-attributed executions).
|
|
493
|
+
*/
|
|
494
|
+
getReportEnvironments(orgId: string, from: string, to: string, orgIds?: string[]): Promise<string[]>;
|
|
495
|
+
/** 1.6 Stage bottlenecks — slowest stages per pipeline. */
|
|
496
|
+
getStageBottlenecks(orgId: string, from: string, to: string, orgIds?: string[]): Promise<StageBottleneck[]>;
|
|
497
|
+
/** 1.7 Action failure rate — which plugin steps fail most. */
|
|
498
|
+
getActionFailures(orgId: string, from: string, to: string, orgIds?: string[]): Promise<ActionFailure[]>;
|
|
499
|
+
/**
|
|
500
|
+
* 1.8 Error categorization — group failure messages. Execution report, so
|
|
501
|
+
* rollup-aware exactly like the sibling execution reports: with `orgIds`
|
|
502
|
+
* (the org→team subtree) the org gate becomes an `IN (...)` and the read runs
|
|
503
|
+
* fresh under sysadmin via `runReport`; single-org reads keep the per-org cache.
|
|
504
|
+
*/
|
|
505
|
+
getErrors(orgId: string, from: string, to: string, limit?: number, orgIds?: string[]): Promise<ErrorEntry[]>;
|
|
506
|
+
/**
|
|
507
|
+
* 1.9 DORA metrics over a [from,to] deploy-completion window, org-scoped +
|
|
508
|
+
* rollup-aware. DEPLOY-BASIS ONLY — every metric derives from real deploy-stage
|
|
509
|
+
* executions (`event_type='STAGE'` with a non-null `environment`, set by the
|
|
510
|
+
* forwarder for the stages a user declared in `pb.deploys`). No run-based
|
|
511
|
+
* fallback: a pipeline with no deploy stage produces no DORA data.
|
|
512
|
+
*
|
|
513
|
+
* Four scoped SQL scans run inside one tx (each org-gated by `p.org_id ${pred}`
|
|
514
|
+
* / `o.org_id ${pred}` so a rollup passes the org→team subtree and a foreign
|
|
515
|
+
* org's rows never enter): (1) terminal deploy rows PER (execution, env) — one
|
|
516
|
+
* "deployment" = one execution reaching an env (D1) — → DF / deploy-time CFR /
|
|
517
|
+
* measured lead time (lead time joins the execution's earliest commit, D2);
|
|
518
|
+
* the scan also reaches back `windowHours` before `from` so incidents opened
|
|
519
|
+
* near the window start can correlate to a just-prior deploy; (2) `deployment_outcomes`
|
|
520
|
+
* in-window → post-deploy CFR component; (3) production restored/failed
|
|
521
|
+
* outcomes joined to their deploy → MTTR; (4) registry vs deploying → coverage.
|
|
522
|
+
* The cross-source medians (lead time, MTTR) are computed in JS from clamped
|
|
523
|
+
* (≥0) deltas.
|
|
524
|
+
*
|
|
525
|
+
* @param opts.pipelineId restrict to one pipeline (per-pipeline DORA)
|
|
526
|
+
* @param opts.environment restrict to one deploy environment
|
|
527
|
+
*/
|
|
528
|
+
getDoraMetrics(orgId: string, from: string, to: string, orgIds?: string[], opts?: DoraOptions): Promise<DoraMetrics>;
|
|
529
|
+
/**
|
|
530
|
+
* Shape the raw DORA scan rows into the public {@link DoraMetrics}. Buckets the
|
|
531
|
+
* terminal deploy rows by environment (DF / deploy-time CFR / measured lead),
|
|
532
|
+
* folds in the post-deploy failure counts (manual outcomes + webhook-ingested
|
|
533
|
+
* incidents, deduped by deploy execution), computes production MTTR from both
|
|
534
|
+
* sources (incident `resolved_at − opened_at` taking precedence over the manual
|
|
535
|
+
* `restored − deployed`), and reconciles coverage — all cross-source medians
|
|
536
|
+
* over deltas clamped ≥0.
|
|
537
|
+
*
|
|
538
|
+
* Phase 5 incident correlation: each incident is attributed to the most recent
|
|
539
|
+
* SUCCESSFUL deploy to its environment with `completed_at ≤ opened_at` within
|
|
540
|
+
* {@link DORA_INCIDENT_WINDOW_HOURS}. That deploy is a post-deploy failure; an
|
|
541
|
+
* uncorrelated incident (no eligible deploy) contributes nothing.
|
|
542
|
+
*/
|
|
543
|
+
private shapeDora;
|
|
544
|
+
/**
|
|
545
|
+
* 1.9b DORA trend — deployment frequency + deploy-time change-failure rate
|
|
546
|
+
* bucketed by `interval` (day/week/month) for a sparkline. Deploy-basis like
|
|
547
|
+
* getDoraMetrics: buckets terminal deploy-stage executions on their
|
|
548
|
+
* `completed_at`. Shares the org/rollup + pipelineId/environment scoping. MTTR,
|
|
549
|
+
* lead time, and post-deploy CFR are intentionally omitted (too heavy to bucket).
|
|
550
|
+
*/
|
|
551
|
+
getDoraTrend(orgId: string, interval: string, from: string, to: string, orgIds?: string[], opts?: DoraOptions): Promise<DoraTrendPoint[]>;
|
|
552
|
+
/**
|
|
553
|
+
* 1.10 Per-pipeline BUILD HEALTH (Phase 6) — a standard (NOT `advanced_reporting`)
|
|
554
|
+
* per-stage breakdown for one pipeline over a [from,to] window. Aggregates the
|
|
555
|
+
* existing `pipeline_events` STAGE rows: each stage is rolled up per execution
|
|
556
|
+
* to a terminal status (FAILED wins, then SUCCEEDED) + its max duration, then
|
|
557
|
+
* grouped per stage into run/success/failure counts, a success rate, and
|
|
558
|
+
* duration percentiles (p50/p90/p99). Totals sum across stages. Org-scoped via
|
|
559
|
+
* the pipeline join (`p.org_id ${pred}`) + rollup-aware like the sibling reports;
|
|
560
|
+
* a pipelineId owned by another org returns an empty breakdown.
|
|
561
|
+
*/
|
|
562
|
+
getBuildHealth(orgId: string, pipelineId: string, from: string, to: string, orgIds?: string[]): Promise<BuildHealth>;
|
|
563
|
+
/** 2.1 Plugin summary — counts and breakdowns.
|
|
564
|
+
* INTENTIONALLY SINGLE-ORG (no rollup): plugin inventory is an org-owned
|
|
565
|
+
* asset count, not an execution/build activity report, so a parent's view is
|
|
566
|
+
* its own plugins — teams manage their own inventory. Single `= $org` scope. */
|
|
567
|
+
getPluginSummary(orgId: string): Promise<PluginSummary>;
|
|
568
|
+
/** 2.2 Type & compute distribution.
|
|
569
|
+
* INTENTIONALLY SINGLE-ORG (no rollup): plugin inventory is per-org (see
|
|
570
|
+
* getPluginSummary). Single `= $org` scope. */
|
|
571
|
+
getPluginDistribution(orgId: string): Promise<TypeComputeDistribution[]>;
|
|
572
|
+
/** 2.3 Version counts per plugin name.
|
|
573
|
+
* INTENTIONALLY SINGLE-ORG (no rollup): plugin inventory is per-org (see
|
|
574
|
+
* getPluginSummary). Single `= $org` scope. */
|
|
575
|
+
getPluginVersions(orgId: string): Promise<VersionCount[]>;
|
|
576
|
+
/**
|
|
577
|
+
* 2.4 Build success rate over time.
|
|
578
|
+
*
|
|
579
|
+
* STATUS CASING NOTE: This query filters by `event_source = 'plugin-build'`
|
|
580
|
+
* and uses lowercase status values (`'completed'`, `'failed'`), while
|
|
581
|
+
* `getSuccessRate` (1.2) filters by `event_type = 'PIPELINE'` with
|
|
582
|
+
* uppercase AWS-style statuses (`'SUCCEEDED'`, `'FAILED'`, `'CANCELED'`).
|
|
583
|
+
* The casing drift is intentional and tracks the producer:
|
|
584
|
+
* - `plugin-build` events come from our own build pipeline (lowercase)
|
|
585
|
+
* - `PIPELINE` events come from AWS CodePipeline (uppercase)
|
|
586
|
+
* The ingest Zod schema at api/reporting/src/routes/event-ingest.ts
|
|
587
|
+
* SHOULD enum these per-eventSource so we catch drift at ingest rather
|
|
588
|
+
* than silently producing zero rows here. See findings N71.
|
|
589
|
+
*/
|
|
590
|
+
getBuildSuccessRate(orgId: string, interval: string, from: string, to: string, orgIds?: string[]): Promise<BuildTimeSeriesEntry[]>;
|
|
591
|
+
/** 2.5 Build duration per plugin. Build activity report — rollup-aware. */
|
|
592
|
+
getBuildDuration(orgId: string, from: string, to: string, orgIds?: string[]): Promise<BuildDuration[]>;
|
|
593
|
+
/** 2.6 Build failures — top error messages. Build activity report — rollup-aware. */
|
|
594
|
+
getBuildFailures(orgId: string, from: string, to: string, limit?: number, orgIds?: string[]): Promise<BuildFailure[]>;
|
|
595
|
+
/**
|
|
596
|
+
* Record a manual post-deploy outcome marker (Phase 2): a user marks a
|
|
597
|
+
* deployment `failed` (a production incident linked to the deploy) or
|
|
598
|
+
* `restored` (recovered). Idempotent — keyed on (execution_id, outcome) so a
|
|
599
|
+
* duplicate POST refreshes `at` rather than double-counting, while a
|
|
600
|
+
* failed→restored pair stays two rows. Feeds the post-deploy CFR component and
|
|
601
|
+
* the real MTTR. Runs under the caller's org context (RLS WITH CHECK).
|
|
602
|
+
*/
|
|
603
|
+
recordDeploymentOutcome(orgId: string, executionId: string, input: {
|
|
604
|
+
outcome: 'failed' | 'restored';
|
|
605
|
+
at: string;
|
|
606
|
+
environment?: string;
|
|
607
|
+
}): Promise<void>;
|
|
608
|
+
/**
|
|
609
|
+
* Ingest a production incident (Phase 5) from the org's incident tooling
|
|
610
|
+
* (PagerDuty / Datadog / Alertmanager webhook). Idempotent — keyed on
|
|
611
|
+
* (org_id, incident_id) so a later resolve re-post upserts `resolved_at`
|
|
612
|
+
* (and refreshes the other mutable fields) instead of inserting a duplicate.
|
|
613
|
+
* DORA correlates each incident to the most recent successful deploy to its
|
|
614
|
+
* `environment`, producing an automated post-deploy failure + a real MTTR.
|
|
615
|
+
* Runs under the caller's org context (RLS WITH CHECK); free-form fields are
|
|
616
|
+
* AWS-id scrubbed at this persistence boundary like the other ingest paths.
|
|
617
|
+
*/
|
|
618
|
+
recordIncident(orgId: string, input: IncidentInput): Promise<void>;
|
|
619
|
+
/**
|
|
620
|
+
* Read the per-org DORA settings (Phase 5b). Returns the stored
|
|
621
|
+
* `incidentWindowHours` override (or `null` when unset) plus the global env
|
|
622
|
+
* default, so the settings UI can show both. Runs under the caller's tenant
|
|
623
|
+
* context (RLS-scoped); a single-org read.
|
|
624
|
+
*/
|
|
625
|
+
getIncidentSettings(orgId: string): Promise<IncidentSettings>;
|
|
626
|
+
/**
|
|
627
|
+
* Upsert per-org reporting settings (Phase 5b incident window + Phase 7
|
|
628
|
+
* retention overrides), idempotent on `org_id`. A partial write — only the
|
|
629
|
+
* fields present in `patch` are set (so updating retention never clears the
|
|
630
|
+
* incident window, and vice-versa). Set self-serve by an org admin via
|
|
631
|
+
* `PUT /api/reports/settings/incidents`. Runs under the caller's org context
|
|
632
|
+
* (RLS WITH CHECK); a changed value affects DORA aggregates so the org's
|
|
633
|
+
* cached reports are dropped.
|
|
634
|
+
*/
|
|
635
|
+
setReportingSettings(orgId: string, patch: ReportingSettingsPatch): Promise<void>;
|
|
636
|
+
/**
|
|
637
|
+
* List recent incidents for an org (Phase 5b org-admin surface), newest first,
|
|
638
|
+
* paginated. Each row carries its resolved state and its deploy correlation —
|
|
639
|
+
* the most recent SUCCESSFUL deploy to the incident's `environment` whose
|
|
640
|
+
* `completed_at` falls within the effective per-org correlation window before
|
|
641
|
+
* `opened_at` (the same rule DORA's CFR/MTTR correlation applies). Single-org
|
|
642
|
+
* (no rollup) — an org admin views their own org's incidents. RLS-scoped via
|
|
643
|
+
* the `p.org_id`/`i.org_id` predicates + the tenant context.
|
|
644
|
+
*/
|
|
645
|
+
listIncidents(orgId: string, opts: {
|
|
646
|
+
limit: number;
|
|
647
|
+
offset: number;
|
|
648
|
+
}): Promise<IncidentListItem[]>;
|
|
649
|
+
/**
|
|
650
|
+
* Wiring-test dry-run (Phase 5b): would a synthetic incident opening NOW for
|
|
651
|
+
* `environment` correlate to a recent successful deploy under the org's
|
|
652
|
+
* effective window? This is a NON-persisting correlation check — it verifies
|
|
653
|
+
* the admin's environment naming + window line up with real deploy events
|
|
654
|
+
* WITHOUT writing an incident (so a "test" never pollutes CFR/MTTR). RLS-scoped.
|
|
655
|
+
*/
|
|
656
|
+
testIncidentCorrelation(orgId: string, environment: string): Promise<IncidentTestResult>;
|
|
657
|
+
/**
|
|
658
|
+
* Upsert per-org ingestion health (Phase 3): the AWS events Lambda periodically
|
|
659
|
+
* reports forwarded/dropped counters + the last event timestamp so the Reports
|
|
660
|
+
* UI can show flowing / stale / dropping. One row per org (upsert on org_id).
|
|
661
|
+
*/
|
|
662
|
+
recordIngestHealth(orgId: string, input: {
|
|
663
|
+
forwarded?: number;
|
|
664
|
+
dropped?: number;
|
|
665
|
+
lastEventAt?: string;
|
|
666
|
+
}): Promise<void>;
|
|
667
|
+
/**
|
|
668
|
+
* Reporting retention sweep (Phase 7). Hard-deletes rows older than their
|
|
669
|
+
* retention window, by `created_at`, across every org that has reporting data —
|
|
670
|
+
* a **split** policy so high-volume standard events expire faster than the
|
|
671
|
+
* low-volume DORA source:
|
|
672
|
+
* - `pipeline_events WHERE environment IS NULL` → standard-event window.
|
|
673
|
+
* - `pipeline_events WHERE environment IS NOT NULL` (deploy stages),
|
|
674
|
+
* `deployment_outcomes`, and `incidents` → DORA-source window.
|
|
675
|
+
* Each org's windows come from its `dora_settings` override, else the global
|
|
676
|
+
* env defaults (see {@link resolveEventRetentionDays} / {@link resolveDoraRetentionDays}).
|
|
677
|
+
* `ingest_health` and `dora_settings` are never purged. One `now` for the whole
|
|
678
|
+
* tick (rows crossing the boundary mid-sweep wait for the next). Establishes a
|
|
679
|
+
* sysadmin tenant scope so the deletes span all orgs / bypass RLS — this is a
|
|
680
|
+
* cross-tenant housekeeping job. Returns (and logs) per-window purge tallies.
|
|
681
|
+
*/
|
|
682
|
+
purgeExpiredReportingData(opts?: ReportingRetentionOptions): Promise<ReportingRetentionCounts>;
|
|
683
|
+
}
|
|
684
|
+
export declare const reportingService: ReportingService;
|
|
685
|
+
export {};
|