@oneuptime/common 12.0.0 → 12.0.2
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/Host.ts +12 -4
- package/Models/DatabaseModels/KubernetesContainer.ts +5 -4
- package/Models/DatabaseModels/KubernetesResource.ts +8 -7
- package/Models/DatabaseModels/OnCallDutyPolicyScheduleLayer.ts +12 -2
- package/Models/DatabaseModels/PodmanResource.ts +6 -6
- package/Models/DatabaseModels/ServiceLevelObjective.ts +91 -0
- package/Models/DatabaseModels/StatusPage.ts +95 -0
- package/Server/API/TeamMemberAPI.ts +85 -0
- package/Server/Infrastructure/Postgres/LocalMigrationGenerationDataSource.ts +12 -2
- package/Server/Infrastructure/Postgres/SchemaMigrations/1785900000000-AddSloMonitorLabelRule.ts +84 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1785915638551-IncreaseHostIpAddressesLength.ts +40 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1785930000000-WidenInventoryResourceNameColumns.ts +131 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1785930709405-AddStatusPageReportPeriod.ts +25 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +8 -0
- package/Server/Middleware/ProjectAuthorization.ts +66 -4
- package/Server/Services/BillingService.ts +600 -51
- package/Server/Services/CephClusterService.ts +47 -5
- package/Server/Services/CephResourceService.ts +43 -4
- package/Server/Services/CloudResourceService.ts +47 -5
- package/Server/Services/DatabaseService.ts +87 -6
- package/Server/Services/DockerHostService.ts +47 -5
- package/Server/Services/DockerResourceService.ts +62 -8
- package/Server/Services/DockerSwarmClusterService.ts +48 -5
- package/Server/Services/DockerSwarmResourceService.ts +53 -4
- package/Server/Services/HostService.ts +48 -5
- package/Server/Services/IoTDeviceService.ts +1 -17
- package/Server/Services/IoTFleetService.ts +47 -5
- package/Server/Services/KubernetesClusterService.ts +48 -5
- package/Server/Services/KubernetesContainerService.ts +50 -9
- package/Server/Services/KubernetesResourceService.ts +53 -7
- package/Server/Services/LabelService.ts +106 -1
- package/Server/Services/LlmProviderService.ts +69 -9
- package/Server/Services/MonitorService.ts +57 -0
- package/Server/Services/PodmanHostService.ts +47 -5
- package/Server/Services/PodmanResourceService.ts +62 -8
- package/Server/Services/ProjectService.ts +165 -35
- package/Server/Services/ProxmoxClusterService.ts +47 -5
- package/Server/Services/ProxmoxResourceService.ts +43 -4
- package/Server/Services/RumApplicationService.ts +48 -5
- package/Server/Services/RunnerService.ts +1 -7
- package/Server/Services/ServerlessFunctionService.ts +48 -5
- package/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.ts +426 -0
- package/Server/Services/ServiceLevelObjectiveService.ts +39 -0
- package/Server/Services/StatusPageService.ts +95 -36
- package/Server/Services/StatusPageSubscriberNotificationTemplateService.ts +18 -1
- package/Server/Services/StatusPageSubscriberService.ts +3 -0
- package/Server/Types/Database/JSONColumnQuery.ts +621 -0
- package/Server/Types/Database/Permissions/PublicPermission.ts +9 -2
- package/Server/Types/Database/QueryHelper.ts +17 -32
- package/Server/Utils/Database/TruncateColumnValue.ts +84 -0
- package/Server/Utils/Monitor/Criteria/CompareCriteria.ts +12 -0
- package/Tests/App/Dashboard/RunnerInstallInstructions.test.tsx +119 -0
- package/Tests/App/Dashboard/RunnerStatus.test.tsx +274 -0
- package/Tests/App/Dashboard/UsersTableGroupsByPerson.test.tsx +448 -0
- package/Tests/Server/API/BaseAPIApiKeyAuth.test.ts +704 -0
- package/Tests/Server/API/TeamMemberRemoveUserFromProjectAPI.test.ts +303 -0
- package/Tests/Server/Middleware/MasterAdminAuthorization.test.ts +350 -0
- package/Tests/Server/Middleware/ProjectAuthorizationApiKeyHeader.test.ts +362 -0
- package/Tests/Server/Middleware/ProjectAuthorizationApiKeyMiddleware.test.ts +687 -0
- package/Tests/Server/Services/BillingService.test.ts +78 -3
- package/Tests/Server/Services/BillingServiceChangePlanCancel.test.ts +645 -0
- package/Tests/Server/Services/BillingServiceTrialPlanChange.test.ts +1005 -0
- package/Tests/Server/Services/HookFreeWriteLengthClamp.test.ts +503 -0
- package/Tests/Server/Services/HostIpAddressesColumnWidth.test.ts +198 -0
- package/Tests/Server/Services/HostServiceUpdateLastSeen.test.ts +510 -0
- package/Tests/Server/Services/InventoryResourceNameColumnWidth.test.ts +500 -0
- package/Tests/Server/Services/InventoryUpsertSchemaParity.test.ts +586 -0
- package/Tests/Server/Services/InventoryUpsertTruncationGuard.test.ts +1242 -0
- package/Tests/Server/Services/LlmProviderUsableByProject.test.ts +293 -0
- package/Tests/Server/Services/ProjectServiceChangePlan.test.ts +650 -0
- package/Tests/Server/Services/ProjectServiceExtendTrial.test.ts +1 -0
- package/Tests/Server/Services/ResourceUpdateLastSeenLivenessFallback.test.ts +387 -0
- package/Tests/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.test.ts +770 -0
- package/Tests/Server/Services/ServiceLevelObjectiveService.test.ts +130 -0
- package/Tests/Server/Services/SloMonitorLabelRuleHooks.test.ts +352 -0
- package/Tests/Server/Services/StatusPageSubscriberReport.test.ts +29 -13
- package/Tests/Server/TestingUtils/Services/BillingServiceHelper.ts +69 -8
- package/Tests/Server/Types/Database/JSONColumnQuery.test.ts +593 -0
- package/Tests/Server/Types/Database/Permissions/PublicPermission.test.ts +495 -0
- package/Tests/Server/Types/Database/QueryUtil.test.ts +113 -0
- package/Tests/Server/Utils/AI/ToolArgsExtractors.test.ts +141 -0
- package/Tests/Server/Utils/AI/Toolbox/WidgetBuilder.test.ts +205 -0
- package/Tests/Server/Utils/Database/TruncateColumnValue.test.ts +179 -0
- package/Tests/Server/Utils/Monitor/Criteria/CompareCriteria.test.ts +897 -0
- package/Tests/Types/Events/Recurring.test.ts +157 -0
- package/Tests/Types/Runner/RunnerLiveStatus.test.ts +320 -0
- package/Tests/UI/Components/AiInvestigationSettingsCard.test.tsx +285 -0
- package/Tests/UI/Components/Detail/EntityFields.test.tsx +166 -0
- package/Tests/UI/Components/ModelTable/ModelTableGroupsProjectUsers.test.tsx +375 -0
- package/Tests/UI/Components/ModelTable/useCustomFieldColumns.test.tsx +196 -0
- package/Tests/UI/Utils/ProjectUsersModelAPI.test.ts +739 -0
- package/Tests/UI/Utils/TeamMembersByUser.test.ts +633 -0
- package/Tests/Utils/StatusPage/ReportPeriod.test.ts +218 -0
- package/Tests/Utils/Telemetry/HostIpAddresses.test.ts +291 -0
- package/Types/CustomField/CustomFieldDefinition.ts +24 -0
- package/Types/Date.ts +112 -0
- package/Types/Events/Recurring.ts +99 -1
- package/Types/Runbook/RunbookStep.ts +15 -0
- package/Types/Runner/RunnerLiveStatus.ts +114 -0
- package/Types/StatusPage/StatusPageReport.ts +11 -0
- package/Types/StatusPage/StatusPageReportPeriodType.ts +21 -0
- package/UI/Components/Detail/Detail.tsx +155 -0
- package/UI/Components/ModelTable/CustomFieldColumns.tsx +2 -6
- package/UI/Components/ModelTable/useCustomFieldColumns.ts +21 -2
- package/UI/Utils/ModelAPI/ProjectUsersModelAPI.ts +305 -0
- package/UI/Utils/TeamMembersByUser.ts +302 -0
- package/Utils/StatusPage/ReportPeriod.ts +236 -0
- package/Utils/Telemetry/HostIpAddresses.ts +98 -0
- package/build/dist/Models/DatabaseModels/Host.js +12 -4
- package/build/dist/Models/DatabaseModels/Host.js.map +1 -1
- package/build/dist/Models/DatabaseModels/KubernetesContainer.js +4 -4
- package/build/dist/Models/DatabaseModels/KubernetesContainer.js.map +1 -1
- package/build/dist/Models/DatabaseModels/KubernetesResource.js +7 -7
- package/build/dist/Models/DatabaseModels/KubernetesResource.js.map +1 -1
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleLayer.js +16 -4
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleLayer.js.map +1 -1
- package/build/dist/Models/DatabaseModels/PodmanResource.js +6 -6
- package/build/dist/Models/DatabaseModels/PodmanResource.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ServiceLevelObjective.js +87 -0
- package/build/dist/Models/DatabaseModels/ServiceLevelObjective.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPage.js +98 -0
- package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
- package/build/dist/Server/API/TeamMemberAPI.js +57 -2
- package/build/dist/Server/API/TeamMemberAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/LocalMigrationGenerationDataSource.js +11 -1
- package/build/dist/Server/Infrastructure/Postgres/LocalMigrationGenerationDataSource.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785900000000-AddSloMonitorLabelRule.js +45 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785900000000-AddSloMonitorLabelRule.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785915638551-IncreaseHostIpAddressesLength.js +31 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785915638551-IncreaseHostIpAddressesLength.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785930000000-WidenInventoryResourceNameColumns.js +90 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785930000000-WidenInventoryResourceNameColumns.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785930709405-AddStatusPageReportPeriod.js +14 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785930709405-AddStatusPageReportPeriod.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +8 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Middleware/ProjectAuthorization.js +59 -4
- package/build/dist/Server/Middleware/ProjectAuthorization.js.map +1 -1
- package/build/dist/Server/Services/BillingService.js +466 -36
- package/build/dist/Server/Services/BillingService.js.map +1 -1
- package/build/dist/Server/Services/CephClusterService.js +42 -5
- package/build/dist/Server/Services/CephClusterService.js.map +1 -1
- package/build/dist/Server/Services/CephResourceService.js +28 -3
- package/build/dist/Server/Services/CephResourceService.js.map +1 -1
- package/build/dist/Server/Services/CloudResourceService.js +42 -5
- package/build/dist/Server/Services/CloudResourceService.js.map +1 -1
- package/build/dist/Server/Services/DatabaseService.js +69 -2
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/DockerHostService.js +42 -5
- package/build/dist/Server/Services/DockerHostService.js.map +1 -1
- package/build/dist/Server/Services/DockerResourceService.js +34 -4
- package/build/dist/Server/Services/DockerResourceService.js.map +1 -1
- package/build/dist/Server/Services/DockerSwarmClusterService.js +43 -5
- package/build/dist/Server/Services/DockerSwarmClusterService.js.map +1 -1
- package/build/dist/Server/Services/DockerSwarmResourceService.js +28 -3
- package/build/dist/Server/Services/DockerSwarmResourceService.js.map +1 -1
- package/build/dist/Server/Services/HostService.js +43 -5
- package/build/dist/Server/Services/HostService.js.map +1 -1
- package/build/dist/Server/Services/IoTDeviceService.js +1 -8
- package/build/dist/Server/Services/IoTDeviceService.js.map +1 -1
- package/build/dist/Server/Services/IoTFleetService.js +42 -5
- package/build/dist/Server/Services/IoTFleetService.js.map +1 -1
- package/build/dist/Server/Services/KubernetesClusterService.js +43 -5
- package/build/dist/Server/Services/KubernetesClusterService.js.map +1 -1
- package/build/dist/Server/Services/KubernetesContainerService.js +29 -3
- package/build/dist/Server/Services/KubernetesContainerService.js.map +1 -1
- package/build/dist/Server/Services/KubernetesResourceService.js +32 -4
- package/build/dist/Server/Services/KubernetesResourceService.js.map +1 -1
- package/build/dist/Server/Services/LabelService.js +92 -0
- package/build/dist/Server/Services/LabelService.js.map +1 -1
- package/build/dist/Server/Services/LlmProviderService.js +61 -8
- package/build/dist/Server/Services/LlmProviderService.js.map +1 -1
- package/build/dist/Server/Services/MonitorService.js +47 -0
- package/build/dist/Server/Services/MonitorService.js.map +1 -1
- package/build/dist/Server/Services/PodmanHostService.js +42 -5
- package/build/dist/Server/Services/PodmanHostService.js.map +1 -1
- package/build/dist/Server/Services/PodmanResourceService.js +34 -4
- package/build/dist/Server/Services/PodmanResourceService.js.map +1 -1
- package/build/dist/Server/Services/ProjectService.js +135 -21
- package/build/dist/Server/Services/ProjectService.js.map +1 -1
- package/build/dist/Server/Services/ProxmoxClusterService.js +42 -5
- package/build/dist/Server/Services/ProxmoxClusterService.js.map +1 -1
- package/build/dist/Server/Services/ProxmoxResourceService.js +28 -3
- package/build/dist/Server/Services/ProxmoxResourceService.js.map +1 -1
- package/build/dist/Server/Services/RumApplicationService.js +43 -5
- package/build/dist/Server/Services/RumApplicationService.js.map +1 -1
- package/build/dist/Server/Services/RunnerService.js +1 -6
- package/build/dist/Server/Services/RunnerService.js.map +1 -1
- package/build/dist/Server/Services/ServerlessFunctionService.js +43 -5
- package/build/dist/Server/Services/ServerlessFunctionService.js.map +1 -1
- package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.js +347 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.js.map +1 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveService.js +33 -1
- package/build/dist/Server/Services/ServiceLevelObjectiveService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageService.js +69 -46
- package/build/dist/Server/Services/StatusPageService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateService.js +17 -1
- package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageSubscriberService.js +3 -0
- package/build/dist/Server/Services/StatusPageSubscriberService.js.map +1 -1
- package/build/dist/Server/Types/Database/JSONColumnQuery.js +402 -0
- package/build/dist/Server/Types/Database/JSONColumnQuery.js.map +1 -0
- package/build/dist/Server/Types/Database/Permissions/PublicPermission.js +9 -2
- package/build/dist/Server/Types/Database/Permissions/PublicPermission.js.map +1 -1
- package/build/dist/Server/Types/Database/QueryHelper.js +17 -27
- package/build/dist/Server/Types/Database/QueryHelper.js.map +1 -1
- package/build/dist/Server/Utils/Database/TruncateColumnValue.js +32 -0
- package/build/dist/Server/Utils/Database/TruncateColumnValue.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js +11 -0
- package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js.map +1 -1
- package/build/dist/Types/CustomField/CustomFieldDefinition.js +2 -0
- package/build/dist/Types/CustomField/CustomFieldDefinition.js.map +1 -0
- package/build/dist/Types/Date.js +68 -0
- package/build/dist/Types/Date.js.map +1 -1
- package/build/dist/Types/Events/Recurring.js +63 -0
- package/build/dist/Types/Events/Recurring.js.map +1 -1
- package/build/dist/Types/Runbook/RunbookStep.js.map +1 -1
- package/build/dist/Types/Runner/RunnerLiveStatus.js +69 -0
- package/build/dist/Types/Runner/RunnerLiveStatus.js.map +1 -0
- package/build/dist/Types/StatusPage/StatusPageReportPeriodType.js +22 -0
- package/build/dist/Types/StatusPage/StatusPageReportPeriodType.js.map +1 -0
- package/build/dist/UI/Components/Detail/Detail.js +97 -0
- package/build/dist/UI/Components/Detail/Detail.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/useCustomFieldColumns.js +18 -2
- package/build/dist/UI/Components/ModelTable/useCustomFieldColumns.js.map +1 -1
- package/build/dist/UI/Utils/ModelAPI/ProjectUsersModelAPI.js +213 -0
- package/build/dist/UI/Utils/ModelAPI/ProjectUsersModelAPI.js.map +1 -0
- package/build/dist/UI/Utils/TeamMembersByUser.js +195 -0
- package/build/dist/UI/Utils/TeamMembersByUser.js.map +1 -0
- package/build/dist/Utils/StatusPage/ReportPeriod.js +131 -0
- package/build/dist/Utils/StatusPage/ReportPeriod.js.map +1 -0
- package/build/dist/Utils/Telemetry/HostIpAddresses.js +82 -0
- package/build/dist/Utils/Telemetry/HostIpAddresses.js.map +1 -0
- package/jest.config.json +2 -0
- package/package.json +1 -1
- package/tsconfig.json +12 -1
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, jest } from "@jest/globals";
|
|
2
|
+
import { render, screen, waitFor } from "@testing-library/react";
|
|
3
|
+
import userEvent from "@testing-library/user-event";
|
|
4
|
+
import * as React from "react";
|
|
5
|
+
import Color from "../../../Types/Color";
|
|
6
|
+
import ObjectID from "../../../Types/ObjectID";
|
|
7
|
+
import Permission, { UserPermission } from "../../../Types/Permission";
|
|
8
|
+
import FieldType from "../../../UI/Components/Types/FieldType";
|
|
9
|
+
import FormFieldSchemaType from "../../../UI/Components/Forms/Types/FormFieldSchemaType";
|
|
10
|
+
import getJestMockFunction, { MockFunction } from "../../MockType";
|
|
11
|
+
|
|
12
|
+
/*
|
|
13
|
+
* The incident and alert "AI" settings pages render the investigation severity
|
|
14
|
+
* floor from a relation column. Detail.tsx had no branch for FieldType.Entity,
|
|
15
|
+
* so the whole IncidentSeverity model reached React as a child - which throws
|
|
16
|
+
* "Objects are not valid as a React child" (minified #31), trips the error
|
|
17
|
+
* boundary, and blanks the page for every project that had a floor set.
|
|
18
|
+
*
|
|
19
|
+
* This drives the real CardModelDetail -> ModelDetail -> Detail stack with the
|
|
20
|
+
* field configuration those two pages use, so the crash cannot come back, and
|
|
21
|
+
* pins the two things around it: the relation is actually asked for in the
|
|
22
|
+
* select, and the edit form is grouped into steps.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
const getItemMock: MockFunction = getJestMockFunction();
|
|
26
|
+
const getListMock: MockFunction = getJestMockFunction();
|
|
27
|
+
const createOrUpdateMock: MockFunction = getJestMockFunction();
|
|
28
|
+
|
|
29
|
+
jest.mock("../../../UI/Utils/ModelAPI/ModelAPI", () => {
|
|
30
|
+
return {
|
|
31
|
+
__esModule: true,
|
|
32
|
+
default: {
|
|
33
|
+
getItem: (...args: Array<any>) => {
|
|
34
|
+
return getItemMock(...args);
|
|
35
|
+
},
|
|
36
|
+
getList: (...args: Array<any>) => {
|
|
37
|
+
return getListMock(...args);
|
|
38
|
+
},
|
|
39
|
+
createOrUpdate: (...args: Array<any>) => {
|
|
40
|
+
return createOrUpdateMock(...args);
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
jest.mock("../../../UI/Components/Toast/ToastInit", () => {
|
|
47
|
+
return {
|
|
48
|
+
__esModule: true,
|
|
49
|
+
default: () => {
|
|
50
|
+
return null;
|
|
51
|
+
},
|
|
52
|
+
ShowToastNotification: () => {
|
|
53
|
+
return null;
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const OWNER_PERMISSIONS: Array<Permission> = [
|
|
59
|
+
Permission.Public,
|
|
60
|
+
Permission.User,
|
|
61
|
+
Permission.CurrentUser,
|
|
62
|
+
Permission.ProjectOwner,
|
|
63
|
+
];
|
|
64
|
+
|
|
65
|
+
jest.mock("../../../UI/Utils/Permission", () => {
|
|
66
|
+
return {
|
|
67
|
+
__esModule: true,
|
|
68
|
+
default: {
|
|
69
|
+
getAllPermissions: () => {
|
|
70
|
+
return OWNER_PERMISSIONS;
|
|
71
|
+
},
|
|
72
|
+
getProjectPermissions: () => {
|
|
73
|
+
return {
|
|
74
|
+
permissions: OWNER_PERMISSIONS.map((permission: Permission) => {
|
|
75
|
+
return {
|
|
76
|
+
permission,
|
|
77
|
+
labelIds: [],
|
|
78
|
+
_type: "UserPermission",
|
|
79
|
+
} as UserPermission;
|
|
80
|
+
}),
|
|
81
|
+
};
|
|
82
|
+
},
|
|
83
|
+
getGlobalPermissions: () => {
|
|
84
|
+
return { globalPermissions: OWNER_PERMISSIONS };
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
jest.mock("../../../UI/Utils/User", () => {
|
|
91
|
+
return {
|
|
92
|
+
__esModule: true,
|
|
93
|
+
default: {
|
|
94
|
+
isMasterAdmin: () => {
|
|
95
|
+
return false;
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
import CardModelDetail from "../../../UI/Components/ModelDetail/CardModelDetail";
|
|
102
|
+
import IncidentSeverity from "../../../Models/DatabaseModels/IncidentSeverity";
|
|
103
|
+
import Project from "../../../Models/DatabaseModels/Project";
|
|
104
|
+
|
|
105
|
+
// These render real components that fetch, so give the waits room on a loaded box.
|
|
106
|
+
const WAIT_TIMEOUT: number = 20000;
|
|
107
|
+
|
|
108
|
+
const PROJECT_ID: ObjectID = new ObjectID(
|
|
109
|
+
"22222222-2222-4222-8222-222222222222",
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
const SEVERITY_ID: ObjectID = new ObjectID(
|
|
113
|
+
"33333333-3333-4333-8333-333333333333",
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
type RenderCardFunction = () => void;
|
|
117
|
+
|
|
118
|
+
const renderCard: RenderCardFunction = (): void => {
|
|
119
|
+
render(
|
|
120
|
+
<CardModelDetail<Project>
|
|
121
|
+
name="Automatic Incident Investigation"
|
|
122
|
+
cardProps={{
|
|
123
|
+
title: "Automatic Incident Investigation",
|
|
124
|
+
description: "Investigate every new incident.",
|
|
125
|
+
}}
|
|
126
|
+
isEditable={true}
|
|
127
|
+
editButtonText={"Update"}
|
|
128
|
+
formSteps={[
|
|
129
|
+
{ title: "Investigation", id: "investigation" },
|
|
130
|
+
{ title: "Limits", id: "limits" },
|
|
131
|
+
{ title: "Fix Tasks", id: "fix-tasks" },
|
|
132
|
+
]}
|
|
133
|
+
formFields={[
|
|
134
|
+
{
|
|
135
|
+
field: { enableAutomaticIncidentInvestigation: true },
|
|
136
|
+
stepId: "investigation",
|
|
137
|
+
title: "Automatically Investigate Incidents",
|
|
138
|
+
required: false,
|
|
139
|
+
fieldType: FormFieldSchemaType.Toggle,
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
field: { incidentInvestigationMinimumSeverity: true },
|
|
143
|
+
stepId: "investigation",
|
|
144
|
+
title: "Minimum Severity To Investigate",
|
|
145
|
+
required: false,
|
|
146
|
+
fieldType: FormFieldSchemaType.Dropdown,
|
|
147
|
+
dropdownModal: {
|
|
148
|
+
type: IncidentSeverity,
|
|
149
|
+
labelField: "name",
|
|
150
|
+
valueField: "_id",
|
|
151
|
+
},
|
|
152
|
+
placeholder: "Every severity",
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
field: { aiMaxConcurrentInvestigations: true },
|
|
156
|
+
stepId: "limits",
|
|
157
|
+
title: "Max Concurrent Investigations",
|
|
158
|
+
required: false,
|
|
159
|
+
fieldType: FormFieldSchemaType.Number,
|
|
160
|
+
placeholder: "3",
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
field: { aiDailyFixTaskLimit: true },
|
|
164
|
+
stepId: "fix-tasks",
|
|
165
|
+
title: "Daily AI Fix Task Limit",
|
|
166
|
+
required: false,
|
|
167
|
+
fieldType: FormFieldSchemaType.Number,
|
|
168
|
+
placeholder: "25",
|
|
169
|
+
},
|
|
170
|
+
]}
|
|
171
|
+
modelDetailProps={{
|
|
172
|
+
modelType: Project,
|
|
173
|
+
id: "model-detail-project-incident-ai-settings",
|
|
174
|
+
modelId: PROJECT_ID,
|
|
175
|
+
fields: [
|
|
176
|
+
{
|
|
177
|
+
field: { enableAutomaticIncidentInvestigation: true },
|
|
178
|
+
title: "Automatically Investigate Incidents",
|
|
179
|
+
placeholder: "Disabled",
|
|
180
|
+
fieldType: FieldType.Boolean,
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
field: {
|
|
184
|
+
incidentInvestigationMinimumSeverity: {
|
|
185
|
+
name: true,
|
|
186
|
+
color: true,
|
|
187
|
+
},
|
|
188
|
+
},
|
|
189
|
+
title: "Minimum Severity To Investigate",
|
|
190
|
+
placeholder: "Every severity",
|
|
191
|
+
fieldType: FieldType.Entity,
|
|
192
|
+
},
|
|
193
|
+
],
|
|
194
|
+
}}
|
|
195
|
+
/>,
|
|
196
|
+
);
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
type LoadedProjectFunction = () => Project;
|
|
200
|
+
|
|
201
|
+
const loadedProject: LoadedProjectFunction = (): Project => {
|
|
202
|
+
const severity: IncidentSeverity = new IncidentSeverity();
|
|
203
|
+
severity.id = SEVERITY_ID;
|
|
204
|
+
severity.name = "Critical";
|
|
205
|
+
severity.color = new Color("#ef4444");
|
|
206
|
+
|
|
207
|
+
const project: Project = new Project();
|
|
208
|
+
project.id = PROJECT_ID;
|
|
209
|
+
project.enableAutomaticIncidentInvestigation = true;
|
|
210
|
+
project.incidentInvestigationMinimumSeverity = severity;
|
|
211
|
+
|
|
212
|
+
return project;
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
describe("AI investigation settings card", () => {
|
|
216
|
+
beforeEach(() => {
|
|
217
|
+
getItemMock.mockReset();
|
|
218
|
+
getListMock.mockReset();
|
|
219
|
+
createOrUpdateMock.mockReset();
|
|
220
|
+
getItemMock.mockResolvedValue(loadedProject());
|
|
221
|
+
getListMock.mockResolvedValue({ data: [], count: 0, skip: 0, limit: 10 });
|
|
222
|
+
createOrUpdateMock.mockResolvedValue({ data: {} });
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
it("shows the severity floor instead of blanking the page on a relation", async () => {
|
|
226
|
+
renderCard();
|
|
227
|
+
|
|
228
|
+
const badge: HTMLElement = await screen.findByText(
|
|
229
|
+
"Critical",
|
|
230
|
+
{},
|
|
231
|
+
{ timeout: WAIT_TIMEOUT },
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
expect(badge).toBeDefined();
|
|
235
|
+
// The error boundary text that used to replace the whole page.
|
|
236
|
+
expect(screen.queryByText(/unexpected error/i)).toBeNull();
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
it("asks the API for the relation columns the card renders", async () => {
|
|
240
|
+
renderCard();
|
|
241
|
+
|
|
242
|
+
await waitFor(
|
|
243
|
+
() => {
|
|
244
|
+
expect(getItemMock).toHaveBeenCalled();
|
|
245
|
+
},
|
|
246
|
+
{ timeout: WAIT_TIMEOUT },
|
|
247
|
+
);
|
|
248
|
+
|
|
249
|
+
const detailCall: any = (getItemMock.mock.calls as Array<Array<any>>).find(
|
|
250
|
+
(call: Array<any>) => {
|
|
251
|
+
return call[0]?.select?.["incidentInvestigationMinimumSeverity"];
|
|
252
|
+
},
|
|
253
|
+
);
|
|
254
|
+
|
|
255
|
+
expect(detailCall).toBeDefined();
|
|
256
|
+
expect(detailCall[0].select.incidentInvestigationMinimumSeverity).toEqual({
|
|
257
|
+
name: true,
|
|
258
|
+
color: true,
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
it("groups the edit form into steps", async () => {
|
|
263
|
+
renderCard();
|
|
264
|
+
|
|
265
|
+
await userEvent.click(
|
|
266
|
+
await screen.findByText("Update", {}, { timeout: WAIT_TIMEOUT }),
|
|
267
|
+
);
|
|
268
|
+
|
|
269
|
+
await waitFor(
|
|
270
|
+
() => {
|
|
271
|
+
expect(screen.getByText("Investigation")).toBeDefined();
|
|
272
|
+
},
|
|
273
|
+
{ timeout: WAIT_TIMEOUT },
|
|
274
|
+
);
|
|
275
|
+
|
|
276
|
+
expect(screen.getByText("Limits")).toBeDefined();
|
|
277
|
+
expect(screen.getByText("Fix Tasks")).toBeDefined();
|
|
278
|
+
|
|
279
|
+
/*
|
|
280
|
+
* Only the first step's fields are on screen - which is the point of
|
|
281
|
+
* steps, and proves stepId actually took rather than being ignored.
|
|
282
|
+
*/
|
|
283
|
+
expect(screen.queryByText("Daily AI Fix Task Limit")).toBeNull();
|
|
284
|
+
});
|
|
285
|
+
});
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import Detail from "../../../../UI/Components/Detail/Detail";
|
|
2
|
+
import Field from "../../../../UI/Components/Detail/Field";
|
|
3
|
+
import FieldType from "../../../../UI/Components/Types/FieldType";
|
|
4
|
+
import IncidentSeverity from "../../../../Models/DatabaseModels/IncidentSeverity";
|
|
5
|
+
import Label from "../../../../Models/DatabaseModels/Label";
|
|
6
|
+
import Color from "../../../../Types/Color";
|
|
7
|
+
import ObjectID from "../../../../Types/ObjectID";
|
|
8
|
+
import { cleanup, render } from "@testing-library/react";
|
|
9
|
+
import React from "react";
|
|
10
|
+
import { afterEach, describe, expect, test } from "@jest/globals";
|
|
11
|
+
|
|
12
|
+
interface DetailItem {
|
|
13
|
+
incidentSeverity?: IncidentSeverity | undefined;
|
|
14
|
+
labels?: Array<Label> | undefined;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const renderDetail: (
|
|
18
|
+
item: DetailItem,
|
|
19
|
+
field: Field<DetailItem>,
|
|
20
|
+
) => HTMLElement = (
|
|
21
|
+
item: DetailItem,
|
|
22
|
+
field: Field<DetailItem>,
|
|
23
|
+
): HTMLElement => {
|
|
24
|
+
const { container } = render(
|
|
25
|
+
<Detail<DetailItem>
|
|
26
|
+
item={item}
|
|
27
|
+
fields={[field]}
|
|
28
|
+
showDetailsInNumberOfColumns={1}
|
|
29
|
+
/>,
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
return container;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const buildSeverity: (name: string, color?: Color) => IncidentSeverity = (
|
|
36
|
+
name: string,
|
|
37
|
+
color?: Color,
|
|
38
|
+
): IncidentSeverity => {
|
|
39
|
+
const severity: IncidentSeverity = new IncidentSeverity();
|
|
40
|
+
severity._id = new ObjectID("severity-id").toString();
|
|
41
|
+
severity.name = name;
|
|
42
|
+
|
|
43
|
+
if (color) {
|
|
44
|
+
severity.color = color;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return severity;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const severityField: Field<DetailItem> = {
|
|
51
|
+
key: "incidentSeverity",
|
|
52
|
+
title: "Minimum Severity To Investigate",
|
|
53
|
+
fieldType: FieldType.Entity,
|
|
54
|
+
placeholder: "Every severity",
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const badgeSelector: string = '[data-dropdown-value-badge="true"]';
|
|
58
|
+
|
|
59
|
+
const requireElement: (
|
|
60
|
+
element: HTMLElement | null | undefined,
|
|
61
|
+
) => HTMLElement = (element: HTMLElement | null | undefined): HTMLElement => {
|
|
62
|
+
if (!element) {
|
|
63
|
+
throw new Error("Expected the element to be rendered");
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return element;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
afterEach(() => {
|
|
70
|
+
cleanup();
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
describe("Detail entity fields", () => {
|
|
74
|
+
test("renders a related model as a labelled badge instead of throwing", () => {
|
|
75
|
+
const container: HTMLElement = renderDetail(
|
|
76
|
+
{ incidentSeverity: buildSeverity("Critical", new Color("#ef4444")) },
|
|
77
|
+
severityField,
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
const badge: HTMLElement = requireElement(
|
|
81
|
+
container.querySelector<HTMLElement>(badgeSelector),
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
expect(badge.textContent).toContain("Critical");
|
|
85
|
+
expect(badge.style.backgroundColor).toEqual("rgb(239, 68, 68)");
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test("renders an uncolored badge when the relation has no color selected", () => {
|
|
89
|
+
const container: HTMLElement = renderDetail(
|
|
90
|
+
{ incidentSeverity: buildSeverity("Major") },
|
|
91
|
+
severityField,
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
const badge: HTMLElement = requireElement(
|
|
95
|
+
container.querySelector<HTMLElement>(badgeSelector),
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
expect(badge.textContent).toContain("Major");
|
|
99
|
+
expect(badge.classList.contains("bg-indigo-50")).toBe(true);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test("shows the placeholder when the relation is not set", () => {
|
|
103
|
+
const container: HTMLElement = renderDetail({}, severityField);
|
|
104
|
+
|
|
105
|
+
expect(container.textContent).toContain("Every severity");
|
|
106
|
+
expect(container.querySelector(badgeSelector)).toBeNull();
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
test("renders one badge per item for an entity array", () => {
|
|
110
|
+
const first: Label = new Label();
|
|
111
|
+
first.name = "Backend";
|
|
112
|
+
first.color = new Color("#22c55e");
|
|
113
|
+
|
|
114
|
+
const second: Label = new Label();
|
|
115
|
+
second.name = "Database";
|
|
116
|
+
|
|
117
|
+
const container: HTMLElement = renderDetail(
|
|
118
|
+
{ labels: [first, second] },
|
|
119
|
+
{
|
|
120
|
+
key: "labels",
|
|
121
|
+
title: "Labels",
|
|
122
|
+
fieldType: FieldType.EntityArray,
|
|
123
|
+
placeholder: "No labels",
|
|
124
|
+
},
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
const badges: Array<HTMLElement> = Array.from(
|
|
128
|
+
container.querySelectorAll<HTMLElement>(badgeSelector),
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
expect(badges).toHaveLength(2);
|
|
132
|
+
expect(requireElement(badges[0]).textContent).toContain("Backend");
|
|
133
|
+
expect(requireElement(badges[0]).style.backgroundColor).toEqual(
|
|
134
|
+
"rgb(34, 197, 94)",
|
|
135
|
+
);
|
|
136
|
+
expect(requireElement(badges[1]).textContent).toContain("Database");
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
test("shows the placeholder for an empty entity array", () => {
|
|
140
|
+
const container: HTMLElement = renderDetail(
|
|
141
|
+
{ labels: [] },
|
|
142
|
+
{
|
|
143
|
+
key: "labels",
|
|
144
|
+
title: "Labels",
|
|
145
|
+
fieldType: FieldType.EntityArray,
|
|
146
|
+
placeholder: "No labels",
|
|
147
|
+
},
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
expect(container.textContent).toContain("No labels");
|
|
151
|
+
expect(container.querySelector(badgeSelector)).toBeNull();
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
test("falls back to a label when a relation is declared without a field type", () => {
|
|
155
|
+
const container: HTMLElement = renderDetail(
|
|
156
|
+
{ incidentSeverity: buildSeverity("Critical") },
|
|
157
|
+
{
|
|
158
|
+
key: "incidentSeverity",
|
|
159
|
+
title: "Minimum Severity To Investigate",
|
|
160
|
+
placeholder: "Every severity",
|
|
161
|
+
},
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
expect(container.textContent).toContain("Critical");
|
|
165
|
+
});
|
|
166
|
+
});
|