mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-11 14:00:29 +00:00
Hide monitored system wire aliases from client contract
This commit is contained in:
@@ -256,8 +256,9 @@ That freshest grouped observation is now canonically exposed as the structured
|
||||
`latest_included_signal` object. Its `at`, `source`, `name`, and `type` fields
|
||||
identify exactly which included top-level surface reported most recently.
|
||||
`latest_included_signal_at`, `latest_included_signal_source`, and `last_seen`
|
||||
remain rollout compatibility fields only; consumers should treat the object as
|
||||
the primary contract and preserve its meaning in presentation.
|
||||
remain rollout compatibility fields only on the raw wire shape; normalized
|
||||
frontend clients should treat the object as the primary contract and must not
|
||||
re-export those flat aliases in their public response model.
|
||||
That client contract must also fail closed when older or partial payloads omit
|
||||
the nested explanation object: the frontend may normalize missing explanation
|
||||
fields to empty reasons/surfaces plus a safe default summary, but it must not
|
||||
|
||||
@@ -225,17 +225,19 @@ That same settings surface must also label the monitored-system signal by its
|
||||
real meaning. The canonical API shape is now the structured
|
||||
`latest_included_signal` object, while `latest_included_signal_at`,
|
||||
`latest_included_signal_source`, and `last_seen` are rollout compatibility
|
||||
fields only. It represents the freshest included grouped observation, not a
|
||||
guarantee that every grouped source is healthy, so the UI must not present it
|
||||
with single-source `Last Seen` wording. When the canonical object is present,
|
||||
the surface should use its source/name/type attribution instead of showing an
|
||||
unqualified aggregate timestamp, and that attribution should stay
|
||||
customer-facing rather than exposing raw monitored-system type/source slugs in
|
||||
the settings table or included-surface details. When the row expands to show
|
||||
status reasoning, it should also restate the freshest included surface and
|
||||
timestamp there so a degraded reason and a fresher grouped signal remain
|
||||
readable together. When mixed-version payloads omit that canonical freshest
|
||||
signal entirely, the settings surface should degrade to a safe customer-facing
|
||||
fields only on the raw response payload. The normalized frontend client
|
||||
contract should expose only the canonical object. It represents the freshest
|
||||
included grouped observation, not a guarantee that every grouped source is
|
||||
healthy, so the UI must not present it with single-source `Last Seen` wording.
|
||||
When the canonical object is present, the surface should use its
|
||||
source/name/type attribution instead of showing an unqualified aggregate
|
||||
timestamp, and that attribution should stay customer-facing rather than
|
||||
exposing raw monitored-system type/source slugs in the settings table or
|
||||
included-surface details. When the row expands to show status reasoning, it
|
||||
should also restate the freshest included surface and timestamp there so a
|
||||
degraded reason and a fresher grouped signal remain readable together. When
|
||||
mixed-version payloads omit that canonical freshest signal entirely, the
|
||||
settings surface should degrade to a safe customer-facing
|
||||
fallback instead of an unexplained placeholder glyph.
|
||||
That same billing support boundary now also owns the shared monitored-system
|
||||
presentation helper. `frontend-modern/src/utils/monitoredSystemPresentation.ts`
|
||||
|
||||
@@ -196,14 +196,15 @@ describe('MonitoredSystemLedgerAPI', () => {
|
||||
|
||||
const result = await MonitoredSystemLedgerAPI.getLedger();
|
||||
|
||||
expect(result.systems[0]?.latest_included_signal_at).toBe('2026-03-23T11:59:50Z');
|
||||
expect(result.systems[0]?.latest_included_signal_source).toBe('docker');
|
||||
expect(result.systems[0]?.latest_included_signal).toEqual({
|
||||
name: 'tower.local',
|
||||
type: 'docker-host',
|
||||
source: 'docker',
|
||||
at: '2026-03-23T11:59:50Z',
|
||||
});
|
||||
expect(result.systems[0]).not.toHaveProperty('latest_included_signal_at');
|
||||
expect(result.systems[0]).not.toHaveProperty('latest_included_signal_source');
|
||||
expect(result.systems[0]).not.toHaveProperty('last_seen');
|
||||
});
|
||||
|
||||
it('falls back to legacy latest signal fields for older payloads', async () => {
|
||||
@@ -223,14 +224,15 @@ describe('MonitoredSystemLedgerAPI', () => {
|
||||
|
||||
const result = await MonitoredSystemLedgerAPI.getLedger();
|
||||
|
||||
expect(result.systems[0]?.latest_included_signal_at).toBe('2026-03-23T11:59:50Z');
|
||||
expect(result.systems[0]?.latest_included_signal_source).toBeUndefined();
|
||||
expect(result.systems[0]?.latest_included_signal).toEqual({
|
||||
name: 'Tower',
|
||||
type: 'host',
|
||||
source: undefined,
|
||||
at: '2026-03-23T11:59:50Z',
|
||||
});
|
||||
expect(result.systems[0]).not.toHaveProperty('latest_included_signal_at');
|
||||
expect(result.systems[0]).not.toHaveProperty('latest_included_signal_source');
|
||||
expect(result.systems[0]).not.toHaveProperty('last_seen');
|
||||
});
|
||||
|
||||
it('preserves canonical status explanation reasons from the API contract', async () => {
|
||||
|
||||
@@ -51,9 +51,6 @@ export interface MonitoredSystemLedgerEntry {
|
||||
status: MonitoredSystemLedgerStatus;
|
||||
status_explanation: MonitoredSystemLedgerStatusExplanation;
|
||||
latest_included_signal: MonitoredSystemLedgerLatestSignal;
|
||||
latest_included_signal_at: string; // freshest included observation, RFC3339 or empty
|
||||
latest_included_signal_source?: string;
|
||||
last_seen?: string; // deprecated compatibility alias
|
||||
source: string;
|
||||
explanation: MonitoredSystemLedgerExplanation;
|
||||
}
|
||||
@@ -75,6 +72,9 @@ type MonitoredSystemLedgerRawEntry = Omit<
|
||||
MonitoredSystemLedgerEntry,
|
||||
'status_explanation' | 'latest_included_signal' | 'explanation'
|
||||
> & {
|
||||
latest_included_signal_at?: string; // freshest included observation, RFC3339 or empty
|
||||
latest_included_signal_source?: string;
|
||||
last_seen?: string; // deprecated compatibility alias
|
||||
status_explanation?: MonitoredSystemLedgerStatusExplanation;
|
||||
latest_included_signal?: MonitoredSystemLedgerLatestSignal;
|
||||
explanation?: MonitoredSystemLedgerExplanation;
|
||||
@@ -106,13 +106,11 @@ function normalizeMonitoredSystemLedgerEntry(
|
||||
entry,
|
||||
);
|
||||
return {
|
||||
...entry,
|
||||
name: entry.name,
|
||||
type: entry.type,
|
||||
status,
|
||||
source: entry.source,
|
||||
latest_included_signal: latestIncludedSignal,
|
||||
latest_included_signal_at: latestIncludedSignal.at,
|
||||
latest_included_signal_source: normalizeMonitoredSystemLedgerSource(
|
||||
latestIncludedSignal.source,
|
||||
),
|
||||
status_explanation: {
|
||||
summary: entry.status_explanation?.summary ?? getMonitoredSystemStatusFallbackSummary(status),
|
||||
reasons: (entry.status_explanation?.reasons ?? []).map(normalizeMonitoredSystemLedgerStatusReason),
|
||||
|
||||
@@ -85,9 +85,6 @@ describe('MonitoredSystemLedgerPanel', () => {
|
||||
source: 'agent',
|
||||
at: '2026-01-01T00:00:00Z',
|
||||
},
|
||||
latest_included_signal_at: '2026-01-01T00:00:00Z',
|
||||
latest_included_signal_source: 'agent',
|
||||
last_seen: '2026-01-01T00:00:00Z',
|
||||
source: 'agent',
|
||||
explanation: {
|
||||
summary:
|
||||
@@ -156,9 +153,6 @@ describe('MonitoredSystemLedgerPanel', () => {
|
||||
source: 'agent',
|
||||
at: '2026-01-01T00:00:00Z',
|
||||
},
|
||||
latest_included_signal_at: '2026-01-01T00:00:00Z',
|
||||
latest_included_signal_source: 'agent',
|
||||
last_seen: '2026-01-01T00:00:00Z',
|
||||
source: 'agent',
|
||||
explanation: {
|
||||
summary:
|
||||
@@ -199,9 +193,6 @@ describe('MonitoredSystemLedgerPanel', () => {
|
||||
source: 'pbs',
|
||||
at: '2026-01-02T00:00:00Z',
|
||||
},
|
||||
latest_included_signal_at: '2026-01-02T00:00:00Z',
|
||||
latest_included_signal_source: 'pbs',
|
||||
last_seen: '2026-01-02T00:00:00Z',
|
||||
source: 'pbs',
|
||||
explanation: {
|
||||
summary:
|
||||
@@ -303,9 +294,6 @@ describe('MonitoredSystemLedgerPanel', () => {
|
||||
source: 'agent',
|
||||
at: '2026-01-01T00:00:00Z',
|
||||
},
|
||||
latest_included_signal_at: '2026-01-01T00:00:00Z',
|
||||
latest_included_signal_source: 'agent',
|
||||
last_seen: '2026-01-01T00:00:00Z',
|
||||
source: 'agent',
|
||||
},
|
||||
],
|
||||
@@ -352,8 +340,6 @@ describe('MonitoredSystemLedgerPanel', () => {
|
||||
source: 'agent',
|
||||
at: '',
|
||||
},
|
||||
latest_included_signal_at: '',
|
||||
latest_included_signal_source: 'agent',
|
||||
source: 'agent',
|
||||
},
|
||||
],
|
||||
@@ -393,9 +379,6 @@ describe('MonitoredSystemLedgerPanel', () => {
|
||||
source: 'agent',
|
||||
at: '2026-01-01T00:00:00Z',
|
||||
},
|
||||
latest_included_signal_at: '2026-01-01T00:00:00Z',
|
||||
latest_included_signal_source: 'agent',
|
||||
last_seen: '2026-01-01T00:00:00Z',
|
||||
source: 'agent',
|
||||
},
|
||||
],
|
||||
|
||||
+5
@@ -252,6 +252,11 @@ describe('monitored-system model guardrails', () => {
|
||||
expect(monitoredSystemLedgerApiSource).toContain('getMonitoredSystemExplanationFallbackSummary');
|
||||
expect(monitoredSystemLedgerApiSource).toContain('type MonitoredSystemLedgerRawEntry =');
|
||||
expect(monitoredSystemLedgerApiSource).toContain('systems?: MonitoredSystemLedgerRawEntry[];');
|
||||
expect(monitoredSystemLedgerApiSource).toContain('latest_included_signal_at?: string;');
|
||||
expect(monitoredSystemLedgerApiSource).toContain('last_seen?: string;');
|
||||
expect(monitoredSystemLedgerApiSource).not.toContain(
|
||||
'latest_included_signal_at: string; // freshest included observation, RFC3339 or empty',
|
||||
);
|
||||
expect(monitoredSystemLedgerApiSource).not.toContain(
|
||||
'All included top-level collection paths currently report online status.',
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user