@oneuptime/common 12.0.11 → 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/Types/Workflow/Components/Conditions/IfElse.ts +9 -0
- package/Server/Types/Workflow/Components/JavaScript.ts +9 -0
- 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/MonitorCriteriaEvaluator.ts +12 -0
- 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/VM/VMRunner.ts +693 -874
- 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/Types/Workflow/Components/CustomCodeScriptError.test.ts +121 -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/MonitorCriteriaEvaluatorJavascriptExpression.test.ts +92 -0
- 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/VM/VMRunnerIsolation.test.ts +385 -0
- package/Tests/Server/Utils/VM/VMRunnerSsrf.test.ts +3 -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/CustomCodeMonitor/CustomCodeMonitorResponse.ts +14 -2
- 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/Types/Workflow/Components/Conditions/IfElse.js +8 -0
- package/build/dist/Server/Types/Workflow/Components/Conditions/IfElse.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/JavaScript.js +8 -0
- package/build/dist/Server/Types/Workflow/Components/JavaScript.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/MonitorCriteriaEvaluator.js +12 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.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/VM/VMRunner.js +402 -525
- package/build/dist/Server/Utils/VM/VMRunner.js.map +1 -1
- 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
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import InMemoryTTLCache from "../../../Server/Infrastructure/InMemoryTTLCache";
|
|
2
|
+
import { afterEach, describe, expect, jest, test } from "@jest/globals";
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
* Contract under test — the bounded TTL cache the telemetry ingest rule
|
|
6
|
+
* caches (log/trace pipelines, drop filters, scrub rules, metric rules)
|
|
7
|
+
* sit on. Two properties matter for those callers:
|
|
8
|
+
*
|
|
9
|
+
* 1. Expiry physically deletes: a read past the TTL must both miss AND
|
|
10
|
+
* shrink the store, or per-project entries accumulate for every tenant
|
|
11
|
+
* ever seen by a long-lived ingest process.
|
|
12
|
+
* 2. Capacity is a hard cap with oldest-write eviction, so the cache can
|
|
13
|
+
* never grow past maxEntries no matter how many distinct keys arrive.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/*
|
|
17
|
+
* Minimal structural view of a jest spy — the @jest/globals and @types/jest
|
|
18
|
+
* spy types disagree in this repo, so annotate with just the surface these
|
|
19
|
+
* tests use.
|
|
20
|
+
*/
|
|
21
|
+
type NowSpyLike = {
|
|
22
|
+
mockReturnValue: (value: number) => unknown;
|
|
23
|
+
mockRestore: () => void;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
function spyNow(value: number): NowSpyLike {
|
|
27
|
+
const spy: NowSpyLike = jest.spyOn(Date, "now") as unknown as NowSpyLike;
|
|
28
|
+
spy.mockReturnValue(value);
|
|
29
|
+
return spy;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
afterEach(() => {
|
|
33
|
+
jest.restoreAllMocks();
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
describe("InMemoryTTLCache TTL behavior", () => {
|
|
37
|
+
test("get returns the value within its TTL", () => {
|
|
38
|
+
const cache: InMemoryTTLCache<string> = new InMemoryTTLCache<string>(3);
|
|
39
|
+
|
|
40
|
+
cache.set("a", "value-a", 60_000);
|
|
41
|
+
|
|
42
|
+
expect(cache.get("a")).toBe("value-a");
|
|
43
|
+
expect(cache.has("a")).toBe(true);
|
|
44
|
+
expect(cache.size()).toBe(1);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test("get past the TTL misses AND physically deletes the entry", () => {
|
|
48
|
+
const t0: number = 1_000_000;
|
|
49
|
+
const nowSpy: NowSpyLike = spyNow(t0);
|
|
50
|
+
|
|
51
|
+
const cache: InMemoryTTLCache<string> = new InMemoryTTLCache<string>(3);
|
|
52
|
+
cache.set("a", "value-a", 1_000);
|
|
53
|
+
expect(cache.size()).toBe(1);
|
|
54
|
+
|
|
55
|
+
// Boundary: at exactly expiresAt the entry is still valid (strict >).
|
|
56
|
+
nowSpy.mockReturnValue(t0 + 1_000);
|
|
57
|
+
expect(cache.get("a")).toBe("value-a");
|
|
58
|
+
expect(cache.size()).toBe(1);
|
|
59
|
+
|
|
60
|
+
nowSpy.mockReturnValue(t0 + 1_001);
|
|
61
|
+
expect(cache.get("a")).toBeUndefined();
|
|
62
|
+
expect(cache.size()).toBe(0);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test("has() reports false for an expired entry and deletes it", () => {
|
|
66
|
+
const t0: number = 1_000_000;
|
|
67
|
+
const nowSpy: NowSpyLike = spyNow(t0);
|
|
68
|
+
|
|
69
|
+
const cache: InMemoryTTLCache<string> = new InMemoryTTLCache<string>(3);
|
|
70
|
+
cache.set("a", "value-a", 1_000);
|
|
71
|
+
|
|
72
|
+
nowSpy.mockReturnValue(t0 + 5_000);
|
|
73
|
+
expect(cache.has("a")).toBe(false);
|
|
74
|
+
expect(cache.size()).toBe(0);
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
describe("InMemoryTTLCache capacity eviction", () => {
|
|
79
|
+
test("insert beyond capacity evicts the oldest key", () => {
|
|
80
|
+
const cache: InMemoryTTLCache<string> = new InMemoryTTLCache<string>(3);
|
|
81
|
+
|
|
82
|
+
cache.set("a", "value-a", 60_000);
|
|
83
|
+
cache.set("b", "value-b", 60_000);
|
|
84
|
+
cache.set("c", "value-c", 60_000);
|
|
85
|
+
cache.set("d", "value-d", 60_000);
|
|
86
|
+
|
|
87
|
+
expect(cache.size()).toBe(3);
|
|
88
|
+
expect(cache.get("a")).toBeUndefined();
|
|
89
|
+
expect(cache.get("b")).toBe("value-b");
|
|
90
|
+
expect(cache.get("c")).toBe("value-c");
|
|
91
|
+
expect(cache.get("d")).toBe("value-d");
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test("re-setting an existing key refreshes insertion order so it is not the eviction victim", () => {
|
|
95
|
+
const cache: InMemoryTTLCache<string> = new InMemoryTTLCache<string>(3);
|
|
96
|
+
|
|
97
|
+
cache.set("a", "value-a", 60_000);
|
|
98
|
+
cache.set("b", "value-b", 60_000);
|
|
99
|
+
cache.set("c", "value-c", 60_000);
|
|
100
|
+
|
|
101
|
+
// Overwrite at capacity: no eviction, "a" moves to newest position.
|
|
102
|
+
cache.set("a", "value-a2", 60_000);
|
|
103
|
+
expect(cache.size()).toBe(3);
|
|
104
|
+
expect(cache.get("b")).toBe("value-b");
|
|
105
|
+
expect(cache.get("c")).toBe("value-c");
|
|
106
|
+
|
|
107
|
+
// Next new key evicts "b" (now the oldest), not the refreshed "a".
|
|
108
|
+
cache.set("d", "value-d", 60_000);
|
|
109
|
+
expect(cache.size()).toBe(3);
|
|
110
|
+
expect(cache.get("a")).toBe("value-a2");
|
|
111
|
+
expect(cache.get("b")).toBeUndefined();
|
|
112
|
+
expect(cache.get("c")).toBe("value-c");
|
|
113
|
+
expect(cache.get("d")).toBe("value-d");
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
describe("InMemoryTTLCache basics", () => {
|
|
118
|
+
test("values are stored by reference", () => {
|
|
119
|
+
const cache: InMemoryTTLCache<Array<string>> = new InMemoryTTLCache<
|
|
120
|
+
Array<string>
|
|
121
|
+
>(3);
|
|
122
|
+
|
|
123
|
+
const value: Array<string> = [];
|
|
124
|
+
cache.set("a", value, 60_000);
|
|
125
|
+
|
|
126
|
+
expect(cache.get("a")).toBe(value);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
test("delete removes an entry", () => {
|
|
130
|
+
const cache: InMemoryTTLCache<string> = new InMemoryTTLCache<string>(3);
|
|
131
|
+
|
|
132
|
+
cache.set("a", "value-a", 60_000);
|
|
133
|
+
cache.delete("a");
|
|
134
|
+
|
|
135
|
+
expect(cache.get("a")).toBeUndefined();
|
|
136
|
+
expect(cache.has("a")).toBe(false);
|
|
137
|
+
expect(cache.size()).toBe(0);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
test("clear empties the cache", () => {
|
|
141
|
+
const cache: InMemoryTTLCache<string> = new InMemoryTTLCache<string>(3);
|
|
142
|
+
|
|
143
|
+
cache.set("a", "value-a", 60_000);
|
|
144
|
+
cache.set("b", "value-b", 60_000);
|
|
145
|
+
cache.clear();
|
|
146
|
+
|
|
147
|
+
expect(cache.size()).toBe(0);
|
|
148
|
+
expect(cache.get("a")).toBeUndefined();
|
|
149
|
+
expect(cache.get("b")).toBeUndefined();
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
test("has returns false for a key that was never set", () => {
|
|
153
|
+
const cache: InMemoryTTLCache<string> = new InMemoryTTLCache<string>(3);
|
|
154
|
+
|
|
155
|
+
expect(cache.has("missing")).toBe(false);
|
|
156
|
+
expect(cache.get("missing")).toBeUndefined();
|
|
157
|
+
});
|
|
158
|
+
});
|
package/Tests/Server/Infrastructure/Postgres/DeviceRoleAndDeclaredLinkParentMigration.test.ts
CHANGED
|
@@ -20,16 +20,14 @@ import { AddDeviceRoleAndDeclaredLinkParent1787400000000 } from "../../../../Ser
|
|
|
20
20
|
* either would silently restate every device and every link in every
|
|
21
21
|
* project as something an operator had chosen.
|
|
22
22
|
*
|
|
23
|
-
* The second is
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
* Whoever adds the next migration should move this test's ordering block
|
|
32
|
-
* to theirs.
|
|
23
|
+
* The second is REGISTRATION: an import that never reaches the default
|
|
24
|
+
* export array leaves the migration unregistered, and it silently never
|
|
25
|
+
* runs.
|
|
26
|
+
*
|
|
27
|
+
* The ordering guard this file used to carry — "this is the newest
|
|
28
|
+
* migration, so its timestamp must sort above every other" — has moved on
|
|
29
|
+
* to AddNetworkDeviceReachabilityColumnsMigration.test.ts, which is now the
|
|
30
|
+
* newest. Whoever adds the next migration should move it again.
|
|
33
31
|
*/
|
|
34
32
|
|
|
35
33
|
const MIGRATION_PATH: string = path.join(
|
|
@@ -39,15 +37,6 @@ const MIGRATION_PATH: string = path.join(
|
|
|
39
37
|
|
|
40
38
|
const SOURCE: string = fs.readFileSync(MIGRATION_PATH, "utf8");
|
|
41
39
|
|
|
42
|
-
interface MigrationClass {
|
|
43
|
-
name: string;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function timestampOf(migrationClass: MigrationClass): number | null {
|
|
47
|
-
const match: RegExpMatchArray | null = migrationClass.name.match(/(\d+)$/);
|
|
48
|
-
return match ? Number(match[1]) : null;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
40
|
describe("the columns it adds", () => {
|
|
52
41
|
test("NetworkDevice gains deviceRole", () => {
|
|
53
42
|
expect(SOURCE).toContain(
|
|
@@ -190,50 +179,10 @@ describe("the migration runs", () => {
|
|
|
190
179
|
);
|
|
191
180
|
});
|
|
192
181
|
|
|
193
|
-
// The guard described in the header. Hand it to the next migration.
|
|
194
|
-
test("its timestamp sorts after every other registered migration", () => {
|
|
195
|
-
const ourTimestamp: number | null = timestampOf(
|
|
196
|
-
AddDeviceRoleAndDeclaredLinkParent1787400000000,
|
|
197
|
-
);
|
|
198
|
-
|
|
199
|
-
expect(ourTimestamp).not.toBeNull();
|
|
200
|
-
|
|
201
|
-
const otherTimestamps: Array<number> = [];
|
|
202
|
-
|
|
203
|
-
for (const migrationClass of SchemaMigrations) {
|
|
204
|
-
if (migrationClass === AddDeviceRoleAndDeclaredLinkParent1787400000000) {
|
|
205
|
-
continue;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
const timestamp: number | null = timestampOf(
|
|
209
|
-
migrationClass as MigrationClass,
|
|
210
|
-
);
|
|
211
|
-
|
|
212
|
-
if (timestamp !== null) {
|
|
213
|
-
otherTimestamps.push(timestamp);
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
/*
|
|
218
|
-
* Math.max() of an empty list is -Infinity, which every timestamp beats.
|
|
219
|
-
* Prove the list was actually enumerated before leaning on the maximum.
|
|
220
|
-
*/
|
|
221
|
-
expect(otherTimestamps.length).toBeGreaterThan(100);
|
|
222
|
-
|
|
223
|
-
expect(ourTimestamp).toBeGreaterThan(Math.max(...otherTimestamps));
|
|
224
|
-
});
|
|
225
|
-
|
|
226
|
-
test("it is registered last, matching that timestamp", () => {
|
|
227
|
-
expect(SchemaMigrations[SchemaMigrations.length - 1]).toBe(
|
|
228
|
-
AddDeviceRoleAndDeclaredLinkParent1787400000000,
|
|
229
|
-
);
|
|
230
|
-
});
|
|
231
|
-
|
|
232
182
|
/*
|
|
233
183
|
* ...and the timestamp in the class name is the one on disk. A class
|
|
234
184
|
* renamed without renaming its file (or two migrations landing on the
|
|
235
|
-
* same timestamp)
|
|
236
|
-
* not what actually runs.
|
|
185
|
+
* same timestamp) runs in an order nobody declared.
|
|
237
186
|
*/
|
|
238
187
|
test("exactly one file on disk carries its timestamp, and it is this one", () => {
|
|
239
188
|
const directory: string = path.dirname(MIGRATION_PATH);
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import APIKeyPermission from "../../../Models/DatabaseModels/ApiKeyPermission";
|
|
2
1
|
import GlobalConfig from "../../../Models/DatabaseModels/GlobalConfig";
|
|
3
2
|
import User from "../../../Models/DatabaseModels/User";
|
|
4
3
|
import ProjectMiddleware from "../../../Server/Middleware/ProjectAuthorization";
|
|
@@ -37,6 +36,19 @@ import {
|
|
|
37
36
|
test,
|
|
38
37
|
} from "@jest/globals";
|
|
39
38
|
|
|
39
|
+
/*
|
|
40
|
+
* PasswordHash has a known, pre-existing TS5.9 compile failure under ts-jest
|
|
41
|
+
* (Buffer vs BinaryLike) that breaks every suite whose import graph reaches
|
|
42
|
+
* it. Nothing here touches password hashing; stub the module before the
|
|
43
|
+
* service import graph drags it into compilation.
|
|
44
|
+
*/
|
|
45
|
+
jest.mock("../../../Server/Utils/PasswordHash", () => {
|
|
46
|
+
return {
|
|
47
|
+
__esModule: true,
|
|
48
|
+
default: class PasswordHashStub {},
|
|
49
|
+
};
|
|
50
|
+
});
|
|
51
|
+
|
|
40
52
|
/*
|
|
41
53
|
* ProjectAuthorization.isValidProjectIdAndApiKeyMiddleware is the single door
|
|
42
54
|
* every API-key request in the product walks through, and it had no test at all.
|
|
@@ -67,9 +79,9 @@ describe("ProjectMiddleware.isValidProjectIdAndApiKeyMiddleware", () => {
|
|
|
67
79
|
ApiKeyService,
|
|
68
80
|
"findApiKey",
|
|
69
81
|
);
|
|
70
|
-
const
|
|
82
|
+
const spyFindApiKeyPermissions: jest.SpyInstance = getJestSpyOn(
|
|
71
83
|
ApiKeyPermissionService,
|
|
72
|
-
"
|
|
84
|
+
"findPermissionsByApiKeyId",
|
|
73
85
|
);
|
|
74
86
|
const spyGlobalConfigFindOneBy: jest.SpyInstance = getJestSpyOn(
|
|
75
87
|
GlobalConfigService,
|
|
@@ -89,7 +101,7 @@ describe("ProjectMiddleware.isValidProjectIdAndApiKeyMiddleware", () => {
|
|
|
89
101
|
jest.clearAllMocks();
|
|
90
102
|
next = getJestMockFunction();
|
|
91
103
|
spyFindApiKey.mockResolvedValue(null);
|
|
92
|
-
|
|
104
|
+
spyFindApiKeyPermissions.mockResolvedValue([]);
|
|
93
105
|
spyGlobalConfigFindOneBy.mockResolvedValue(null);
|
|
94
106
|
spyUserFindOneBy.mockResolvedValue(null);
|
|
95
107
|
});
|
|
@@ -129,16 +141,17 @@ describe("ProjectMiddleware.isValidProjectIdAndApiKeyMiddleware", () => {
|
|
|
129
141
|
type MockResolvedApiKeyFunction = () => void;
|
|
130
142
|
|
|
131
143
|
const mockResolvedApiKey: MockResolvedApiKeyFunction = (): void => {
|
|
132
|
-
const apiKeyPermission: APIKeyPermission = new APIKeyPermission();
|
|
133
|
-
apiKeyPermission.permission = Permission.ProjectMember;
|
|
134
|
-
apiKeyPermission.labels = [];
|
|
135
|
-
apiKeyPermission.isBlockPermission = false;
|
|
136
|
-
|
|
137
144
|
spyFindApiKey.mockResolvedValue({
|
|
138
145
|
id: apiKeyId,
|
|
139
146
|
projectId: keyProjectId,
|
|
140
147
|
});
|
|
141
|
-
|
|
148
|
+
spyFindApiKeyPermissions.mockResolvedValue([
|
|
149
|
+
{
|
|
150
|
+
permission: Permission.ProjectMember,
|
|
151
|
+
labelIds: [],
|
|
152
|
+
isBlockPermission: false,
|
|
153
|
+
},
|
|
154
|
+
]);
|
|
142
155
|
};
|
|
143
156
|
|
|
144
157
|
test("should call next with no argument and stamp API identity, tenant and permissions on the request", async () => {
|
|
@@ -211,11 +224,7 @@ describe("ProjectMiddleware.isValidProjectIdAndApiKeyMiddleware", () => {
|
|
|
211
224
|
|
|
212
225
|
await runMiddleware(req);
|
|
213
226
|
|
|
214
|
-
expect(
|
|
215
|
-
expect.objectContaining({
|
|
216
|
-
query: { apiKeyId: apiKeyId },
|
|
217
|
-
}),
|
|
218
|
-
);
|
|
227
|
+
expect(spyFindApiKeyPermissions).toHaveBeenCalledWith(apiKeyId);
|
|
219
228
|
|
|
220
229
|
const permissions: Array<UserPermission> =
|
|
221
230
|
(req as OneUptimeRequest).userTenantAccessPermission?.[
|
|
@@ -50,6 +50,25 @@ jest.mock("../../../Server/Services/ProjectService");
|
|
|
50
50
|
jest.mock("../../../Server/Services/TeamMemberService");
|
|
51
51
|
jest.mock("../../../Types/HashedString");
|
|
52
52
|
jest.mock("../../../Types/JSONFunctions");
|
|
53
|
+
/*
|
|
54
|
+
* PasswordHash carries a pre-existing TS5.9 diagnostic that fails any suite
|
|
55
|
+
* whose runtime require graph reaches it (DatabaseService, the base class of
|
|
56
|
+
* every concrete service, imports it). Nothing password-related is under
|
|
57
|
+
* test here, so the module is replaced WITH A FACTORY — an automock would
|
|
58
|
+
* still require (and type-check) the real file.
|
|
59
|
+
*/
|
|
60
|
+
jest.mock("../../../Server/Utils/PasswordHash", () => {
|
|
61
|
+
return {
|
|
62
|
+
__esModule: true,
|
|
63
|
+
default: {
|
|
64
|
+
hash: jest.fn(),
|
|
65
|
+
verify: jest.fn(),
|
|
66
|
+
generateSalt: jest.fn(),
|
|
67
|
+
needsUpgrade: jest.fn(),
|
|
68
|
+
applyPepper: jest.fn(),
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
});
|
|
53
72
|
|
|
54
73
|
type StringOrUndefined = string | undefined;
|
|
55
74
|
|
|
@@ -409,6 +428,66 @@ describe("UserMiddleware", () => {
|
|
|
409
428
|
expect(spyGetUserGlobalAccessPermission).toHaveBeenCalledWith(userId);
|
|
410
429
|
});
|
|
411
430
|
|
|
431
|
+
test("should not set global-permissions and global-permissions-hash in the response header, when the global-permissions-hash set in the request header equals the globalPermissionsHash computed from userGlobalAccessPermission", async () => {
|
|
432
|
+
const mockedRequest: Partial<ExpressRequest> = {
|
|
433
|
+
headers: { "global-permissions-hash": hashValue },
|
|
434
|
+
};
|
|
435
|
+
|
|
436
|
+
getJestSpyOn(ProjectMiddleware, "getProjectId").mockReturnValueOnce(null);
|
|
437
|
+
getJestSpyOn(JSONFunctions, "serialize").mockReturnValueOnce({});
|
|
438
|
+
const spyGetUserGlobalAccessPermission: jest.SpyInstance = getJestSpyOn(
|
|
439
|
+
AccessTokenService,
|
|
440
|
+
"getUserGlobalAccessPermission",
|
|
441
|
+
).mockResolvedValueOnce(mockedGlobalAccessPermission);
|
|
442
|
+
|
|
443
|
+
await UserMiddleware.getUserMiddleware(
|
|
444
|
+
mockedRequest as ExpressRequest,
|
|
445
|
+
res,
|
|
446
|
+
next,
|
|
447
|
+
);
|
|
448
|
+
|
|
449
|
+
expect(res.set).not.toHaveBeenCalledWith(
|
|
450
|
+
"global-permissions",
|
|
451
|
+
expect.anything(),
|
|
452
|
+
);
|
|
453
|
+
expect(res.set).not.toHaveBeenCalledWith(
|
|
454
|
+
"global-permissions-hash",
|
|
455
|
+
expect.anything(),
|
|
456
|
+
);
|
|
457
|
+
expect(next).toHaveBeenCalled();
|
|
458
|
+
expect(spyGetUserGlobalAccessPermission).toHaveBeenCalledWith(userId);
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
test("should set global-permissions and global-permissions-hash in the response header, when the global-permissions-hash set in the request header does not equal the globalPermissionsHash computed from userGlobalAccessPermission", async () => {
|
|
462
|
+
const mockedRequest: Partial<ExpressRequest> = {
|
|
463
|
+
headers: { "global-permissions-hash": "different-hash" },
|
|
464
|
+
};
|
|
465
|
+
|
|
466
|
+
getJestSpyOn(ProjectMiddleware, "getProjectId").mockReturnValueOnce(null);
|
|
467
|
+
getJestSpyOn(JSONFunctions, "serialize").mockReturnValueOnce({});
|
|
468
|
+
const spyGetUserGlobalAccessPermission: jest.SpyInstance = getJestSpyOn(
|
|
469
|
+
AccessTokenService,
|
|
470
|
+
"getUserGlobalAccessPermission",
|
|
471
|
+
).mockResolvedValueOnce(mockedGlobalAccessPermission);
|
|
472
|
+
|
|
473
|
+
await UserMiddleware.getUserMiddleware(
|
|
474
|
+
mockedRequest as ExpressRequest,
|
|
475
|
+
res,
|
|
476
|
+
next,
|
|
477
|
+
);
|
|
478
|
+
|
|
479
|
+
expect(res.set).toHaveBeenCalledWith(
|
|
480
|
+
"global-permissions",
|
|
481
|
+
JSON.stringify({}),
|
|
482
|
+
);
|
|
483
|
+
expect(res.set).toHaveBeenCalledWith(
|
|
484
|
+
"global-permissions-hash",
|
|
485
|
+
hashValue,
|
|
486
|
+
);
|
|
487
|
+
expect(next).toHaveBeenCalled();
|
|
488
|
+
expect(spyGetUserGlobalAccessPermission).toHaveBeenCalledWith(userId);
|
|
489
|
+
});
|
|
490
|
+
|
|
412
491
|
test("should call Response.sendErrorResponse, when tenantId is passed in the header and getUserTenantAccessPermissionWithTenantId throws an exception", async () => {
|
|
413
492
|
const spyGetUserTenantAccessPermissionWithTenantId: jest.SpyInstance =
|
|
414
493
|
getJestSpyOn(
|