@crowdin/app-project-module 1.5.4 → 1.5.5

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.
@@ -37,7 +37,7 @@ function getHumanETA(ms) {
37
37
  }
38
38
  function handle(config) {
39
39
  return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
40
- var _a, _b;
40
+ var _a, _b, _c;
41
41
  const isApi = req.isApiCall;
42
42
  const id = ((_a = req.query) === null || _a === void 0 ? void 0 : _a.jobId) || ((_b = req.body) === null || _b === void 0 ? void 0 : _b.jobId);
43
43
  if (!id) {
@@ -85,7 +85,15 @@ function handle(config) {
85
85
  res.send([filteredJob]);
86
86
  return;
87
87
  }
88
- if (job && (job === null || job === void 0 ? void 0 : job.updatedAt) && Date.now() - job.updatedAt >= MINUTES * 60 * 1000) {
88
+ const jobTimestamp = (_c = job === null || job === void 0 ? void 0 : job.updatedAt) !== null && _c !== void 0 ? _c : job === null || job === void 0 ? void 0 : job.createdAt;
89
+ const isJobStuck = !!job && !!jobTimestamp && Date.now() - jobTimestamp >= MINUTES * 60 * 1000;
90
+ if (isJobStuck) {
91
+ const settingsJobTypes = [types_1.JobType.CROWDIN_SYNC_SETTINGS_SAVE, types_1.JobType.INTEGRATION_SYNC_SETTINGS_SAVE];
92
+ if (settingsJobTypes.includes(job.type)) {
93
+ req.logInfo(`Canceling stuck settings job '${job.id}' after no updates for more than '${MINUTES}' minutes.`);
94
+ yield (0, storage_1.getStorage)().updateJob({ id: job.id, status: types_1.JobStatus.CANCELED });
95
+ return res.send(Object.assign(Object.assign({}, job), { status: types_1.JobStatus.CANCELED }));
96
+ }
89
97
  const context = req.crowdinContext;
90
98
  const projectId = context.jwtPayload.context.project_id;
91
99
  const integration = config.projectIntegration;
@@ -85,7 +85,7 @@ function handle(config, integration) {
85
85
  ((_a = integration.syncNewElements) === null || _a === void 0 ? void 0 : _a[provider]) &&
86
86
  (settings[`new-${provider}-files`] || hasSyncedFolders);
87
87
  if (needsSnapshot) {
88
- yield (0, snapshot_1.createOrUpdateFileSnapshot)(config, integration, req, provider);
88
+ yield (0, snapshot_1.createOrUpdateFileSnapshot)(config, integration, req, provider, job);
89
89
  }
90
90
  }),
91
91
  });
@@ -65,7 +65,7 @@ function expandFilesTree(nodes, req, integration, job) {
65
65
  }
66
66
  const files = nodes.filter((file) => file.nodeType === undefined || file.nodeType === '1');
67
67
  const folders = nodes.filter((folder) => folder.nodeType === '0' && !nodes.find((node) => node.parentId === folder.id));
68
- for (const { id } of folders) {
68
+ for (const { id, name } of folders) {
69
69
  let integrationData = [];
70
70
  try {
71
71
  integrationData = yield integration.getIntegrationFiles({
@@ -75,6 +75,9 @@ function expandFilesTree(nodes, req, integration, job) {
75
75
  settings: req.integrationSettings,
76
76
  parentId: id,
77
77
  });
78
+ if (job) {
79
+ yield job.update({ info: `Loading files from '${name}'` });
80
+ }
78
81
  }
79
82
  catch (e) {
80
83
  if (job) {
@@ -1,12 +1,14 @@
1
1
  import Crowdin from '@crowdin/crowdin-api-client';
2
2
  import { Config } from '../../../types';
3
3
  import { IntegrationLogic, IntegrationRequest, Provider, TreeItem } from '../types';
4
+ import { JobClient } from './types';
4
5
  export declare function getCrowdinSnapshot(config: Config, integration: IntegrationLogic, crowdinApiClient: Crowdin, projectId: number, integrationSettings: any): Promise<TreeItem[]>;
5
- export declare function getIntegrationSnapshot({ integration, integrationCredentials, integrationSettings, client, projectId, }: {
6
+ export declare function getIntegrationSnapshot({ integration, integrationCredentials, integrationSettings, client, projectId, job, }: {
6
7
  integration: IntegrationLogic;
7
8
  integrationCredentials: any;
8
9
  integrationSettings: any;
9
10
  client?: Crowdin;
10
11
  projectId?: number;
12
+ job?: JobClient;
11
13
  }): Promise<TreeItem[]>;
12
- export declare function createOrUpdateFileSnapshot(config: Config, integration: IntegrationLogic, req: IntegrationRequest, provider: Provider): Promise<void>;
14
+ export declare function createOrUpdateFileSnapshot(config: Config, integration: IntegrationLogic, req: IntegrationRequest, provider: Provider, job?: JobClient): Promise<void>;
@@ -16,6 +16,7 @@ const types_1 = require("../types");
16
16
  const storage_1 = require("../../../storage");
17
17
  const defaults_1 = require("./defaults");
18
18
  const files_1 = require("./files");
19
+ const types_2 = require("./types");
19
20
  const DEFAULT_SNAPSHOT_FETCH_CONCURRENCY = 1;
20
21
  function getCrowdinSnapshot(config, integration, crowdinApiClient, projectId, integrationSettings) {
21
22
  return __awaiter(this, void 0, void 0, function* () {
@@ -42,10 +43,10 @@ function getTreeItems(integrationData) {
42
43
  }
43
44
  return files;
44
45
  }
45
- function getOneLevelFetchingFiles(integration, integrationCredentials, integrationSettings, parentFiles, client, projectId) {
46
- return __awaiter(this, void 0, void 0, function* () {
46
+ function getOneLevelFetchingFiles(integration_1, integrationCredentials_1, integrationSettings_1, parentFiles_1, client_1, projectId_1, job_1) {
47
+ return __awaiter(this, arguments, void 0, function* (integration, integrationCredentials, integrationSettings, parentFiles, client, projectId, job, visitedIds = new Set()) {
47
48
  var _a;
48
- const folders = parentFiles.filter((file) => !file.type);
49
+ const folders = parentFiles.filter((file) => !file.type && !visitedIds.has(file.id));
49
50
  if (folders.length === 0) {
50
51
  return parentFiles;
51
52
  }
@@ -54,6 +55,11 @@ function getOneLevelFetchingFiles(integration, integrationCredentials, integrati
54
55
  for (let i = 0; i < folders.length; i += concurrency) {
55
56
  const batch = folders.slice(i, i + concurrency);
56
57
  const batchResults = yield Promise.all(batch.map((folder) => __awaiter(this, void 0, void 0, function* () {
58
+ var _a;
59
+ if (job && types_2.JobStatus.CANCELED === ((_a = (yield job.get())) === null || _a === void 0 ? void 0 : _a.status)) {
60
+ throw new Error('Job canceled');
61
+ }
62
+ visitedIds.add(folder.id);
57
63
  const childs = getTreeItems(yield integration.getIntegrationFiles({
58
64
  credentials: integrationCredentials,
59
65
  client,
@@ -63,10 +69,13 @@ function getOneLevelFetchingFiles(integration, integrationCredentials, integrati
63
69
  search: '',
64
70
  page: 0,
65
71
  }));
72
+ if (job) {
73
+ yield job.update({ info: `Loading snapshot from ${folder.name}` });
74
+ }
66
75
  if (childs.length === 0) {
67
76
  return [];
68
77
  }
69
- return getOneLevelFetchingFiles(integration, integrationCredentials, integrationSettings, childs, client, projectId);
78
+ return getOneLevelFetchingFiles(integration, integrationCredentials, integrationSettings, childs, client, projectId, job, visitedIds);
70
79
  })));
71
80
  results.push(...batchResults.flat());
72
81
  }
@@ -74,7 +83,7 @@ function getOneLevelFetchingFiles(integration, integrationCredentials, integrati
74
83
  });
75
84
  }
76
85
  function getIntegrationSnapshot(_a) {
77
- return __awaiter(this, arguments, void 0, function* ({ integration, integrationCredentials, integrationSettings, client, projectId, }) {
86
+ return __awaiter(this, arguments, void 0, function* ({ integration, integrationCredentials, integrationSettings, client, projectId, job, }) {
78
87
  var _b;
79
88
  let files = [];
80
89
  let integrationData = [];
@@ -88,7 +97,7 @@ function getIntegrationSnapshot(_a) {
88
97
  });
89
98
  files = getTreeItems(integrationData);
90
99
  if (integration.integrationOneLevelFetching) {
91
- files = yield getOneLevelFetchingFiles(integration, integrationCredentials, integrationSettings, files, client, projectId);
100
+ files = yield getOneLevelFetchingFiles(integration, integrationCredentials, integrationSettings, files, client, projectId, job);
92
101
  }
93
102
  if ((integrationSettings === null || integrationSettings === void 0 ? void 0 : integrationSettings.skipIntegrationNodesToggle) === true ||
94
103
  ((integrationSettings === null || integrationSettings === void 0 ? void 0 : integrationSettings.skipIntegrationNodesToggle) === null && ((_b = integration.skipIntegrationNodesToggle) === null || _b === void 0 ? void 0 : _b.value))) {
@@ -99,7 +108,7 @@ function getIntegrationSnapshot(_a) {
99
108
  return files;
100
109
  });
101
110
  }
102
- function createOrUpdateFileSnapshot(config, integration, req, provider) {
111
+ function createOrUpdateFileSnapshot(config, integration, req, provider, job) {
103
112
  return __awaiter(this, void 0, void 0, function* () {
104
113
  let files = [];
105
114
  const existingSnapshot = yield (0, storage_1.getStorage)().getFilesSnapshot(req.crowdinContext.clientId, req.crowdinContext.crowdinId, provider);
@@ -113,6 +122,7 @@ function createOrUpdateFileSnapshot(config, integration, req, provider) {
113
122
  integrationSettings: req.integrationSettings,
114
123
  client: req.crowdinApiClient,
115
124
  projectId: req.crowdinContext.jwtPayload.context.project_id,
125
+ job,
116
126
  });
117
127
  }
118
128
  if (existingSnapshot) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crowdin/app-project-module",
3
- "version": "1.5.4",
3
+ "version": "1.5.5",
4
4
  "description": "Module that generates for you all common endpoints for serving standalone Crowdin App",
5
5
  "main": "out/index.js",
6
6
  "types": "out/index.d.ts",