@crowdin/app-project-module 0.39.1 → 0.41.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 (46) hide show
  1. package/out/handlers/integration/crowdin-update.js +36 -17
  2. package/out/handlers/integration/integration-data.js +2 -0
  3. package/out/handlers/integration/integration-update.js +35 -17
  4. package/out/handlers/integration/integration-webhook.js +1 -1
  5. package/out/handlers/integration/job-cancel.d.ts +3 -0
  6. package/out/handlers/integration/job-cancel.js +28 -0
  7. package/out/handlers/integration/job-info.d.ts +3 -0
  8. package/out/handlers/integration/job-info.js +54 -0
  9. package/out/handlers/integration/main.js +6 -3
  10. package/out/handlers/integration/settings-save.js +8 -1
  11. package/out/handlers/integration/user-errors.d.ts +3 -0
  12. package/out/handlers/{user-errors.js → integration/user-errors.js} +2 -2
  13. package/out/handlers/uninstall.js +4 -2
  14. package/out/index.d.ts +2 -1
  15. package/out/index.js +35 -27
  16. package/out/middlewares/crowdin-client.js +1 -0
  17. package/out/middlewares/integration-credentials.js +3 -2
  18. package/out/middlewares/ui-module.js +1 -0
  19. package/out/models/index.d.ts +54 -18
  20. package/out/models/index.js +1 -0
  21. package/out/models/job.d.ts +44 -0
  22. package/out/models/job.js +16 -0
  23. package/out/static/js/form.js +13 -13
  24. package/out/static/js/main.js +1 -1
  25. package/out/storage/index.d.ts +11 -2
  26. package/out/storage/index.js +3 -0
  27. package/out/storage/mysql.d.ts +11 -2
  28. package/out/storage/mysql.js +155 -10
  29. package/out/storage/postgre.d.ts +11 -2
  30. package/out/storage/postgre.js +153 -9
  31. package/out/storage/sqlite.d.ts +14 -3
  32. package/out/storage/sqlite.js +160 -14
  33. package/out/util/cron.d.ts +1 -0
  34. package/out/util/cron.js +53 -6
  35. package/out/util/defaults.js +4 -11
  36. package/out/util/file-snapshot.js +2 -0
  37. package/out/util/files.d.ts +2 -1
  38. package/out/util/files.js +19 -1
  39. package/out/util/index.js +3 -1
  40. package/out/util/job.d.ts +12 -0
  41. package/out/util/job.js +88 -0
  42. package/out/util/logger.js +4 -0
  43. package/out/util/webhooks.js +53 -6
  44. package/out/views/main.handlebars +153 -5
  45. package/package.json +16 -15
  46. package/out/handlers/user-errors.d.ts +0 -3
@@ -15,6 +15,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
15
15
  Object.defineProperty(exports, "__esModule", { value: true });
16
16
  exports.SQLiteStorage = void 0;
17
17
  const path_1 = require("path");
18
+ const uuid_1 = require("uuid");
19
+ const job_1 = require("../models/job");
18
20
  class SQLiteStorage {
19
21
  constructor(config) {
20
22
  this.dbPromise = new Promise((res, rej) => {
@@ -92,25 +94,45 @@ class SQLiteStorage {
92
94
  });
93
95
  });
94
96
  }
95
- updateTable(newColumns, tableName) {
97
+ removeColumns(column, tableName) {
98
+ return __awaiter(this, void 0, void 0, function* () {
99
+ const tableInfo = yield this.each(`PRAGMA table_info(${tableName});`, []);
100
+ const exists = tableInfo.some((columnInfo) => columnInfo.name === column);
101
+ if (exists) {
102
+ yield this.run(`ALTER TABLE ${tableName} DROP COLUMN ${column};`, []);
103
+ }
104
+ });
105
+ }
106
+ addColumns(columns, tableName) {
96
107
  return __awaiter(this, void 0, void 0, function* () {
97
108
  const tableInfo = yield this.each(`PRAGMA table_info(${tableName});`, []);
98
109
  //@ts-ignore
99
110
  tableInfo.map((columnInfo) => __awaiter(this, void 0, void 0, function* () {
100
- const index = newColumns.indexOf(columnInfo.name);
111
+ const index = columns.indexOf(columnInfo.name);
101
112
  if (~index) {
102
- newColumns.splice(index, 1);
113
+ columns.splice(index, 1);
103
114
  }
104
115
  }));
105
- for (const column of newColumns) {
116
+ for (const column of columns) {
106
117
  yield this.run(`ALTER TABLE ${tableName} ADD COLUMN ${column} varchar null;`, []);
107
118
  }
108
119
  });
109
120
  }
110
- addColumns() {
121
+ updateTables() {
111
122
  return __awaiter(this, void 0, void 0, function* () {
112
- yield this.updateTable(['app_secret', 'domain', 'user_id', 'organization_id', 'base_url'], 'crowdin_credentials');
113
- yield this.updateTable(['crowdin_id'], 'app_metadata');
123
+ yield this.addColumns(['app_secret', 'domain', 'user_id', 'organization_id', 'base_url'], 'crowdin_credentials');
124
+ yield this.addColumns(['crowdin_id'], 'app_metadata');
125
+ });
126
+ }
127
+ moveIntegrationSettings() {
128
+ return __awaiter(this, void 0, void 0, function* () {
129
+ const integrationCredentials = yield this.each('SELECT * FROM integration_credentials', []);
130
+ for (const credentials of integrationCredentials) {
131
+ if (credentials.config) {
132
+ yield this.saveIntegrationConfig(credentials.id, credentials.crowdin_id, credentials.config);
133
+ }
134
+ }
135
+ yield this.removeColumns('config', 'integration_credentials');
114
136
  });
115
137
  }
116
138
  migrate() {
@@ -153,7 +175,6 @@ class SQLiteStorage {
153
175
  (
154
176
  id varchar not null primary key,
155
177
  credentials varchar not null,
156
- config varchar null,
157
178
  crowdin_id varchar not null
158
179
  );
159
180
  `, []);
@@ -207,10 +228,38 @@ class SQLiteStorage {
207
228
  crowdin_id varchar not null,
208
229
  integration_id varchar null
209
230
  );
231
+ `, []);
232
+ yield this._run(`
233
+ create table if not exists integration_settings
234
+ (
235
+ id integer not null primary key autoincrement,
236
+ integration_id varchar not null,
237
+ crowdin_id varchar not null,
238
+ config varchar null
239
+ );
240
+ `, []);
241
+ yield this._run(`
242
+ create table if not exists job
243
+ (
244
+ id varchar not null primary key,
245
+ integration_id varchar not null,
246
+ crowdin_id varchar not null,
247
+ type varchar not null,
248
+ title varchar null,
249
+ progress integer DEFAULT 0,
250
+ status varchar DEFAULT '${job_1.JobStatus.CREATED}',
251
+ payload varchar null,
252
+ info varchar null,
253
+ data varchar null,
254
+ created_at varchar not null,
255
+ updated_at varchar null,
256
+ finished_at varchar null
257
+ );
210
258
  `, []);
211
259
  this._res && this._res();
212
260
  // TODO: temporary code
213
- yield this.addColumns();
261
+ yield this.updateTables();
262
+ yield this.moveIntegrationSettings();
214
263
  }
215
264
  catch (e) {
216
265
  this._rej && this._rej(e);
@@ -264,6 +313,8 @@ class SQLiteStorage {
264
313
  yield this.run('DELETE FROM files_snapshot WHERE crowdin_id = ?', [id]);
265
314
  yield this.run('DELETE FROM webhooks WHERE crowdin_id = ?', [id]);
266
315
  yield this.run('DELETE FROM user_errors WHERE crowdin_id = ?', [id]);
316
+ yield this.run('DELETE FROM integration_settings WHERE crowdin_id = ?', [id]);
317
+ yield this.run('DELETE FROM job WHERE crowdin_id = ?', [id]);
267
318
  });
268
319
  }
269
320
  saveIntegrationCredentials(id, credentials, crowdinId) {
@@ -276,19 +327,16 @@ class SQLiteStorage {
276
327
  updateIntegrationCredentials(id, credentials) {
277
328
  return this.run('UPDATE integration_credentials SET credentials = ? WHERE id = ?', [credentials, id]);
278
329
  }
279
- updateIntegrationConfig(id, config) {
280
- return this.run('UPDATE integration_credentials SET config = ? WHERE id = ?', [config, id]);
281
- }
282
330
  getIntegrationCredentials(id) {
283
331
  return __awaiter(this, void 0, void 0, function* () {
284
- const row = yield this.get('SELECT id, credentials, config, crowdin_id as crowdinId FROM integration_credentials WHERE id = ?', [id]);
332
+ const row = yield this.get('SELECT id, credentials, crowdin_id as crowdinId FROM integration_credentials WHERE id = ?', [id]);
285
333
  if (row) {
286
334
  return row;
287
335
  }
288
336
  });
289
337
  }
290
338
  getAllIntegrationCredentials(crowdinId) {
291
- return this.each('SELECT id, credentials, config, crowdin_id as crowdinId FROM integration_credentials WHERE crowdin_id = ?', [crowdinId]);
339
+ return this.each('SELECT id, credentials, crowdin_id as crowdinId FROM integration_credentials WHERE crowdin_id = ?', [crowdinId]);
292
340
  }
293
341
  deleteIntegrationCredentials(id) {
294
342
  return __awaiter(this, void 0, void 0, function* () {
@@ -296,6 +344,7 @@ class SQLiteStorage {
296
344
  yield this.run('DELETE FROM sync_settings where integration_id = ?', [id]);
297
345
  yield this.run('DELETE FROM files_snapshot where integration_id = ?', [id]);
298
346
  yield this.run('DELETE FROM webhooks where integration_id = ?', [id]);
347
+ yield this.run('DELETE FROM job where integration_id = ?', [id]);
299
348
  });
300
349
  }
301
350
  deleteAllIntegrationCredentials(crowdinId) {
@@ -305,6 +354,7 @@ class SQLiteStorage {
305
354
  yield this.run('DELETE FROM files_snapshot where crowdin_id = ?', [crowdinId]);
306
355
  yield this.run('DELETE FROM webhooks where crowdin_id = ?', [crowdinId]);
307
356
  yield this.run('DELETE FROM user_errors where crowdin_id = ?', [crowdinId]);
357
+ yield this.run('DELETE FROM job where crowdin_id = ?', [crowdinId]);
308
358
  });
309
359
  }
310
360
  saveMetadata(id, metadata, crowdinId) {
@@ -426,5 +476,101 @@ class SQLiteStorage {
426
476
  return this.run(`DELETE FROM user_errors WHERE created_at < ? AND crowdin_id = ? AND ${whereIntegrationCondition}`, params);
427
477
  });
428
478
  }
479
+ saveIntegrationConfig(integrationId, crowdinId, config) {
480
+ return this.run('INSERT INTO integration_settings(integration_id, crowdin_id, config) VALUES (?, ?, ?)', [
481
+ integrationId,
482
+ crowdinId,
483
+ config,
484
+ ]);
485
+ }
486
+ getAllIntegrationConfigs(crowdinId) {
487
+ return this.each('SELECT config, integration_id as integrationId FROM integration_settings WHERE crowdin_id = ?', [crowdinId]);
488
+ }
489
+ getIntegrationConfig(integrationId) {
490
+ return __awaiter(this, void 0, void 0, function* () {
491
+ const row = yield this.get('SELECT config FROM integration_settings WHERE integration_id = ?', [
492
+ integrationId,
493
+ ]);
494
+ if (row) {
495
+ return row;
496
+ }
497
+ });
498
+ }
499
+ updateIntegrationConfig(integrationId, config) {
500
+ return this.run('UPDATE integration_settings SET config = ? WHERE integration_id = ?', [config, integrationId]);
501
+ }
502
+ createJob({ integrationId, crowdinId, type, title, payload }) {
503
+ return __awaiter(this, void 0, void 0, function* () {
504
+ const id = (0, uuid_1.v4)();
505
+ yield this.run(`
506
+ INSERT
507
+ INTO job(id, integration_id, crowdin_id, type, payload, title, created_at)
508
+ VALUES (?, ?, ?, ?, ?, ?, ?)
509
+ `, [id, integrationId, crowdinId, type, payload, title, Date.now().toString()]);
510
+ return id;
511
+ });
512
+ }
513
+ updateJob({ id, progress, status, info, data }) {
514
+ const updateFields = ['updated_at = ?'];
515
+ const updateParams = [Date.now().toString()];
516
+ if (progress) {
517
+ updateFields.push('progress = ?');
518
+ updateParams.push(Math.round(progress));
519
+ if (progress >= 100) {
520
+ updateFields.push('finished_at = ?');
521
+ updateParams.push(Date.now().toString());
522
+ }
523
+ }
524
+ if (status) {
525
+ updateFields.push('status = ?');
526
+ updateParams.push(status);
527
+ if (!updateFields.includes('finished_at = ?') && [job_1.JobStatus.FAILED, job_1.JobStatus.CANCELED].includes(status)) {
528
+ updateFields.push('finished_at = ?');
529
+ updateParams.push(Date.now().toString());
530
+ }
531
+ }
532
+ if (data) {
533
+ updateFields.push('data = ?');
534
+ updateParams.push(data);
535
+ }
536
+ if (info) {
537
+ updateFields.push('info = ?');
538
+ updateParams.push(info);
539
+ }
540
+ updateParams.push(id);
541
+ const query = `
542
+ UPDATE job
543
+ SET ${updateFields.join(', ')}
544
+ WHERE id = ?
545
+ `;
546
+ return this.run(query, updateParams);
547
+ }
548
+ getJob({ id }) {
549
+ return __awaiter(this, void 0, void 0, function* () {
550
+ const row = yield this.get(`
551
+ SELECT id, integration_id as integrationId, crowdin_id as crowdinId, type, payload, progress, status,
552
+ title, info, payload, data, created_at as createdAt, updated_at as updatedAt, finished_at as finishedAt
553
+ FROM job
554
+ WHERE id = ?
555
+ `, [id]);
556
+ if (row) {
557
+ return row;
558
+ }
559
+ });
560
+ }
561
+ getActiveJobs({ integrationId, crowdinId }) {
562
+ return __awaiter(this, void 0, void 0, function* () {
563
+ return this.each(`
564
+ SELECT id, integration_id as integrationId, crowdin_id as crowdinId, type, payload, progress, status, title, info, payload, data, created_at as createdAt, updated_at as updatedAt, finished_at as finishedAt
565
+ FROM job
566
+ WHERE integration_id = ? AND crowdin_id = ? AND finished_at is NULL
567
+ `, [integrationId, crowdinId]);
568
+ });
569
+ }
570
+ deleteFinishedJobs() {
571
+ return __awaiter(this, void 0, void 0, function* () {
572
+ yield this.run('DELETE FROM job WHERE finished_at is not NULL', []);
573
+ });
574
+ }
429
575
  }
430
576
  exports.SQLiteStorage = SQLiteStorage;
@@ -4,3 +4,4 @@ export declare function runJob(config: Config, integration: IntegrationLogic, jo
4
4
  export declare function filesCron(config: Config, integration: IntegrationLogic, period: string): Promise<void>;
5
5
  export declare function skipFoldersFromIntegrationRequest(config: Config, integration: IntegrationLogic, projectId: number, crowdinFiles: UpdateIntegrationRequest, crowdinClient: Crowdin): Promise<UpdateIntegrationRequest>;
6
6
  export declare function createOrUpdateSyncSettings(config: Config, req: IntegrationRequest, files: any, provider: Provider, onlyCreate?: boolean): Promise<void>;
7
+ export declare function removeFinishedJobs(): Promise<void>;
package/out/util/cron.js CHANGED
@@ -32,14 +32,16 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
32
32
  });
33
33
  };
34
34
  Object.defineProperty(exports, "__esModule", { value: true });
35
- exports.createOrUpdateSyncSettings = exports.skipFoldersFromIntegrationRequest = exports.filesCron = exports.runJob = void 0;
35
+ exports.removeFinishedJobs = exports.createOrUpdateSyncSettings = exports.skipFoldersFromIntegrationRequest = exports.filesCron = exports.runJob = void 0;
36
36
  const crowdinAppFunctions = __importStar(require("@crowdin/crowdin-apps-functions"));
37
37
  const models_1 = require("../models");
38
+ const job_1 = require("../models/job");
38
39
  const storage_1 = require("../storage");
39
40
  const connection_1 = require("./connection");
40
41
  const defaults_1 = require("./defaults");
41
42
  const file_snapshot_1 = require("./file-snapshot");
42
43
  const logger_1 = require("./logger");
44
+ const job_2 = require("./job");
43
45
  function runJob(config, integration, job) {
44
46
  return __awaiter(this, void 0, void 0, function* () {
45
47
  (0, logger_1.log)(`Starting cron job with expression [${job.expression}]`);
@@ -61,11 +63,13 @@ function runJob(config, integration, job) {
61
63
  return;
62
64
  }
63
65
  const integrationCredentialsList = yield (0, storage_1.getStorage)().getAllIntegrationCredentials(crowdinCredentials.id);
66
+ const allIntegrationConfigs = yield (0, storage_1.getStorage)().getAllIntegrationConfigs(crowdinCredentials.id);
64
67
  for (const integrationCredentials of integrationCredentialsList) {
68
+ const integrationConfig = allIntegrationConfigs.find(({ integrationId }) => integrationId === integrationCredentials.id);
65
69
  const projectId = crowdinAppFunctions.getProjectId(integrationCredentials.id);
66
70
  const apiCredentials = yield (0, connection_1.prepareIntegrationCredentials)(config, integration, integrationCredentials);
67
71
  const rootFolder = yield (0, defaults_1.getRootFolder)(config, integration, crowdinClient, projectId);
68
- const intConfig = integrationCredentials.config ? JSON.parse(integrationCredentials.config) : undefined;
72
+ const intConfig = (integrationConfig === null || integrationConfig === void 0 ? void 0 : integrationConfig.config) ? JSON.parse(integrationConfig.config) : undefined;
69
73
  (0, logger_1.log)(`Executing task for cron job with expression [${job.expression}] for project ${projectId}`);
70
74
  yield job.task(projectId, crowdinClient, apiCredentials, rootFolder, intConfig);
71
75
  (0, logger_1.log)(`Task for cron job with expression [${job.expression}] for project ${projectId} completed`);
@@ -85,11 +89,12 @@ function filesCron(config, integration, period) {
85
89
  let newFiles = [];
86
90
  const crowdinCredentials = yield (0, storage_1.getStorage)().getCrowdinCredentials(syncSettings.crowdinId);
87
91
  const integrationCredentials = yield (0, storage_1.getStorage)().getIntegrationCredentials(syncSettings.integrationId);
92
+ const integrationConfig = yield (0, storage_1.getStorage)().getIntegrationConfig(syncSettings.integrationId);
88
93
  if (!crowdinCredentials || !integrationCredentials) {
89
94
  return;
90
95
  }
91
- const intConfig = integrationCredentials.config
92
- ? JSON.parse(integrationCredentials.config)
96
+ const intConfig = (integrationConfig === null || integrationConfig === void 0 ? void 0 : integrationConfig.config)
97
+ ? JSON.parse(integrationConfig.config)
93
98
  : { schedule: '0', condition: '0' };
94
99
  if (period !== intConfig.schedule) {
95
100
  return;
@@ -175,7 +180,24 @@ function filesCron(config, integration, period) {
175
180
  }
176
181
  const apiCredentials = yield (0, connection_1.prepareIntegrationCredentials)(config, integration, integrationCredentials);
177
182
  try {
178
- yield integration.updateIntegration(projectId, crowdinClient, apiCredentials, filesToProcess, rootFolder, intConfig);
183
+ yield (0, job_2.runAsJob)({
184
+ integrationId: syncSettings.integrationId,
185
+ crowdinId: syncSettings.crowdinId,
186
+ type: job_1.JobType.UPDATE_TO_INTEGRATION,
187
+ title: `Sync files to ${config.name} [scheduled]`,
188
+ payload: filesToProcess,
189
+ jobCallback: (job) => __awaiter(this, void 0, void 0, function* () {
190
+ yield integration.updateIntegration({
191
+ projectId,
192
+ client: crowdinClient,
193
+ credentials: apiCredentials,
194
+ request: filesToProcess,
195
+ rootFolder,
196
+ appSettings: intConfig,
197
+ job,
198
+ });
199
+ }),
200
+ });
179
201
  }
180
202
  catch (e) {
181
203
  (0, logger_1.logError)(e, context);
@@ -198,7 +220,24 @@ function filesCron(config, integration, period) {
198
220
  (0, logger_1.log)(`Executing updateCrowdin task for files cron job with period [${period}] for project ${projectId}. Files ${intFiles.length}`);
199
221
  const apiCredentials = yield (0, connection_1.prepareIntegrationCredentials)(config, integration, integrationCredentials);
200
222
  try {
201
- yield integration.updateCrowdin(projectId, crowdinClient, apiCredentials, intFiles, rootFolder, intConfig);
223
+ yield (0, job_2.runAsJob)({
224
+ integrationId: syncSettings.integrationId,
225
+ crowdinId: syncSettings.crowdinId,
226
+ type: job_1.JobType.UPDATE_TO_CROWDIN,
227
+ title: 'Sync files to Crowdin [scheduled]',
228
+ payload: intFiles,
229
+ jobCallback: (job) => __awaiter(this, void 0, void 0, function* () {
230
+ yield integration.updateCrowdin({
231
+ projectId,
232
+ client: crowdinClient,
233
+ credentials: apiCredentials,
234
+ request: intFiles,
235
+ rootFolder,
236
+ appSettings: intConfig,
237
+ job,
238
+ });
239
+ }),
240
+ });
202
241
  }
203
242
  catch (e) {
204
243
  (0, logger_1.logError)(e, context);
@@ -311,3 +350,11 @@ function createOrUpdateSyncSettings(config, req, files, provider, onlyCreate = f
311
350
  });
312
351
  }
313
352
  exports.createOrUpdateSyncSettings = createOrUpdateSyncSettings;
353
+ function removeFinishedJobs() {
354
+ return __awaiter(this, void 0, void 0, function* () {
355
+ (0, logger_1.log)('Removing all finished jobs');
356
+ yield (0, storage_1.getStorage)().deleteFinishedJobs();
357
+ (0, logger_1.log)('Removed all finished jobs');
358
+ });
359
+ }
360
+ exports.removeFinishedJobs = removeFinishedJobs;
@@ -149,17 +149,11 @@ function applyIntegrationModuleDefaults(config, integration) {
149
149
  if (getUserSettings) {
150
150
  fields = yield getUserSettings(projectId, crowdinClient, integrationCredentials);
151
151
  }
152
- const defaultSettings = [
153
- {
154
- label: 'Background synchronization',
155
- },
156
- ];
152
+ const defaultSettings = [];
157
153
  defaultSettings.push({
158
154
  key: 'schedule',
159
- label: 'Sync schedule',
160
- helpText: integration.webhooks
161
- ? 'Set the frequency for pushing sources and translations. The source file changes made in integration will be synced with Crowdin continuously.'
162
- : 'Set the frequency for pushing sources and translations',
155
+ label: 'Auto sync',
156
+ helpText: 'Adjust the update frequency for sources and translations. If enabled, make sure Auto Sync is enabled for your selected directories and files in the dual pane view.',
163
157
  type: 'select',
164
158
  defaultValue: '0',
165
159
  options: [
@@ -282,11 +276,10 @@ function convertClientConfig(clientConfig) {
282
276
  const clientSecret = clientConfig.clientSecret || process.env.CROWDIN_CLIENT_SECRET;
283
277
  const port = clientConfig.port || process.env.PORT || 3000;
284
278
  const { region = process.env.AWS_REGION || process.env.AWS_DEFAULT_REGION, tmpBucketName = process.env.AWS_TMP_BUCKET_NAME, } = clientConfig.awsConfig || {};
285
- const sentryDsn = clientConfig.sentryDsn || process.env.SENTRY_DSN;
286
279
  if (!baseUrl || !clientId || !clientSecret) {
287
280
  throw new Error('One of following parameters are not defined [baseUrl, clientId, clientSecret]');
288
281
  }
289
- return Object.assign(Object.assign({}, clientConfig), { sentryDsn, baseUrl: baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl, clientId,
282
+ return Object.assign(Object.assign({}, clientConfig), { baseUrl: baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl, clientId,
290
283
  clientSecret, awsConfig: {
291
284
  tmpBucketName,
292
285
  region,
@@ -14,6 +14,7 @@ const models_1 = require("../models");
14
14
  const storage_1 = require("../storage");
15
15
  const defaults_1 = require("./defaults");
16
16
  const index_1 = require("./index");
17
+ const files_1 = require("./files");
17
18
  function getFileDiff(currentFiles, savedFiles) {
18
19
  return currentFiles.filter((x) => !savedFiles.some((x2) => x2.id === x.id));
19
20
  }
@@ -117,6 +118,7 @@ function getIntegrationSnapshot(integration, integrationCredentials, integration
117
118
  if (integration.integrationOneLevelFetching) {
118
119
  files = yield getOneLevelFetchingFiles(integration, integrationCredentials, integrationSettings, files);
119
120
  }
121
+ files = (0, files_1.skipFilesByRegex)(files, integration.skipIntegrationNodes);
120
122
  // trick for compatibility in requests and set files
121
123
  files = files.map((file) => (Object.assign(Object.assign({}, file), { parentId: file.parent_id || file.parentId,
122
124
  // eslint-disable-next-line @typescript-eslint/camelcase
@@ -1,6 +1,7 @@
1
1
  /// <reference types="node" />
2
- import { ProcessFileString } from '../models';
2
+ import { ProcessFileString, SkipIntegrationNodes, TreeItem } from '../models';
3
3
  export declare const MAX_BODY_SIZE: number;
4
4
  export declare function storeFile(fileContent: Buffer, folder: string): Promise<string>;
5
5
  export declare function getFileContent(url: string): Promise<Buffer>;
6
6
  export declare function getFileStrings(url: string): Promise<ProcessFileString[]>;
7
+ export declare function skipFilesByRegex(files: TreeItem[] | undefined, skipIntegrationNodes?: SkipIntegrationNodes): TreeItem[];
package/out/util/files.js CHANGED
@@ -12,7 +12,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
12
12
  return (mod && mod.__esModule) ? mod : { "default": mod };
13
13
  };
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
- exports.getFileStrings = exports.getFileContent = exports.storeFile = exports.MAX_BODY_SIZE = void 0;
15
+ exports.skipFilesByRegex = exports.getFileStrings = exports.getFileContent = exports.storeFile = exports.MAX_BODY_SIZE = void 0;
16
16
  const axios_1 = __importDefault(require("axios"));
17
17
  const fs_1 = __importDefault(require("fs"));
18
18
  const path_1 = __importDefault(require("path"));
@@ -45,3 +45,21 @@ function getFileStrings(url) {
45
45
  });
46
46
  }
47
47
  exports.getFileStrings = getFileStrings;
48
+ function skipFilesByRegex(files, skipIntegrationNodes) {
49
+ if (!Array.isArray(files)) {
50
+ return [];
51
+ }
52
+ if (skipIntegrationNodes) {
53
+ files = files.filter((file) => file);
54
+ if (skipIntegrationNodes.fileNamePattern) {
55
+ const regex = new RegExp(skipIntegrationNodes.fileNamePattern);
56
+ files = files.filter((file) => !('type' in file) || !regex.test(file.name));
57
+ }
58
+ if (skipIntegrationNodes.folderNamePattern) {
59
+ const regex = new RegExp(skipIntegrationNodes.folderNamePattern);
60
+ files = files.filter((file) => 'type' in file || !regex.test(file.name));
61
+ }
62
+ }
63
+ return files;
64
+ }
65
+ exports.skipFilesByRegex = skipFilesByRegex;
package/out/util/index.js CHANGED
@@ -56,7 +56,9 @@ function handleError(err, req, res) {
56
56
  res.redirect('/');
57
57
  return;
58
58
  }
59
- res.status(code).send({ message: (0, logger_1.getErrorMessage)(err), code });
59
+ if (!res.headersSent) {
60
+ res.status(code).send({ message: (0, logger_1.getErrorMessage)(err), code });
61
+ }
60
62
  });
61
63
  }
62
64
  function runAsyncWrapper(callback) {
@@ -0,0 +1,12 @@
1
+ import { JobClient, JobType } from '../models/job';
2
+ import { Response } from 'express';
3
+ export declare function runAsJob({ integrationId, crowdinId, type, title, payload, res, jobCallback, onError, }: {
4
+ integrationId: string;
5
+ crowdinId: string;
6
+ type: JobType;
7
+ title?: string;
8
+ payload?: any;
9
+ res?: Response;
10
+ jobCallback: (arg1: JobClient) => Promise<any>;
11
+ onError?: (e: any) => Promise<void>;
12
+ }): Promise<void>;
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.runAsJob = void 0;
13
+ const job_1 = require("../models/job");
14
+ const storage_1 = require("../storage");
15
+ const logger_1 = require("./logger");
16
+ const blockingJobs = {
17
+ [job_1.JobType.UPDATE_TO_CROWDIN]: [job_1.JobType.UPDATE_TO_CROWDIN, job_1.JobType.UPDATE_TO_INTEGRATION],
18
+ [job_1.JobType.UPDATE_TO_INTEGRATION]: [job_1.JobType.UPDATE_TO_CROWDIN, job_1.JobType.UPDATE_TO_INTEGRATION],
19
+ };
20
+ function runAsJob({ integrationId, crowdinId, type, title, payload, res, jobCallback, onError, }) {
21
+ return __awaiter(this, void 0, void 0, function* () {
22
+ const storage = (0, storage_1.getStorage)();
23
+ const activeJobs = yield storage.getActiveJobs({ integrationId, crowdinId });
24
+ if (activeJobs === null || activeJobs === void 0 ? void 0 : activeJobs.length) {
25
+ const existingJob = activeJobs.find((job) => blockingJobs[type].includes(job.type));
26
+ if (existingJob === null || existingJob === void 0 ? void 0 : existingJob.id) {
27
+ if (res) {
28
+ res.status(202).send({ jobId: existingJob.id, message: 'Job is already running' });
29
+ }
30
+ return;
31
+ }
32
+ }
33
+ const jobId = yield storage.createJob({
34
+ integrationId,
35
+ crowdinId,
36
+ type,
37
+ title: title || '',
38
+ payload: JSON.stringify(payload),
39
+ });
40
+ if (res) {
41
+ res.status(202).send({ jobId });
42
+ }
43
+ const job = {
44
+ get: function getJob() {
45
+ return __awaiter(this, void 0, void 0, function* () {
46
+ return yield storage.getJob({ id: jobId });
47
+ });
48
+ },
49
+ update: function updateProgress({ progress, status, info, data }) {
50
+ return __awaiter(this, void 0, void 0, function* () {
51
+ const prevData = yield this.get();
52
+ if ((prevData === null || prevData === void 0 ? void 0 : prevData.status) === job_1.JobStatus.CANCELED) {
53
+ return { isCanceled: true };
54
+ }
55
+ yield storage.updateJob({
56
+ id: jobId,
57
+ progress,
58
+ status: status || job_1.JobStatus.IN_PROGRESS,
59
+ info,
60
+ data: JSON.stringify(data),
61
+ });
62
+ return { isCanceled: false };
63
+ });
64
+ },
65
+ };
66
+ try {
67
+ const data = yield jobCallback(job);
68
+ yield job.update({
69
+ progress: 100,
70
+ status: job_1.JobStatus.FINISHED,
71
+ data,
72
+ });
73
+ }
74
+ catch (e) {
75
+ yield job.update({
76
+ status: job_1.JobStatus.FAILED,
77
+ info: (0, logger_1.getErrorMessage)(e),
78
+ });
79
+ if (onError) {
80
+ yield onError(e);
81
+ }
82
+ else {
83
+ throw e;
84
+ }
85
+ }
86
+ });
87
+ }
88
+ exports.runAsJob = runAsJob;
@@ -67,7 +67,9 @@ function log(message, context) {
67
67
  else {
68
68
  let prefix = `[${new Date().toISOString()}]`;
69
69
  if (context) {
70
+ logsFormatter.resetContext();
70
71
  logsFormatter.setContext({
72
+ appIdentifier: context.appIdentifier || '',
71
73
  project: {
72
74
  id: context.jwtPayload.context.project_id,
73
75
  identifier: (_a = context.jwtPayload.context.project_identifier) !== null && _a !== void 0 ? _a : '',
@@ -100,7 +102,9 @@ function logError(e, context) {
100
102
  }
101
103
  else {
102
104
  if (context) {
105
+ logsFormatter.resetContext();
103
106
  logsFormatter.setContext({
107
+ appIdentifier: context.appIdentifier || '',
104
108
  project: {
105
109
  id: context.jwtPayload.context.project_id,
106
110
  identifier: (_a = context.jwtPayload.context.project_identifier) !== null && _a !== void 0 ? _a : '',