@pipeline-builder/pipeline-data 4.1.15 → 4.1.17
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/lib/api/access-control-builder.d.ts +18 -1
- package/lib/api/access-control-builder.js +19 -6
- package/lib/api/crud-service.d.ts +94 -36
- package/lib/api/crud-service.js +188 -166
- package/lib/api/plugin-resolution.d.ts +253 -0
- package/lib/api/plugin-resolution.js +649 -0
- package/lib/api/public-directory.d.ts +281 -0
- package/lib/api/public-directory.js +457 -0
- package/lib/api/query-builders.d.ts +13 -0
- package/lib/api/query-builders.js +74 -9
- package/lib/api/reporting/caches.js +4 -4
- package/lib/api/reporting/dora-scoring.d.ts +3 -3
- package/lib/api/reporting/dora-scoring.js +4 -4
- package/lib/api/reporting/dora.d.ts +6 -6
- package/lib/api/reporting/dora.js +97 -73
- package/lib/api/reporting/plugin-runtime.d.ts +25 -0
- package/lib/api/reporting/plugin-runtime.js +106 -0
- package/lib/api/reporting/query-scope.d.ts +8 -2
- package/lib/api/reporting/query-scope.js +16 -7
- package/lib/api/reporting/retention.d.ts +4 -10
- package/lib/api/reporting/retention.js +8 -11
- package/lib/api/reporting/sql-helpers.d.ts +4 -7
- package/lib/api/reporting/sql-helpers.js +8 -9
- package/lib/api/reporting/types.d.ts +55 -17
- package/lib/api/reporting/types.js +1 -1
- package/lib/api/reporting-service.d.ts +35 -26
- package/lib/api/reporting-service.js +245 -161
- package/lib/api/semver-range.d.ts +73 -0
- package/lib/api/semver-range.js +227 -0
- package/lib/api/soft-delete-sweep.js +8 -20
- package/lib/core/query-filters.d.ts +18 -3
- package/lib/core/query-filters.js +1 -1
- package/lib/database/drizzle-schema.d.ts +7790 -2461
- package/lib/database/drizzle-schema.js +29 -1
- package/lib/database/index.d.ts +4 -2
- package/lib/database/index.js +5 -3
- package/lib/database/pg-result.d.ts +19 -0
- package/lib/database/pg-result.js +32 -0
- package/lib/database/postgres-connection.js +14 -24
- package/lib/database/public-reader.d.ts +14 -0
- package/lib/database/public-reader.js +68 -0
- package/lib/database/retry-strategy.d.ts +3 -5
- package/lib/database/retry-strategy.js +4 -6
- package/lib/database/schema/compliance.d.ts +16 -34
- package/lib/database/schema/compliance.js +4 -4
- package/lib/database/schema/dashboard.js +4 -2
- package/lib/database/schema/ecosystem.d.ts +6586 -0
- package/lib/database/schema/ecosystem.js +709 -0
- package/lib/database/schema/message.d.ts +6 -12
- package/lib/database/schema/message.js +3 -1
- package/lib/database/schema/pipeline.d.ts +79 -5
- package/lib/database/schema/pipeline.js +46 -25
- package/lib/database/schema/plugin.d.ts +587 -3
- package/lib/database/schema/plugin.js +86 -14
- package/lib/database/tenancy.d.ts +6 -7
- package/lib/database/tenancy.js +13 -14
- package/lib/index.d.ts +7 -4
- package/lib/index.js +7 -4
- package/lib/testing/exports.json +146 -0
- package/package.json +44 -7
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
// Copyright 2026 Pipeline Builder Contributors
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
/**
|
|
4
|
+
* Per-plugin RUNTIME telemetry: how plugins behave when
|
|
5
|
+
* pipelines run them, as opposed to the plugin BUILD reports. Reads the
|
|
6
|
+
* `plugin_publisher/plugin_name/plugin_version` that event ingest stamps on
|
|
7
|
+
* ACTION events from the pipeline's step manifest.
|
|
8
|
+
*
|
|
9
|
+
* A "run" is one terminal ACTION event — SUCCEEDED or FAILED. Canceled,
|
|
10
|
+
* abandoned and superseded actions say nothing about the plugin and are left
|
|
11
|
+
* out of both the count and the rate.
|
|
12
|
+
*
|
|
13
|
+
* Two audiences:
|
|
14
|
+
* - the org's own dashboard ({@link getPluginRuntime}): org-scoped (rollup
|
|
15
|
+
* aware), grouped per plugin version;
|
|
16
|
+
* - the ecosystem, service-to-service ({@link getPluginRuntimeAggregate},
|
|
17
|
+
* {@link hasVerifiedPluginUse}): cross-org aggregates keyed on a listing's
|
|
18
|
+
* `(publisher, name)`, feeding `plugin_stats` and the review "verified use"
|
|
19
|
+
* badge. Never exposed to a user token — they read every org's events.
|
|
20
|
+
*/
|
|
21
|
+
import { sql } from 'drizzle-orm';
|
|
22
|
+
import { orgScope, runReport } from './query-scope.js';
|
|
23
|
+
import { schema } from '../../database/drizzle-schema.js';
|
|
24
|
+
import { runWithTenantContext, withTenantTx } from '../../database/tenancy.js';
|
|
25
|
+
import { drizzleRows } from '../crud-service.js';
|
|
26
|
+
/** Window for the ecosystem aggregate (`plugin_stats.success_rate_30d`). */
|
|
27
|
+
export const PLUGIN_RUNTIME_AGGREGATE_DAYS = 30;
|
|
28
|
+
/** Window for review "verified use": a successful run in the last 90 days. */
|
|
29
|
+
export const PLUGIN_VERIFIED_USE_DAYS = 90;
|
|
30
|
+
/** The terminal-ACTION, plugin-attributed predicate every runtime read shares (alias `e`). */
|
|
31
|
+
const pluginRuns = sql `e.event_type = 'ACTION' AND e.plugin_name IS NOT NULL
|
|
32
|
+
AND e.status IN ('SUCCEEDED', 'FAILED')`;
|
|
33
|
+
/** `IS NULL` for an own-org (publisher-less) plugin, `=` otherwise. */
|
|
34
|
+
function publisherPredicate(publisher) {
|
|
35
|
+
return publisher === null ? sql `e.plugin_publisher IS NULL` : sql `e.plugin_publisher = ${publisher}`;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Per plugin version: runs, success rate and duration (p50/p95) over
|
|
39
|
+
* `[from, to]` on `completed_at`, for the org (or its rollup subtree).
|
|
40
|
+
* Optional filters narrow to one plugin name / publisher (`null` = own-org
|
|
41
|
+
* plugins) / version.
|
|
42
|
+
*/
|
|
43
|
+
export async function getPluginRuntime(orgId, from, to, filter = {}, orgIds) {
|
|
44
|
+
const { pred, multi } = orgScope(orgId, orgIds);
|
|
45
|
+
const nameClause = filter.name !== undefined ? sql `AND e.plugin_name = ${filter.name}` : sql ``;
|
|
46
|
+
const publisherClause = filter.publisher !== undefined ? sql `AND ${publisherPredicate(filter.publisher)}` : sql ``;
|
|
47
|
+
const versionClause = filter.version !== undefined ? sql `AND e.plugin_version = ${filter.version}` : sql ``;
|
|
48
|
+
const exec = () => withTenantTx((tx) => tx.execute(sql `
|
|
49
|
+
SELECT
|
|
50
|
+
e.plugin_publisher AS "pluginPublisher",
|
|
51
|
+
e.plugin_name AS "pluginName",
|
|
52
|
+
e.plugin_version AS "pluginVersion",
|
|
53
|
+
COUNT(*)::int AS runs,
|
|
54
|
+
COUNT(*) FILTER (WHERE e.status = 'SUCCEEDED')::int AS succeeded,
|
|
55
|
+
COUNT(*) FILTER (WHERE e.status = 'FAILED')::int AS failed,
|
|
56
|
+
ROUND(COUNT(*) FILTER (WHERE e.status = 'SUCCEEDED')::numeric
|
|
57
|
+
/ NULLIF(COUNT(*), 0) * 100, 1)::float AS "successPct",
|
|
58
|
+
PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY e.duration_ms)::int AS "p50Ms",
|
|
59
|
+
PERCENTILE_CONT(0.95) WITHIN GROUP (ORDER BY e.duration_ms)::int AS "p95Ms",
|
|
60
|
+
MAX(e.completed_at)::text AS "lastRun"
|
|
61
|
+
FROM ${schema.pipelineEvent} e
|
|
62
|
+
WHERE e.org_id ${pred} AND ${pluginRuns}
|
|
63
|
+
AND e.completed_at >= ${from}::timestamptz AND e.completed_at <= ${to}::timestamptz
|
|
64
|
+
${nameClause} ${publisherClause} ${versionClause}
|
|
65
|
+
GROUP BY e.plugin_publisher, e.plugin_name, e.plugin_version
|
|
66
|
+
ORDER BY runs DESC
|
|
67
|
+
`).then(r => drizzleRows(r.rows)));
|
|
68
|
+
const filterKey = `${filter.name ?? ''}:${filter.publisher === null ? '\u0000' : filter.publisher ?? ''}:${filter.version ?? ''}`;
|
|
69
|
+
return runReport(`${orgId}:plugin-runtime:${from}:${to}:${filterKey}`, multi, exec);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Ecosystem aggregate for one listing over the last 30 days, across EVERY org:
|
|
73
|
+
* runs, success rate (0–1; null with no runs) and how many distinct orgs ran it.
|
|
74
|
+
* Runs under system context — the caller is the plugin service computing
|
|
75
|
+
* `plugin_stats`, and `activeOrgCount30d` is only ever shown at ≥ 5.
|
|
76
|
+
*/
|
|
77
|
+
export async function getPluginRuntimeAggregate(publisher, name) {
|
|
78
|
+
const rows = await runWithTenantContext({ isSuperAdmin: true }, () => withTenantTx((tx) => tx.execute(sql `
|
|
79
|
+
SELECT
|
|
80
|
+
COUNT(*)::int AS "runs30d",
|
|
81
|
+
(COUNT(*) FILTER (WHERE e.status = 'SUCCEEDED')::float
|
|
82
|
+
/ NULLIF(COUNT(*), 0)) AS "successRate30d",
|
|
83
|
+
COUNT(DISTINCT e.org_id)::int AS "activeOrgCount30d"
|
|
84
|
+
FROM ${schema.pipelineEvent} e
|
|
85
|
+
WHERE ${pluginRuns} AND ${publisherPredicate(publisher)} AND e.plugin_name = ${name}
|
|
86
|
+
AND e.completed_at >= now() - make_interval(days => ${PLUGIN_RUNTIME_AGGREGATE_DAYS})
|
|
87
|
+
`).then(r => drizzleRows(r.rows))));
|
|
88
|
+
return rows[0] ?? { runs30d: 0, successRate30d: null, activeOrgCount30d: 0 };
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Review "verified use": did `orgId` run `(publisher, name)` successfully
|
|
92
|
+
* in the last 90 days? Bounded by the org's event retention — a run older than
|
|
93
|
+
* the retention horizon has been swept and no longer counts.
|
|
94
|
+
*/
|
|
95
|
+
export async function hasVerifiedPluginUse(orgId, publisher, name) {
|
|
96
|
+
const rows = await runWithTenantContext({ orgId, isSuperAdmin: false }, () => withTenantTx((tx) => tx.execute(sql `
|
|
97
|
+
SELECT EXISTS (
|
|
98
|
+
SELECT 1 FROM ${schema.pipelineEvent} e
|
|
99
|
+
WHERE e.org_id = ${orgId} AND ${pluginRuns} AND e.status = 'SUCCEEDED'
|
|
100
|
+
AND ${publisherPredicate(publisher)} AND e.plugin_name = ${name}
|
|
101
|
+
AND e.completed_at >= now() - make_interval(days => ${PLUGIN_VERIFIED_USE_DAYS})
|
|
102
|
+
) AS "verified"
|
|
103
|
+
`).then(r => drizzleRows(r.rows))));
|
|
104
|
+
return rows[0]?.verified === true;
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGx1Z2luLXJ1bnRpbWUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvYXBpL3JlcG9ydGluZy9wbHVnaW4tcnVudGltZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwrQ0FBK0M7QUFDL0Msc0NBQXNDO0FBRXRDOzs7Ozs7Ozs7Ozs7Ozs7OztHQWlCRztBQUVILE9BQU8sRUFBRSxHQUFHLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFDbEMsT0FBTyxFQUFFLFFBQVEsRUFBRSxTQUFTLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUV2RCxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sa0NBQWtDLENBQUM7QUFDMUQsT0FBTyxFQUFFLG9CQUFvQixFQUFFLFlBQVksRUFBRSxNQUFNLDJCQUEyQixDQUFDO0FBQy9FLE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQUVqRCw0RUFBNEU7QUFDNUUsTUFBTSxDQUFDLE1BQU0sNkJBQTZCLEdBQUcsRUFBRSxDQUFDO0FBQ2hELDhFQUE4RTtBQUM5RSxNQUFNLENBQUMsTUFBTSx3QkFBd0IsR0FBRyxFQUFFLENBQUM7QUFFM0MsOEZBQThGO0FBQzlGLE1BQU0sVUFBVSxHQUFHLEdBQUcsQ0FBQTtrREFDNEIsQ0FBQztBQUVuRCx1RUFBdUU7QUFDdkUsU0FBUyxrQkFBa0IsQ0FBQyxTQUF3QjtJQUNsRCxPQUFPLFNBQVMsS0FBSyxJQUFJLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQSw0QkFBNEIsQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFBLHdCQUF3QixTQUFTLEVBQUUsQ0FBQztBQUN2RyxDQUFDO0FBRUQ7Ozs7O0dBS0c7QUFDSCxNQUFNLENBQUMsS0FBSyxVQUFVLGdCQUFnQixDQUNwQyxLQUFhLEVBQ2IsSUFBWSxFQUNaLEVBQVUsRUFDVixNQUFNLEdBQXdCLEVBQUUsRUFDaEMsTUFBaUI7SUFFakIsTUFBTSxFQUFFLElBQUksRUFBRSxLQUFLLEVBQUUsR0FBRyxRQUFRLENBQUMsS0FBSyxFQUFFLE1BQU0sQ0FBQyxDQUFDO0lBQ2hELE1BQU0sVUFBVSxHQUFHLE1BQU0sQ0FBQyxJQUFJLEtBQUssU0FBUyxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUEsdUJBQXVCLE1BQU0sQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFBLEVBQUUsQ0FBQztJQUMvRixNQUFNLGVBQWUsR0FBRyxNQUFNLENBQUMsU0FBUyxLQUFLLFNBQVMsQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFBLE9BQU8sa0JBQWtCLENBQUMsTUFBTSxDQUFDLFNBQVMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQSxFQUFFLENBQUM7SUFDbEgsTUFBTSxhQUFhLEdBQUcsTUFBTSxDQUFDLE9BQU8sS0FBSyxTQUFTLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQSwwQkFBMEIsTUFBTSxDQUFDLE9BQU8sRUFBRSxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUEsRUFBRSxDQUFDO0lBQzNHLE1BQU0sSUFBSSxHQUFHLEdBQUcsRUFBRSxDQUFDLFlBQVksQ0FBQyxDQUFDLEVBQUUsRUFBRSxFQUFFLENBQUMsRUFBRSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUE7Ozs7Ozs7Ozs7Ozs7ZUFhekMsTUFBTSxDQUFDLGFBQWE7eUJBQ1YsSUFBSSxRQUFRLFVBQVU7a0NBQ2IsSUFBSSx1Q0FBdUMsRUFBRTtZQUNuRSxVQUFVLElBQUksZUFBZSxJQUFJLGFBQWE7OztPQUduRCxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsV0FBVyxDQUFxQixDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQzNELE1BQU0sU0FBUyxHQUFHLEdBQUcsTUFBTSxDQUFDLElBQUksSUFBSSxFQUFFLElBQUksTUFBTSxDQUFDLFNBQVMsS0FBSyxJQUFJLENBQUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLFNBQVMsSUFBSSxFQUFFLElBQUksTUFBTSxDQUFDLE9BQU8sSUFBSSxFQUFFLEVBQUUsQ0FBQztJQUNsSSxPQUFPLFNBQVMsQ0FBQyxHQUFHLEtBQUssbUJBQW1CLElBQUksSUFBSSxFQUFFLElBQUksU0FBUyxFQUFFLEVBQUUsS0FBSyxFQUFFLElBQUksQ0FBQyxDQUFDO0FBQ3RGLENBQUM7QUFFRDs7Ozs7R0FLRztBQUNILE1BQU0sQ0FBQyxLQUFLLFVBQVUseUJBQXlCLENBQUMsU0FBaUIsRUFBRSxJQUFZO0lBQzdFLE1BQU0sSUFBSSxHQUFHLE1BQU0sb0JBQW9CLENBQUMsRUFBRSxZQUFZLEVBQUUsSUFBSSxFQUFFLEVBQUUsR0FBRyxFQUFFLENBQUMsWUFBWSxDQUFDLENBQUMsRUFBRSxFQUFFLEVBQUUsQ0FBQyxFQUFFLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQTs7Ozs7O2VBTTVGLE1BQU0sQ0FBQyxhQUFhO2dCQUNuQixVQUFVLFFBQVEsa0JBQWtCLENBQUMsU0FBUyxDQUFDLHdCQUF3QixJQUFJO2dFQUMzQiw2QkFBNkI7T0FDdEYsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLFdBQVcsQ0FBeUIsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQ2hFLE9BQU8sSUFBSSxDQUFDLENBQUMsQ0FBQyxJQUFJLEVBQUUsT0FBTyxFQUFFLENBQUMsRUFBRSxjQUFjLEVBQUUsSUFBSSxFQUFFLGlCQUFpQixFQUFFLENBQUMsRUFBRSxDQUFDO0FBQy9FLENBQUM7QUFFRDs7OztHQUlHO0FBQ0gsTUFBTSxDQUFDLEtBQUssVUFBVSxvQkFBb0IsQ0FBQyxLQUFhLEVBQUUsU0FBaUIsRUFBRSxJQUFZO0lBQ3ZGLE1BQU0sSUFBSSxHQUFHLE1BQU0sb0JBQW9CLENBQUMsRUFBRSxLQUFLLEVBQUUsWUFBWSxFQUFFLEtBQUssRUFBRSxFQUFFLEdBQUcsRUFBRSxDQUFDLFlBQVksQ0FBQyxDQUFDLEVBQUUsRUFBRSxFQUFFLENBQUMsRUFBRSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUE7OzBCQUV6RixNQUFNLENBQUMsYUFBYTs2QkFDakIsS0FBSyxRQUFRLFVBQVU7a0JBQ2xDLGtCQUFrQixDQUFDLFNBQVMsQ0FBQyx3QkFBd0IsSUFBSTtrRUFDVCx3QkFBd0I7O09BRW5GLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxXQUFXLENBQXdCLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUMvRCxPQUFPLElBQUksQ0FBQyxDQUFDLENBQUMsRUFBRSxRQUFRLEtBQUssSUFBSSxDQUFDO0FBQ3BDLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBDb3B5cmlnaHQgMjAyNiBQaXBlbGluZSBCdWlsZGVyIENvbnRyaWJ1dG9yc1xuLy8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IEFwYWNoZS0yLjBcblxuLyoqXG4gKiBQZXItcGx1Z2luIFJVTlRJTUUgdGVsZW1ldHJ5OiBob3cgcGx1Z2lucyBiZWhhdmUgd2hlblxuICogcGlwZWxpbmVzIHJ1biB0aGVtLCBhcyBvcHBvc2VkIHRvIHRoZSBwbHVnaW4gQlVJTEQgcmVwb3J0cy4gUmVhZHMgdGhlXG4gKiBgcGx1Z2luX3B1Ymxpc2hlci9wbHVnaW5fbmFtZS9wbHVnaW5fdmVyc2lvbmAgdGhhdCBldmVudCBpbmdlc3Qgc3RhbXBzIG9uXG4gKiBBQ1RJT04gZXZlbnRzIGZyb20gdGhlIHBpcGVsaW5lJ3Mgc3RlcCBtYW5pZmVzdC5cbiAqXG4gKiBBIFwicnVuXCIgaXMgb25lIHRlcm1pbmFsIEFDVElPTiBldmVudCDigJQgU1VDQ0VFREVEIG9yIEZBSUxFRC4gQ2FuY2VsZWQsXG4gKiBhYmFuZG9uZWQgYW5kIHN1cGVyc2VkZWQgYWN0aW9ucyBzYXkgbm90aGluZyBhYm91dCB0aGUgcGx1Z2luIGFuZCBhcmUgbGVmdFxuICogb3V0IG9mIGJvdGggdGhlIGNvdW50IGFuZCB0aGUgcmF0ZS5cbiAqXG4gKiBUd28gYXVkaWVuY2VzOlxuICogICAtIHRoZSBvcmcncyBvd24gZGFzaGJvYXJkICh7QGxpbmsgZ2V0UGx1Z2luUnVudGltZX0pOiBvcmctc2NvcGVkIChyb2xsdXBcbiAqICAgICBhd2FyZSksIGdyb3VwZWQgcGVyIHBsdWdpbiB2ZXJzaW9uO1xuICogICAtIHRoZSBlY29zeXN0ZW0sIHNlcnZpY2UtdG8tc2VydmljZSAoe0BsaW5rIGdldFBsdWdpblJ1bnRpbWVBZ2dyZWdhdGV9LFxuICogICAgIHtAbGluayBoYXNWZXJpZmllZFBsdWdpblVzZX0pOiBjcm9zcy1vcmcgYWdncmVnYXRlcyBrZXllZCBvbiBhIGxpc3Rpbmcnc1xuICogICAgIGAocHVibGlzaGVyLCBuYW1lKWAsIGZlZWRpbmcgYHBsdWdpbl9zdGF0c2AgYW5kIHRoZSByZXZpZXcgXCJ2ZXJpZmllZCB1c2VcIlxuICogICAgIGJhZGdlLiBOZXZlciBleHBvc2VkIHRvIGEgdXNlciB0b2tlbiDigJQgdGhleSByZWFkIGV2ZXJ5IG9yZydzIGV2ZW50cy5cbiAqL1xuXG5pbXBvcnQgeyBzcWwgfSBmcm9tICdkcml6emxlLW9ybSc7XG5pbXBvcnQgeyBvcmdTY29wZSwgcnVuUmVwb3J0IH0gZnJvbSAnLi9xdWVyeS1zY29wZS5qcyc7XG5pbXBvcnQgdHlwZSB7IFBsdWdpblJ1bnRpbWVBZ2dyZWdhdGUsIFBsdWdpblJ1bnRpbWVGaWx0ZXIsIFBsdWdpblJ1bnRpbWVTdGF0cyB9IGZyb20gJy4vdHlwZXMuanMnO1xuaW1wb3J0IHsgc2NoZW1hIH0gZnJvbSAnLi4vLi4vZGF0YWJhc2UvZHJpenpsZS1zY2hlbWEuanMnO1xuaW1wb3J0IHsgcnVuV2l0aFRlbmFudENvbnRleHQsIHdpdGhUZW5hbnRUeCB9IGZyb20gJy4uLy4uL2RhdGFiYXNlL3RlbmFuY3kuanMnO1xuaW1wb3J0IHsgZHJpenpsZVJvd3MgfSBmcm9tICcuLi9jcnVkLXNlcnZpY2UuanMnO1xuXG4vKiogV2luZG93IGZvciB0aGUgZWNvc3lzdGVtIGFnZ3JlZ2F0ZSAoYHBsdWdpbl9zdGF0cy5zdWNjZXNzX3JhdGVfMzBkYCkuICovXG5leHBvcnQgY29uc3QgUExVR0lOX1JVTlRJTUVfQUdHUkVHQVRFX0RBWVMgPSAzMDtcbi8qKiBXaW5kb3cgZm9yIHJldmlldyBcInZlcmlmaWVkIHVzZVwiOiBhIHN1Y2Nlc3NmdWwgcnVuIGluIHRoZSBsYXN0IDkwIGRheXMuICovXG5leHBvcnQgY29uc3QgUExVR0lOX1ZFUklGSUVEX1VTRV9EQVlTID0gOTA7XG5cbi8qKiBUaGUgdGVybWluYWwtQUNUSU9OLCBwbHVnaW4tYXR0cmlidXRlZCBwcmVkaWNhdGUgZXZlcnkgcnVudGltZSByZWFkIHNoYXJlcyAoYWxpYXMgYGVgKS4gKi9cbmNvbnN0IHBsdWdpblJ1bnMgPSBzcWxgZS5ldmVudF90eXBlID0gJ0FDVElPTicgQU5EIGUucGx1Z2luX25hbWUgSVMgTk9UIE5VTExcbiAgICAgICAgICBBTkQgZS5zdGF0dXMgSU4gKCdTVUNDRUVERUQnLCAnRkFJTEVEJylgO1xuXG4vKiogYElTIE5VTExgIGZvciBhbiBvd24tb3JnIChwdWJsaXNoZXItbGVzcykgcGx1Z2luLCBgPWAgb3RoZXJ3aXNlLiAqL1xuZnVuY3Rpb24gcHVibGlzaGVyUHJlZGljYXRlKHB1Ymxpc2hlcjogc3RyaW5nIHwgbnVsbCkge1xuICByZXR1cm4gcHVibGlzaGVyID09PSBudWxsID8gc3FsYGUucGx1Z2luX3B1Ymxpc2hlciBJUyBOVUxMYCA6IHNxbGBlLnBsdWdpbl9wdWJsaXNoZXIgPSAke3B1Ymxpc2hlcn1gO1xufVxuXG4vKipcbiAqIFBlciBwbHVnaW4gdmVyc2lvbjogcnVucywgc3VjY2VzcyByYXRlIGFuZCBkdXJhdGlvbiAocDUwL3A5NSkgb3ZlclxuICogYFtmcm9tLCB0b11gIG9uIGBjb21wbGV0ZWRfYXRgLCBmb3IgdGhlIG9yZyAob3IgaXRzIHJvbGx1cCBzdWJ0cmVlKS5cbiAqIE9wdGlvbmFsIGZpbHRlcnMgbmFycm93IHRvIG9uZSBwbHVnaW4gbmFtZSAvIHB1Ymxpc2hlciAoYG51bGxgID0gb3duLW9yZ1xuICogcGx1Z2lucykgLyB2ZXJzaW9uLlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gZ2V0UGx1Z2luUnVudGltZShcbiAgb3JnSWQ6IHN0cmluZyxcbiAgZnJvbTogc3RyaW5nLFxuICB0bzogc3RyaW5nLFxuICBmaWx0ZXI6IFBsdWdpblJ1bnRpbWVGaWx0ZXIgPSB7fSxcbiAgb3JnSWRzPzogc3RyaW5nW10sXG4pOiBQcm9taXNlPFBsdWdpblJ1bnRpbWVTdGF0c1tdPiB7XG4gIGNvbnN0IHsgcHJlZCwgbXVsdGkgfSA9IG9yZ1Njb3BlKG9yZ0lkLCBvcmdJZHMpO1xuICBjb25zdCBuYW1lQ2xhdXNlID0gZmlsdGVyLm5hbWUgIT09IHVuZGVmaW5lZCA/IHNxbGBBTkQgZS5wbHVnaW5fbmFtZSA9ICR7ZmlsdGVyLm5hbWV9YCA6IHNxbGBgO1xuICBjb25zdCBwdWJsaXNoZXJDbGF1c2UgPSBmaWx0ZXIucHVibGlzaGVyICE9PSB1bmRlZmluZWQgPyBzcWxgQU5EICR7cHVibGlzaGVyUHJlZGljYXRlKGZpbHRlci5wdWJsaXNoZXIpfWAgOiBzcWxgYDtcbiAgY29uc3QgdmVyc2lvbkNsYXVzZSA9IGZpbHRlci52ZXJzaW9uICE9PSB1bmRlZmluZWQgPyBzcWxgQU5EIGUucGx1Z2luX3ZlcnNpb24gPSAke2ZpbHRlci52ZXJzaW9ufWAgOiBzcWxgYDtcbiAgY29uc3QgZXhlYyA9ICgpID0+IHdpdGhUZW5hbnRUeCgodHgpID0+IHR4LmV4ZWN1dGUoc3FsYFxuICAgICAgICBTRUxFQ1RcbiAgICAgICAgICBlLnBsdWdpbl9wdWJsaXNoZXIgQVMgXCJwbHVnaW5QdWJsaXNoZXJcIixcbiAgICAgICAgICBlLnBsdWdpbl9uYW1lIEFTIFwicGx1Z2luTmFtZVwiLFxuICAgICAgICAgIGUucGx1Z2luX3ZlcnNpb24gQVMgXCJwbHVnaW5WZXJzaW9uXCIsXG4gICAgICAgICAgQ09VTlQoKik6OmludCBBUyBydW5zLFxuICAgICAgICAgIENPVU5UKCopIEZJTFRFUiAoV0hFUkUgZS5zdGF0dXMgPSAnU1VDQ0VFREVEJyk6OmludCBBUyBzdWNjZWVkZWQsXG4gICAgICAgICAgQ09VTlQoKikgRklMVEVSIChXSEVSRSBlLnN0YXR1cyA9ICdGQUlMRUQnKTo6aW50IEFTIGZhaWxlZCxcbiAgICAgICAgICBST1VORChDT1VOVCgqKSBGSUxURVIgKFdIRVJFIGUuc3RhdHVzID0gJ1NVQ0NFRURFRCcpOjpudW1lcmljXG4gICAgICAgICAgICAvIE5VTExJRihDT1VOVCgqKSwgMCkgKiAxMDAsIDEpOjpmbG9hdCBBUyBcInN1Y2Nlc3NQY3RcIixcbiAgICAgICAgICBQRVJDRU5USUxFX0NPTlQoMC41KSBXSVRISU4gR1JPVVAgKE9SREVSIEJZIGUuZHVyYXRpb25fbXMpOjppbnQgQVMgXCJwNTBNc1wiLFxuICAgICAgICAgIFBFUkNFTlRJTEVfQ09OVCgwLjk1KSBXSVRISU4gR1JPVVAgKE9SREVSIEJZIGUuZHVyYXRpb25fbXMpOjppbnQgQVMgXCJwOTVNc1wiLFxuICAgICAgICAgIE1BWChlLmNvbXBsZXRlZF9hdCk6OnRleHQgQVMgXCJsYXN0UnVuXCJcbiAgICAgICAgRlJPTSAke3NjaGVtYS5waXBlbGluZUV2ZW50fSBlXG4gICAgICAgIFdIRVJFIGUub3JnX2lkICR7cHJlZH0gQU5EICR7cGx1Z2luUnVuc31cbiAgICAgICAgICBBTkQgZS5jb21wbGV0ZWRfYXQgPj0gJHtmcm9tfTo6dGltZXN0YW1wdHogQU5EIGUuY29tcGxldGVkX2F0IDw9ICR7dG99Ojp0aW1lc3RhbXB0elxuICAgICAgICAgICR7bmFtZUNsYXVzZX0gJHtwdWJsaXNoZXJDbGF1c2V9ICR7dmVyc2lvbkNsYXVzZX1cbiAgICAgICAgR1JPVVAgQlkgZS5wbHVnaW5fcHVibGlzaGVyLCBlLnBsdWdpbl9uYW1lLCBlLnBsdWdpbl92ZXJzaW9uXG4gICAgICAgIE9SREVSIEJZIHJ1bnMgREVTQ1xuICAgICAgYCkudGhlbihyID0+IGRyaXp6bGVSb3dzPFBsdWdpblJ1bnRpbWVTdGF0cz4oci5yb3dzKSkpO1xuICBjb25zdCBmaWx0ZXJLZXkgPSBgJHtmaWx0ZXIubmFtZSA/PyAnJ306JHtmaWx0ZXIucHVibGlzaGVyID09PSBudWxsID8gJ1xcdTAwMDAnIDogZmlsdGVyLnB1Ymxpc2hlciA/PyAnJ306JHtmaWx0ZXIudmVyc2lvbiA/PyAnJ31gO1xuICByZXR1cm4gcnVuUmVwb3J0KGAke29yZ0lkfTpwbHVnaW4tcnVudGltZToke2Zyb219OiR7dG99OiR7ZmlsdGVyS2V5fWAsIG11bHRpLCBleGVjKTtcbn1cblxuLyoqXG4gKiBFY29zeXN0ZW0gYWdncmVnYXRlIGZvciBvbmUgbGlzdGluZyBvdmVyIHRoZSBsYXN0IDMwIGRheXMsIGFjcm9zcyBFVkVSWSBvcmc6XG4gKiBydW5zLCBzdWNjZXNzIHJhdGUgKDDigJMxOyBudWxsIHdpdGggbm8gcnVucykgYW5kIGhvdyBtYW55IGRpc3RpbmN0IG9yZ3MgcmFuIGl0LlxuICogUnVucyB1bmRlciBzeXN0ZW0gY29udGV4dCDigJQgdGhlIGNhbGxlciBpcyB0aGUgcGx1Z2luIHNlcnZpY2UgY29tcHV0aW5nXG4gKiBgcGx1Z2luX3N0YXRzYCwgYW5kIGBhY3RpdmVPcmdDb3VudDMwZGAgaXMgb25seSBldmVyIHNob3duIGF0IOKJpSA1LlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gZ2V0UGx1Z2luUnVudGltZUFnZ3JlZ2F0ZShwdWJsaXNoZXI6IHN0cmluZywgbmFtZTogc3RyaW5nKTogUHJvbWlzZTxQbHVnaW5SdW50aW1lQWdncmVnYXRlPiB7XG4gIGNvbnN0IHJvd3MgPSBhd2FpdCBydW5XaXRoVGVuYW50Q29udGV4dCh7IGlzU3VwZXJBZG1pbjogdHJ1ZSB9LCAoKSA9PiB3aXRoVGVuYW50VHgoKHR4KSA9PiB0eC5leGVjdXRlKHNxbGBcbiAgICAgICAgU0VMRUNUXG4gICAgICAgICAgQ09VTlQoKik6OmludCBBUyBcInJ1bnMzMGRcIixcbiAgICAgICAgICAoQ09VTlQoKikgRklMVEVSIChXSEVSRSBlLnN0YXR1cyA9ICdTVUNDRUVERUQnKTo6ZmxvYXRcbiAgICAgICAgICAgIC8gTlVMTElGKENPVU5UKCopLCAwKSkgQVMgXCJzdWNjZXNzUmF0ZTMwZFwiLFxuICAgICAgICAgIENPVU5UKERJU1RJTkNUIGUub3JnX2lkKTo6aW50IEFTIFwiYWN0aXZlT3JnQ291bnQzMGRcIlxuICAgICAgICBGUk9NICR7c2NoZW1hLnBpcGVsaW5lRXZlbnR9IGVcbiAgICAgICAgV0hFUkUgJHtwbHVnaW5SdW5zfSBBTkQgJHtwdWJsaXNoZXJQcmVkaWNhdGUocHVibGlzaGVyKX0gQU5EIGUucGx1Z2luX25hbWUgPSAke25hbWV9XG4gICAgICAgICAgQU5EIGUuY29tcGxldGVkX2F0ID49IG5vdygpIC0gbWFrZV9pbnRlcnZhbChkYXlzID0+ICR7UExVR0lOX1JVTlRJTUVfQUdHUkVHQVRFX0RBWVN9KVxuICAgICAgYCkudGhlbihyID0+IGRyaXp6bGVSb3dzPFBsdWdpblJ1bnRpbWVBZ2dyZWdhdGU+KHIucm93cykpKSk7XG4gIHJldHVybiByb3dzWzBdID8/IHsgcnVuczMwZDogMCwgc3VjY2Vzc1JhdGUzMGQ6IG51bGwsIGFjdGl2ZU9yZ0NvdW50MzBkOiAwIH07XG59XG5cbi8qKlxuICogUmV2aWV3IFwidmVyaWZpZWQgdXNlXCI6IGRpZCBgb3JnSWRgIHJ1biBgKHB1Ymxpc2hlciwgbmFtZSlgIHN1Y2Nlc3NmdWxseVxuICogaW4gdGhlIGxhc3QgOTAgZGF5cz8gQm91bmRlZCBieSB0aGUgb3JnJ3MgZXZlbnQgcmV0ZW50aW9uIOKAlCBhIHJ1biBvbGRlciB0aGFuXG4gKiB0aGUgcmV0ZW50aW9uIGhvcml6b24gaGFzIGJlZW4gc3dlcHQgYW5kIG5vIGxvbmdlciBjb3VudHMuXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBoYXNWZXJpZmllZFBsdWdpblVzZShvcmdJZDogc3RyaW5nLCBwdWJsaXNoZXI6IHN0cmluZywgbmFtZTogc3RyaW5nKTogUHJvbWlzZTxib29sZWFuPiB7XG4gIGNvbnN0IHJvd3MgPSBhd2FpdCBydW5XaXRoVGVuYW50Q29udGV4dCh7IG9yZ0lkLCBpc1N1cGVyQWRtaW46IGZhbHNlIH0sICgpID0+IHdpdGhUZW5hbnRUeCgodHgpID0+IHR4LmV4ZWN1dGUoc3FsYFxuICAgICAgICBTRUxFQ1QgRVhJU1RTIChcbiAgICAgICAgICBTRUxFQ1QgMSBGUk9NICR7c2NoZW1hLnBpcGVsaW5lRXZlbnR9IGVcbiAgICAgICAgICBXSEVSRSBlLm9yZ19pZCA9ICR7b3JnSWR9IEFORCAke3BsdWdpblJ1bnN9IEFORCBlLnN0YXR1cyA9ICdTVUNDRUVERUQnXG4gICAgICAgICAgICBBTkQgJHtwdWJsaXNoZXJQcmVkaWNhdGUocHVibGlzaGVyKX0gQU5EIGUucGx1Z2luX25hbWUgPSAke25hbWV9XG4gICAgICAgICAgICBBTkQgZS5jb21wbGV0ZWRfYXQgPj0gbm93KCkgLSBtYWtlX2ludGVydmFsKGRheXMgPT4gJHtQTFVHSU5fVkVSSUZJRURfVVNFX0RBWVN9KVxuICAgICAgICApIEFTIFwidmVyaWZpZWRcIlxuICAgICAgYCkudGhlbihyID0+IGRyaXp6bGVSb3dzPHsgdmVyaWZpZWQ6IGJvb2xlYW4gfT4oci5yb3dzKSkpKTtcbiAgcmV0dXJuIHJvd3NbMF0/LnZlcmlmaWVkID09PSB0cnVlO1xufVxuIl19
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type SQL } from 'drizzle-orm';
|
|
1
2
|
/**
|
|
2
3
|
* Build the org-scope predicate for a report query. With `orgIds` (the
|
|
3
4
|
* org → team rollup — a parent's `[self, ...descendants]`) it becomes an
|
|
@@ -7,7 +8,7 @@
|
|
|
7
8
|
* cache key.
|
|
8
9
|
*/
|
|
9
10
|
export declare function orgScope(orgId: string, orgIds?: string[]): {
|
|
10
|
-
pred:
|
|
11
|
+
pred: SQL<unknown>;
|
|
11
12
|
multi: boolean;
|
|
12
13
|
};
|
|
13
14
|
/**
|
|
@@ -18,4 +19,9 @@ export declare function orgScope(orgId: string, orgIds?: string[]): {
|
|
|
18
19
|
* has no org tree), so caching them would serve stale aggregates. Always fresh.
|
|
19
20
|
*/
|
|
20
21
|
export declare function runReport<T>(cacheKey: string, multi: boolean, exec: () => Promise<T>): Promise<T>;
|
|
21
|
-
/**
|
|
22
|
+
/**
|
|
23
|
+
* One org-scoped report read: build the SQL from the org-scope predicate, run it
|
|
24
|
+
* in the tenant transaction and return the rows, through {@link runReport}'s
|
|
25
|
+
* cache/rollup policy. `key` is the cache key below the org.
|
|
26
|
+
*/
|
|
27
|
+
export declare function report<T>(key: string, orgId: string, orgIds: string[] | undefined, query: (pred: SQL) => SQL): Promise<T[]>;
|
|
@@ -3,15 +3,15 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* Tenancy scoping and cache/context policy for reporting reads.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* use the identical scoping rules without reaching back into the class.
|
|
6
|
+
* Plain functions (no instance state — only the module-level logger and
|
|
7
|
+
* timeseries cache) so the per-domain query modules (dora.ts) use the identical
|
|
8
|
+
* scoping rules as ReportingService.
|
|
10
9
|
*/
|
|
11
10
|
import { createLogger } from '@pipeline-builder/api-core';
|
|
12
11
|
import { sql } from 'drizzle-orm';
|
|
13
12
|
import { timeseriesCache } from './caches.js';
|
|
14
|
-
import {
|
|
13
|
+
import { resultRows } from '../../database/pg-result.js';
|
|
14
|
+
import { runWithTenantContext, withTenantTx } from '../../database/tenancy.js';
|
|
15
15
|
const logger = createLogger('reporting-service');
|
|
16
16
|
/**
|
|
17
17
|
* Build the org-scope predicate for a report query. With `orgIds` (the
|
|
@@ -58,5 +58,14 @@ export function runReport(cacheKey, multi, exec) {
|
|
|
58
58
|
? runWithTenantContext({ isSuperAdmin: true }, exec)
|
|
59
59
|
: timeseriesCache.getOrSet(cacheKey, exec);
|
|
60
60
|
}
|
|
61
|
-
/**
|
|
62
|
-
|
|
61
|
+
/**
|
|
62
|
+
* One org-scoped report read: build the SQL from the org-scope predicate, run it
|
|
63
|
+
* in the tenant transaction and return the rows, through {@link runReport}'s
|
|
64
|
+
* cache/rollup policy. `key` is the cache key below the org.
|
|
65
|
+
*/
|
|
66
|
+
export function report(key, orgId, orgIds, query) {
|
|
67
|
+
const { pred, multi } = orgScope(orgId, orgIds);
|
|
68
|
+
const exec = () => withTenantTx((tx) => tx.execute(query(pred))).then((r) => resultRows(r));
|
|
69
|
+
return runReport(`${orgId}:${key}`, multi, exec);
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicXVlcnktc2NvcGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvYXBpL3JlcG9ydGluZy9xdWVyeS1zY29wZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwrQ0FBK0M7QUFDL0Msc0NBQXNDO0FBRXRDOzs7Ozs7R0FNRztBQUVILE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSw0QkFBNEIsQ0FBQztBQUMxRCxPQUFPLEVBQUUsR0FBRyxFQUFZLE1BQU0sYUFBYSxDQUFDO0FBQzVDLE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFDOUMsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLDZCQUE2QixDQUFDO0FBQ3pELE9BQU8sRUFBRSxvQkFBb0IsRUFBRSxZQUFZLEVBQUUsTUFBTSwyQkFBMkIsQ0FBQztBQUUvRSxNQUFNLE1BQU0sR0FBRyxZQUFZLENBQUMsbUJBQW1CLENBQUMsQ0FBQztBQUVqRDs7Ozs7OztHQU9HO0FBQ0gsTUFBTSxVQUFVLFFBQVEsQ0FBQyxLQUFhLEVBQUUsTUFBaUI7SUFDdkQsMEVBQTBFO0lBQzFFLDZFQUE2RTtJQUM3RSw4RUFBOEU7SUFDOUUsNkVBQTZFO0lBQzdFLDZFQUE2RTtJQUM3RSw2RUFBNkU7SUFDN0UsNkVBQTZFO0lBQzdFLHdFQUF3RTtJQUN4RSxnREFBZ0Q7SUFDaEQsTUFBTSxNQUFNLEdBQUcsS0FBSyxDQUFDLFdBQVcsRUFBRSxDQUFDO0lBQ25DLE1BQU0sUUFBUSxHQUFHLENBQUMsTUFBTSxJQUFJLEVBQUUsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxDQUFDLEVBQUUsRUFBRSxFQUFFLENBQUMsT0FBTyxFQUFFLEtBQUssUUFBUSxJQUFJLEVBQUUsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDLENBQUM7SUFDeEYsTUFBTSxRQUFRLEdBQUcsUUFBUSxDQUFDLElBQUksQ0FBQyxDQUFDLEVBQUUsRUFBRSxFQUFFLENBQUMsRUFBRSxDQUFDLFdBQVcsRUFBRSxLQUFLLE1BQU0sQ0FBQyxDQUFDO0lBQ3BFLElBQUksUUFBUSxDQUFDLE1BQU0sR0FBRyxDQUFDLElBQUksQ0FBQyxRQUFRLEVBQUUsQ0FBQztRQUNyQyxNQUFNLENBQUMsSUFBSSxDQUFDLGdHQUFnRyxFQUFFO1lBQzVHLEtBQUssRUFBRSxNQUFNLEVBQUUsV0FBVyxFQUFFLFFBQVEsQ0FBQyxNQUFNO1NBQzVDLENBQUMsQ0FBQztJQUNMLENBQUM7SUFDRCxNQUFNLEdBQUcsR0FBRyxRQUFRLENBQUMsTUFBTSxHQUFHLENBQUMsSUFBSSxRQUFRLENBQUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUNqRSxNQUFNLEtBQUssR0FBRyxHQUFHLENBQUMsTUFBTSxHQUFHLENBQUMsQ0FBQztJQUM3QixNQUFNLElBQUksR0FBRyxLQUFLO1FBQ2hCLENBQUMsQ0FBQyxHQUFHLENBQUEsT0FBTyxHQUFHLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQyxFQUFFLEVBQUUsRUFBRSxDQUFDLEdBQUcsQ0FBQSxHQUFHLEVBQUUsRUFBRSxDQUFDLEVBQUUsR0FBRyxDQUFBLElBQUksQ0FBQyxHQUFHO1FBQzdELENBQUMsQ0FBQyxHQUFHLENBQUEsS0FBSyxHQUFHLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQztJQUNyQixPQUFPLEVBQUUsSUFBSSxFQUFFLEtBQUssRUFBRSxDQUFDO0FBQ3pCLENBQUM7QUFFRDs7Ozs7O0dBTUc7QUFDSCxNQUFNLFVBQVUsU0FBUyxDQUFJLFFBQWdCLEVBQUUsS0FBYyxFQUFFLElBQXNCO0lBQ25GLE9BQU8sS0FBSztRQUNWLENBQUMsQ0FBQyxvQkFBb0IsQ0FBQyxFQUFFLFlBQVksRUFBRSxJQUFJLEVBQUUsRUFBRSxJQUFJLENBQUM7UUFDcEQsQ0FBQyxDQUFDLGVBQWUsQ0FBQyxRQUFRLENBQUMsUUFBUSxFQUFFLElBQUksQ0FBQyxDQUFDO0FBQy9DLENBQUM7QUFFRDs7OztHQUlHO0FBQ0gsTUFBTSxVQUFVLE1BQU0sQ0FBSSxHQUFXLEVBQUUsS0FBYSxFQUFFLE1BQTRCLEVBQUUsS0FBeUI7SUFDM0csTUFBTSxFQUFFLElBQUksRUFBRSxLQUFLLEVBQUUsR0FBRyxRQUFRLENBQUMsS0FBSyxFQUFFLE1BQU0sQ0FBQyxDQUFDO0lBQ2hELE1BQU0sSUFBSSxHQUFHLEdBQUcsRUFBRSxDQUFDLFlBQVksQ0FBQyxDQUFDLEVBQUUsRUFBRSxFQUFFLENBQUMsRUFBRSxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsVUFBVSxDQUFJLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDL0YsT0FBTyxTQUFTLENBQUMsR0FBRyxLQUFLLElBQUksR0FBRyxFQUFFLEVBQUUsS0FBSyxFQUFFLElBQUksQ0FBQyxDQUFDO0FBQ25ELENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBDb3B5cmlnaHQgMjAyNiBQaXBlbGluZSBCdWlsZGVyIENvbnRyaWJ1dG9yc1xuLy8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IEFwYWNoZS0yLjBcblxuLyoqXG4gKiBUZW5hbmN5IHNjb3BpbmcgYW5kIGNhY2hlL2NvbnRleHQgcG9saWN5IGZvciByZXBvcnRpbmcgcmVhZHMuXG4gKlxuICogUGxhaW4gZnVuY3Rpb25zIChubyBpbnN0YW5jZSBzdGF0ZSDigJQgb25seSB0aGUgbW9kdWxlLWxldmVsIGxvZ2dlciBhbmRcbiAqIHRpbWVzZXJpZXMgY2FjaGUpIHNvIHRoZSBwZXItZG9tYWluIHF1ZXJ5IG1vZHVsZXMgKGRvcmEudHMpIHVzZSB0aGUgaWRlbnRpY2FsXG4gKiBzY29waW5nIHJ1bGVzIGFzIFJlcG9ydGluZ1NlcnZpY2UuXG4gKi9cblxuaW1wb3J0IHsgY3JlYXRlTG9nZ2VyIH0gZnJvbSAnQHBpcGVsaW5lLWJ1aWxkZXIvYXBpLWNvcmUnO1xuaW1wb3J0IHsgc3FsLCB0eXBlIFNRTCB9IGZyb20gJ2RyaXp6bGUtb3JtJztcbmltcG9ydCB7IHRpbWVzZXJpZXNDYWNoZSB9IGZyb20gJy4vY2FjaGVzLmpzJztcbmltcG9ydCB7IHJlc3VsdFJvd3MgfSBmcm9tICcuLi8uLi9kYXRhYmFzZS9wZy1yZXN1bHQuanMnO1xuaW1wb3J0IHsgcnVuV2l0aFRlbmFudENvbnRleHQsIHdpdGhUZW5hbnRUeCB9IGZyb20gJy4uLy4uL2RhdGFiYXNlL3RlbmFuY3kuanMnO1xuXG5jb25zdCBsb2dnZXIgPSBjcmVhdGVMb2dnZXIoJ3JlcG9ydGluZy1zZXJ2aWNlJyk7XG5cbi8qKlxuICogQnVpbGQgdGhlIG9yZy1zY29wZSBwcmVkaWNhdGUgZm9yIGEgcmVwb3J0IHF1ZXJ5LiBXaXRoIGBvcmdJZHNgICh0aGVcbiAqIG9yZyDihpIgdGVhbSByb2xsdXAg4oCUIGEgcGFyZW50J3MgYFtzZWxmLCAuLi5kZXNjZW5kYW50c11gKSBpdCBiZWNvbWVzIGFuXG4gKiBgSU4gKC4uLilgIG92ZXIgdGhlIHN1YnRyZWU7IG90aGVyd2lzZSB0aGUgc2luZ2xlLW9yZyBgPSAkb3JnYC4gUmV0dXJucyBhXG4gKiBgbXVsdGlgIGZsYWcgc28gY2FsbGVycyBjYW4gcnVuIG11bHRpLW9yZyByZWFkcyB1bmRlciBzeXNhZG1pbiBjb250ZXh0XG4gKiAodGhlIHN1YnRyZWUgc3BhbnMgb3JncyBvdXRzaWRlIHRoZSByZXF1ZXN0J3MgUkxTIHNjb3BlKSBhbmQgc2FsdCB0aGVcbiAqIGNhY2hlIGtleS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIG9yZ1Njb3BlKG9yZ0lkOiBzdHJpbmcsIG9yZ0lkcz86IHN0cmluZ1tdKSB7XG4gIC8vIERlZmVuc2UtaW4tZGVwdGg6IHRoZSBtdWx0aS1vcmcgcm9sbHVwIHJ1bnMgdW5kZXIgc3lzYWRtaW4gY29udGV4dCAoUkxTXG4gIC8vIE9GRiwgc2VlIHJ1blJlcG9ydCksIHNvIGBvcmdJZHNgIGlzIHRoZSBPTkxZIHRlbmFuY3kgZ2F0ZSBvbiB0aGUgcmVhZC4gVGhlXG4gIC8vIHJvdXRlIHJlc29sdmVzIGl0IGZyb20gdGhlIHBsYXRmb3JtJ3MgYXV0aG9yaXRhdGl2ZSBkZXNjZW5kYW50cyBlbmRwb2ludCBhc1xuICAvLyBgW3NlbGYsIC4uLmRlc2NlbmRhbnRzXWAsIGJ1dCB2YWxpZGF0ZSB0aGUgc3VidHJlZSBkZWZlbnNpdmVseSBoZXJlIGJlZm9yZVxuICAvLyB0aGUgYnlwYXNzOiB0aGUgcmVxdWVzdGluZyAoYW5jaG9yKSBvcmcgTVVTVCBhcHBlYXIgaW4gdGhlIHNldC4gQSBzZXQgdGhhdFxuICAvLyBvbWl0cyB0aGUgY2FsbGVyJ3Mgb3duIG9yZyBpcyBhIG1hbGZvcm1lZC9zcG9vZmVkIHJvbGx1cCDigJQgY29sbGFwc2UgdG8gdGhlXG4gIC8vIHNhZmUgc2luZ2xlLW9yZyBzY29wZSByYXRoZXIgdGhhbiByZWFkaW5nIGFuIGFyYml0cmFyeSBvcmcgdW5kZXIgc3lzYWRtaW4uXG4gIC8vIChGdWxsIGRlc2NlbmRhbnQtbWVtYmVyc2hpcCB2YWxpZGF0aW9uIG5lZWRzIHRoZSBvcmcgdHJlZSwgd2hpY2ggdGhpc1xuICAvLyBzZXJ2aWNlIGRvZXMgbm90IGhvbGQg4oCUIHNlZSB0aGUgcmVwb3J0IEZMQUcuKVxuICBjb25zdCBhbmNob3IgPSBvcmdJZC50b0xvd2VyQ2FzZSgpO1xuICBjb25zdCBwcm92aWRlZCA9IChvcmdJZHMgPz8gW10pLmZpbHRlcigoaWQpID0+IHR5cGVvZiBpZCA9PT0gJ3N0cmluZycgJiYgaWQubGVuZ3RoID4gMCk7XG4gIGNvbnN0IGFuY2hvcmVkID0gcHJvdmlkZWQuc29tZSgoaWQpID0+IGlkLnRvTG93ZXJDYXNlKCkgPT09IGFuY2hvcik7XG4gIGlmIChwcm92aWRlZC5sZW5ndGggPiAwICYmICFhbmNob3JlZCkge1xuICAgIGxvZ2dlci53YXJuKCdSZXBvcnRpbmcgcm9sbHVwIHJlamVjdGVkOiByZXF1ZXN0aW5nIG9yZyBhYnNlbnQgZnJvbSByZXNvbHZlZCBzdWJ0cmVlOyB1c2luZyBzaW5nbGUtb3JnIHNjb3BlJywge1xuICAgICAgb3JnSWQ6IGFuY2hvciwgc3VidHJlZVNpemU6IHByb3ZpZGVkLmxlbmd0aCxcbiAgICB9KTtcbiAgfVxuICBjb25zdCBpZHMgPSBwcm92aWRlZC5sZW5ndGggPiAwICYmIGFuY2hvcmVkID8gcHJvdmlkZWQgOiBbb3JnSWRdO1xuICBjb25zdCBtdWx0aSA9IGlkcy5sZW5ndGggPiAxO1xuICBjb25zdCBwcmVkID0gbXVsdGlcbiAgICA/IHNxbGBJTiAoJHtzcWwuam9pbihpZHMubWFwKChpZCkgPT4gc3FsYCR7aWR9YCksIHNxbGAsIGApfSlgXG4gICAgOiBzcWxgPSAke2lkc1swXX1gO1xuICByZXR1cm4geyBwcmVkLCBtdWx0aSB9O1xufVxuXG4vKipcbiAqIFJ1biBhIHJlcG9ydCByZWFkLiBTaW5nbGUtb3JnIHJlYWRzIHVzZSB0aGUgcGVyLW9yZyBjYWNoZSAoaW52YWxpZGF0ZWQgb25cbiAqIHRoYXQgb3JnJ3MgZXZlbnQgaW5nZXN0KS4gUm9sbHVwIChtdWx0aS1vcmcpIHJlYWRzICoqYnlwYXNzIHRoZSBjYWNoZSoqIGFuZFxuICogcnVuIHVuZGVyIHN5c2FkbWluIGNvbnRleHQ6IHRoZXkncmUgYWRtaW4tb25seSBhbmQgbG93ZXItZnJlcXVlbmN5LCBhbmQgYVxuICogY2hpbGQgb3JnJ3MgZXZlbnQgaW5nZXN0IGNhbid0IGludmFsaWRhdGUgYSBwYXJlbnQncyByb2xsdXAgZW50cnkgKHJlcG9ydGluZ1xuICogaGFzIG5vIG9yZyB0cmVlKSwgc28gY2FjaGluZyB0aGVtIHdvdWxkIHNlcnZlIHN0YWxlIGFnZ3JlZ2F0ZXMuIEFsd2F5cyBmcmVzaC5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHJ1blJlcG9ydDxUPihjYWNoZUtleTogc3RyaW5nLCBtdWx0aTogYm9vbGVhbiwgZXhlYzogKCkgPT4gUHJvbWlzZTxUPik6IFByb21pc2U8VD4ge1xuICByZXR1cm4gbXVsdGlcbiAgICA/IHJ1bldpdGhUZW5hbnRDb250ZXh0KHsgaXNTdXBlckFkbWluOiB0cnVlIH0sIGV4ZWMpXG4gICAgOiB0aW1lc2VyaWVzQ2FjaGUuZ2V0T3JTZXQoY2FjaGVLZXksIGV4ZWMpO1xufVxuXG4vKipcbiAqIE9uZSBvcmctc2NvcGVkIHJlcG9ydCByZWFkOiBidWlsZCB0aGUgU1FMIGZyb20gdGhlIG9yZy1zY29wZSBwcmVkaWNhdGUsIHJ1biBpdFxuICogaW4gdGhlIHRlbmFudCB0cmFuc2FjdGlvbiBhbmQgcmV0dXJuIHRoZSByb3dzLCB0aHJvdWdoIHtAbGluayBydW5SZXBvcnR9J3NcbiAqIGNhY2hlL3JvbGx1cCBwb2xpY3kuIGBrZXlgIGlzIHRoZSBjYWNoZSBrZXkgYmVsb3cgdGhlIG9yZy5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHJlcG9ydDxUPihrZXk6IHN0cmluZywgb3JnSWQ6IHN0cmluZywgb3JnSWRzOiBzdHJpbmdbXSB8IHVuZGVmaW5lZCwgcXVlcnk6IChwcmVkOiBTUUwpID0+IFNRTCk6IFByb21pc2U8VFtdPiB7XG4gIGNvbnN0IHsgcHJlZCwgbXVsdGkgfSA9IG9yZ1Njb3BlKG9yZ0lkLCBvcmdJZHMpO1xuICBjb25zdCBleGVjID0gKCkgPT4gd2l0aFRlbmFudFR4KCh0eCkgPT4gdHguZXhlY3V0ZShxdWVyeShwcmVkKSkpLnRoZW4oKHIpID0+IHJlc3VsdFJvd3M8VD4ocikpO1xuICByZXR1cm4gcnVuUmVwb3J0KGAke29yZ0lkfToke2tleX1gLCBtdWx0aSwgZXhlYyk7XG59XG4iXX0=
|
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* range, and overridable per-org by the settings row.
|
|
4
|
-
*/
|
|
5
|
-
/**
|
|
6
|
-
* Reporting retention windows (Phase 7). Records in `pipeline_events`,
|
|
2
|
+
* Reporting retention windows. Records in `pipeline_events`,
|
|
7
3
|
* `deployment_outcomes`, and `incidents` grow unbounded without a sweep, so a
|
|
8
4
|
* split, per-org retention purge (see {@link ReportingService.purgeExpiredReportingData})
|
|
9
5
|
* hard-deletes rows older than these windows, by `created_at`:
|
|
@@ -17,22 +13,20 @@
|
|
|
17
13
|
* `ingest_health` and `dora_settings` are never purged. Env-overridable globals;
|
|
18
14
|
* a per-org override in `dora_settings` wins (see {@link resolveEventRetentionDays}).
|
|
19
15
|
*/
|
|
20
|
-
export declare const RETENTION_MIN_DAYS = 1;
|
|
21
|
-
export declare const RETENTION_MAX_DAYS = 730;
|
|
22
16
|
/** Parse a positive-int env day-count, clamped to [MIN, MAX], else `fallback`. */
|
|
23
17
|
export declare function retentionEnvDays(name: string, fallback: number): number;
|
|
24
18
|
export declare const REPORTING_EVENT_RETENTION_DAYS: number;
|
|
25
19
|
export declare const REPORTING_DORA_RETENTION_DAYS: number;
|
|
26
20
|
/** Effective standard-event retention (days): a valid per-org override wins,
|
|
27
21
|
* else the global env default. Shared by the sweep + the settings surface so
|
|
28
|
-
* they can't drift. The `-1` unlimited sentinel
|
|
22
|
+
* they can't drift. The `-1` unlimited sentinel passes straight
|
|
29
23
|
* through — the sweep reads it as "keep forever, skip this org's standard
|
|
30
24
|
* events". A positive override clamps to [1, RETENTION_MAX_DAYS]; a `null`, a
|
|
31
25
|
* non-integer, or any other out-of-range value (e.g. 0, -2) falls back to the
|
|
32
26
|
* env default. */
|
|
33
27
|
export declare function resolveEventRetentionDays(override?: number | null): number;
|
|
34
28
|
/** Effective DORA-source retention (days): a valid per-org override wins, else
|
|
35
|
-
* the global env default. The `-1` unlimited sentinel
|
|
29
|
+
* the global env default. The `-1` unlimited sentinel passes straight
|
|
36
30
|
* through — the sweep reads it as "keep forever, skip this org's DORA-source
|
|
37
31
|
* windows". A positive override clamps to [1, RETENTION_MAX_DAYS]; a `null`, a
|
|
38
32
|
* non-integer, or any other out-of-range value (e.g. 0, -2) falls back to the
|
|
@@ -41,4 +35,4 @@ export declare function resolveDoraRetentionDays(override?: number | null): numb
|
|
|
41
35
|
/** The cutoff instant for a retention window: rows with `created_at` strictly
|
|
42
36
|
* before this (`< cutoff`) are expired. Deterministic for a fixed `now`. */
|
|
43
37
|
export declare function retentionCutoff(now: Date, days: number): Date;
|
|
44
|
-
/** Per-org retention override row
|
|
38
|
+
/** Per-org retention override row. Both nullable ⇒ global defaults. */
|
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
* Retention windows for reporting data: env-configurable, clamped to a sane
|
|
5
5
|
* range, and overridable per-org by the settings row.
|
|
6
6
|
*/
|
|
7
|
+
import { envInt, RETENTION_MAX_DAYS, RETENTION_MIN_DAYS } from '@pipeline-builder/api-core';
|
|
7
8
|
/**
|
|
8
|
-
* Reporting retention windows
|
|
9
|
+
* Reporting retention windows. Records in `pipeline_events`,
|
|
9
10
|
* `deployment_outcomes`, and `incidents` grow unbounded without a sweep, so a
|
|
10
11
|
* split, per-org retention purge (see {@link ReportingService.purgeExpiredReportingData})
|
|
11
12
|
* hard-deletes rows older than these windows, by `created_at`:
|
|
@@ -19,20 +20,16 @@
|
|
|
19
20
|
* `ingest_health` and `dora_settings` are never purged. Env-overridable globals;
|
|
20
21
|
* a per-org override in `dora_settings` wins (see {@link resolveEventRetentionDays}).
|
|
21
22
|
*/
|
|
22
|
-
export const RETENTION_MIN_DAYS = 1;
|
|
23
|
-
export const RETENTION_MAX_DAYS = 730;
|
|
24
23
|
/** Parse a positive-int env day-count, clamped to [MIN, MAX], else `fallback`. */
|
|
25
24
|
export function retentionEnvDays(name, fallback) {
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
return fallback;
|
|
29
|
-
return Math.min(raw, RETENTION_MAX_DAYS);
|
|
25
|
+
const days = envInt(name, fallback);
|
|
26
|
+
return days < RETENTION_MIN_DAYS ? fallback : Math.min(days, RETENTION_MAX_DAYS);
|
|
30
27
|
}
|
|
31
28
|
export const REPORTING_EVENT_RETENTION_DAYS = retentionEnvDays('REPORTING_EVENT_RETENTION_DAYS', 30);
|
|
32
29
|
export const REPORTING_DORA_RETENTION_DAYS = retentionEnvDays('REPORTING_DORA_RETENTION_DAYS', 180);
|
|
33
30
|
/** Effective standard-event retention (days): a valid per-org override wins,
|
|
34
31
|
* else the global env default. Shared by the sweep + the settings surface so
|
|
35
|
-
* they can't drift. The `-1` unlimited sentinel
|
|
32
|
+
* they can't drift. The `-1` unlimited sentinel passes straight
|
|
36
33
|
* through — the sweep reads it as "keep forever, skip this org's standard
|
|
37
34
|
* events". A positive override clamps to [1, RETENTION_MAX_DAYS]; a `null`, a
|
|
38
35
|
* non-integer, or any other out-of-range value (e.g. 0, -2) falls back to the
|
|
@@ -44,7 +41,7 @@ export function resolveEventRetentionDays(override) {
|
|
|
44
41
|
? Math.min(override, RETENTION_MAX_DAYS) : REPORTING_EVENT_RETENTION_DAYS;
|
|
45
42
|
}
|
|
46
43
|
/** Effective DORA-source retention (days): a valid per-org override wins, else
|
|
47
|
-
* the global env default. The `-1` unlimited sentinel
|
|
44
|
+
* the global env default. The `-1` unlimited sentinel passes straight
|
|
48
45
|
* through — the sweep reads it as "keep forever, skip this org's DORA-source
|
|
49
46
|
* windows". A positive override clamps to [1, RETENTION_MAX_DAYS]; a `null`, a
|
|
50
47
|
* non-integer, or any other out-of-range value (e.g. 0, -2) falls back to the
|
|
@@ -60,5 +57,5 @@ export function resolveDoraRetentionDays(override) {
|
|
|
60
57
|
export function retentionCutoff(now, days) {
|
|
61
58
|
return new Date(now.getTime() - days * 86_400_000);
|
|
62
59
|
}
|
|
63
|
-
/** Per-org retention override row
|
|
64
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
60
|
+
/** Per-org retention override row. Both nullable ⇒ global defaults. */
|
|
61
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmV0ZW50aW9uLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2FwaS9yZXBvcnRpbmcvcmV0ZW50aW9uLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLCtDQUErQztBQUMvQyxzQ0FBc0M7QUFFdEM7OztHQUdHO0FBRUgsT0FBTyxFQUFFLE1BQU0sRUFBRSxrQkFBa0IsRUFBRSxrQkFBa0IsRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBRTVGOzs7Ozs7Ozs7Ozs7OztHQWNHO0FBRUgsa0ZBQWtGO0FBQ2xGLE1BQU0sVUFBVSxnQkFBZ0IsQ0FBQyxJQUFZLEVBQUUsUUFBZ0I7SUFDN0QsTUFBTSxJQUFJLEdBQUcsTUFBTSxDQUFDLElBQUksRUFBRSxRQUFRLENBQUMsQ0FBQztJQUNwQyxPQUFPLElBQUksR0FBRyxrQkFBa0IsQ0FBQyxDQUFDLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLElBQUksRUFBRSxrQkFBa0IsQ0FBQyxDQUFDO0FBQ25GLENBQUM7QUFFRCxNQUFNLENBQUMsTUFBTSw4QkFBOEIsR0FBRyxnQkFBZ0IsQ0FBQyxnQ0FBZ0MsRUFBRSxFQUFFLENBQUMsQ0FBQztBQUNyRyxNQUFNLENBQUMsTUFBTSw2QkFBNkIsR0FBRyxnQkFBZ0IsQ0FBQywrQkFBK0IsRUFBRSxHQUFHLENBQUMsQ0FBQztBQUVwRzs7Ozs7O21CQU1tQjtBQUNuQixNQUFNLFVBQVUseUJBQXlCLENBQUMsUUFBd0I7SUFDaEUsSUFBSSxRQUFRLEtBQUssQ0FBQyxDQUFDO1FBQUUsT0FBTyxDQUFDLENBQUMsQ0FBQztJQUMvQixPQUFPLFFBQVEsSUFBSSxJQUFJLElBQUksTUFBTSxDQUFDLFNBQVMsQ0FBQyxRQUFRLENBQUMsSUFBSSxRQUFRLElBQUksa0JBQWtCO1FBQ3JGLENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLFFBQVEsRUFBRSxrQkFBa0IsQ0FBQyxDQUFDLENBQUMsQ0FBQyw4QkFBOEIsQ0FBQztBQUM5RSxDQUFDO0FBRUQ7Ozs7O21CQUttQjtBQUNuQixNQUFNLFVBQVUsd0JBQXdCLENBQUMsUUFBd0I7SUFDL0QsSUFBSSxRQUFRLEtBQUssQ0FBQyxDQUFDO1FBQUUsT0FBTyxDQUFDLENBQUMsQ0FBQztJQUMvQixPQUFPLFFBQVEsSUFBSSxJQUFJLElBQUksTUFBTSxDQUFDLFNBQVMsQ0FBQyxRQUFRLENBQUMsSUFBSSxRQUFRLElBQUksa0JBQWtCO1FBQ3JGLENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLFFBQVEsRUFBRSxrQkFBa0IsQ0FBQyxDQUFDLENBQUMsQ0FBQyw2QkFBNkIsQ0FBQztBQUM3RSxDQUFDO0FBRUQ7NkVBQzZFO0FBQzdFLE1BQU0sVUFBVSxlQUFlLENBQUMsR0FBUyxFQUFFLElBQVk7SUFDckQsT0FBTyxJQUFJLElBQUksQ0FBQyxHQUFHLENBQUMsT0FBTyxFQUFFLEdBQUcsSUFBSSxHQUFHLFVBQVUsQ0FBQyxDQUFDO0FBQ3JELENBQUM7QUFFRCx1RUFBdUUiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBDb3B5cmlnaHQgMjAyNiBQaXBlbGluZSBCdWlsZGVyIENvbnRyaWJ1dG9yc1xuLy8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IEFwYWNoZS0yLjBcblxuLyoqXG4gKiBSZXRlbnRpb24gd2luZG93cyBmb3IgcmVwb3J0aW5nIGRhdGE6IGVudi1jb25maWd1cmFibGUsIGNsYW1wZWQgdG8gYSBzYW5lXG4gKiByYW5nZSwgYW5kIG92ZXJyaWRhYmxlIHBlci1vcmcgYnkgdGhlIHNldHRpbmdzIHJvdy5cbiAqL1xuXG5pbXBvcnQgeyBlbnZJbnQsIFJFVEVOVElPTl9NQVhfREFZUywgUkVURU5USU9OX01JTl9EQVlTIH0gZnJvbSAnQHBpcGVsaW5lLWJ1aWxkZXIvYXBpLWNvcmUnO1xuXG4vKipcbiAqIFJlcG9ydGluZyByZXRlbnRpb24gd2luZG93cy4gUmVjb3JkcyBpbiBgcGlwZWxpbmVfZXZlbnRzYCxcbiAqIGBkZXBsb3ltZW50X291dGNvbWVzYCwgYW5kIGBpbmNpZGVudHNgIGdyb3cgdW5ib3VuZGVkIHdpdGhvdXQgYSBzd2VlcCwgc28gYVxuICogc3BsaXQsIHBlci1vcmcgcmV0ZW50aW9uIHB1cmdlIChzZWUge0BsaW5rIFJlcG9ydGluZ1NlcnZpY2UucHVyZ2VFeHBpcmVkUmVwb3J0aW5nRGF0YX0pXG4gKiBoYXJkLWRlbGV0ZXMgcm93cyBvbGRlciB0aGFuIHRoZXNlIHdpbmRvd3MsIGJ5IGBjcmVhdGVkX2F0YDpcbiAqICAtICoqU3RhbmRhcmQgZXZlbnRzKiog4oCUIGBwaXBlbGluZV9ldmVudHNgIHdpdGggYGVudmlyb25tZW50IElTIE5VTExgXG4gKiAgICAobm9uLWRlcGxveSBTVEFHRS9BQ1RJT04vYnVpbGQpLiBIaWdoIHZvbHVtZSDihpIgc2hvcnQgZGVmYXVsdCAoMzAgZGF5cykuXG4gKiAgLSAqKkRPUkEgc291cmNlKiog4oCUIGBwaXBlbGluZV9ldmVudHNgIHdpdGggYGVudmlyb25tZW50IElTIE5PVCBOVUxMYCAoZGVwbG95XG4gKiAgICBzdGFnZXMpIHBsdXMgYWxsIG9mIGBkZXBsb3ltZW50X291dGNvbWVzYCBhbmQgYGluY2lkZW50c2AuIExvdyB2b2x1bWUg4oaSXG4gKiAgICBsb25nZXIgZGVmYXVsdCAoMTgwIGRheXMpLiBET1JBIGhpc3RvcnkgaXMgdGhlcmVmb3JlIGJvdW5kZWQgYnkgdGhpcyB3aW5kb3c7XG4gKiAgICB0aGUgcmVwb3J0IHF1ZXJ5IHN0aWxsIGhhcmQtY2FwcyBhdCAzNjUgZGF5cyAoYSBsb25nZXIgcGVyLW9yZyBvdmVycmlkZSBvbmx5XG4gKiAgICByZXRhaW5zIHJhdyBzb3VyY2Ugcm93cywgbmV2ZXIgd2lkZW5zIGEgcmVwb3J0KS5cbiAqIGBpbmdlc3RfaGVhbHRoYCBhbmQgYGRvcmFfc2V0dGluZ3NgIGFyZSBuZXZlciBwdXJnZWQuIEVudi1vdmVycmlkYWJsZSBnbG9iYWxzO1xuICogYSBwZXItb3JnIG92ZXJyaWRlIGluIGBkb3JhX3NldHRpbmdzYCB3aW5zIChzZWUge0BsaW5rIHJlc29sdmVFdmVudFJldGVudGlvbkRheXN9KS5cbiAqL1xuXG4vKiogUGFyc2UgYSBwb3NpdGl2ZS1pbnQgZW52IGRheS1jb3VudCwgY2xhbXBlZCB0byBbTUlOLCBNQVhdLCBlbHNlIGBmYWxsYmFja2AuICovXG5leHBvcnQgZnVuY3Rpb24gcmV0ZW50aW9uRW52RGF5cyhuYW1lOiBzdHJpbmcsIGZhbGxiYWNrOiBudW1iZXIpOiBudW1iZXIge1xuICBjb25zdCBkYXlzID0gZW52SW50KG5hbWUsIGZhbGxiYWNrKTtcbiAgcmV0dXJuIGRheXMgPCBSRVRFTlRJT05fTUlOX0RBWVMgPyBmYWxsYmFjayA6IE1hdGgubWluKGRheXMsIFJFVEVOVElPTl9NQVhfREFZUyk7XG59XG5cbmV4cG9ydCBjb25zdCBSRVBPUlRJTkdfRVZFTlRfUkVURU5USU9OX0RBWVMgPSByZXRlbnRpb25FbnZEYXlzKCdSRVBPUlRJTkdfRVZFTlRfUkVURU5USU9OX0RBWVMnLCAzMCk7XG5leHBvcnQgY29uc3QgUkVQT1JUSU5HX0RPUkFfUkVURU5USU9OX0RBWVMgPSByZXRlbnRpb25FbnZEYXlzKCdSRVBPUlRJTkdfRE9SQV9SRVRFTlRJT05fREFZUycsIDE4MCk7XG5cbi8qKiBFZmZlY3RpdmUgc3RhbmRhcmQtZXZlbnQgcmV0ZW50aW9uIChkYXlzKTogYSB2YWxpZCBwZXItb3JnIG92ZXJyaWRlIHdpbnMsXG4gKiAgZWxzZSB0aGUgZ2xvYmFsIGVudiBkZWZhdWx0LiBTaGFyZWQgYnkgdGhlIHN3ZWVwICsgdGhlIHNldHRpbmdzIHN1cmZhY2Ugc29cbiAqICB0aGV5IGNhbid0IGRyaWZ0LiBUaGUgYC0xYCB1bmxpbWl0ZWQgc2VudGluZWwgcGFzc2VzIHN0cmFpZ2h0XG4gKiAgdGhyb3VnaCDigJQgdGhlIHN3ZWVwIHJlYWRzIGl0IGFzIFwia2VlcCBmb3JldmVyLCBza2lwIHRoaXMgb3JnJ3Mgc3RhbmRhcmRcbiAqICBldmVudHNcIi4gQSBwb3NpdGl2ZSBvdmVycmlkZSBjbGFtcHMgdG8gWzEsIFJFVEVOVElPTl9NQVhfREFZU107IGEgYG51bGxgLCBhXG4gKiAgbm9uLWludGVnZXIsIG9yIGFueSBvdGhlciBvdXQtb2YtcmFuZ2UgdmFsdWUgKGUuZy4gMCwgLTIpIGZhbGxzIGJhY2sgdG8gdGhlXG4gKiAgZW52IGRlZmF1bHQuICovXG5leHBvcnQgZnVuY3Rpb24gcmVzb2x2ZUV2ZW50UmV0ZW50aW9uRGF5cyhvdmVycmlkZT86IG51bWJlciB8IG51bGwpOiBudW1iZXIge1xuICBpZiAob3ZlcnJpZGUgPT09IC0xKSByZXR1cm4gLTE7XG4gIHJldHVybiBvdmVycmlkZSAhPSBudWxsICYmIE51bWJlci5pc0ludGVnZXIob3ZlcnJpZGUpICYmIG92ZXJyaWRlID49IFJFVEVOVElPTl9NSU5fREFZU1xuICAgID8gTWF0aC5taW4ob3ZlcnJpZGUsIFJFVEVOVElPTl9NQVhfREFZUykgOiBSRVBPUlRJTkdfRVZFTlRfUkVURU5USU9OX0RBWVM7XG59XG5cbi8qKiBFZmZlY3RpdmUgRE9SQS1zb3VyY2UgcmV0ZW50aW9uIChkYXlzKTogYSB2YWxpZCBwZXItb3JnIG92ZXJyaWRlIHdpbnMsIGVsc2VcbiAqICB0aGUgZ2xvYmFsIGVudiBkZWZhdWx0LiBUaGUgYC0xYCB1bmxpbWl0ZWQgc2VudGluZWwgcGFzc2VzIHN0cmFpZ2h0XG4gKiAgdGhyb3VnaCDigJQgdGhlIHN3ZWVwIHJlYWRzIGl0IGFzIFwia2VlcCBmb3JldmVyLCBza2lwIHRoaXMgb3JnJ3MgRE9SQS1zb3VyY2VcbiAqICB3aW5kb3dzXCIuIEEgcG9zaXRpdmUgb3ZlcnJpZGUgY2xhbXBzIHRvIFsxLCBSRVRFTlRJT05fTUFYX0RBWVNdOyBhIGBudWxsYCwgYVxuICogIG5vbi1pbnRlZ2VyLCBvciBhbnkgb3RoZXIgb3V0LW9mLXJhbmdlIHZhbHVlIChlLmcuIDAsIC0yKSBmYWxscyBiYWNrIHRvIHRoZVxuICogIGVudiBkZWZhdWx0LiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHJlc29sdmVEb3JhUmV0ZW50aW9uRGF5cyhvdmVycmlkZT86IG51bWJlciB8IG51bGwpOiBudW1iZXIge1xuICBpZiAob3ZlcnJpZGUgPT09IC0xKSByZXR1cm4gLTE7XG4gIHJldHVybiBvdmVycmlkZSAhPSBudWxsICYmIE51bWJlci5pc0ludGVnZXIob3ZlcnJpZGUpICYmIG92ZXJyaWRlID49IFJFVEVOVElPTl9NSU5fREFZU1xuICAgID8gTWF0aC5taW4ob3ZlcnJpZGUsIFJFVEVOVElPTl9NQVhfREFZUykgOiBSRVBPUlRJTkdfRE9SQV9SRVRFTlRJT05fREFZUztcbn1cblxuLyoqIFRoZSBjdXRvZmYgaW5zdGFudCBmb3IgYSByZXRlbnRpb24gd2luZG93OiByb3dzIHdpdGggYGNyZWF0ZWRfYXRgIHN0cmljdGx5XG4gKiAgYmVmb3JlIHRoaXMgKGA8IGN1dG9mZmApIGFyZSBleHBpcmVkLiBEZXRlcm1pbmlzdGljIGZvciBhIGZpeGVkIGBub3dgLiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHJldGVudGlvbkN1dG9mZihub3c6IERhdGUsIGRheXM6IG51bWJlcik6IERhdGUge1xuICByZXR1cm4gbmV3IERhdGUobm93LmdldFRpbWUoKSAtIGRheXMgKiA4Nl80MDBfMDAwKTtcbn1cblxuLyoqIFBlci1vcmcgcmV0ZW50aW9uIG92ZXJyaWRlIHJvdy4gQm90aCBudWxsYWJsZSDih5IgZ2xvYmFsIGRlZmF1bHRzLiAqL1xuIl19
|
|
@@ -15,11 +15,6 @@ export declare function assertReportInterval(interval: string): void;
|
|
|
15
15
|
* string field is funneled through this before insert.
|
|
16
16
|
*/
|
|
17
17
|
export declare function scrubOptional(value: string | undefined): string | undefined;
|
|
18
|
-
/**
|
|
19
|
-
* Cache for reporting aggregations. Two tiers:
|
|
20
|
-
* - Inventory queries (plugin summary/distribution/versions): 5 min TTL — changes on plugin CRUD
|
|
21
|
-
* - Execution/build queries with date ranges: 2 min TTL — new events arrive continuously
|
|
22
|
-
*/
|
|
23
18
|
/**
|
|
24
19
|
* Roll a group of deploy/stage events (grouped per execution) up to one terminal
|
|
25
20
|
* status: FAILED wins, then SUCCEEDED, else OTHER (still-running / non-terminal).
|
|
@@ -28,6 +23,8 @@ export declare function scrubOptional(value: string | undefined): string | undef
|
|
|
28
23
|
* grouped event alias is `e`.
|
|
29
24
|
*/
|
|
30
25
|
export declare const terminalStatusRollup: import("drizzle-orm").SQL<unknown>;
|
|
26
|
+
/** The report window on `e.started_at` (inclusive `[from, to]`). */
|
|
27
|
+
export declare function startedAtWindow(from: string, to: string): ReturnType<typeof sql>;
|
|
31
28
|
/** Optional `[from,to]` window on `e.started_at` for the execution reports —
|
|
32
29
|
* a no-op `sql` fragment when either bound is absent (all-time). Shared by the
|
|
33
30
|
* execution-count and per-pipeline-execution reports so they narrow identically. */
|
|
@@ -36,10 +33,10 @@ export declare function optionalStartedAtRange(range?: {
|
|
|
36
33
|
to?: string;
|
|
37
34
|
}): ReturnType<typeof sql>;
|
|
38
35
|
/**
|
|
39
|
-
* Per-execution terminal deploy row (
|
|
36
|
+
* Per-execution terminal deploy row (one row per (environment, execution),
|
|
40
37
|
* NOT per stage). The SQL rolls every deploy STAGE targeting an env within an
|
|
41
38
|
* execution up to a single terminal status; the JS shaping buckets these by
|
|
42
|
-
* environment. `commit_ts`
|
|
39
|
+
* environment. `commit_ts` is the execution's EARLIEST commit time across
|
|
43
40
|
* all its events — commit enrichment rides the PIPELINE/source event, not the
|
|
44
41
|
* deploy STAGE — joined by execution_id (null when unresolved → lead `unknown`).
|
|
45
42
|
* `in_window` is false for a look-back-only row kept solely so incidents opened
|
|
@@ -27,11 +27,6 @@ export function assertReportInterval(interval) {
|
|
|
27
27
|
export function scrubOptional(value) {
|
|
28
28
|
return value !== undefined ? scrubAwsIdentifiersFromString(value) : undefined;
|
|
29
29
|
}
|
|
30
|
-
/**
|
|
31
|
-
* Cache for reporting aggregations. Two tiers:
|
|
32
|
-
* - Inventory queries (plugin summary/distribution/versions): 5 min TTL — changes on plugin CRUD
|
|
33
|
-
* - Execution/build queries with date ranges: 2 min TTL — new events arrive continuously
|
|
34
|
-
*/
|
|
35
30
|
/**
|
|
36
31
|
* Roll a group of deploy/stage events (grouped per execution) up to one terminal
|
|
37
32
|
* status: FAILED wins, then SUCCEEDED, else OTHER (still-running / non-terminal).
|
|
@@ -44,22 +39,26 @@ export const terminalStatusRollup = sql `CASE
|
|
|
44
39
|
WHEN bool_or(e.status = 'SUCCEEDED') THEN 'SUCCEEDED'
|
|
45
40
|
ELSE 'OTHER'
|
|
46
41
|
END`;
|
|
42
|
+
/** The report window on `e.started_at` (inclusive `[from, to]`). */
|
|
43
|
+
export function startedAtWindow(from, to) {
|
|
44
|
+
return sql `e.started_at >= ${from}::timestamptz AND e.started_at <= ${to}::timestamptz`;
|
|
45
|
+
}
|
|
47
46
|
/** Optional `[from,to]` window on `e.started_at` for the execution reports —
|
|
48
47
|
* a no-op `sql` fragment when either bound is absent (all-time). Shared by the
|
|
49
48
|
* execution-count and per-pipeline-execution reports so they narrow identically. */
|
|
50
49
|
export function optionalStartedAtRange(range) {
|
|
51
50
|
return range?.from && range?.to
|
|
52
|
-
? sql `AND
|
|
51
|
+
? sql `AND ${startedAtWindow(range.from, range.to)}`
|
|
53
52
|
: sql ``;
|
|
54
53
|
}
|
|
55
54
|
/**
|
|
56
|
-
* Per-execution terminal deploy row (
|
|
55
|
+
* Per-execution terminal deploy row (one row per (environment, execution),
|
|
57
56
|
* NOT per stage). The SQL rolls every deploy STAGE targeting an env within an
|
|
58
57
|
* execution up to a single terminal status; the JS shaping buckets these by
|
|
59
|
-
* environment. `commit_ts`
|
|
58
|
+
* environment. `commit_ts` is the execution's EARLIEST commit time across
|
|
60
59
|
* all its events — commit enrichment rides the PIPELINE/source event, not the
|
|
61
60
|
* deploy STAGE — joined by execution_id (null when unresolved → lead `unknown`).
|
|
62
61
|
* `in_window` is false for a look-back-only row kept solely so incidents opened
|
|
63
62
|
* near `from` can correlate to a deploy that completed just before the window.
|
|
64
63
|
*/
|
|
65
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
64
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3FsLWhlbHBlcnMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvYXBpL3JlcG9ydGluZy9zcWwtaGVscGVycy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwrQ0FBK0M7QUFDL0Msc0NBQXNDO0FBRXRDOztHQUVHO0FBRUgsT0FBTyxFQUFFLGdCQUFnQixFQUFFLDZCQUE2QixFQUFFLE1BQU0sNEJBQTRCLENBQUM7QUFDN0YsT0FBTyxFQUFFLEdBQUcsRUFBRSxNQUFNLGFBQWEsQ0FBQztBQUVsQzs7Ozs7OztHQU9HO0FBQ0gsTUFBTSxVQUFVLG9CQUFvQixDQUFDLFFBQWdCO0lBQ25ELElBQUksQ0FBRSxnQkFBc0MsQ0FBQyxRQUFRLENBQUMsUUFBUSxDQUFDLEVBQUUsQ0FBQztRQUNoRSxNQUFNLElBQUksS0FBSyxDQUFDLDRCQUE0QixRQUFRLHNCQUFzQixnQkFBZ0IsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxDQUFDO0lBQzNHLENBQUM7QUFDSCxDQUFDO0FBRUQ7Ozs7O0dBS0c7QUFDSCxNQUFNLFVBQVUsYUFBYSxDQUFDLEtBQXlCO0lBQ3JELE9BQU8sS0FBSyxLQUFLLFNBQVMsQ0FBQyxDQUFDLENBQUMsNkJBQTZCLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLFNBQVMsQ0FBQztBQUNoRixDQUFDO0FBRUQ7Ozs7OztHQU1HO0FBQ0gsTUFBTSxDQUFDLE1BQU0sb0JBQW9CLEdBQUcsR0FBRyxDQUFBOzs7O2dCQUl2QixDQUFDO0FBRWpCLG9FQUFvRTtBQUNwRSxNQUFNLFVBQVUsZUFBZSxDQUFDLElBQVksRUFBRSxFQUFVO0lBQ3RELE9BQU8sR0FBRyxDQUFBLG1CQUFtQixJQUFJLHFDQUFxQyxFQUFFLGVBQWUsQ0FBQztBQUMxRixDQUFDO0FBRUQ7O3FGQUVxRjtBQUNyRixNQUFNLFVBQVUsc0JBQXNCLENBQUMsS0FBc0M7SUFDM0UsT0FBTyxLQUFLLEVBQUUsSUFBSSxJQUFJLEtBQUssRUFBRSxFQUFFO1FBQzdCLENBQUMsQ0FBQyxHQUFHLENBQUEsT0FBTyxlQUFlLENBQUMsS0FBSyxDQUFDLElBQUksRUFBRSxLQUFLLENBQUMsRUFBRSxDQUFDLEVBQUU7UUFDbkQsQ0FBQyxDQUFDLEdBQUcsQ0FBQSxFQUFFLENBQUM7QUFDWixDQUFDO0FBRUQ7Ozs7Ozs7OztHQVNHIiwic291cmNlc0NvbnRlbnQiOlsiLy8gQ29weXJpZ2h0IDIwMjYgUGlwZWxpbmUgQnVpbGRlciBDb250cmlidXRvcnNcbi8vIFNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBBcGFjaGUtMi4wXG5cbi8qKlxuICogU21hbGwgU1FMIGZyYWdtZW50cyBhbmQgaW5wdXQgZ3VhcmRzIHNoYXJlZCBhY3Jvc3MgdGhlIHJlcG9ydGluZyBxdWVyaWVzLlxuICovXG5cbmltcG9ydCB7IFJFUE9SVF9JTlRFUlZBTFMsIHNjcnViQXdzSWRlbnRpZmllcnNGcm9tU3RyaW5nIH0gZnJvbSAnQHBpcGVsaW5lLWJ1aWxkZXIvYXBpLWNvcmUnO1xuaW1wb3J0IHsgc3FsIH0gZnJvbSAnZHJpenpsZS1vcm0nO1xuXG4vKipcbiAqIERlZmVuc2UtaW4tZGVwdGggZ3VhcmQgZm9yIHRoZSBgREFURV9UUlVOQygke2ludGVydmFsfSwg4oCmKWAgYnVja2V0IGFyZ3VtZW50LlxuICogVGhlIHJvdXRlIGxheWVyIChgcGFyc2VSZXBvcnRJbnRlcnZhbGApIGlzIHRoZSBzZWN1cml0eSBib3VuZGFyeSBhbmQgYWxyZWFkeVxuICogcmVqZWN0cyB1bmtub3duIGludGVydmFsczsgdGhpcyBhc3NlcnQgbWFrZXMgYW4gaW52YWxpZCB2YWx1ZSBmYWlsIGZhc3QgYXQgdGhlXG4gKiBzZXJ2aWNlIGJvdW5kYXJ5IHdpdGggYSBjbGVhciBlcnJvciBpbnN0ZWFkIG9mIHN1cmZhY2luZyBhIHJhdyBQb3N0Z3JlcyBlcnJvclxuICogaWYgYSBjYWxsZXIgZXZlciBieXBhc3NlcyB0aGUgcm91dGUgdmFsaWRhdGlvbi4gU2hhcmVzIGFwaS1jb3JlJ3NcbiAqIFJFUE9SVF9JTlRFUlZBTFMgYWxsb3ctbGlzdCBzbyB0aGUgdHdvIGNhbid0IGRyaWZ0LlxuICovXG5leHBvcnQgZnVuY3Rpb24gYXNzZXJ0UmVwb3J0SW50ZXJ2YWwoaW50ZXJ2YWw6IHN0cmluZyk6IHZvaWQge1xuICBpZiAoIShSRVBPUlRfSU5URVJWQUxTIGFzIHJlYWRvbmx5IHN0cmluZ1tdKS5pbmNsdWRlcyhpbnRlcnZhbCkpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoYEludmFsaWQgcmVwb3J0IGludGVydmFsOiAke2ludGVydmFsfS4gRXhwZWN0ZWQgb25lIG9mOiAke1JFUE9SVF9JTlRFUlZBTFMuam9pbignLCAnKX1gKTtcbiAgfVxufVxuXG4vKipcbiAqIFNjcnViIEFXUyBpZGVudGlmaWVycyBmcm9tIGFuIG9wdGlvbmFsIGZyZWUtZm9ybSBzdHJpbmcsIHByZXNlcnZpbmcgYHVuZGVmaW5lZGBcbiAqIChzbyBhbiBhYnNlbnQgZmllbGQgc3RheXMgYWJzZW50IHJhdGhlciB0aGFuIGJlY29taW5nIGFuIGVtcHR5IHNjcnViYmVkIHN0cmluZykuXG4gKiBUaGUgRFVSQUJMRSBwZXJzaXN0ZW5jZSBib3VuZGFyeSBmb3IgdGhlIGluZ2VzdCBwYXRocyDigJQgZXZlcnkgdXNlci9BV1MtZGVyaXZlZFxuICogc3RyaW5nIGZpZWxkIGlzIGZ1bm5lbGVkIHRocm91Z2ggdGhpcyBiZWZvcmUgaW5zZXJ0LlxuICovXG5leHBvcnQgZnVuY3Rpb24gc2NydWJPcHRpb25hbCh2YWx1ZTogc3RyaW5nIHwgdW5kZWZpbmVkKTogc3RyaW5nIHwgdW5kZWZpbmVkIHtcbiAgcmV0dXJuIHZhbHVlICE9PSB1bmRlZmluZWQgPyBzY3J1YkF3c0lkZW50aWZpZXJzRnJvbVN0cmluZyh2YWx1ZSkgOiB1bmRlZmluZWQ7XG59XG5cbi8qKlxuICogUm9sbCBhIGdyb3VwIG9mIGRlcGxveS9zdGFnZSBldmVudHMgKGdyb3VwZWQgcGVyIGV4ZWN1dGlvbikgdXAgdG8gb25lIHRlcm1pbmFsXG4gKiBzdGF0dXM6IEZBSUxFRCB3aW5zLCB0aGVuIFNVQ0NFRURFRCwgZWxzZSBPVEhFUiAoc3RpbGwtcnVubmluZyAvIG5vbi10ZXJtaW5hbCkuXG4gKiBTaGFyZWQgYnkgdGhlIHRocmVlIHBlci0oZW52fHN0YWdlLCBleGVjdXRpb24pIHJvbGx1cCBDVEVzIOKAlCBET1JBIG1ldHJpY3MsIERPUkFcbiAqIHRyZW5kLCBhbmQgYnVpbGQgaGVhbHRoIOKAlCBzbyB0aGUgcHJlY2VkZW5jZSBjYW4ndCBkcmlmdCBiZXR3ZWVuIHRoZW0uIEFzc3VtZXMgdGhlXG4gKiBncm91cGVkIGV2ZW50IGFsaWFzIGlzIGBlYC5cbiAqL1xuZXhwb3J0IGNvbnN0IHRlcm1pbmFsU3RhdHVzUm9sbHVwID0gc3FsYENBU0VcbiAgICAgICAgICAgICAgV0hFTiBib29sX29yKGUuc3RhdHVzID0gJ0ZBSUxFRCcpIFRIRU4gJ0ZBSUxFRCdcbiAgICAgICAgICAgICAgV0hFTiBib29sX29yKGUuc3RhdHVzID0gJ1NVQ0NFRURFRCcpIFRIRU4gJ1NVQ0NFRURFRCdcbiAgICAgICAgICAgICAgRUxTRSAnT1RIRVInXG4gICAgICAgICAgICBFTkRgO1xuXG4vKiogVGhlIHJlcG9ydCB3aW5kb3cgb24gYGUuc3RhcnRlZF9hdGAgKGluY2x1c2l2ZSBgW2Zyb20sIHRvXWApLiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHN0YXJ0ZWRBdFdpbmRvdyhmcm9tOiBzdHJpbmcsIHRvOiBzdHJpbmcpOiBSZXR1cm5UeXBlPHR5cGVvZiBzcWw+IHtcbiAgcmV0dXJuIHNxbGBlLnN0YXJ0ZWRfYXQgPj0gJHtmcm9tfTo6dGltZXN0YW1wdHogQU5EIGUuc3RhcnRlZF9hdCA8PSAke3RvfTo6dGltZXN0YW1wdHpgO1xufVxuXG4vKiogT3B0aW9uYWwgYFtmcm9tLHRvXWAgd2luZG93IG9uIGBlLnN0YXJ0ZWRfYXRgIGZvciB0aGUgZXhlY3V0aW9uIHJlcG9ydHMg4oCUXG4gKiAgYSBuby1vcCBgc3FsYCBmcmFnbWVudCB3aGVuIGVpdGhlciBib3VuZCBpcyBhYnNlbnQgKGFsbC10aW1lKS4gU2hhcmVkIGJ5IHRoZVxuICogIGV4ZWN1dGlvbi1jb3VudCBhbmQgcGVyLXBpcGVsaW5lLWV4ZWN1dGlvbiByZXBvcnRzIHNvIHRoZXkgbmFycm93IGlkZW50aWNhbGx5LiAqL1xuZXhwb3J0IGZ1bmN0aW9uIG9wdGlvbmFsU3RhcnRlZEF0UmFuZ2UocmFuZ2U/OiB7IGZyb20/OiBzdHJpbmc7IHRvPzogc3RyaW5nIH0pOiBSZXR1cm5UeXBlPHR5cGVvZiBzcWw+IHtcbiAgcmV0dXJuIHJhbmdlPy5mcm9tICYmIHJhbmdlPy50b1xuICAgID8gc3FsYEFORCAke3N0YXJ0ZWRBdFdpbmRvdyhyYW5nZS5mcm9tLCByYW5nZS50byl9YFxuICAgIDogc3FsYGA7XG59XG5cbi8qKlxuICogUGVyLWV4ZWN1dGlvbiB0ZXJtaW5hbCBkZXBsb3kgcm93IChvbmUgcm93IHBlciAoZW52aXJvbm1lbnQsIGV4ZWN1dGlvbiksXG4gKiBOT1QgcGVyIHN0YWdlKS4gVGhlIFNRTCByb2xscyBldmVyeSBkZXBsb3kgU1RBR0UgdGFyZ2V0aW5nIGFuIGVudiB3aXRoaW4gYW5cbiAqIGV4ZWN1dGlvbiB1cCB0byBhIHNpbmdsZSB0ZXJtaW5hbCBzdGF0dXM7IHRoZSBKUyBzaGFwaW5nIGJ1Y2tldHMgdGhlc2UgYnlcbiAqIGVudmlyb25tZW50LiBgY29tbWl0X3RzYCBpcyB0aGUgZXhlY3V0aW9uJ3MgRUFSTElFU1QgY29tbWl0IHRpbWUgYWNyb3NzXG4gKiBhbGwgaXRzIGV2ZW50cyDigJQgY29tbWl0IGVucmljaG1lbnQgcmlkZXMgdGhlIFBJUEVMSU5FL3NvdXJjZSBldmVudCwgbm90IHRoZVxuICogZGVwbG95IFNUQUdFIOKAlCBqb2luZWQgYnkgZXhlY3V0aW9uX2lkIChudWxsIHdoZW4gdW5yZXNvbHZlZCDihpIgbGVhZCBgdW5rbm93bmApLlxuICogYGluX3dpbmRvd2AgaXMgZmFsc2UgZm9yIGEgbG9vay1iYWNrLW9ubHkgcm93IGtlcHQgc29sZWx5IHNvIGluY2lkZW50cyBvcGVuZWRcbiAqIG5lYXIgYGZyb21gIGNhbiBjb3JyZWxhdGUgdG8gYSBkZXBsb3kgdGhhdCBjb21wbGV0ZWQganVzdCBiZWZvcmUgdGhlIHdpbmRvdy5cbiAqL1xuIl19
|
|
@@ -103,6 +103,37 @@ export interface BuildDuration {
|
|
|
103
103
|
maxMs: number;
|
|
104
104
|
builds: number;
|
|
105
105
|
}
|
|
106
|
+
/**
|
|
107
|
+
* Plugin runtime report filters. `publisher: null` selects own-org
|
|
108
|
+
* (publisher-less) plugins; an omitted field doesn't filter.
|
|
109
|
+
*/
|
|
110
|
+
export interface PluginRuntimeFilter {
|
|
111
|
+
name?: string;
|
|
112
|
+
publisher?: string | null;
|
|
113
|
+
version?: string;
|
|
114
|
+
}
|
|
115
|
+
/** Per plugin version runtime stats over a window (terminal ACTION runs). */
|
|
116
|
+
export interface PluginRuntimeStats {
|
|
117
|
+
pluginPublisher: string | null;
|
|
118
|
+
pluginName: string;
|
|
119
|
+
pluginVersion: string;
|
|
120
|
+
runs: number;
|
|
121
|
+
succeeded: number;
|
|
122
|
+
failed: number;
|
|
123
|
+
/** 0–100, one decimal. */
|
|
124
|
+
successPct: number;
|
|
125
|
+
/** Null when no run in the group carried a duration. */
|
|
126
|
+
p50Ms: number | null;
|
|
127
|
+
p95Ms: number | null;
|
|
128
|
+
lastRun: string;
|
|
129
|
+
}
|
|
130
|
+
/** Cross-org 30-day aggregate for one `(publisher, name)` listing. */
|
|
131
|
+
export interface PluginRuntimeAggregate {
|
|
132
|
+
runs30d: number;
|
|
133
|
+
/** 0–1; null when there were no runs. */
|
|
134
|
+
successRate30d: number | null;
|
|
135
|
+
activeOrgCount30d: number;
|
|
136
|
+
}
|
|
106
137
|
export interface BuildFailure {
|
|
107
138
|
pluginName: string;
|
|
108
139
|
errorMessage: string;
|
|
@@ -114,7 +145,7 @@ export interface BuildFailure {
|
|
|
114
145
|
* industry-standard DORA signal; MTTR is measured production-only, and this
|
|
115
146
|
* environment's card is the summary shown at the top of the panel.
|
|
116
147
|
*/
|
|
117
|
-
/** Per-org retention override row
|
|
148
|
+
/** Per-org retention override row. Both nullable ⇒ global defaults. */
|
|
118
149
|
export interface ReportingRetentionSettings {
|
|
119
150
|
eventRetentionDays: number | null;
|
|
120
151
|
doraRetentionDays: number | null;
|
|
@@ -130,6 +161,15 @@ export interface ReportingRetentionOptions {
|
|
|
130
161
|
maxBatchesPerTable?: number;
|
|
131
162
|
/** Fixed "now" for the whole sweep (default `new Date()`). */
|
|
132
163
|
now?: Date;
|
|
164
|
+
/**
|
|
165
|
+
* Resolve the org whose `dora_settings` retention governs `orgId`'s rows — its
|
|
166
|
+
* account ROOT. Retention is a billing entitlement synced onto the root only,
|
|
167
|
+
* so a team's rows must follow the root's (purchased) window, not the env
|
|
168
|
+
* default. Return `null` when the root can't be resolved: that org is SKIPPED
|
|
169
|
+
* this tick (never purged on a possibly-shorter window). Default: identity
|
|
170
|
+
* (every org is its own root — a flat deployment).
|
|
171
|
+
*/
|
|
172
|
+
resolveRetentionOrgId?: (orgId: string) => Promise<string | null>;
|
|
133
173
|
}
|
|
134
174
|
/** Per-run purge tallies (also logged). */
|
|
135
175
|
export interface ReportingRetentionCounts {
|
|
@@ -160,32 +200,32 @@ export interface DoraOptions {
|
|
|
160
200
|
* Per-org incident→deploy correlation window override (hours). When set (a
|
|
161
201
|
* positive finite number), the incident correlation uses it instead of the
|
|
162
202
|
* global `DORA_INCIDENT_WINDOW_HOURS`. The route resolves this from the org's
|
|
163
|
-
* `dora_settings` row (see {@link ReportingService.
|
|
203
|
+
* `dora_settings` row (see {@link ReportingService.getReportingSettings}) and
|
|
164
204
|
* passes it in; unset falls back to the env default.
|
|
165
205
|
*/
|
|
166
206
|
incidentWindowHours?: number;
|
|
167
207
|
}
|
|
168
208
|
/**
|
|
169
209
|
* Per-org reporting settings surfaced to the org-admin panel. `incidentWindowHours`
|
|
170
|
-
*
|
|
171
|
-
* `doraRetentionDays`
|
|
210
|
+
* is the incident→deploy correlation window; `eventRetentionDays` /
|
|
211
|
+
* `doraRetentionDays` are the two retention-sweep windows. Each override
|
|
172
212
|
* is `null` when unset (the paired `default*` field shows the env fallback applied).
|
|
173
213
|
*/
|
|
174
|
-
export interface
|
|
214
|
+
export interface ReportingSettings {
|
|
175
215
|
/** The org's stored correlation-window override in hours, or `null` when unset. */
|
|
176
216
|
incidentWindowHours: number | null;
|
|
177
217
|
/** The global env default applied when no override is stored. */
|
|
178
218
|
defaultWindowHours: number;
|
|
179
|
-
/** Standard-event retention override in days, or `null` when unset
|
|
219
|
+
/** Standard-event retention override in days, or `null` when unset. */
|
|
180
220
|
eventRetentionDays: number | null;
|
|
181
|
-
/** DORA-source retention override in days, or `null` when unset
|
|
221
|
+
/** DORA-source retention override in days, or `null` when unset. */
|
|
182
222
|
doraRetentionDays: number | null;
|
|
183
223
|
/** Global standard-event retention default applied when unset (days). */
|
|
184
224
|
defaultEventRetentionDays: number;
|
|
185
225
|
/** Global DORA-source retention default applied when unset (days). */
|
|
186
226
|
defaultDoraRetentionDays: number;
|
|
187
227
|
}
|
|
188
|
-
/** A partial reporting-settings write (
|
|
228
|
+
/** A partial reporting-settings write (7). Only provided fields are
|
|
189
229
|
* upserted; omitted fields are left unchanged (an omitted retention field keeps
|
|
190
230
|
* its stored override / the global default). */
|
|
191
231
|
export interface ReportingSettingsPatch {
|
|
@@ -327,7 +367,7 @@ export interface DoraTrendPoint {
|
|
|
327
367
|
/** failed/total as a percent (0 when total is 0). Deploy-time CFR only. */
|
|
328
368
|
changeFailurePct: number;
|
|
329
369
|
}
|
|
330
|
-
/** Per-stage build-health metrics
|
|
370
|
+
/** Per-stage build-health metrics for one pipeline over a window. */
|
|
331
371
|
export interface BuildHealthStage {
|
|
332
372
|
stage: string;
|
|
333
373
|
/** Terminal stage runs (succeeded + failed) in the window. */
|
|
@@ -342,7 +382,7 @@ export interface BuildHealthStage {
|
|
|
342
382
|
p99Ms: number | null;
|
|
343
383
|
}
|
|
344
384
|
/**
|
|
345
|
-
* Per-pipeline build-health breakdown
|
|
385
|
+
* Per-pipeline build-health breakdown. Standard reporting (NOT
|
|
346
386
|
* `advanced_reporting`-gated) — per-stage success rate + timing percentiles, with
|
|
347
387
|
* totals summed across stages.
|
|
348
388
|
*/
|
|
@@ -354,7 +394,7 @@ export interface BuildHealth {
|
|
|
354
394
|
failureRate: number;
|
|
355
395
|
};
|
|
356
396
|
}
|
|
357
|
-
/** Post-deploy incident marker
|
|
397
|
+
/** Post-deploy incident marker accepted by `ReportingService.recordIncident`. */
|
|
358
398
|
export interface IncidentInput {
|
|
359
399
|
incidentId: string;
|
|
360
400
|
environment: string;
|
|
@@ -414,16 +454,14 @@ export interface IngestResult {
|
|
|
414
454
|
* Every org with at least one row in this batch, deduped.
|
|
415
455
|
*
|
|
416
456
|
* Already computed for post-commit cache invalidation; surfaced because the
|
|
417
|
-
* ingest route needs the same set to push its live SSE frame.
|
|
418
|
-
*
|
|
419
|
-
*
|
|
420
|
-
* frame at all, and the dashboard silently went back to needing a manual
|
|
421
|
-
* refresh for exactly the events an execution view is about.
|
|
457
|
+
* ingest route needs the same set to push its live SSE frame. The stage-metric
|
|
458
|
+
* hook can't drive that fan-out: it only fires for STAGE events, so a batch of
|
|
459
|
+
* PIPELINE or BUILD events would land rows and push no frame at all.
|
|
422
460
|
*/
|
|
423
461
|
affectedOrgs: string[];
|
|
424
462
|
}
|
|
425
463
|
/**
|
|
426
|
-
* One org's ingestion-health row as READ back by the Reports UI
|
|
464
|
+
* One org's ingestion-health row as READ back by the Reports UI. The
|
|
427
465
|
* AWS events Lambda writes it; this is the shape that lets the UI tell
|
|
428
466
|
* "ingestion is healthy, there were simply no deploys in the range" apart from
|
|
429
467
|
* "we haven't heard from the ingest pipeline since X".
|