@oneuptime/common 12.0.34 → 13.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Models/DatabaseModels/GlobalConfig.ts +12 -12
- package/Models/DatabaseModels/InventoryItem.ts +44 -1
- package/Models/DatabaseModels/NetworkDevice.ts +126 -0
- package/Server/EnvironmentConfig.ts +47 -16
- package/Server/Infrastructure/Postgres/SchemaMigrations/1791700000000-AddMacAddressToNetworkDevice.ts +25 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +2 -0
- package/Server/Infrastructure/Redis.ts +8 -8
- package/Server/Infrastructure/Status.ts +10 -4
- package/Server/Services/DatabaseService.ts +65 -13
- package/Server/Services/InventoryItemService.ts +31 -1
- package/Server/Services/MonitorTemplateService.ts +140 -19
- package/Server/Services/NetworkDeviceAutoImportRuleEngineService.ts +349 -41
- package/Server/Services/NetworkDeviceDiscoveryScanService.ts +87 -2
- package/Server/Services/NetworkDeviceService.ts +69 -0
- package/Server/Services/UserNotificationRuleService.ts +76 -25
- package/Server/Types/Database/Permissions/ReadPermission.ts +45 -5
- package/Server/Types/Database/QueryUtil.ts +56 -0
- package/Server/Types/Markdown.ts +93 -1
- package/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.ts +82 -17
- package/Server/Utils/EmailRollup/EmailRollupFlushRunner.ts +10 -1
- package/Server/Utils/EmailRollup/EmailRollupRenderer.ts +2 -2
- package/Server/Utils/Monitor/Criteria/CompareCriteria.ts +82 -35
- package/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.ts +31 -5
- package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +202 -37
- package/Server/Utils/Monitor/MonitorCriteriaExpectationBuilder.ts +82 -9
- package/Server/Utils/Monitor/MonitorCriteriaMessageBuilder.ts +16 -7
- package/Server/Utils/Monitor/MonitorCriteriaMessageFormatter.ts +29 -17
- package/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.ts +107 -48
- package/Server/Utils/Monitor/NetworkDeviceMacLearningUtil.ts +158 -0
- package/Server/Utils/Monitor/NetworkInventoryUtil.ts +27 -0
- package/Server/Utils/Telemetry/EntityRegistry.ts +11 -1
- package/Server/Utils/Telemetry/TelemetryEntity.ts +83 -9
- package/Tests/App/Dashboard/AddNeighborToMonitoringModal.test.tsx +32 -0
- package/Tests/App/Dashboard/AdoptedDeviceDrawer.test.tsx +374 -0
- package/Tests/App/Dashboard/DashboardLabelVariables.test.tsx +580 -0
- package/Tests/App/Dashboard/DashboardMonitorLabelVariable.test.tsx +438 -0
- package/Tests/App/Dashboard/DashboardVariableAllContract.test.tsx +5 -2
- package/Tests/App/Dashboard/DeviceAttachmentCard.test.tsx +273 -0
- package/Tests/App/Dashboard/DiscoveryReviewInventoryRefresh.test.tsx +505 -0
- package/Tests/App/Dashboard/EntityDetailPanel.test.tsx +163 -0
- package/Tests/App/Dashboard/InfrastructureExplorer.test.tsx +401 -0
- package/Tests/App/Dashboard/InfrastructureGraph.test.tsx +232 -0
- package/Tests/App/Dashboard/InventoryItems.test.tsx +201 -0
- package/Tests/App/Dashboard/InventoryTypeAndStatusFacets.test.tsx +798 -0
- package/Tests/App/Dashboard/NetworkTopologyLiveView.test.tsx +320 -0
- package/Tests/App/Dashboard/NetworkTopologyToolbar.test.tsx +275 -0
- package/Tests/App/Dashboard/OnCallCalendarFeedEmptyState.test.tsx +975 -0
- package/Tests/App/Dashboard/OnCallCalendarFeedPlanGate.test.tsx +63 -0
- package/Tests/App/Dashboard/ServiceMapGraph.test.tsx +626 -0
- package/Tests/App/Dashboard/TopologyDataLoading.test.tsx +348 -0
- package/Tests/App/Dashboard/TopologyPageNavigation.test.tsx +331 -0
- package/Tests/App/Dashboard/UserSettingsEmailPreferences.test.tsx +603 -0
- package/Tests/App/Dashboard/UserSettingsNotificationSettings.test.tsx +324 -0
- package/Tests/App/PublicDashboard/DashboardVariableSelector.test.tsx +13 -4
- package/Tests/App/StatusPage/StatusPageOidcOrigin.test.tsx +223 -0
- package/Tests/Models/InventoryItemStatus.test.ts +147 -0
- package/Tests/Models/NetworkDeviceMacAddressColumn.test.ts +294 -0
- package/Tests/Server/API/DashboardPublicResourceListAPI.test.ts +129 -0
- package/Tests/Server/EnvironmentConfigValkey.test.ts +227 -0
- package/Tests/Server/Infrastructure/Postgres/AddMacAddressToNetworkDeviceMigration.test.ts +365 -0
- package/Tests/Server/Infrastructure/Postgres/CustomFieldValueMappingMigration.test.ts +62 -23
- package/Tests/Server/Infrastructure/ValkeyDeploymentConfig.test.ts +265 -0
- package/Tests/Server/Middleware/HttpMetricsMiddleware.test.ts +382 -0
- package/Tests/Server/Middleware/SCIMAuthorization.test.ts +525 -0
- package/Tests/Server/Services/DatabaseServiceAggregateBy.test.ts +291 -56
- package/Tests/Server/Services/DatabaseServiceSortTiebreaker.test.ts +151 -0
- package/Tests/Server/Services/InventoryItemManualCreate.test.ts +105 -0
- package/Tests/Server/Services/InventoryItemPromotionGate.test.ts +178 -0
- package/Tests/Server/Services/MonitorTemplateServiceCustomFieldSync.test.ts +502 -0
- package/Tests/Server/Services/NetworkDeviceAutoImportRuleEngineService.test.ts +1133 -81
- package/Tests/Server/Services/NetworkDeviceDiscoveryScanRegistration.test.ts +520 -0
- package/Tests/Server/Services/NetworkDeviceMacAddressNormalization.test.ts +572 -0
- package/Tests/Server/Types/Database/InventoryStatusPostgres.test.ts +368 -0
- package/Tests/Server/Types/Database/Permissions/ReadBlockPermission.test.ts +261 -14
- package/Tests/Server/Types/Database/QueryUtilIncludesAnyOfGroups.test.ts +169 -0
- package/Tests/Server/Types/Database/QueryUtilIncludesAnyOfGroupsPostgres.test.ts +297 -0
- package/Tests/Server/Types/Markdown.test.ts +194 -1
- package/Tests/Server/Types/Workflow/Components/IncomingWebhookUtils.test.ts +279 -0
- package/Tests/Server/Types/Workflow/Components/TextToJson.test.ts +212 -0
- package/Tests/Server/Utils/CustomField/CustomFieldMappingRegistry.test.ts +436 -0
- package/Tests/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.test.ts +214 -0
- package/Tests/Server/Utils/EmailRollup/EmailRollupFlushRunnerBehaviour.test.ts +1 -1
- package/Tests/Server/Utils/EmailRollup/EmailRollupRenderer.test.ts +10 -3
- package/Tests/Server/Utils/EventLoop.test.ts +359 -0
- package/Tests/Server/Utils/JsonWebToken.test.ts +30 -0
- package/Tests/Server/Utils/Monitor/Criteria/CompareCriteria.test.ts +242 -0
- package/Tests/Server/Utils/Monitor/Criteria/CompareCriteriaAggregation.test.ts +13 -7
- package/Tests/Server/Utils/Monitor/Criteria/DatabaseMonitorCriteria.test.ts +29 -0
- package/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.test.ts +30 -6
- package/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteriaAnomalyUnits.test.ts +246 -0
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEmailEvalLogAgreement.test.ts +280 -0
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluator.test.ts +224 -10
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorMetricUnits.test.ts +570 -0
- package/Tests/Server/Utils/Monitor/MonitorCriteriaExpectationBuilderUnits.test.ts +30 -5
- package/Tests/Server/Utils/Monitor/MonitorCriteriaMessageBuilderUnits.test.ts +68 -21
- package/Tests/Server/Utils/Monitor/MonitorCriteriaObservationBuilderUnits.test.ts +36 -19
- package/Tests/Server/Utils/Monitor/NetworkDeviceMacLearningUtil.test.ts +573 -0
- package/Tests/Server/Utils/Monitor/NetworkInventoryUtil.test.ts +317 -0
- package/Tests/Server/Utils/Telemetry/ContextSpanProcessor.test.ts +281 -0
- package/Tests/Server/Utils/Telemetry/InventoryDuplicateIdentity.test.ts +316 -0
- package/Tests/Server/Utils/Telemetry/TelemetryEntity.test.ts +111 -8
- package/Tests/Types/Database/IncludesAnyOfGroups.test.ts +106 -0
- package/Tests/Types/JSONFunctions.test.ts +40 -0
- package/Tests/Types/NetworkAutomation/AutoImportRunMerge.test.ts +235 -0
- package/Tests/Types/NetworkAutomation/RuleRunResult.test.ts +66 -0
- package/Tests/Types/NetworkAutomation/RuleRunResultDescribe.test.ts +309 -1
- package/Tests/Types/Telemetry/InventoryLiveness.test.ts +99 -0
- package/Tests/UI/Components/CustomFields/CustomFieldsDetail.test.tsx +160 -0
- package/Tests/UI/ReactRouterSingletonBuild.test.ts +133 -0
- package/Tests/UI/Rum/ReplayHeader.test.tsx +10 -2
- package/Tests/UI/Rum/ReplayPlayerChrome.test.tsx +514 -0
- package/Tests/UI/Rum/ReplayTimeFormat.test.ts +94 -0
- package/Tests/UI/Rum/ReplayUi.test.tsx +559 -0
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +5 -0
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +1 -0
- package/Tests/Utils/Dashboard/Components/DashboardMonitorListComponent.test.ts +11 -1
- package/Tests/Utils/Dashboard/LabelVariable.test.ts +257 -0
- package/Tests/Utils/Dashboard/VariableUrlState.test.ts +144 -14
- package/Tests/Utils/MetricUnitUtil.test.ts +81 -0
- package/Tests/Utils/Monitor/DeviceMacLearningUtil.test.ts +497 -0
- package/Tests/Utils/Monitor/MetricValueFormatter.test.ts +546 -0
- package/Tests/Utils/Monitor/MonitorTemplateCustomFieldUtil.test.ts +218 -0
- package/Tests/Utils/Monitor/NetworkTopologyEndpointAdoption.test.ts +1755 -0
- package/Tests/Utils/NetworkAutomation/AutoImportRunChain.test.ts +320 -0
- package/Types/BaseDatabase/IncludesAnyOfGroups.ts +77 -0
- package/Types/Dashboard/DashboardComponents/ComponentArgument.ts +1 -0
- package/Types/Dashboard/DashboardComponents/DashboardMonitorListComponent.ts +1 -0
- package/Types/Dashboard/DashboardVariable.ts +8 -0
- package/Types/JSON.ts +4 -0
- package/Types/JSONFunctions.ts +16 -0
- package/Types/Monitor/SnmpMonitor/NetworkTopology.ts +13 -0
- package/Types/NetworkAutomation/RuleRunResult.ts +402 -13
- package/Types/SerializableObjectDictionary.ts +4 -0
- package/Types/Telemetry/InventoryLiveness.ts +56 -0
- package/UI/Components/CSVFileUpload/CSVFileUpload.tsx +9 -2
- package/UI/Components/CustomFields/CustomFieldsDetail.tsx +46 -21
- package/UI/Components/Dashboard/DashboardVariableControl.tsx +285 -0
- package/UI/esbuild-config.js +6 -0
- package/Utils/Dashboard/Components/DashboardMonitorListComponent.ts +12 -1
- package/Utils/Dashboard/LabelVariable.ts +152 -0
- package/Utils/Dashboard/VariableUrlState.ts +27 -5
- package/Utils/MetricUnitUtil.ts +34 -0
- package/Utils/Monitor/DeviceMacLearningUtil.ts +177 -0
- package/Utils/Monitor/MetricValueFormatter.ts +342 -0
- package/Utils/Monitor/MonitorTemplateCustomFieldUtil.ts +124 -0
- package/Utils/Monitor/NetworkDeviceMonitorTemplateUtil.ts +7 -2
- package/Utils/Monitor/NetworkTopologyUtil.ts +693 -0
- package/Utils/NetworkAutomation/AutoImportRunChain.ts +138 -0
- package/build/dist/Models/DatabaseModels/GlobalConfig.js +12 -12
- package/build/dist/Models/DatabaseModels/GlobalConfig.js.map +1 -1
- package/build/dist/Models/DatabaseModels/InventoryItem.js +34 -1
- package/build/dist/Models/DatabaseModels/InventoryItem.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkDevice.js +128 -0
- package/build/dist/Models/DatabaseModels/NetworkDevice.js.map +1 -1
- package/build/dist/Server/EnvironmentConfig.js +44 -12
- package/build/dist/Server/EnvironmentConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791700000000-AddMacAddressToNetworkDevice.js +14 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791700000000-AddMacAddressToNetworkDevice.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +2 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Infrastructure/Redis.js +8 -8
- package/build/dist/Server/Infrastructure/Redis.js.map +1 -1
- package/build/dist/Server/Infrastructure/Status.js +10 -4
- package/build/dist/Server/Infrastructure/Status.js.map +1 -1
- package/build/dist/Server/Services/DatabaseService.js +58 -13
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/InventoryItemService.js +28 -1
- package/build/dist/Server/Services/InventoryItemService.js.map +1 -1
- package/build/dist/Server/Services/MonitorTemplateService.js +116 -17
- package/build/dist/Server/Services/MonitorTemplateService.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceAutoImportRuleEngineService.js +270 -37
- package/build/dist/Server/Services/NetworkDeviceAutoImportRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceDiscoveryScanService.js +65 -0
- package/build/dist/Server/Services/NetworkDeviceDiscoveryScanService.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceService.js +54 -0
- package/build/dist/Server/Services/NetworkDeviceService.js.map +1 -1
- package/build/dist/Server/Services/UserNotificationRuleService.js +60 -25
- package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/ReadPermission.js +29 -4
- package/build/dist/Server/Types/Database/Permissions/ReadPermission.js.map +1 -1
- package/build/dist/Server/Types/Database/QueryUtil.js +36 -0
- package/build/dist/Server/Types/Database/QueryUtil.js.map +1 -1
- package/build/dist/Server/Types/Markdown.js +75 -0
- package/build/dist/Server/Types/Markdown.js.map +1 -1
- package/build/dist/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.js +53 -18
- package/build/dist/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.js.map +1 -1
- package/build/dist/Server/Utils/EmailRollup/EmailRollupFlushRunner.js +10 -1
- package/build/dist/Server/Utils/EmailRollup/EmailRollupFlushRunner.js.map +1 -1
- package/build/dist/Server/Utils/EmailRollup/EmailRollupRenderer.js +2 -2
- package/build/dist/Server/Utils/EmailRollup/EmailRollupRenderer.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js +58 -35
- package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js +29 -5
- package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +166 -35
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaExpectationBuilder.js +58 -9
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaExpectationBuilder.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaMessageBuilder.js +9 -3
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaMessageBuilder.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaMessageFormatter.js +26 -12
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaMessageFormatter.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js +74 -33
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/NetworkDeviceMacLearningUtil.js +114 -0
- package/build/dist/Server/Utils/Monitor/NetworkDeviceMacLearningUtil.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js +25 -0
- package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/EntityRegistry.js +9 -1
- package/build/dist/Server/Utils/Telemetry/EntityRegistry.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js +70 -13
- package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js.map +1 -1
- package/build/dist/Types/BaseDatabase/IncludesAnyOfGroups.js +54 -0
- package/build/dist/Types/BaseDatabase/IncludesAnyOfGroups.js.map +1 -0
- package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js +1 -0
- package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js.map +1 -1
- package/build/dist/Types/Dashboard/DashboardVariable.js +1 -0
- package/build/dist/Types/Dashboard/DashboardVariable.js.map +1 -1
- package/build/dist/Types/JSON.js +1 -0
- package/build/dist/Types/JSON.js.map +1 -1
- package/build/dist/Types/JSONFunctions.js +13 -0
- package/build/dist/Types/JSONFunctions.js.map +1 -1
- package/build/dist/Types/NetworkAutomation/RuleRunResult.js +291 -6
- package/build/dist/Types/NetworkAutomation/RuleRunResult.js.map +1 -1
- package/build/dist/Types/SerializableObjectDictionary.js +4 -0
- package/build/dist/Types/SerializableObjectDictionary.js.map +1 -1
- package/build/dist/Types/Telemetry/InventoryLiveness.js +37 -0
- package/build/dist/Types/Telemetry/InventoryLiveness.js.map +1 -0
- package/build/dist/UI/Components/CSVFileUpload/CSVFileUpload.js +7 -0
- package/build/dist/UI/Components/CSVFileUpload/CSVFileUpload.js.map +1 -1
- package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js +9 -2
- package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js.map +1 -1
- package/build/dist/UI/Components/Dashboard/DashboardVariableControl.js +137 -0
- package/build/dist/UI/Components/Dashboard/DashboardVariableControl.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/DashboardMonitorListComponent.js +9 -1
- package/build/dist/Utils/Dashboard/Components/DashboardMonitorListComponent.js.map +1 -1
- package/build/dist/Utils/Dashboard/LabelVariable.js +96 -0
- package/build/dist/Utils/Dashboard/LabelVariable.js.map +1 -0
- package/build/dist/Utils/Dashboard/VariableUrlState.js +19 -4
- package/build/dist/Utils/Dashboard/VariableUrlState.js.map +1 -1
- package/build/dist/Utils/MetricUnitUtil.js +26 -0
- package/build/dist/Utils/MetricUnitUtil.js.map +1 -1
- package/build/dist/Utils/Monitor/DeviceMacLearningUtil.js +112 -0
- package/build/dist/Utils/Monitor/DeviceMacLearningUtil.js.map +1 -0
- package/build/dist/Utils/Monitor/MetricValueFormatter.js +276 -0
- package/build/dist/Utils/Monitor/MetricValueFormatter.js.map +1 -0
- package/build/dist/Utils/Monitor/MonitorTemplateCustomFieldUtil.js +101 -0
- package/build/dist/Utils/Monitor/MonitorTemplateCustomFieldUtil.js.map +1 -0
- package/build/dist/Utils/Monitor/NetworkDeviceMonitorTemplateUtil.js +7 -2
- package/build/dist/Utils/Monitor/NetworkDeviceMonitorTemplateUtil.js.map +1 -1
- package/build/dist/Utils/Monitor/NetworkTopologyUtil.js +484 -0
- package/build/dist/Utils/Monitor/NetworkTopologyUtil.js.map +1 -1
- package/build/dist/Utils/NetworkAutomation/AutoImportRunChain.js +59 -0
- package/build/dist/Utils/NetworkAutomation/AutoImportRunChain.js.map +1 -0
- package/jest.config.json +2 -0
- package/package.json +1 -1
- package/test-setup.sh +9 -9
- package/tsconfig.json +51 -11
- package/Tests/App/Dashboard/NotificationEmailPreferences.test.tsx +0 -399
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
import AutoImportRunChain, {
|
|
2
|
+
AutoImportRunChainOutcome,
|
|
3
|
+
} from "../../../Utils/NetworkAutomation/AutoImportRunChain";
|
|
4
|
+
import {
|
|
5
|
+
AutoImportRuleRunResult,
|
|
6
|
+
MAX_AUTO_IMPORT_RUN_PASSES,
|
|
7
|
+
} from "../../../Types/NetworkAutomation/RuleRunResult";
|
|
8
|
+
import { describe, expect, it } from "@jest/globals";
|
|
9
|
+
|
|
10
|
+
/*
|
|
11
|
+
* One press of "Run Rule" drives as many capped server passes as the estate
|
|
12
|
+
* needs. Before OneUptime issue #3642 it drove exactly one, so a 909-device
|
|
13
|
+
* estate imported 500 and stopped — with the remainder reachable only by an
|
|
14
|
+
* operator who noticed the cap line and pressed the button again.
|
|
15
|
+
*
|
|
16
|
+
* The loop's exits are the whole contract: done, no progress, cancelled, or
|
|
17
|
+
* the chain's own ceiling. Each one is asserted here, with no renderer in the
|
|
18
|
+
* import graph.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
function pass(
|
|
22
|
+
overrides: Partial<AutoImportRuleRunResult>,
|
|
23
|
+
): AutoImportRuleRunResult {
|
|
24
|
+
return {
|
|
25
|
+
hostsEvaluated: 0,
|
|
26
|
+
hostsMatched: 0,
|
|
27
|
+
hostsExcluded: 0,
|
|
28
|
+
hostsSkippedAlreadyRegistered: 0,
|
|
29
|
+
devicesCreated: 0,
|
|
30
|
+
devicesFailed: 0,
|
|
31
|
+
monitorsWouldCreate: 0,
|
|
32
|
+
monitorsCreated: 0,
|
|
33
|
+
monitorsSkippedAlreadyExisting: 0,
|
|
34
|
+
monitorsSkippedUnsupportedHost: 0,
|
|
35
|
+
monitorsFailed: 0,
|
|
36
|
+
deviceFailureReasons: [],
|
|
37
|
+
monitorFailureReasons: [],
|
|
38
|
+
monitorProvisioningHalted: false,
|
|
39
|
+
isTruncated: false,
|
|
40
|
+
hostsPendingImport: 0,
|
|
41
|
+
monitorsPendingCreation: 0,
|
|
42
|
+
hasUnevaluatedScans: false,
|
|
43
|
+
hasMoreScans: false,
|
|
44
|
+
isDryRun: false,
|
|
45
|
+
matchedIpAddressSample: [],
|
|
46
|
+
...overrides,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Answers with the given passes in order, then repeats the last one forever.
|
|
51
|
+
function serve(responses: Array<AutoImportRuleRunResult | null>): {
|
|
52
|
+
runPass: () => Promise<AutoImportRuleRunResult | null>;
|
|
53
|
+
calls: () => number;
|
|
54
|
+
} {
|
|
55
|
+
let index: number = 0;
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
calls: (): number => {
|
|
59
|
+
return index;
|
|
60
|
+
},
|
|
61
|
+
runPass: async (): Promise<AutoImportRuleRunResult | null> => {
|
|
62
|
+
const answer: AutoImportRuleRunResult | null =
|
|
63
|
+
responses[Math.min(index, responses.length - 1)] ?? null;
|
|
64
|
+
index++;
|
|
65
|
+
return answer;
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
describe("AutoImportRunChain", () => {
|
|
71
|
+
/*
|
|
72
|
+
* The issue, reproduced at the seam that fixes it: the server can only do
|
|
73
|
+
* 500 at a time, the estate is 909, and one press has to finish it.
|
|
74
|
+
*/
|
|
75
|
+
it("keeps running passes until the server stops reporting truncation", async () => {
|
|
76
|
+
const server: ReturnType<typeof serve> = serve([
|
|
77
|
+
pass({
|
|
78
|
+
hostsEvaluated: 909,
|
|
79
|
+
hostsMatched: 909,
|
|
80
|
+
devicesCreated: 500,
|
|
81
|
+
hostsPendingImport: 409,
|
|
82
|
+
isTruncated: true,
|
|
83
|
+
}),
|
|
84
|
+
pass({
|
|
85
|
+
hostsEvaluated: 909,
|
|
86
|
+
hostsMatched: 909,
|
|
87
|
+
hostsSkippedAlreadyRegistered: 500,
|
|
88
|
+
devicesCreated: 409,
|
|
89
|
+
}),
|
|
90
|
+
]);
|
|
91
|
+
|
|
92
|
+
const outcome: AutoImportRunChainOutcome = await AutoImportRunChain.run({
|
|
93
|
+
isDryRun: false,
|
|
94
|
+
runPass: server.runPass,
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
expect(server.calls()).toBe(2);
|
|
98
|
+
expect(outcome.passCount).toBe(2);
|
|
99
|
+
expect(outcome.result?.devicesCreated).toBe(909);
|
|
100
|
+
expect(outcome.result?.isTruncated).toBe(false);
|
|
101
|
+
expect(outcome.result?.hostsPendingImport).toBe(0);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it("stops after one pass when nothing was truncated", async () => {
|
|
105
|
+
const server: ReturnType<typeof serve> = serve([
|
|
106
|
+
pass({ devicesCreated: 3 }),
|
|
107
|
+
]);
|
|
108
|
+
|
|
109
|
+
const outcome: AutoImportRunChainOutcome = await AutoImportRunChain.run({
|
|
110
|
+
isDryRun: false,
|
|
111
|
+
runPass: server.runPass,
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
expect(server.calls()).toBe(1);
|
|
115
|
+
expect(outcome.result?.devicesCreated).toBe(3);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
/*
|
|
119
|
+
* A pass that hit the cap without creating anything — every attempt failed,
|
|
120
|
+
* or a concurrent sweep had already spent the project's budget — would fail
|
|
121
|
+
* identically next time. Chaining into it would spin.
|
|
122
|
+
*/
|
|
123
|
+
it("stops when a truncated pass made no progress", async () => {
|
|
124
|
+
const server: ReturnType<typeof serve> = serve([
|
|
125
|
+
pass({
|
|
126
|
+
devicesCreated: 0,
|
|
127
|
+
monitorsCreated: 0,
|
|
128
|
+
devicesFailed: 500,
|
|
129
|
+
hostsPendingImport: 409,
|
|
130
|
+
isTruncated: true,
|
|
131
|
+
}),
|
|
132
|
+
]);
|
|
133
|
+
|
|
134
|
+
const outcome: AutoImportRunChainOutcome = await AutoImportRunChain.run({
|
|
135
|
+
isDryRun: false,
|
|
136
|
+
runPass: server.runPass,
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
expect(server.calls()).toBe(1);
|
|
140
|
+
expect(outcome.result?.isTruncated).toBe(true);
|
|
141
|
+
// The remainder is still reported, so the operator is not left guessing.
|
|
142
|
+
expect(outcome.result?.hostsPendingImport).toBe(409);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
/*
|
|
146
|
+
* A pass that halted monitor provisioning has proved the fault is systemic
|
|
147
|
+
* (issue #3643). It creates nothing, so the no-progress exit already stops
|
|
148
|
+
* the chain — pinned here because chaining into a halted run would repeat
|
|
149
|
+
* the identical failure up to the pass ceiling.
|
|
150
|
+
*/
|
|
151
|
+
it("does not chain past a run that halted monitor provisioning", async () => {
|
|
152
|
+
const server: ReturnType<typeof serve> = serve([
|
|
153
|
+
pass({
|
|
154
|
+
monitorsFailed: 500,
|
|
155
|
+
monitorProvisioningHalted: true,
|
|
156
|
+
monitorFailureReasons: [
|
|
157
|
+
"This rule's Monitor Template could not be loaded.",
|
|
158
|
+
],
|
|
159
|
+
monitorsPendingCreation: 409,
|
|
160
|
+
isTruncated: true,
|
|
161
|
+
}),
|
|
162
|
+
]);
|
|
163
|
+
|
|
164
|
+
const outcome: AutoImportRunChainOutcome = await AutoImportRunChain.run({
|
|
165
|
+
isDryRun: false,
|
|
166
|
+
runPass: server.runPass,
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
expect(server.calls()).toBe(1);
|
|
170
|
+
expect(outcome.result?.monitorProvisioningHalted).toBe(true);
|
|
171
|
+
expect(outcome.result?.monitorFailureReasons).toHaveLength(1);
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
// A monitor-only pass IS progress, even with zero devices created.
|
|
175
|
+
it("keeps going when only monitors were created", async () => {
|
|
176
|
+
const server: ReturnType<typeof serve> = serve([
|
|
177
|
+
pass({
|
|
178
|
+
monitorsCreated: 500,
|
|
179
|
+
monitorsPendingCreation: 409,
|
|
180
|
+
isTruncated: true,
|
|
181
|
+
}),
|
|
182
|
+
pass({ monitorsCreated: 409 }),
|
|
183
|
+
]);
|
|
184
|
+
|
|
185
|
+
const outcome: AutoImportRunChainOutcome = await AutoImportRunChain.run({
|
|
186
|
+
isDryRun: false,
|
|
187
|
+
runPass: server.runPass,
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
expect(server.calls()).toBe(2);
|
|
191
|
+
expect(outcome.result?.monitorsCreated).toBe(909);
|
|
192
|
+
expect(outcome.result?.monitorsPendingCreation).toBe(0);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
/*
|
|
196
|
+
* A dry run writes nothing, so the next pass would evaluate the identical
|
|
197
|
+
* estate and answer identically. Chaining it would be an infinite loop that
|
|
198
|
+
* accomplishes nothing.
|
|
199
|
+
*/
|
|
200
|
+
it("never chains a dry run", async () => {
|
|
201
|
+
const server: ReturnType<typeof serve> = serve([
|
|
202
|
+
pass({ isDryRun: true, isTruncated: true, monitorsWouldCreate: 500 }),
|
|
203
|
+
]);
|
|
204
|
+
|
|
205
|
+
const outcome: AutoImportRunChainOutcome = await AutoImportRunChain.run({
|
|
206
|
+
isDryRun: true,
|
|
207
|
+
runPass: server.runPass,
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
expect(server.calls()).toBe(1);
|
|
211
|
+
expect(outcome.result?.isTruncated).toBe(true);
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
/*
|
|
215
|
+
* "One press finishes the job" must not mean "one press runs for an hour".
|
|
216
|
+
* Past the ceiling the operator gets a report of what is left and decides.
|
|
217
|
+
*/
|
|
218
|
+
it("stops at its own ceiling and reports what is left", async () => {
|
|
219
|
+
const server: ReturnType<typeof serve> = serve([
|
|
220
|
+
pass({ devicesCreated: 500, hostsPendingImport: 400, isTruncated: true }),
|
|
221
|
+
]);
|
|
222
|
+
|
|
223
|
+
const outcome: AutoImportRunChainOutcome = await AutoImportRunChain.run({
|
|
224
|
+
isDryRun: false,
|
|
225
|
+
maxPasses: 3,
|
|
226
|
+
runPass: server.runPass,
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
expect(server.calls()).toBe(3);
|
|
230
|
+
expect(outcome.passCount).toBe(3);
|
|
231
|
+
expect(outcome.result?.devicesCreated).toBe(1500);
|
|
232
|
+
expect(outcome.result?.isTruncated).toBe(true);
|
|
233
|
+
expect(outcome.result?.hostsPendingImport).toBe(400);
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
it("defaults to the shared pass ceiling", async () => {
|
|
237
|
+
const server: ReturnType<typeof serve> = serve([
|
|
238
|
+
pass({ devicesCreated: 1, hostsPendingImport: 1, isTruncated: true }),
|
|
239
|
+
]);
|
|
240
|
+
|
|
241
|
+
await AutoImportRunChain.run({ isDryRun: false, runPass: server.runPass });
|
|
242
|
+
|
|
243
|
+
expect(server.calls()).toBe(MAX_AUTO_IMPORT_RUN_PASSES);
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
/*
|
|
247
|
+
* A failed pass does not undo the ones before it: 500 devices really were
|
|
248
|
+
* imported, and throwing that summary away would leave the operator less
|
|
249
|
+
* informed than before they pressed the button.
|
|
250
|
+
*/
|
|
251
|
+
it("keeps earlier passes when a later one fails", async () => {
|
|
252
|
+
const server: ReturnType<typeof serve> = serve([
|
|
253
|
+
pass({ devicesCreated: 500, hostsPendingImport: 409, isTruncated: true }),
|
|
254
|
+
null,
|
|
255
|
+
]);
|
|
256
|
+
|
|
257
|
+
const outcome: AutoImportRunChainOutcome = await AutoImportRunChain.run({
|
|
258
|
+
isDryRun: false,
|
|
259
|
+
runPass: server.runPass,
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
expect(server.calls()).toBe(2);
|
|
263
|
+
expect(outcome.passCount).toBe(1);
|
|
264
|
+
expect(outcome.result?.devicesCreated).toBe(500);
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
it("has nothing to report when the very first pass fails", async () => {
|
|
268
|
+
const outcome: AutoImportRunChainOutcome = await AutoImportRunChain.run({
|
|
269
|
+
isDryRun: false,
|
|
270
|
+
runPass: async (): Promise<AutoImportRuleRunResult | null> => {
|
|
271
|
+
return null;
|
|
272
|
+
},
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
expect(outcome.result).toBeNull();
|
|
276
|
+
expect(outcome.passCount).toBe(0);
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
// Closing the dialog must not leave a chain importing behind the operator.
|
|
280
|
+
it("stops chaining once cancelled", async () => {
|
|
281
|
+
const server: ReturnType<typeof serve> = serve([
|
|
282
|
+
pass({ devicesCreated: 500, hostsPendingImport: 409, isTruncated: true }),
|
|
283
|
+
]);
|
|
284
|
+
let cancelled: boolean = false;
|
|
285
|
+
|
|
286
|
+
const outcome: AutoImportRunChainOutcome = await AutoImportRunChain.run({
|
|
287
|
+
isDryRun: false,
|
|
288
|
+
runPass: async (): Promise<AutoImportRuleRunResult | null> => {
|
|
289
|
+
cancelled = true;
|
|
290
|
+
return server.runPass();
|
|
291
|
+
},
|
|
292
|
+
isCancelled: (): boolean => {
|
|
293
|
+
return cancelled;
|
|
294
|
+
},
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
expect(server.calls()).toBe(1);
|
|
298
|
+
expect(outcome.wasCancelled).toBe(true);
|
|
299
|
+
// What the one completed pass did is still accounted for.
|
|
300
|
+
expect(outcome.result?.devicesCreated).toBe(500);
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
it("reports progress after each truncated pass, never after the last", async () => {
|
|
304
|
+
const server: ReturnType<typeof serve> = serve([
|
|
305
|
+
pass({ devicesCreated: 500, hostsPendingImport: 409, isTruncated: true }),
|
|
306
|
+
pass({ devicesCreated: 409 }),
|
|
307
|
+
]);
|
|
308
|
+
const progressSeen: Array<number> = [];
|
|
309
|
+
|
|
310
|
+
await AutoImportRunChain.run({
|
|
311
|
+
isDryRun: false,
|
|
312
|
+
runPass: server.runPass,
|
|
313
|
+
onProgress: (progress: AutoImportRuleRunResult): void => {
|
|
314
|
+
progressSeen.push(progress.devicesCreated);
|
|
315
|
+
},
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
expect(progressSeen).toEqual([500]);
|
|
319
|
+
});
|
|
320
|
+
});
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import BadDataException from "../Exception/BadDataException";
|
|
2
|
+
import { JSONObject, ObjectType } from "../JSON";
|
|
3
|
+
import QueryOperator from "./QueryOperator";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Matches at least one related entity in each group. Groups are combined with
|
|
7
|
+
* AND, while ids within a group are combined with OR. An empty group matches
|
|
8
|
+
* nothing; callers should omit the operator when there are no conditions.
|
|
9
|
+
*/
|
|
10
|
+
export default class IncludesAnyOfGroups extends QueryOperator<
|
|
11
|
+
Array<Array<string>>
|
|
12
|
+
> {
|
|
13
|
+
private readonly _groups: Array<Array<string>>;
|
|
14
|
+
|
|
15
|
+
public constructor(groups: Array<Array<string>>) {
|
|
16
|
+
super();
|
|
17
|
+
|
|
18
|
+
if (!Array.isArray(groups) || groups.length === 0 || groups.length > 32) {
|
|
19
|
+
throw new BadDataException(
|
|
20
|
+
"IncludesAnyOfGroups requires between 1 and 32 groups.",
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
let totalValues: number = 0;
|
|
25
|
+
for (const group of groups) {
|
|
26
|
+
if (!Array.isArray(group) || group.length > 1000) {
|
|
27
|
+
throw new BadDataException(
|
|
28
|
+
"IncludesAnyOfGroups requires arrays of at most 1000 ids.",
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
totalValues += group.length;
|
|
33
|
+
for (const value of group) {
|
|
34
|
+
if (
|
|
35
|
+
typeof value !== "string" ||
|
|
36
|
+
value.length === 0 ||
|
|
37
|
+
value.length > 256
|
|
38
|
+
) {
|
|
39
|
+
throw new BadDataException(
|
|
40
|
+
"IncludesAnyOfGroups ids must be nonempty strings of at most 256 characters.",
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (totalValues > 10000) {
|
|
47
|
+
throw new BadDataException(
|
|
48
|
+
"IncludesAnyOfGroups supports at most 10000 ids in total.",
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
this._groups = groups.map((group: Array<string>) => {
|
|
53
|
+
return [...new Set(group)];
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
public get groups(): Array<Array<string>> {
|
|
58
|
+
return this._groups.map((group: Array<string>) => {
|
|
59
|
+
return [...group];
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
public override toJSON(): JSONObject {
|
|
64
|
+
return {
|
|
65
|
+
_type: ObjectType.IncludesAnyOfGroups,
|
|
66
|
+
value: this.groups,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
public static override fromJSON(json: JSONObject): IncludesAnyOfGroups {
|
|
71
|
+
if (json["_type"] !== ObjectType.IncludesAnyOfGroups) {
|
|
72
|
+
throw new BadDataException("Invalid IncludesAnyOfGroups JSON type.");
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return new IncludesAnyOfGroups(json["value"] as Array<Array<string>>);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -18,6 +18,7 @@ export enum ComponentInputType {
|
|
|
18
18
|
MultiSelectDropdown = "MultiSelectDropdown",
|
|
19
19
|
EntityDropdown = "EntityDropdown",
|
|
20
20
|
EntityMultiSelectDropdown = "EntityMultiSelectDropdown",
|
|
21
|
+
ProjectLabelVariable = "ProjectLabelVariable",
|
|
21
22
|
// Monaco code editors, one per language.
|
|
22
23
|
Html = "Html",
|
|
23
24
|
Css = "Css",
|
|
@@ -13,5 +13,6 @@ export default interface DashboardMonitorListComponent extends BaseComponent {
|
|
|
13
13
|
monitorStatusIds?: Array<string> | undefined;
|
|
14
14
|
monitorTypes?: Array<string> | undefined;
|
|
15
15
|
labelIds?: Array<string> | undefined;
|
|
16
|
+
labelVariableId?: string | undefined;
|
|
16
17
|
};
|
|
17
18
|
}
|
|
@@ -3,6 +3,12 @@ export enum DashboardVariableType {
|
|
|
3
3
|
Query = "Query",
|
|
4
4
|
TextInput = "Text Input",
|
|
5
5
|
TelemetryAttribute = "Telemetry Attribute",
|
|
6
|
+
ProjectLabel = "Project Labels",
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface DashboardVariableOption {
|
|
10
|
+
label: string;
|
|
11
|
+
value: string;
|
|
6
12
|
}
|
|
7
13
|
|
|
8
14
|
export default interface DashboardVariable {
|
|
@@ -12,6 +18,8 @@ export default interface DashboardVariable {
|
|
|
12
18
|
type: DashboardVariableType;
|
|
13
19
|
// For CustomList: comma-separated values
|
|
14
20
|
customListValues?: string | undefined;
|
|
21
|
+
// Author-selected project label IDs and their published display names.
|
|
22
|
+
labelOptions?: Array<DashboardVariableOption> | undefined;
|
|
15
23
|
// For Query: a ClickHouse query to populate options
|
|
16
24
|
query?: string | undefined;
|
|
17
25
|
/*
|
package/Types/JSON.ts
CHANGED
|
@@ -8,6 +8,7 @@ import GreaterThanOrEqual from "./BaseDatabase/GreaterThanOrEqual";
|
|
|
8
8
|
import InBetween from "./BaseDatabase/InBetween";
|
|
9
9
|
import Includes from "./BaseDatabase/Includes";
|
|
10
10
|
import IncludesAll from "./BaseDatabase/IncludesAll";
|
|
11
|
+
import IncludesAnyOfGroups from "./BaseDatabase/IncludesAnyOfGroups";
|
|
11
12
|
import IncludesNone from "./BaseDatabase/IncludesNone";
|
|
12
13
|
import StartsWith from "./BaseDatabase/StartsWith";
|
|
13
14
|
import EndsWith from "./BaseDatabase/EndsWith";
|
|
@@ -81,6 +82,7 @@ export enum ObjectType {
|
|
|
81
82
|
IsNull = "IsNull",
|
|
82
83
|
Includes = "Includes",
|
|
83
84
|
IncludesAll = "IncludesAll",
|
|
85
|
+
IncludesAnyOfGroups = "IncludesAnyOfGroups",
|
|
84
86
|
IncludesNone = "IncludesNone",
|
|
85
87
|
StartsWith = "StartsWith",
|
|
86
88
|
EndsWith = "EndsWith",
|
|
@@ -176,6 +178,8 @@ export type JSONValue =
|
|
|
176
178
|
| Array<Includes>
|
|
177
179
|
| IncludesAll
|
|
178
180
|
| Array<IncludesAll>
|
|
181
|
+
| IncludesAnyOfGroups
|
|
182
|
+
| Array<IncludesAnyOfGroups>
|
|
179
183
|
| IncludesNone
|
|
180
184
|
| Array<IncludesNone>
|
|
181
185
|
| StartsWith<string>
|
package/Types/JSONFunctions.ts
CHANGED
|
@@ -548,6 +548,16 @@ export default class JSONFunctions {
|
|
|
548
548
|
return JSON5.parse(val);
|
|
549
549
|
}
|
|
550
550
|
|
|
551
|
+
/*
|
|
552
|
+
* Guarding only against an array leaves every OTHER non-object through:
|
|
553
|
+
* `42`, `"text"`, `true` and `null` are all valid JSON that JSON5.parse
|
|
554
|
+
* returns happily, and none of them is an array, so each used to be handed
|
|
555
|
+
* back typed as a JSONObject. Nothing downstream re-checks - the Text to
|
|
556
|
+
* JSON workflow component reported a bare number as a SUCCESSFUL parse and
|
|
557
|
+
* put it on its `json` port, and JSONWebToken.decodeJsonPayload read
|
|
558
|
+
* properties off a string payload and got undefined for every one of them.
|
|
559
|
+
* A parse that cannot produce an object should say so where it happens.
|
|
560
|
+
*/
|
|
551
561
|
public static parseJSONObject(val: string): JSONObject {
|
|
552
562
|
const result: JSONObject | JSONArray = this.parse(val);
|
|
553
563
|
|
|
@@ -555,6 +565,12 @@ export default class JSONFunctions {
|
|
|
555
565
|
throw new Error("Expected JSONObject, but got JSONArray");
|
|
556
566
|
}
|
|
557
567
|
|
|
568
|
+
if (result === null || typeof result !== "object") {
|
|
569
|
+
throw new Error(
|
|
570
|
+
`Expected JSONObject, but got ${result === null ? "null" : typeof result}`,
|
|
571
|
+
);
|
|
572
|
+
}
|
|
573
|
+
|
|
558
574
|
return result;
|
|
559
575
|
}
|
|
560
576
|
|
|
@@ -237,6 +237,19 @@ export interface NetworkTopologyEdge {
|
|
|
237
237
|
* absent is "not stated", never "these are peers".
|
|
238
238
|
*/
|
|
239
239
|
parentNodeId?: string | undefined;
|
|
240
|
+
/*
|
|
241
|
+
* On an "fdb" edge, the end whose forwarding table LEARNED the other
|
|
242
|
+
* end's MAC — the switch. Always one of `fromNodeId`/`toNodeId`.
|
|
243
|
+
*
|
|
244
|
+
* Needed because the pair may already have been joined the other way
|
|
245
|
+
* round: a hand-drawn link stored device→switch, or an LLDP edge read
|
|
246
|
+
* from the device's side, keeps its ends when the attachment merges in,
|
|
247
|
+
* so "the switch is the from end" is true of a fresh attachment and
|
|
248
|
+
* false of a merged one. Readers that need to know which end learned
|
|
249
|
+
* (the drawer's "Connected to", the link panel's sentence) read this,
|
|
250
|
+
* and fall back to `fromNodeId` on a payload that predates it.
|
|
251
|
+
*/
|
|
252
|
+
learnedByNodeId?: string | undefined;
|
|
240
253
|
}
|
|
241
254
|
|
|
242
255
|
export default interface NetworkTopology {
|