@oneuptime/common 12.0.12 → 12.0.13
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/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.ts +32 -5
- package/Models/DatabaseModels/NetworkDevice.ts +64 -0
- package/Models/DatabaseModels/StatusPage.ts +59 -0
- package/Models/DatabaseModels/StatusPageOidc.ts +2 -0
- package/Models/DatabaseModels/WorkspaceProjectAuthToken.ts +19 -2
- package/Server/API/AIChatAPI.ts +55 -56
- package/Server/API/CommonAPI.ts +89 -0
- package/Server/API/MicrosoftTeamsAPI.ts +20 -9
- package/Server/API/SlackAPI.ts +13 -10
- package/Server/API/StatusPageAPI.ts +2197 -2128
- package/Server/API/TelemetryAPI.ts +72 -3
- package/Server/Infrastructure/Postgres/SchemaMigrations/1787500000000-AddEnableSearchEngineIndexingToStatusPage.ts +30 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1787600000000-AddNetworkDeviceReachabilityColumns.ts +47 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +4 -0
- package/Server/Middleware/UserAuthorization.ts +11 -2
- package/Server/Services/ApiKeyPermissionService.ts +116 -2
- package/Server/Services/DatabaseService.ts +17 -6
- package/Server/Services/LogAggregationService.ts +34 -0
- package/Server/Services/LogService.ts +21 -0
- package/Server/Services/NetworkSiteService.ts +12 -0
- package/Server/Services/SpanService.ts +21 -0
- package/Server/Services/TraceAggregationService.ts +15 -0
- package/Server/Services/WorkspaceNotificationRuleService.ts +172 -220
- package/Server/Types/AnalyticsDatabase/ModelPermission.ts +9 -0
- package/Server/Types/Database/Permissions/AccessControlPermission.ts +47 -16
- package/Server/Utils/APIKey/AccessPermission.ts +16 -40
- package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +144 -0
- package/Server/Utils/LogRedaction.ts +576 -0
- package/Server/Utils/Logger.ts +123 -46
- package/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.ts +94 -23
- package/Server/Utils/Monitor/MonitorResource.ts +29 -9
- package/Server/Utils/Monitor/NetworkInventoryUtil.ts +28 -7
- package/Server/Utils/SessionReplay/SessionReplayGateCacheStore.ts +56 -10
- package/Server/Utils/StartServer.ts +30 -9
- package/Server/Utils/StatusPageSearchEngineIndexing.ts +33 -0
- package/Server/Utils/Telemetry/ResourceEntityFilter.ts +441 -0
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +488 -46
- package/Tests/App/Dashboard/MonitorTypePicker.test.tsx +300 -0
- package/Tests/App/StatusPage/StatusPageIndexPageRobotsMeta.test.ts +130 -0
- package/Tests/Models/DatabaseModels/DatabaseBaseModelToJSONObjectCache.test.ts +180 -0
- package/Tests/Models/StatusPageEnableSearchEngineIndexing.test.ts +157 -0
- package/Tests/Server/API/AIChatCancelAndFeedback.test.ts +23 -1
- package/Tests/Server/API/AIChatRouteAuthorization.test.ts +785 -0
- package/Tests/Server/API/BaseAPIApiKeyAuth.test.ts +21 -8
- package/Tests/Server/API/CommonAPIAuthGuard.test.ts +290 -0
- package/Tests/Server/API/MicrosoftTeamsManifest.test.ts +7 -1
- package/Tests/Server/API/StatusPageOverviewCache.test.ts +474 -0
- package/Tests/Server/API/StatusPageSeoSearchEngineIndexing.test.ts +189 -0
- package/Tests/Server/Infrastructure/InMemoryTTLCache.test.ts +158 -0
- package/Tests/Server/Infrastructure/Postgres/DeviceRoleAndDeclaredLinkParentMigration.test.ts +9 -60
- package/Tests/Server/Middleware/ProjectAuthorizationApiKeyMiddleware.test.ts +24 -15
- package/Tests/Server/Middleware/UserAuthorization.test.ts +79 -0
- package/Tests/Server/Services/AddNetworkDeviceReachabilityColumnsMigration.test.ts +332 -0
- package/Tests/Server/Services/ApiKeyPermissionService.test.ts +347 -0
- package/Tests/Server/Services/DatabaseServiceUpdateDebugLogging.test.ts +282 -0
- package/Tests/Server/Services/NetworkSiteService.test.ts +56 -2
- package/Tests/Server/Services/TelemetryResourceFacetFilters.test.ts +267 -0
- package/Tests/Server/Services/WorkspaceNotificationRuleTestRuleChannels.test.ts +702 -0
- package/Tests/Server/Types/Database/Permissions/AccessControlPermission.test.ts +165 -0
- package/Tests/Server/Utils/APIKey/AccessPermission.test.ts +48 -36
- package/Tests/Server/Utils/AnalyticsDatabase/StatementGenerator.test.ts +213 -0
- package/Tests/Server/Utils/LogRedaction.test.ts +415 -0
- package/Tests/Server/Utils/LoggerCredentialLeak.test.ts +245 -0
- package/Tests/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.test.ts +374 -4
- package/Tests/Server/Utils/Monitor/MonitorResourceIngestedStepSelection.test.ts +455 -0
- package/Tests/Server/Utils/Monitor/NetworkDeviceReachabilityRoundTrip.test.ts +369 -0
- package/Tests/Server/Utils/Monitor/NetworkInventoryUtil.test.ts +86 -8
- package/Tests/Server/Utils/PrivacyFilterUtil.test.ts +64 -0
- package/Tests/Server/Utils/SessionReplay/SessionReplayGateCacheStore.test.ts +207 -0
- package/Tests/Server/Utils/StartServerBodyVerify.test.ts +335 -0
- package/Tests/Server/Utils/StatusPageSearchEngineIndexing.test.ts +60 -0
- package/Tests/Server/Utils/Telemetry/ResourceEntityFilter.test.ts +435 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsChannelSend.test.ts +607 -62
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsInstalledTeamIdSpace.test.ts +778 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsTeamInstalls.test.ts +44 -10
- package/Tests/Types/API/HostnameFromAuthority.test.ts +164 -0
- package/Tests/Types/Database/AccessControl/ColumnAccessControlCache.test.ts +344 -0
- package/Tests/Types/Database/DatabasePropertyFindOperator.test.ts +518 -0
- package/Tests/Types/Database/TableColumnMetadataCache.test.ts +286 -0
- package/Tests/Types/Monitor/MonitorStep.test.ts +100 -0
- package/Tests/Types/Monitor/MonitorTypeKeywords.test.ts +360 -0
- package/Tests/Types/StatusPage/SearchEngineIndexing.test.ts +86 -0
- package/Tests/Types/Telemetry/ResourceEntityFacet.test.ts +254 -0
- package/Tests/UI/Components/CardSelect.test.tsx +1021 -0
- package/Tests/UI/Components/MasterPage.test.tsx +36 -1
- package/Tests/Utils/Monitor/MonitorMetricType.test.ts +66 -1
- package/Tests/Utils/Monitor/NetworkTopologyUtil.test.ts +115 -6
- package/Tests/Utils/NetworkDevice/DeviceReachabilityUtil.test.ts +610 -0
- package/Tests/Utils/NetworkSite/SiteStatusRollupUtil.test.ts +114 -45
- package/Types/API/Hostname.ts +79 -0
- package/Types/Database/AccessControl/ColumnAccessControl.ts +33 -11
- package/Types/Database/AccessControl/ColumnBillingAccessControl.ts +33 -11
- package/Types/Database/DatabaseProperty.ts +38 -0
- package/Types/Database/TableColumn.ts +34 -7
- package/Types/Events/Recurring.ts +16 -1
- package/Types/Monitor/MonitorStep.ts +13 -1
- package/Types/Monitor/MonitorType.ts +345 -40
- package/Types/Monitor/SSLMonitor/SslMonitorResponse.ts +43 -0
- package/Types/StatusPage/SearchEngineIndexing.ts +61 -0
- package/Types/Telemetry/ResourceEntityFacet.ts +211 -0
- package/UI/Components/CardSelect/CardSelect.tsx +714 -105
- package/UI/Components/EmptyState/EmptyState.tsx +8 -1
- package/UI/Components/Footer/Footer.tsx +32 -6
- package/UI/Components/Forms/Fields/FormField.tsx +3 -0
- package/UI/Components/Forms/Types/Field.ts +8 -0
- package/UI/Components/Loader/PageLoader.tsx +29 -8
- package/UI/Components/LogsViewer/components/LogsAnalyticsView.tsx +26 -4
- package/UI/Components/Markdown.tsx/LazyMarkdownViewer.tsx +17 -1
- package/UI/Components/MasterPage/MasterPage.tsx +24 -11
- package/Utils/Monitor/MonitorMetricType.ts +16 -1
- package/Utils/Monitor/NetworkTopologyUtil.ts +60 -8
- package/Utils/NetworkDevice/DeviceReachabilityUtil.ts +262 -0
- package/Utils/NetworkSite/SiteStatusRollupUtil.ts +41 -24
- package/Utils/Telemetry/CrossSignalScope.ts +75 -4
- package/build/dist/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.js +20 -4
- package/build/dist/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkDevice.js +66 -0
- package/build/dist/Models/DatabaseModels/NetworkDevice.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPage.js +60 -0
- package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageOidc.js +2 -0
- package/build/dist/Models/DatabaseModels/StatusPageOidc.js.map +1 -1
- package/build/dist/Models/DatabaseModels/WorkspaceProjectAuthToken.js.map +1 -1
- package/build/dist/Server/API/AIChatAPI.js +48 -35
- package/build/dist/Server/API/AIChatAPI.js.map +1 -1
- package/build/dist/Server/API/CommonAPI.js +59 -0
- package/build/dist/Server/API/CommonAPI.js.map +1 -1
- package/build/dist/Server/API/MicrosoftTeamsAPI.js +20 -6
- package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -1
- package/build/dist/Server/API/SlackAPI.js +13 -10
- package/build/dist/Server/API/SlackAPI.js.map +1 -1
- package/build/dist/Server/API/StatusPageAPI.js +745 -673
- package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
- package/build/dist/Server/API/TelemetryAPI.js +34 -3
- package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787500000000-AddEnableSearchEngineIndexingToStatusPage.js +23 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787500000000-AddEnableSearchEngineIndexingToStatusPage.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787600000000-AddNetworkDeviceReachabilityColumns.js +34 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787600000000-AddNetworkDeviceReachabilityColumns.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +4 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Middleware/UserAuthorization.js +6 -2
- package/build/dist/Server/Middleware/UserAuthorization.js.map +1 -1
- package/build/dist/Server/Services/ApiKeyPermissionService.js +90 -1
- package/build/dist/Server/Services/ApiKeyPermissionService.js.map +1 -1
- package/build/dist/Server/Services/DatabaseService.js +15 -6
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/LogAggregationService.js +2 -0
- package/build/dist/Server/Services/LogAggregationService.js.map +1 -1
- package/build/dist/Server/Services/LogService.js +16 -0
- package/build/dist/Server/Services/LogService.js.map +1 -1
- package/build/dist/Server/Services/NetworkSiteService.js +12 -0
- package/build/dist/Server/Services/NetworkSiteService.js.map +1 -1
- package/build/dist/Server/Services/SpanService.js +16 -0
- package/build/dist/Server/Services/SpanService.js.map +1 -1
- package/build/dist/Server/Services/TraceAggregationService.js +2 -0
- package/build/dist/Server/Services/TraceAggregationService.js.map +1 -1
- package/build/dist/Server/Services/WorkspaceNotificationRuleService.js +137 -164
- package/build/dist/Server/Services/WorkspaceNotificationRuleService.js.map +1 -1
- package/build/dist/Server/Types/AnalyticsDatabase/ModelPermission.js +9 -0
- package/build/dist/Server/Types/AnalyticsDatabase/ModelPermission.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/AccessControlPermission.js +21 -13
- package/build/dist/Server/Types/Database/Permissions/AccessControlPermission.js.map +1 -1
- package/build/dist/Server/Utils/APIKey/AccessPermission.js +9 -32
- package/build/dist/Server/Utils/APIKey/AccessPermission.js.map +1 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +87 -0
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js.map +1 -1
- package/build/dist/Server/Utils/LogRedaction.js +449 -0
- package/build/dist/Server/Utils/LogRedaction.js.map +1 -0
- package/build/dist/Server/Utils/Logger.js +98 -45
- package/build/dist/Server/Utils/Logger.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.js +65 -13
- package/build/dist/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorResource.js +20 -3
- package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js +27 -7
- package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js.map +1 -1
- package/build/dist/Server/Utils/SessionReplay/SessionReplayGateCacheStore.js +29 -10
- package/build/dist/Server/Utils/SessionReplay/SessionReplayGateCacheStore.js.map +1 -1
- package/build/dist/Server/Utils/StartServer.js +10 -7
- package/build/dist/Server/Utils/StartServer.js.map +1 -1
- package/build/dist/Server/Utils/StatusPageSearchEngineIndexing.js +24 -0
- package/build/dist/Server/Utils/StatusPageSearchEngineIndexing.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/ResourceEntityFilter.js +311 -0
- package/build/dist/Server/Utils/Telemetry/ResourceEntityFilter.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +366 -37
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
- package/build/dist/Types/API/Hostname.js +63 -0
- package/build/dist/Types/API/Hostname.js.map +1 -1
- package/build/dist/Types/Database/AccessControl/ColumnAccessControl.js +20 -6
- package/build/dist/Types/Database/AccessControl/ColumnAccessControl.js.map +1 -1
- package/build/dist/Types/Database/AccessControl/ColumnBillingAccessControl.js +20 -6
- package/build/dist/Types/Database/AccessControl/ColumnBillingAccessControl.js.map +1 -1
- package/build/dist/Types/Database/DatabaseProperty.js +38 -0
- package/build/dist/Types/Database/DatabaseProperty.js.map +1 -1
- package/build/dist/Types/Database/TableColumn.js +24 -6
- package/build/dist/Types/Database/TableColumn.js.map +1 -1
- package/build/dist/Types/Events/Recurring.js +13 -1
- package/build/dist/Types/Events/Recurring.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorStep.js +9 -1
- package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorType.js +330 -31
- package/build/dist/Types/Monitor/MonitorType.js.map +1 -1
- package/build/dist/Types/StatusPage/SearchEngineIndexing.js +55 -0
- package/build/dist/Types/StatusPage/SearchEngineIndexing.js.map +1 -0
- package/build/dist/Types/Telemetry/ResourceEntityFacet.js +156 -0
- package/build/dist/Types/Telemetry/ResourceEntityFacet.js.map +1 -0
- package/build/dist/UI/Components/CardSelect/CardSelect.js +360 -40
- package/build/dist/UI/Components/CardSelect/CardSelect.js.map +1 -1
- package/build/dist/UI/Components/EmptyState/EmptyState.js +1 -1
- package/build/dist/UI/Components/EmptyState/EmptyState.js.map +1 -1
- package/build/dist/UI/Components/Footer/Footer.js +7 -4
- package/build/dist/UI/Components/Footer/Footer.js.map +1 -1
- package/build/dist/UI/Components/Forms/Fields/FormField.js +1 -1
- package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
- package/build/dist/UI/Components/Loader/PageLoader.js +15 -5
- package/build/dist/UI/Components/Loader/PageLoader.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js +16 -3
- package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js.map +1 -1
- package/build/dist/UI/Components/Markdown.tsx/LazyMarkdownViewer.js +13 -1
- package/build/dist/UI/Components/Markdown.tsx/LazyMarkdownViewer.js.map +1 -1
- package/build/dist/UI/Components/MasterPage/MasterPage.js +10 -2
- package/build/dist/UI/Components/MasterPage/MasterPage.js.map +1 -1
- package/build/dist/Utils/Monitor/MonitorMetricType.js +13 -1
- package/build/dist/Utils/Monitor/MonitorMetricType.js.map +1 -1
- package/build/dist/Utils/Monitor/NetworkTopologyUtil.js +34 -7
- package/build/dist/Utils/Monitor/NetworkTopologyUtil.js.map +1 -1
- package/build/dist/Utils/NetworkDevice/DeviceReachabilityUtil.js +177 -0
- package/build/dist/Utils/NetworkDevice/DeviceReachabilityUtil.js.map +1 -0
- package/build/dist/Utils/NetworkSite/SiteStatusRollupUtil.js +23 -27
- package/build/dist/Utils/NetworkSite/SiteStatusRollupUtil.js.map +1 -1
- package/build/dist/Utils/Telemetry/CrossSignalScope.js +44 -4
- package/build/dist/Utils/Telemetry/CrossSignalScope.js.map +1 -1
- package/package.json +1 -1
|
@@ -6,6 +6,11 @@ import SiteStatusRollupUtil, {
|
|
|
6
6
|
/*
|
|
7
7
|
* MonitorStatus priority is HIGHER = WORSE (seeded: Operational 1,
|
|
8
8
|
* Degraded 2, Offline 3).
|
|
9
|
+
*
|
|
10
|
+
* The SNMP half of this rollup now asks DeviceReachabilityUtil — "did the
|
|
11
|
+
* last poll succeed" — rather than "was the last success recent". That is
|
|
12
|
+
* what stops a site card going red because its probe is behind on a large
|
|
13
|
+
* fleet while every device under it is answering (issue #3220).
|
|
9
14
|
*/
|
|
10
15
|
const OPERATIONAL: RollupStatusOption = {
|
|
11
16
|
monitorStatusId: "status-operational",
|
|
@@ -26,12 +31,31 @@ function minutesAgo(minutes: number): Date {
|
|
|
26
31
|
return new Date(NOW.getTime() - minutes * 60 * 1000);
|
|
27
32
|
}
|
|
28
33
|
|
|
34
|
+
// A device whose last poll succeeded `minutes` ago.
|
|
35
|
+
function answered(minutes: number): DeviceHealthState {
|
|
36
|
+
return {
|
|
37
|
+
isReachable: true,
|
|
38
|
+
lastPolledAt: minutesAgo(minutes),
|
|
39
|
+
lastSeenAt: minutesAgo(minutes),
|
|
40
|
+
pollingIntervalInMinutes: 5,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// A device whose last poll failed `minutes` ago.
|
|
45
|
+
function failed(minutes: number): DeviceHealthState {
|
|
46
|
+
return {
|
|
47
|
+
isReachable: false,
|
|
48
|
+
lastPolledAt: minutesAgo(minutes),
|
|
49
|
+
lastSeenAt: minutesAgo(minutes + 60),
|
|
50
|
+
pollingIntervalInMinutes: 5,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
29
54
|
function worst(
|
|
30
55
|
deviceStates: Array<DeviceHealthState>,
|
|
31
56
|
overrides?: {
|
|
32
57
|
operationalStatus?: RollupStatusOption | null;
|
|
33
58
|
offlineStatus?: RollupStatusOption | null;
|
|
34
|
-
freshnessWindowInMinutes?: number;
|
|
35
59
|
},
|
|
36
60
|
): string | null {
|
|
37
61
|
return SiteStatusRollupUtil.worstStatus({
|
|
@@ -45,7 +69,6 @@ function worst(
|
|
|
45
69
|
? overrides.offlineStatus
|
|
46
70
|
: OFFLINE,
|
|
47
71
|
now: NOW,
|
|
48
|
-
freshnessWindowInMinutes: overrides?.freshnessWindowInMinutes,
|
|
49
72
|
});
|
|
50
73
|
}
|
|
51
74
|
|
|
@@ -84,65 +107,103 @@ describe("SiteStatusRollupUtil.worstStatus", () => {
|
|
|
84
107
|
).toBe(OFFLINE.monitorStatusId);
|
|
85
108
|
});
|
|
86
109
|
|
|
87
|
-
it("a
|
|
88
|
-
expect(worst([
|
|
110
|
+
it("a device whose last poll succeeded maps to the operational equivalent", () => {
|
|
111
|
+
expect(worst([answered(5)])).toBe(OPERATIONAL.monitorStatusId);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it("a device whose last poll failed maps to the offline equivalent", () => {
|
|
115
|
+
expect(worst([failed(2)])).toBe(OFFLINE.monitorStatusId);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
/*
|
|
119
|
+
* The rollup half of issue #3220. 21 minutes was the reported lag on a
|
|
120
|
+
* fleet the probe could not get round in 15, and every device under the
|
|
121
|
+
* site was answering.
|
|
122
|
+
*/
|
|
123
|
+
it("issue #3220: a site whose devices are all answering, 21 minutes behind, is operational", () => {
|
|
124
|
+
expect(worst([answered(21), answered(19), answered(25)])).toBe(
|
|
89
125
|
OPERATIONAL.monitorStatusId,
|
|
90
126
|
);
|
|
91
127
|
});
|
|
92
128
|
|
|
93
|
-
it("a
|
|
94
|
-
expect(
|
|
95
|
-
|
|
96
|
-
|
|
129
|
+
it("a slow-polled device is operational right up to its next poll", () => {
|
|
130
|
+
expect(
|
|
131
|
+
worst([
|
|
132
|
+
{
|
|
133
|
+
isReachable: true,
|
|
134
|
+
lastPolledAt: minutesAgo(29),
|
|
135
|
+
lastSeenAt: minutesAgo(29),
|
|
136
|
+
pollingIntervalInMinutes: 30,
|
|
137
|
+
},
|
|
138
|
+
]),
|
|
139
|
+
).toBe(OPERATIONAL.monitorStatusId);
|
|
97
140
|
});
|
|
98
141
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
142
|
+
/*
|
|
143
|
+
* A site nothing has polled for hours keeps its last known status rather
|
|
144
|
+
* than flipping offline. Turning "nobody has checked lately" into a red
|
|
145
|
+
* site card is the same inference this change removed from the device
|
|
146
|
+
* pill — and doing it here alone would put the site card at odds with
|
|
147
|
+
* every device under it, which is the inconsistency issue #3220 reported.
|
|
148
|
+
*/
|
|
149
|
+
it("a device out of contact past its stale window keeps its last verdict", () => {
|
|
150
|
+
expect(worst([answered(61)])).toBe(OPERATIONAL.monitorStatusId);
|
|
151
|
+
expect(worst([answered(60 * 24)])).toBe(OPERATIONAL.monitorStatusId);
|
|
102
152
|
});
|
|
103
153
|
|
|
104
|
-
it("
|
|
105
|
-
expect(worst([
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
154
|
+
it("...but a device that actually failed its last poll still rolls up offline", () => {
|
|
155
|
+
expect(worst([failed(60 * 24)])).toBe(OFFLINE.monitorStatusId);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
/*
|
|
159
|
+
* Changed deliberately: a never-polled device used to be counted as
|
|
160
|
+
* offline, which pinned a brand-new site red until its first walk landed
|
|
161
|
+
* — while the device list showed the very same device as a gray
|
|
162
|
+
* "Pending". It now contributes nothing, and the caller reads null as
|
|
163
|
+
* "leave the site's status alone".
|
|
164
|
+
*/
|
|
165
|
+
it("a never-polled device contributes nothing at all", () => {
|
|
166
|
+
expect(worst([{}])).toBeNull();
|
|
167
|
+
expect(worst([{ isReachable: null, lastSeenAt: null }])).toBeNull();
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it("a never-polled device does not drag down a site with healthy devices", () => {
|
|
171
|
+
expect(worst([answered(1), {}])).toBe(OPERATIONAL.monitorStatusId);
|
|
111
172
|
});
|
|
112
173
|
|
|
113
|
-
it("mix: a
|
|
174
|
+
it("mix: a failing device outranks a monitored operational one", () => {
|
|
114
175
|
expect(
|
|
115
176
|
worst([
|
|
116
177
|
{
|
|
117
178
|
currentMonitorStatusId: OPERATIONAL.monitorStatusId,
|
|
118
179
|
monitorStatusPriority: OPERATIONAL.priority,
|
|
119
180
|
},
|
|
120
|
-
|
|
181
|
+
failed(1),
|
|
121
182
|
]),
|
|
122
183
|
).toBe(OFFLINE.monitorStatusId);
|
|
123
184
|
});
|
|
124
185
|
|
|
125
|
-
it("mix: a monitored offline device outranks
|
|
186
|
+
it("mix: a monitored offline device outranks answering ones", () => {
|
|
126
187
|
expect(
|
|
127
188
|
worst([
|
|
128
|
-
|
|
189
|
+
answered(1),
|
|
129
190
|
{
|
|
130
191
|
currentMonitorStatusId: OFFLINE.monitorStatusId,
|
|
131
192
|
monitorStatusPriority: OFFLINE.priority,
|
|
132
193
|
},
|
|
133
|
-
|
|
194
|
+
answered(2),
|
|
134
195
|
]),
|
|
135
196
|
).toBe(OFFLINE.monitorStatusId);
|
|
136
197
|
});
|
|
137
198
|
|
|
138
|
-
it("a stamped status whose priority is unknown falls back to
|
|
139
|
-
// The MonitorStatus row was deleted: treat the device by
|
|
199
|
+
it("a stamped status whose priority is unknown falls back to reachability", () => {
|
|
200
|
+
// The MonitorStatus row was deleted: treat the device by its last poll.
|
|
140
201
|
expect(
|
|
141
202
|
worst([
|
|
142
203
|
{
|
|
143
204
|
currentMonitorStatusId: "deleted-status",
|
|
144
205
|
monitorStatusPriority: undefined,
|
|
145
|
-
|
|
206
|
+
...answered(1),
|
|
146
207
|
},
|
|
147
208
|
]),
|
|
148
209
|
).toBe(OPERATIONAL.monitorStatusId);
|
|
@@ -151,7 +212,7 @@ describe("SiteStatusRollupUtil.worstStatus", () => {
|
|
|
151
212
|
{
|
|
152
213
|
currentMonitorStatusId: "deleted-status",
|
|
153
214
|
monitorStatusPriority: null,
|
|
154
|
-
|
|
215
|
+
...failed(1),
|
|
155
216
|
},
|
|
156
217
|
]),
|
|
157
218
|
).toBe(OFFLINE.monitorStatusId);
|
|
@@ -172,9 +233,9 @@ describe("SiteStatusRollupUtil.worstStatus", () => {
|
|
|
172
233
|
).toBe("status-a");
|
|
173
234
|
});
|
|
174
235
|
|
|
175
|
-
it("returns null when only
|
|
236
|
+
it("returns null when only SNMP fallbacks apply but the project has no flagged rows", () => {
|
|
176
237
|
expect(
|
|
177
|
-
worst([
|
|
238
|
+
worst([answered(1), failed(1)], {
|
|
178
239
|
operationalStatus: null,
|
|
179
240
|
offlineStatus: null,
|
|
180
241
|
}),
|
|
@@ -185,7 +246,7 @@ describe("SiteStatusRollupUtil.worstStatus", () => {
|
|
|
185
246
|
expect(
|
|
186
247
|
worst(
|
|
187
248
|
[
|
|
188
|
-
|
|
249
|
+
failed(1), // offline equivalent missing -> skipped
|
|
189
250
|
{
|
|
190
251
|
currentMonitorStatusId: DEGRADED.monitorStatusId,
|
|
191
252
|
monitorStatusPriority: DEGRADED.priority,
|
|
@@ -196,26 +257,34 @@ describe("SiteStatusRollupUtil.worstStatus", () => {
|
|
|
196
257
|
).toBe(DEGRADED.monitorStatusId);
|
|
197
258
|
});
|
|
198
259
|
|
|
199
|
-
it("
|
|
260
|
+
it("a lastPolledAt in the future counts as fresh, not an error", () => {
|
|
200
261
|
expect(
|
|
201
|
-
worst([
|
|
202
|
-
|
|
203
|
-
|
|
262
|
+
worst([
|
|
263
|
+
{
|
|
264
|
+
isReachable: true,
|
|
265
|
+
lastPolledAt: minutesAgo(-5),
|
|
266
|
+
lastSeenAt: minutesAgo(-5),
|
|
267
|
+
},
|
|
268
|
+
]),
|
|
204
269
|
).toBe(OPERATIONAL.monitorStatusId);
|
|
205
|
-
expect(
|
|
206
|
-
worst([{ lastSeenAt: minutesAgo(20) }], {
|
|
207
|
-
freshnessWindowInMinutes: 10,
|
|
208
|
-
}),
|
|
209
|
-
).toBe(OFFLINE.monitorStatusId);
|
|
210
270
|
});
|
|
211
271
|
|
|
212
|
-
|
|
213
|
-
|
|
272
|
+
/*
|
|
273
|
+
* Rows written before the reachability columns existed carry only
|
|
274
|
+
* lastSeenAt. They must keep rolling up sensibly until the upgrade
|
|
275
|
+
* migration (or their next walk) fills the rest in.
|
|
276
|
+
*/
|
|
277
|
+
it("legacy rows with only lastSeenAt still roll up by freshness", () => {
|
|
278
|
+
/*
|
|
279
|
+
* The one place freshness survives: a row written before isReachable
|
|
280
|
+
* existed has nothing else to go on. The upgrade migration backfills
|
|
281
|
+
* these, so it is a short-lived path.
|
|
282
|
+
*/
|
|
283
|
+
expect(worst([{ lastSeenAt: minutesAgo(5) }])).toBe(
|
|
214
284
|
OPERATIONAL.monitorStatusId,
|
|
215
285
|
);
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
expect(SiteStatusRollupUtil.DEFAULT_FRESHNESS_WINDOW_IN_MINUTES).toBe(15);
|
|
286
|
+
expect(worst([{ lastSeenAt: minutesAgo(600) }])).toBe(
|
|
287
|
+
OFFLINE.monitorStatusId,
|
|
288
|
+
);
|
|
220
289
|
});
|
|
221
290
|
});
|
package/Types/API/Hostname.ts
CHANGED
|
@@ -182,6 +182,85 @@ export default class Hostname extends DatabaseProperty {
|
|
|
182
182
|
return new Hostname(hostname);
|
|
183
183
|
}
|
|
184
184
|
|
|
185
|
+
/*
|
|
186
|
+
* Splits a URL authority into its bare host and its port, using the same
|
|
187
|
+
* structure isValid() already understands: optional userinfo, then either
|
|
188
|
+
* a bracketed IPv6 literal, an unbracketed IPv6 literal, or a host with an
|
|
189
|
+
* optional ":port".
|
|
190
|
+
*
|
|
191
|
+
* fromString() is NOT a substitute. It splits on the FIRST colon, so it
|
|
192
|
+
* turns "[::1]:8080" into a host of "[" and a port of "", and any IPv6
|
|
193
|
+
* literal into nonsense. Callers that hold an authority (a URL's host
|
|
194
|
+
* component) and need the two parts apart must use this.
|
|
195
|
+
*
|
|
196
|
+
* Userinfo is dropped rather than preserved: the caller asked for a host
|
|
197
|
+
* to connect to, and credentials are not part of one. Anything that needs
|
|
198
|
+
* the original string still has the authority it passed in.
|
|
199
|
+
*/
|
|
200
|
+
public static fromAuthority(authority: string): Hostname {
|
|
201
|
+
const trimmedAuthority: string = (authority || "").trim();
|
|
202
|
+
|
|
203
|
+
if (!trimmedAuthority) {
|
|
204
|
+
return new Hostname("");
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/*
|
|
208
|
+
* Split on the LAST "@" for the same reason isValid() does: userinfo may
|
|
209
|
+
* itself contain an "@", and everything after the final one is the
|
|
210
|
+
* authority proper.
|
|
211
|
+
*/
|
|
212
|
+
const atIndex: number = trimmedAuthority.lastIndexOf("@");
|
|
213
|
+
const hostAndPort: string =
|
|
214
|
+
atIndex === -1
|
|
215
|
+
? trimmedAuthority
|
|
216
|
+
: trimmedAuthority.substring(atIndex + 1);
|
|
217
|
+
|
|
218
|
+
if (!hostAndPort) {
|
|
219
|
+
return new Hostname("");
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Bracketed IPv6, with or without a port: "[::1]", "[::1]:8080".
|
|
223
|
+
const bracketedIpv6Match: RegExpMatchArray | null = hostAndPort.match(
|
|
224
|
+
/^(\[[0-9a-fA-F:.]+\])(?::(\d{1,5}))?$/,
|
|
225
|
+
);
|
|
226
|
+
|
|
227
|
+
if (bracketedIpv6Match) {
|
|
228
|
+
const ipv6Host: string = bracketedIpv6Match[1] as string;
|
|
229
|
+
const ipv6Port: string | undefined = bracketedIpv6Match[2];
|
|
230
|
+
|
|
231
|
+
return ipv6Port
|
|
232
|
+
? new Hostname(ipv6Host, ipv6Port)
|
|
233
|
+
: new Hostname(ipv6Host);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/*
|
|
237
|
+
* Unbracketed IPv6 literal: two or more colons cannot be a "host:port",
|
|
238
|
+
* so every colon belongs to the address itself and there is no port.
|
|
239
|
+
*/
|
|
240
|
+
if ((hostAndPort.match(/:/g) || []).length > 1) {
|
|
241
|
+
return new Hostname(hostAndPort);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const colonIndex: number = hostAndPort.indexOf(":");
|
|
245
|
+
|
|
246
|
+
if (colonIndex === -1) {
|
|
247
|
+
return new Hostname(hostAndPort);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const host: string = hostAndPort.substring(0, colonIndex);
|
|
251
|
+
const port: string = hostAndPort.substring(colonIndex + 1);
|
|
252
|
+
|
|
253
|
+
/*
|
|
254
|
+
* A trailing colon with no digits ("host:") is not a port. Keep the host
|
|
255
|
+
* and drop the empty port rather than constructing an invalid Port.
|
|
256
|
+
*/
|
|
257
|
+
if (!port) {
|
|
258
|
+
return new Hostname(host);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
return new Hostname(host, port);
|
|
262
|
+
}
|
|
263
|
+
|
|
185
264
|
public static override toDatabase(
|
|
186
265
|
value: Hostname | FindOperator<Hostname>,
|
|
187
266
|
): string | null {
|
|
@@ -30,20 +30,42 @@ type GetColumnAccessControlForAllColumnsFunction = <T extends BaseModel>(
|
|
|
30
30
|
target: T,
|
|
31
31
|
) => Dictionary<ColumnAccessControl>;
|
|
32
32
|
|
|
33
|
+
/*
|
|
34
|
+
* Per-class cache. Safe to key on the constructor because every decorated
|
|
35
|
+
* column is initialised to `undefined` in the class body, so Object.keys()
|
|
36
|
+
* is identical for every instance of a class (see OwnerOnlyColumn.ts).
|
|
37
|
+
* Callers must treat the returned dictionary as read-only.
|
|
38
|
+
*/
|
|
39
|
+
const accessControlCache: WeakMap<
|
|
40
|
+
{ new (): BaseModel },
|
|
41
|
+
Dictionary<ColumnAccessControl>
|
|
42
|
+
> = new WeakMap();
|
|
43
|
+
|
|
33
44
|
export const getColumnAccessControlForAllColumns: GetColumnAccessControlForAllColumnsFunction =
|
|
34
45
|
<T extends BaseModel>(target: T): Dictionary<ColumnAccessControl> => {
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
46
|
+
const modelClass: { new (): BaseModel } = target.constructor as {
|
|
47
|
+
new (): BaseModel;
|
|
48
|
+
};
|
|
49
|
+
let cached: Dictionary<ColumnAccessControl> | undefined =
|
|
50
|
+
accessControlCache.get(modelClass);
|
|
51
|
+
|
|
52
|
+
if (!cached) {
|
|
53
|
+
const dictonary: Dictionary<ColumnAccessControl> = {};
|
|
54
|
+
const keys: Array<string> = Object.keys(target);
|
|
55
|
+
|
|
56
|
+
for (const key of keys) {
|
|
57
|
+
const accessControl: ColumnAccessControl | undefined =
|
|
58
|
+
Reflect.getMetadata(accessControlSymbol, target, key) as
|
|
59
|
+
| ColumnAccessControl
|
|
60
|
+
| undefined;
|
|
61
|
+
if (accessControl) {
|
|
62
|
+
dictonary[key] = accessControl;
|
|
63
|
+
}
|
|
45
64
|
}
|
|
65
|
+
|
|
66
|
+
cached = dictonary;
|
|
67
|
+
accessControlCache.set(modelClass, cached);
|
|
46
68
|
}
|
|
47
69
|
|
|
48
|
-
return
|
|
70
|
+
return cached;
|
|
49
71
|
};
|
|
@@ -30,20 +30,42 @@ type GetColumnBillingAccessControlForAllColumnsFunction = <T extends BaseModel>(
|
|
|
30
30
|
target: T,
|
|
31
31
|
) => Dictionary<ColumnBillingAccessControl>;
|
|
32
32
|
|
|
33
|
+
/*
|
|
34
|
+
* Per-class cache. Safe to key on the constructor because every decorated
|
|
35
|
+
* column is initialised to `undefined` in the class body, so Object.keys()
|
|
36
|
+
* is identical for every instance of a class (see OwnerOnlyColumn.ts).
|
|
37
|
+
* Callers must treat the returned dictionary as read-only.
|
|
38
|
+
*/
|
|
39
|
+
const billingAccessControlCache: WeakMap<
|
|
40
|
+
{ new (): BaseModel },
|
|
41
|
+
Dictionary<ColumnBillingAccessControl>
|
|
42
|
+
> = new WeakMap();
|
|
43
|
+
|
|
33
44
|
export const getColumnBillingAccessControlForAllColumns: GetColumnBillingAccessControlForAllColumnsFunction =
|
|
34
45
|
<T extends BaseModel>(target: T): Dictionary<ColumnBillingAccessControl> => {
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
46
|
+
const modelClass: { new (): BaseModel } = target.constructor as {
|
|
47
|
+
new (): BaseModel;
|
|
48
|
+
};
|
|
49
|
+
let cached: Dictionary<ColumnBillingAccessControl> | undefined =
|
|
50
|
+
billingAccessControlCache.get(modelClass);
|
|
51
|
+
|
|
52
|
+
if (!cached) {
|
|
53
|
+
const dictonary: Dictionary<ColumnBillingAccessControl> = {};
|
|
54
|
+
const keys: Array<string> = Object.keys(target);
|
|
55
|
+
|
|
56
|
+
for (const key of keys) {
|
|
57
|
+
const accessControl: ColumnBillingAccessControl | undefined =
|
|
58
|
+
Reflect.getMetadata(accessControlSymbol, target, key) as
|
|
59
|
+
| ColumnBillingAccessControl
|
|
60
|
+
| undefined;
|
|
61
|
+
if (accessControl) {
|
|
62
|
+
dictonary[key] = accessControl;
|
|
63
|
+
}
|
|
45
64
|
}
|
|
65
|
+
|
|
66
|
+
cached = dictonary;
|
|
67
|
+
billingAccessControlCache.set(modelClass, cached);
|
|
46
68
|
}
|
|
47
69
|
|
|
48
|
-
return
|
|
70
|
+
return cached;
|
|
49
71
|
};
|
|
@@ -39,6 +39,44 @@ export default class DatabaseProperty extends SerializableObject {
|
|
|
39
39
|
return value as any;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
/*
|
|
43
|
+
* A FindOperator reaches a transformer when the query value is an
|
|
44
|
+
* operator rather than a plain value. Two ways in:
|
|
45
|
+
*
|
|
46
|
+
* - Nested. `And(Equal(id), Raw(privacyClause))` — the shape every
|
|
47
|
+
* privacy/scope filter builds (Server/Utils/PrivacyFilterUtil.ts).
|
|
48
|
+
* TypeORM calls `operator.transformValue(column.transformer)`
|
|
49
|
+
* (SelectQueryBuilder.buildWhere), and FindOperator.transformValue
|
|
50
|
+
* hands each CHILD OPERATOR to this function whenever the operator
|
|
51
|
+
* carries multiple parameters — which And() and Or() both do.
|
|
52
|
+
* - Top level. ColumnMetadata.getEntityValue applies the transformer to
|
|
53
|
+
* the whole criteria of a .where()/.update()/.delete() call.
|
|
54
|
+
*
|
|
55
|
+
* Every toDatabase() below expects a value, so an operator makes it
|
|
56
|
+
* produce garbage: "[object Object]" from the string-ish types (ObjectID
|
|
57
|
+
* included — a uuid column then fails with Postgres 22P02 invalid input
|
|
58
|
+
* syntax for type uuid), `null` from Port, a serialized operator from the
|
|
59
|
+
* JSON types. The child is REPLACED by that garbage, so the caller's own
|
|
60
|
+
* predicate is lost and the privacy clause it was ANDed with can no
|
|
61
|
+
* longer match anything.
|
|
62
|
+
*
|
|
63
|
+
* Transform the operator's leaf value(s) in place and hand the operator
|
|
64
|
+
* back, so `And`/`Or`/`In`/`Not`/`Between` keep their structure and only
|
|
65
|
+
* the leaves are converted. Recursion terminates because transformValue
|
|
66
|
+
* always descends into the operator's value, and a leaf is never a
|
|
67
|
+
* FindOperator.
|
|
68
|
+
*
|
|
69
|
+
* NOTE: transformValue mutates. One query object can legitimately be
|
|
70
|
+
* transformed more than once — BaseAPI.getList shares it between findBy
|
|
71
|
+
* and countBy — so every toDatabase() has to be a fixpoint: handed its
|
|
72
|
+
* own output it must return that output unchanged. Tests/Types/Database/
|
|
73
|
+
* DatabasePropertyFindOperator.test.ts enforces that for each type.
|
|
74
|
+
*/
|
|
75
|
+
if (value instanceof FindOperator) {
|
|
76
|
+
value.transformValue(this.getDatabaseTransformer());
|
|
77
|
+
return value as any;
|
|
78
|
+
}
|
|
79
|
+
|
|
42
80
|
return this.toDatabase(value);
|
|
43
81
|
}
|
|
44
82
|
|
|
@@ -63,21 +63,48 @@ type GetTableColumnsFunction = <T extends BaseModel>(
|
|
|
63
63
|
target: T,
|
|
64
64
|
) => Dictionary<TableColumnMetadata>;
|
|
65
65
|
|
|
66
|
+
/*
|
|
67
|
+
* Per-class cache. Safe to key on the constructor because every decorated
|
|
68
|
+
* column is initialised to `undefined` in the class body, so Object.keys()
|
|
69
|
+
* is identical for every instance of a class (see OwnerOnlyColumn.ts).
|
|
70
|
+
*/
|
|
71
|
+
const tableColumnsCache: WeakMap<
|
|
72
|
+
{ new (): BaseModel },
|
|
73
|
+
Dictionary<TableColumnMetadata>
|
|
74
|
+
> = new WeakMap();
|
|
75
|
+
|
|
66
76
|
export const getTableColumns: GetTableColumnsFunction = <T extends BaseModel>(
|
|
67
77
|
target: T,
|
|
68
78
|
): Dictionary<TableColumnMetadata> => {
|
|
69
|
-
const
|
|
70
|
-
|
|
79
|
+
const modelClass: { new (): BaseModel } = target.constructor as {
|
|
80
|
+
new (): BaseModel;
|
|
81
|
+
};
|
|
82
|
+
let cached: Dictionary<TableColumnMetadata> | undefined =
|
|
83
|
+
tableColumnsCache.get(modelClass);
|
|
84
|
+
|
|
85
|
+
if (!cached) {
|
|
86
|
+
const dictonary: Dictionary<TableColumnMetadata> = {};
|
|
87
|
+
const keys: Array<string> = Object.keys(target);
|
|
71
88
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
dictonary[key] = Reflect.getMetadata(
|
|
89
|
+
for (const key of keys) {
|
|
90
|
+
const metadata: TableColumnMetadata | undefined = Reflect.getMetadata(
|
|
75
91
|
tableColumn,
|
|
76
92
|
target,
|
|
77
93
|
key,
|
|
78
|
-
) as TableColumnMetadata;
|
|
94
|
+
) as TableColumnMetadata | undefined;
|
|
95
|
+
if (metadata) {
|
|
96
|
+
dictonary[key] = metadata;
|
|
97
|
+
}
|
|
79
98
|
}
|
|
99
|
+
|
|
100
|
+
cached = dictonary;
|
|
101
|
+
tableColumnsCache.set(modelClass, cached);
|
|
80
102
|
}
|
|
81
103
|
|
|
82
|
-
|
|
104
|
+
/*
|
|
105
|
+
* Hand out a copy, not the cached dictionary itself: callers mutate the
|
|
106
|
+
* result (the API reference docs delete entries from it), which would
|
|
107
|
+
* otherwise strip columns from every later call for the same class.
|
|
108
|
+
*/
|
|
109
|
+
return { ...cached };
|
|
83
110
|
};
|
|
@@ -313,7 +313,22 @@ export default class Recurring extends DatabaseProperty {
|
|
|
313
313
|
value: Recurring | Array<Recurring> | FindOperator<Recurring>,
|
|
314
314
|
): JSONObject | Array<JSONObject> | null {
|
|
315
315
|
if (Array.isArray(value)) {
|
|
316
|
-
|
|
316
|
+
/*
|
|
317
|
+
* Per item rather than through toJSONArray, which assumes every item is
|
|
318
|
+
* still a Recurring instance and throws "item.toJSON is not a function"
|
|
319
|
+
* on an already-serialized array. TypeORM can hand a transformer its own
|
|
320
|
+
* output — BaseAPI.getList shares one query object between findBy and
|
|
321
|
+
* countBy — so this has to be a fixpoint.
|
|
322
|
+
*/
|
|
323
|
+
return (value as Array<Recurring | JSONObject>).map(
|
|
324
|
+
(item: Recurring | JSONObject) => {
|
|
325
|
+
if (item instanceof Recurring) {
|
|
326
|
+
return item.toJSON();
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
return JSONFunctions.serialize(item as JSONObject);
|
|
330
|
+
},
|
|
331
|
+
);
|
|
317
332
|
}
|
|
318
333
|
|
|
319
334
|
if (value && value instanceof Recurring) {
|
|
@@ -1156,8 +1156,20 @@ export default class MonitorStep extends DatabaseProperty {
|
|
|
1156
1156
|
),
|
|
1157
1157
|
)
|
|
1158
1158
|
: undefined,
|
|
1159
|
+
/*
|
|
1160
|
+
* Normalized for the same reason domainMonitor above is: a config
|
|
1161
|
+
* authored through the API or a template need not carry `resolvers`,
|
|
1162
|
+
* and the probe iterates that list without a guard. A raw passthrough
|
|
1163
|
+
* therefore threw a TypeError inside the probe BEFORE it posted any
|
|
1164
|
+
* result, so the monitor produced nothing at all - no check, no
|
|
1165
|
+
* status change, no error the user could see.
|
|
1166
|
+
*/
|
|
1159
1167
|
dnssecMonitor: json["dnssecMonitor"]
|
|
1160
|
-
? (
|
|
1168
|
+
? MonitorStepDnssecMonitorUtil.toJSON(
|
|
1169
|
+
MonitorStepDnssecMonitorUtil.fromJSON(
|
|
1170
|
+
json["dnssecMonitor"] as JSONObject,
|
|
1171
|
+
),
|
|
1172
|
+
)
|
|
1161
1173
|
: undefined,
|
|
1162
1174
|
sqlMonitor: json["sqlMonitor"]
|
|
1163
1175
|
? (json["sqlMonitor"] as JSONObject)
|