@oneuptime/common 12.0.12 → 12.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.ts +32 -5
- package/Models/DatabaseModels/NetworkDevice.ts +64 -0
- package/Models/DatabaseModels/StatusPage.ts +59 -0
- package/Models/DatabaseModels/StatusPageOidc.ts +2 -0
- package/Models/DatabaseModels/WorkspaceProjectAuthToken.ts +19 -2
- package/Server/API/AIChatAPI.ts +55 -56
- package/Server/API/CommonAPI.ts +89 -0
- package/Server/API/MicrosoftTeamsAPI.ts +20 -9
- package/Server/API/SlackAPI.ts +13 -10
- package/Server/API/StatusPageAPI.ts +2197 -2128
- package/Server/API/TelemetryAPI.ts +72 -3
- package/Server/Infrastructure/Postgres/SchemaMigrations/1787500000000-AddEnableSearchEngineIndexingToStatusPage.ts +30 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1787600000000-AddNetworkDeviceReachabilityColumns.ts +47 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +4 -0
- package/Server/Middleware/UserAuthorization.ts +11 -2
- package/Server/Services/ApiKeyPermissionService.ts +116 -2
- package/Server/Services/DatabaseService.ts +17 -6
- package/Server/Services/LogAggregationService.ts +34 -0
- package/Server/Services/LogService.ts +21 -0
- package/Server/Services/NetworkSiteService.ts +12 -0
- package/Server/Services/SpanService.ts +21 -0
- package/Server/Services/TraceAggregationService.ts +15 -0
- package/Server/Services/WorkspaceNotificationRuleService.ts +172 -220
- package/Server/Types/AnalyticsDatabase/ModelPermission.ts +9 -0
- package/Server/Types/Database/Permissions/AccessControlPermission.ts +47 -16
- package/Server/Utils/APIKey/AccessPermission.ts +16 -40
- package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +144 -0
- package/Server/Utils/LogRedaction.ts +576 -0
- package/Server/Utils/Logger.ts +123 -46
- package/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.ts +94 -23
- package/Server/Utils/Monitor/MonitorResource.ts +29 -9
- package/Server/Utils/Monitor/NetworkInventoryUtil.ts +28 -7
- package/Server/Utils/SessionReplay/SessionReplayGateCacheStore.ts +56 -10
- package/Server/Utils/StartServer.ts +30 -9
- package/Server/Utils/StatusPageSearchEngineIndexing.ts +33 -0
- package/Server/Utils/Telemetry/ResourceEntityFilter.ts +441 -0
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +488 -46
- package/Tests/App/Dashboard/MonitorTypePicker.test.tsx +300 -0
- package/Tests/App/StatusPage/StatusPageIndexPageRobotsMeta.test.ts +130 -0
- package/Tests/Models/DatabaseModels/DatabaseBaseModelToJSONObjectCache.test.ts +180 -0
- package/Tests/Models/StatusPageEnableSearchEngineIndexing.test.ts +157 -0
- package/Tests/Server/API/AIChatCancelAndFeedback.test.ts +23 -1
- package/Tests/Server/API/AIChatRouteAuthorization.test.ts +785 -0
- package/Tests/Server/API/BaseAPIApiKeyAuth.test.ts +21 -8
- package/Tests/Server/API/CommonAPIAuthGuard.test.ts +290 -0
- package/Tests/Server/API/MicrosoftTeamsManifest.test.ts +7 -1
- package/Tests/Server/API/StatusPageOverviewCache.test.ts +474 -0
- package/Tests/Server/API/StatusPageSeoSearchEngineIndexing.test.ts +189 -0
- package/Tests/Server/Infrastructure/InMemoryTTLCache.test.ts +158 -0
- package/Tests/Server/Infrastructure/Postgres/DeviceRoleAndDeclaredLinkParentMigration.test.ts +9 -60
- package/Tests/Server/Middleware/ProjectAuthorizationApiKeyMiddleware.test.ts +24 -15
- package/Tests/Server/Middleware/UserAuthorization.test.ts +79 -0
- package/Tests/Server/Services/AddNetworkDeviceReachabilityColumnsMigration.test.ts +332 -0
- package/Tests/Server/Services/ApiKeyPermissionService.test.ts +347 -0
- package/Tests/Server/Services/DatabaseServiceUpdateDebugLogging.test.ts +282 -0
- package/Tests/Server/Services/NetworkSiteService.test.ts +56 -2
- package/Tests/Server/Services/TelemetryResourceFacetFilters.test.ts +267 -0
- package/Tests/Server/Services/WorkspaceNotificationRuleTestRuleChannels.test.ts +702 -0
- package/Tests/Server/Types/Database/Permissions/AccessControlPermission.test.ts +165 -0
- package/Tests/Server/Utils/APIKey/AccessPermission.test.ts +48 -36
- package/Tests/Server/Utils/AnalyticsDatabase/StatementGenerator.test.ts +213 -0
- package/Tests/Server/Utils/LogRedaction.test.ts +415 -0
- package/Tests/Server/Utils/LoggerCredentialLeak.test.ts +245 -0
- package/Tests/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.test.ts +374 -4
- package/Tests/Server/Utils/Monitor/MonitorResourceIngestedStepSelection.test.ts +455 -0
- package/Tests/Server/Utils/Monitor/NetworkDeviceReachabilityRoundTrip.test.ts +369 -0
- package/Tests/Server/Utils/Monitor/NetworkInventoryUtil.test.ts +86 -8
- package/Tests/Server/Utils/PrivacyFilterUtil.test.ts +64 -0
- package/Tests/Server/Utils/SessionReplay/SessionReplayGateCacheStore.test.ts +207 -0
- package/Tests/Server/Utils/StartServerBodyVerify.test.ts +335 -0
- package/Tests/Server/Utils/StatusPageSearchEngineIndexing.test.ts +60 -0
- package/Tests/Server/Utils/Telemetry/ResourceEntityFilter.test.ts +435 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsChannelSend.test.ts +607 -62
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsInstalledTeamIdSpace.test.ts +778 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsTeamInstalls.test.ts +44 -10
- package/Tests/Types/API/HostnameFromAuthority.test.ts +164 -0
- package/Tests/Types/Database/AccessControl/ColumnAccessControlCache.test.ts +344 -0
- package/Tests/Types/Database/DatabasePropertyFindOperator.test.ts +518 -0
- package/Tests/Types/Database/TableColumnMetadataCache.test.ts +286 -0
- package/Tests/Types/Monitor/MonitorStep.test.ts +100 -0
- package/Tests/Types/Monitor/MonitorTypeKeywords.test.ts +360 -0
- package/Tests/Types/StatusPage/SearchEngineIndexing.test.ts +86 -0
- package/Tests/Types/Telemetry/ResourceEntityFacet.test.ts +254 -0
- package/Tests/UI/Components/CardSelect.test.tsx +1021 -0
- package/Tests/UI/Components/MasterPage.test.tsx +36 -1
- package/Tests/Utils/Monitor/MonitorMetricType.test.ts +66 -1
- package/Tests/Utils/Monitor/NetworkTopologyUtil.test.ts +115 -6
- package/Tests/Utils/NetworkDevice/DeviceReachabilityUtil.test.ts +610 -0
- package/Tests/Utils/NetworkSite/SiteStatusRollupUtil.test.ts +114 -45
- package/Types/API/Hostname.ts +79 -0
- package/Types/Database/AccessControl/ColumnAccessControl.ts +33 -11
- package/Types/Database/AccessControl/ColumnBillingAccessControl.ts +33 -11
- package/Types/Database/DatabaseProperty.ts +38 -0
- package/Types/Database/TableColumn.ts +34 -7
- package/Types/Events/Recurring.ts +16 -1
- package/Types/Monitor/MonitorStep.ts +13 -1
- package/Types/Monitor/MonitorType.ts +345 -40
- package/Types/Monitor/SSLMonitor/SslMonitorResponse.ts +43 -0
- package/Types/StatusPage/SearchEngineIndexing.ts +61 -0
- package/Types/Telemetry/ResourceEntityFacet.ts +211 -0
- package/UI/Components/CardSelect/CardSelect.tsx +714 -105
- package/UI/Components/EmptyState/EmptyState.tsx +8 -1
- package/UI/Components/Footer/Footer.tsx +32 -6
- package/UI/Components/Forms/Fields/FormField.tsx +3 -0
- package/UI/Components/Forms/Types/Field.ts +8 -0
- package/UI/Components/Loader/PageLoader.tsx +29 -8
- package/UI/Components/LogsViewer/components/LogsAnalyticsView.tsx +26 -4
- package/UI/Components/Markdown.tsx/LazyMarkdownViewer.tsx +17 -1
- package/UI/Components/MasterPage/MasterPage.tsx +24 -11
- package/Utils/Monitor/MonitorMetricType.ts +16 -1
- package/Utils/Monitor/NetworkTopologyUtil.ts +60 -8
- package/Utils/NetworkDevice/DeviceReachabilityUtil.ts +262 -0
- package/Utils/NetworkSite/SiteStatusRollupUtil.ts +41 -24
- package/Utils/Telemetry/CrossSignalScope.ts +75 -4
- package/build/dist/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.js +20 -4
- package/build/dist/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkDevice.js +66 -0
- package/build/dist/Models/DatabaseModels/NetworkDevice.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPage.js +60 -0
- package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageOidc.js +2 -0
- package/build/dist/Models/DatabaseModels/StatusPageOidc.js.map +1 -1
- package/build/dist/Models/DatabaseModels/WorkspaceProjectAuthToken.js.map +1 -1
- package/build/dist/Server/API/AIChatAPI.js +48 -35
- package/build/dist/Server/API/AIChatAPI.js.map +1 -1
- package/build/dist/Server/API/CommonAPI.js +59 -0
- package/build/dist/Server/API/CommonAPI.js.map +1 -1
- package/build/dist/Server/API/MicrosoftTeamsAPI.js +20 -6
- package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -1
- package/build/dist/Server/API/SlackAPI.js +13 -10
- package/build/dist/Server/API/SlackAPI.js.map +1 -1
- package/build/dist/Server/API/StatusPageAPI.js +745 -673
- package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
- package/build/dist/Server/API/TelemetryAPI.js +34 -3
- package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787500000000-AddEnableSearchEngineIndexingToStatusPage.js +23 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787500000000-AddEnableSearchEngineIndexingToStatusPage.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787600000000-AddNetworkDeviceReachabilityColumns.js +34 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787600000000-AddNetworkDeviceReachabilityColumns.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +4 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Middleware/UserAuthorization.js +6 -2
- package/build/dist/Server/Middleware/UserAuthorization.js.map +1 -1
- package/build/dist/Server/Services/ApiKeyPermissionService.js +90 -1
- package/build/dist/Server/Services/ApiKeyPermissionService.js.map +1 -1
- package/build/dist/Server/Services/DatabaseService.js +15 -6
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/LogAggregationService.js +2 -0
- package/build/dist/Server/Services/LogAggregationService.js.map +1 -1
- package/build/dist/Server/Services/LogService.js +16 -0
- package/build/dist/Server/Services/LogService.js.map +1 -1
- package/build/dist/Server/Services/NetworkSiteService.js +12 -0
- package/build/dist/Server/Services/NetworkSiteService.js.map +1 -1
- package/build/dist/Server/Services/SpanService.js +16 -0
- package/build/dist/Server/Services/SpanService.js.map +1 -1
- package/build/dist/Server/Services/TraceAggregationService.js +2 -0
- package/build/dist/Server/Services/TraceAggregationService.js.map +1 -1
- package/build/dist/Server/Services/WorkspaceNotificationRuleService.js +137 -164
- package/build/dist/Server/Services/WorkspaceNotificationRuleService.js.map +1 -1
- package/build/dist/Server/Types/AnalyticsDatabase/ModelPermission.js +9 -0
- package/build/dist/Server/Types/AnalyticsDatabase/ModelPermission.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/AccessControlPermission.js +21 -13
- package/build/dist/Server/Types/Database/Permissions/AccessControlPermission.js.map +1 -1
- package/build/dist/Server/Utils/APIKey/AccessPermission.js +9 -32
- package/build/dist/Server/Utils/APIKey/AccessPermission.js.map +1 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +87 -0
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js.map +1 -1
- package/build/dist/Server/Utils/LogRedaction.js +449 -0
- package/build/dist/Server/Utils/LogRedaction.js.map +1 -0
- package/build/dist/Server/Utils/Logger.js +98 -45
- package/build/dist/Server/Utils/Logger.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.js +65 -13
- package/build/dist/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorResource.js +20 -3
- package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js +27 -7
- package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js.map +1 -1
- package/build/dist/Server/Utils/SessionReplay/SessionReplayGateCacheStore.js +29 -10
- package/build/dist/Server/Utils/SessionReplay/SessionReplayGateCacheStore.js.map +1 -1
- package/build/dist/Server/Utils/StartServer.js +10 -7
- package/build/dist/Server/Utils/StartServer.js.map +1 -1
- package/build/dist/Server/Utils/StatusPageSearchEngineIndexing.js +24 -0
- package/build/dist/Server/Utils/StatusPageSearchEngineIndexing.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/ResourceEntityFilter.js +311 -0
- package/build/dist/Server/Utils/Telemetry/ResourceEntityFilter.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +366 -37
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
- package/build/dist/Types/API/Hostname.js +63 -0
- package/build/dist/Types/API/Hostname.js.map +1 -1
- package/build/dist/Types/Database/AccessControl/ColumnAccessControl.js +20 -6
- package/build/dist/Types/Database/AccessControl/ColumnAccessControl.js.map +1 -1
- package/build/dist/Types/Database/AccessControl/ColumnBillingAccessControl.js +20 -6
- package/build/dist/Types/Database/AccessControl/ColumnBillingAccessControl.js.map +1 -1
- package/build/dist/Types/Database/DatabaseProperty.js +38 -0
- package/build/dist/Types/Database/DatabaseProperty.js.map +1 -1
- package/build/dist/Types/Database/TableColumn.js +24 -6
- package/build/dist/Types/Database/TableColumn.js.map +1 -1
- package/build/dist/Types/Events/Recurring.js +13 -1
- package/build/dist/Types/Events/Recurring.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorStep.js +9 -1
- package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorType.js +330 -31
- package/build/dist/Types/Monitor/MonitorType.js.map +1 -1
- package/build/dist/Types/StatusPage/SearchEngineIndexing.js +55 -0
- package/build/dist/Types/StatusPage/SearchEngineIndexing.js.map +1 -0
- package/build/dist/Types/Telemetry/ResourceEntityFacet.js +156 -0
- package/build/dist/Types/Telemetry/ResourceEntityFacet.js.map +1 -0
- package/build/dist/UI/Components/CardSelect/CardSelect.js +360 -40
- package/build/dist/UI/Components/CardSelect/CardSelect.js.map +1 -1
- package/build/dist/UI/Components/EmptyState/EmptyState.js +1 -1
- package/build/dist/UI/Components/EmptyState/EmptyState.js.map +1 -1
- package/build/dist/UI/Components/Footer/Footer.js +7 -4
- package/build/dist/UI/Components/Footer/Footer.js.map +1 -1
- package/build/dist/UI/Components/Forms/Fields/FormField.js +1 -1
- package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
- package/build/dist/UI/Components/Loader/PageLoader.js +15 -5
- package/build/dist/UI/Components/Loader/PageLoader.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js +16 -3
- package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js.map +1 -1
- package/build/dist/UI/Components/Markdown.tsx/LazyMarkdownViewer.js +13 -1
- package/build/dist/UI/Components/Markdown.tsx/LazyMarkdownViewer.js.map +1 -1
- package/build/dist/UI/Components/MasterPage/MasterPage.js +10 -2
- package/build/dist/UI/Components/MasterPage/MasterPage.js.map +1 -1
- package/build/dist/Utils/Monitor/MonitorMetricType.js +13 -1
- package/build/dist/Utils/Monitor/MonitorMetricType.js.map +1 -1
- package/build/dist/Utils/Monitor/NetworkTopologyUtil.js +34 -7
- package/build/dist/Utils/Monitor/NetworkTopologyUtil.js.map +1 -1
- package/build/dist/Utils/NetworkDevice/DeviceReachabilityUtil.js +177 -0
- package/build/dist/Utils/NetworkDevice/DeviceReachabilityUtil.js.map +1 -0
- package/build/dist/Utils/NetworkSite/SiteStatusRollupUtil.js +23 -27
- package/build/dist/Utils/NetworkSite/SiteStatusRollupUtil.js.map +1 -1
- package/build/dist/Utils/Telemetry/CrossSignalScope.js +44 -4
- package/build/dist/Utils/Telemetry/CrossSignalScope.js.map +1 -1
- package/package.json +1 -1
|
@@ -95,6 +95,10 @@ import ResourceFacetResolver, {
|
|
|
95
95
|
ResolvedFacetValue,
|
|
96
96
|
ResourceFacetSpec,
|
|
97
97
|
} from "../Utils/Telemetry/ResourceFacetResolver";
|
|
98
|
+
import ResourceEntityFilter, {
|
|
99
|
+
ResourceEntityScope,
|
|
100
|
+
} from "../Utils/Telemetry/ResourceEntityFilter";
|
|
101
|
+
import { ResourceEntityFacetSelections } from "../../Types/Telemetry/ResourceEntityFacet";
|
|
98
102
|
import Label from "../../Models/DatabaseModels/Label";
|
|
99
103
|
import RumApplication from "../../Models/DatabaseModels/RumApplication";
|
|
100
104
|
import RumApplicationService from "../Services/RumApplicationService";
|
|
@@ -456,6 +460,9 @@ router.post(
|
|
|
456
460
|
? (body["attributes"] as Record<string, string | Array<string>>)
|
|
457
461
|
: undefined;
|
|
458
462
|
|
|
463
|
+
const resourceScopes: Array<ResourceEntityScope> | undefined =
|
|
464
|
+
await resolveResourceScopesFromBody(body, databaseProps.tenantId);
|
|
465
|
+
|
|
459
466
|
const request: HistogramRequest = {
|
|
460
467
|
projectId: databaseProps.tenantId,
|
|
461
468
|
startTime,
|
|
@@ -463,6 +470,7 @@ router.post(
|
|
|
463
470
|
bucketSizeInMinutes,
|
|
464
471
|
serviceIds,
|
|
465
472
|
entityKeys,
|
|
473
|
+
resourceScopes,
|
|
466
474
|
severityTexts,
|
|
467
475
|
bodySearchText,
|
|
468
476
|
traceIds,
|
|
@@ -573,6 +581,9 @@ router.post(
|
|
|
573
581
|
*/
|
|
574
582
|
const projectId: ObjectID = databaseProps.tenantId;
|
|
575
583
|
|
|
584
|
+
const resourceScopes: Array<ResourceEntityScope> | undefined =
|
|
585
|
+
await resolveResourceScopesFromBody(body, projectId);
|
|
586
|
+
|
|
576
587
|
/*
|
|
577
588
|
* Run facet queries in parallel so a slow individual facet can't
|
|
578
589
|
* starve the endpoint. Per-facet errors degrade gracefully to [].
|
|
@@ -592,6 +603,7 @@ router.post(
|
|
|
592
603
|
limit,
|
|
593
604
|
serviceIds,
|
|
594
605
|
entityKeys,
|
|
606
|
+
resourceScopes,
|
|
595
607
|
severityTexts,
|
|
596
608
|
bodySearchText,
|
|
597
609
|
traceIds,
|
|
@@ -656,6 +668,41 @@ router.post(
|
|
|
656
668
|
},
|
|
657
669
|
);
|
|
658
670
|
|
|
671
|
+
/**
|
|
672
|
+
* Resolve the `resourceFilters` field — the explorers' non-Service resource
|
|
673
|
+
* facet selections (Kubernetes cluster / host / docker host / podman host),
|
|
674
|
+
* sent as Postgres ids — into the scopes the aggregation services compile.
|
|
675
|
+
*
|
|
676
|
+
* Kept out of the sync body parsers because turning an id into an entity
|
|
677
|
+
* key needs a Postgres round trip: the id names a row whose identifying
|
|
678
|
+
* value (`clusterIdentifier` / `hostIdentifier`) is what ingest hashed into
|
|
679
|
+
* `entityKeys`.
|
|
680
|
+
*
|
|
681
|
+
* Returns undefined when nothing was selected so the request field stays
|
|
682
|
+
* absent rather than becoming an empty array.
|
|
683
|
+
*/
|
|
684
|
+
async function resolveResourceScopesFromBody(
|
|
685
|
+
body: JSONObject,
|
|
686
|
+
projectId: ObjectID,
|
|
687
|
+
): Promise<Array<ResourceEntityScope> | undefined> {
|
|
688
|
+
const selections: ResourceEntityFacetSelections =
|
|
689
|
+
ResourceEntityFilter.parseSelections(
|
|
690
|
+
body["resourceFilters"] as JSONObject | undefined,
|
|
691
|
+
);
|
|
692
|
+
|
|
693
|
+
if (Object.keys(selections).length === 0) {
|
|
694
|
+
return undefined;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
const scopes: Array<ResourceEntityScope> =
|
|
698
|
+
await ResourceEntityFilter.resolveScopes({
|
|
699
|
+
projectId,
|
|
700
|
+
selections,
|
|
701
|
+
});
|
|
702
|
+
|
|
703
|
+
return scopes.length > 0 ? scopes : undefined;
|
|
704
|
+
}
|
|
705
|
+
|
|
659
706
|
/*
|
|
660
707
|
* Shared body parsing for every trace aggregation endpoint (histogram,
|
|
661
708
|
* facets, analytics). Defensive about shapes: arrays are validated and
|
|
@@ -844,7 +891,13 @@ router.post(
|
|
|
844
891
|
(body["bucketSizeInMinutes"] as number) ||
|
|
845
892
|
computeDefaultBucketSize(startTime, endTime);
|
|
846
893
|
|
|
847
|
-
const traceFilters: TraceFilters =
|
|
894
|
+
const traceFilters: TraceFilters = {
|
|
895
|
+
...parseTraceFilterBody(body),
|
|
896
|
+
resourceScopes: await resolveResourceScopesFromBody(
|
|
897
|
+
body,
|
|
898
|
+
databaseProps.tenantId,
|
|
899
|
+
),
|
|
900
|
+
};
|
|
848
901
|
|
|
849
902
|
const request: TraceHistogramRequest = {
|
|
850
903
|
projectId: databaseProps.tenantId,
|
|
@@ -904,7 +957,13 @@ router.post(
|
|
|
904
957
|
|
|
905
958
|
const limit: number = (body["limit"] as number) || 500;
|
|
906
959
|
|
|
907
|
-
const traceFilters: TraceFilters =
|
|
960
|
+
const traceFilters: TraceFilters = {
|
|
961
|
+
...parseTraceFilterBody(body),
|
|
962
|
+
resourceScopes: await resolveResourceScopesFromBody(
|
|
963
|
+
body,
|
|
964
|
+
databaseProps.tenantId,
|
|
965
|
+
),
|
|
966
|
+
};
|
|
908
967
|
|
|
909
968
|
/*
|
|
910
969
|
* Per-facet partial-match filter applied at the Postgres source-of-truth
|
|
@@ -1195,7 +1254,13 @@ router.post(
|
|
|
1195
1254
|
? Math.min(Math.max(Math.trunc(rawLimit), 1), 1000)
|
|
1196
1255
|
: undefined;
|
|
1197
1256
|
|
|
1198
|
-
const traceFilters: TraceFilters =
|
|
1257
|
+
const traceFilters: TraceFilters = {
|
|
1258
|
+
...parseTraceFilterBody(body),
|
|
1259
|
+
resourceScopes: await resolveResourceScopesFromBody(
|
|
1260
|
+
body,
|
|
1261
|
+
databaseProps.tenantId,
|
|
1262
|
+
),
|
|
1263
|
+
};
|
|
1199
1264
|
|
|
1200
1265
|
const request: TraceAnalyticsRequest = {
|
|
1201
1266
|
projectId: databaseProps.tenantId,
|
|
@@ -1800,6 +1865,9 @@ router.post(
|
|
|
1800
1865
|
? (body["limit"] as number)
|
|
1801
1866
|
: undefined;
|
|
1802
1867
|
|
|
1868
|
+
const resourceScopes: Array<ResourceEntityScope> | undefined =
|
|
1869
|
+
await resolveResourceScopesFromBody(body, databaseProps.tenantId);
|
|
1870
|
+
|
|
1803
1871
|
const request: AnalyticsRequest = {
|
|
1804
1872
|
projectId: databaseProps.tenantId,
|
|
1805
1873
|
startTime,
|
|
@@ -1810,6 +1878,7 @@ router.post(
|
|
|
1810
1878
|
aggregation,
|
|
1811
1879
|
aggregationField,
|
|
1812
1880
|
serviceIds,
|
|
1881
|
+
resourceScopes,
|
|
1813
1882
|
severityTexts,
|
|
1814
1883
|
bodySearchText,
|
|
1815
1884
|
traceIds,
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { MigrationInterface, QueryRunner } from "typeorm";
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* Status page owners can now keep their page out of Google/Bing while it stays
|
|
5
|
+
* reachable by link. Indexing stays on unless it is turned off, so the column
|
|
6
|
+
* defaults to true.
|
|
7
|
+
*
|
|
8
|
+
* ADD COLUMN ... NOT NULL DEFAULT true populates every existing row with true
|
|
9
|
+
* in the same statement, so no backfill is needed and every status page that
|
|
10
|
+
* exists today keeps being indexed exactly as it is now. That matters because
|
|
11
|
+
* the renderers treat "not false" as indexable: a NULL row would read as
|
|
12
|
+
* indexable anyway, but there is no reason to leave that to interpretation.
|
|
13
|
+
*/
|
|
14
|
+
export class AddEnableSearchEngineIndexingToStatusPage1787500000000
|
|
15
|
+
implements MigrationInterface
|
|
16
|
+
{
|
|
17
|
+
public name = "AddEnableSearchEngineIndexingToStatusPage1787500000000";
|
|
18
|
+
|
|
19
|
+
public async up(queryRunner: QueryRunner): Promise<void> {
|
|
20
|
+
await queryRunner.query(
|
|
21
|
+
`ALTER TABLE "StatusPage" ADD "enableSearchEngineIndexing" boolean NOT NULL DEFAULT true`,
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
public async down(queryRunner: QueryRunner): Promise<void> {
|
|
26
|
+
await queryRunner.query(
|
|
27
|
+
`ALTER TABLE "StatusPage" DROP COLUMN "enableSearchEngineIndexing"`,
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { MigrationInterface, QueryRunner } from "typeorm";
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* Reachability becomes the OUTCOME of the last poll instead of the age of
|
|
5
|
+
* the last success.
|
|
6
|
+
*
|
|
7
|
+
* "lastSeenAt within 15 minutes = up" could not tell "the device did not
|
|
8
|
+
* answer" apart from "the probe has not asked recently", so any fleet whose
|
|
9
|
+
* real poll cadence exceeded 15 minutes — and any device configured with a
|
|
10
|
+
* polling interval of 15 minutes or more — read as permanently down while
|
|
11
|
+
* its own interface inventory, written by those same successful walks, read
|
|
12
|
+
* as up.
|
|
13
|
+
*
|
|
14
|
+
* lastPolledAt records every ATTEMPT and isReachable records its result;
|
|
15
|
+
* lastSeenAt keeps its existing meaning (last successful walk).
|
|
16
|
+
*
|
|
17
|
+
* Backfill: an existing row with a lastSeenAt did answer the last walk we
|
|
18
|
+
* have any record of — there is no record of a failure — so it is seeded
|
|
19
|
+
* reachable, with that timestamp as its last attempt. Rows that were never
|
|
20
|
+
* seen stay NULL and read as Pending, exactly as they did before.
|
|
21
|
+
*/
|
|
22
|
+
export class AddNetworkDeviceReachabilityColumns1787600000000
|
|
23
|
+
implements MigrationInterface
|
|
24
|
+
{
|
|
25
|
+
public name = "AddNetworkDeviceReachabilityColumns1787600000000";
|
|
26
|
+
|
|
27
|
+
public async up(queryRunner: QueryRunner): Promise<void> {
|
|
28
|
+
await queryRunner.query(
|
|
29
|
+
`ALTER TABLE "NetworkDevice" ADD "lastPolledAt" TIMESTAMP WITH TIME ZONE`,
|
|
30
|
+
);
|
|
31
|
+
await queryRunner.query(
|
|
32
|
+
`ALTER TABLE "NetworkDevice" ADD "isReachable" boolean`,
|
|
33
|
+
);
|
|
34
|
+
await queryRunner.query(
|
|
35
|
+
`UPDATE "NetworkDevice" SET "lastPolledAt" = "lastSeenAt", "isReachable" = true WHERE "lastSeenAt" IS NOT NULL`,
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
public async down(queryRunner: QueryRunner): Promise<void> {
|
|
40
|
+
await queryRunner.query(
|
|
41
|
+
`ALTER TABLE "NetworkDevice" DROP COLUMN "isReachable"`,
|
|
42
|
+
);
|
|
43
|
+
await queryRunner.query(
|
|
44
|
+
`ALTER TABLE "NetworkDevice" DROP COLUMN "lastPolledAt"`,
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -528,6 +528,8 @@ import { AddAIConversationPageContext1787100000000 } from "./1787100000000-AddAI
|
|
|
528
528
|
import { AddAIChatMessageFeedback1787200000000 } from "./1787200000000-AddAIChatMessageFeedback";
|
|
529
529
|
import { AddEpisodeMemberNotifyIndexes1787300000000 } from "./1787300000000-AddEpisodeMemberNotifyIndexes";
|
|
530
530
|
import { AddDeviceRoleAndDeclaredLinkParent1787400000000 } from "./1787400000000-AddDeviceRoleAndDeclaredLinkParent";
|
|
531
|
+
import { AddEnableSearchEngineIndexingToStatusPage1787500000000 } from "./1787500000000-AddEnableSearchEngineIndexingToStatusPage";
|
|
532
|
+
import { AddNetworkDeviceReachabilityColumns1787600000000 } from "./1787600000000-AddNetworkDeviceReachabilityColumns";
|
|
531
533
|
|
|
532
534
|
export default [
|
|
533
535
|
InitialMigration,
|
|
@@ -1060,4 +1062,6 @@ export default [
|
|
|
1060
1062
|
AddAIChatMessageFeedback1787200000000,
|
|
1061
1063
|
AddEpisodeMemberNotifyIndexes1787300000000,
|
|
1062
1064
|
AddDeviceRoleAndDeclaredLinkParent1787400000000,
|
|
1065
|
+
AddEnableSearchEngineIndexingToStatusPage1787500000000,
|
|
1066
|
+
AddNetworkDeviceReachabilityColumns1787600000000,
|
|
1063
1067
|
];
|
|
@@ -449,8 +449,17 @@ export default class UserMiddleware {
|
|
|
449
449
|
globalValue,
|
|
450
450
|
null,
|
|
451
451
|
);
|
|
452
|
-
|
|
453
|
-
|
|
452
|
+
|
|
453
|
+
if (
|
|
454
|
+
!(
|
|
455
|
+
req.headers &&
|
|
456
|
+
req.headers["global-permissions-hash"] &&
|
|
457
|
+
req.headers["global-permissions-hash"] === globalPermissionsHash
|
|
458
|
+
)
|
|
459
|
+
) {
|
|
460
|
+
res.set("global-permissions", globalValue);
|
|
461
|
+
res.set("global-permissions-hash", globalPermissionsHash);
|
|
462
|
+
}
|
|
454
463
|
}
|
|
455
464
|
|
|
456
465
|
// set project permissions hash.
|
|
@@ -1,20 +1,120 @@
|
|
|
1
1
|
import CreateBy from "../Types/Database/CreateBy";
|
|
2
|
-
import
|
|
2
|
+
import DeleteBy from "../Types/Database/DeleteBy";
|
|
3
|
+
import { OnCreate, OnDelete, OnUpdate } from "../Types/Database/Hooks";
|
|
3
4
|
import UpdateBy from "../Types/Database/UpdateBy";
|
|
4
5
|
import DatabaseService from "./DatabaseService";
|
|
5
|
-
import { LIMIT_PER_PROJECT } from "../../Types/Database/LimitMax";
|
|
6
|
+
import LIMIT_MAX, { LIMIT_PER_PROJECT } from "../../Types/Database/LimitMax";
|
|
6
7
|
import BadDataException from "../../Types/Exception/BadDataException";
|
|
7
8
|
import Model from "../../Models/DatabaseModels/ApiKeyPermission";
|
|
9
|
+
import Label from "../../Models/DatabaseModels/Label";
|
|
10
|
+
import ObjectID from "../../Types/ObjectID";
|
|
11
|
+
import Permission from "../../Types/Permission";
|
|
8
12
|
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
|
13
|
+
import InMemoryTTLCache from "../Infrastructure/InMemoryTTLCache";
|
|
14
|
+
|
|
15
|
+
/*
|
|
16
|
+
* 60s is the worst-case staleness on any single API node after a key's
|
|
17
|
+
* permissions are edited. We invalidate in-process immediately on
|
|
18
|
+
* create/update/delete; this TTL is the upper bound for *other* processes
|
|
19
|
+
* (same bound the ApiKeyService key cache accepted).
|
|
20
|
+
*/
|
|
21
|
+
const PERMISSIONS_TTL_MS: number = 60 * 1000;
|
|
22
|
+
|
|
23
|
+
interface CachedApiKeyPermissionRow {
|
|
24
|
+
permission: Permission;
|
|
25
|
+
labelIds: Array<string>;
|
|
26
|
+
isBlockPermission: boolean | undefined;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface ApiKeyPermissionRow {
|
|
30
|
+
permission: Permission;
|
|
31
|
+
labelIds: Array<ObjectID>;
|
|
32
|
+
isBlockPermission: boolean | undefined;
|
|
33
|
+
}
|
|
34
|
+
|
|
9
35
|
export class Service extends DatabaseService<Model> {
|
|
36
|
+
/*
|
|
37
|
+
* Cache of `apiKeyId -> permission rows`. The API-key auth path builds the
|
|
38
|
+
* request's tenant permissions from these rows on every request; without it
|
|
39
|
+
* that's a Postgres findBy (with a labels join) per request.
|
|
40
|
+
*/
|
|
41
|
+
private permissionCache: InMemoryTTLCache<Array<CachedApiKeyPermissionRow>> =
|
|
42
|
+
new InMemoryTTLCache(10_000);
|
|
43
|
+
|
|
10
44
|
public constructor() {
|
|
11
45
|
super(Model);
|
|
12
46
|
}
|
|
13
47
|
|
|
48
|
+
public clearCache(): void {
|
|
49
|
+
this.permissionCache.clear();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Resolves the permission rows granted to an API key, with a short-lived
|
|
54
|
+
* in-process cache. Returns freshly built objects on every call (hit or
|
|
55
|
+
* miss), so callers never share mutable state with the cache. Use this from
|
|
56
|
+
* the auth hot path instead of calling `findBy` directly.
|
|
57
|
+
*/
|
|
58
|
+
@CaptureSpan()
|
|
59
|
+
public async findPermissionsByApiKeyId(
|
|
60
|
+
apiKeyId: ObjectID,
|
|
61
|
+
): Promise<Array<ApiKeyPermissionRow>> {
|
|
62
|
+
const cacheKey: string = apiKeyId.toString();
|
|
63
|
+
|
|
64
|
+
let cachedRows: Array<CachedApiKeyPermissionRow> | undefined =
|
|
65
|
+
this.permissionCache.get(cacheKey);
|
|
66
|
+
|
|
67
|
+
if (cachedRows === undefined) {
|
|
68
|
+
const rows: Array<Model> = await this.findBy({
|
|
69
|
+
query: {
|
|
70
|
+
apiKeyId: apiKeyId,
|
|
71
|
+
},
|
|
72
|
+
select: {
|
|
73
|
+
permission: true,
|
|
74
|
+
labels: {
|
|
75
|
+
_id: true,
|
|
76
|
+
},
|
|
77
|
+
isBlockPermission: true,
|
|
78
|
+
},
|
|
79
|
+
limit: LIMIT_MAX,
|
|
80
|
+
skip: 0,
|
|
81
|
+
props: {
|
|
82
|
+
isRoot: true,
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
cachedRows = rows.map((row: Model): CachedApiKeyPermissionRow => {
|
|
87
|
+
return {
|
|
88
|
+
permission: row.permission!,
|
|
89
|
+
labelIds: (row.labels ?? []).map((label: Label) => {
|
|
90
|
+
return label.id!.toString();
|
|
91
|
+
}),
|
|
92
|
+
isBlockPermission: row.isBlockPermission,
|
|
93
|
+
};
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
this.permissionCache.set(cacheKey, cachedRows, PERMISSIONS_TTL_MS);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return cachedRows.map(
|
|
100
|
+
(row: CachedApiKeyPermissionRow): ApiKeyPermissionRow => {
|
|
101
|
+
return {
|
|
102
|
+
permission: row.permission,
|
|
103
|
+
labelIds: row.labelIds.map((id: string) => {
|
|
104
|
+
return new ObjectID(id);
|
|
105
|
+
}),
|
|
106
|
+
isBlockPermission: row.isBlockPermission,
|
|
107
|
+
};
|
|
108
|
+
},
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
14
112
|
@CaptureSpan()
|
|
15
113
|
protected override async onBeforeCreate(
|
|
16
114
|
createBy: CreateBy<Model>,
|
|
17
115
|
): Promise<OnCreate<Model>> {
|
|
116
|
+
this.clearCache();
|
|
117
|
+
|
|
18
118
|
if (!createBy.data.apiKeyId) {
|
|
19
119
|
throw new BadDataException("API Key ID is required to create permission");
|
|
20
120
|
}
|
|
@@ -92,6 +192,12 @@ export class Service extends DatabaseService<Model> {
|
|
|
92
192
|
protected override async onBeforeUpdate(
|
|
93
193
|
updateBy: UpdateBy<Model>,
|
|
94
194
|
): Promise<OnUpdate<Model>> {
|
|
195
|
+
/*
|
|
196
|
+
* We don't know which keys are being updated without a query; updates
|
|
197
|
+
* are rare so clearing is cheap.
|
|
198
|
+
*/
|
|
199
|
+
this.clearCache();
|
|
200
|
+
|
|
95
201
|
if (updateBy.data.labels && updateBy.data.labels.length > 0) {
|
|
96
202
|
const existingPermissions: Array<Model> = await this.findBy({
|
|
97
203
|
query: updateBy.query,
|
|
@@ -153,6 +259,14 @@ export class Service extends DatabaseService<Model> {
|
|
|
153
259
|
|
|
154
260
|
return { updateBy, carryForward: null };
|
|
155
261
|
}
|
|
262
|
+
|
|
263
|
+
@CaptureSpan()
|
|
264
|
+
protected override async onBeforeDelete(
|
|
265
|
+
deleteBy: DeleteBy<Model>,
|
|
266
|
+
): Promise<OnDelete<Model>> {
|
|
267
|
+
this.clearCache();
|
|
268
|
+
return { deleteBy, carryForward: null };
|
|
269
|
+
}
|
|
156
270
|
}
|
|
157
271
|
|
|
158
272
|
export default new Service();
|
|
@@ -34,6 +34,7 @@ import Encryption from "../Utils/Encryption";
|
|
|
34
34
|
import PasswordHash from "../Utils/PasswordHash";
|
|
35
35
|
import PostgresErrorTranslator from "../Utils/Database/PostgresErrorTranslator";
|
|
36
36
|
import logger, { LogAttributes } from "../Utils/Logger";
|
|
37
|
+
import ConfigLogLevel from "../Types/ConfigLogLevel";
|
|
37
38
|
import BaseService from "./BaseService";
|
|
38
39
|
import BaseModel from "../../Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel";
|
|
39
40
|
import { WorkflowRoute } from "../../ServiceRoute";
|
|
@@ -2268,6 +2269,14 @@ class DatabaseService<TBaseModel extends BaseModel> extends BaseService {
|
|
|
2268
2269
|
*/
|
|
2269
2270
|
const affectedItems: Array<TBaseModel> = [];
|
|
2270
2271
|
|
|
2272
|
+
/*
|
|
2273
|
+
* The per-item debug payload below is a pretty-printed JSON.stringify
|
|
2274
|
+
* of every matched row; skip building it entirely unless the log level
|
|
2275
|
+
* is DEBUG, since logger.debug() no-ops at any other level.
|
|
2276
|
+
*/
|
|
2277
|
+
const isDebugLogEnabled: boolean =
|
|
2278
|
+
logger.getLogLevel() === ConfigLogLevel.DEBUG;
|
|
2279
|
+
|
|
2271
2280
|
for (const item of items) {
|
|
2272
2281
|
/*
|
|
2273
2282
|
* _id must be set AFTER the spread: update data can carry an
|
|
@@ -2282,12 +2291,14 @@ class DatabaseService<TBaseModel extends BaseModel> extends BaseService {
|
|
|
2282
2291
|
_id: item._id!,
|
|
2283
2292
|
} as any;
|
|
2284
2293
|
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2294
|
+
if (isDebugLogEnabled) {
|
|
2295
|
+
logger.debug("Updated Item", {
|
|
2296
|
+
projectId: updateBy.props.tenantId?.toString(),
|
|
2297
|
+
} as LogAttributes);
|
|
2298
|
+
logger.debug(JSON.stringify(updatedItem, null, 2), {
|
|
2299
|
+
projectId: updateBy.props.tenantId?.toString(),
|
|
2300
|
+
} as LogAttributes);
|
|
2301
|
+
}
|
|
2291
2302
|
|
|
2292
2303
|
if (hasRelationUpdates) {
|
|
2293
2304
|
await this.getRepository().save(updatedItem);
|
|
@@ -10,6 +10,10 @@ import AnalyticsTableName from "../../Types/AnalyticsDatabase/AnalyticsTableName
|
|
|
10
10
|
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
|
11
11
|
import { DbJSONResponse, Results } from "./AnalyticsDatabaseService";
|
|
12
12
|
import ServiceType from "../../Types/Telemetry/ServiceType";
|
|
13
|
+
import {
|
|
14
|
+
appendResourceScopeFilters,
|
|
15
|
+
ResourceEntityScope,
|
|
16
|
+
} from "../Utils/Telemetry/ResourceEntityFilter";
|
|
13
17
|
|
|
14
18
|
export interface HistogramBucket {
|
|
15
19
|
time: string;
|
|
@@ -27,6 +31,15 @@ export interface HistogramRequest {
|
|
|
27
31
|
bucketSizeInMinutes: number;
|
|
28
32
|
serviceIds?: Array<ObjectID> | undefined;
|
|
29
33
|
entityKeys?: Array<string> | undefined;
|
|
34
|
+
/*
|
|
35
|
+
* Resource-facet selections (Kubernetes cluster / host / docker host /
|
|
36
|
+
* podman host) already resolved to their entity keys. One scope per
|
|
37
|
+
* facet: the branches inside a scope OR (a row proves membership either
|
|
38
|
+
* by being primary-keyed on the resource or by carrying its entity key),
|
|
39
|
+
* and the scopes AND with each other so two facets intersect. See
|
|
40
|
+
* ResourceEntityFilter.
|
|
41
|
+
*/
|
|
42
|
+
resourceScopes?: Array<ResourceEntityScope> | undefined;
|
|
30
43
|
severityTexts?: Array<string> | undefined;
|
|
31
44
|
bodySearchText?: string | undefined;
|
|
32
45
|
traceIds?: Array<string> | undefined;
|
|
@@ -49,6 +62,15 @@ export interface FacetRequest {
|
|
|
49
62
|
limit?: number | undefined;
|
|
50
63
|
serviceIds?: Array<ObjectID> | undefined;
|
|
51
64
|
entityKeys?: Array<string> | undefined;
|
|
65
|
+
/*
|
|
66
|
+
* Resource-facet selections (Kubernetes cluster / host / docker host /
|
|
67
|
+
* podman host) already resolved to their entity keys. One scope per
|
|
68
|
+
* facet: the branches inside a scope OR (a row proves membership either
|
|
69
|
+
* by being primary-keyed on the resource or by carrying its entity key),
|
|
70
|
+
* and the scopes AND with each other so two facets intersect. See
|
|
71
|
+
* ResourceEntityFilter.
|
|
72
|
+
*/
|
|
73
|
+
resourceScopes?: Array<ResourceEntityScope> | undefined;
|
|
52
74
|
severityTexts?: Array<string> | undefined;
|
|
53
75
|
bodySearchText?: string | undefined;
|
|
54
76
|
traceIds?: Array<string> | undefined;
|
|
@@ -70,6 +92,15 @@ export interface AnalyticsRequest {
|
|
|
70
92
|
aggregation: AnalyticsAggregation;
|
|
71
93
|
aggregationField?: string | undefined;
|
|
72
94
|
serviceIds?: Array<ObjectID> | undefined;
|
|
95
|
+
/*
|
|
96
|
+
* Resource-facet selections (Kubernetes cluster / host / docker host /
|
|
97
|
+
* podman host) already resolved to their entity keys. One scope per
|
|
98
|
+
* facet: the branches inside a scope OR (a row proves membership either
|
|
99
|
+
* by being primary-keyed on the resource or by carrying its entity key),
|
|
100
|
+
* and the scopes AND with each other so two facets intersect. See
|
|
101
|
+
* ResourceEntityFilter.
|
|
102
|
+
*/
|
|
103
|
+
resourceScopes?: Array<ResourceEntityScope> | undefined;
|
|
73
104
|
severityTexts?: Array<string> | undefined;
|
|
74
105
|
bodySearchText?: string | undefined;
|
|
75
106
|
traceIds?: Array<string> | undefined;
|
|
@@ -764,6 +795,7 @@ export class LogAggregationService {
|
|
|
764
795
|
HistogramRequest,
|
|
765
796
|
| "serviceIds"
|
|
766
797
|
| "entityKeys"
|
|
798
|
+
| "resourceScopes"
|
|
767
799
|
| "severityTexts"
|
|
768
800
|
| "bodySearchText"
|
|
769
801
|
| "traceIds"
|
|
@@ -794,6 +826,8 @@ export class LogAggregationService {
|
|
|
794
826
|
);
|
|
795
827
|
}
|
|
796
828
|
|
|
829
|
+
appendResourceScopeFilters(statement, request.resourceScopes);
|
|
830
|
+
|
|
797
831
|
if (request.severityTexts && request.severityTexts.length > 0) {
|
|
798
832
|
statement.append(
|
|
799
833
|
SQL` AND severityText IN (${{
|
|
@@ -1,11 +1,32 @@
|
|
|
1
1
|
import ClickhouseDatabase from "../Infrastructure/ClickhouseDatabase";
|
|
2
2
|
import AnalyticsDatabaseService from "./AnalyticsDatabaseService";
|
|
3
3
|
import Log from "../../Models/AnalyticsModels/Log";
|
|
4
|
+
import FindBy from "../Types/AnalyticsDatabase/FindBy";
|
|
5
|
+
import { OnFind } from "../Types/AnalyticsDatabase/Hooks";
|
|
6
|
+
import ResourceEntityFilter from "../Utils/Telemetry/ResourceEntityFilter";
|
|
4
7
|
|
|
5
8
|
export class LogService extends AnalyticsDatabaseService<Log> {
|
|
6
9
|
public constructor(clickhouseDatabase?: ClickhouseDatabase | undefined) {
|
|
7
10
|
super({ modelType: Log, database: clickhouseDatabase });
|
|
8
11
|
}
|
|
12
|
+
|
|
13
|
+
/*
|
|
14
|
+
* Resolve the logs explorer's resource-facet selections (a Kubernetes
|
|
15
|
+
* cluster, a host, ...) before the query is compiled. They arrive as
|
|
16
|
+
* Postgres ids under `resourceFilters`; matching them needs the
|
|
17
|
+
* resource's entity key, which only Postgres can supply. See
|
|
18
|
+
* ResourceEntityFilter.
|
|
19
|
+
*/
|
|
20
|
+
protected override async onBeforeFind(
|
|
21
|
+
findBy: FindBy<Log>,
|
|
22
|
+
): Promise<OnFind<Log>> {
|
|
23
|
+
await ResourceEntityFilter.rewriteAnalyticsQuery({
|
|
24
|
+
query: findBy.query as unknown as Record<string, unknown>,
|
|
25
|
+
projectId: findBy.props?.tenantId,
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
return { findBy, carryForward: null };
|
|
29
|
+
}
|
|
9
30
|
}
|
|
10
31
|
|
|
11
32
|
export default new LogService();
|
|
@@ -849,7 +849,16 @@ export class Service extends DatabaseService<Model> {
|
|
|
849
849
|
select: {
|
|
850
850
|
_id: true,
|
|
851
851
|
currentMonitorStatusId: true,
|
|
852
|
+
/*
|
|
853
|
+
* All four reachability inputs. isReachable is the one that decides
|
|
854
|
+
* up/down; the other three only size the "polling has stopped
|
|
855
|
+
* entirely" backstop. Selecting lastSeenAt alone silently drops the
|
|
856
|
+
* rollup back to the old freshness rule.
|
|
857
|
+
*/
|
|
858
|
+
isReachable: true,
|
|
859
|
+
lastPolledAt: true,
|
|
852
860
|
lastSeenAt: true,
|
|
861
|
+
pollingIntervalInMinutes: true,
|
|
853
862
|
},
|
|
854
863
|
limit: LIMIT_MAX,
|
|
855
864
|
skip: 0,
|
|
@@ -908,7 +917,10 @@ export class Service extends DatabaseService<Model> {
|
|
|
908
917
|
monitorStatusPriority: statusId
|
|
909
918
|
? priorityByStatusId.get(statusId)
|
|
910
919
|
: undefined,
|
|
920
|
+
isReachable: device.isReachable,
|
|
921
|
+
lastPolledAt: device.lastPolledAt,
|
|
911
922
|
lastSeenAt: device.lastSeenAt,
|
|
923
|
+
pollingIntervalInMinutes: device.pollingIntervalInMinutes,
|
|
912
924
|
};
|
|
913
925
|
},
|
|
914
926
|
);
|
|
@@ -14,6 +14,9 @@ import SortOrder from "../../Types/BaseDatabase/SortOrder";
|
|
|
14
14
|
import InBetween from "../../Types/BaseDatabase/InBetween";
|
|
15
15
|
import Includes from "../../Types/BaseDatabase/Includes";
|
|
16
16
|
import ObjectID from "../../Types/ObjectID";
|
|
17
|
+
import FindBy from "../Types/AnalyticsDatabase/FindBy";
|
|
18
|
+
import { OnFind } from "../Types/AnalyticsDatabase/Hooks";
|
|
19
|
+
import ResourceEntityFilter from "../Utils/Telemetry/ResourceEntityFilter";
|
|
17
20
|
|
|
18
21
|
/**
|
|
19
22
|
* Columns the proj_hist_by_minute projection can answer with. If a count
|
|
@@ -35,6 +38,24 @@ export class SpanService extends AnalyticsDatabaseService<Span> {
|
|
|
35
38
|
super({ modelType: Span, database: clickhouseDatabase });
|
|
36
39
|
}
|
|
37
40
|
|
|
41
|
+
/*
|
|
42
|
+
* Resolve the traces explorer's resource-facet selections (a Kubernetes
|
|
43
|
+
* cluster, a host, ...) before the query is compiled. They arrive as
|
|
44
|
+
* Postgres ids under `resourceFilters`; matching them needs the
|
|
45
|
+
* resource's entity key, which only Postgres can supply. See
|
|
46
|
+
* ResourceEntityFilter.
|
|
47
|
+
*/
|
|
48
|
+
protected override async onBeforeFind(
|
|
49
|
+
findBy: FindBy<Span>,
|
|
50
|
+
): Promise<OnFind<Span>> {
|
|
51
|
+
await ResourceEntityFilter.rewriteAnalyticsQuery({
|
|
52
|
+
query: findBy.query as unknown as Record<string, unknown>,
|
|
53
|
+
projectId: findBy.props?.tenantId,
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
return { findBy, carryForward: null };
|
|
57
|
+
}
|
|
58
|
+
|
|
38
59
|
/**
|
|
39
60
|
* Normalize a JSON-deserialized date value to a Date instance. When a query
|
|
40
61
|
* crosses the API boundary, InBetween's startValue/endValue come back as ISO
|
|
@@ -11,6 +11,10 @@ import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
|
|
11
11
|
import { DbJSONResponse, Results } from "./AnalyticsDatabaseService";
|
|
12
12
|
import logger from "../Utils/Logger";
|
|
13
13
|
import ServiceType from "../../Types/Telemetry/ServiceType";
|
|
14
|
+
import {
|
|
15
|
+
appendResourceScopeFilters,
|
|
16
|
+
ResourceEntityScope,
|
|
17
|
+
} from "../Utils/Telemetry/ResourceEntityFilter";
|
|
14
18
|
import ResourceFacetResolver, {
|
|
15
19
|
ResolvedFacetValue,
|
|
16
20
|
ResourceFacetSpec,
|
|
@@ -33,6 +37,15 @@ export type TraceAttributeFilters = Record<string, TraceAttributeFilterValue>;
|
|
|
33
37
|
export interface TraceFilters {
|
|
34
38
|
serviceIds?: Array<ObjectID> | undefined;
|
|
35
39
|
entityKeys?: Array<string> | undefined;
|
|
40
|
+
/*
|
|
41
|
+
* Resource-facet selections (Kubernetes cluster / host / docker host /
|
|
42
|
+
* podman host) already resolved to their entity keys. One scope per
|
|
43
|
+
* facet: the branches inside a scope OR (a span proves membership either
|
|
44
|
+
* by being primary-keyed on the resource or by carrying its entity key),
|
|
45
|
+
* and the scopes AND with each other so two facets intersect. See
|
|
46
|
+
* ResourceEntityFilter.
|
|
47
|
+
*/
|
|
48
|
+
resourceScopes?: Array<ResourceEntityScope> | undefined;
|
|
36
49
|
statusCodes?: Array<number> | undefined;
|
|
37
50
|
spanKinds?: Array<string> | undefined;
|
|
38
51
|
spanNames?: Array<string> | undefined;
|
|
@@ -1008,6 +1021,8 @@ export class TraceAggregationService {
|
|
|
1008
1021
|
);
|
|
1009
1022
|
}
|
|
1010
1023
|
|
|
1024
|
+
appendResourceScopeFilters(statement, request.resourceScopes);
|
|
1025
|
+
|
|
1011
1026
|
if (request.statusCodes && request.statusCodes.length > 0) {
|
|
1012
1027
|
statement.append(
|
|
1013
1028
|
SQL` AND statusCode IN (${{
|