@crowdin/app-project-module 0.39.0 → 0.40.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 (44) 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/user-errors.d.ts +3 -0
  11. package/out/handlers/{user-errors.js → integration/user-errors.js} +5 -5
  12. package/out/index.d.ts +2 -1
  13. package/out/index.js +35 -27
  14. package/out/middlewares/crowdin-client.js +1 -0
  15. package/out/middlewares/ui-module.js +1 -0
  16. package/out/models/index.d.ts +48 -17
  17. package/out/models/index.js +1 -0
  18. package/out/models/job.d.ts +44 -0
  19. package/out/models/job.js +16 -0
  20. package/out/static/css/styles.css +1 -1
  21. package/out/static/js/form.js +17 -41
  22. package/out/static/js/main.js +1 -1
  23. package/out/storage/index.d.ts +6 -0
  24. package/out/storage/index.js +3 -0
  25. package/out/storage/mysql.d.ts +6 -0
  26. package/out/storage/mysql.js +106 -0
  27. package/out/storage/postgre.d.ts +6 -0
  28. package/out/storage/postgre.js +107 -0
  29. package/out/storage/sqlite.d.ts +6 -0
  30. package/out/storage/sqlite.js +96 -0
  31. package/out/util/cron.d.ts +1 -0
  32. package/out/util/cron.js +47 -3
  33. package/out/util/defaults.js +4 -11
  34. package/out/util/file-snapshot.js +2 -0
  35. package/out/util/files.d.ts +2 -1
  36. package/out/util/files.js +19 -1
  37. package/out/util/index.js +3 -1
  38. package/out/util/job.d.ts +12 -0
  39. package/out/util/job.js +88 -0
  40. package/out/util/logger.js +4 -0
  41. package/out/util/webhooks.js +50 -4
  42. package/out/views/main.handlebars +154 -6
  43. package/package.json +16 -15
  44. package/out/handlers/user-errors.d.ts +0 -3
@@ -64,7 +64,7 @@ function checkResponse(response) {
64
64
  response
65
65
  .json()
66
66
  .then((res) => {
67
- if (![200, 201, 304].includes(response.status)) {
67
+ if (![200, 201, 202, 304].includes(response.status)) {
68
68
  reject(res);
69
69
  } else {
70
70
  if (res.message) {
@@ -1,4 +1,5 @@
1
1
  import { Config, CrowdinCredentials, IntegrationCredentials, IntegrationFilesSnapshot, IntegrationSyncSettings, IntegrationWebhooks, Provider, UserErrors } from '../models';
2
+ import { CreateJobParams, GetActiveJobsParams, GetJobParams, Job, UpdateJobParams } from '../models/job';
2
3
  export interface Storage {
3
4
  migrate(): Promise<void>;
4
5
  saveCrowdinCredentials(credentials: CrowdinCredentials): Promise<void>;
@@ -32,6 +33,11 @@ export interface Storage {
32
33
  getAllUserErrors(crowdinId: string, integrationId?: string): Promise<UserErrors[] | undefined>;
33
34
  saveUserError(action: string, message: string, data: any, createdAt: string, crowdinId: string, integrationId?: string): Promise<void>;
34
35
  deleteUserErrors(date: string, crowdinId: string, integrationId?: string): Promise<void>;
36
+ createJob(params: CreateJobParams): Promise<string>;
37
+ updateJob(params: UpdateJobParams): Promise<void>;
38
+ getJob(params: GetJobParams): Promise<Job | undefined>;
39
+ getActiveJobs(params: GetActiveJobsParams): Promise<Job[] | undefined>;
40
+ deleteFinishedJobs(): Promise<void>;
35
41
  }
36
42
  export declare function initialize(config: Config): Promise<void>;
37
43
  export declare function getStorage(): Storage;
@@ -34,6 +34,9 @@ function initialize(config) {
34
34
  }
35
35
  exports.initialize = initialize;
36
36
  function getStorage() {
37
+ if (!storage) {
38
+ throw new Error('Storage not initialized');
39
+ }
37
40
  return storage;
38
41
  }
39
42
  exports.getStorage = getStorage;
@@ -1,5 +1,6 @@
1
1
  import { Storage } from '.';
2
2
  import { CrowdinCredentials, IntegrationCredentials, IntegrationFilesSnapshot, IntegrationSyncSettings, IntegrationWebhooks, UserErrors } from '../models';
3
+ import { CreateJobParams, GetActiveJobsParams, GetJobParams, Job, UpdateJobParams } from '../models/job';
3
4
  export interface MySQLStorageConfig {
4
5
  uri?: string;
5
6
  host?: string;
@@ -49,4 +50,9 @@ export declare class MySQLStorage implements Storage {
49
50
  getAllUserErrors(crowdinId: string, integrationId?: string): Promise<UserErrors[] | undefined>;
50
51
  saveUserError(action: string, message: string, data: any, createdAt: string, crowdinId: string, integrationId?: string): Promise<void>;
51
52
  deleteUserErrors(createdAt: string, crowdinId: string, integrationId?: string): Promise<void>;
53
+ createJob({ integrationId, crowdinId, type, title, payload }: CreateJobParams): Promise<string>;
54
+ updateJob({ id, progress, status, info, data }: UpdateJobParams): Promise<void>;
55
+ getJob({ id }: GetJobParams): Promise<Job | undefined>;
56
+ getActiveJobs({ integrationId, crowdinId }: GetActiveJobsParams): Promise<Job[] | undefined>;
57
+ deleteFinishedJobs(): Promise<void>;
52
58
  }
@@ -13,6 +13,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
13
13
  };
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.MySQLStorage = void 0;
16
+ const uuid_1 = require("uuid");
17
+ const job_1 = require("../models/job");
16
18
  const util_1 = require("../util");
17
19
  class MySQLStorage {
18
20
  constructor(config) {
@@ -136,6 +138,25 @@ class MySQLStorage {
136
138
  integration_id varchar(255)
137
139
  )
138
140
  `);
141
+ yield connection.execute(`
142
+ create table if not exists job
143
+ (
144
+ id varchar(255) not null primary key,
145
+ integration_id varchar(255) not null,
146
+ crowdin_id varchar(255) not null,
147
+ type varchar(255) not null,
148
+ payload text,
149
+ title text,
150
+ progress int 0,
151
+ status varchar(255) '${job_1.JobStatus.CREATED}',
152
+ payload text,
153
+ info text,
154
+ data text,
155
+ created_at varchar(255) not null,
156
+ updated_at varchar(255),
157
+ finished_at varchar(255)
158
+ )
159
+ `);
139
160
  });
140
161
  }
141
162
  saveCrowdinCredentials(credentials) {
@@ -200,6 +221,7 @@ class MySQLStorage {
200
221
  yield connection.execute('DELETE FROM files_snapshot WHERE crowdin_id = ?', [id]);
201
222
  yield connection.execute('DELETE FROM webhooks WHERE crowdin_id = ?', [id]);
202
223
  yield connection.execute('DELETE FROM user_errors WHERE crowdin_id = ?', [id]);
224
+ yield connection.execute('DELETE FROM job WHERE crowdin_id = ?', [id]);
203
225
  }));
204
226
  });
205
227
  }
@@ -251,6 +273,7 @@ class MySQLStorage {
251
273
  yield connection.execute('DELETE FROM sync_settings where integration_id = ?', [id]);
252
274
  yield connection.execute('DELETE FROM files_snapshot where integration_id = ?', [id]);
253
275
  yield connection.execute('DELETE FROM webhooks where integration_id = ?', [id]);
276
+ yield connection.execute('DELETE FROM job where integration_id = ?', [id]);
254
277
  }));
255
278
  });
256
279
  }
@@ -263,6 +286,7 @@ class MySQLStorage {
263
286
  yield connection.execute('DELETE FROM files_snapshot where crowdin_id = ?', [crowdinId]);
264
287
  yield connection.execute('DELETE FROM webhooks where crowdin_id = ?', [crowdinId]);
265
288
  yield connection.execute('DELETE FROM user_errors where crowdin_id = ?', [crowdinId]);
289
+ yield connection.execute('DELETE FROM job where crowdin_id = ?', [crowdinId]);
266
290
  }));
267
291
  });
268
292
  }
@@ -429,5 +453,87 @@ class MySQLStorage {
429
453
  });
430
454
  });
431
455
  }
456
+ createJob({ integrationId, crowdinId, type, title, payload }) {
457
+ return __awaiter(this, void 0, void 0, function* () {
458
+ const id = (0, uuid_1.v4)();
459
+ yield this.dbPromise;
460
+ yield this.executeQuery((connection) => connection.execute(`
461
+ INSERT INTO jobs(id, integration_id, crowdin_id, type, payload, title, created_at)
462
+ VALUES (?, ?, ?, ?, ?, ?, ?)
463
+ `, [id, integrationId, crowdinId, type, payload, title, Date.now().toString()]));
464
+ return id;
465
+ });
466
+ }
467
+ updateJob({ id, progress, status, info, data }) {
468
+ return __awaiter(this, void 0, void 0, function* () {
469
+ const updateFields = ['updated_at'];
470
+ const updateParams = [Date.now().toString()];
471
+ if (progress) {
472
+ updateFields.push('progress = ?');
473
+ updateParams.push(progress);
474
+ if (progress >= 100) {
475
+ updateFields.push('finished_at = ?');
476
+ updateParams.push(Date.now().toString());
477
+ }
478
+ }
479
+ if (status) {
480
+ updateFields.push('status = ?');
481
+ updateParams.push(status);
482
+ if (!updateFields.includes('finished_at = ?') && [job_1.JobStatus.FAILED, job_1.JobStatus.CANCELED].includes(status)) {
483
+ updateFields.push('finished_at = ?');
484
+ updateParams.push(Date.now().toString());
485
+ }
486
+ }
487
+ if (data) {
488
+ updateFields.push('data = ?');
489
+ updateParams.push(data);
490
+ }
491
+ if (info) {
492
+ updateFields.push('info = ?');
493
+ updateParams.push(info);
494
+ }
495
+ updateParams.push(id);
496
+ yield this.dbPromise;
497
+ yield this.executeQuery((connection) => connection.execute(`
498
+ UPDATE job
499
+ SET ${updateFields.join(', ')}
500
+ WHERE id = ?
501
+ `, updateParams));
502
+ });
503
+ }
504
+ getJob({ id }) {
505
+ return __awaiter(this, void 0, void 0, function* () {
506
+ yield this.dbPromise;
507
+ return this.executeQuery((connection) => __awaiter(this, void 0, void 0, function* () {
508
+ const [rows] = yield connection.execute(`
509
+ SELECT id, integration_id as integrationId, crowdin_id as crowdinId, type, payload, progress, status,
510
+ title, info, payload, data, created_at as createdAt, updated_at as updatedAt, finished_at as finishedAt
511
+ FROM job
512
+ WHERE id = ?
513
+ `, [id]);
514
+ return (rows || [])[0];
515
+ }));
516
+ });
517
+ }
518
+ getActiveJobs({ integrationId, crowdinId }) {
519
+ return __awaiter(this, void 0, void 0, function* () {
520
+ yield this.dbPromise;
521
+ return this.executeQuery((connection) => __awaiter(this, void 0, void 0, function* () {
522
+ const [rows] = yield connection.execute(`
523
+ SELECT id, integration_id as integrationId, crowdin_id as crowdinId, type, payload, progress, status,
524
+ title, info, payload, data, created_at as createdAt, updated_at as updatedAt, finished_at as finishedAt
525
+ FROM job
526
+ WHERE integration_id = ? AND crowdin_id = ? AND finished_at is NULL
527
+ `, [integrationId, crowdinId]);
528
+ return rows || [];
529
+ }));
530
+ });
531
+ }
532
+ deleteFinishedJobs() {
533
+ return __awaiter(this, void 0, void 0, function* () {
534
+ yield this.dbPromise;
535
+ yield this.executeQuery((connection) => connection.execute('DELETE FROM job WHERE finished_at is not NULL', []));
536
+ });
537
+ }
432
538
  }
433
539
  exports.MySQLStorage = MySQLStorage;
@@ -1,6 +1,7 @@
1
1
  import { Client } from 'pg';
2
2
  import { Storage } from '.';
3
3
  import { CrowdinCredentials, IntegrationCredentials, IntegrationFilesSnapshot, IntegrationSyncSettings, IntegrationWebhooks, UserErrors } from '../models';
4
+ import { CreateJobParams, GetActiveJobsParams, GetJobParams, Job, UpdateJobParams } from '../models/job';
4
5
  export interface PostgreStorageConfig {
5
6
  host?: string;
6
7
  connectionString?: string;
@@ -54,4 +55,9 @@ export declare class PostgreStorage implements Storage {
54
55
  getAllUserErrors(crowdinId: string, integrationId?: string): Promise<UserErrors[] | undefined>;
55
56
  saveUserError(action: string, message: string, data: any, createdAt: string, crowdinId: string, integrationId?: string): Promise<void>;
56
57
  deleteUserErrors(createdAt: string, crowdinId: string, integrationId?: string): Promise<void>;
58
+ createJob({ integrationId, crowdinId, type, payload, title }: CreateJobParams): Promise<string>;
59
+ updateJob({ id, progress, status, info, data }: UpdateJobParams): Promise<void>;
60
+ getJob({ id }: GetJobParams): Promise<Job | undefined>;
61
+ getActiveJobs({ integrationId, crowdinId }: GetActiveJobsParams): Promise<Job[] | undefined>;
62
+ deleteFinishedJobs(): Promise<void>;
57
63
  }
@@ -12,6 +12,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
12
12
  Object.defineProperty(exports, "__esModule", { value: true });
13
13
  exports.PostgreStorage = void 0;
14
14
  const pg_1 = require("pg");
15
+ const uuid_1 = require("uuid");
16
+ const job_1 = require("../models/job");
15
17
  const util_1 = require("../util");
16
18
  class PostgreStorage {
17
19
  constructor(config) {
@@ -150,6 +152,25 @@ class PostgreStorage {
150
152
  integration_id varchar
151
153
  )
152
154
  `);
155
+ yield client.query(`
156
+ create table if not exists job
157
+ (
158
+ id varchar not null primary key,
159
+ integration_id varchar not null,
160
+ crowdin_id varchar not null,
161
+ type varchar not null,
162
+ payload varchar null,
163
+ title varchar null,
164
+ progress int 0,
165
+ status varchar '${job_1.JobStatus.CREATED}',
166
+ payload varchar null,
167
+ info varchar null,
168
+ data varchar null,
169
+ created_at varchar not null,
170
+ updated_at varchar null,
171
+ finished_at varchar null
172
+ )
173
+ `);
153
174
  });
154
175
  }
155
176
  saveCrowdinCredentials(credentials) {
@@ -214,6 +235,7 @@ class PostgreStorage {
214
235
  yield client.query('DELETE FROM app_metadata WHERE crowdin_id = $1', [id]);
215
236
  yield client.query('DELETE FROM webhooks WHERE crowdin_id = $1', [id]);
216
237
  yield client.query('DELETE FROM user_errors WHERE crowdin_id = $1', [id]);
238
+ yield client.query('DELETE FROM job WHERE crowdin_id = $1', [id]);
217
239
  }));
218
240
  });
219
241
  }
@@ -265,6 +287,7 @@ class PostgreStorage {
265
287
  yield client.query('DELETE FROM sync_settings where integration_id = $1', [id]);
266
288
  yield client.query('DELETE FROM files_snapshot where integration_id = $1', [id]);
267
289
  yield client.query('DELETE FROM webhooks where integration_id = $1', [id]);
290
+ yield client.query('DELETE FROM job where integration_id = $1', [id]);
268
291
  }));
269
292
  });
270
293
  }
@@ -277,6 +300,7 @@ class PostgreStorage {
277
300
  yield client.query('DELETE FROM files_snapshot where crowdin_id = $1', [crowdinId]);
278
301
  yield client.query('DELETE FROM webhooks where crowdin_id = $1', [crowdinId]);
279
302
  yield client.query('DELETE FROM user_errors where crowdin_id = $1', [crowdinId]);
303
+ yield client.query('DELETE FROM job where crowdin_id = $1', [crowdinId]);
280
304
  }));
281
305
  });
282
306
  }
@@ -444,5 +468,88 @@ class PostgreStorage {
444
468
  });
445
469
  });
446
470
  }
471
+ createJob({ integrationId, crowdinId, type, payload, title }) {
472
+ return __awaiter(this, void 0, void 0, function* () {
473
+ const id = (0, uuid_1.v4)();
474
+ yield this.dbPromise;
475
+ yield this.executeQuery((client) => client.query(`
476
+ INSERT
477
+ INTO job(id, integration_id, crowdin_id, type, payload, title, created_at)
478
+ VALUES ($1, $2, $3, $4, $5, $6, $7)
479
+ `, [id, integrationId, crowdinId, type, payload, title, Date.now().toString()]));
480
+ return id;
481
+ });
482
+ }
483
+ updateJob({ id, progress, status, info, data }) {
484
+ return __awaiter(this, void 0, void 0, function* () {
485
+ const updateFields = ['updated_at'];
486
+ const updateParams = [Date.now().toString()];
487
+ if (progress) {
488
+ updateFields.push('progress = $' + updateParams.length.toString());
489
+ updateParams.push(progress);
490
+ if (progress >= 100) {
491
+ updateFields.push('finished_at = $' + updateParams.length.toString());
492
+ updateParams.push(Date.now().toString());
493
+ }
494
+ }
495
+ if (status) {
496
+ updateFields.push('status = $' + updateParams.length.toString());
497
+ updateParams.push(status);
498
+ if ((!progress || progress <= 100) && [job_1.JobStatus.FAILED, job_1.JobStatus.CANCELED].includes(status)) {
499
+ updateFields.push('finished_at = $' + updateParams.length.toString());
500
+ updateParams.push(Date.now().toString());
501
+ }
502
+ }
503
+ if (data) {
504
+ updateFields.push('data = $' + updateParams.length.toString());
505
+ updateParams.push(data);
506
+ }
507
+ if (info) {
508
+ updateFields.push('info = $' + updateParams.length.toString());
509
+ updateParams.push(data);
510
+ }
511
+ updateParams.push(id);
512
+ yield this.dbPromise;
513
+ yield this.executeQuery((client) => client.query(`
514
+ UPDATE job
515
+ SET ${updateFields.join(', ')}
516
+ WHERE id = $${updateParams.length.toString()}
517
+ `, updateParams));
518
+ });
519
+ }
520
+ getJob({ id }) {
521
+ return __awaiter(this, void 0, void 0, function* () {
522
+ yield this.dbPromise;
523
+ return this.executeQuery((client) => __awaiter(this, void 0, void 0, function* () {
524
+ const res = yield client.query(`
525
+ SELECT id, integration_id as integrationId, crowdin_id as crowdinId, type, payload, progress, status,
526
+ title, info, payload, data, created_at as createdAt, updated_at as updatedAt, finished_at as finishedAt
527
+ FROM jobs
528
+ WHERE id = $1
529
+ `, [id]);
530
+ return res === null || res === void 0 ? void 0 : res.rows[0];
531
+ }));
532
+ });
533
+ }
534
+ getActiveJobs({ integrationId, crowdinId }) {
535
+ return __awaiter(this, void 0, void 0, function* () {
536
+ yield this.dbPromise;
537
+ return this.executeQuery((client) => __awaiter(this, void 0, void 0, function* () {
538
+ const res = yield client.query(`
539
+ SELECT id, integration_id as integrationId, crowdin_id as crowdinId, type, payload, progress, status,
540
+ title, info, payload, data, created_at as createdAt, updated_at as updatedAt, finished_at as finishedAt
541
+ FROM jobs
542
+ WHERE integration_id = $1 AND crowdin_id = $2 AND finished_at is NULL
543
+ `, [integrationId, crowdinId]);
544
+ return (res === null || res === void 0 ? void 0 : res.rows) || [];
545
+ }));
546
+ });
547
+ }
548
+ deleteFinishedJobs() {
549
+ return __awaiter(this, void 0, void 0, function* () {
550
+ yield this.dbPromise;
551
+ yield this.executeQuery((client) => client.query(' DELETE FROM job WHERE finished_at is not NULL', []));
552
+ });
553
+ }
447
554
  }
448
555
  exports.PostgreStorage = PostgreStorage;
@@ -1,5 +1,6 @@
1
1
  import { Storage } from '.';
2
2
  import { CrowdinCredentials, IntegrationCredentials, IntegrationFilesSnapshot, IntegrationSyncSettings, IntegrationWebhooks, UserErrors } from '../models';
3
+ import { CreateJobParams, GetActiveJobsParams, GetJobParams, Job, UpdateJobParams } from '../models/job';
3
4
  export interface SQLiteStorageConfig {
4
5
  dbFolder: string;
5
6
  }
@@ -48,4 +49,9 @@ export declare class SQLiteStorage implements Storage {
48
49
  getAllUserErrors(crowdinId: string, integrationId?: string): Promise<UserErrors[]>;
49
50
  saveUserError(action: string, message: string, data: any, createdAt: string, crowdinId: string, integrationId?: string): Promise<void>;
50
51
  deleteUserErrors(createAt: string, crowdinId: string, integrationId?: string): Promise<void>;
52
+ createJob({ integrationId, crowdinId, type, title, payload }: CreateJobParams): Promise<string>;
53
+ updateJob({ id, progress, status, info, data }: UpdateJobParams): Promise<void>;
54
+ getJob({ id }: GetJobParams): Promise<Job | undefined>;
55
+ getActiveJobs({ integrationId, crowdinId }: GetActiveJobsParams): Promise<Job[] | undefined>;
56
+ deleteFinishedJobs(): Promise<void>;
51
57
  }
@@ -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) => {
@@ -207,6 +209,24 @@ class SQLiteStorage {
207
209
  crowdin_id varchar not null,
208
210
  integration_id varchar null
209
211
  );
212
+ `, []);
213
+ yield this._run(`
214
+ create table if not exists job
215
+ (
216
+ id varchar not null primary key,
217
+ integration_id varchar not null,
218
+ crowdin_id varchar not null,
219
+ type varchar not null,
220
+ title varchar null,
221
+ progress integer DEFAULT 0,
222
+ status varchar DEFAULT '${job_1.JobStatus.CREATED}',
223
+ payload varchar null,
224
+ info varchar null,
225
+ data varchar null,
226
+ created_at varchar not null,
227
+ updated_at varchar null,
228
+ finished_at varchar null
229
+ );
210
230
  `, []);
211
231
  this._res && this._res();
212
232
  // TODO: temporary code
@@ -264,6 +284,7 @@ class SQLiteStorage {
264
284
  yield this.run('DELETE FROM files_snapshot WHERE crowdin_id = ?', [id]);
265
285
  yield this.run('DELETE FROM webhooks WHERE crowdin_id = ?', [id]);
266
286
  yield this.run('DELETE FROM user_errors WHERE crowdin_id = ?', [id]);
287
+ yield this.run('DELETE FROM job WHERE crowdin_id = ?', [id]);
267
288
  });
268
289
  }
269
290
  saveIntegrationCredentials(id, credentials, crowdinId) {
@@ -296,6 +317,7 @@ class SQLiteStorage {
296
317
  yield this.run('DELETE FROM sync_settings where integration_id = ?', [id]);
297
318
  yield this.run('DELETE FROM files_snapshot where integration_id = ?', [id]);
298
319
  yield this.run('DELETE FROM webhooks where integration_id = ?', [id]);
320
+ yield this.run('DELETE FROM job where integration_id = ?', [id]);
299
321
  });
300
322
  }
301
323
  deleteAllIntegrationCredentials(crowdinId) {
@@ -305,6 +327,7 @@ class SQLiteStorage {
305
327
  yield this.run('DELETE FROM files_snapshot where crowdin_id = ?', [crowdinId]);
306
328
  yield this.run('DELETE FROM webhooks where crowdin_id = ?', [crowdinId]);
307
329
  yield this.run('DELETE FROM user_errors where crowdin_id = ?', [crowdinId]);
330
+ yield this.run('DELETE FROM job where crowdin_id = ?', [crowdinId]);
308
331
  });
309
332
  }
310
333
  saveMetadata(id, metadata, crowdinId) {
@@ -426,5 +449,78 @@ class SQLiteStorage {
426
449
  return this.run(`DELETE FROM user_errors WHERE created_at < ? AND crowdin_id = ? AND ${whereIntegrationCondition}`, params);
427
450
  });
428
451
  }
452
+ createJob({ integrationId, crowdinId, type, title, payload }) {
453
+ return __awaiter(this, void 0, void 0, function* () {
454
+ const id = (0, uuid_1.v4)();
455
+ yield this.run(`
456
+ INSERT
457
+ INTO job(id, integration_id, crowdin_id, type, payload, title, created_at)
458
+ VALUES (?, ?, ?, ?, ?, ?, ?)
459
+ `, [id, integrationId, crowdinId, type, payload, title, Date.now().toString()]);
460
+ return id;
461
+ });
462
+ }
463
+ updateJob({ id, progress, status, info, data }) {
464
+ const updateFields = ['updated_at = ?'];
465
+ const updateParams = [Date.now().toString()];
466
+ if (progress) {
467
+ updateFields.push('progress = ?');
468
+ updateParams.push(Math.round(progress));
469
+ if (progress >= 100) {
470
+ updateFields.push('finished_at = ?');
471
+ updateParams.push(Date.now().toString());
472
+ }
473
+ }
474
+ if (status) {
475
+ updateFields.push('status = ?');
476
+ updateParams.push(status);
477
+ if (!updateFields.includes('finished_at = ?') && [job_1.JobStatus.FAILED, job_1.JobStatus.CANCELED].includes(status)) {
478
+ updateFields.push('finished_at = ?');
479
+ updateParams.push(Date.now().toString());
480
+ }
481
+ }
482
+ if (data) {
483
+ updateFields.push('data = ?');
484
+ updateParams.push(data);
485
+ }
486
+ if (info) {
487
+ updateFields.push('info = ?');
488
+ updateParams.push(info);
489
+ }
490
+ updateParams.push(id);
491
+ const query = `
492
+ UPDATE job
493
+ SET ${updateFields.join(', ')}
494
+ WHERE id = ?
495
+ `;
496
+ return this.run(query, updateParams);
497
+ }
498
+ getJob({ id }) {
499
+ return __awaiter(this, void 0, void 0, function* () {
500
+ const row = yield this.get(`
501
+ SELECT id, integration_id as integrationId, crowdin_id as crowdinId, type, payload, progress, status,
502
+ title, info, payload, data, created_at as createdAt, updated_at as updatedAt, finished_at as finishedAt
503
+ FROM job
504
+ WHERE id = ?
505
+ `, [id]);
506
+ if (row) {
507
+ return row;
508
+ }
509
+ });
510
+ }
511
+ getActiveJobs({ integrationId, crowdinId }) {
512
+ return __awaiter(this, void 0, void 0, function* () {
513
+ return this.each(`
514
+ 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
515
+ FROM job
516
+ WHERE integration_id = ? AND crowdin_id = ? AND finished_at is NULL
517
+ `, [integrationId, crowdinId]);
518
+ });
519
+ }
520
+ deleteFinishedJobs() {
521
+ return __awaiter(this, void 0, void 0, function* () {
522
+ yield this.run('DELETE FROM job WHERE finished_at is not NULL', []);
523
+ });
524
+ }
429
525
  }
430
526
  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}]`);
@@ -175,7 +177,24 @@ function filesCron(config, integration, period) {
175
177
  }
176
178
  const apiCredentials = yield (0, connection_1.prepareIntegrationCredentials)(config, integration, integrationCredentials);
177
179
  try {
178
- yield integration.updateIntegration(projectId, crowdinClient, apiCredentials, filesToProcess, rootFolder, intConfig);
180
+ yield (0, job_2.runAsJob)({
181
+ integrationId: syncSettings.integrationId,
182
+ crowdinId: syncSettings.crowdinId,
183
+ type: job_1.JobType.UPDATE_TO_INTEGRATION,
184
+ title: `Sync files to ${config.name} [scheduled]`,
185
+ payload: filesToProcess,
186
+ jobCallback: (job) => __awaiter(this, void 0, void 0, function* () {
187
+ yield integration.updateIntegration({
188
+ projectId,
189
+ client: crowdinClient,
190
+ credentials: apiCredentials,
191
+ request: filesToProcess,
192
+ rootFolder,
193
+ appSettings: intConfig,
194
+ job,
195
+ });
196
+ }),
197
+ });
179
198
  }
180
199
  catch (e) {
181
200
  (0, logger_1.logError)(e, context);
@@ -198,7 +217,24 @@ function filesCron(config, integration, period) {
198
217
  (0, logger_1.log)(`Executing updateCrowdin task for files cron job with period [${period}] for project ${projectId}. Files ${intFiles.length}`);
199
218
  const apiCredentials = yield (0, connection_1.prepareIntegrationCredentials)(config, integration, integrationCredentials);
200
219
  try {
201
- yield integration.updateCrowdin(projectId, crowdinClient, apiCredentials, intFiles, rootFolder, intConfig);
220
+ yield (0, job_2.runAsJob)({
221
+ integrationId: syncSettings.integrationId,
222
+ crowdinId: syncSettings.crowdinId,
223
+ type: job_1.JobType.UPDATE_TO_CROWDIN,
224
+ title: 'Sync files to Crowdin [scheduled]',
225
+ payload: intFiles,
226
+ jobCallback: (job) => __awaiter(this, void 0, void 0, function* () {
227
+ yield integration.updateCrowdin({
228
+ projectId,
229
+ client: crowdinClient,
230
+ credentials: apiCredentials,
231
+ request: intFiles,
232
+ rootFolder,
233
+ appSettings: intConfig,
234
+ job,
235
+ });
236
+ }),
237
+ });
202
238
  }
203
239
  catch (e) {
204
240
  (0, logger_1.logError)(e, context);
@@ -311,3 +347,11 @@ function createOrUpdateSyncSettings(config, req, files, provider, onlyCreate = f
311
347
  });
312
348
  }
313
349
  exports.createOrUpdateSyncSettings = createOrUpdateSyncSettings;
350
+ function removeFinishedJobs() {
351
+ return __awaiter(this, void 0, void 0, function* () {
352
+ (0, logger_1.log)('Removing all finished jobs');
353
+ yield (0, storage_1.getStorage)().deleteFinishedJobs();
354
+ (0, logger_1.log)('Removed all finished jobs');
355
+ });
356
+ }
357
+ 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