@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,147 @@
|
|
|
1
|
+
import Entities from "../../Models/DatabaseModels/Index";
|
|
2
|
+
import InventoryItem from "../../Models/DatabaseModels/InventoryItem";
|
|
3
|
+
import QueryUtil from "../../Server/Types/Database/QueryUtil";
|
|
4
|
+
import Query from "../../Types/BaseDatabase/Query";
|
|
5
|
+
import Includes from "../../Types/BaseDatabase/Includes";
|
|
6
|
+
import IncludesNone from "../../Types/BaseDatabase/IncludesNone";
|
|
7
|
+
import LessThan from "../../Types/BaseDatabase/LessThan";
|
|
8
|
+
import { getColumnAccessControlForAllColumns } from "../../Types/Database/AccessControl/ColumnAccessControl";
|
|
9
|
+
import { ColumnAccessControl } from "../../Types/BaseDatabase/AccessControl";
|
|
10
|
+
import TableColumnType from "../../Types/Database/TableColumnType";
|
|
11
|
+
import { JSONObject } from "../../Types/JSON";
|
|
12
|
+
import JSONFunctions from "../../Types/JSONFunctions";
|
|
13
|
+
import ObjectID from "../../Types/ObjectID";
|
|
14
|
+
import EntitySource from "../../Types/Telemetry/EntitySource";
|
|
15
|
+
import EntityType from "../../Types/Telemetry/EntityType";
|
|
16
|
+
import { InventoryLiveness } from "../../Types/Telemetry/InventoryLiveness";
|
|
17
|
+
import { DataSource, FindOptionsWhere } from "typeorm";
|
|
18
|
+
import { ColumnMetadata } from "typeorm/metadata/ColumnMetadata";
|
|
19
|
+
|
|
20
|
+
describe("inventory status is a read-only computed query column", () => {
|
|
21
|
+
let database: DataSource;
|
|
22
|
+
|
|
23
|
+
beforeAll(async () => {
|
|
24
|
+
database = new DataSource({
|
|
25
|
+
type: "postgres",
|
|
26
|
+
entities: Entities,
|
|
27
|
+
synchronize: false,
|
|
28
|
+
});
|
|
29
|
+
await (
|
|
30
|
+
database as unknown as { buildMetadatas: () => Promise<void> }
|
|
31
|
+
).buildMetadatas();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
function buildQuery(query: Query<InventoryItem>): [string, Array<unknown>] {
|
|
35
|
+
return database
|
|
36
|
+
.getRepository(InventoryItem)
|
|
37
|
+
.createQueryBuilder("inventory")
|
|
38
|
+
.setFindOptions({
|
|
39
|
+
select: { _id: true, inventoryStatus: true },
|
|
40
|
+
where: QueryUtil.serializeQuery(
|
|
41
|
+
InventoryItem,
|
|
42
|
+
query,
|
|
43
|
+
) as unknown as FindOptionsWhere<InventoryItem>,
|
|
44
|
+
})
|
|
45
|
+
.getQueryAndParameters();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
test("TypeORM excludes status from inserts, updates and physical schema columns", () => {
|
|
49
|
+
const column: ColumnMetadata = database
|
|
50
|
+
.getMetadata(InventoryItem)
|
|
51
|
+
.findColumnWithPropertyName("inventoryStatus")!;
|
|
52
|
+
expect(column.isVirtualProperty).toBe(true);
|
|
53
|
+
expect(column.isInsert).toBe(false);
|
|
54
|
+
expect(column.isUpdate).toBe(false);
|
|
55
|
+
expect(column.query).toBeDefined();
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test("model metadata allows reads and filtering but rejects client writes", () => {
|
|
59
|
+
const model: InventoryItem = new InventoryItem();
|
|
60
|
+
expect(model.getTableColumnMetadata("inventoryStatus").type).toBe(
|
|
61
|
+
TableColumnType.ShortText,
|
|
62
|
+
);
|
|
63
|
+
const control: ColumnAccessControl =
|
|
64
|
+
getColumnAccessControlForAllColumns(model)["inventoryStatus"]!;
|
|
65
|
+
expect(control.read.length).toBeGreaterThan(0);
|
|
66
|
+
expect(control.create).toEqual([]);
|
|
67
|
+
expect(control.update).toEqual([]);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test.each([false, true])(
|
|
71
|
+
"the real query compiler uses CASE for a status selection (negated: %s)",
|
|
72
|
+
(negated: boolean) => {
|
|
73
|
+
const values: Array<string> = [
|
|
74
|
+
InventoryLiveness.Live,
|
|
75
|
+
InventoryLiveness.Never,
|
|
76
|
+
];
|
|
77
|
+
const [sql, parameters]: [string, Array<unknown>] = buildQuery({
|
|
78
|
+
inventoryStatus: negated
|
|
79
|
+
? new IncludesNone(values)
|
|
80
|
+
: new Includes(values),
|
|
81
|
+
});
|
|
82
|
+
const where: string = sql.slice(sql.indexOf("WHERE"));
|
|
83
|
+
expect(where).toContain("CASE");
|
|
84
|
+
expect(where).toContain('"inventory"."source"');
|
|
85
|
+
expect(where).toContain('"inventory"."lastSeenAt"');
|
|
86
|
+
expect(where).not.toContain('"inventory"."inventoryStatus"');
|
|
87
|
+
expect(where).toContain(negated ? "NOT IN" : " IN");
|
|
88
|
+
expect(parameters).toEqual(values);
|
|
89
|
+
expect(sql).toContain('AS "inventory_inventoryStatus"');
|
|
90
|
+
},
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
test("status composes with type, source, dates, project and archive filters", () => {
|
|
94
|
+
const projectId: ObjectID = ObjectID.generate();
|
|
95
|
+
const before: Date = new Date("2026-09-06T12:00:00.000Z");
|
|
96
|
+
const [sql, parameters]: [string, Array<unknown>] = buildQuery({
|
|
97
|
+
projectId,
|
|
98
|
+
entityType: new Includes([EntityType.Service]),
|
|
99
|
+
source: EntitySource.Discovered,
|
|
100
|
+
lastSeenAt: new LessThan(before.toISOString()),
|
|
101
|
+
isArchived: false,
|
|
102
|
+
inventoryStatus: new Includes([InventoryLiveness.Stale]),
|
|
103
|
+
});
|
|
104
|
+
for (const column of [
|
|
105
|
+
"projectId",
|
|
106
|
+
"entityType",
|
|
107
|
+
"source",
|
|
108
|
+
"lastSeenAt",
|
|
109
|
+
"isArchived",
|
|
110
|
+
]) {
|
|
111
|
+
expect(sql).toContain(`"inventory"."${column}"`);
|
|
112
|
+
}
|
|
113
|
+
expect(parameters).toEqual(
|
|
114
|
+
expect.arrayContaining([
|
|
115
|
+
projectId.toString(),
|
|
116
|
+
EntityType.Service,
|
|
117
|
+
EntitySource.Discovered,
|
|
118
|
+
before.toISOString(),
|
|
119
|
+
false,
|
|
120
|
+
InventoryLiveness.Stale,
|
|
121
|
+
]),
|
|
122
|
+
);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
test("API serialization preserves status negation and multiple values", () => {
|
|
126
|
+
const payload: JSONObject = JSON.parse(
|
|
127
|
+
JSON.stringify(
|
|
128
|
+
JSONFunctions.serialize({
|
|
129
|
+
inventoryStatus: new IncludesNone([
|
|
130
|
+
InventoryLiveness.Stale,
|
|
131
|
+
InventoryLiveness.Never,
|
|
132
|
+
]),
|
|
133
|
+
}),
|
|
134
|
+
),
|
|
135
|
+
);
|
|
136
|
+
const query: Query<InventoryItem> = JSONFunctions.deserialize(
|
|
137
|
+
payload,
|
|
138
|
+
) as Query<InventoryItem>;
|
|
139
|
+
expect(query.inventoryStatus).toBeInstanceOf(IncludesNone);
|
|
140
|
+
const [sql, parameters]: [string, Array<unknown>] = buildQuery(query);
|
|
141
|
+
expect(sql).toContain("NOT IN");
|
|
142
|
+
expect(parameters).toEqual([
|
|
143
|
+
InventoryLiveness.Stale,
|
|
144
|
+
InventoryLiveness.Never,
|
|
145
|
+
]);
|
|
146
|
+
});
|
|
147
|
+
});
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NetworkDevice.macAddress column contract (issue #3489).
|
|
3
|
+
*
|
|
4
|
+
* A ping-only device — a POS register, a handset, a kiosk — speaks neither
|
|
5
|
+
* LLDP nor CDP, so nothing it says places it on a switch port. The switches
|
|
6
|
+
* already know: every walked switch reports its forwarding table, and the
|
|
7
|
+
* server keeps one NetworkEndpoint row per MAC it learned. This column is
|
|
8
|
+
* what lets a device be recognised in that inventory, and everything below
|
|
9
|
+
* pins a property that, quietly changed, either refuses a write the device
|
|
10
|
+
* form is about to make or leaves the column unreadable by the one page that
|
|
11
|
+
* needs it.
|
|
12
|
+
*
|
|
13
|
+
* Three properties matter more than the rest:
|
|
14
|
+
*
|
|
15
|
+
* - NOT REQUIRED. The MAC is optional twice over — a device whose hostname
|
|
16
|
+
* is an IP address that a router's ARP table resolves is matched by
|
|
17
|
+
* address without it — and every existing create path (the form, the
|
|
18
|
+
* CRUD API, discovery import, seeding) omits it. A required column would
|
|
19
|
+
* break all of them at once.
|
|
20
|
+
* - NULLABLE WITH NO DEFAULT. NULL means "not declared, and not learned
|
|
21
|
+
* yet", which is what every row already on disk should say; a default
|
|
22
|
+
* would give the fleet a MAC it does not have, and the ARP learner only
|
|
23
|
+
* ever fills an EMPTY value, so a default would also stop it filling
|
|
24
|
+
* anything.
|
|
25
|
+
* - READABLE ON A RELATION QUERY, and governed exactly as `hostname` is.
|
|
26
|
+
* The topology API selects it off the device list, and an operator who
|
|
27
|
+
* may set a device's address may set its MAC — the two are the layer-3
|
|
28
|
+
* and layer-2 spellings of the same fact.
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
import NetworkDevice from "../../Models/DatabaseModels/NetworkDevice";
|
|
32
|
+
import { ColumnAccessControl } from "../../Types/BaseDatabase/AccessControl";
|
|
33
|
+
import { TableColumnMetadata } from "../../Types/Database/TableColumn";
|
|
34
|
+
import TableColumnType from "../../Types/Database/TableColumnType";
|
|
35
|
+
import ColumnType from "../../Types/Database/ColumnType";
|
|
36
|
+
import ColumnLength from "../../Types/Database/ColumnLength";
|
|
37
|
+
import Columns from "../../Types/Database/Columns";
|
|
38
|
+
import Permission from "../../Types/Permission";
|
|
39
|
+
import { normalizeMac } from "../../Utils/Monitor/EndpointAttachmentUtil";
|
|
40
|
+
import { describe, expect, test } from "@jest/globals";
|
|
41
|
+
import { getMetadataArgsStorage } from "typeorm";
|
|
42
|
+
import { ColumnMetadataArgs } from "typeorm/metadata-args/ColumnMetadataArgs";
|
|
43
|
+
|
|
44
|
+
const COLUMN: string = "macAddress";
|
|
45
|
+
|
|
46
|
+
/*
|
|
47
|
+
* The column this one is measured against. `hostname` is the device's
|
|
48
|
+
* layer-3 address and this is its layer-2 one; whoever may state one may
|
|
49
|
+
* state the other. Compared against rather than written out as a permission
|
|
50
|
+
* list, so the two are revised together or not at all.
|
|
51
|
+
*/
|
|
52
|
+
const PEER_ADDRESS_COLUMN: string = "hostname";
|
|
53
|
+
|
|
54
|
+
function metadata(): TableColumnMetadata {
|
|
55
|
+
return new NetworkDevice().getTableColumnMetadata(COLUMN);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function typeOrmColumn(): ColumnMetadataArgs | undefined {
|
|
59
|
+
return getMetadataArgsStorage().columns.find((column: ColumnMetadataArgs) => {
|
|
60
|
+
return column.target === NetworkDevice && column.propertyName === COLUMN;
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function accessControlFor(column: string): ColumnAccessControl | null {
|
|
65
|
+
return new NetworkDevice().getColumnAccessControlFor(column);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
describe("NetworkDevice.macAddress", () => {
|
|
69
|
+
test("exists as a short text column", () => {
|
|
70
|
+
expect(metadata()).toBeDefined();
|
|
71
|
+
expect(metadata().type).toBe(TableColumnType.ShortText);
|
|
72
|
+
expect(typeOrmColumn()).toBeDefined();
|
|
73
|
+
expect(typeOrmColumn()?.options.type).toBe(ColumnType.ShortText);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
/*
|
|
77
|
+
* The title is the label on the device form and the heading in the
|
|
78
|
+
* generated API docs; the form field module reads it from here.
|
|
79
|
+
*/
|
|
80
|
+
test("is titled MAC Address", () => {
|
|
81
|
+
expect(metadata().title).toBe("MAC Address");
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
/*
|
|
85
|
+
* The API documentation is the only explanation an integrator gets. It
|
|
86
|
+
* has to say what the column is FOR — the switch port, found from the
|
|
87
|
+
* forwarding tables — and that it is optional because ARP can stand in.
|
|
88
|
+
*/
|
|
89
|
+
test("its description says what the column is for and that it is optional", () => {
|
|
90
|
+
const description: string = metadata().description ?? "";
|
|
91
|
+
|
|
92
|
+
expect(description).toContain("switch port");
|
|
93
|
+
expect(description).toContain("forwarding tables");
|
|
94
|
+
expect(description).toContain("LLDP");
|
|
95
|
+
expect(description).toContain("CDP");
|
|
96
|
+
expect(description).toContain("ping");
|
|
97
|
+
expect(description).toContain("ARP");
|
|
98
|
+
expect(description).toContain("Optional");
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
/*
|
|
102
|
+
* The example is what the generated docs show and what a form placeholder
|
|
103
|
+
* is modelled on. It has to be in the STORED form — lowercase colon — so
|
|
104
|
+
* that what an integrator copies from the docs is what they read back.
|
|
105
|
+
*/
|
|
106
|
+
test("its example is already in the stored form", () => {
|
|
107
|
+
const example: string = String(metadata().example);
|
|
108
|
+
|
|
109
|
+
expect(normalizeMac(example)).toBe(example);
|
|
110
|
+
expect(example).toBe(example.toLowerCase());
|
|
111
|
+
expect(example.split(":")).toHaveLength(6);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
/*
|
|
115
|
+
* THE assertion of the column. A required column makes the API reject a
|
|
116
|
+
* create that omits it — and every create path in the product omits it.
|
|
117
|
+
*/
|
|
118
|
+
test("is not required, so a create that omits it is still accepted", () => {
|
|
119
|
+
expect(metadata().required).toBeFalsy();
|
|
120
|
+
|
|
121
|
+
const requiredColumns: Columns = new NetworkDevice().getRequiredColumns();
|
|
122
|
+
|
|
123
|
+
expect(requiredColumns.columns).not.toContain(COLUMN);
|
|
124
|
+
// ...and the check is not vacuous: the peer address genuinely is required.
|
|
125
|
+
expect(requiredColumns.columns).toContain(PEER_ADDRESS_COLUMN);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
/*
|
|
129
|
+
* NULL is a meaning here: "not declared, not learned yet". A default
|
|
130
|
+
* would restate every existing device as having a MAC it does not have,
|
|
131
|
+
* and the ARP learner — which only ever fills an EMPTY value — would then
|
|
132
|
+
* never fill anything.
|
|
133
|
+
*/
|
|
134
|
+
test("is nullable with no default", () => {
|
|
135
|
+
expect(typeOrmColumn()?.options.nullable).toBe(true);
|
|
136
|
+
expect(typeOrmColumn()?.options.default).toBeUndefined();
|
|
137
|
+
expect(metadata().defaultValue).toBeUndefined();
|
|
138
|
+
expect(new NetworkDevice().isDefaultValueColumn(COLUMN)).toBe(false);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
/*
|
|
142
|
+
* character varying(100): the shared ShortText length, so the migration's
|
|
143
|
+
* SQL and the entity agree without either restating the number.
|
|
144
|
+
*/
|
|
145
|
+
test("is a ShortText-length varchar", () => {
|
|
146
|
+
expect(typeOrmColumn()?.options.length).toBe(ColumnLength.ShortText);
|
|
147
|
+
expect(ColumnLength.ShortText).toBe(100);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
/*
|
|
151
|
+
* The topology API reads devices with a nested select, and the builder
|
|
152
|
+
* needs the MAC on every one of them to match endpoints against. A column
|
|
153
|
+
* that cannot be read through a relation would be silently dropped from
|
|
154
|
+
* that select, and adoption would never fire.
|
|
155
|
+
*/
|
|
156
|
+
test("is readable from a relation query, like the address beside it", () => {
|
|
157
|
+
expect(metadata().canReadOnRelationQuery).toBe(true);
|
|
158
|
+
expect(
|
|
159
|
+
new NetworkDevice().getTableColumnMetadata(PEER_ADDRESS_COLUMN)
|
|
160
|
+
.canReadOnRelationQuery,
|
|
161
|
+
).toBe(true);
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
describe("NetworkDevice.macAddress access control", () => {
|
|
166
|
+
/*
|
|
167
|
+
* The named grants the feature leans on. A project member fills the form
|
|
168
|
+
* in; the fine-grained device permissions are what an API key carries;
|
|
169
|
+
* and the topology page is readable by a Viewer, so the column it draws
|
|
170
|
+
* from has to be too.
|
|
171
|
+
*/
|
|
172
|
+
test("lets a project member and the device permissions set it", () => {
|
|
173
|
+
const column: ColumnAccessControl | null = accessControlFor(COLUMN);
|
|
174
|
+
|
|
175
|
+
expect(column?.create).toContain(Permission.ProjectMember);
|
|
176
|
+
expect(column?.create).toContain(Permission.CreateNetworkDevice);
|
|
177
|
+
expect(column?.update).toContain(Permission.ProjectMember);
|
|
178
|
+
expect(column?.update).toContain(Permission.EditNetworkDevice);
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
test("lets a viewer read it, since the topology map is viewer-readable", () => {
|
|
182
|
+
const column: ColumnAccessControl | null = accessControlFor(COLUMN);
|
|
183
|
+
|
|
184
|
+
expect(column?.read).toContain(Permission.Viewer);
|
|
185
|
+
expect(column?.read).toContain(Permission.ReadNetworkDevice);
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
/*
|
|
189
|
+
* The whole claim, in one assertion: this column is governed exactly as
|
|
190
|
+
* the device's hostname is. Written as a comparison rather than a
|
|
191
|
+
* permission list on purpose — the two are the layer-3 and layer-2
|
|
192
|
+
* address of the same device and must be revised together, so a change
|
|
193
|
+
* that widens both keeps this green while one drifting from the other
|
|
194
|
+
* fails here, which is the case worth catching.
|
|
195
|
+
*/
|
|
196
|
+
test("is governed exactly as the hostname is", () => {
|
|
197
|
+
expect(accessControlFor(COLUMN)).toEqual(
|
|
198
|
+
accessControlFor(PEER_ADDRESS_COLUMN),
|
|
199
|
+
);
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
// Broken out per operation so a failure says WHICH of the three drifted.
|
|
203
|
+
test("grants the same create, read and update permissions as the hostname", () => {
|
|
204
|
+
const column: ColumnAccessControl | null = accessControlFor(COLUMN);
|
|
205
|
+
const peer: ColumnAccessControl | null =
|
|
206
|
+
accessControlFor(PEER_ADDRESS_COLUMN);
|
|
207
|
+
|
|
208
|
+
expect(column?.create).toEqual(peer?.create);
|
|
209
|
+
expect(column?.read).toEqual(peer?.read);
|
|
210
|
+
expect(column?.update).toEqual(peer?.update);
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
/*
|
|
214
|
+
* ...and the comparison above is not two empty objects agreeing with each
|
|
215
|
+
* other. A column with no create permission at all is silently dropped
|
|
216
|
+
* from every write, which would leave the form field doing nothing.
|
|
217
|
+
*/
|
|
218
|
+
test("actually grants somebody the right to set, read and change it", () => {
|
|
219
|
+
const column: ColumnAccessControl | null = accessControlFor(COLUMN);
|
|
220
|
+
|
|
221
|
+
expect(column).not.toBeNull();
|
|
222
|
+
expect(column?.create?.length).toBeGreaterThan(0);
|
|
223
|
+
expect(column?.read?.length).toBeGreaterThan(0);
|
|
224
|
+
expect(column?.update?.length).toBeGreaterThan(0);
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
/*
|
|
228
|
+
* Everyone who may create a device may declare its MAC, everyone who may
|
|
229
|
+
* read a device may see it, and everyone who may edit a device may change
|
|
230
|
+
* it. Anything narrower would produce a device whose MAC its creator
|
|
231
|
+
* could not state, or a map that cannot place a device its viewer can see.
|
|
232
|
+
*/
|
|
233
|
+
test("its setters, readers and editors are those of the table", () => {
|
|
234
|
+
const device: NetworkDevice = new NetworkDevice();
|
|
235
|
+
const column: ColumnAccessControl | null = accessControlFor(COLUMN);
|
|
236
|
+
|
|
237
|
+
expect(column?.create).toEqual(device.getCreatePermissions());
|
|
238
|
+
expect(column?.read).toEqual(device.getReadPermissions());
|
|
239
|
+
expect(column?.update).toEqual(device.getUpdatePermissions());
|
|
240
|
+
});
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
/*
|
|
244
|
+
* The create contract, re-derived the way NetworkDeviceCreateContract.test.ts
|
|
245
|
+
* derives it. Adding a column must not change what a caller has to supply,
|
|
246
|
+
* and this column in particular is optional by design — so the set of
|
|
247
|
+
* caller-supplied required columns is exactly what it was before it existed.
|
|
248
|
+
*/
|
|
249
|
+
describe("NetworkDevice.macAddress leaves the create contract alone", () => {
|
|
250
|
+
const device: NetworkDevice = new NetworkDevice();
|
|
251
|
+
|
|
252
|
+
function callerSuppliedRequiredColumns(): Array<string> {
|
|
253
|
+
return device.getRequiredColumns().columns.filter((column: string) => {
|
|
254
|
+
return !device.isDefaultValueColumn(column);
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
test("the caller is still asked for exactly the four identity columns", () => {
|
|
259
|
+
expect(callerSuppliedRequiredColumns().sort()).toEqual([
|
|
260
|
+
"hostname",
|
|
261
|
+
"name",
|
|
262
|
+
"projectId",
|
|
263
|
+
"slug",
|
|
264
|
+
]);
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
test("and never for the MAC", () => {
|
|
268
|
+
expect(callerSuppliedRequiredColumns()).not.toContain(COLUMN);
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
describe("the isMacAddressLearned column", () => {
|
|
273
|
+
const FLAG: string = "isMacAddressLearned";
|
|
274
|
+
|
|
275
|
+
test("is an optional boolean that defaults to false", () => {
|
|
276
|
+
const metadata: TableColumnMetadata =
|
|
277
|
+
new NetworkDevice().getTableColumnMetadata(FLAG);
|
|
278
|
+
|
|
279
|
+
expect(metadata.type).toBe(TableColumnType.Boolean);
|
|
280
|
+
expect(metadata.required).toBeFalsy();
|
|
281
|
+
expect(metadata.defaultValue).toBe(false);
|
|
282
|
+
expect(metadata.title).toBe("MAC Address Learned");
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
test("is writable by exactly the roles that may write the MAC beside it", () => {
|
|
286
|
+
const device: NetworkDevice = new NetworkDevice();
|
|
287
|
+
const flagAccess: ColumnAccessControl =
|
|
288
|
+
device.getColumnAccessControlFor(FLAG)!;
|
|
289
|
+
const macAccess: ColumnAccessControl =
|
|
290
|
+
device.getColumnAccessControlFor("macAddress")!;
|
|
291
|
+
|
|
292
|
+
expect(flagAccess).toEqual(macAccess);
|
|
293
|
+
});
|
|
294
|
+
});
|
|
@@ -28,6 +28,7 @@ import {
|
|
|
28
28
|
} from "../../../Server/Utils/Express";
|
|
29
29
|
import InBetween from "../../../Types/BaseDatabase/InBetween";
|
|
30
30
|
import Includes from "../../../Types/BaseDatabase/Includes";
|
|
31
|
+
import IncludesAnyOfGroups from "../../../Types/BaseDatabase/IncludesAnyOfGroups";
|
|
31
32
|
import SortOrder from "../../../Types/BaseDatabase/SortOrder";
|
|
32
33
|
import DashboardComponentType from "../../../Types/Dashboard/DashboardComponentType";
|
|
33
34
|
import DashboardViewConfig from "../../../Types/Dashboard/DashboardViewConfig";
|
|
@@ -880,6 +881,134 @@ describe("DashboardAPI public resource-list", () => {
|
|
|
880
881
|
expect(query["kind"]).toBe("Pod");
|
|
881
882
|
});
|
|
882
883
|
|
|
884
|
+
describe("published monitor label variable selections", () => {
|
|
885
|
+
const firstLabelId: string = ObjectID.generate().toString();
|
|
886
|
+
const secondLabelId: string = ObjectID.generate().toString();
|
|
887
|
+
const fixedLabelId: string = ObjectID.generate().toString();
|
|
888
|
+
const labelVariable: JSONObject = {
|
|
889
|
+
id: "unit",
|
|
890
|
+
name: "UNIT",
|
|
891
|
+
type: DashboardVariableType.ProjectLabel,
|
|
892
|
+
labelOptions: [
|
|
893
|
+
{ label: "Payments", value: firstLabelId },
|
|
894
|
+
{ label: "Support", value: secondLabelId },
|
|
895
|
+
],
|
|
896
|
+
defaultValue: firstLabelId,
|
|
897
|
+
};
|
|
898
|
+
|
|
899
|
+
it("applies the chosen label while preserving stored monitor policy and project", async () => {
|
|
900
|
+
const monitor: BuiltWidget = buildWidget({
|
|
901
|
+
componentType: DashboardComponentType.MonitorList,
|
|
902
|
+
argumentsObject: {
|
|
903
|
+
labelVariableId: "unit",
|
|
904
|
+
labelIds: [fixedLabelId],
|
|
905
|
+
statusFilter: "operational",
|
|
906
|
+
monitorTypes: ["Ping"],
|
|
907
|
+
maxRows: 6,
|
|
908
|
+
},
|
|
909
|
+
});
|
|
910
|
+
setDashboardWidgets([monitor.widget], [labelVariable]);
|
|
911
|
+
|
|
912
|
+
await callRoute({
|
|
913
|
+
resourceType: "monitor",
|
|
914
|
+
body: {
|
|
915
|
+
componentId: monitor.componentId.toString(),
|
|
916
|
+
variables: [{ id: "unit", selectedValue: secondLabelId }],
|
|
917
|
+
},
|
|
918
|
+
});
|
|
919
|
+
|
|
920
|
+
expect(nextFunction).not.toHaveBeenCalled();
|
|
921
|
+
const findBy: JSONObject = getFindByArgs(MonitorService);
|
|
922
|
+
expect(findBy["query"]).toEqual({
|
|
923
|
+
projectId,
|
|
924
|
+
currentMonitorStatus: { isOperationalState: true },
|
|
925
|
+
monitorType: new Includes(["Ping"]),
|
|
926
|
+
labels: new IncludesAnyOfGroups([[fixedLabelId], [secondLabelId]]),
|
|
927
|
+
});
|
|
928
|
+
expect(findBy["limit"]).toBe(6);
|
|
929
|
+
expect(findBy["sort"]).toEqual({ name: SortOrder.Ascending });
|
|
930
|
+
expect(findBy["select"]).toEqual({
|
|
931
|
+
_id: true,
|
|
932
|
+
name: true,
|
|
933
|
+
monitorType: true,
|
|
934
|
+
currentMonitorStatus: { name: true, color: true },
|
|
935
|
+
});
|
|
936
|
+
});
|
|
937
|
+
|
|
938
|
+
it("retains fixed labels when the public viewer chooses All", async () => {
|
|
939
|
+
const monitor: BuiltWidget = buildWidget({
|
|
940
|
+
componentType: DashboardComponentType.MonitorList,
|
|
941
|
+
argumentsObject: {
|
|
942
|
+
labelVariableId: "unit",
|
|
943
|
+
labelIds: [fixedLabelId],
|
|
944
|
+
},
|
|
945
|
+
});
|
|
946
|
+
setDashboardWidgets([monitor.widget], [labelVariable]);
|
|
947
|
+
|
|
948
|
+
await callRoute({
|
|
949
|
+
resourceType: "monitor",
|
|
950
|
+
body: {
|
|
951
|
+
componentId: monitor.componentId.toString(),
|
|
952
|
+
variables: [{ id: "unit", selectedValue: "" }],
|
|
953
|
+
},
|
|
954
|
+
});
|
|
955
|
+
|
|
956
|
+
expect(nextFunction).not.toHaveBeenCalled();
|
|
957
|
+
expect(getFindByArgs(MonitorService)["query"]).toEqual({
|
|
958
|
+
projectId,
|
|
959
|
+
labels: new Includes([fixedLabelId]),
|
|
960
|
+
});
|
|
961
|
+
});
|
|
962
|
+
|
|
963
|
+
it("accepts several published label IDs for a saved multi-select", async () => {
|
|
964
|
+
const monitor: BuiltWidget = buildWidget({
|
|
965
|
+
componentType: DashboardComponentType.MonitorList,
|
|
966
|
+
argumentsObject: { labelVariableId: "unit" },
|
|
967
|
+
});
|
|
968
|
+
setDashboardWidgets(
|
|
969
|
+
[monitor.widget],
|
|
970
|
+
[{ ...labelVariable, isMultiSelect: true }],
|
|
971
|
+
);
|
|
972
|
+
|
|
973
|
+
await callRoute({
|
|
974
|
+
resourceType: "monitor",
|
|
975
|
+
body: {
|
|
976
|
+
componentId: monitor.componentId.toString(),
|
|
977
|
+
variables: [
|
|
978
|
+
{ id: "unit", selectedValues: [firstLabelId, secondLabelId] },
|
|
979
|
+
],
|
|
980
|
+
},
|
|
981
|
+
});
|
|
982
|
+
|
|
983
|
+
expect(nextFunction).not.toHaveBeenCalled();
|
|
984
|
+
expect(getFindByArgs(MonitorService)["query"]).toEqual({
|
|
985
|
+
projectId,
|
|
986
|
+
labels: new Includes([firstLabelId, secondLabelId]),
|
|
987
|
+
});
|
|
988
|
+
});
|
|
989
|
+
|
|
990
|
+
it("reports an invalid selection before reading monitors when a choice was removed", async () => {
|
|
991
|
+
const monitor: BuiltWidget = buildWidget({
|
|
992
|
+
componentType: DashboardComponentType.MonitorList,
|
|
993
|
+
argumentsObject: { labelVariableId: "unit" },
|
|
994
|
+
});
|
|
995
|
+
setDashboardWidgets([monitor.widget], [labelVariable]);
|
|
996
|
+
|
|
997
|
+
await callRoute({
|
|
998
|
+
resourceType: "monitor",
|
|
999
|
+
body: {
|
|
1000
|
+
componentId: monitor.componentId.toString(),
|
|
1001
|
+
variables: [
|
|
1002
|
+
{ id: "unit", selectedValue: ObjectID.generate().toString() },
|
|
1003
|
+
],
|
|
1004
|
+
},
|
|
1005
|
+
});
|
|
1006
|
+
|
|
1007
|
+
expect(getThrownError()).toBeInstanceOf(BadDataException);
|
|
1008
|
+
expectNothingListed();
|
|
1009
|
+
});
|
|
1010
|
+
});
|
|
1011
|
+
|
|
883
1012
|
it("passes only dynamic time and bounded variable selections into policy", async () => {
|
|
884
1013
|
const buildSpy: ReturnType<typeof jest.spyOn> = jest.spyOn(
|
|
885
1014
|
PublicDashboardResourceListPolicy,
|