@n8n/db 0.3.0 → 0.4.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 (129) hide show
  1. package/dist/build.tsbuildinfo +1 -1
  2. package/dist/entities/project-relation.d.ts +2 -1
  3. package/dist/entities/project-relation.js.map +1 -1
  4. package/dist/entities/shared-credentials.d.ts +1 -1
  5. package/dist/entities/shared-credentials.js +1 -1
  6. package/dist/entities/shared-credentials.js.map +1 -1
  7. package/dist/entities/shared-workflow.d.ts +1 -1
  8. package/dist/entities/shared-workflow.js +1 -1
  9. package/dist/entities/shared-workflow.js.map +1 -1
  10. package/dist/entities/types-db.d.ts +31 -2
  11. package/dist/entities/types-db.js.map +1 -1
  12. package/dist/entities/user.d.ts +4 -7
  13. package/dist/entities/user.js +0 -24
  14. package/dist/entities/user.js.map +1 -1
  15. package/dist/entities/webhook-entity.d.ts +1 -0
  16. package/dist/entities/webhook-entity.js +3 -0
  17. package/dist/entities/webhook-entity.js.map +1 -1
  18. package/dist/entities/workflow-entity.d.ts +1 -0
  19. package/dist/entities/workflow-entity.js +4 -0
  20. package/dist/entities/workflow-entity.js.map +1 -1
  21. package/dist/index.d.ts +1 -0
  22. package/dist/index.js +1 -0
  23. package/dist/index.js.map +1 -1
  24. package/dist/repositories/annotation-tag-mapping.repository.ee.d.ts +6 -0
  25. package/dist/repositories/annotation-tag-mapping.repository.ee.js +36 -0
  26. package/dist/repositories/annotation-tag-mapping.repository.ee.js.map +1 -0
  27. package/dist/repositories/annotation-tag.repository.ee.d.ts +5 -0
  28. package/dist/repositories/annotation-tag.repository.ee.js +26 -0
  29. package/dist/repositories/annotation-tag.repository.ee.js.map +1 -0
  30. package/dist/repositories/api-key.repository.d.ts +5 -0
  31. package/dist/repositories/api-key.repository.js +26 -0
  32. package/dist/repositories/api-key.repository.js.map +1 -0
  33. package/dist/repositories/auth-identity.repository.d.ts +5 -0
  34. package/dist/repositories/auth-identity.repository.js +26 -0
  35. package/dist/repositories/auth-identity.repository.js.map +1 -0
  36. package/dist/repositories/auth-provider-sync-history.repository.d.ts +5 -0
  37. package/dist/repositories/auth-provider-sync-history.repository.js +26 -0
  38. package/dist/repositories/auth-provider-sync-history.repository.js.map +1 -0
  39. package/dist/repositories/auth-user.repository.d.ts +5 -0
  40. package/dist/repositories/auth-user.repository.js +26 -0
  41. package/dist/repositories/auth-user.repository.js.map +1 -0
  42. package/dist/repositories/credentials.repository.d.ts +20 -0
  43. package/dist/repositories/credentials.repository.js +132 -0
  44. package/dist/repositories/credentials.repository.js.map +1 -0
  45. package/dist/repositories/event-destinations.repository.d.ts +5 -0
  46. package/dist/repositories/event-destinations.repository.js +26 -0
  47. package/dist/repositories/event-destinations.repository.js.map +1 -0
  48. package/dist/repositories/execution-annotation.repository.d.ts +5 -0
  49. package/dist/repositories/execution-annotation.repository.js +26 -0
  50. package/dist/repositories/execution-annotation.repository.js.map +1 -0
  51. package/dist/repositories/execution-data.repository.d.ts +9 -0
  52. package/dist/repositories/execution-data.repository.js +37 -0
  53. package/dist/repositories/execution-data.repository.js.map +1 -0
  54. package/dist/repositories/execution-metadata.repository.d.ts +5 -0
  55. package/dist/repositories/execution-metadata.repository.js +26 -0
  56. package/dist/repositories/execution-metadata.repository.js.map +1 -0
  57. package/dist/repositories/execution.repository.d.ts +119 -0
  58. package/dist/repositories/execution.repository.js +695 -0
  59. package/dist/repositories/execution.repository.js.map +1 -0
  60. package/dist/repositories/folder-tag-mapping.repository.d.ts +6 -0
  61. package/dist/repositories/folder-tag-mapping.repository.js +33 -0
  62. package/dist/repositories/folder-tag-mapping.repository.js.map +1 -0
  63. package/dist/repositories/folder.repository.d.ts +32 -0
  64. package/dist/repositories/folder.repository.js +269 -0
  65. package/dist/repositories/folder.repository.js.map +1 -0
  66. package/dist/repositories/index.d.ts +33 -0
  67. package/dist/repositories/index.js +70 -0
  68. package/dist/repositories/index.js.map +1 -0
  69. package/dist/repositories/installed-nodes.repository.d.ts +5 -0
  70. package/dist/repositories/installed-nodes.repository.js +26 -0
  71. package/dist/repositories/installed-nodes.repository.js.map +1 -0
  72. package/dist/repositories/installed-packages.repository.d.ts +9 -0
  73. package/dist/repositories/installed-packages.repository.js +54 -0
  74. package/dist/repositories/installed-packages.repository.js.map +1 -0
  75. package/dist/repositories/invalid-auth-token.repository.d.ts +5 -0
  76. package/dist/repositories/invalid-auth-token.repository.js +26 -0
  77. package/dist/repositories/invalid-auth-token.repository.js.map +1 -0
  78. package/dist/repositories/license-metrics.repository.d.ts +20 -0
  79. package/dist/repositories/license-metrics.repository.js +69 -0
  80. package/dist/repositories/license-metrics.repository.js.map +1 -0
  81. package/dist/repositories/processed-data.repository.d.ts +5 -0
  82. package/dist/repositories/processed-data.repository.js +26 -0
  83. package/dist/repositories/processed-data.repository.js.map +1 -0
  84. package/dist/repositories/project-relation.repository.d.ts +14 -0
  85. package/dist/repositories/project-relation.repository.js +72 -0
  86. package/dist/repositories/project-relation.repository.js.map +1 -0
  87. package/dist/repositories/project.repository.d.ts +13 -0
  88. package/dist/repositories/project.repository.js +56 -0
  89. package/dist/repositories/project.repository.js.map +1 -0
  90. package/dist/repositories/settings.repository.d.ts +6 -0
  91. package/dist/repositories/settings.repository.js +29 -0
  92. package/dist/repositories/settings.repository.js.map +1 -0
  93. package/dist/repositories/shared-credentials.repository.d.ts +17 -0
  94. package/dist/repositories/shared-credentials.repository.js +104 -0
  95. package/dist/repositories/shared-credentials.repository.js.map +1 -0
  96. package/dist/repositories/shared-workflow.repository.d.ts +25 -0
  97. package/dist/repositories/shared-workflow.repository.js +134 -0
  98. package/dist/repositories/shared-workflow.repository.js.map +1 -0
  99. package/dist/repositories/tag.repository.d.ts +10 -0
  100. package/dist/repositories/tag.repository.js +68 -0
  101. package/dist/repositories/tag.repository.js.map +1 -0
  102. package/dist/repositories/test-case-execution.repository.ee.d.ts +35 -0
  103. package/dist/repositories/test-case-execution.repository.ee.js +85 -0
  104. package/dist/repositories/test-case-execution.repository.ee.js.map +1 -0
  105. package/dist/repositories/test-definition.repository.ee.d.ts +17 -0
  106. package/dist/repositories/test-definition.repository.ee.js +75 -0
  107. package/dist/repositories/test-definition.repository.ee.js.map +1 -0
  108. package/dist/repositories/test-metric.repository.ee.d.ts +5 -0
  109. package/dist/repositories/test-metric.repository.ee.js +26 -0
  110. package/dist/repositories/test-metric.repository.ee.js.map +1 -0
  111. package/dist/repositories/test-run.repository.ee.d.ts +37 -0
  112. package/dist/repositories/test-run.repository.ee.js +97 -0
  113. package/dist/repositories/test-run.repository.ee.js.map +1 -0
  114. package/dist/repositories/variables.repository.d.ts +5 -0
  115. package/dist/repositories/variables.repository.js +26 -0
  116. package/dist/repositories/variables.repository.js.map +1 -0
  117. package/dist/repositories/workflow-history.repository.d.ts +6 -0
  118. package/dist/repositories/workflow-history.repository.js +29 -0
  119. package/dist/repositories/workflow-history.repository.js.map +1 -0
  120. package/dist/repositories/workflow-statistics.repository.d.ts +15 -0
  121. package/dist/repositories/workflow-statistics.repository.js +123 -0
  122. package/dist/repositories/workflow-statistics.repository.js.map +1 -0
  123. package/dist/repositories/workflow-tag-mapping.repository.d.ts +6 -0
  124. package/dist/repositories/workflow-tag-mapping.repository.js +33 -0
  125. package/dist/repositories/workflow-tag-mapping.repository.js.map +1 -0
  126. package/dist/utils/get-final-test-result.d.ts +3 -0
  127. package/dist/utils/get-final-test-result.js +21 -0
  128. package/dist/utils/get-final-test-result.js.map +1 -0
  129. package/package.json +8 -5
@@ -0,0 +1,695 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.ExecutionRepository = void 0;
16
+ const config_1 = require("@n8n/config");
17
+ const di_1 = require("@n8n/di");
18
+ const typeorm_1 = require("@n8n/typeorm");
19
+ const DateUtils_1 = require("@n8n/typeorm/util/DateUtils");
20
+ const flatted_1 = require("flatted");
21
+ const pick_1 = __importDefault(require("lodash/pick"));
22
+ const n8n_core_1 = require("n8n-core");
23
+ const n8n_workflow_1 = require("n8n-workflow");
24
+ const execution_data_repository_1 = require("./execution-data.repository");
25
+ const entities_1 = require("../entities");
26
+ const separate_1 = require("../utils/separate");
27
+ class PostgresLiveRowsRetrievalError extends n8n_workflow_1.UnexpectedError {
28
+ constructor(rows) {
29
+ super('Failed to retrieve live execution rows in Postgres', { extra: { rows } });
30
+ }
31
+ }
32
+ function parseFiltersToQueryBuilder(qb, filters) {
33
+ if (filters?.status) {
34
+ qb.andWhere('execution.status IN (:...workflowStatus)', {
35
+ workflowStatus: filters.status,
36
+ });
37
+ }
38
+ if (filters?.finished) {
39
+ qb.andWhere({ finished: filters.finished });
40
+ }
41
+ if (filters?.metadata) {
42
+ qb.leftJoin(entities_1.ExecutionMetadata, 'md', 'md.executionId = execution.id');
43
+ for (const md of filters.metadata) {
44
+ qb.andWhere('md.key = :key AND md.value = :value', md);
45
+ }
46
+ }
47
+ if (filters?.startedAfter) {
48
+ qb.andWhere({
49
+ startedAt: (0, typeorm_1.MoreThanOrEqual)(DateUtils_1.DateUtils.mixedDateToUtcDatetimeString(new Date(filters.startedAfter))),
50
+ });
51
+ }
52
+ if (filters?.startedBefore) {
53
+ qb.andWhere({
54
+ startedAt: (0, typeorm_1.LessThanOrEqual)(DateUtils_1.DateUtils.mixedDateToUtcDatetimeString(new Date(filters.startedBefore))),
55
+ });
56
+ }
57
+ if (filters?.workflowId) {
58
+ qb.andWhere({
59
+ workflowId: filters.workflowId,
60
+ });
61
+ }
62
+ }
63
+ const lessThanOrEqual = (date) => {
64
+ return (0, typeorm_1.LessThanOrEqual)(DateUtils_1.DateUtils.mixedDateToUtcDatetimeString(new Date(date)));
65
+ };
66
+ const moreThanOrEqual = (date) => {
67
+ return (0, typeorm_1.MoreThanOrEqual)(DateUtils_1.DateUtils.mixedDateToUtcDatetimeString(new Date(date)));
68
+ };
69
+ let ExecutionRepository = class ExecutionRepository extends typeorm_1.Repository {
70
+ constructor(dataSource, globalConfig, logger, errorReporter, executionDataRepository, binaryDataService) {
71
+ super(entities_1.ExecutionEntity, dataSource.manager);
72
+ this.globalConfig = globalConfig;
73
+ this.logger = logger;
74
+ this.errorReporter = errorReporter;
75
+ this.executionDataRepository = executionDataRepository;
76
+ this.binaryDataService = binaryDataService;
77
+ this.hardDeletionBatchSize = 100;
78
+ this.summaryFields = {
79
+ id: true,
80
+ workflowId: true,
81
+ mode: true,
82
+ retryOf: true,
83
+ status: true,
84
+ createdAt: true,
85
+ startedAt: true,
86
+ stoppedAt: true,
87
+ };
88
+ this.annotationFields = {
89
+ id: true,
90
+ vote: true,
91
+ };
92
+ }
93
+ async findMultipleExecutions(queryParams, options) {
94
+ if (options?.includeData) {
95
+ if (!queryParams.relations) {
96
+ queryParams.relations = [];
97
+ }
98
+ if (Array.isArray(queryParams.relations)) {
99
+ queryParams.relations.push('executionData', 'metadata');
100
+ }
101
+ else {
102
+ queryParams.relations.executionData = true;
103
+ queryParams.relations.metadata = true;
104
+ }
105
+ }
106
+ const executions = await this.find(queryParams);
107
+ if (options?.includeData && options?.unflattenData) {
108
+ const [valid, invalid] = (0, separate_1.separate)(executions, (e) => e.executionData !== null);
109
+ this.reportInvalidExecutions(invalid);
110
+ return valid.map((execution) => {
111
+ const { executionData, metadata, ...rest } = execution;
112
+ return {
113
+ ...rest,
114
+ data: (0, flatted_1.parse)(executionData.data),
115
+ workflowData: executionData.workflowData,
116
+ customData: Object.fromEntries(metadata.map((m) => [m.key, m.value])),
117
+ };
118
+ });
119
+ }
120
+ else if (options?.includeData) {
121
+ const [valid, invalid] = (0, separate_1.separate)(executions, (e) => e.executionData !== null);
122
+ this.reportInvalidExecutions(invalid);
123
+ return valid.map((execution) => {
124
+ const { executionData, metadata, ...rest } = execution;
125
+ return {
126
+ ...rest,
127
+ data: execution.executionData.data,
128
+ workflowData: execution.executionData.workflowData,
129
+ customData: Object.fromEntries(metadata.map((m) => [m.key, m.value])),
130
+ };
131
+ });
132
+ }
133
+ return executions.map((execution) => {
134
+ const { executionData, ...rest } = execution;
135
+ return rest;
136
+ });
137
+ }
138
+ reportInvalidExecutions(executions) {
139
+ if (executions.length === 0)
140
+ return;
141
+ this.errorReporter.error(new n8n_workflow_1.UnexpectedError('Found executions without executionData', {
142
+ extra: { executionIds: executions.map(({ id }) => id) },
143
+ }));
144
+ }
145
+ serializeAnnotation(annotation) {
146
+ if (!annotation)
147
+ return null;
148
+ const { id, vote, tags } = annotation;
149
+ return {
150
+ id,
151
+ vote,
152
+ tags: tags?.map((tag) => (0, pick_1.default)(tag, ['id', 'name'])) ?? [],
153
+ };
154
+ }
155
+ async findSingleExecution(id, options) {
156
+ const findOptions = {
157
+ where: {
158
+ id,
159
+ ...options?.where,
160
+ },
161
+ };
162
+ if (options?.includeData) {
163
+ findOptions.relations = { executionData: true, metadata: true };
164
+ }
165
+ if (options?.includeAnnotation) {
166
+ findOptions.relations = {
167
+ ...findOptions.relations,
168
+ annotation: {
169
+ tags: true,
170
+ },
171
+ };
172
+ }
173
+ const execution = await this.findOne(findOptions);
174
+ if (!execution) {
175
+ return undefined;
176
+ }
177
+ const { executionData, metadata, annotation, ...rest } = execution;
178
+ const serializedAnnotation = this.serializeAnnotation(annotation);
179
+ if (execution.status === 'success' && executionData?.data === '[]') {
180
+ this.errorReporter.error('Found successful execution where data is empty stringified array', {
181
+ extra: {
182
+ executionId: execution.id,
183
+ workflowId: executionData?.workflowData.id,
184
+ },
185
+ });
186
+ }
187
+ return {
188
+ ...rest,
189
+ ...(options?.includeData && {
190
+ data: options?.unflattenData
191
+ ? (0, flatted_1.parse)(executionData.data)
192
+ : executionData.data,
193
+ workflowData: executionData?.workflowData,
194
+ customData: Object.fromEntries(metadata.map((m) => [m.key, m.value])),
195
+ }),
196
+ ...(options?.includeAnnotation &&
197
+ serializedAnnotation && { annotation: serializedAnnotation }),
198
+ };
199
+ }
200
+ async createNewExecution(execution) {
201
+ const { data: dataObj, workflowData: currentWorkflow, ...rest } = execution;
202
+ const { connections, nodes, name, settings } = currentWorkflow ?? {};
203
+ const workflowData = { connections, nodes, name, settings, id: currentWorkflow.id };
204
+ const data = (0, flatted_1.stringify)(dataObj);
205
+ const { type: dbType, sqlite: sqliteConfig } = this.globalConfig.database;
206
+ if (dbType === 'sqlite' && sqliteConfig.poolSize === 0) {
207
+ const { identifiers: inserted } = await this.insert({ ...rest, createdAt: new Date() });
208
+ const { id: executionId } = inserted[0];
209
+ await this.executionDataRepository.insert({ executionId, workflowData, data });
210
+ return String(executionId);
211
+ }
212
+ else {
213
+ return await this.manager.transaction(async (transactionManager) => {
214
+ const { identifiers: inserted } = await transactionManager.insert(entities_1.ExecutionEntity, {
215
+ ...rest,
216
+ createdAt: new Date(),
217
+ });
218
+ const { id: executionId } = inserted[0];
219
+ await this.executionDataRepository.createExecutionDataForExecution({ executionId, workflowData, data }, transactionManager);
220
+ return String(executionId);
221
+ });
222
+ }
223
+ }
224
+ async markAsCrashed(executionIds) {
225
+ if (!Array.isArray(executionIds))
226
+ executionIds = [executionIds];
227
+ await this.update({ id: (0, typeorm_1.In)(executionIds) }, {
228
+ status: 'crashed',
229
+ stoppedAt: new Date(),
230
+ });
231
+ this.logger.info('Marked executions as `crashed`', { executionIds });
232
+ }
233
+ async hardDelete(ids) {
234
+ return await Promise.all([
235
+ this.delete(ids.executionId),
236
+ this.binaryDataService.deleteMany([ids]),
237
+ ]);
238
+ }
239
+ async setRunning(executionId) {
240
+ const startedAt = new Date();
241
+ await this.update({ id: executionId }, { status: 'running', startedAt });
242
+ return startedAt;
243
+ }
244
+ async updateExistingExecution(executionId, execution) {
245
+ const { id, data, workflowId, workflowData, createdAt, startedAt, customData, ...executionInformation } = execution;
246
+ const executionData = {};
247
+ if (workflowData)
248
+ executionData.workflowData = workflowData;
249
+ if (data)
250
+ executionData.data = (0, flatted_1.stringify)(data);
251
+ const { type: dbType, sqlite: sqliteConfig } = this.globalConfig.database;
252
+ if (dbType === 'sqlite' && sqliteConfig.poolSize === 0) {
253
+ if (Object.keys(executionInformation).length > 0) {
254
+ await this.update({ id: executionId }, executionInformation);
255
+ }
256
+ if (Object.keys(executionData).length > 0) {
257
+ await this.executionDataRepository.update({ executionId }, executionData);
258
+ }
259
+ return;
260
+ }
261
+ await this.manager.transaction(async (tx) => {
262
+ if (Object.keys(executionInformation).length > 0) {
263
+ await tx.update(entities_1.ExecutionEntity, { id: executionId }, executionInformation);
264
+ }
265
+ if (Object.keys(executionData).length > 0) {
266
+ await tx.update(entities_1.ExecutionData, { executionId }, executionData);
267
+ }
268
+ });
269
+ }
270
+ async deleteExecutionsByFilter(filters, accessibleWorkflowIds, deleteConditions) {
271
+ if (!deleteConditions?.deleteBefore && !deleteConditions?.ids) {
272
+ throw new n8n_workflow_1.UnexpectedError('Either "deleteBefore" or "ids" must be present in the request body');
273
+ }
274
+ const query = this.createQueryBuilder('execution')
275
+ .select(['execution.id', 'execution.workflowId'])
276
+ .andWhere('execution.workflowId IN (:...accessibleWorkflowIds)', { accessibleWorkflowIds });
277
+ if (deleteConditions.deleteBefore) {
278
+ query.andWhere('execution.startedAt <= :deleteBefore', {
279
+ deleteBefore: deleteConditions.deleteBefore,
280
+ });
281
+ parseFiltersToQueryBuilder(query, filters);
282
+ }
283
+ else if (deleteConditions.ids) {
284
+ query.andWhere('execution.id IN (:...executionIds)', { executionIds: deleteConditions.ids });
285
+ }
286
+ const executions = await query.getMany();
287
+ if (!executions.length) {
288
+ if (deleteConditions.ids) {
289
+ this.logger.error('Failed to delete an execution due to insufficient permissions', {
290
+ executionIds: deleteConditions.ids,
291
+ });
292
+ }
293
+ return;
294
+ }
295
+ const ids = executions.map(({ id, workflowId }) => ({
296
+ executionId: id,
297
+ workflowId,
298
+ }));
299
+ do {
300
+ const batch = ids.splice(0, this.hardDeletionBatchSize);
301
+ await Promise.all([
302
+ this.delete(batch.map(({ executionId }) => executionId)),
303
+ this.binaryDataService.deleteMany(batch),
304
+ ]);
305
+ } while (ids.length > 0);
306
+ }
307
+ async getIdsSince(date) {
308
+ return await this.find({
309
+ select: ['id'],
310
+ where: {
311
+ startedAt: (0, typeorm_1.MoreThanOrEqual)(DateUtils_1.DateUtils.mixedDateToUtcDatetimeString(date)),
312
+ },
313
+ }).then((executions) => executions.map(({ id }) => id));
314
+ }
315
+ async softDeletePrunableExecutions() {
316
+ const { pruneDataMaxAge, pruneDataMaxCount } = this.globalConfig.executions;
317
+ const annotatedExecutionsSubQuery = this.manager
318
+ .createQueryBuilder()
319
+ .subQuery()
320
+ .select('annotation.executionId')
321
+ .from(entities_1.ExecutionAnnotation, 'annotation');
322
+ const date = new Date();
323
+ date.setHours(date.getHours() - pruneDataMaxAge);
324
+ const toPrune = [
325
+ { stoppedAt: (0, typeorm_1.LessThanOrEqual)(DateUtils_1.DateUtils.mixedDateToUtcDatetimeString(date)) },
326
+ ];
327
+ if (pruneDataMaxCount > 0) {
328
+ const executions = await this.createQueryBuilder('execution')
329
+ .select('execution.id')
330
+ .where('execution.id NOT IN ' + annotatedExecutionsSubQuery.getQuery())
331
+ .skip(pruneDataMaxCount)
332
+ .take(1)
333
+ .orderBy('execution.id', 'DESC')
334
+ .getMany();
335
+ if (executions[0]) {
336
+ toPrune.push({ id: (0, typeorm_1.LessThanOrEqual)(executions[0].id) });
337
+ }
338
+ }
339
+ const [timeBasedWhere, countBasedWhere] = toPrune;
340
+ return await this.createQueryBuilder()
341
+ .update(entities_1.ExecutionEntity)
342
+ .set({ deletedAt: new Date() })
343
+ .where({
344
+ deletedAt: (0, typeorm_1.IsNull)(),
345
+ status: (0, typeorm_1.Not)((0, typeorm_1.In)(['new', 'running', 'waiting'])),
346
+ })
347
+ .andWhere('id NOT IN ' + annotatedExecutionsSubQuery.getQuery())
348
+ .andWhere(new typeorm_1.Brackets((qb) => countBasedWhere
349
+ ? qb.where(timeBasedWhere).orWhere(countBasedWhere)
350
+ : qb.where(timeBasedWhere)))
351
+ .execute();
352
+ }
353
+ async findSoftDeletedExecutions() {
354
+ const date = new Date();
355
+ date.setHours(date.getHours() - this.globalConfig.executions.pruneDataHardDeleteBuffer);
356
+ const workflowIdsAndExecutionIds = (await this.find({
357
+ select: ['workflowId', 'id'],
358
+ where: {
359
+ deletedAt: (0, typeorm_1.LessThanOrEqual)(DateUtils_1.DateUtils.mixedDateToUtcDatetimeString(date)),
360
+ },
361
+ take: this.hardDeletionBatchSize,
362
+ withDeleted: true,
363
+ })).map(({ id: executionId, workflowId }) => ({ workflowId, executionId }));
364
+ return workflowIdsAndExecutionIds;
365
+ }
366
+ async deleteByIds(executionIds) {
367
+ return await this.delete({ id: (0, typeorm_1.In)(executionIds) });
368
+ }
369
+ async getWaitingExecutions() {
370
+ const waitTill = new Date(Date.now() + 70000);
371
+ const where = {
372
+ waitTill: (0, typeorm_1.LessThanOrEqual)(waitTill),
373
+ status: (0, typeorm_1.Not)('crashed'),
374
+ };
375
+ const dbType = this.globalConfig.database.type;
376
+ if (dbType === 'sqlite') {
377
+ where.waitTill = (0, typeorm_1.LessThanOrEqual)(DateUtils_1.DateUtils.mixedDateToUtcDatetimeString(waitTill));
378
+ }
379
+ return await this.findMultipleExecutions({
380
+ select: ['id', 'waitTill'],
381
+ where,
382
+ order: {
383
+ waitTill: 'ASC',
384
+ },
385
+ });
386
+ }
387
+ async getExecutionsCountForPublicApi(data) {
388
+ const executions = await this.count({
389
+ where: {
390
+ ...(data.lastId && { id: (0, typeorm_1.LessThan)(data.lastId) }),
391
+ ...(data.status && { ...this.getStatusCondition(data.status) }),
392
+ ...(data.workflowIds && { workflowId: (0, typeorm_1.In)(data.workflowIds) }),
393
+ ...(data.excludedWorkflowIds && { workflowId: (0, typeorm_1.Not)((0, typeorm_1.In)(data.excludedWorkflowIds)) }),
394
+ },
395
+ take: data.limit,
396
+ });
397
+ return executions;
398
+ }
399
+ getStatusCondition(status) {
400
+ const condition = {};
401
+ if (status === 'success') {
402
+ condition.status = 'success';
403
+ }
404
+ else if (status === 'waiting') {
405
+ condition.status = 'waiting';
406
+ }
407
+ else if (status === 'error') {
408
+ condition.status = (0, typeorm_1.In)(['error', 'crashed']);
409
+ }
410
+ return condition;
411
+ }
412
+ async getExecutionsForPublicApi(params) {
413
+ let where = {};
414
+ if (params.lastId && params.excludedExecutionsIds?.length) {
415
+ where.id = (0, typeorm_1.Raw)((id) => `${id} < :lastId AND ${id} NOT IN (:...excludedExecutionsIds)`, {
416
+ lastId: params.lastId,
417
+ excludedExecutionsIds: params.excludedExecutionsIds,
418
+ });
419
+ }
420
+ else if (params.lastId) {
421
+ where.id = (0, typeorm_1.LessThan)(params.lastId);
422
+ }
423
+ else if (params.excludedExecutionsIds?.length) {
424
+ where.id = (0, typeorm_1.Not)((0, typeorm_1.In)(params.excludedExecutionsIds));
425
+ }
426
+ if (params.status) {
427
+ where = { ...where, ...this.getStatusCondition(params.status) };
428
+ }
429
+ if (params.workflowIds) {
430
+ where = { ...where, workflowId: (0, typeorm_1.In)(params.workflowIds) };
431
+ }
432
+ return await this.findMultipleExecutions({
433
+ select: [
434
+ 'id',
435
+ 'mode',
436
+ 'retryOf',
437
+ 'retrySuccessId',
438
+ 'startedAt',
439
+ 'stoppedAt',
440
+ 'workflowId',
441
+ 'waitTill',
442
+ 'finished',
443
+ ],
444
+ where,
445
+ order: { id: 'DESC' },
446
+ take: params.limit,
447
+ relations: ['executionData'],
448
+ }, {
449
+ includeData: params.includeData,
450
+ unflattenData: true,
451
+ });
452
+ }
453
+ async getExecutionInWorkflowsForPublicApi(id, workflowIds, includeData) {
454
+ return await this.findSingleExecution(id, {
455
+ where: {
456
+ workflowId: (0, typeorm_1.In)(workflowIds),
457
+ },
458
+ includeData,
459
+ unflattenData: true,
460
+ });
461
+ }
462
+ async findWithUnflattenedData(executionId, accessibleWorkflowIds) {
463
+ return await this.findSingleExecution(executionId, {
464
+ where: {
465
+ workflowId: (0, typeorm_1.In)(accessibleWorkflowIds),
466
+ },
467
+ includeData: true,
468
+ unflattenData: true,
469
+ includeAnnotation: true,
470
+ });
471
+ }
472
+ async findIfShared(executionId, sharedWorkflowIds) {
473
+ return await this.findSingleExecution(executionId, {
474
+ where: {
475
+ workflowId: (0, typeorm_1.In)(sharedWorkflowIds),
476
+ },
477
+ includeData: true,
478
+ unflattenData: false,
479
+ includeAnnotation: true,
480
+ });
481
+ }
482
+ async findIfAccessible(executionId, accessibleWorkflowIds) {
483
+ return await this.findSingleExecution(executionId, {
484
+ where: { workflowId: (0, typeorm_1.In)(accessibleWorkflowIds) },
485
+ });
486
+ }
487
+ async stopBeforeRun(execution) {
488
+ execution.status = 'canceled';
489
+ execution.stoppedAt = new Date();
490
+ await this.update({ id: execution.id }, { status: execution.status, stoppedAt: execution.stoppedAt });
491
+ return execution;
492
+ }
493
+ async stopDuringRun(execution) {
494
+ const error = new n8n_workflow_1.ExecutionCancelledError(execution.id);
495
+ execution.data ??= { resultData: { runData: {} } };
496
+ execution.data.resultData.error = {
497
+ ...error,
498
+ message: error.message,
499
+ stack: error.stack,
500
+ };
501
+ execution.stoppedAt = new Date();
502
+ execution.waitTill = null;
503
+ execution.status = 'canceled';
504
+ await this.updateExistingExecution(execution.id, execution);
505
+ return execution;
506
+ }
507
+ async cancelMany(executionIds) {
508
+ await this.update({ id: (0, typeorm_1.In)(executionIds) }, { status: 'canceled', stoppedAt: new Date() });
509
+ }
510
+ reduceExecutionsWithAnnotations(rawExecutionsWithTags) {
511
+ return rawExecutionsWithTags.reduce((acc, { annotation_id: _, annotation_vote: vote, annotation_tags_id: tagId, annotation_tags_name: tagName, ...row }) => {
512
+ const existingExecution = acc.find((e) => e.id === row.id);
513
+ if (existingExecution) {
514
+ if (tagId) {
515
+ existingExecution.annotation = existingExecution.annotation ?? {
516
+ vote,
517
+ tags: [],
518
+ };
519
+ existingExecution.annotation.tags.push({ id: tagId, name: tagName });
520
+ }
521
+ }
522
+ else {
523
+ acc.push({
524
+ ...row,
525
+ annotation: {
526
+ vote,
527
+ tags: tagId ? [{ id: tagId, name: tagName }] : [],
528
+ },
529
+ });
530
+ }
531
+ return acc;
532
+ }, []);
533
+ }
534
+ async findManyByRangeQuery(query) {
535
+ if (query?.accessibleWorkflowIds?.length === 0) {
536
+ throw new n8n_workflow_1.UnexpectedError('Expected accessible workflow IDs');
537
+ }
538
+ const qb = this.toQueryBuilderWithAnnotations(query);
539
+ const rawExecutionsWithTags = await qb.getRawMany();
540
+ const executions = this.reduceExecutionsWithAnnotations(rawExecutionsWithTags);
541
+ return executions.map((execution) => this.toSummary(execution));
542
+ }
543
+ toSummary(execution) {
544
+ execution.id = execution.id.toString();
545
+ const normalizeDateString = (date) => {
546
+ if (date.includes(' '))
547
+ return date.replace(' ', 'T') + 'Z';
548
+ return date;
549
+ };
550
+ if (execution.createdAt) {
551
+ execution.createdAt =
552
+ execution.createdAt instanceof Date
553
+ ? execution.createdAt.toISOString()
554
+ : normalizeDateString(execution.createdAt);
555
+ }
556
+ if (execution.startedAt) {
557
+ execution.startedAt =
558
+ execution.startedAt instanceof Date
559
+ ? execution.startedAt.toISOString()
560
+ : normalizeDateString(execution.startedAt);
561
+ }
562
+ if (execution.waitTill) {
563
+ execution.waitTill =
564
+ execution.waitTill instanceof Date
565
+ ? execution.waitTill.toISOString()
566
+ : normalizeDateString(execution.waitTill);
567
+ }
568
+ if (execution.stoppedAt) {
569
+ execution.stoppedAt =
570
+ execution.stoppedAt instanceof Date
571
+ ? execution.stoppedAt.toISOString()
572
+ : normalizeDateString(execution.stoppedAt);
573
+ }
574
+ return execution;
575
+ }
576
+ async fetchCount(query) {
577
+ return await this.toQueryBuilder(query).getCount();
578
+ }
579
+ async getLiveExecutionRowsOnPostgres() {
580
+ const tableName = `${this.globalConfig.database.tablePrefix}execution_entity`;
581
+ const pgSql = `SELECT n_live_tup as result FROM pg_stat_all_tables WHERE relname = '${tableName}';`;
582
+ try {
583
+ const rows = (await this.query(pgSql));
584
+ if (rows.length !== 1)
585
+ throw new PostgresLiveRowsRetrievalError(rows);
586
+ const [row] = rows;
587
+ return parseInt(row.result, 10);
588
+ }
589
+ catch (error) {
590
+ if (error instanceof Error)
591
+ this.logger.error(error.message, { error });
592
+ return -1;
593
+ }
594
+ }
595
+ toQueryBuilder(query) {
596
+ const { accessibleWorkflowIds, status, finished, workflowId, startedBefore, startedAfter, metadata, annotationTags, vote, projectId, } = query;
597
+ const fields = Object.keys(this.summaryFields)
598
+ .concat(['waitTill', 'retrySuccessId'])
599
+ .map((key) => `execution.${key} AS "${key}"`)
600
+ .concat('workflow.name AS "workflowName"');
601
+ const qb = this.createQueryBuilder('execution')
602
+ .select(fields)
603
+ .innerJoin('execution.workflow', 'workflow')
604
+ .where('execution.workflowId IN (:...accessibleWorkflowIds)', { accessibleWorkflowIds });
605
+ if (query.kind === 'range') {
606
+ const { limit, firstId, lastId } = query.range;
607
+ qb.limit(limit);
608
+ if (firstId)
609
+ qb.andWhere('execution.id > :firstId', { firstId });
610
+ if (lastId)
611
+ qb.andWhere('execution.id < :lastId', { lastId });
612
+ if (query.order?.startedAt === 'DESC') {
613
+ qb.orderBy({ 'execution.startedAt': 'DESC' });
614
+ }
615
+ else if (query.order?.top) {
616
+ qb.orderBy(`(CASE WHEN execution.status = '${query.order.top}' THEN 0 ELSE 1 END)`);
617
+ }
618
+ else {
619
+ qb.orderBy({ 'execution.id': 'DESC' });
620
+ }
621
+ }
622
+ if (status)
623
+ qb.andWhere('execution.status IN (:...status)', { status });
624
+ if (finished)
625
+ qb.andWhere({ finished });
626
+ if (workflowId)
627
+ qb.andWhere({ workflowId });
628
+ if (startedBefore)
629
+ qb.andWhere({ startedAt: lessThanOrEqual(startedBefore) });
630
+ if (startedAfter)
631
+ qb.andWhere({ startedAt: moreThanOrEqual(startedAfter) });
632
+ if (metadata?.length === 1) {
633
+ const [{ key, value }] = metadata;
634
+ qb.innerJoin(entities_1.ExecutionMetadata, 'md', 'md.executionId = execution.id AND md.key = :key AND md.value = :value');
635
+ qb.setParameter('key', key);
636
+ qb.setParameter('value', value);
637
+ }
638
+ if (annotationTags?.length || vote) {
639
+ qb.innerJoin('execution.annotation', 'annotation');
640
+ if (annotationTags?.length) {
641
+ for (let index = 0; index < annotationTags.length; index++) {
642
+ qb.innerJoin(entities_1.AnnotationTagMapping, `atm_${index}`, `atm_${index}.annotationId = annotation.id AND atm_${index}.tagId = :tagId_${index}`);
643
+ qb.setParameter(`tagId_${index}`, annotationTags[index]);
644
+ }
645
+ }
646
+ if (vote) {
647
+ qb.andWhere('annotation.vote = :vote', { vote });
648
+ }
649
+ }
650
+ if (projectId) {
651
+ qb.innerJoin(entities_1.WorkflowEntity, 'w', 'w.id = execution.workflowId')
652
+ .innerJoin(entities_1.SharedWorkflow, 'sw', 'sw.workflowId = w.id')
653
+ .andWhere('sw.projectId = :projectId', { projectId });
654
+ }
655
+ return qb;
656
+ }
657
+ toQueryBuilderWithAnnotations(query) {
658
+ const annotationFields = Object.keys(this.annotationFields).map((key) => `annotation.${key} AS "annotation_${key}"`);
659
+ const subQuery = this.toQueryBuilder(query).addSelect(annotationFields);
660
+ if (!subQuery.expressionMap.joinAttributes.some((join) => join.alias.name === 'annotation')) {
661
+ subQuery.leftJoin('execution.annotation', 'annotation');
662
+ }
663
+ return this.manager
664
+ .createQueryBuilder()
665
+ .select(['e.*', 'ate.id AS "annotation_tags_id"', 'ate.name AS "annotation_tags_name"'])
666
+ .from(`(${subQuery.getQuery()})`, 'e')
667
+ .setParameters(subQuery.getParameters())
668
+ .leftJoin(entities_1.AnnotationTagMapping, 'atm', 'atm.annotationId = e.annotation_id')
669
+ .leftJoin(entities_1.AnnotationTagEntity, 'ate', 'ate.id = atm.tagId');
670
+ }
671
+ async getAllIds() {
672
+ const executions = await this.find({ select: ['id'], order: { id: 'ASC' } });
673
+ return executions.map(({ id }) => id);
674
+ }
675
+ async getInProgressExecutionIds(batchSize) {
676
+ const executions = await this.find({
677
+ select: ['id'],
678
+ where: { status: (0, typeorm_1.In)(['new', 'running']) },
679
+ order: { startedAt: 'DESC' },
680
+ take: batchSize,
681
+ });
682
+ return executions.map(({ id }) => id);
683
+ }
684
+ };
685
+ exports.ExecutionRepository = ExecutionRepository;
686
+ exports.ExecutionRepository = ExecutionRepository = __decorate([
687
+ (0, di_1.Service)(),
688
+ __metadata("design:paramtypes", [typeorm_1.DataSource,
689
+ config_1.GlobalConfig,
690
+ n8n_core_1.Logger,
691
+ n8n_core_1.ErrorReporter,
692
+ execution_data_repository_1.ExecutionDataRepository,
693
+ n8n_core_1.BinaryDataService])
694
+ ], ExecutionRepository);
695
+ //# sourceMappingURL=execution.repository.js.map