@oneuptime/common 11.5.12 → 11.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (193) hide show
  1. package/Models/DatabaseModels/AIInsight.ts +30 -0
  2. package/Models/DatabaseModels/EnterpriseLicense.ts +21 -0
  3. package/Models/DatabaseModels/EnterpriseLicenseInstance.ts +25 -0
  4. package/Models/DatabaseModels/GlobalConfig.ts +76 -0
  5. package/Models/DatabaseModels/Project.ts +30 -0
  6. package/Models/DatabaseModels/TelemetryException.ts +128 -0
  7. package/Server/API/AIAgentDataAPI.ts +129 -5
  8. package/Server/API/AIInvestigationAPI.ts +87 -0
  9. package/Server/API/EnterpriseLicenseAPI.ts +57 -0
  10. package/Server/API/GlobalConfigAPI.ts +62 -1
  11. package/Server/EnvironmentConfig.ts +22 -0
  12. package/Server/Infrastructure/Postgres/SchemaMigrations/1784640000000-AddExceptionTriageAndPrivacyColumns.ts +56 -0
  13. package/Server/Infrastructure/Postgres/SchemaMigrations/1784659816363-AddInstanceVersionAndLatestReleaseColumns.ts +38 -0
  14. package/Server/Infrastructure/Postgres/SchemaMigrations/1784705487674-AddEnterpriseLicenseEvaluationColumns.ts +25 -0
  15. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
  16. package/Server/Services/AIRunService.ts +8 -0
  17. package/Server/Services/AnalyticsDatabaseService.ts +48 -45
  18. package/Server/Services/DatabaseService.ts +66 -1
  19. package/Server/Services/TelemetryExceptionService.ts +320 -24
  20. package/Server/Services/WorkflowService.ts +100 -39
  21. package/Server/Types/Workflow/Components/Schedule.ts +57 -32
  22. package/Server/Utils/AI/SRE/Insights/Detectors/ExceptionSpikeDetector.ts +15 -4
  23. package/Server/Utils/AI/SRE/Insights/Detectors/NewExceptionDetector.ts +15 -4
  24. package/Server/Utils/AI/SRE/Insights/InsightScanner.ts +222 -24
  25. package/Server/Utils/AI/SRE/Insights/InsightTriageRunner.ts +225 -9
  26. package/Server/Utils/AI/SRE/SubjectCodeFixRun.ts +39 -0
  27. package/Server/Utils/AI/SRE/TelemetryImprovementTaskTrigger.ts +114 -0
  28. package/Server/Utils/AnalyticsDatabase/InsertDedupContext.ts +59 -0
  29. package/Server/Utils/Express.ts +13 -0
  30. package/Server/Utils/Telemetry/ExceptionSanitizer.ts +222 -0
  31. package/Server/Utils/Telemetry/TelemetryFanInWriter.ts +799 -0
  32. package/Server/Utils/Telemetry/TelemetryWriterClient.ts +263 -0
  33. package/Server/Utils/Telemetry/TelemetryWriterServer.ts +240 -0
  34. package/Server/Utils/Telemetry/TelemetryWriterShedMetrics.ts +93 -0
  35. package/Tests/Server/Services/DatabaseServiceSanitizeUpdateData.test.ts +232 -0
  36. package/Tests/Server/Services/TelemetryExceptionCodeFixRun.test.ts +205 -1
  37. package/Tests/Server/Services/UpdateOneByIdKeepsRowId.test.ts +107 -0
  38. package/Tests/Server/Services/WorkflowService.test.ts +308 -0
  39. package/Tests/Server/Utils/AI/Insights/InsightScanner.test.ts +150 -141
  40. package/Tests/Server/Utils/AI/Insights/InsightTriageRunner.test.ts +277 -1
  41. package/Tests/Server/Utils/Attribution.test.ts +203 -0
  42. package/Tests/Server/Utils/ExceptionSanitizer.test.ts +70 -0
  43. package/Tests/Server/Utils/InsightTriageClassification.test.ts +57 -0
  44. package/Tests/Server/Utils/Marketing/ConversionUploadProvider.test.ts +183 -0
  45. package/Tests/Server/Utils/Marketing/GoogleAds.test.ts +340 -0
  46. package/Tests/Server/Utils/Monitor/Criteria/ExceptionMonitorCriteria.test.ts +87 -0
  47. package/Tests/Server/Utils/Monitor/Criteria/LogMonitorCriteria.test.ts +165 -0
  48. package/Tests/Server/Utils/Monitor/Criteria/TraceMonitorCriteria.test.ts +86 -0
  49. package/Tests/Server/Utils/Telemetry/TelemetryFanInWriter.test.ts +1565 -0
  50. package/Tests/Server/Utils/Telemetry/TelemetryWriterClient.test.ts +313 -0
  51. package/Tests/Server/Utils/Telemetry/TelemetryWriterServer.test.ts +270 -0
  52. package/Tests/Server/Utils/Telemetry/TelemetryWriterShedMetrics.test.ts +127 -0
  53. package/Tests/Server/Utils/TelemetryImprovementTaskTrigger.test.ts +141 -0
  54. package/Tests/Types/AI/CodeFixTaskType.test.ts +3 -0
  55. package/Tests/Types/Monitor/MonitorCriteriaMetricVariables.test.ts +228 -0
  56. package/Tests/Types/Monitor/MonitorStepDefaultTelemetryConfig.test.ts +169 -0
  57. package/Tests/Types/Monitor/MonitorStepExceptionMonitor.test.ts +289 -0
  58. package/Tests/Types/Monitor/MonitorStepLogMonitor.test.ts +231 -0
  59. package/Tests/Types/Monitor/MonitorStepMetricViewConfigUtil.test.ts +245 -0
  60. package/Tests/Types/Monitor/MonitorStepNetworkDeviceMonitor.test.ts +155 -0
  61. package/Tests/Types/Monitor/MonitorStepTelemetrySerialization.test.ts +141 -0
  62. package/Tests/Types/Monitor/MonitorStepTraceMonitor.test.ts +137 -0
  63. package/Tests/Types/Workspace/NotificationRules/NotificationRuleCondition.test.ts +311 -0
  64. package/Tests/UI/Components/ModelTableSelectFromColumns.test.ts +259 -0
  65. package/Tests/UI/Utils/Breadcrumb/BreadcrumbTrailResolver.test.ts +461 -0
  66. package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +2852 -0
  67. package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +758 -0
  68. package/Tests/UI/Utils/NotificationMethodUtil.test.ts +564 -0
  69. package/Tests/Utils/Array.test.ts +156 -0
  70. package/Tests/Utils/CronTab.test.ts +199 -0
  71. package/Tests/Utils/ModelImportExport.test.ts +101 -0
  72. package/Tests/Utils/Number.test.ts +179 -0
  73. package/Tests/Utils/VersionUtil.test.ts +348 -0
  74. package/Tests/jest.setup.ts +1 -0
  75. package/Types/AI/CodeFixTaskContext.ts +6 -0
  76. package/Types/AI/CodeFixTaskType.ts +38 -1
  77. package/Types/AI/ExceptionAIClassification.ts +27 -0
  78. package/Types/EnterpriseLicense/EnterpriseLicenseInstanceSummary.ts +6 -0
  79. package/Types/Log/LogScrubPatternType.ts +7 -0
  80. package/Types/Monitor/MonitorStep.ts +24 -4
  81. package/Types/Monitor/MonitorStepMetricViewConfigUtil.ts +105 -0
  82. package/Types/Trace/TraceScrubPatternType.ts +7 -0
  83. package/UI/Components/EditionLabel/EditionLabel.tsx +445 -12
  84. package/UI/Components/ModelTable/BaseModelTable.tsx +15 -46
  85. package/UI/Components/ModelTable/SelectFromColumns.ts +129 -0
  86. package/UI/Components/Workflow/ArgumentsForm.tsx +68 -30
  87. package/UI/Components/Workflow/CronScheduleField.tsx +411 -0
  88. package/UI/Utils/Breadcrumb/BreadcrumbTrailResolver.ts +176 -0
  89. package/UI/Utils/NotificationMethodUtil.ts +310 -0
  90. package/Utils/CronTab.ts +823 -0
  91. package/Utils/ModelImportExport.ts +21 -6
  92. package/Utils/VersionUtil.ts +260 -0
  93. package/build/dist/Models/DatabaseModels/AIInsight.js +31 -0
  94. package/build/dist/Models/DatabaseModels/AIInsight.js.map +1 -1
  95. package/build/dist/Models/DatabaseModels/EnterpriseLicense.js +22 -0
  96. package/build/dist/Models/DatabaseModels/EnterpriseLicense.js.map +1 -1
  97. package/build/dist/Models/DatabaseModels/EnterpriseLicenseInstance.js +26 -0
  98. package/build/dist/Models/DatabaseModels/EnterpriseLicenseInstance.js.map +1 -1
  99. package/build/dist/Models/DatabaseModels/GlobalConfig.js +81 -0
  100. package/build/dist/Models/DatabaseModels/GlobalConfig.js.map +1 -1
  101. package/build/dist/Models/DatabaseModels/Project.js +31 -0
  102. package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
  103. package/build/dist/Models/DatabaseModels/TelemetryException.js +134 -0
  104. package/build/dist/Models/DatabaseModels/TelemetryException.js.map +1 -1
  105. package/build/dist/Server/API/AIAgentDataAPI.js +97 -8
  106. package/build/dist/Server/API/AIAgentDataAPI.js.map +1 -1
  107. package/build/dist/Server/API/AIInvestigationAPI.js +55 -0
  108. package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
  109. package/build/dist/Server/API/EnterpriseLicenseAPI.js +47 -0
  110. package/build/dist/Server/API/EnterpriseLicenseAPI.js.map +1 -1
  111. package/build/dist/Server/API/GlobalConfigAPI.js +52 -1
  112. package/build/dist/Server/API/GlobalConfigAPI.js.map +1 -1
  113. package/build/dist/Server/EnvironmentConfig.js +17 -0
  114. package/build/dist/Server/EnvironmentConfig.js.map +1 -1
  115. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784640000000-AddExceptionTriageAndPrivacyColumns.js +33 -0
  116. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784640000000-AddExceptionTriageAndPrivacyColumns.js.map +1 -0
  117. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784659816363-AddInstanceVersionAndLatestReleaseColumns.js +18 -0
  118. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784659816363-AddInstanceVersionAndLatestReleaseColumns.js.map +1 -0
  119. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784705487674-AddEnterpriseLicenseEvaluationColumns.js +14 -0
  120. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784705487674-AddEnterpriseLicenseEvaluationColumns.js.map +1 -0
  121. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -0
  122. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  123. package/build/dist/Server/Services/AIRunService.js +9 -2
  124. package/build/dist/Server/Services/AIRunService.js.map +1 -1
  125. package/build/dist/Server/Services/AnalyticsDatabaseService.js +44 -16
  126. package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
  127. package/build/dist/Server/Services/DatabaseService.js +53 -1
  128. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  129. package/build/dist/Server/Services/TelemetryExceptionService.js +263 -22
  130. package/build/dist/Server/Services/TelemetryExceptionService.js.map +1 -1
  131. package/build/dist/Server/Services/WorkflowService.js +80 -30
  132. package/build/dist/Server/Services/WorkflowService.js.map +1 -1
  133. package/build/dist/Server/Types/Workflow/Components/Schedule.js +44 -19
  134. package/build/dist/Server/Types/Workflow/Components/Schedule.js.map +1 -1
  135. package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/ExceptionSpikeDetector.js +12 -4
  136. package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/ExceptionSpikeDetector.js.map +1 -1
  137. package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/NewExceptionDetector.js +12 -4
  138. package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/NewExceptionDetector.js.map +1 -1
  139. package/build/dist/Server/Utils/AI/SRE/Insights/InsightScanner.js +193 -22
  140. package/build/dist/Server/Utils/AI/SRE/Insights/InsightScanner.js.map +1 -1
  141. package/build/dist/Server/Utils/AI/SRE/Insights/InsightTriageRunner.js +178 -9
  142. package/build/dist/Server/Utils/AI/SRE/Insights/InsightTriageRunner.js.map +1 -1
  143. package/build/dist/Server/Utils/AI/SRE/SubjectCodeFixRun.js +37 -0
  144. package/build/dist/Server/Utils/AI/SRE/SubjectCodeFixRun.js.map +1 -1
  145. package/build/dist/Server/Utils/AI/SRE/TelemetryImprovementTaskTrigger.js +98 -0
  146. package/build/dist/Server/Utils/AI/SRE/TelemetryImprovementTaskTrigger.js.map +1 -0
  147. package/build/dist/Server/Utils/AnalyticsDatabase/InsertDedupContext.js +24 -0
  148. package/build/dist/Server/Utils/AnalyticsDatabase/InsertDedupContext.js.map +1 -0
  149. package/build/dist/Server/Utils/Express.js +12 -0
  150. package/build/dist/Server/Utils/Express.js.map +1 -1
  151. package/build/dist/Server/Utils/Telemetry/ExceptionSanitizer.js +149 -0
  152. package/build/dist/Server/Utils/Telemetry/ExceptionSanitizer.js.map +1 -0
  153. package/build/dist/Server/Utils/Telemetry/TelemetryFanInWriter.js +496 -0
  154. package/build/dist/Server/Utils/Telemetry/TelemetryFanInWriter.js.map +1 -0
  155. package/build/dist/Server/Utils/Telemetry/TelemetryWriterClient.js +165 -0
  156. package/build/dist/Server/Utils/Telemetry/TelemetryWriterClient.js.map +1 -0
  157. package/build/dist/Server/Utils/Telemetry/TelemetryWriterServer.js +136 -0
  158. package/build/dist/Server/Utils/Telemetry/TelemetryWriterServer.js.map +1 -0
  159. package/build/dist/Server/Utils/Telemetry/TelemetryWriterShedMetrics.js +76 -0
  160. package/build/dist/Server/Utils/Telemetry/TelemetryWriterShedMetrics.js.map +1 -0
  161. package/build/dist/Types/AI/CodeFixTaskType.js +38 -1
  162. package/build/dist/Types/AI/CodeFixTaskType.js.map +1 -1
  163. package/build/dist/Types/AI/ExceptionAIClassification.js +28 -0
  164. package/build/dist/Types/AI/ExceptionAIClassification.js.map +1 -0
  165. package/build/dist/Types/Log/LogScrubPatternType.js +7 -0
  166. package/build/dist/Types/Log/LogScrubPatternType.js.map +1 -1
  167. package/build/dist/Types/Monitor/MonitorStep.js +20 -4
  168. package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
  169. package/build/dist/Types/Monitor/MonitorStepMetricViewConfigUtil.js +73 -0
  170. package/build/dist/Types/Monitor/MonitorStepMetricViewConfigUtil.js.map +1 -0
  171. package/build/dist/Types/Trace/TraceScrubPatternType.js +7 -0
  172. package/build/dist/Types/Trace/TraceScrubPatternType.js.map +1 -1
  173. package/build/dist/UI/Components/EditionLabel/EditionLabel.js +261 -13
  174. package/build/dist/UI/Components/EditionLabel/EditionLabel.js.map +1 -1
  175. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +12 -37
  176. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  177. package/build/dist/UI/Components/ModelTable/SelectFromColumns.js +82 -0
  178. package/build/dist/UI/Components/ModelTable/SelectFromColumns.js.map +1 -0
  179. package/build/dist/UI/Components/Workflow/ArgumentsForm.js +30 -6
  180. package/build/dist/UI/Components/Workflow/ArgumentsForm.js.map +1 -1
  181. package/build/dist/UI/Components/Workflow/CronScheduleField.js +209 -0
  182. package/build/dist/UI/Components/Workflow/CronScheduleField.js.map +1 -0
  183. package/build/dist/UI/Utils/Breadcrumb/BreadcrumbTrailResolver.js +129 -0
  184. package/build/dist/UI/Utils/Breadcrumb/BreadcrumbTrailResolver.js.map +1 -0
  185. package/build/dist/UI/Utils/NotificationMethodUtil.js +189 -0
  186. package/build/dist/UI/Utils/NotificationMethodUtil.js.map +1 -0
  187. package/build/dist/Utils/CronTab.js +609 -0
  188. package/build/dist/Utils/CronTab.js.map +1 -0
  189. package/build/dist/Utils/ModelImportExport.js +20 -6
  190. package/build/dist/Utils/ModelImportExport.js.map +1 -1
  191. package/build/dist/Utils/VersionUtil.js +193 -0
  192. package/build/dist/Utils/VersionUtil.js.map +1 -0
  193. package/package.json +1 -1
@@ -0,0 +1,308 @@
1
+ /**
2
+ * WorkflowService trigger denormalization tests.
3
+ *
4
+ * The runner never parses workflow graphs. It finds workflows by the
5
+ * triggerId / triggerArguments columns, which are derived from the graph and
6
+ * written onto the row. Anything that puts a graph on a workflow without
7
+ * populating those columns produces a workflow that looks correct in the
8
+ * builder and silently never fires.
9
+ *
10
+ * That is exactly what creating a workflow with a graph already attached does
11
+ * - importing a JSON export, or duplicating an existing workflow - so the
12
+ * create path has to denormalize the trigger just like the update path does.
13
+ *
14
+ * updateOneById and the outbound call to the workflow service are both spied
15
+ * on, so no database or network is touched.
16
+ */
17
+
18
+ import WorkflowService from "../../../Server/Services/WorkflowService";
19
+ import Workflow from "../../../Models/DatabaseModels/Workflow";
20
+ import { JSONObject } from "../../../Types/JSON";
21
+ import ObjectID from "../../../Types/ObjectID";
22
+ import API from "../../../Utils/API";
23
+ import { afterEach, describe, expect, test, jest } from "@jest/globals";
24
+
25
+ const WORKFLOW_ID: string = "550e8400-e29b-41d4-a716-446655440000";
26
+
27
+ function scheduleGraph(): JSONObject {
28
+ return {
29
+ nodes: [
30
+ {
31
+ id: "node-1",
32
+ data: {
33
+ metadataId: "Schedule",
34
+ componentType: "Trigger",
35
+ nodeType: "Node",
36
+ arguments: { "schedule-cron": "0 0 * * *" },
37
+ },
38
+ },
39
+ {
40
+ id: "node-2",
41
+ data: {
42
+ metadataId: "Webhook",
43
+ componentType: "Component",
44
+ nodeType: "Node",
45
+ arguments: {},
46
+ },
47
+ },
48
+ ],
49
+ edges: [],
50
+ };
51
+ }
52
+
53
+ function createdWorkflow(graph: JSONObject | undefined): Workflow {
54
+ /*
55
+ * webhookSecretKey is set so onCreateSuccess skips its own key-generation
56
+ * update, leaving updateOneById calls attributable to trigger derivation.
57
+ * projectId is left undefined so the fire-and-forget label/owner rule
58
+ * engines are skipped.
59
+ */
60
+ return {
61
+ _id: WORKFLOW_ID,
62
+ id: new ObjectID(WORKFLOW_ID),
63
+ graph: graph,
64
+ webhookSecretKey: "already-set",
65
+ } as unknown as Workflow;
66
+ }
67
+
68
+ function spyOnUpdateOneById(): jest.SpyInstance {
69
+ return jest
70
+ .spyOn(WorkflowService, "updateOneById")
71
+ .mockResolvedValue(undefined as never) as unknown as jest.SpyInstance;
72
+ }
73
+
74
+ function spyOnApiPost(): jest.SpyInstance {
75
+ return jest
76
+ .spyOn(API, "post")
77
+ .mockResolvedValue({} as never) as unknown as jest.SpyInstance;
78
+ }
79
+
80
+ type OnCreateSuccessFunction = (
81
+ onCreate: unknown,
82
+ createdItem: Workflow,
83
+ ) => Promise<Workflow>;
84
+
85
+ function onCreateSuccess(createdItem: Workflow): Promise<Workflow> {
86
+ const hook: OnCreateSuccessFunction = (
87
+ WorkflowService as unknown as { onCreateSuccess: OnCreateSuccessFunction }
88
+ ).onCreateSuccess.bind(WorkflowService);
89
+
90
+ return hook({ createBy: { data: createdItem }, carryForward: null }, {
91
+ ...createdItem,
92
+ } as Workflow);
93
+ }
94
+
95
+ type OnUpdateSuccessFunction = (
96
+ onUpdate: unknown,
97
+ updatedItemIds: Array<ObjectID>,
98
+ ) => Promise<unknown>;
99
+
100
+ function onUpdateSuccess(graph: JSONObject | undefined): Promise<unknown> {
101
+ const hook: OnUpdateSuccessFunction = (
102
+ WorkflowService as unknown as { onUpdateSuccess: OnUpdateSuccessFunction }
103
+ ).onUpdateSuccess.bind(WorkflowService);
104
+
105
+ return hook(
106
+ {
107
+ updateBy: {
108
+ query: { _id: WORKFLOW_ID },
109
+ data: graph === undefined ? {} : { graph: graph },
110
+ },
111
+ },
112
+ [new ObjectID(WORKFLOW_ID)],
113
+ );
114
+ }
115
+
116
+ describe("WorkflowService trigger denormalization on create", () => {
117
+ afterEach(() => {
118
+ jest.restoreAllMocks();
119
+ });
120
+
121
+ test("writes the trigger from the graph when a workflow is created with one", async () => {
122
+ const updateSpy: jest.SpyInstance = spyOnUpdateOneById();
123
+ spyOnApiPost();
124
+
125
+ await onCreateSuccess(createdWorkflow(scheduleGraph()));
126
+
127
+ expect(updateSpy).toHaveBeenCalledTimes(1);
128
+
129
+ const updateArgs: JSONObject = updateSpy.mock.calls[0]![0] as JSONObject;
130
+ const data: JSONObject = updateArgs["data"] as JSONObject;
131
+
132
+ expect(data["triggerId"]).toBe("Schedule");
133
+ expect(data["triggerArguments"]).toEqual({ "schedule-cron": "0 0 * * *" });
134
+
135
+ // must not recurse back through the hooks it is running inside of.
136
+ expect(updateArgs["props"]).toEqual({ isRoot: true, ignoreHooks: true });
137
+ });
138
+
139
+ test("notifies the workflow service so schedule triggers get registered", async () => {
140
+ spyOnUpdateOneById();
141
+ const postSpy: jest.SpyInstance = spyOnApiPost();
142
+
143
+ await onCreateSuccess(createdWorkflow(scheduleGraph()));
144
+
145
+ expect(postSpy).toHaveBeenCalledTimes(1);
146
+
147
+ const postArgs: JSONObject = postSpy.mock.calls[0]![0] as JSONObject;
148
+
149
+ expect(postArgs["url"]!.toString()).toContain(
150
+ `/workflow/update/${WORKFLOW_ID}`,
151
+ );
152
+ });
153
+
154
+ test("clears the trigger when the graph has no trigger node", async () => {
155
+ const updateSpy: jest.SpyInstance = spyOnUpdateOneById();
156
+ spyOnApiPost();
157
+
158
+ await onCreateSuccess(
159
+ createdWorkflow({
160
+ nodes: [
161
+ {
162
+ id: "node-1",
163
+ data: {
164
+ metadataId: "Webhook",
165
+ componentType: "Component",
166
+ nodeType: "Node",
167
+ arguments: {},
168
+ },
169
+ },
170
+ ],
171
+ edges: [],
172
+ }),
173
+ );
174
+
175
+ const data: JSONObject = (updateSpy.mock.calls[0]![0] as JSONObject)[
176
+ "data"
177
+ ] as JSONObject;
178
+
179
+ expect(data["triggerId"]).toBeNull();
180
+ expect(data["triggerArguments"]).toEqual({});
181
+ });
182
+
183
+ test("does no trigger work for a workflow created without a graph", async () => {
184
+ const updateSpy: jest.SpyInstance = spyOnUpdateOneById();
185
+ const postSpy: jest.SpyInstance = spyOnApiPost();
186
+
187
+ await onCreateSuccess(createdWorkflow(undefined));
188
+
189
+ expect(updateSpy).not.toHaveBeenCalled();
190
+ expect(postSpy).not.toHaveBeenCalled();
191
+ });
192
+
193
+ test("does not fail the create when the workflow service is unreachable", async () => {
194
+ const updateSpy: jest.SpyInstance = spyOnUpdateOneById();
195
+
196
+ jest
197
+ .spyOn(API, "post")
198
+ .mockRejectedValue(new Error("connect ECONNREFUSED") as never);
199
+
200
+ /*
201
+ * The row and its trigger are already persisted at this point, so a
202
+ * workflow service outage must not turn a successful import into an error.
203
+ */
204
+ await expect(
205
+ onCreateSuccess(createdWorkflow(scheduleGraph())),
206
+ ).resolves.toBeDefined();
207
+
208
+ expect(updateSpy).toHaveBeenCalledTimes(1);
209
+ });
210
+
211
+ test("derives the trigger for a genuinely imported workflow, which has no webhook secret yet", async () => {
212
+ const updateSpy: jest.SpyInstance = spyOnUpdateOneById();
213
+ spyOnApiPost();
214
+
215
+ /*
216
+ * webhookSecretKey carries create: [] access control, so it is stripped
217
+ * from export files and an imported workflow reaches this hook without
218
+ * one. That means two updates run in sequence - key generation, then
219
+ * trigger derivation - and the trigger write must still happen.
220
+ */
221
+ const imported: Workflow = {
222
+ _id: WORKFLOW_ID,
223
+ id: new ObjectID(WORKFLOW_ID),
224
+ graph: scheduleGraph(),
225
+ } as unknown as Workflow;
226
+
227
+ await onCreateSuccess(imported);
228
+
229
+ expect(updateSpy).toHaveBeenCalledTimes(2);
230
+
231
+ const secretUpdate: JSONObject = (
232
+ updateSpy.mock.calls[0]![0] as JSONObject
233
+ )["data"] as JSONObject;
234
+ const triggerUpdate: JSONObject = (
235
+ updateSpy.mock.calls[1]![0] as JSONObject
236
+ )["data"] as JSONObject;
237
+
238
+ expect(secretUpdate["webhookSecretKey"]).toBeDefined();
239
+ expect(triggerUpdate["triggerId"]).toBe("Schedule");
240
+ });
241
+
242
+ test("writes no trigger for a graph that carries no nodes key", async () => {
243
+ const updateSpy: jest.SpyInstance = spyOnUpdateOneById();
244
+ spyOnApiPost();
245
+
246
+ await onCreateSuccess(createdWorkflow({ edges: [] }));
247
+
248
+ expect(updateSpy).not.toHaveBeenCalled();
249
+ });
250
+ });
251
+
252
+ describe("WorkflowService trigger denormalization on update", () => {
253
+ afterEach(() => {
254
+ jest.restoreAllMocks();
255
+ });
256
+
257
+ test("writes the trigger when a saved graph carries one", async () => {
258
+ const updateSpy: jest.SpyInstance = spyOnUpdateOneById();
259
+ spyOnApiPost();
260
+
261
+ await onUpdateSuccess(scheduleGraph());
262
+
263
+ expect(updateSpy).toHaveBeenCalledTimes(1);
264
+
265
+ const data: JSONObject = (updateSpy.mock.calls[0]![0] as JSONObject)[
266
+ "data"
267
+ ] as JSONObject;
268
+
269
+ expect(data["triggerId"]).toBe("Schedule");
270
+ expect(data["triggerArguments"]).toEqual({ "schedule-cron": "0 0 * * *" });
271
+ });
272
+
273
+ test("clears the trigger when the trigger node is removed in the builder", async () => {
274
+ const updateSpy: jest.SpyInstance = spyOnUpdateOneById();
275
+ spyOnApiPost();
276
+
277
+ await onUpdateSuccess({ nodes: [], edges: [] });
278
+
279
+ const data: JSONObject = (updateSpy.mock.calls[0]![0] as JSONObject)[
280
+ "data"
281
+ ] as JSONObject;
282
+
283
+ expect(data["triggerId"]).toBeNull();
284
+ expect(data["triggerArguments"]).toEqual({});
285
+ });
286
+
287
+ test("touches no trigger columns when an update does not carry the graph", async () => {
288
+ const updateSpy: jest.SpyInstance = spyOnUpdateOneById();
289
+ spyOnApiPost();
290
+
291
+ // e.g. renaming a workflow must leave its trigger alone.
292
+ await onUpdateSuccess(undefined);
293
+
294
+ expect(updateSpy).not.toHaveBeenCalled();
295
+ });
296
+
297
+ test("always notifies the workflow service so trigger changes take effect", async () => {
298
+ spyOnUpdateOneById();
299
+ const postSpy: jest.SpyInstance = spyOnApiPost();
300
+
301
+ await onUpdateSuccess(scheduleGraph());
302
+
303
+ expect(postSpy).toHaveBeenCalledTimes(1);
304
+ expect(
305
+ (postSpy.mock.calls[0]![0] as JSONObject)["url"]!.toString(),
306
+ ).toContain(`/workflow/update/${WORKFLOW_ID}`);
307
+ });
308
+ });