@crowdin/app-project-module 0.23.2 → 0.23.4
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/handlers/custom-file-format/process.js +45 -45
- package/out/middlewares/crowdin-client.js +11 -11
- package/out/util/cron.js +98 -22
- package/out/util/defaults.js +18 -1
- package/package.json +1 -1
|
@@ -18,17 +18,48 @@ const path_1 = __importDefault(require("path"));
|
|
|
18
18
|
const models_1 = require("../../models");
|
|
19
19
|
const util_1 = require("../../util");
|
|
20
20
|
const MAX_BODY_SIZE = 4.9 * 1024 * 1024; //4.9mb
|
|
21
|
-
function
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
21
|
+
function handle(baseConfig, baseUrl, folder, config) {
|
|
22
|
+
if (!fs_1.default.existsSync(path_1.default.join(folder, 'custom-file-format'))) {
|
|
23
|
+
fs_1.default.mkdirSync(path_1.default.join(folder, 'custom-file-format'), { recursive: true });
|
|
24
|
+
}
|
|
25
|
+
return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
|
|
26
|
+
const baseFilesUrl = `${baseUrl}/file/download`;
|
|
27
|
+
const body = req.body;
|
|
28
|
+
let logData;
|
|
29
|
+
if (body.strings) {
|
|
30
|
+
logData = Object.assign({}, body, { strings: body.strings.slice(0, 5) });
|
|
26
31
|
}
|
|
27
32
|
else {
|
|
28
|
-
|
|
33
|
+
logData = body;
|
|
29
34
|
}
|
|
30
|
-
|
|
35
|
+
(0, util_1.log)(`Received request to process file ${JSON.stringify(logData, null, 2)}`, baseConfig.logger);
|
|
36
|
+
let file;
|
|
37
|
+
if (body.file.content) {
|
|
38
|
+
file = Buffer.from(body.file.content, 'base64').toString();
|
|
39
|
+
}
|
|
40
|
+
else if (body.file.contentUrl) {
|
|
41
|
+
file = (yield axios_1.default.get(body.file.contentUrl)).data;
|
|
42
|
+
}
|
|
43
|
+
let response = {};
|
|
44
|
+
let error;
|
|
45
|
+
switch (body.jobType) {
|
|
46
|
+
case models_1.ProcessFileJobType.BUILD_FILE:
|
|
47
|
+
const buildFileResult = yield handleBuildFile(baseFilesUrl, folder, config, body, req.crowdinApiClient, req.crowdinContext, req.crowdinContext.jwtPayload.context.project_id, file);
|
|
48
|
+
response = buildFileResult.response;
|
|
49
|
+
error = buildFileResult.error;
|
|
50
|
+
break;
|
|
51
|
+
case models_1.ProcessFileJobType.PARSE_FILE:
|
|
52
|
+
const parseFileResult = yield handleParseFile(baseFilesUrl, folder, config, body, req.crowdinApiClient, req.crowdinContext, req.crowdinContext.jwtPayload.context.project_id, file);
|
|
53
|
+
response = parseFileResult.response;
|
|
54
|
+
error = parseFileResult.error;
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
const responseLength = Buffer.byteLength(JSON.stringify(response), 'utf8');
|
|
58
|
+
(0, util_1.log)(`Returning response (${responseLength} bytes) from process file action`, baseConfig.logger);
|
|
59
|
+
res.send({ data: response, error: error ? { message: error } : undefined });
|
|
60
|
+
}), baseConfig.onError);
|
|
31
61
|
}
|
|
62
|
+
exports.default = handle;
|
|
32
63
|
function handleBuildFile(baseUrl, dataFolder, config, req, client, context, projectId, file) {
|
|
33
64
|
return __awaiter(this, void 0, void 0, function* () {
|
|
34
65
|
const response = {};
|
|
@@ -131,45 +162,14 @@ function handleParseFile(baseUrl, dataFolder, config, req, client, context, proj
|
|
|
131
162
|
return { response, error: res.error };
|
|
132
163
|
});
|
|
133
164
|
}
|
|
134
|
-
function
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
const baseFilesUrl = `${baseUrl}/file/download`;
|
|
140
|
-
const body = req.body;
|
|
141
|
-
let logData;
|
|
142
|
-
if (body.strings) {
|
|
143
|
-
logData = Object.assign({}, body, { strings: body.strings.slice(0, 5) });
|
|
165
|
+
function storeFile(fileContent, folder) {
|
|
166
|
+
const fileName = `file${Date.now()}`;
|
|
167
|
+
return new Promise((res, rej) => fs_1.default.writeFile(path_1.default.join(folder, 'custom-file-format', fileName), fileContent, err => {
|
|
168
|
+
if (err) {
|
|
169
|
+
rej(err);
|
|
144
170
|
}
|
|
145
171
|
else {
|
|
146
|
-
|
|
147
|
-
}
|
|
148
|
-
(0, util_1.log)(`Received request to process file ${JSON.stringify(logData, null, 2)}`, baseConfig.logger);
|
|
149
|
-
let file;
|
|
150
|
-
if (body.file.content) {
|
|
151
|
-
file = Buffer.from(body.file.content, 'base64').toString();
|
|
152
|
-
}
|
|
153
|
-
else if (body.file.contentUrl) {
|
|
154
|
-
file = (yield axios_1.default.get(body.file.contentUrl)).data;
|
|
155
|
-
}
|
|
156
|
-
let response = {};
|
|
157
|
-
let error;
|
|
158
|
-
switch (body.jobType) {
|
|
159
|
-
case models_1.ProcessFileJobType.BUILD_FILE:
|
|
160
|
-
const buildFileResult = yield handleBuildFile(baseFilesUrl, folder, config, body, req.crowdinApiClient, req.crowdinContext, req.crowdinContext.jwtPayload.context.project_id, file);
|
|
161
|
-
response = buildFileResult.response;
|
|
162
|
-
error = buildFileResult.error;
|
|
163
|
-
break;
|
|
164
|
-
case models_1.ProcessFileJobType.PARSE_FILE:
|
|
165
|
-
const parseFileResult = yield handleParseFile(baseFilesUrl, folder, config, body, req.crowdinApiClient, req.crowdinContext, req.crowdinContext.jwtPayload.context.project_id, file);
|
|
166
|
-
response = parseFileResult.response;
|
|
167
|
-
error = parseFileResult.error;
|
|
168
|
-
break;
|
|
172
|
+
res(fileName);
|
|
169
173
|
}
|
|
170
|
-
|
|
171
|
-
(0, util_1.log)(`Returning response (${responseLength} bytes) from process file action`, baseConfig.logger);
|
|
172
|
-
res.send({ data: response, error: error ? { message: error } : undefined });
|
|
173
|
-
}), baseConfig.onError);
|
|
174
|
+
}));
|
|
174
175
|
}
|
|
175
|
-
exports.default = handle;
|
|
@@ -14,17 +14,6 @@ const crowdin_apps_functions_1 = require("@crowdin/crowdin-apps-functions");
|
|
|
14
14
|
const storage_1 = require("../storage");
|
|
15
15
|
const util_1 = require("../util");
|
|
16
16
|
const connection_1 = require("../util/connection");
|
|
17
|
-
function getToken(req) {
|
|
18
|
-
const tokenJwt = (req.query.tokenJwt || req.query.jwtToken);
|
|
19
|
-
if (tokenJwt) {
|
|
20
|
-
return tokenJwt;
|
|
21
|
-
}
|
|
22
|
-
if (req.headers.authorization) {
|
|
23
|
-
if (req.headers.authorization.startsWith('Bearer ') || req.headers.authorization.startsWith('bearer ')) {
|
|
24
|
-
return req.headers.authorization.substring(7);
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
17
|
function prepareCrowdinRequest(jwtToken, config, optional = false, checkSubscriptionExpiration = true) {
|
|
29
18
|
return __awaiter(this, void 0, void 0, function* () {
|
|
30
19
|
(0, util_1.log)('Validating jwt token from incoming request', config.logger);
|
|
@@ -86,3 +75,14 @@ function handle(config, optional = false, checkSubscriptionExpiration = true) {
|
|
|
86
75
|
}), config.onError);
|
|
87
76
|
}
|
|
88
77
|
exports.default = handle;
|
|
78
|
+
function getToken(req) {
|
|
79
|
+
const tokenJwt = (req.query.tokenJwt || req.query.jwtToken);
|
|
80
|
+
if (tokenJwt) {
|
|
81
|
+
return tokenJwt;
|
|
82
|
+
}
|
|
83
|
+
if (req.headers.authorization) {
|
|
84
|
+
if (req.headers.authorization.startsWith('Bearer ') || req.headers.authorization.startsWith('bearer ')) {
|
|
85
|
+
return req.headers.authorization.substring(7);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
package/out/util/cron.js
CHANGED
|
@@ -34,6 +34,12 @@ const _1 = require(".");
|
|
|
34
34
|
const storage_1 = require("../storage");
|
|
35
35
|
const connection_1 = require("./connection");
|
|
36
36
|
const defaults_1 = require("./defaults");
|
|
37
|
+
var SyncCondition;
|
|
38
|
+
(function (SyncCondition) {
|
|
39
|
+
SyncCondition["ALL"] = "0";
|
|
40
|
+
SyncCondition["TRANSLATED"] = "1";
|
|
41
|
+
SyncCondition["APPROVED"] = "2";
|
|
42
|
+
})(SyncCondition || (SyncCondition = {}));
|
|
37
43
|
function runJob(config, integration, job) {
|
|
38
44
|
return __awaiter(this, void 0, void 0, function* () {
|
|
39
45
|
(0, _1.log)(`Starting cron job with expression [${job.expression}]`, config.logger);
|
|
@@ -70,34 +76,104 @@ function filesCron(config, integration, period) {
|
|
|
70
76
|
const files = JSON.parse(syncSettings.files);
|
|
71
77
|
const crowdinCredentials = yield (0, storage_1.getStorage)().getCrowdinCredentials(syncSettings.crowdinId);
|
|
72
78
|
const integrationCredentials = yield (0, storage_1.getStorage)().getIntegrationCredentials(syncSettings.integrationId);
|
|
73
|
-
if (crowdinCredentials
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
79
|
+
if (!crowdinCredentials || !integrationCredentials || !files) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
const intConfig = integrationCredentials.config
|
|
83
|
+
? JSON.parse(integrationCredentials.config)
|
|
84
|
+
: { schedule: '0', condition: '0' };
|
|
85
|
+
if (period !== intConfig.schedule) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
const projectId = crowdinAppFunctions.getProjectId(integrationCredentials.id);
|
|
89
|
+
const { client: crowdinClient, token } = yield (0, connection_1.prepareCrowdinClient)(config, crowdinCredentials);
|
|
90
|
+
const { expired } = yield (0, connection_1.checkSubscription)(config, token, crowdinCredentials.id, crowdinCredentials.type);
|
|
91
|
+
if (expired) {
|
|
92
|
+
(0, _1.log)(`Subscription expired. Skipping job [${period}] for organization ${crowdinCredentials.id}`);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
const apiCredentials = yield (0, connection_1.prepareIntegrationCredentials)(config, integration, integrationCredentials);
|
|
96
|
+
const rootFolder = yield (0, defaults_1.getRootFolder)(config, integration, crowdinClient, projectId);
|
|
97
|
+
if (syncSettings.provider === 'crowdin') {
|
|
98
|
+
const crowdinFiles = files;
|
|
99
|
+
const onlyTranslated = intConfig.condition === SyncCondition.TRANSLATED;
|
|
100
|
+
const onlyApproved = intConfig.condition === SyncCondition.APPROVED;
|
|
101
|
+
const all = intConfig.condition === SyncCondition.ALL;
|
|
102
|
+
(0, _1.log)(`Executing updateIntegration task for files cron job with period [${period}] for project ${projectId} and request ${JSON.stringify(files, null, 2)}`, config.logger);
|
|
103
|
+
if (all) {
|
|
104
|
+
yield integration.updateIntegration(projectId, crowdinClient, apiCredentials, crowdinFiles, rootFolder, intConfig);
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
const filteredFiles = yield getOnlyTranslatedOrApprovedFiles(config, projectId, crowdinFiles, crowdinClient, onlyApproved, onlyTranslated);
|
|
108
|
+
if (Object.keys(filteredFiles).length === 0) {
|
|
109
|
+
(0, _1.log)(`There is no ${onlyApproved ? 'approved' : 'translated'} file`, config.logger);
|
|
83
110
|
return;
|
|
84
111
|
}
|
|
85
|
-
|
|
86
|
-
const rootFolder = yield (0, defaults_1.getRootFolder)(config, integration, crowdinClient, projectId);
|
|
87
|
-
if (syncSettings.provider === 'crowdin') {
|
|
88
|
-
(0, _1.log)(`Executing updateIntegration task for files cron job with period [${period}] for project ${projectId} and request ${JSON.stringify(files, null, 2)}`, config.logger);
|
|
89
|
-
yield integration.updateIntegration(projectId, crowdinClient, apiCredentials, files, rootFolder, intConfig);
|
|
90
|
-
(0, _1.log)(`updateIntegration task for files cron job with period [${period}] for project ${projectId} completed`, config.logger);
|
|
91
|
-
}
|
|
92
|
-
else {
|
|
93
|
-
(0, _1.log)(`Executing updateCrowdin task for files cron job with period [${period}] for project ${projectId} and request ${JSON.stringify(files, null, 2)}`, config.logger);
|
|
94
|
-
yield integration.updateCrowdin(projectId, crowdinClient, apiCredentials, files, rootFolder, intConfig);
|
|
95
|
-
(0, _1.log)(`updateCrowdin task for files cron job with period [${period}] for project ${projectId} completed`, config.logger);
|
|
96
|
-
}
|
|
112
|
+
yield integration.updateIntegration(projectId, crowdinClient, apiCredentials, filteredFiles, rootFolder, intConfig);
|
|
97
113
|
}
|
|
114
|
+
(0, _1.log)(`updateIntegration task for files cron job with period [${period}] for project ${projectId} completed`, config.logger);
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
const intFiles = files;
|
|
118
|
+
(0, _1.log)(`Executing updateCrowdin task for files cron job with period [${period}] for project ${projectId} and request ${JSON.stringify(files, null, 2)}`, config.logger);
|
|
119
|
+
yield integration.updateCrowdin(projectId, crowdinClient, apiCredentials, intFiles, rootFolder, intConfig);
|
|
120
|
+
(0, _1.log)(`updateCrowdin task for files cron job with period [${period}] for project ${projectId} completed`, config.logger);
|
|
98
121
|
}
|
|
99
122
|
})));
|
|
100
123
|
(0, _1.log)(`Files cron job with period [${period}] completed`, config.logger);
|
|
101
124
|
});
|
|
102
125
|
}
|
|
103
126
|
exports.filesCron = filesCron;
|
|
127
|
+
function getOnlyTranslatedOrApprovedFiles(config, projectId, crowdinFiles, crowdinClient, onlyApproved, onlyTranslated) {
|
|
128
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
129
|
+
(0, _1.log)(`Filtering files to process only ${onlyApproved ? 'approved' : 'translated'} files`, config.logger);
|
|
130
|
+
const filesInfo = yield Promise.all(Object.keys(crowdinFiles).map((fileId) => __awaiter(this, void 0, void 0, function* () {
|
|
131
|
+
const res = yield crowdinClient.translationStatusApi
|
|
132
|
+
.withFetchAll()
|
|
133
|
+
.getFileProgress(projectId, Number(fileId));
|
|
134
|
+
return {
|
|
135
|
+
id: fileId,
|
|
136
|
+
info: res.data.map(e => e.data),
|
|
137
|
+
};
|
|
138
|
+
})));
|
|
139
|
+
const filteredFiles = {};
|
|
140
|
+
Object.keys(crowdinFiles).forEach(fileId => {
|
|
141
|
+
const fileInfo = filesInfo.find(info => info.id === fileId);
|
|
142
|
+
if (!fileInfo) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
const languages = crowdinFiles[fileId];
|
|
146
|
+
languages.forEach(language => {
|
|
147
|
+
const languageInfo = fileInfo.info.find(info => info.languageId === language);
|
|
148
|
+
if (!languageInfo) {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
if (onlyTranslated) {
|
|
152
|
+
if (languageInfo.translationProgress === 100) {
|
|
153
|
+
(0, _1.log)(`File ${fileId} is fully translated for language ${language}`, config.logger);
|
|
154
|
+
if (!filteredFiles[fileId]) {
|
|
155
|
+
filteredFiles[fileId] = [];
|
|
156
|
+
}
|
|
157
|
+
filteredFiles[fileId].push(language);
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
(0, _1.log)(`File ${fileId} is not fully translated for language ${language}, progress ${languageInfo.translationProgress}`, config.logger);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
if (onlyApproved) {
|
|
164
|
+
if (languageInfo.approvalProgress === 100) {
|
|
165
|
+
(0, _1.log)(`File ${fileId} is fully approved for language ${language}`, config.logger);
|
|
166
|
+
if (!filteredFiles[fileId]) {
|
|
167
|
+
filteredFiles[fileId] = [];
|
|
168
|
+
}
|
|
169
|
+
filteredFiles[fileId].push(language);
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
(0, _1.log)(`File ${fileId} is not fully approved for language ${language}, progress ${languageInfo.approvalProgress}`, config.logger);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
return filteredFiles;
|
|
178
|
+
});
|
|
179
|
+
}
|
package/out/util/defaults.js
CHANGED
|
@@ -141,7 +141,24 @@ function applyDefaults(config, integration) {
|
|
|
141
141
|
],
|
|
142
142
|
},
|
|
143
143
|
{
|
|
144
|
-
|
|
144
|
+
key: 'condition',
|
|
145
|
+
label: 'Files export settings',
|
|
146
|
+
type: 'select',
|
|
147
|
+
defaultValue: '0',
|
|
148
|
+
options: [
|
|
149
|
+
{
|
|
150
|
+
value: '0',
|
|
151
|
+
label: 'Export all',
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
value: '1',
|
|
155
|
+
label: 'Export translated only',
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
value: '2',
|
|
159
|
+
label: 'Export approved only',
|
|
160
|
+
},
|
|
161
|
+
],
|
|
145
162
|
},
|
|
146
163
|
...fields,
|
|
147
164
|
];
|
package/package.json
CHANGED