@oneuptime/common 12.0.2 → 12.0.3

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.
Files changed (228) hide show
  1. package/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.ts +14 -0
  2. package/Models/DatabaseModels/Index.ts +2 -0
  3. package/Models/DatabaseModels/LogDropFilter.ts +9 -20
  4. package/Models/DatabaseModels/StatusPageMonitorRule.ts +911 -0
  5. package/Models/DatabaseModels/StatusPageResource.ts +73 -0
  6. package/Models/DatabaseModels/TraceDropFilter.ts +9 -20
  7. package/Server/API/BaseAPI.ts +13 -2
  8. package/Server/API/DashboardAPI.ts +39 -0
  9. package/Server/EnvironmentConfig.ts +31 -1
  10. package/Server/Infrastructure/GlobalCache.ts +115 -0
  11. package/Server/Infrastructure/Postgres/DataSourceOptions.ts +16 -0
  12. package/Server/Infrastructure/Postgres/SchemaMigrations/1786005052769-AddStatusPageMonitorRule.ts +123 -0
  13. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +2 -0
  14. package/Server/Services/AlertStateTimelineService.ts +20 -0
  15. package/Server/Services/CephClusterService.ts +26 -86
  16. package/Server/Services/CloudResourceService.ts +23 -83
  17. package/Server/Services/DatabaseService.ts +111 -0
  18. package/Server/Services/DockerHostService.ts +19 -79
  19. package/Server/Services/DockerSwarmClusterService.ts +27 -88
  20. package/Server/Services/HostService.ts +46 -99
  21. package/Server/Services/IncidentStateTimelineService.ts +20 -0
  22. package/Server/Services/Index.ts +2 -0
  23. package/Server/Services/IoTFleetService.ts +19 -79
  24. package/Server/Services/KubernetesClusterService.ts +17 -78
  25. package/Server/Services/LabelService.ts +57 -10
  26. package/Server/Services/MetricTypeService.ts +84 -0
  27. package/Server/Services/MonitorService.ts +89 -14
  28. package/Server/Services/MonitorStatusTimelineService.ts +29 -0
  29. package/Server/Services/MonitorTemplateService.ts +65 -0
  30. package/Server/Services/PodmanHostService.ts +19 -79
  31. package/Server/Services/ProxmoxClusterService.ts +23 -83
  32. package/Server/Services/RumApplicationService.ts +19 -80
  33. package/Server/Services/ScheduledMaintenanceStateTimelineService.ts +21 -0
  34. package/Server/Services/ServerlessFunctionService.ts +24 -85
  35. package/Server/Services/ServiceService.ts +299 -73
  36. package/Server/Services/StatusPageMonitorRuleEngineService.ts +752 -0
  37. package/Server/Services/StatusPageMonitorRuleService.ts +471 -0
  38. package/Server/Services/TelemetryEntityRelationshipService.ts +1 -0
  39. package/Server/Services/TelemetryEntityService.ts +1 -0
  40. package/Server/Utils/Database/ProjectScopedReferenceValidator.ts +170 -39
  41. package/Server/Utils/Monitor/MonitorResource.ts +63 -36
  42. package/Server/Utils/Monitor/MonitorStatusTimeline.ts +36 -0
  43. package/Server/Utils/Monitor/MonitorStepsProjectValidator.ts +148 -127
  44. package/Server/Utils/Monitor/MonitorStepsReferenceExtractor.ts +434 -0
  45. package/Server/Utils/SingleFlight.ts +83 -0
  46. package/Server/Utils/StatusPage/MonitorRulePatternValidator.ts +46 -0
  47. package/Server/Utils/Telemetry/EntityRegistry.ts +107 -26
  48. package/Server/Utils/Telemetry/ResourceHeartbeat.ts +314 -0
  49. package/Server/Utils/Telemetry/Telemetry.ts +351 -122
  50. package/Tests/App/Dashboard/WidgetCatalog.test.ts +2 -0
  51. package/Tests/Models/DatabaseModels/ColumnTransformerInvariants.test.ts +306 -0
  52. package/Tests/Models/DatabaseModels/NumberColumnDeserialization.test.ts +333 -0
  53. package/Tests/Models/DatabaseModels/StatusPageMonitorRuleModel.test.ts +190 -0
  54. package/Tests/Server/API/DashboardPublicResourceListAPI.test.ts +26 -0
  55. package/Tests/Server/Infrastructure/GlobalCache.test.ts +226 -0
  56. package/Tests/Server/Infrastructure/PostgresLockTimeoutOptions.test.ts +181 -0
  57. package/Tests/Server/Services/DatabaseServiceUpdateColumnsIfUnlocked.test.ts +342 -0
  58. package/Tests/Server/Services/DropFilterSaveValidation.test.ts +245 -0
  59. package/Tests/Server/Services/HostServiceUpdateLastSeen.test.ts +163 -15
  60. package/Tests/Server/Services/MetricTypeAttachServices.test.ts +222 -0
  61. package/Tests/Server/Services/MissingReferenceWriteGuard.test.ts +614 -0
  62. package/Tests/Server/Services/MonitorStatusTimelineFastPath.test.ts +23 -0
  63. package/Tests/Server/Services/ResourceUpdateLastSeenLivenessFallback.test.ts +305 -56
  64. package/Tests/Server/Services/ServiceUpdateLastSeenSplitGates.test.ts +705 -0
  65. package/Tests/Server/Services/StatusPageMonitorRuleEngineService.test.ts +1719 -0
  66. package/Tests/Server/Services/StatusPageMonitorRuleHooks.test.ts +1212 -0
  67. package/Tests/Server/Services/StatusPageMonitorRuleMonitorCreateHook.test.ts +228 -0
  68. package/Tests/Server/Utils/APIKey/AccessPermission.test.ts +267 -0
  69. package/Tests/Server/Utils/Database/ProjectScopedReferenceValidator.test.ts +260 -13
  70. package/Tests/Server/Utils/EntityRegistryRowFence.test.ts +302 -0
  71. package/Tests/Server/Utils/MetricTypeIndexConvergence.test.ts +598 -0
  72. package/Tests/Server/Utils/Monitor/MonitorStatusTimelineUnusableStatus.test.ts +154 -0
  73. package/Tests/Server/Utils/Monitor/MonitorStepsProjectValidator.test.ts +638 -92
  74. package/Tests/Server/Utils/Monitor/MonitorStepsReferenceExtractor.test.ts +724 -0
  75. package/Tests/Server/Utils/SingleFlight.test.ts +221 -0
  76. package/Tests/Types/Dashboard/NetworkDashboardTemplate.test.ts +793 -0
  77. package/Tests/Types/Database/BigIntColumnTransformer.test.ts +147 -0
  78. package/Tests/Types/Database/DatabasePropertyTransformer.test.ts +140 -0
  79. package/Tests/Types/Database/NumericColumnValue.test.ts +127 -0
  80. package/Tests/Types/Decimal.test.ts +37 -0
  81. package/Tests/Types/Monitor/SnmpVersionParsing.test.ts +152 -0
  82. package/Tests/Types/Port.test.ts +38 -0
  83. package/Tests/UI/Components/LogsHistogram.test.tsx +276 -0
  84. package/Tests/UI/Components/UseLiveLogsRefresh.test.tsx +489 -0
  85. package/Tests/UI/Components/UseLogsHistogram.test.tsx +500 -0
  86. package/Tests/Utils/Dashboard/Components/DashboardComponentsUtil.test.ts +120 -0
  87. package/Tests/Utils/Dashboard/DashboardNetworkMapComponent.test.ts +327 -0
  88. package/Types/Dashboard/DashboardComponentType.ts +1 -0
  89. package/Types/Dashboard/DashboardComponents/ComponentArgument.ts +1 -0
  90. package/Types/Dashboard/DashboardComponents/DashboardNetworkMapComponent.ts +40 -0
  91. package/Types/Dashboard/DashboardTemplates.ts +397 -0
  92. package/Types/Database/BigIntColumnTransformer.ts +74 -0
  93. package/Types/Database/DatabaseProperty.ts +26 -0
  94. package/Types/Database/NumericColumnValue.ts +128 -0
  95. package/Types/Decimal.ts +16 -4
  96. package/Types/Permission.ts +48 -0
  97. package/Types/Port.ts +16 -4
  98. package/UI/Components/LogsViewer/useLiveLogsRefresh.ts +86 -0
  99. package/UI/Components/LogsViewer/useLogsHistogram.ts +113 -0
  100. package/Utils/Dashboard/Components/DashboardNetworkMapComponent.ts +136 -0
  101. package/Utils/Dashboard/Components/Index.ts +7 -0
  102. package/build/dist/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.js +12 -0
  103. package/build/dist/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.js.map +1 -1
  104. package/build/dist/Models/DatabaseModels/Index.js +2 -0
  105. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  106. package/build/dist/Models/DatabaseModels/LogDropFilter.js +9 -20
  107. package/build/dist/Models/DatabaseModels/LogDropFilter.js.map +1 -1
  108. package/build/dist/Models/DatabaseModels/StatusPageMonitorRule.js +925 -0
  109. package/build/dist/Models/DatabaseModels/StatusPageMonitorRule.js.map +1 -0
  110. package/build/dist/Models/DatabaseModels/StatusPageResource.js +72 -0
  111. package/build/dist/Models/DatabaseModels/StatusPageResource.js.map +1 -1
  112. package/build/dist/Models/DatabaseModels/TraceDropFilter.js +9 -20
  113. package/build/dist/Models/DatabaseModels/TraceDropFilter.js.map +1 -1
  114. package/build/dist/Server/API/BaseAPI.js +11 -1
  115. package/build/dist/Server/API/BaseAPI.js.map +1 -1
  116. package/build/dist/Server/API/DashboardAPI.js +39 -0
  117. package/build/dist/Server/API/DashboardAPI.js.map +1 -1
  118. package/build/dist/Server/EnvironmentConfig.js +27 -1
  119. package/build/dist/Server/EnvironmentConfig.js.map +1 -1
  120. package/build/dist/Server/Infrastructure/GlobalCache.js +91 -0
  121. package/build/dist/Server/Infrastructure/GlobalCache.js.map +1 -1
  122. package/build/dist/Server/Infrastructure/Postgres/DataSourceOptions.js +5 -3
  123. package/build/dist/Server/Infrastructure/Postgres/DataSourceOptions.js.map +1 -1
  124. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786005052769-AddStatusPageMonitorRule.js +48 -0
  125. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786005052769-AddStatusPageMonitorRule.js.map +1 -0
  126. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +2 -0
  127. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  128. package/build/dist/Server/Services/AlertStateTimelineService.js +19 -0
  129. package/build/dist/Server/Services/AlertStateTimelineService.js.map +1 -1
  130. package/build/dist/Server/Services/CephClusterService.js +26 -76
  131. package/build/dist/Server/Services/CephClusterService.js.map +1 -1
  132. package/build/dist/Server/Services/CloudResourceService.js +23 -73
  133. package/build/dist/Server/Services/CloudResourceService.js.map +1 -1
  134. package/build/dist/Server/Services/DatabaseService.js +86 -0
  135. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  136. package/build/dist/Server/Services/DockerHostService.js +19 -69
  137. package/build/dist/Server/Services/DockerHostService.js.map +1 -1
  138. package/build/dist/Server/Services/DockerSwarmClusterService.js +27 -78
  139. package/build/dist/Server/Services/DockerSwarmClusterService.js.map +1 -1
  140. package/build/dist/Server/Services/HostService.js +58 -102
  141. package/build/dist/Server/Services/HostService.js.map +1 -1
  142. package/build/dist/Server/Services/IncidentStateTimelineService.js +19 -0
  143. package/build/dist/Server/Services/IncidentStateTimelineService.js.map +1 -1
  144. package/build/dist/Server/Services/Index.js +2 -0
  145. package/build/dist/Server/Services/Index.js.map +1 -1
  146. package/build/dist/Server/Services/IoTFleetService.js +19 -69
  147. package/build/dist/Server/Services/IoTFleetService.js.map +1 -1
  148. package/build/dist/Server/Services/KubernetesClusterService.js +17 -68
  149. package/build/dist/Server/Services/KubernetesClusterService.js.map +1 -1
  150. package/build/dist/Server/Services/LabelService.js +42 -7
  151. package/build/dist/Server/Services/LabelService.js.map +1 -1
  152. package/build/dist/Server/Services/MetricTypeService.js +74 -0
  153. package/build/dist/Server/Services/MetricTypeService.js.map +1 -1
  154. package/build/dist/Server/Services/MonitorService.js +81 -9
  155. package/build/dist/Server/Services/MonitorService.js.map +1 -1
  156. package/build/dist/Server/Services/MonitorStatusTimelineService.js +28 -0
  157. package/build/dist/Server/Services/MonitorStatusTimelineService.js.map +1 -1
  158. package/build/dist/Server/Services/MonitorTemplateService.js +60 -0
  159. package/build/dist/Server/Services/MonitorTemplateService.js.map +1 -1
  160. package/build/dist/Server/Services/PodmanHostService.js +19 -69
  161. package/build/dist/Server/Services/PodmanHostService.js.map +1 -1
  162. package/build/dist/Server/Services/ProxmoxClusterService.js +23 -73
  163. package/build/dist/Server/Services/ProxmoxClusterService.js.map +1 -1
  164. package/build/dist/Server/Services/RumApplicationService.js +19 -70
  165. package/build/dist/Server/Services/RumApplicationService.js.map +1 -1
  166. package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js +20 -0
  167. package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js.map +1 -1
  168. package/build/dist/Server/Services/ServerlessFunctionService.js +24 -75
  169. package/build/dist/Server/Services/ServerlessFunctionService.js.map +1 -1
  170. package/build/dist/Server/Services/ServiceService.js +236 -61
  171. package/build/dist/Server/Services/ServiceService.js.map +1 -1
  172. package/build/dist/Server/Services/StatusPageMonitorRuleEngineService.js +603 -0
  173. package/build/dist/Server/Services/StatusPageMonitorRuleEngineService.js.map +1 -0
  174. package/build/dist/Server/Services/StatusPageMonitorRuleService.js +401 -0
  175. package/build/dist/Server/Services/StatusPageMonitorRuleService.js.map +1 -0
  176. package/build/dist/Server/Services/TelemetryEntityRelationshipService.js +1 -0
  177. package/build/dist/Server/Services/TelemetryEntityRelationshipService.js.map +1 -1
  178. package/build/dist/Server/Services/TelemetryEntityService.js +1 -0
  179. package/build/dist/Server/Services/TelemetryEntityService.js.map +1 -1
  180. package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js +105 -29
  181. package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js.map +1 -1
  182. package/build/dist/Server/Utils/Monitor/MonitorResource.js +56 -32
  183. package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
  184. package/build/dist/Server/Utils/Monitor/MonitorStatusTimeline.js +30 -3
  185. package/build/dist/Server/Utils/Monitor/MonitorStatusTimeline.js.map +1 -1
  186. package/build/dist/Server/Utils/Monitor/MonitorStepsProjectValidator.js +110 -98
  187. package/build/dist/Server/Utils/Monitor/MonitorStepsProjectValidator.js.map +1 -1
  188. package/build/dist/Server/Utils/Monitor/MonitorStepsReferenceExtractor.js +312 -0
  189. package/build/dist/Server/Utils/Monitor/MonitorStepsReferenceExtractor.js.map +1 -0
  190. package/build/dist/Server/Utils/SingleFlight.js +76 -0
  191. package/build/dist/Server/Utils/SingleFlight.js.map +1 -0
  192. package/build/dist/Server/Utils/StatusPage/MonitorRulePatternValidator.js +29 -0
  193. package/build/dist/Server/Utils/StatusPage/MonitorRulePatternValidator.js.map +1 -0
  194. package/build/dist/Server/Utils/Telemetry/EntityRegistry.js +78 -19
  195. package/build/dist/Server/Utils/Telemetry/EntityRegistry.js.map +1 -1
  196. package/build/dist/Server/Utils/Telemetry/ResourceHeartbeat.js +160 -0
  197. package/build/dist/Server/Utils/Telemetry/ResourceHeartbeat.js.map +1 -0
  198. package/build/dist/Server/Utils/Telemetry/Telemetry.js +279 -104
  199. package/build/dist/Server/Utils/Telemetry/Telemetry.js.map +1 -1
  200. package/build/dist/Types/Dashboard/DashboardComponentType.js +1 -0
  201. package/build/dist/Types/Dashboard/DashboardComponentType.js.map +1 -1
  202. package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js +1 -0
  203. package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js.map +1 -1
  204. package/build/dist/Types/Dashboard/DashboardComponents/DashboardNetworkMapComponent.js +2 -0
  205. package/build/dist/Types/Dashboard/DashboardComponents/DashboardNetworkMapComponent.js.map +1 -0
  206. package/build/dist/Types/Dashboard/DashboardTemplates.js +368 -0
  207. package/build/dist/Types/Dashboard/DashboardTemplates.js.map +1 -1
  208. package/build/dist/Types/Database/BigIntColumnTransformer.js +66 -0
  209. package/build/dist/Types/Database/BigIntColumnTransformer.js.map +1 -0
  210. package/build/dist/Types/Database/DatabaseProperty.js +25 -0
  211. package/build/dist/Types/Database/DatabaseProperty.js.map +1 -1
  212. package/build/dist/Types/Database/NumericColumnValue.js +97 -0
  213. package/build/dist/Types/Database/NumericColumnValue.js.map +1 -0
  214. package/build/dist/Types/Decimal.js +14 -3
  215. package/build/dist/Types/Decimal.js.map +1 -1
  216. package/build/dist/Types/Permission.js +42 -0
  217. package/build/dist/Types/Permission.js.map +1 -1
  218. package/build/dist/Types/Port.js +15 -5
  219. package/build/dist/Types/Port.js.map +1 -1
  220. package/build/dist/UI/Components/LogsViewer/useLiveLogsRefresh.js +44 -0
  221. package/build/dist/UI/Components/LogsViewer/useLiveLogsRefresh.js.map +1 -0
  222. package/build/dist/UI/Components/LogsViewer/useLogsHistogram.js +75 -0
  223. package/build/dist/UI/Components/LogsViewer/useLogsHistogram.js.map +1 -0
  224. package/build/dist/Utils/Dashboard/Components/DashboardNetworkMapComponent.js +114 -0
  225. package/build/dist/Utils/Dashboard/Components/DashboardNetworkMapComponent.js.map +1 -0
  226. package/build/dist/Utils/Dashboard/Components/Index.js +4 -0
  227. package/build/dist/Utils/Dashboard/Components/Index.js.map +1 -1
  228. package/package.json +1 -1
@@ -0,0 +1,327 @@
1
+ import {
2
+ ComponentArgument,
3
+ ComponentArgumentSection,
4
+ ComponentInputType,
5
+ EntityFilterModelType,
6
+ } from "../../../Types/Dashboard/DashboardComponents/ComponentArgument";
7
+ import DashboardBaseComponent from "../../../Types/Dashboard/DashboardComponents/DashboardBaseComponent";
8
+ import DashboardNetworkMapComponent from "../../../Types/Dashboard/DashboardComponents/DashboardNetworkMapComponent";
9
+ import DashboardComponentType from "../../../Types/Dashboard/DashboardComponentType";
10
+ import { ObjectType } from "../../../Types/JSON";
11
+ import { DropdownOption } from "../../../UI/Components/Dropdown/Dropdown";
12
+ import DashboardComponentsUtil from "../../../Utils/Dashboard/Components/Index";
13
+ import DashboardNetworkMapComponentUtil, {
14
+ DEFAULT_MAX_SITES,
15
+ } from "../../../Utils/Dashboard/Components/DashboardNetworkMapComponent";
16
+
17
+ type ArgumentIds = keyof DashboardNetworkMapComponent["arguments"];
18
+
19
+ function getArguments(): Array<
20
+ ComponentArgument<DashboardNetworkMapComponent>
21
+ > {
22
+ return DashboardNetworkMapComponentUtil.getComponentConfigArguments();
23
+ }
24
+
25
+ function getArgumentById(
26
+ id: ArgumentIds,
27
+ ): ComponentArgument<DashboardNetworkMapComponent> {
28
+ const found: ComponentArgument<DashboardNetworkMapComponent> | undefined =
29
+ getArguments().find(
30
+ (arg: ComponentArgument<DashboardNetworkMapComponent>) => {
31
+ return arg.id === id;
32
+ },
33
+ );
34
+
35
+ if (!found) {
36
+ throw new Error(`No Network Map widget argument declared with id "${id}"`);
37
+ }
38
+
39
+ return found;
40
+ }
41
+
42
+ function getDropdownValues(id: ArgumentIds): Array<string> {
43
+ return (getArgumentById(id).dropdownOptions || []).map(
44
+ (option: DropdownOption): string => {
45
+ return String(option.value);
46
+ },
47
+ );
48
+ }
49
+
50
+ describe("DashboardNetworkMapComponentUtil", () => {
51
+ describe("getDefaultComponent", () => {
52
+ it("declares its componentType as NetworkMap so the renderer dispatch matches", () => {
53
+ expect(
54
+ DashboardNetworkMapComponentUtil.getDefaultComponent().componentType,
55
+ ).toBe(DashboardComponentType.NetworkMap);
56
+ });
57
+
58
+ it("marks itself as a dashboard component so it survives config serialization", () => {
59
+ expect(DashboardNetworkMapComponentUtil.getDefaultComponent()._type).toBe(
60
+ ObjectType.DashboardComponent,
61
+ );
62
+ });
63
+
64
+ it("places a 6x5 tile at the canvas origin", () => {
65
+ const component: DashboardNetworkMapComponent =
66
+ DashboardNetworkMapComponentUtil.getDefaultComponent();
67
+
68
+ expect(component.widthInDashboardUnits).toBe(6);
69
+ expect(component.heightInDashboardUnits).toBe(5);
70
+ expect(component.topInDashboardUnits).toBe(0);
71
+ expect(component.leftInDashboardUnits).toBe(0);
72
+ });
73
+
74
+ /*
75
+ * The world is roughly 2:1, so a tile that is allowed to be short and
76
+ * wide letterboxes the map into an unreadable strip. The minimum has to
77
+ * bound BOTH axes, and the default has to sit at or above it — a widget
78
+ * that spawns below its own minimum is one the resize handles then
79
+ * refuse to shrink.
80
+ */
81
+ it("cannot be resized below a square-ish 4x4, and spawns at or above that", () => {
82
+ const component: DashboardNetworkMapComponent =
83
+ DashboardNetworkMapComponentUtil.getDefaultComponent();
84
+
85
+ expect(component.minWidthInDashboardUnits).toBe(4);
86
+ expect(component.minHeightInDashboardUnits).toBe(4);
87
+ expect(component.widthInDashboardUnits).toBeGreaterThanOrEqual(
88
+ component.minWidthInDashboardUnits,
89
+ );
90
+ expect(component.heightInDashboardUnits).toBeGreaterThanOrEqual(
91
+ component.minHeightInDashboardUnits,
92
+ );
93
+ });
94
+
95
+ it("fits inside the 12-unit dashboard grid", () => {
96
+ const component: DashboardNetworkMapComponent =
97
+ DashboardNetworkMapComponentUtil.getDefaultComponent();
98
+
99
+ expect(
100
+ component.leftInDashboardUnits + component.widthInDashboardUnits,
101
+ ).toBeLessThanOrEqual(12);
102
+ });
103
+
104
+ it("opens on the map rather than the list, with names on and the row cap seeded", () => {
105
+ const component: DashboardNetworkMapComponent =
106
+ DashboardNetworkMapComponentUtil.getDefaultComponent();
107
+
108
+ expect(component.arguments.viewMode).toBe("map");
109
+ expect(component.arguments.showLabels).toBe(true);
110
+ expect(component.arguments.maxSites).toBe(DEFAULT_MAX_SITES);
111
+ });
112
+
113
+ it("seeds no filters, so a freshly added widget shows the whole estate", () => {
114
+ const component: DashboardNetworkMapComponent =
115
+ DashboardNetworkMapComponentUtil.getDefaultComponent();
116
+
117
+ expect(component.arguments.statusFilter).toBeUndefined();
118
+ expect(component.arguments.networkSiteTypeIds).toBeUndefined();
119
+ });
120
+
121
+ /*
122
+ * Two widgets dropped on the same dashboard must not share an id — the
123
+ * canvas keys, selects and persists components by it.
124
+ */
125
+ it("generates a fresh component id on every call", () => {
126
+ const first: DashboardNetworkMapComponent =
127
+ DashboardNetworkMapComponentUtil.getDefaultComponent();
128
+ const second: DashboardNetworkMapComponent =
129
+ DashboardNetworkMapComponentUtil.getDefaultComponent();
130
+
131
+ expect(first.componentId.toString()).not.toBe(
132
+ second.componentId.toString(),
133
+ );
134
+ });
135
+
136
+ it("caps the fetch at a bounded number of sites", () => {
137
+ expect(DEFAULT_MAX_SITES).toBeGreaterThan(0);
138
+ expect(Number.isInteger(DEFAULT_MAX_SITES)).toBe(true);
139
+ });
140
+ });
141
+
142
+ describe("getComponentConfigArguments", () => {
143
+ it("declares exactly the arguments the widget reads", () => {
144
+ const ids: Array<unknown> = getArguments().map(
145
+ (arg: ComponentArgument<DashboardNetworkMapComponent>) => {
146
+ return arg.id;
147
+ },
148
+ );
149
+
150
+ expect(ids).toEqual([
151
+ "title",
152
+ "viewMode",
153
+ "maxSites",
154
+ "showLabels",
155
+ "networkSiteTypeIds",
156
+ "statusFilter",
157
+ ]);
158
+ });
159
+
160
+ it("declares no duplicate argument ids", () => {
161
+ const ids: Array<unknown> = getArguments().map(
162
+ (arg: ComponentArgument<DashboardNetworkMapComponent>) => {
163
+ return arg.id;
164
+ },
165
+ );
166
+
167
+ expect(new Set(ids).size).toBe(ids.length);
168
+ });
169
+
170
+ it("gives every argument a name, a description and no required flag", () => {
171
+ for (const arg of getArguments()) {
172
+ expect(arg.name.trim().length).toBeGreaterThan(0);
173
+ expect(arg.description.trim().length).toBeGreaterThan(0);
174
+ /*
175
+ * Every argument has a working default, so none of them may be
176
+ * required — a required field with a default is a form the user
177
+ * cannot submit without re-entering what is already true.
178
+ */
179
+ expect(arg.required).toBe(false);
180
+ }
181
+ });
182
+
183
+ it("types each argument as the input the settings form should render", () => {
184
+ expect(getArgumentById("title").type).toBe(ComponentInputType.Text);
185
+ expect(getArgumentById("viewMode").type).toBe(
186
+ ComponentInputType.Dropdown,
187
+ );
188
+ expect(getArgumentById("maxSites").type).toBe(ComponentInputType.Number);
189
+ expect(getArgumentById("showLabels").type).toBe(
190
+ ComponentInputType.Boolean,
191
+ );
192
+ expect(getArgumentById("networkSiteTypeIds").type).toBe(
193
+ ComponentInputType.EntityMultiSelectDropdown,
194
+ );
195
+ expect(getArgumentById("statusFilter").type).toBe(
196
+ ComponentInputType.Dropdown,
197
+ );
198
+ });
199
+
200
+ /*
201
+ * The site-type picker has to name a model the EntityFilterDropdown
202
+ * switch knows about, or it throws when the settings modal opens.
203
+ */
204
+ it("binds the site-type picker to the NetworkSiteType lookup table", () => {
205
+ expect(getArgumentById("networkSiteTypeIds").entityFilterModelType).toBe(
206
+ EntityFilterModelType.NetworkSiteType,
207
+ );
208
+ });
209
+
210
+ it("gives only the entity picker an entityFilterModelType", () => {
211
+ for (const arg of getArguments()) {
212
+ if (arg.id === "networkSiteTypeIds") {
213
+ continue;
214
+ }
215
+ expect(arg.entityFilterModelType).toBeUndefined();
216
+ }
217
+ });
218
+
219
+ it("offers exactly the two view modes the renderer implements", () => {
220
+ expect(getDropdownValues("viewMode")).toEqual(["map", "list"]);
221
+ });
222
+
223
+ /*
224
+ * The empty option is what lets somebody UNDO a status filter. Without
225
+ * it the only way back to "all sites" is to delete the widget.
226
+ */
227
+ it("offers an empty status option so the filter can be cleared", () => {
228
+ expect(getDropdownValues("statusFilter")).toEqual([
229
+ "",
230
+ "down",
231
+ "operational",
232
+ ]);
233
+ });
234
+
235
+ it("gives every dropdown option a non-empty label", () => {
236
+ for (const arg of getArguments()) {
237
+ for (const option of arg.dropdownOptions || []) {
238
+ expect(String(option.label).trim().length).toBeGreaterThan(0);
239
+ }
240
+ }
241
+ });
242
+
243
+ it("splits the arguments into a Display section and a collapsed Filters section", () => {
244
+ const sectionOf: (
245
+ id: ArgumentIds,
246
+ ) => ComponentArgumentSection | undefined = (
247
+ id: ArgumentIds,
248
+ ): ComponentArgumentSection | undefined => {
249
+ return getArgumentById(id).section;
250
+ };
251
+
252
+ for (const id of [
253
+ "title",
254
+ "viewMode",
255
+ "maxSites",
256
+ "showLabels",
257
+ ] as Array<ArgumentIds>) {
258
+ expect(sectionOf(id)?.name).toBe("Display Options");
259
+ }
260
+
261
+ for (const id of [
262
+ "networkSiteTypeIds",
263
+ "statusFilter",
264
+ ] as Array<ArgumentIds>) {
265
+ expect(sectionOf(id)?.name).toBe("Filters");
266
+ }
267
+
268
+ expect(sectionOf("title")?.order).toBeLessThan(
269
+ sectionOf("statusFilter")?.order as number,
270
+ );
271
+ expect(sectionOf("statusFilter")?.defaultCollapsed).toBe(true);
272
+ });
273
+
274
+ it("returns a fresh array each call so a caller cannot mutate the declaration", () => {
275
+ const first: Array<ComponentArgument<DashboardNetworkMapComponent>> =
276
+ getArguments();
277
+ first.pop();
278
+
279
+ expect(getArguments()).toHaveLength(6);
280
+ });
281
+ });
282
+
283
+ /*
284
+ * These go through the registry's public lookup rather than the util
285
+ * directly, so forgetting to wire DashboardComponentType.NetworkMap into
286
+ * Common/Utils/Dashboard/Components/Index.ts fails the suite. Nothing else
287
+ * in the codebase asserts that registry is exhaustive.
288
+ */
289
+ describe("registration in DashboardComponentsUtil", () => {
290
+ it("resolves DashboardComponentType.NetworkMap instead of throwing", () => {
291
+ expect(() => {
292
+ return DashboardComponentsUtil.getComponentSettingsArguments(
293
+ DashboardComponentType.NetworkMap,
294
+ );
295
+ }).not.toThrow();
296
+ });
297
+
298
+ it("returns the Network Map widget's own arguments for that type", () => {
299
+ const fromRegistry: Array<ComponentArgument<DashboardBaseComponent>> =
300
+ DashboardComponentsUtil.getComponentSettingsArguments(
301
+ DashboardComponentType.NetworkMap,
302
+ );
303
+
304
+ expect(
305
+ fromRegistry.map(
306
+ (arg: ComponentArgument<DashboardBaseComponent>): unknown => {
307
+ return arg.id;
308
+ },
309
+ ),
310
+ ).toEqual(
311
+ getArguments().map(
312
+ (arg: ComponentArgument<DashboardNetworkMapComponent>): unknown => {
313
+ return arg.id;
314
+ },
315
+ ),
316
+ );
317
+ });
318
+
319
+ it("still throws for a component type that is not registered", () => {
320
+ expect(() => {
321
+ return DashboardComponentsUtil.getComponentSettingsArguments(
322
+ "NotARealWidget" as DashboardComponentType,
323
+ );
324
+ }).toThrow();
325
+ });
326
+ });
327
+ });
@@ -39,6 +39,7 @@ enum DashboardComponentType {
39
39
  DockerSwarmServiceList = `DockerSwarmServiceList`,
40
40
  CephOsdList = `CephOsdList`,
41
41
  CephPoolList = `CephPoolList`,
42
+ NetworkMap = `NetworkMap`,
42
43
  Html = `Html`,
43
44
  }
44
45
 
@@ -39,6 +39,7 @@ export enum EntityFilterModelType {
39
39
  ProxmoxCluster = "ProxmoxCluster",
40
40
  CephCluster = "CephCluster",
41
41
  DockerSwarmCluster = "DockerSwarmCluster",
42
+ NetworkSiteType = "NetworkSiteType",
42
43
  }
43
44
 
44
45
  export interface ComponentArgumentSection {
@@ -0,0 +1,40 @@
1
+ import ObjectID from "../../ObjectID";
2
+ import DashboardComponentType from "../DashboardComponentType";
3
+ import BaseComponent from "./DashboardBaseComponent";
4
+
5
+ /*
6
+ * The dashboard-embeddable half of the Network Map: a world map of this
7
+ * project's NetworkSites, each pinned at its own coordinates and colored by
8
+ * the monitor status rolled up onto it.
9
+ *
10
+ * It is deliberately NOT the drill-down map from Pages/NetworkSite/NetworkMap
11
+ * — that one navigates, and a dashboard tile is a picture rather than a
12
+ * place you walk around in. This widget answers one question at a glance:
13
+ * where is the network, and what is red right now.
14
+ */
15
+ export default interface DashboardNetworkMapComponent extends BaseComponent {
16
+ componentType: DashboardComponentType.NetworkMap;
17
+ componentId: ObjectID;
18
+ arguments: {
19
+ title?: string | undefined;
20
+ /*
21
+ * How many sites the widget will draw. Sites past the cap are dropped
22
+ * rather than silently merged, and the footer says so — a count that
23
+ * quietly disagrees with the Sites page is how somebody concludes a
24
+ * site has disappeared.
25
+ */
26
+ maxSites?: number | undefined;
27
+ // Restrict the map to sites of these NetworkSiteType rows.
28
+ networkSiteTypeIds?: Array<string> | undefined;
29
+ /*
30
+ * "" / undefined — every site with coordinates.
31
+ * "down" — only sites whose rolled-up status is not operational.
32
+ * "operational" — only sites whose rolled-up status is operational.
33
+ */
34
+ statusFilter?: string | undefined;
35
+ // "map" (default) draws the world map; "list" falls back to a table.
36
+ viewMode?: "map" | "list" | undefined;
37
+ // Print each marker's site name under it when the map is not too busy.
38
+ showLabels?: boolean | undefined;
39
+ };
40
+ }