@oneuptime/common 11.5.13 → 11.6.1
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/AnalyticsModels/AnalyticsBaseModel/AnalyticsBaseModel.ts +41 -24
- package/Models/AnalyticsModels/MetricBaselineHourly.ts +2 -0
- package/Models/AnalyticsModels/MetricItemAggMV1m.ts +5 -2
- package/Models/AnalyticsModels/MetricItemAggMV1mByContainer.ts +5 -2
- package/Models/AnalyticsModels/MetricItemAggMV1mByHostV2.ts +5 -2
- package/Models/AnalyticsModels/MetricItemAggMV1mByK8sCluster.ts +5 -2
- package/Models/AnalyticsModels/MetricItemAggMV1mByService.ts +5 -2
- package/Models/DatabaseModels/EnterpriseLicense.ts +21 -0
- package/Models/DatabaseModels/EnterpriseLicenseInstance.ts +25 -0
- package/Models/DatabaseModels/GlobalConfig.ts +76 -0
- package/Server/API/EnterpriseLicenseAPI.ts +57 -0
- package/Server/API/GlobalConfigAPI.ts +62 -1
- package/Server/EnvironmentConfig.ts +22 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1784659816363-AddInstanceVersionAndLatestReleaseColumns.ts +38 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1784705487674-AddEnterpriseLicenseEvaluationColumns.ts +25 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +4 -0
- package/Server/Services/AnalyticsDatabaseService.ts +61 -46
- package/Server/Services/DatabaseService.ts +66 -1
- package/Server/Services/WorkflowService.ts +100 -39
- package/Server/Types/Workflow/Components/Schedule.ts +57 -32
- package/Server/Utils/AnalyticsDatabase/InsertDedupContext.ts +59 -0
- package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +41 -4
- package/Server/Utils/Express.ts +13 -0
- package/Server/Utils/Telemetry/TelemetryFanInWriter.ts +799 -0
- package/Server/Utils/Telemetry/TelemetryWriterClient.ts +263 -0
- package/Server/Utils/Telemetry/TelemetryWriterServer.ts +240 -0
- package/Server/Utils/Telemetry/TelemetryWriterShedMetrics.ts +93 -0
- package/Tests/Models/AnalyticsModels/AnalyticsBaseModel.test.ts +122 -0
- package/Tests/Server/Services/DatabaseServiceSanitizeUpdateData.test.ts +232 -0
- package/Tests/Server/Services/UpdateOneByIdKeepsRowId.test.ts +107 -0
- package/Tests/Server/Services/WorkflowService.test.ts +308 -0
- package/Tests/Server/Utils/AnalyticsDatabase/ClusterAwareSchema.test.ts +164 -3
- package/Tests/Server/Utils/AnalyticsDatabase/StatementGenerator.test.ts +99 -0
- package/Tests/Server/Utils/Attribution.test.ts +203 -0
- package/Tests/Server/Utils/Marketing/ConversionUploadProvider.test.ts +183 -0
- package/Tests/Server/Utils/Marketing/GoogleAds.test.ts +340 -0
- package/Tests/Server/Utils/Monitor/Criteria/ExceptionMonitorCriteria.test.ts +87 -0
- package/Tests/Server/Utils/Monitor/Criteria/LogMonitorCriteria.test.ts +165 -0
- package/Tests/Server/Utils/Monitor/Criteria/TraceMonitorCriteria.test.ts +86 -0
- package/Tests/Server/Utils/Telemetry/TelemetryFanInWriter.test.ts +1565 -0
- package/Tests/Server/Utils/Telemetry/TelemetryWriterClient.test.ts +313 -0
- package/Tests/Server/Utils/Telemetry/TelemetryWriterServer.test.ts +270 -0
- package/Tests/Server/Utils/Telemetry/TelemetryWriterShedMetrics.test.ts +127 -0
- package/Tests/Types/AI/CodeFixTaskType.test.ts +3 -0
- package/Tests/Types/HashCode.test.ts +41 -0
- package/Tests/Types/Log/LogQueryParser.test.ts +186 -0
- package/Tests/Types/Monitor/MonitorCriteriaMetricVariables.test.ts +228 -0
- package/Tests/Types/Monitor/MonitorStepDefaultTelemetryConfig.test.ts +169 -0
- package/Tests/Types/Monitor/MonitorStepExceptionMonitor.test.ts +289 -0
- package/Tests/Types/Monitor/MonitorStepLogMonitor.test.ts +231 -0
- package/Tests/Types/Monitor/MonitorStepMetricViewConfigUtil.test.ts +245 -0
- package/Tests/Types/Monitor/MonitorStepNetworkDeviceMonitor.test.ts +155 -0
- package/Tests/Types/Monitor/MonitorStepTelemetrySerialization.test.ts +141 -0
- package/Tests/Types/Monitor/MonitorStepTraceMonitor.test.ts +137 -0
- package/Tests/Types/Workspace/NotificationRules/NotificationRuleCondition.test.ts +311 -0
- package/Tests/UI/Utils/Breadcrumb/BreadcrumbTrailResolver.test.ts +461 -0
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +2852 -0
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +758 -0
- package/Tests/UI/Utils/NotificationMethodUtil.test.ts +564 -0
- package/Tests/Utils/Array.test.ts +156 -0
- package/Tests/Utils/CronTab.test.ts +199 -0
- package/Tests/Utils/Dashboard/VariableInterpolation.test.ts +197 -0
- package/Tests/Utils/ModelImportExport.test.ts +101 -0
- package/Tests/Utils/Number.test.ts +179 -0
- package/Tests/Utils/ValueFormatter.test.ts +305 -0
- package/Tests/Utils/VersionUtil.test.ts +348 -0
- package/Tests/jest.setup.ts +1 -0
- package/Types/AnalyticsDatabase/TableColumn.ts +23 -0
- package/Types/EnterpriseLicense/EnterpriseLicenseInstanceSummary.ts +6 -0
- package/Types/Monitor/MonitorStep.ts +24 -4
- package/Types/Monitor/MonitorStepMetricViewConfigUtil.ts +105 -0
- package/UI/Components/EditionLabel/EditionLabel.tsx +445 -12
- package/UI/Components/Workflow/ArgumentsForm.tsx +68 -30
- package/UI/Components/Workflow/CronScheduleField.tsx +411 -0
- package/UI/Utils/Breadcrumb/BreadcrumbTrailResolver.ts +176 -0
- package/UI/Utils/NotificationMethodUtil.ts +310 -0
- package/Utils/CronTab.ts +823 -0
- package/Utils/ModelImportExport.ts +21 -6
- package/Utils/VersionUtil.ts +260 -0
- package/build/dist/Models/AnalyticsModels/AnalyticsBaseModel/AnalyticsBaseModel.js +25 -20
- package/build/dist/Models/AnalyticsModels/AnalyticsBaseModel/AnalyticsBaseModel.js.map +1 -1
- package/build/dist/Models/AnalyticsModels/MetricBaselineHourly.js +2 -0
- package/build/dist/Models/AnalyticsModels/MetricBaselineHourly.js.map +1 -1
- package/build/dist/Models/AnalyticsModels/MetricItemAggMV1m.js +5 -2
- package/build/dist/Models/AnalyticsModels/MetricItemAggMV1m.js.map +1 -1
- package/build/dist/Models/AnalyticsModels/MetricItemAggMV1mByContainer.js +5 -2
- package/build/dist/Models/AnalyticsModels/MetricItemAggMV1mByContainer.js.map +1 -1
- package/build/dist/Models/AnalyticsModels/MetricItemAggMV1mByHostV2.js +5 -2
- package/build/dist/Models/AnalyticsModels/MetricItemAggMV1mByHostV2.js.map +1 -1
- package/build/dist/Models/AnalyticsModels/MetricItemAggMV1mByK8sCluster.js +5 -2
- package/build/dist/Models/AnalyticsModels/MetricItemAggMV1mByK8sCluster.js.map +1 -1
- package/build/dist/Models/AnalyticsModels/MetricItemAggMV1mByService.js +5 -2
- package/build/dist/Models/AnalyticsModels/MetricItemAggMV1mByService.js.map +1 -1
- package/build/dist/Models/DatabaseModels/EnterpriseLicense.js +22 -0
- package/build/dist/Models/DatabaseModels/EnterpriseLicense.js.map +1 -1
- package/build/dist/Models/DatabaseModels/EnterpriseLicenseInstance.js +26 -0
- package/build/dist/Models/DatabaseModels/EnterpriseLicenseInstance.js.map +1 -1
- package/build/dist/Models/DatabaseModels/GlobalConfig.js +81 -0
- package/build/dist/Models/DatabaseModels/GlobalConfig.js.map +1 -1
- package/build/dist/Server/API/EnterpriseLicenseAPI.js +47 -0
- package/build/dist/Server/API/EnterpriseLicenseAPI.js.map +1 -1
- package/build/dist/Server/API/GlobalConfigAPI.js +52 -1
- package/build/dist/Server/API/GlobalConfigAPI.js.map +1 -1
- package/build/dist/Server/EnvironmentConfig.js +17 -0
- package/build/dist/Server/EnvironmentConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784659816363-AddInstanceVersionAndLatestReleaseColumns.js +18 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784659816363-AddInstanceVersionAndLatestReleaseColumns.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784705487674-AddEnterpriseLicenseEvaluationColumns.js +14 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784705487674-AddEnterpriseLicenseEvaluationColumns.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/Services/AnalyticsDatabaseService.js +53 -17
- package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
- package/build/dist/Server/Services/DatabaseService.js +53 -1
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/WorkflowService.js +80 -30
- package/build/dist/Server/Services/WorkflowService.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/Schedule.js +44 -19
- package/build/dist/Server/Types/Workflow/Components/Schedule.js.map +1 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/InsertDedupContext.js +24 -0
- package/build/dist/Server/Utils/AnalyticsDatabase/InsertDedupContext.js.map +1 -0
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +29 -4
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js.map +1 -1
- package/build/dist/Server/Utils/Express.js +12 -0
- package/build/dist/Server/Utils/Express.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/TelemetryFanInWriter.js +496 -0
- package/build/dist/Server/Utils/Telemetry/TelemetryFanInWriter.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/TelemetryWriterClient.js +165 -0
- package/build/dist/Server/Utils/Telemetry/TelemetryWriterClient.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/TelemetryWriterServer.js +136 -0
- package/build/dist/Server/Utils/Telemetry/TelemetryWriterServer.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/TelemetryWriterShedMetrics.js +76 -0
- package/build/dist/Server/Utils/Telemetry/TelemetryWriterShedMetrics.js.map +1 -0
- package/build/dist/Types/AnalyticsDatabase/TableColumn.js +7 -0
- package/build/dist/Types/AnalyticsDatabase/TableColumn.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorStep.js +20 -4
- package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorStepMetricViewConfigUtil.js +73 -0
- package/build/dist/Types/Monitor/MonitorStepMetricViewConfigUtil.js.map +1 -0
- package/build/dist/UI/Components/EditionLabel/EditionLabel.js +261 -13
- package/build/dist/UI/Components/EditionLabel/EditionLabel.js.map +1 -1
- package/build/dist/UI/Components/Workflow/ArgumentsForm.js +30 -6
- package/build/dist/UI/Components/Workflow/ArgumentsForm.js.map +1 -1
- package/build/dist/UI/Components/Workflow/CronScheduleField.js +209 -0
- package/build/dist/UI/Components/Workflow/CronScheduleField.js.map +1 -0
- package/build/dist/UI/Utils/Breadcrumb/BreadcrumbTrailResolver.js +129 -0
- package/build/dist/UI/Utils/Breadcrumb/BreadcrumbTrailResolver.js.map +1 -0
- package/build/dist/UI/Utils/NotificationMethodUtil.js +189 -0
- package/build/dist/UI/Utils/NotificationMethodUtil.js.map +1 -0
- package/build/dist/Utils/CronTab.js +609 -0
- package/build/dist/Utils/CronTab.js.map +1 -0
- package/build/dist/Utils/ModelImportExport.js +20 -6
- package/build/dist/Utils/ModelImportExport.js.map +1 -1
- package/build/dist/Utils/VersionUtil.js +193 -0
- package/build/dist/Utils/VersionUtil.js.map +1 -0
- package/package.json +1 -1
|
@@ -17,8 +17,14 @@ import API from "../../Utils/API";
|
|
|
17
17
|
import HTTPErrorResponse from "../../Types/API/HTTPErrorResponse";
|
|
18
18
|
import HTTPResponse from "../../Types/API/HTTPResponse";
|
|
19
19
|
import PartialEntity from "../../Types/Database/PartialEntity";
|
|
20
|
-
import {
|
|
20
|
+
import {
|
|
21
|
+
AppVersion,
|
|
22
|
+
DisableUpdateCheck,
|
|
23
|
+
EnterpriseLicenseValidationUrl,
|
|
24
|
+
Host,
|
|
25
|
+
} from "../EnvironmentConfig";
|
|
21
26
|
import EnterpriseLicenseInstanceSummary from "../../Types/EnterpriseLicense/EnterpriseLicenseInstanceSummary";
|
|
27
|
+
import VersionUtil from "../../Utils/VersionUtil";
|
|
22
28
|
import UserMiddleware from "../Middleware/UserAuthorization";
|
|
23
29
|
|
|
24
30
|
export default class GlobalConfigAPI extends BaseAPI<
|
|
@@ -67,11 +73,15 @@ export default class GlobalConfigAPI extends BaseAPI<
|
|
|
67
73
|
enterpriseLicenseExpiresAt: true,
|
|
68
74
|
enterpriseLicenseKey: true,
|
|
69
75
|
enterpriseLicenseToken: true,
|
|
76
|
+
enterpriseLicenseIsEvaluation: true,
|
|
70
77
|
enterpriseLicenseUserLimit: true,
|
|
71
78
|
enterpriseLicenseCurrentUserCount: true,
|
|
72
79
|
enterpriseLicenseUserCountUpdatedAt: true,
|
|
73
80
|
enterpriseLicenseInstances: true,
|
|
74
81
|
instanceId: true,
|
|
82
|
+
latestReleaseVersion: true,
|
|
83
|
+
latestReleasePublishedAt: true,
|
|
84
|
+
latestReleaseCheckedAt: true,
|
|
75
85
|
},
|
|
76
86
|
props: {
|
|
77
87
|
isRoot: true,
|
|
@@ -106,6 +116,12 @@ export default class GlobalConfigAPI extends BaseAPI<
|
|
|
106
116
|
? config?.enterpriseLicenseToken || null
|
|
107
117
|
: null,
|
|
108
118
|
licenseValid: licenseValid,
|
|
119
|
+
/*
|
|
120
|
+
* Whether this is an evaluation/testing license. Benign like the
|
|
121
|
+
* company name and expiry, so it is not gated behind sign-in — the
|
|
122
|
+
* edition modal shows the evaluation notice to anyone who opens it.
|
|
123
|
+
*/
|
|
124
|
+
isEvaluationLicense: Boolean(config?.enterpriseLicenseIsEvaluation),
|
|
109
125
|
userLimit:
|
|
110
126
|
typeof config?.enterpriseLicenseUserLimit === "number"
|
|
111
127
|
? config.enterpriseLicenseUserLimit
|
|
@@ -126,6 +142,38 @@ export default class GlobalConfigAPI extends BaseAPI<
|
|
|
126
142
|
isAuthenticatedUser && config?.instanceId
|
|
127
143
|
? config.instanceId.toString()
|
|
128
144
|
: null,
|
|
145
|
+
/*
|
|
146
|
+
* Which build this installation runs, and whether a newer one has
|
|
147
|
+
* been released, are gated the same way as the instance topology:
|
|
148
|
+
* telling an anonymous visitor on the login page that this server
|
|
149
|
+
* is behind on patches advertises an unpatched target.
|
|
150
|
+
*/
|
|
151
|
+
currentVersion: isAuthenticatedUser ? AppVersion : null,
|
|
152
|
+
latestVersion: isAuthenticatedUser
|
|
153
|
+
? config?.latestReleaseVersion || null
|
|
154
|
+
: null,
|
|
155
|
+
latestVersionPublishedAt:
|
|
156
|
+
isAuthenticatedUser && config?.latestReleasePublishedAt
|
|
157
|
+
? config.latestReleasePublishedAt.toISOString()
|
|
158
|
+
: null,
|
|
159
|
+
latestVersionCheckedAt:
|
|
160
|
+
isAuthenticatedUser && config?.latestReleaseCheckedAt
|
|
161
|
+
? config.latestReleaseCheckedAt.toISOString()
|
|
162
|
+
: null,
|
|
163
|
+
isUpdateAvailable: isAuthenticatedUser
|
|
164
|
+
? VersionUtil.isUpdateAvailable({
|
|
165
|
+
currentVersion: AppVersion,
|
|
166
|
+
latestVersion: config?.latestReleaseVersion,
|
|
167
|
+
})
|
|
168
|
+
: false,
|
|
169
|
+
/*
|
|
170
|
+
* Lets the modal say "update checks are off" instead of "has not
|
|
171
|
+
* checked yet", which would be a promise the installation is never
|
|
172
|
+
* going to keep.
|
|
173
|
+
*/
|
|
174
|
+
isUpdateCheckDisabled: isAuthenticatedUser
|
|
175
|
+
? DisableUpdateCheck
|
|
176
|
+
: false,
|
|
129
177
|
};
|
|
130
178
|
|
|
131
179
|
return Response.sendJsonObjectResponse(req, res, responseBody);
|
|
@@ -179,6 +227,12 @@ export default class GlobalConfigAPI extends BaseAPI<
|
|
|
179
227
|
licenseKey,
|
|
180
228
|
instanceId: instanceId.toString(),
|
|
181
229
|
host: Host,
|
|
230
|
+
/*
|
|
231
|
+
* Sent here as well as from the daily report job so the version
|
|
232
|
+
* lands on the license server the moment the key is validated,
|
|
233
|
+
* rather than up to 24 hours later.
|
|
234
|
+
*/
|
|
235
|
+
version: AppVersion,
|
|
182
236
|
},
|
|
183
237
|
});
|
|
184
238
|
|
|
@@ -239,6 +293,9 @@ export default class GlobalConfigAPI extends BaseAPI<
|
|
|
239
293
|
}
|
|
240
294
|
}
|
|
241
295
|
|
|
296
|
+
const isEvaluationLicense: boolean =
|
|
297
|
+
payload["isEvaluationLicense"] === true;
|
|
298
|
+
|
|
242
299
|
const instances: Array<EnterpriseLicenseInstanceSummary> =
|
|
243
300
|
Array.isArray(payload["instances"])
|
|
244
301
|
? (payload[
|
|
@@ -251,6 +308,7 @@ export default class GlobalConfigAPI extends BaseAPI<
|
|
|
251
308
|
enterpriseLicenseKey: licenseKeyRaw || null,
|
|
252
309
|
enterpriseLicenseExpiresAt: licenseExpiry || null,
|
|
253
310
|
enterpriseLicenseToken: licenseToken || null,
|
|
311
|
+
enterpriseLicenseIsEvaluation: isEvaluationLicense,
|
|
254
312
|
enterpriseLicenseUserLimit: userLimit,
|
|
255
313
|
enterpriseLicenseCurrentUserCount: currentUserCount,
|
|
256
314
|
enterpriseLicenseUserCountUpdatedAt: userCountUpdatedAt,
|
|
@@ -287,6 +345,8 @@ export default class GlobalConfigAPI extends BaseAPI<
|
|
|
287
345
|
newConfig.enterpriseLicenseToken = licenseToken;
|
|
288
346
|
}
|
|
289
347
|
|
|
348
|
+
newConfig.enterpriseLicenseIsEvaluation = isEvaluationLicense;
|
|
349
|
+
|
|
290
350
|
if (licenseExpiry) {
|
|
291
351
|
newConfig.enterpriseLicenseExpiresAt = licenseExpiry;
|
|
292
352
|
}
|
|
@@ -321,6 +381,7 @@ export default class GlobalConfigAPI extends BaseAPI<
|
|
|
321
381
|
expiresAt: licenseExpiry ? licenseExpiry.toISOString() : null,
|
|
322
382
|
licenseKey: licenseKeyRaw || null,
|
|
323
383
|
token: licenseToken || null,
|
|
384
|
+
isEvaluationLicense: isEvaluationLicense,
|
|
324
385
|
userLimit: userLimit,
|
|
325
386
|
currentUserCount: currentUserCount,
|
|
326
387
|
userCountUpdatedAt: userCountUpdatedAt
|
|
@@ -665,6 +665,14 @@ export const IpWhitelist: string = process.env["IP_WHITELIST"] || "";
|
|
|
665
665
|
export const DisableTelemetry: boolean =
|
|
666
666
|
process.env["DISABLE_TELEMETRY"] === "true";
|
|
667
667
|
|
|
668
|
+
/*
|
|
669
|
+
* Opt out of the daily "is a newer OneUptime released?" check against the
|
|
670
|
+
* GitHub API. Deliberately separate from DISABLE_TELEMETRY, which turns off
|
|
671
|
+
* the OpenTelemetry SDK and says nothing about outbound calls.
|
|
672
|
+
*/
|
|
673
|
+
export const DisableUpdateCheck: boolean =
|
|
674
|
+
process.env["DISABLE_UPDATE_CHECK"] === "true";
|
|
675
|
+
|
|
668
676
|
export const EnableProfiling: boolean =
|
|
669
677
|
process.env["ENABLE_PROFILING"] === "true";
|
|
670
678
|
|
|
@@ -782,6 +790,20 @@ export const EnterpriseLicenseUserCountReportUrl: URL = URL.fromString(
|
|
|
782
790
|
"https://oneuptime.com/api/enterprise-license/report-user-count",
|
|
783
791
|
);
|
|
784
792
|
|
|
793
|
+
/*
|
|
794
|
+
* GitHub's "latest release" endpoint. It already excludes drafts and
|
|
795
|
+
* prereleases, so whatever it returns is a version an administrator can
|
|
796
|
+
* safely be told to upgrade to.
|
|
797
|
+
*
|
|
798
|
+
* Overridable so an installation with no route to github.com can point the
|
|
799
|
+
* check at an internal mirror instead of turning it off entirely. The mirror
|
|
800
|
+
* must answer with GitHub's release shape (tag_name, html_url, published_at).
|
|
801
|
+
*/
|
|
802
|
+
export const LatestReleaseCheckUrl: URL = URL.fromString(
|
|
803
|
+
process.env["LATEST_RELEASE_CHECK_URL"] ||
|
|
804
|
+
"https://api.github.com/repos/OneUptime/oneuptime/releases/latest",
|
|
805
|
+
);
|
|
806
|
+
|
|
785
807
|
// Inbound Email Configuration for Incoming Email Monitor
|
|
786
808
|
export enum InboundEmailProviderType {
|
|
787
809
|
SendGrid = "SendGrid",
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { MigrationInterface, QueryRunner } from "typeorm";
|
|
2
|
+
|
|
3
|
+
export class AddInstanceVersionAndLatestReleaseColumns1784659816363
|
|
4
|
+
implements MigrationInterface
|
|
5
|
+
{
|
|
6
|
+
public name: string =
|
|
7
|
+
"AddInstanceVersionAndLatestReleaseColumns1784659816363";
|
|
8
|
+
|
|
9
|
+
public async up(queryRunner: QueryRunner): Promise<void> {
|
|
10
|
+
await queryRunner.query(
|
|
11
|
+
`ALTER TABLE "EnterpriseLicenseInstance" ADD "oneuptimeVersion" character varying(100)`,
|
|
12
|
+
);
|
|
13
|
+
await queryRunner.query(
|
|
14
|
+
`ALTER TABLE "GlobalConfig" ADD "latestReleaseVersion" character varying(100)`,
|
|
15
|
+
);
|
|
16
|
+
await queryRunner.query(
|
|
17
|
+
`ALTER TABLE "GlobalConfig" ADD "latestReleasePublishedAt" TIMESTAMP WITH TIME ZONE`,
|
|
18
|
+
);
|
|
19
|
+
await queryRunner.query(
|
|
20
|
+
`ALTER TABLE "GlobalConfig" ADD "latestReleaseCheckedAt" TIMESTAMP WITH TIME ZONE`,
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
public async down(queryRunner: QueryRunner): Promise<void> {
|
|
25
|
+
await queryRunner.query(
|
|
26
|
+
`ALTER TABLE "GlobalConfig" DROP COLUMN "latestReleaseCheckedAt"`,
|
|
27
|
+
);
|
|
28
|
+
await queryRunner.query(
|
|
29
|
+
`ALTER TABLE "GlobalConfig" DROP COLUMN "latestReleasePublishedAt"`,
|
|
30
|
+
);
|
|
31
|
+
await queryRunner.query(
|
|
32
|
+
`ALTER TABLE "GlobalConfig" DROP COLUMN "latestReleaseVersion"`,
|
|
33
|
+
);
|
|
34
|
+
await queryRunner.query(
|
|
35
|
+
`ALTER TABLE "EnterpriseLicenseInstance" DROP COLUMN "oneuptimeVersion"`,
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { MigrationInterface, QueryRunner } from "typeorm";
|
|
2
|
+
|
|
3
|
+
export class AddEnterpriseLicenseEvaluationColumns1784705487674
|
|
4
|
+
implements MigrationInterface
|
|
5
|
+
{
|
|
6
|
+
public name: string = "AddEnterpriseLicenseEvaluationColumns1784705487674";
|
|
7
|
+
|
|
8
|
+
public async up(queryRunner: QueryRunner): Promise<void> {
|
|
9
|
+
await queryRunner.query(
|
|
10
|
+
`ALTER TABLE "EnterpriseLicense" ADD "isEvaluationLicense" boolean NOT NULL DEFAULT false`,
|
|
11
|
+
);
|
|
12
|
+
await queryRunner.query(
|
|
13
|
+
`ALTER TABLE "GlobalConfig" ADD "enterpriseLicenseIsEvaluation" boolean DEFAULT false`,
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
public async down(queryRunner: QueryRunner): Promise<void> {
|
|
18
|
+
await queryRunner.query(
|
|
19
|
+
`ALTER TABLE "GlobalConfig" DROP COLUMN "enterpriseLicenseIsEvaluation"`,
|
|
20
|
+
);
|
|
21
|
+
await queryRunner.query(
|
|
22
|
+
`ALTER TABLE "EnterpriseLicense" DROP COLUMN "isEvaluationLicense"`,
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -462,6 +462,8 @@ import { AddEnterpriseLicenseNotificationColumns1784218257664 } from "./17842182
|
|
|
462
462
|
import { AddAttributionColumnsToUserAndProject1784293516000 } from "./1784293516000-AddAttributionColumnsToUserAndProject";
|
|
463
463
|
import { AddMarketingConversionTable1784298000000 } from "./1784298000000-AddMarketingConversionTable";
|
|
464
464
|
import { AddExceptionTriageAndPrivacyColumns1784640000000 } from "./1784640000000-AddExceptionTriageAndPrivacyColumns";
|
|
465
|
+
import { AddInstanceVersionAndLatestReleaseColumns1784659816363 } from "./1784659816363-AddInstanceVersionAndLatestReleaseColumns";
|
|
466
|
+
import { AddEnterpriseLicenseEvaluationColumns1784705487674 } from "./1784705487674-AddEnterpriseLicenseEvaluationColumns";
|
|
465
467
|
|
|
466
468
|
export default [
|
|
467
469
|
InitialMigration,
|
|
@@ -928,4 +930,6 @@ export default [
|
|
|
928
930
|
AddAttributionColumnsToUserAndProject1784293516000,
|
|
929
931
|
AddMarketingConversionTable1784298000000,
|
|
930
932
|
AddExceptionTriageAndPrivacyColumns1784640000000,
|
|
933
|
+
AddInstanceVersionAndLatestReleaseColumns1784659816363,
|
|
934
|
+
AddEnterpriseLicenseEvaluationColumns1784705487674,
|
|
931
935
|
];
|
|
@@ -47,7 +47,7 @@ import {
|
|
|
47
47
|
ResponseJSON,
|
|
48
48
|
ResultSet,
|
|
49
49
|
} from "@clickhouse/client";
|
|
50
|
-
import {
|
|
50
|
+
import { nextInsertDedupToken } from "../Utils/AnalyticsDatabase/InsertDedupContext";
|
|
51
51
|
import AnalyticsBaseModel from "../../Models/AnalyticsModels/AnalyticsBaseModel/AnalyticsBaseModel";
|
|
52
52
|
import { WorkflowRoute } from "../../ServiceRoute";
|
|
53
53
|
import Protocol from "../../Types/API/Protocol";
|
|
@@ -140,40 +140,42 @@ export const MigrationExecuteOptions: ClickhouseExecuteOptions = {
|
|
|
140
140
|
},
|
|
141
141
|
};
|
|
142
142
|
|
|
143
|
-
|
|
144
|
-
*
|
|
145
|
-
*
|
|
146
|
-
*
|
|
147
|
-
*
|
|
148
|
-
* "<tokenBase>:<table>:<chunkIndex>"` plus async_insert_deduplicate=1 /
|
|
149
|
-
* wait_for_async_insert=1, so a stalled-job retry that re-processes the
|
|
150
|
-
* same payload re-issues byte-identical tokens and ClickHouse drops the
|
|
151
|
-
* duplicate blocks (on replicated tables; on plain MergeTree the token is
|
|
152
|
-
* ignored unless non_replicated_deduplication_window is set — no harm
|
|
153
|
-
* either way). The chunk counter is per table because one job inserts
|
|
154
|
-
* into several tables (e.g. Span + ExceptionInstance) in a deterministic
|
|
155
|
-
* order.
|
|
143
|
+
/*
|
|
144
|
+
* The insert-dedup ambient context lives in
|
|
145
|
+
* Utils/AnalyticsDatabase/InsertDedupContext so that both this service and
|
|
146
|
+
* TelemetryFanInWriter can consume deterministic tokens without an import
|
|
147
|
+
* cycle. Re-exported here for existing callers.
|
|
156
148
|
*
|
|
157
|
-
* HTTP-path inserts run outside the context and
|
|
158
|
-
*
|
|
159
|
-
* affordable off the request
|
|
149
|
+
* HTTP-path inserts run outside the context and are always fire-and-forget
|
|
150
|
+
* (wait_for_async_insert=0) — flush waiting, when opted into via
|
|
151
|
+
* TELEMETRY_WAIT_FOR_ASYNC_INSERT, is only affordable off the request
|
|
152
|
+
* thread and therefore only applies to tokened (queue-job) inserts.
|
|
160
153
|
*/
|
|
161
|
-
export
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
154
|
+
export {
|
|
155
|
+
runWithInsertDedup,
|
|
156
|
+
nextInsertDedupToken,
|
|
157
|
+
type InsertDedupContextStore,
|
|
158
|
+
} from "../Utils/AnalyticsDatabase/InsertDedupContext";
|
|
165
159
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
160
|
+
/*
|
|
161
|
+
* Ack mode for telemetry ClickHouse inserts.
|
|
162
|
+
*
|
|
163
|
+
* Default (false): wait_for_async_insert=0 — fire-and-forget. ClickHouse
|
|
164
|
+
* acks as soon as the batch is accepted into its async-insert buffer and
|
|
165
|
+
* owns flushing from there; inserts release their query slot almost
|
|
166
|
+
* immediately. The trade: flush-time errors surface only in server logs
|
|
167
|
+
* (system.asynchronous_inserts / part log), and a ClickHouse crash between
|
|
168
|
+
* buffer-accept and flush loses that buffer even though callers were acked.
|
|
169
|
+
*
|
|
170
|
+
* Set TELEMETRY_WAIT_FOR_ASYNC_INSERT=true to make every tokened insert
|
|
171
|
+
* wait for the durable flush before acking (ack-after-flush end to end
|
|
172
|
+
* through the fan-in writer and writer tier) — at the cost of each waiting
|
|
173
|
+
* insert holding a ClickHouse query slot until its buffer flushes.
|
|
174
|
+
*/
|
|
175
|
+
export function shouldWaitForAsyncInsert(): boolean {
|
|
176
|
+
const raw: string | undefined =
|
|
177
|
+
process.env["TELEMETRY_WAIT_FOR_ASYNC_INSERT"];
|
|
178
|
+
return raw === "true" || raw === "1";
|
|
177
179
|
}
|
|
178
180
|
|
|
179
181
|
export default class AnalyticsDatabaseService<
|
|
@@ -259,30 +261,31 @@ export default class AnalyticsDatabaseService<
|
|
|
259
261
|
let dedupToken: string | undefined = options?.dedupToken;
|
|
260
262
|
|
|
261
263
|
if (!dedupToken) {
|
|
262
|
-
|
|
263
|
-
insertDedupContext.getStore();
|
|
264
|
-
if (dedupStore) {
|
|
265
|
-
const chunkIndex: number =
|
|
266
|
-
dedupStore.chunkIndexByTable.get(tableName) ?? 0;
|
|
267
|
-
dedupStore.chunkIndexByTable.set(tableName, chunkIndex + 1);
|
|
268
|
-
dedupToken = `${dedupStore.tokenBase}:${tableName}:${chunkIndex}`;
|
|
269
|
-
}
|
|
264
|
+
dedupToken = nextInsertDedupToken(tableName);
|
|
270
265
|
}
|
|
271
266
|
|
|
267
|
+
const waitForAsyncInsert: 0 | 1 = shouldWaitForAsyncInsert() ? 1 : 0;
|
|
268
|
+
|
|
272
269
|
let clickhouseSettings: ClickHouseSettings = {
|
|
273
270
|
async_insert: 1,
|
|
274
|
-
wait_for_async_insert:
|
|
271
|
+
wait_for_async_insert: waitForAsyncInsert,
|
|
275
272
|
};
|
|
276
273
|
|
|
277
274
|
if (dedupToken) {
|
|
278
275
|
/*
|
|
279
|
-
*
|
|
280
|
-
*
|
|
281
|
-
*
|
|
276
|
+
* Dedup settings ride along in both ack modes. For async inserts
|
|
277
|
+
* ClickHouse dedups by content hash of the insert body when
|
|
278
|
+
* async_insert_deduplicate=1 (the explicit token is not yet honored
|
|
279
|
+
* for async inserts — ClickHouse #52018), so byte-identical queue
|
|
280
|
+
* retries are still dropped. The ack mode is a separate, deliberate
|
|
281
|
+
* trade (see shouldWaitForAsyncInsert): by default ClickHouse owns
|
|
282
|
+
* flushing and an ack means "accepted into the async-insert buffer";
|
|
283
|
+
* with TELEMETRY_WAIT_FOR_ASYNC_INSERT=true the ack waits for the
|
|
284
|
+
* durable flush instead.
|
|
282
285
|
*/
|
|
283
286
|
clickhouseSettings = {
|
|
284
287
|
async_insert: 1,
|
|
285
|
-
wait_for_async_insert:
|
|
288
|
+
wait_for_async_insert: waitForAsyncInsert,
|
|
286
289
|
async_insert_deduplicate: 1,
|
|
287
290
|
insert_deduplication_token: dedupToken,
|
|
288
291
|
};
|
|
@@ -980,7 +983,19 @@ export default class AnalyticsDatabaseService<
|
|
|
980
983
|
onBeforeFind.select = {} as any;
|
|
981
984
|
}
|
|
982
985
|
|
|
983
|
-
|
|
986
|
+
/*
|
|
987
|
+
* Derived aggregate targets deliberately omit AnalyticsBaseModel's
|
|
988
|
+
* synthetic `_id`: the aggregation key is the row identity. Only force
|
|
989
|
+
* `_id` into generic reads when the model actually declares it.
|
|
990
|
+
*/
|
|
991
|
+
if (
|
|
992
|
+
this.model.tableColumns.some(
|
|
993
|
+
(column: AnalyticsTableColumn): boolean => {
|
|
994
|
+
return column.key === "_id";
|
|
995
|
+
},
|
|
996
|
+
) &&
|
|
997
|
+
!(onBeforeFind.select as any)["_id"]
|
|
998
|
+
) {
|
|
984
999
|
(onBeforeFind.select as any)["_id"] = true;
|
|
985
1000
|
}
|
|
986
1001
|
|
|
@@ -1743,10 +1743,67 @@ class DatabaseService<TBaseModel extends BaseModel> extends BaseService {
|
|
|
1743
1743
|
});
|
|
1744
1744
|
}
|
|
1745
1745
|
|
|
1746
|
+
/*
|
|
1747
|
+
* Update `data` may arrive as a full model instance rather than a plain
|
|
1748
|
+
* partial — the QueryDeepPartialEntity type structurally admits both, and
|
|
1749
|
+
* callers do construct `new Model()` payloads. A model instance carries
|
|
1750
|
+
* non-column own properties from DatabaseBaseModel (every column
|
|
1751
|
+
* initializer plus `isPermissionIf = {}`), and _updateBy turns every data
|
|
1752
|
+
* key into a select column for its internal find, so the extra keys made
|
|
1753
|
+
* that find throw `TableColumnMetadata not found for isPermissionIf
|
|
1754
|
+
* column` and fail the whole update. Strip a model instance down to its
|
|
1755
|
+
* set table columns; plain objects pass through untouched so a typo'd
|
|
1756
|
+
* column name in a literal still fails loudly instead of being silently
|
|
1757
|
+
* dropped.
|
|
1758
|
+
*
|
|
1759
|
+
* `_id`, `createdAt`, `updatedAt` and `version` are dropped for model
|
|
1760
|
+
* instances the same way BaseAPI drops them from client payloads: the row
|
|
1761
|
+
* is located by the update query (spreading a foreign `_id` into the save
|
|
1762
|
+
* payload would redirect the write), timestamps are database-managed, and
|
|
1763
|
+
* `version` is TypeORM's optimistic-concurrency counter.
|
|
1764
|
+
*/
|
|
1765
|
+
public sanitizeUpdateData(
|
|
1766
|
+
data: UpdateBy<TBaseModel>["data"],
|
|
1767
|
+
): UpdateBy<TBaseModel>["data"] {
|
|
1768
|
+
if (!(data instanceof BaseModel)) {
|
|
1769
|
+
return data;
|
|
1770
|
+
}
|
|
1771
|
+
|
|
1772
|
+
const plainData: JSONObject = {};
|
|
1773
|
+
|
|
1774
|
+
for (const key of Object.keys(data)) {
|
|
1775
|
+
if (!this.model.isTableColumn(key)) {
|
|
1776
|
+
continue;
|
|
1777
|
+
}
|
|
1778
|
+
|
|
1779
|
+
if (
|
|
1780
|
+
key === "_id" ||
|
|
1781
|
+
key === "createdAt" ||
|
|
1782
|
+
key === "updatedAt" ||
|
|
1783
|
+
key === "version"
|
|
1784
|
+
) {
|
|
1785
|
+
continue;
|
|
1786
|
+
}
|
|
1787
|
+
|
|
1788
|
+
const value: JSONValue = (data as any)[key];
|
|
1789
|
+
|
|
1790
|
+
// Unset columns must not become writes (or select columns).
|
|
1791
|
+
if (value === undefined) {
|
|
1792
|
+
continue;
|
|
1793
|
+
}
|
|
1794
|
+
|
|
1795
|
+
plainData[key] = value;
|
|
1796
|
+
}
|
|
1797
|
+
|
|
1798
|
+
return plainData as UpdateBy<TBaseModel>["data"];
|
|
1799
|
+
}
|
|
1800
|
+
|
|
1746
1801
|
private async _updateBy(updateBy: UpdateBy<TBaseModel>): Promise<number> {
|
|
1747
1802
|
try {
|
|
1748
1803
|
this.setTelemetryContextFromProps(updateBy.props);
|
|
1749
1804
|
|
|
1805
|
+
updateBy.data = this.sanitizeUpdateData(updateBy.data);
|
|
1806
|
+
|
|
1750
1807
|
const onUpdate: OnUpdate<TBaseModel> = updateBy.props.ignoreHooks
|
|
1751
1808
|
? { updateBy, carryForward: [] }
|
|
1752
1809
|
: await this.onBeforeUpdate(updateBy);
|
|
@@ -1830,9 +1887,17 @@ class DatabaseService<TBaseModel extends BaseModel> extends BaseService {
|
|
|
1830
1887
|
});
|
|
1831
1888
|
|
|
1832
1889
|
for (const item of items) {
|
|
1890
|
+
/*
|
|
1891
|
+
* _id must be set AFTER the spread: update data can carry an
|
|
1892
|
+
* explicit `_id: undefined` (sanitizeUpdateData strips it from model
|
|
1893
|
+
* instances, but a plain object can still hold it), and spreading it
|
|
1894
|
+
* after _id would clobber the located row's id — save() then sees no
|
|
1895
|
+
* primary key, INSERTs instead of updating, and dies on the first
|
|
1896
|
+
* NOT NULL column.
|
|
1897
|
+
*/
|
|
1833
1898
|
const updatedItem: any = {
|
|
1834
|
-
_id: item._id!,
|
|
1835
1899
|
...data,
|
|
1900
|
+
_id: item._id!,
|
|
1836
1901
|
} as any;
|
|
1837
1902
|
|
|
1838
1903
|
logger.debug("Updated Item", {
|
|
@@ -49,6 +49,40 @@ export class Service extends DatabaseService<Model> {
|
|
|
49
49
|
createdItem.webhookSecretKey = secretKey;
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
+
/*
|
|
53
|
+
* A workflow that arrives with its graph already in place - imported from
|
|
54
|
+
* a JSON export, or duplicated from another workflow - never passes
|
|
55
|
+
* through onUpdateSuccess, so nothing has denormalized its trigger onto
|
|
56
|
+
* the row. The runner looks workflows up by triggerId, so without this the
|
|
57
|
+
* workflow is saved and looks correct in the builder but silently never
|
|
58
|
+
* fires.
|
|
59
|
+
*/
|
|
60
|
+
if (createdItem.id && createdItem.graph) {
|
|
61
|
+
await this.saveTriggerFromGraph({
|
|
62
|
+
workflowId: createdItem.id,
|
|
63
|
+
graph: createdItem.graph,
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
/*
|
|
67
|
+
* Registers schedule triggers with the runner. Best effort: the row and
|
|
68
|
+
* its trigger are already persisted, so a workflow service outage must
|
|
69
|
+
* not fail the create. The trigger is already on the row, so the next
|
|
70
|
+
* save - or the runner's own startup scan in Schedule.init, which
|
|
71
|
+
* queries by triggerId - picks the workflow up.
|
|
72
|
+
*/
|
|
73
|
+
try {
|
|
74
|
+
await this.notifyWorkflowService(createdItem.id);
|
|
75
|
+
} catch (error) {
|
|
76
|
+
logger.error(
|
|
77
|
+
`Error notifying workflow service of created workflow: ${error}`,
|
|
78
|
+
{
|
|
79
|
+
projectId: createdItem.projectId?.toString(),
|
|
80
|
+
workflowId: createdItem.id?.toString(),
|
|
81
|
+
} as LogAttributes,
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
52
86
|
if (createdItem.projectId && createdItem.id) {
|
|
53
87
|
/*
|
|
54
88
|
* Run label rule first so rule-added labels are persisted before
|
|
@@ -87,39 +121,13 @@ export class Service extends DatabaseService<Model> {
|
|
|
87
121
|
): Promise<OnUpdate<Model>> {
|
|
88
122
|
/// save trigger and trigger args.
|
|
89
123
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
(onUpdate.updateBy.data as any).graph &&
|
|
93
|
-
(((onUpdate.updateBy.data as any).graph as any)[
|
|
94
|
-
"nodes"
|
|
95
|
-
] as Array<JSONObject>)
|
|
96
|
-
) {
|
|
97
|
-
let trigger: NodeDataProp | null = null;
|
|
98
|
-
|
|
99
|
-
// check if it has a trigger node.
|
|
100
|
-
for (const node of ((onUpdate.updateBy.data as any).graph as any)[
|
|
101
|
-
"nodes"
|
|
102
|
-
] as Array<JSONObject>) {
|
|
103
|
-
const nodeData: NodeDataProp = node["data"] as any;
|
|
104
|
-
if (
|
|
105
|
-
nodeData.componentType === ComponentType.Trigger &&
|
|
106
|
-
nodeData.nodeType === NodeType.Node
|
|
107
|
-
) {
|
|
108
|
-
// found the trigger;
|
|
109
|
-
trigger = nodeData;
|
|
110
|
-
}
|
|
111
|
-
}
|
|
124
|
+
const updatedGraph: JSONObject | undefined = (onUpdate.updateBy.data as any)
|
|
125
|
+
?.graph as JSONObject | undefined;
|
|
112
126
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
triggerArguments: trigger?.arguments || {},
|
|
118
|
-
} as any,
|
|
119
|
-
props: {
|
|
120
|
-
isRoot: true,
|
|
121
|
-
ignoreHooks: true,
|
|
122
|
-
},
|
|
127
|
+
if (updatedGraph) {
|
|
128
|
+
await this.saveTriggerFromGraph({
|
|
129
|
+
workflowId: new ObjectID(onUpdate.updateBy.query._id! as any),
|
|
130
|
+
graph: updatedGraph,
|
|
123
131
|
});
|
|
124
132
|
}
|
|
125
133
|
|
|
@@ -127,23 +135,76 @@ export class Service extends DatabaseService<Model> {
|
|
|
127
135
|
workflowId: onUpdate.updateBy.query._id?.toString(),
|
|
128
136
|
} as LogAttributes);
|
|
129
137
|
|
|
138
|
+
await this.notifyWorkflowService(
|
|
139
|
+
new ObjectID(onUpdate.updateBy.query._id! as any),
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
logger.debug("Updated workflow on the workflow service", {
|
|
143
|
+
workflowId: onUpdate.updateBy.query._id?.toString(),
|
|
144
|
+
} as LogAttributes);
|
|
145
|
+
|
|
146
|
+
return onUpdate;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/*
|
|
150
|
+
* The trigger node is denormalized out of the graph onto triggerId /
|
|
151
|
+
* triggerArguments because the runner queries workflows by trigger and
|
|
152
|
+
* cannot parse every graph to do it. A graph with no trigger node clears
|
|
153
|
+
* both columns, which is how a workflow stops firing when its trigger is
|
|
154
|
+
* removed in the builder.
|
|
155
|
+
*/
|
|
156
|
+
private async saveTriggerFromGraph(data: {
|
|
157
|
+
workflowId: ObjectID;
|
|
158
|
+
graph: JSONObject;
|
|
159
|
+
}): Promise<void> {
|
|
160
|
+
const nodes: Array<JSONObject> | undefined = data.graph["nodes"] as
|
|
161
|
+
| Array<JSONObject>
|
|
162
|
+
| undefined;
|
|
163
|
+
|
|
164
|
+
if (!nodes || !Array.isArray(nodes)) {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
let trigger: NodeDataProp | null = null;
|
|
169
|
+
|
|
170
|
+
// check if it has a trigger node.
|
|
171
|
+
for (const node of nodes) {
|
|
172
|
+
const nodeData: NodeDataProp = node["data"] as any;
|
|
173
|
+
|
|
174
|
+
if (
|
|
175
|
+
nodeData?.componentType === ComponentType.Trigger &&
|
|
176
|
+
nodeData?.nodeType === NodeType.Node
|
|
177
|
+
) {
|
|
178
|
+
// found the trigger;
|
|
179
|
+
trigger = nodeData;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
await this.updateOneById({
|
|
184
|
+
id: data.workflowId,
|
|
185
|
+
data: {
|
|
186
|
+
triggerId: trigger?.metadataId || null,
|
|
187
|
+
triggerArguments: trigger?.arguments || {},
|
|
188
|
+
} as any,
|
|
189
|
+
props: {
|
|
190
|
+
isRoot: true,
|
|
191
|
+
ignoreHooks: true,
|
|
192
|
+
},
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
private async notifyWorkflowService(workflowId: ObjectID): Promise<void> {
|
|
130
197
|
await API.post<EmptyResponseData>({
|
|
131
198
|
url: new URL(
|
|
132
199
|
Protocol.HTTP,
|
|
133
200
|
WorkflowHostname,
|
|
134
|
-
new Route("/workflow/update/" +
|
|
201
|
+
new Route("/workflow/update/" + workflowId.toString()),
|
|
135
202
|
),
|
|
136
203
|
data: {},
|
|
137
204
|
headers: {
|
|
138
205
|
...ClusterKeyAuthorization.getClusterKeyHeaders(),
|
|
139
206
|
},
|
|
140
207
|
});
|
|
141
|
-
|
|
142
|
-
logger.debug("Updated workflow on the workflow service", {
|
|
143
|
-
workflowId: onUpdate.updateBy.query._id?.toString(),
|
|
144
|
-
} as LogAttributes);
|
|
145
|
-
|
|
146
|
-
return onUpdate;
|
|
147
208
|
}
|
|
148
209
|
}
|
|
149
210
|
export default new Service();
|