@oneuptime/common 12.0.12 → 12.0.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (234) hide show
  1. package/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.ts +32 -5
  2. package/Models/DatabaseModels/NetworkDevice.ts +64 -0
  3. package/Models/DatabaseModels/StatusPage.ts +59 -0
  4. package/Models/DatabaseModels/StatusPageOidc.ts +2 -0
  5. package/Models/DatabaseModels/WorkspaceProjectAuthToken.ts +19 -2
  6. package/Server/API/AIChatAPI.ts +55 -56
  7. package/Server/API/CommonAPI.ts +89 -0
  8. package/Server/API/MicrosoftTeamsAPI.ts +20 -9
  9. package/Server/API/SlackAPI.ts +13 -10
  10. package/Server/API/StatusPageAPI.ts +2197 -2128
  11. package/Server/API/TelemetryAPI.ts +72 -3
  12. package/Server/Infrastructure/Postgres/SchemaMigrations/1787500000000-AddEnableSearchEngineIndexingToStatusPage.ts +30 -0
  13. package/Server/Infrastructure/Postgres/SchemaMigrations/1787600000000-AddNetworkDeviceReachabilityColumns.ts +47 -0
  14. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +4 -0
  15. package/Server/Middleware/UserAuthorization.ts +11 -2
  16. package/Server/Services/ApiKeyPermissionService.ts +116 -2
  17. package/Server/Services/DatabaseService.ts +17 -6
  18. package/Server/Services/LogAggregationService.ts +34 -0
  19. package/Server/Services/LogService.ts +21 -0
  20. package/Server/Services/NetworkSiteService.ts +12 -0
  21. package/Server/Services/SpanService.ts +21 -0
  22. package/Server/Services/TraceAggregationService.ts +15 -0
  23. package/Server/Services/WorkspaceNotificationRuleService.ts +172 -220
  24. package/Server/Types/AnalyticsDatabase/ModelPermission.ts +9 -0
  25. package/Server/Types/Database/Permissions/AccessControlPermission.ts +47 -16
  26. package/Server/Utils/APIKey/AccessPermission.ts +16 -40
  27. package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +144 -0
  28. package/Server/Utils/LogRedaction.ts +576 -0
  29. package/Server/Utils/Logger.ts +123 -46
  30. package/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.ts +94 -23
  31. package/Server/Utils/Monitor/MonitorResource.ts +29 -9
  32. package/Server/Utils/Monitor/NetworkInventoryUtil.ts +28 -7
  33. package/Server/Utils/SessionReplay/SessionReplayGateCacheStore.ts +56 -10
  34. package/Server/Utils/StartServer.ts +30 -9
  35. package/Server/Utils/StatusPageSearchEngineIndexing.ts +33 -0
  36. package/Server/Utils/Telemetry/ResourceEntityFilter.ts +441 -0
  37. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +488 -46
  38. package/Tests/App/Dashboard/MonitorTypePicker.test.tsx +300 -0
  39. package/Tests/App/StatusPage/StatusPageIndexPageRobotsMeta.test.ts +130 -0
  40. package/Tests/Models/DatabaseModels/DatabaseBaseModelToJSONObjectCache.test.ts +180 -0
  41. package/Tests/Models/StatusPageEnableSearchEngineIndexing.test.ts +157 -0
  42. package/Tests/Server/API/AIChatCancelAndFeedback.test.ts +23 -1
  43. package/Tests/Server/API/AIChatRouteAuthorization.test.ts +785 -0
  44. package/Tests/Server/API/BaseAPIApiKeyAuth.test.ts +21 -8
  45. package/Tests/Server/API/CommonAPIAuthGuard.test.ts +290 -0
  46. package/Tests/Server/API/MicrosoftTeamsManifest.test.ts +7 -1
  47. package/Tests/Server/API/StatusPageOverviewCache.test.ts +474 -0
  48. package/Tests/Server/API/StatusPageSeoSearchEngineIndexing.test.ts +189 -0
  49. package/Tests/Server/Infrastructure/InMemoryTTLCache.test.ts +158 -0
  50. package/Tests/Server/Infrastructure/Postgres/DeviceRoleAndDeclaredLinkParentMigration.test.ts +9 -60
  51. package/Tests/Server/Middleware/ProjectAuthorizationApiKeyMiddleware.test.ts +24 -15
  52. package/Tests/Server/Middleware/UserAuthorization.test.ts +79 -0
  53. package/Tests/Server/Services/AddNetworkDeviceReachabilityColumnsMigration.test.ts +332 -0
  54. package/Tests/Server/Services/ApiKeyPermissionService.test.ts +347 -0
  55. package/Tests/Server/Services/DatabaseServiceUpdateDebugLogging.test.ts +282 -0
  56. package/Tests/Server/Services/NetworkSiteService.test.ts +56 -2
  57. package/Tests/Server/Services/TelemetryResourceFacetFilters.test.ts +267 -0
  58. package/Tests/Server/Services/WorkspaceNotificationRuleTestRuleChannels.test.ts +702 -0
  59. package/Tests/Server/Types/Database/Permissions/AccessControlPermission.test.ts +165 -0
  60. package/Tests/Server/Utils/APIKey/AccessPermission.test.ts +48 -36
  61. package/Tests/Server/Utils/AnalyticsDatabase/StatementGenerator.test.ts +213 -0
  62. package/Tests/Server/Utils/LogRedaction.test.ts +415 -0
  63. package/Tests/Server/Utils/LoggerCredentialLeak.test.ts +245 -0
  64. package/Tests/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.test.ts +374 -4
  65. package/Tests/Server/Utils/Monitor/MonitorResourceIngestedStepSelection.test.ts +455 -0
  66. package/Tests/Server/Utils/Monitor/NetworkDeviceReachabilityRoundTrip.test.ts +369 -0
  67. package/Tests/Server/Utils/Monitor/NetworkInventoryUtil.test.ts +86 -8
  68. package/Tests/Server/Utils/PrivacyFilterUtil.test.ts +64 -0
  69. package/Tests/Server/Utils/SessionReplay/SessionReplayGateCacheStore.test.ts +207 -0
  70. package/Tests/Server/Utils/StartServerBodyVerify.test.ts +335 -0
  71. package/Tests/Server/Utils/StatusPageSearchEngineIndexing.test.ts +60 -0
  72. package/Tests/Server/Utils/Telemetry/ResourceEntityFilter.test.ts +435 -0
  73. package/Tests/Server/Utils/Workspace/MicrosoftTeamsChannelSend.test.ts +607 -62
  74. package/Tests/Server/Utils/Workspace/MicrosoftTeamsInstalledTeamIdSpace.test.ts +778 -0
  75. package/Tests/Server/Utils/Workspace/MicrosoftTeamsTeamInstalls.test.ts +44 -10
  76. package/Tests/Types/API/HostnameFromAuthority.test.ts +164 -0
  77. package/Tests/Types/Database/AccessControl/ColumnAccessControlCache.test.ts +344 -0
  78. package/Tests/Types/Database/DatabasePropertyFindOperator.test.ts +518 -0
  79. package/Tests/Types/Database/TableColumnMetadataCache.test.ts +286 -0
  80. package/Tests/Types/Monitor/MonitorStep.test.ts +100 -0
  81. package/Tests/Types/Monitor/MonitorTypeKeywords.test.ts +360 -0
  82. package/Tests/Types/StatusPage/SearchEngineIndexing.test.ts +86 -0
  83. package/Tests/Types/Telemetry/ResourceEntityFacet.test.ts +254 -0
  84. package/Tests/UI/Components/CardSelect.test.tsx +1021 -0
  85. package/Tests/UI/Components/MasterPage.test.tsx +36 -1
  86. package/Tests/Utils/Monitor/MonitorMetricType.test.ts +66 -1
  87. package/Tests/Utils/Monitor/NetworkTopologyUtil.test.ts +115 -6
  88. package/Tests/Utils/NetworkDevice/DeviceReachabilityUtil.test.ts +610 -0
  89. package/Tests/Utils/NetworkSite/SiteStatusRollupUtil.test.ts +114 -45
  90. package/Types/API/Hostname.ts +79 -0
  91. package/Types/Database/AccessControl/ColumnAccessControl.ts +33 -11
  92. package/Types/Database/AccessControl/ColumnBillingAccessControl.ts +33 -11
  93. package/Types/Database/DatabaseProperty.ts +38 -0
  94. package/Types/Database/TableColumn.ts +34 -7
  95. package/Types/Events/Recurring.ts +16 -1
  96. package/Types/Monitor/MonitorStep.ts +13 -1
  97. package/Types/Monitor/MonitorType.ts +345 -40
  98. package/Types/Monitor/SSLMonitor/SslMonitorResponse.ts +43 -0
  99. package/Types/StatusPage/SearchEngineIndexing.ts +61 -0
  100. package/Types/Telemetry/ResourceEntityFacet.ts +211 -0
  101. package/UI/Components/CardSelect/CardSelect.tsx +714 -105
  102. package/UI/Components/EmptyState/EmptyState.tsx +8 -1
  103. package/UI/Components/Footer/Footer.tsx +32 -6
  104. package/UI/Components/Forms/Fields/FormField.tsx +3 -0
  105. package/UI/Components/Forms/Types/Field.ts +8 -0
  106. package/UI/Components/Loader/PageLoader.tsx +29 -8
  107. package/UI/Components/LogsViewer/components/LogsAnalyticsView.tsx +26 -4
  108. package/UI/Components/Markdown.tsx/LazyMarkdownViewer.tsx +17 -1
  109. package/UI/Components/MasterPage/MasterPage.tsx +24 -11
  110. package/Utils/Monitor/MonitorMetricType.ts +16 -1
  111. package/Utils/Monitor/NetworkTopologyUtil.ts +60 -8
  112. package/Utils/NetworkDevice/DeviceReachabilityUtil.ts +262 -0
  113. package/Utils/NetworkSite/SiteStatusRollupUtil.ts +41 -24
  114. package/Utils/Telemetry/CrossSignalScope.ts +75 -4
  115. package/build/dist/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.js +20 -4
  116. package/build/dist/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.js.map +1 -1
  117. package/build/dist/Models/DatabaseModels/NetworkDevice.js +66 -0
  118. package/build/dist/Models/DatabaseModels/NetworkDevice.js.map +1 -1
  119. package/build/dist/Models/DatabaseModels/StatusPage.js +60 -0
  120. package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
  121. package/build/dist/Models/DatabaseModels/StatusPageOidc.js +2 -0
  122. package/build/dist/Models/DatabaseModels/StatusPageOidc.js.map +1 -1
  123. package/build/dist/Models/DatabaseModels/WorkspaceProjectAuthToken.js.map +1 -1
  124. package/build/dist/Server/API/AIChatAPI.js +48 -35
  125. package/build/dist/Server/API/AIChatAPI.js.map +1 -1
  126. package/build/dist/Server/API/CommonAPI.js +59 -0
  127. package/build/dist/Server/API/CommonAPI.js.map +1 -1
  128. package/build/dist/Server/API/MicrosoftTeamsAPI.js +20 -6
  129. package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -1
  130. package/build/dist/Server/API/SlackAPI.js +13 -10
  131. package/build/dist/Server/API/SlackAPI.js.map +1 -1
  132. package/build/dist/Server/API/StatusPageAPI.js +745 -673
  133. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  134. package/build/dist/Server/API/TelemetryAPI.js +34 -3
  135. package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
  136. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787500000000-AddEnableSearchEngineIndexingToStatusPage.js +23 -0
  137. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787500000000-AddEnableSearchEngineIndexingToStatusPage.js.map +1 -0
  138. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787600000000-AddNetworkDeviceReachabilityColumns.js +34 -0
  139. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787600000000-AddNetworkDeviceReachabilityColumns.js.map +1 -0
  140. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +4 -0
  141. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  142. package/build/dist/Server/Middleware/UserAuthorization.js +6 -2
  143. package/build/dist/Server/Middleware/UserAuthorization.js.map +1 -1
  144. package/build/dist/Server/Services/ApiKeyPermissionService.js +90 -1
  145. package/build/dist/Server/Services/ApiKeyPermissionService.js.map +1 -1
  146. package/build/dist/Server/Services/DatabaseService.js +15 -6
  147. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  148. package/build/dist/Server/Services/LogAggregationService.js +2 -0
  149. package/build/dist/Server/Services/LogAggregationService.js.map +1 -1
  150. package/build/dist/Server/Services/LogService.js +16 -0
  151. package/build/dist/Server/Services/LogService.js.map +1 -1
  152. package/build/dist/Server/Services/NetworkSiteService.js +12 -0
  153. package/build/dist/Server/Services/NetworkSiteService.js.map +1 -1
  154. package/build/dist/Server/Services/SpanService.js +16 -0
  155. package/build/dist/Server/Services/SpanService.js.map +1 -1
  156. package/build/dist/Server/Services/TraceAggregationService.js +2 -0
  157. package/build/dist/Server/Services/TraceAggregationService.js.map +1 -1
  158. package/build/dist/Server/Services/WorkspaceNotificationRuleService.js +137 -164
  159. package/build/dist/Server/Services/WorkspaceNotificationRuleService.js.map +1 -1
  160. package/build/dist/Server/Types/AnalyticsDatabase/ModelPermission.js +9 -0
  161. package/build/dist/Server/Types/AnalyticsDatabase/ModelPermission.js.map +1 -1
  162. package/build/dist/Server/Types/Database/Permissions/AccessControlPermission.js +21 -13
  163. package/build/dist/Server/Types/Database/Permissions/AccessControlPermission.js.map +1 -1
  164. package/build/dist/Server/Utils/APIKey/AccessPermission.js +9 -32
  165. package/build/dist/Server/Utils/APIKey/AccessPermission.js.map +1 -1
  166. package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +87 -0
  167. package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js.map +1 -1
  168. package/build/dist/Server/Utils/LogRedaction.js +449 -0
  169. package/build/dist/Server/Utils/LogRedaction.js.map +1 -0
  170. package/build/dist/Server/Utils/Logger.js +98 -45
  171. package/build/dist/Server/Utils/Logger.js.map +1 -1
  172. package/build/dist/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.js +65 -13
  173. package/build/dist/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.js.map +1 -1
  174. package/build/dist/Server/Utils/Monitor/MonitorResource.js +20 -3
  175. package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
  176. package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js +27 -7
  177. package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js.map +1 -1
  178. package/build/dist/Server/Utils/SessionReplay/SessionReplayGateCacheStore.js +29 -10
  179. package/build/dist/Server/Utils/SessionReplay/SessionReplayGateCacheStore.js.map +1 -1
  180. package/build/dist/Server/Utils/StartServer.js +10 -7
  181. package/build/dist/Server/Utils/StartServer.js.map +1 -1
  182. package/build/dist/Server/Utils/StatusPageSearchEngineIndexing.js +24 -0
  183. package/build/dist/Server/Utils/StatusPageSearchEngineIndexing.js.map +1 -0
  184. package/build/dist/Server/Utils/Telemetry/ResourceEntityFilter.js +311 -0
  185. package/build/dist/Server/Utils/Telemetry/ResourceEntityFilter.js.map +1 -0
  186. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +366 -37
  187. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
  188. package/build/dist/Types/API/Hostname.js +63 -0
  189. package/build/dist/Types/API/Hostname.js.map +1 -1
  190. package/build/dist/Types/Database/AccessControl/ColumnAccessControl.js +20 -6
  191. package/build/dist/Types/Database/AccessControl/ColumnAccessControl.js.map +1 -1
  192. package/build/dist/Types/Database/AccessControl/ColumnBillingAccessControl.js +20 -6
  193. package/build/dist/Types/Database/AccessControl/ColumnBillingAccessControl.js.map +1 -1
  194. package/build/dist/Types/Database/DatabaseProperty.js +38 -0
  195. package/build/dist/Types/Database/DatabaseProperty.js.map +1 -1
  196. package/build/dist/Types/Database/TableColumn.js +24 -6
  197. package/build/dist/Types/Database/TableColumn.js.map +1 -1
  198. package/build/dist/Types/Events/Recurring.js +13 -1
  199. package/build/dist/Types/Events/Recurring.js.map +1 -1
  200. package/build/dist/Types/Monitor/MonitorStep.js +9 -1
  201. package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
  202. package/build/dist/Types/Monitor/MonitorType.js +330 -31
  203. package/build/dist/Types/Monitor/MonitorType.js.map +1 -1
  204. package/build/dist/Types/StatusPage/SearchEngineIndexing.js +55 -0
  205. package/build/dist/Types/StatusPage/SearchEngineIndexing.js.map +1 -0
  206. package/build/dist/Types/Telemetry/ResourceEntityFacet.js +156 -0
  207. package/build/dist/Types/Telemetry/ResourceEntityFacet.js.map +1 -0
  208. package/build/dist/UI/Components/CardSelect/CardSelect.js +360 -40
  209. package/build/dist/UI/Components/CardSelect/CardSelect.js.map +1 -1
  210. package/build/dist/UI/Components/EmptyState/EmptyState.js +1 -1
  211. package/build/dist/UI/Components/EmptyState/EmptyState.js.map +1 -1
  212. package/build/dist/UI/Components/Footer/Footer.js +7 -4
  213. package/build/dist/UI/Components/Footer/Footer.js.map +1 -1
  214. package/build/dist/UI/Components/Forms/Fields/FormField.js +1 -1
  215. package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
  216. package/build/dist/UI/Components/Loader/PageLoader.js +15 -5
  217. package/build/dist/UI/Components/Loader/PageLoader.js.map +1 -1
  218. package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js +16 -3
  219. package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js.map +1 -1
  220. package/build/dist/UI/Components/Markdown.tsx/LazyMarkdownViewer.js +13 -1
  221. package/build/dist/UI/Components/Markdown.tsx/LazyMarkdownViewer.js.map +1 -1
  222. package/build/dist/UI/Components/MasterPage/MasterPage.js +10 -2
  223. package/build/dist/UI/Components/MasterPage/MasterPage.js.map +1 -1
  224. package/build/dist/Utils/Monitor/MonitorMetricType.js +13 -1
  225. package/build/dist/Utils/Monitor/MonitorMetricType.js.map +1 -1
  226. package/build/dist/Utils/Monitor/NetworkTopologyUtil.js +34 -7
  227. package/build/dist/Utils/Monitor/NetworkTopologyUtil.js.map +1 -1
  228. package/build/dist/Utils/NetworkDevice/DeviceReachabilityUtil.js +177 -0
  229. package/build/dist/Utils/NetworkDevice/DeviceReachabilityUtil.js.map +1 -0
  230. package/build/dist/Utils/NetworkSite/SiteStatusRollupUtil.js +23 -27
  231. package/build/dist/Utils/NetworkSite/SiteStatusRollupUtil.js.map +1 -1
  232. package/build/dist/Utils/Telemetry/CrossSignalScope.js +44 -4
  233. package/build/dist/Utils/Telemetry/CrossSignalScope.js.map +1 -1
  234. package/package.json +1 -1
@@ -0,0 +1,61 @@
1
+ /*
2
+ * Search engine indexing for status pages.
3
+ *
4
+ * A status page is indexable unless its owner turned indexing off
5
+ * (StatusPage.enableSearchEngineIndexing, which defaults to true). The
6
+ * opt-out is carried to crawlers two ways:
7
+ *
8
+ * - <meta name="robots" content="noindex, nofollow"> in the server-rendered
9
+ * index.ejs, which is in the HTML before the JS bundle loads
10
+ * - an X-Robots-Tag response header, which crawlers also honour on
11
+ * responses that are not HTML (the RSS feed, llms.txt)
12
+ *
13
+ * Both live here rather than in each renderer because the status page is
14
+ * rendered by two servers - the standalone status-page container
15
+ * (App/FeatureSet/StatusPage/Serve.ts) and the combined App container
16
+ * (App/FeatureSet/Frontend/Index.ts) - and the two must not drift.
17
+ */
18
+
19
+ /*
20
+ * nofollow rides along with noindex: a page that is meant to stay out of
21
+ * search results should not be passing crawl signals to the pages it links to
22
+ * either. This is the same directive Atlassian Statuspage and Status.io emit
23
+ * for their equivalent setting.
24
+ */
25
+ export const NOINDEX_ROBOTS_DIRECTIVE: string = "noindex, nofollow";
26
+
27
+ export const X_ROBOTS_TAG_HEADER_NAME: string = "X-Robots-Tag";
28
+
29
+ /*
30
+ * Name of the flag on the status page /seo API response, and of the variable
31
+ * handed to index.ejs. Kept as a constant so the API, the two renderers and
32
+ * the template cannot disagree about the spelling.
33
+ */
34
+ export const SEARCH_ENGINE_INDEXING_FLAG_NAME: string =
35
+ "enableSearchEngineIndexing";
36
+
37
+ /**
38
+ * Reads the indexing flag off an untyped value - a JSON field from the /seo
39
+ * API, or a column that predates the migration.
40
+ *
41
+ * Everything that is not an explicit "off" means indexable. Failing this open
42
+ * is deliberate: the flag reaches the renderers over an internal HTTP call,
43
+ * and a blip on that call must not de-index a page whose owner never asked for
44
+ * that. The opposite failure (one render of a noindex page missing its meta
45
+ * tag) costs nothing permanent, because crawlers re-fetch.
46
+ */
47
+ export function isSearchEngineIndexingEnabled(value: unknown): boolean {
48
+ if (value === false) {
49
+ return false;
50
+ }
51
+
52
+ /*
53
+ * JSON round-trips and query strings can turn the boolean into a string.
54
+ * "false" is the only spelling either produces for false.
55
+ */
56
+ if (value === "false") {
57
+ return false;
58
+ }
59
+
60
+ return true;
61
+ }
@@ -0,0 +1,211 @@
1
+ /*
2
+ * The resource facets of the telemetry explorers (Logs / Traces), split by
3
+ * how a selection has to be turned into a predicate.
4
+ *
5
+ * A signal row names exactly one *primary* entity (`primaryEntityId` +
6
+ * `primaryEntityType`) and, since the entity model shipped, the full
7
+ * membership set it belongs to (`entityKeys`). Which of the two a facet
8
+ * selection has to be matched against depends on the ingestion path:
9
+ *
10
+ * - Agent-ingested resource telemetry (Infrastructure / Docker / Podman /
11
+ * Kubernetes agents) has no `service.name`, so the resource IS the
12
+ * primary entity and `primaryEntityId` holds its Postgres id.
13
+ * - OTLP telemetry that carries a `service.name` is primary-keyed on its
14
+ * Service (`OtelIngestBaseService.selectPrimaryEntity`); the host /
15
+ * cluster it runs on is recorded only in `entityKeys`.
16
+ *
17
+ * The second case is why coalescing every resource facet into one
18
+ * `primaryEntityId IN (...)` list returned nothing for a Kubernetes cluster
19
+ * ingested through an OpenTelemetry Collector: the cluster id was compared
20
+ * against a column that only ever held the Service id. Selections on these
21
+ * facets therefore compile to a per-facet OR of both memberships, and the
22
+ * per-facet groups AND with each other so "cluster X" + "service Y"
23
+ * intersects instead of unioning.
24
+ */
25
+
26
+ /**
27
+ * Facets whose values are Service ids — the ids that legitimately live in
28
+ * `primaryEntityId` for OTLP telemetry. `serviceId` is the pre-rename alias
29
+ * kept for stale clients (see ResourceFacetResolver).
30
+ */
31
+ export const SERVICE_FACET_KEYS: ReadonlyArray<string> = [
32
+ "primaryEntityId",
33
+ "serviceId",
34
+ ];
35
+
36
+ /**
37
+ * Facets whose values are ids of a NON-Service resource row. These are the
38
+ * ones that need the entity-key treatment; the value is a Postgres id
39
+ * (Host / DockerHost / PodmanHost / KubernetesCluster) which the server
40
+ * resolves to the resource's identifying value and then to its entity key.
41
+ */
42
+ export const RESOURCE_ENTITY_FACET_KEYS: ReadonlyArray<string> = [
43
+ "hostId",
44
+ "dockerHostId",
45
+ "podmanHostId",
46
+ "kubernetesClusterId",
47
+ ];
48
+
49
+ const RESOURCE_ENTITY_FACET_KEY_SET: ReadonlySet<string> = new Set<string>(
50
+ RESOURCE_ENTITY_FACET_KEYS,
51
+ );
52
+
53
+ const SERVICE_FACET_KEY_SET: ReadonlySet<string> = new Set<string>(
54
+ SERVICE_FACET_KEYS,
55
+ );
56
+
57
+ /**
58
+ * Every facet key that names a resource, in either group. Kept so call
59
+ * sites that only need "is this a resource chip?" (chip rendering, scope
60
+ * hand-off) do not have to consult both sets.
61
+ */
62
+ export const ALL_RESOURCE_FACET_KEYS: ReadonlyArray<string> = [
63
+ ...SERVICE_FACET_KEYS,
64
+ ...RESOURCE_ENTITY_FACET_KEYS,
65
+ ];
66
+
67
+ export function isServiceFacetKey(facetKey: string): boolean {
68
+ return SERVICE_FACET_KEY_SET.has(facetKey);
69
+ }
70
+
71
+ export function isResourceEntityFacetKey(facetKey: string): boolean {
72
+ return RESOURCE_ENTITY_FACET_KEY_SET.has(facetKey);
73
+ }
74
+
75
+ export function isResourceFacetKey(facetKey: string): boolean {
76
+ return isServiceFacetKey(facetKey) || isResourceEntityFacetKey(facetKey);
77
+ }
78
+
79
+ /**
80
+ * Selected ids per non-Service resource facet, e.g.
81
+ * `{ kubernetesClusterId: ["<id>"] }`. This is the wire shape sent to the
82
+ * aggregation endpoints (`resourceFilters` in the POST body) and carried on
83
+ * the analytics list query under the same key.
84
+ */
85
+ export type ResourceEntityFacetSelections = Record<string, Array<string>>;
86
+
87
+ /**
88
+ * Collect the non-Service resource selections out of applied facet filters.
89
+ * Accepts anything entry-iterable so the Logs explorer can pass a
90
+ * `Map<string, Set<string>>` and the Traces explorer a plain record via
91
+ * `Object.entries`.
92
+ *
93
+ * Values are de-duplicated and empty facets are dropped, so an emptied chip
94
+ * group never becomes a `[]` that a reader might read as "match nothing".
95
+ */
96
+ export function collectResourceEntityFacetSelections(
97
+ entries: Iterable<[string, Iterable<string>]>,
98
+ ): ResourceEntityFacetSelections {
99
+ const selections: ResourceEntityFacetSelections = {};
100
+
101
+ for (const [facetKey, values] of entries) {
102
+ if (!isResourceEntityFacetKey(facetKey)) {
103
+ continue;
104
+ }
105
+
106
+ const unique: Set<string> = new Set<string>(selections[facetKey] || []);
107
+
108
+ for (const value of values) {
109
+ if (typeof value === "string" && value.length > 0) {
110
+ unique.add(value);
111
+ }
112
+ }
113
+
114
+ if (unique.size > 0) {
115
+ selections[facetKey] = Array.from(unique);
116
+ }
117
+ }
118
+
119
+ return selections;
120
+ }
121
+
122
+ /** Collect the Service-facet selections, same rules as the helper above. */
123
+ export function collectServiceFacetSelections(
124
+ entries: Iterable<[string, Iterable<string>]>,
125
+ ): Array<string> {
126
+ const unique: Set<string> = new Set<string>();
127
+
128
+ for (const [facetKey, values] of entries) {
129
+ if (!isServiceFacetKey(facetKey)) {
130
+ continue;
131
+ }
132
+
133
+ for (const value of values) {
134
+ if (typeof value === "string" && value.length > 0) {
135
+ unique.add(value);
136
+ }
137
+ }
138
+ }
139
+
140
+ return Array.from(unique);
141
+ }
142
+
143
+ /*
144
+ * Ids are ObjectID (UUID) strings. Validating the shape here means a
145
+ * malformed filter is dropped at the edge instead of reaching a lookup or a
146
+ * SQL parameter.
147
+ */
148
+ const UUID_PATTERN: RegExp =
149
+ /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
150
+
151
+ /** Upper bound on ids per facet, so a crafted request cannot send an unbounded list. */
152
+ export const MAX_RESOURCE_IDS_PER_FACET: number = 200;
153
+
154
+ /**
155
+ * Parse an untrusted `resourceFilters` value (request body field or a
156
+ * persisted saved-view query) into the canonical selection shape.
157
+ *
158
+ * Deliberately lenient: anything that is not `{ <known facet key>:
159
+ * <uuid>[] }` is dropped rather than turned into a predicate, because a
160
+ * malformed filter must mean "no constraint" — never "match nothing", which
161
+ * would show an empty explorer with no explanation.
162
+ */
163
+ export function parseResourceEntityFacetSelections(
164
+ source: unknown,
165
+ ): ResourceEntityFacetSelections {
166
+ const selections: ResourceEntityFacetSelections = {};
167
+
168
+ if (!source || typeof source !== "object" || Array.isArray(source)) {
169
+ return selections;
170
+ }
171
+
172
+ const record: Record<string, unknown> = source as Record<string, unknown>;
173
+
174
+ for (const facetKey of Object.keys(record)) {
175
+ if (!isResourceEntityFacetKey(facetKey)) {
176
+ continue;
177
+ }
178
+
179
+ const raw: unknown = record[facetKey];
180
+
181
+ if (!Array.isArray(raw)) {
182
+ continue;
183
+ }
184
+
185
+ const ids: Array<string> = Array.from(
186
+ new Set(
187
+ (raw as Array<unknown>).filter((value: unknown): value is string => {
188
+ return typeof value === "string" && UUID_PATTERN.test(value);
189
+ }),
190
+ ),
191
+ ).slice(0, MAX_RESOURCE_IDS_PER_FACET);
192
+
193
+ if (ids.length > 0) {
194
+ selections[facetKey] = ids;
195
+ }
196
+ }
197
+
198
+ return selections;
199
+ }
200
+
201
+ export function hasResourceEntityFacetSelections(
202
+ selections: ResourceEntityFacetSelections | undefined,
203
+ ): boolean {
204
+ if (!selections) {
205
+ return false;
206
+ }
207
+
208
+ return Object.keys(selections).some((key: string): boolean => {
209
+ return (selections[key] || []).length > 0;
210
+ });
211
+ }