@crowdin/app-project-module 2.9.0 → 2.10.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.
- package/out/index.d.ts +2 -0
- package/out/index.js +11 -4
- package/out/modules/integration/util/context.d.ts +11 -0
- package/out/modules/integration/util/context.js +17 -0
- package/out/modules/integration/util/cron.js +77 -59
- package/out/modules/integration/util/job.js +99 -80
- package/out/modules/integration/util/webhooks.d.ts +11 -0
- package/out/modules/integration/util/webhooks.js +35 -56
- package/out/static/css/styles.css +2 -2
- package/out/static/ui/about.bundle.js.map +1 -1
- package/out/static/ui/error.bundle.js.map +1 -1
- package/out/static/ui/form.bundle.js +87 -120
- package/out/static/ui/form.bundle.js.map +1 -1
- package/out/static/ui/install.bundle.js.map +1 -1
- package/out/static/ui/login.bundle.js.map +1 -1
- package/out/static/ui/main.bundle.js.map +1 -1
- package/out/static/ui/oauth.bundle.js.map +1 -1
- package/out/static/ui/subscription.bundle.js.map +1 -1
- package/out/util/connection.js +89 -56
- package/out/util/cron.d.ts +0 -2
- package/out/util/cron.js +18 -56
- package/out/util/index.d.ts +1 -0
- package/out/util/index.js +6 -0
- package/out/util/logger.d.ts +23 -1
- package/out/util/logger.js +36 -38
- package/package.json +11 -6
package/out/index.d.ts
CHANGED
|
@@ -5,6 +5,8 @@ import { getRequestCredentialsMasker, postRequestCredentialsMasker, maskKey } fr
|
|
|
5
5
|
export { getRequestCredentialsMasker, postRequestCredentialsMasker, maskKey };
|
|
6
6
|
export * from './types';
|
|
7
7
|
export { Cron } from './util/cron';
|
|
8
|
+
/** for app code that fans work out inside one request or job: each branch opens its own log scope */
|
|
9
|
+
export { runWithContext } from '@crowdin/logs-formatter';
|
|
8
10
|
export { express };
|
|
9
11
|
export { reportInsight as reportAdvisorInsight } from './modules/advisor-inspector/report-insight';
|
|
10
12
|
export declare const metadataStore: CrowdinMetadataStore;
|
package/out/index.js
CHANGED
|
@@ -48,7 +48,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
48
48
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
49
49
|
};
|
|
50
50
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
51
|
-
exports.metadataStore = exports.reportAdvisorInsight = exports.express = exports.maskKey = exports.postRequestCredentialsMasker = exports.getRequestCredentialsMasker = void 0;
|
|
51
|
+
exports.metadataStore = exports.reportAdvisorInsight = exports.express = exports.runWithContext = exports.maskKey = exports.postRequestCredentialsMasker = exports.getRequestCredentialsMasker = void 0;
|
|
52
52
|
exports.createApp = createApp;
|
|
53
53
|
exports.addCrowdinEndpoints = addCrowdinEndpoints;
|
|
54
54
|
const logsFormatter = __importStar(require("@crowdin/logs-formatter"));
|
|
@@ -111,6 +111,9 @@ const automationAction = __importStar(require("./modules/automation-action"));
|
|
|
111
111
|
const authGuard = __importStar(require("./modules/auth-guard"));
|
|
112
112
|
const subscription_1 = require("./util/subscription");
|
|
113
113
|
__exportStar(require("./types"), exports);
|
|
114
|
+
/** for app code that fans work out inside one request or job: each branch opens its own log scope */
|
|
115
|
+
var logs_formatter_1 = require("@crowdin/logs-formatter");
|
|
116
|
+
Object.defineProperty(exports, "runWithContext", { enumerable: true, get: function () { return logs_formatter_1.runWithContext; } });
|
|
114
117
|
var report_insight_1 = require("./modules/advisor-inspector/report-insight");
|
|
115
118
|
Object.defineProperty(exports, "reportAdvisorInsight", { enumerable: true, get: function () { return report_insight_1.reportInsight; } });
|
|
116
119
|
exports.metadataStore = {
|
|
@@ -159,6 +162,12 @@ function addCrowdinEndpoints(app, clientConfig) {
|
|
|
159
162
|
logger.initialize(config);
|
|
160
163
|
storage.initialize(config);
|
|
161
164
|
cron.initialize(config);
|
|
165
|
+
// Mounted first, so that even migration and body-parsing failures are attributed to their request
|
|
166
|
+
if (!config.disableLogsFormatter) {
|
|
167
|
+
logsFormatter.setup({ appIdentifier: config.identifier });
|
|
168
|
+
logsFormatter.registerSanitizer(log_sanitizer_1.crowdinLogSanitizer);
|
|
169
|
+
app.use(logsFormatter.contextResolverMiddleware());
|
|
170
|
+
}
|
|
162
171
|
// Middleware to ensure delayed migration before handling requests
|
|
163
172
|
app.use((req, res, next) => __awaiter(this, void 0, void 0, function* () {
|
|
164
173
|
try {
|
|
@@ -185,9 +194,7 @@ function addCrowdinEndpoints(app, clientConfig) {
|
|
|
185
194
|
}
|
|
186
195
|
});
|
|
187
196
|
if (!config.disableLogsFormatter) {
|
|
188
|
-
|
|
189
|
-
logsFormatter.registerSanitizer(log_sanitizer_1.crowdinLogSanitizer);
|
|
190
|
-
app.use(logsFormatter.contextResolverMiddleware());
|
|
197
|
+
// stays after body parsing: it serves POST /_/log from req.body
|
|
191
198
|
app.use(logsFormatter.expressMiddleware());
|
|
192
199
|
}
|
|
193
200
|
app.use('/assets', (0, static_files_1.serveStatic)(config, config.assetsPath || 'static'));
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { CrowdinContextInfo, CrowdinCredentials } from '../../../types';
|
|
2
|
+
import { IntegrationCredentials } from '../types';
|
|
3
|
+
/**
|
|
4
|
+
* Background work (crons, webhooks, job re-runs) has no JWT - this builds the equivalent context
|
|
5
|
+
* from stored credentials, so the same consumers (logging, client preparation) work either way.
|
|
6
|
+
*/
|
|
7
|
+
export declare function crowdinContextInfoFromCredentials({ projectId, crowdinCredentials, integrationCredentials, }: {
|
|
8
|
+
projectId: number;
|
|
9
|
+
crowdinCredentials: CrowdinCredentials;
|
|
10
|
+
integrationCredentials?: IntegrationCredentials;
|
|
11
|
+
}): CrowdinContextInfo;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.crowdinContextInfoFromCredentials = crowdinContextInfoFromCredentials;
|
|
4
|
+
/**
|
|
5
|
+
* Background work (crons, webhooks, job re-runs) has no JWT - this builds the equivalent context
|
|
6
|
+
* from stored credentials, so the same consumers (logging, client preparation) work either way.
|
|
7
|
+
*/
|
|
8
|
+
function crowdinContextInfoFromCredentials({ projectId, crowdinCredentials, integrationCredentials, }) {
|
|
9
|
+
return Object.assign({ jwtPayload: {
|
|
10
|
+
context: {
|
|
11
|
+
project_id: projectId,
|
|
12
|
+
organization_id: crowdinCredentials.organizationId,
|
|
13
|
+
organization_domain: crowdinCredentials.domain,
|
|
14
|
+
user_id: crowdinCredentials.userId,
|
|
15
|
+
},
|
|
16
|
+
}, crowdinId: crowdinCredentials.id }, ((integrationCredentials === null || integrationCredentials === void 0 ? void 0 : integrationCredentials.id) && { clientId: integrationCredentials.id }));
|
|
17
|
+
}
|
|
@@ -15,6 +15,7 @@ exports.filesCron = filesCron;
|
|
|
15
15
|
exports.filterFilesFromIntegrationRequest = filterFilesFromIntegrationRequest;
|
|
16
16
|
exports.createOrUpdateSyncSettings = createOrUpdateSyncSettings;
|
|
17
17
|
exports.removeFinishedJobs = removeFinishedJobs;
|
|
18
|
+
const logs_formatter_1 = require("@crowdin/logs-formatter");
|
|
18
19
|
const storage_1 = require("../../../storage");
|
|
19
20
|
const util_1 = require("../../../util");
|
|
20
21
|
const token_1 = require("../../../util/app-functions/token");
|
|
@@ -22,50 +23,72 @@ const connection_1 = require("../../../util/connection");
|
|
|
22
23
|
const logger_1 = require("../../../util/logger");
|
|
23
24
|
const subscription_1 = require("../../../util/subscription");
|
|
24
25
|
const types_1 = require("../types");
|
|
26
|
+
const context_1 = require("./context");
|
|
25
27
|
const defaults_1 = require("./defaults");
|
|
26
28
|
const files_1 = require("./files");
|
|
27
29
|
const job_1 = require("./job");
|
|
28
30
|
const snapshot_1 = require("./snapshot");
|
|
29
31
|
const types_2 = require("./types");
|
|
32
|
+
// Maximum number of organizations processed concurrently during a cron run.
|
|
33
|
+
// Keeps DB connection and API usage bounded regardless of how many orgs are registered.
|
|
34
|
+
const CRON_ORG_CONCURRENCY = (0, util_1.getEnvInt)('CRON_ORG_CONCURRENCY', 5);
|
|
35
|
+
function runJobForOrganization(_a) {
|
|
36
|
+
return __awaiter(this, arguments, void 0, function* ({ config, integration, job, crowdinCredentials, }) {
|
|
37
|
+
const { token, client: crowdinClient } = yield (0, connection_1.prepareCrowdinClient)({
|
|
38
|
+
config,
|
|
39
|
+
credentials: crowdinCredentials,
|
|
40
|
+
autoRenew: true,
|
|
41
|
+
});
|
|
42
|
+
const { expired } = yield (0, subscription_1.checkSubscription)({
|
|
43
|
+
config,
|
|
44
|
+
token,
|
|
45
|
+
organization: crowdinCredentials.id,
|
|
46
|
+
accountType: crowdinCredentials.type,
|
|
47
|
+
});
|
|
48
|
+
if (expired) {
|
|
49
|
+
(0, logger_1.log)(`Subscription expired. Skipping job [${job.expression}] for organization ${crowdinCredentials.id}`);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
const integrationCredentialsList = yield (0, storage_1.getStorage)().getAllIntegrationCredentials(crowdinCredentials.id);
|
|
53
|
+
const allIntegrationConfigs = yield (0, storage_1.getStorage)().getAllIntegrationConfigs(crowdinCredentials.id);
|
|
54
|
+
for (const integrationCredentials of integrationCredentialsList) {
|
|
55
|
+
const integrationConfig = allIntegrationConfigs.find(({ integrationId }) => integrationId === integrationCredentials.id);
|
|
56
|
+
const projectId = (0, token_1.getProjectId)(integrationCredentials.id);
|
|
57
|
+
(0, logger_1.setLogCrowdinContext)((0, context_1.crowdinContextInfoFromCredentials)({ projectId, crowdinCredentials, integrationCredentials }));
|
|
58
|
+
const credentials = yield (0, connection_1.prepareIntegrationCredentials)(config, integration, integrationCredentials);
|
|
59
|
+
const rootFolder = yield (0, defaults_1.getRootFolder)(config, integration, crowdinClient, projectId);
|
|
60
|
+
const intConfig = (integrationConfig === null || integrationConfig === void 0 ? void 0 : integrationConfig.config) ? JSON.parse(integrationConfig.config) : undefined;
|
|
61
|
+
(0, logger_1.log)(`Executing task for cron job with expression [${job.expression}] for project ${projectId}`);
|
|
62
|
+
yield job.task({
|
|
63
|
+
projectId,
|
|
64
|
+
client: crowdinClient,
|
|
65
|
+
credentials,
|
|
66
|
+
rootFolder,
|
|
67
|
+
settings: intConfig,
|
|
68
|
+
});
|
|
69
|
+
(0, logger_1.log)(`Task for cron job with expression [${job.expression}] for project ${projectId} completed`);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
}
|
|
30
73
|
function runJob(_a) {
|
|
31
74
|
return __awaiter(this, arguments, void 0, function* ({ config, integration, job, }) {
|
|
32
75
|
(0, logger_1.log)(`Starting cron job with expression [${job.expression}]`);
|
|
33
76
|
const crowdinCredentialsList = yield (0, storage_1.getStorage)().getAllCrowdinCredentials();
|
|
34
|
-
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
77
|
+
for (let i = 0; i < crowdinCredentialsList.length; i += CRON_ORG_CONCURRENCY) {
|
|
78
|
+
const batch = crowdinCredentialsList.slice(i, i + CRON_ORG_CONCURRENCY);
|
|
79
|
+
const results = yield Promise.allSettled(
|
|
80
|
+
// per-organization scope, for the same reason processSyncSettings opens one per branch
|
|
81
|
+
batch.map((crowdinCredentials) => (0, logs_formatter_1.runWithContext)((0, logger_1.logContext)(crowdinCredentials), () => runJobForOrganization({ config, integration, job, crowdinCredentials }))));
|
|
82
|
+
results.forEach((result, index) => {
|
|
83
|
+
if (result.status === 'rejected') {
|
|
84
|
+
// the rejection landed here, outside the branch's scope, so reopen its tenant
|
|
85
|
+
(0, logs_formatter_1.runWithContext)((0, logger_1.logContext)(batch[index]), () => {
|
|
86
|
+
(0, logger_1.log)(`Cron job with expression [${job.expression}] failed for organization '${batch[index].id}'`);
|
|
87
|
+
(0, logger_1.logError)(result.reason);
|
|
88
|
+
});
|
|
89
|
+
}
|
|
45
90
|
});
|
|
46
|
-
|
|
47
|
-
(0, logger_1.log)(`Subscription expired. Skipping job [${job.expression}] for organization ${crowdinCredentials.id}`);
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
const integrationCredentialsList = yield (0, storage_1.getStorage)().getAllIntegrationCredentials(crowdinCredentials.id);
|
|
51
|
-
const allIntegrationConfigs = yield (0, storage_1.getStorage)().getAllIntegrationConfigs(crowdinCredentials.id);
|
|
52
|
-
for (const integrationCredentials of integrationCredentialsList) {
|
|
53
|
-
const integrationConfig = allIntegrationConfigs.find(({ integrationId }) => integrationId === integrationCredentials.id);
|
|
54
|
-
const projectId = (0, token_1.getProjectId)(integrationCredentials.id);
|
|
55
|
-
const credentials = yield (0, connection_1.prepareIntegrationCredentials)(config, integration, integrationCredentials);
|
|
56
|
-
const rootFolder = yield (0, defaults_1.getRootFolder)(config, integration, crowdinClient, projectId);
|
|
57
|
-
const intConfig = (integrationConfig === null || integrationConfig === void 0 ? void 0 : integrationConfig.config) ? JSON.parse(integrationConfig.config) : undefined;
|
|
58
|
-
(0, logger_1.log)(`Executing task for cron job with expression [${job.expression}] for project ${projectId}`);
|
|
59
|
-
yield job.task({
|
|
60
|
-
projectId,
|
|
61
|
-
client: crowdinClient,
|
|
62
|
-
credentials,
|
|
63
|
-
rootFolder,
|
|
64
|
-
settings: intConfig,
|
|
65
|
-
});
|
|
66
|
-
(0, logger_1.log)(`Task for cron job with expression [${job.expression}] for project ${projectId} completed`);
|
|
67
|
-
}
|
|
68
|
-
})));
|
|
91
|
+
}
|
|
69
92
|
(0, logger_1.log)(`Cron job with expression [${job.expression}] completed`);
|
|
70
93
|
});
|
|
71
94
|
}
|
|
@@ -135,22 +158,27 @@ function filesCron(_a) {
|
|
|
135
158
|
const syncSettingsList = yield (0, storage_1.getStorage)().getSyncSettingsBySchedule('schedule', period);
|
|
136
159
|
const crowdinSyncSettings = syncSettingsList.filter((syncSettings) => syncSettings.provider === types_1.Provider.CROWDIN);
|
|
137
160
|
const integrationSyncSettings = syncSettingsList.filter((syncSettings) => syncSettings.provider === types_1.Provider.INTEGRATION);
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
if (result.status === 'rejected') {
|
|
141
|
-
(0, logger_1.logError)(result.reason);
|
|
142
|
-
}
|
|
143
|
-
});
|
|
144
|
-
const integrationResults = yield Promise.allSettled(integrationSyncSettings.map((syncSettings) => processSyncSettings({ config, integration, period, syncSettings })));
|
|
145
|
-
integrationResults.forEach((result) => {
|
|
146
|
-
if (result.status === 'rejected') {
|
|
147
|
-
(0, logger_1.logError)(result.reason);
|
|
148
|
-
}
|
|
149
|
-
});
|
|
161
|
+
yield Promise.all(crowdinSyncSettings.map((syncSettings) => processSyncSettings({ config, integration, period, syncSettings })));
|
|
162
|
+
yield Promise.all(integrationSyncSettings.map((syncSettings) => processSyncSettings({ config, integration, period, syncSettings })));
|
|
150
163
|
(0, logger_1.log)(`Files cron job with period [${period}] completed`);
|
|
151
164
|
});
|
|
152
165
|
}
|
|
153
|
-
|
|
166
|
+
/**
|
|
167
|
+
* Each branch of the fan-out gets a scope of its own: the tick's scope is shared by every tenant
|
|
168
|
+
* due for sync, so a context written there stamps the other tenants' log lines.
|
|
169
|
+
*/
|
|
170
|
+
function processSyncSettings(params) {
|
|
171
|
+
return (0, logs_formatter_1.runWithContext)({}, () => __awaiter(this, void 0, void 0, function* () {
|
|
172
|
+
try {
|
|
173
|
+
yield processSyncSettingsScoped(params);
|
|
174
|
+
}
|
|
175
|
+
catch (e) {
|
|
176
|
+
(0, logger_1.log)(`Files sync failed for organization '${params.syncSettings.crowdinId}'`);
|
|
177
|
+
(0, logger_1.logError)(e);
|
|
178
|
+
}
|
|
179
|
+
}));
|
|
180
|
+
}
|
|
181
|
+
function processSyncSettingsScoped(_a) {
|
|
154
182
|
return __awaiter(this, arguments, void 0, function* ({ config, integration, period, syncSettings, }) {
|
|
155
183
|
var _b, _c, _d, _e;
|
|
156
184
|
let projectData;
|
|
@@ -171,18 +199,8 @@ function processSyncSettings(_a) {
|
|
|
171
199
|
return;
|
|
172
200
|
}
|
|
173
201
|
const projectId = (0, token_1.getProjectId)(integrationCredentials.id);
|
|
174
|
-
const context = {
|
|
175
|
-
|
|
176
|
-
context: {
|
|
177
|
-
project_id: projectId,
|
|
178
|
-
organization_id: crowdinCredentials.organizationId,
|
|
179
|
-
organization_domain: crowdinCredentials.domain,
|
|
180
|
-
user_id: crowdinCredentials.userId,
|
|
181
|
-
},
|
|
182
|
-
},
|
|
183
|
-
crowdinId: crowdinCredentials.id,
|
|
184
|
-
clientId: integrationCredentials.id,
|
|
185
|
-
};
|
|
202
|
+
const context = (0, context_1.crowdinContextInfoFromCredentials)({ projectId, crowdinCredentials, integrationCredentials });
|
|
203
|
+
(0, logger_1.setLogCrowdinContext)(context);
|
|
186
204
|
const logInfo = (0, logger_1.withContext)(context);
|
|
187
205
|
try {
|
|
188
206
|
const preparedCrowdinClient = yield (0, connection_1.prepareCrowdinClient)({
|
|
@@ -11,10 +11,13 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.runAsJob = runAsJob;
|
|
13
13
|
exports.reRunInProgressJobs = reRunInProgressJobs;
|
|
14
|
+
const logs_formatter_1 = require("@crowdin/logs-formatter");
|
|
14
15
|
const storage_1 = require("../../../storage");
|
|
15
16
|
const token_1 = require("../../../util/app-functions/token");
|
|
16
17
|
const connection_1 = require("../../../util/connection");
|
|
18
|
+
const util_1 = require("../../../util");
|
|
17
19
|
const logger_1 = require("../../../util/logger");
|
|
20
|
+
const context_1 = require("./context");
|
|
18
21
|
const cron_1 = require("./cron");
|
|
19
22
|
const defaults_1 = require("./defaults");
|
|
20
23
|
const types_1 = require("./types");
|
|
@@ -27,7 +30,12 @@ const blockingJobs = {
|
|
|
27
30
|
[types_1.JobType.INTEGRATION_SETTINGS_SAVE]: [types_1.JobType.INTEGRATION_SETTINGS_SAVE],
|
|
28
31
|
};
|
|
29
32
|
const maxAttempts = 3;
|
|
30
|
-
|
|
33
|
+
// Maximum number of in-progress jobs retried concurrently on startup.
|
|
34
|
+
const RERUN_JOB_CONCURRENCY = (0, util_1.getEnvInt)('RERUN_JOB_CONCURRENCY', 5);
|
|
35
|
+
// Maximum total etag entries held in the in-memory translation cache.
|
|
36
|
+
const TRANSLATION_CACHE_MAX = (0, util_1.getEnvInt)('TRANSLATION_CACHE_SIZE', 50000);
|
|
37
|
+
let store = {};
|
|
38
|
+
let storeSize = 0;
|
|
31
39
|
function runAsJob(_a) {
|
|
32
40
|
return __awaiter(this, arguments, void 0, function* ({ integrationId, crowdinId, type, title, payload, res, projectId, client, jobType, jobStoreType, responseData, jobCallback, onError, reRunJobId, forcePushTranslations, initiatedBy, }) {
|
|
33
41
|
let jobId;
|
|
@@ -101,22 +109,22 @@ function runAsJob(_a) {
|
|
|
101
109
|
yield storage.saveTranslationCache({ integrationId, crowdinId, fileId, languageId });
|
|
102
110
|
}
|
|
103
111
|
else {
|
|
112
|
+
if (storeSize >= TRANSLATION_CACHE_MAX) {
|
|
113
|
+
(0, logger_1.log)(`Translation etag cache reached limit of ${TRANSLATION_CACHE_MAX} entries, clearing`);
|
|
114
|
+
store = {};
|
|
115
|
+
storeSize = 0;
|
|
116
|
+
}
|
|
104
117
|
if (!store[integrationId]) {
|
|
105
118
|
store[integrationId] = {};
|
|
106
119
|
}
|
|
107
120
|
if (!store[integrationId][crowdinId]) {
|
|
108
|
-
store[integrationId] = {
|
|
109
|
-
[crowdinId]: {},
|
|
110
|
-
};
|
|
121
|
+
store[integrationId][crowdinId] = {};
|
|
111
122
|
}
|
|
112
123
|
if (!store[integrationId][crowdinId][fileId]) {
|
|
113
|
-
store[integrationId] = {
|
|
114
|
-
[crowdinId]: {
|
|
115
|
-
[fileId]: {},
|
|
116
|
-
},
|
|
117
|
-
};
|
|
124
|
+
store[integrationId][crowdinId][fileId] = {};
|
|
118
125
|
}
|
|
119
126
|
store[integrationId][crowdinId][fileId][languageId] = {};
|
|
127
|
+
storeSize++;
|
|
120
128
|
}
|
|
121
129
|
}
|
|
122
130
|
let translation = null;
|
|
@@ -212,81 +220,92 @@ function runAsJob(_a) {
|
|
|
212
220
|
}
|
|
213
221
|
});
|
|
214
222
|
}
|
|
223
|
+
function reRunJob(_a) {
|
|
224
|
+
return __awaiter(this, arguments, void 0, function* ({ config, activeJob }) {
|
|
225
|
+
const storage = (0, storage_1.getStorage)();
|
|
226
|
+
if (activeJob && ![types_1.JobStatus.IN_PROGRESS, types_1.JobStatus.CREATED].includes(activeJob.status)) {
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
if (activeJob.attempt && activeJob.attempt >= maxAttempts) {
|
|
230
|
+
yield storage.updateJob({ id: activeJob.id, status: types_1.JobStatus.FAILED });
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
const { integrationId, crowdinId, type, title, payload } = activeJob;
|
|
234
|
+
const integration = config.projectIntegration;
|
|
235
|
+
let crowdinClient;
|
|
236
|
+
const integrationCredentials = yield storage.getIntegrationCredentials(integrationId);
|
|
237
|
+
const crowdinCredentials = yield storage.getCrowdinCredentials(crowdinId);
|
|
238
|
+
const integrationConfig = yield storage.getIntegrationConfig(integrationId);
|
|
239
|
+
if (!integrationCredentials || !crowdinCredentials) {
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
const projectId = (0, token_1.getProjectId)(integrationCredentials.id);
|
|
243
|
+
const context = (0, context_1.crowdinContextInfoFromCredentials)({
|
|
244
|
+
projectId,
|
|
245
|
+
crowdinCredentials,
|
|
246
|
+
integrationCredentials,
|
|
247
|
+
});
|
|
248
|
+
(0, logger_1.setLogCrowdinContext)(context);
|
|
249
|
+
try {
|
|
250
|
+
const preparedCrowdinClient = yield (0, connection_1.prepareCrowdinClient)({
|
|
251
|
+
config,
|
|
252
|
+
credentials: crowdinCredentials,
|
|
253
|
+
autoRenew: true,
|
|
254
|
+
context,
|
|
255
|
+
});
|
|
256
|
+
crowdinClient = preparedCrowdinClient.client;
|
|
257
|
+
}
|
|
258
|
+
catch (e) {
|
|
259
|
+
(0, logger_1.logError)(e);
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
const rootFolder = yield (0, defaults_1.getRootFolder)(config, integration, crowdinClient, projectId);
|
|
263
|
+
const credentials = yield (0, connection_1.prepareIntegrationCredentials)(config, integration, integrationCredentials);
|
|
264
|
+
const intConfig = (integrationConfig === null || integrationConfig === void 0 ? void 0 : integrationConfig.config)
|
|
265
|
+
? JSON.parse(integrationConfig.config)
|
|
266
|
+
: { schedule: '0', condition: '0' };
|
|
267
|
+
yield storage.updateJob({ attempt: +(activeJob.attempt || 0) + 1, id: activeJob.id });
|
|
268
|
+
if ([types_1.JobType.UPDATE_TO_CROWDIN, types_1.JobType.UPDATE_TO_INTEGRATION].includes(type)) {
|
|
269
|
+
yield (0, cron_1.runUpdateProviderJob)({
|
|
270
|
+
integrationId,
|
|
271
|
+
crowdinId,
|
|
272
|
+
type,
|
|
273
|
+
title,
|
|
274
|
+
payload: JSON.parse(payload),
|
|
275
|
+
jobType: types_1.JobClientType.RERUN,
|
|
276
|
+
projectId,
|
|
277
|
+
client: crowdinClient,
|
|
278
|
+
integration,
|
|
279
|
+
context,
|
|
280
|
+
credentials,
|
|
281
|
+
rootFolder,
|
|
282
|
+
settings: intConfig,
|
|
283
|
+
reRunJobId: activeJob.id,
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
else if ([types_1.JobType.CROWDIN_SYNC_SETTINGS_SAVE, types_1.JobType.INTEGRATION_SYNC_SETTINGS_SAVE].includes(type)) {
|
|
287
|
+
yield storage.updateJob({ status: types_1.JobStatus.CANCELED, id: activeJob.id });
|
|
288
|
+
}
|
|
289
|
+
});
|
|
290
|
+
}
|
|
215
291
|
function reRunInProgressJobs(config) {
|
|
216
292
|
return __awaiter(this, void 0, void 0, function* () {
|
|
217
|
-
const
|
|
218
|
-
const inProgressJobs = yield storage.getAllInProgressJobs();
|
|
293
|
+
const inProgressJobs = yield (0, storage_1.getStorage)().getAllInProgressJobs();
|
|
219
294
|
if (!(inProgressJobs === null || inProgressJobs === void 0 ? void 0 : inProgressJobs.length)) {
|
|
220
295
|
return;
|
|
221
296
|
}
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
const integrationConfig = yield (0, storage_1.getStorage)().getIntegrationConfig(integrationId);
|
|
236
|
-
if (!integrationCredentials || !crowdinCredentials) {
|
|
237
|
-
return;
|
|
238
|
-
}
|
|
239
|
-
const projectId = (0, token_1.getProjectId)(integrationCredentials.id);
|
|
240
|
-
const context = {
|
|
241
|
-
jwtPayload: {
|
|
242
|
-
context: {
|
|
243
|
-
project_id: projectId,
|
|
244
|
-
organization_id: crowdinCredentials.organizationId,
|
|
245
|
-
organization_domain: crowdinCredentials.domain,
|
|
246
|
-
user_id: crowdinCredentials.userId,
|
|
247
|
-
},
|
|
248
|
-
},
|
|
249
|
-
};
|
|
250
|
-
try {
|
|
251
|
-
const preparedCrowdinClient = yield (0, connection_1.prepareCrowdinClient)({
|
|
252
|
-
config,
|
|
253
|
-
credentials: crowdinCredentials,
|
|
254
|
-
autoRenew: true,
|
|
255
|
-
context,
|
|
256
|
-
});
|
|
257
|
-
crowdinClient = preparedCrowdinClient.client;
|
|
258
|
-
}
|
|
259
|
-
catch (e) {
|
|
260
|
-
(0, logger_1.logError)(e);
|
|
261
|
-
return;
|
|
262
|
-
}
|
|
263
|
-
const rootFolder = yield (0, defaults_1.getRootFolder)(config, integration, crowdinClient, projectId);
|
|
264
|
-
const credentials = yield (0, connection_1.prepareIntegrationCredentials)(config, integration, integrationCredentials);
|
|
265
|
-
const intConfig = (integrationConfig === null || integrationConfig === void 0 ? void 0 : integrationConfig.config)
|
|
266
|
-
? JSON.parse(integrationConfig.config)
|
|
267
|
-
: { schedule: '0', condition: '0' };
|
|
268
|
-
yield storage.updateJob({ attempt: +(activeJob.attempt || 0) + 1, id: activeJob.id });
|
|
269
|
-
if ([types_1.JobType.UPDATE_TO_CROWDIN, types_1.JobType.UPDATE_TO_INTEGRATION].includes(type)) {
|
|
270
|
-
yield (0, cron_1.runUpdateProviderJob)({
|
|
271
|
-
integrationId,
|
|
272
|
-
crowdinId,
|
|
273
|
-
type,
|
|
274
|
-
title,
|
|
275
|
-
payload: JSON.parse(payload),
|
|
276
|
-
jobType: types_1.JobClientType.RERUN,
|
|
277
|
-
projectId,
|
|
278
|
-
client: crowdinClient,
|
|
279
|
-
integration,
|
|
280
|
-
context,
|
|
281
|
-
credentials,
|
|
282
|
-
rootFolder,
|
|
283
|
-
settings: intConfig,
|
|
284
|
-
reRunJobId: activeJob.id,
|
|
285
|
-
});
|
|
286
|
-
}
|
|
287
|
-
else if ([types_1.JobType.CROWDIN_SYNC_SETTINGS_SAVE, types_1.JobType.INTEGRATION_SYNC_SETTINGS_SAVE].includes(type)) {
|
|
288
|
-
yield storage.updateJob({ status: types_1.JobStatus.CANCELED, id: activeJob.id });
|
|
289
|
-
}
|
|
290
|
-
})));
|
|
297
|
+
for (let i = 0; i < inProgressJobs.length; i += RERUN_JOB_CONCURRENCY) {
|
|
298
|
+
const batch = inProgressJobs.slice(i, i + RERUN_JOB_CONCURRENCY);
|
|
299
|
+
yield Promise.all(
|
|
300
|
+
// re-runs start at app.listen, outside any request, so each job gets a scope of its own
|
|
301
|
+
batch.map((activeJob) => (0, logs_formatter_1.runWithContext)({}, () => __awaiter(this, void 0, void 0, function* () {
|
|
302
|
+
try {
|
|
303
|
+
yield reRunJob({ config, activeJob });
|
|
304
|
+
}
|
|
305
|
+
catch (e) {
|
|
306
|
+
(0, logger_1.logError)(e);
|
|
307
|
+
}
|
|
308
|
+
}))));
|
|
309
|
+
}
|
|
291
310
|
});
|
|
292
311
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import Crowdin from '@crowdin/crowdin-api-client';
|
|
2
|
+
import { Channel, ConsumeMessage } from 'amqplib';
|
|
2
3
|
import { Config, CrowdinContextInfo } from '../../../types';
|
|
3
4
|
import { AppSettings, IntegrationLogic, IntegrationSyncSettings, Provider, UpdateCrowdinWebhookPayloadsArgs } from '../types';
|
|
4
5
|
export declare const HookEvents: any;
|
|
@@ -35,6 +36,7 @@ export declare function prepareWebhookData({ config, integration, provider, webh
|
|
|
35
36
|
settings: AppSettings;
|
|
36
37
|
syncSettings: null;
|
|
37
38
|
preparedIntegrationCredentials: null;
|
|
39
|
+
logContext?: undefined;
|
|
38
40
|
} | {
|
|
39
41
|
projectId: number;
|
|
40
42
|
crowdinClient: {
|
|
@@ -45,6 +47,7 @@ export declare function prepareWebhookData({ config, integration, provider, webh
|
|
|
45
47
|
rootFolder: import("@crowdin/crowdin-api-client").SourceFilesModel.Directory | undefined;
|
|
46
48
|
settings: AppSettings;
|
|
47
49
|
syncSettings: IntegrationSyncSettings | null;
|
|
50
|
+
logContext: import("@crowdin/logs-formatter").Context;
|
|
48
51
|
}>;
|
|
49
52
|
export declare function updateCrowdinFromWebhookRequest(args: UpdateCrowdinWebhookPayloadsArgs): Promise<void>;
|
|
50
53
|
export declare function listenQueueMessage({ config, integration, queueName, queueUrl, }: {
|
|
@@ -53,3 +56,11 @@ export declare function listenQueueMessage({ config, integration, queueName, que
|
|
|
53
56
|
queueUrl: string;
|
|
54
57
|
queueName: string;
|
|
55
58
|
}): Promise<void>;
|
|
59
|
+
export declare function processMessages({ channel, messagesToAck, webhooksData, webhooksInfo, }: {
|
|
60
|
+
webhooksData: Promise<void>[];
|
|
61
|
+
webhooksInfo: {
|
|
62
|
+
[key: string]: any;
|
|
63
|
+
};
|
|
64
|
+
channel: Channel;
|
|
65
|
+
messagesToAck: ConsumeMessage[];
|
|
66
|
+
}): Promise<void>;
|