@crowdin/app-project-module 0.74.0 → 0.76.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/app-test/integration/update-crowdin.js +2 -0
- package/out/app-test/integration/update-integration.js +2 -0
- package/out/modules/api/api.js +75 -3
- package/out/modules/api/components.d.ts +58 -17
- package/out/modules/api/components.js +58 -17
- package/out/modules/integration/handlers/crowdin-files.js +11 -1
- package/out/modules/integration/handlers/job-cancel.d.ts +2 -1
- package/out/modules/integration/handlers/job-cancel.js +10 -2
- package/out/modules/integration/handlers/job-info.js +32 -1
- package/out/modules/integration/handlers/user-errors.js +2 -14
- package/out/modules/integration/index.js +1 -1
- package/out/modules/integration/types.d.ts +4 -2
- package/out/modules/integration/util/files.d.ts +7 -0
- package/out/modules/integration/util/files.js +44 -1
- package/out/modules/integration/util/job.js +33 -1
- package/out/modules/integration/util/types.d.ts +21 -0
- package/out/modules/integration/util/webhooks.js +18 -12
- package/out/storage/index.d.ts +19 -1
- package/out/storage/index.js +97 -1
- package/out/storage/mysql.d.ts +17 -1
- package/out/storage/mysql.js +134 -117
- package/out/storage/postgre.d.ts +20 -2
- package/out/storage/postgre.js +171 -120
- package/out/storage/sqlite.d.ts +17 -1
- package/out/storage/sqlite.js +131 -119
- package/out/types.d.ts +9 -0
- package/out/types.js +7 -1
- package/out/util/index.d.ts +5 -0
- package/out/util/index.js +22 -1
- package/out/views/main.handlebars +49 -50
- package/package.json +1 -1
|
@@ -108,6 +108,8 @@ const updateCrowdinTest = ({ appConfig, integrationTestConfig, }) => __awaiter(v
|
|
|
108
108
|
type: types_1.JobClientType.MANUAL,
|
|
109
109
|
fetchTranslation: jest.fn(),
|
|
110
110
|
translationUploaded: jest.fn(),
|
|
111
|
+
markFilesAsUnsynced: jest.fn(),
|
|
112
|
+
unmarkFilesAsUnsynced: jest.fn(),
|
|
111
113
|
},
|
|
112
114
|
});
|
|
113
115
|
}, 'Fail to run method updateCrowdin()');
|
|
@@ -65,6 +65,8 @@ const updateIntegrationTest = ({ appConfig, integrationTestConfig, }) => __await
|
|
|
65
65
|
type: types_1.JobClientType.MANUAL,
|
|
66
66
|
fetchTranslation: jest.fn(),
|
|
67
67
|
translationUploaded: jest.fn(),
|
|
68
|
+
markFilesAsUnsynced: jest.fn(),
|
|
69
|
+
unmarkFilesAsUnsynced: jest.fn(),
|
|
68
70
|
},
|
|
69
71
|
});
|
|
70
72
|
}, 'Fail to run method updateIntegration()');
|
package/out/modules/api/api.js
CHANGED
|
@@ -14,6 +14,8 @@ const crowdin_file_progress_1 = __importDefault(require("../integration/handlers
|
|
|
14
14
|
const crowdin_files_1 = __importDefault(require("../integration/handlers/crowdin-files"));
|
|
15
15
|
const crowdin_update_1 = __importDefault(require("../integration/handlers/crowdin-update"));
|
|
16
16
|
const integration_data_1 = __importDefault(require("../integration/handlers/integration-data"));
|
|
17
|
+
const job_info_1 = __importDefault(require("../integration/handlers/job-info"));
|
|
18
|
+
const job_cancel_1 = __importDefault(require("../integration/handlers/job-cancel"));
|
|
17
19
|
const integration_login_1 = __importDefault(require("../integration/handlers/integration-login"));
|
|
18
20
|
const integration_update_1 = __importDefault(require("../integration/handlers/integration-update"));
|
|
19
21
|
const settings_1 = __importDefault(require("../integration/handlers/settings"));
|
|
@@ -55,7 +57,7 @@ function getDefaultApiEndpointsManifest(config) {
|
|
|
55
57
|
description: 'Get a list of synced files',
|
|
56
58
|
documentationUrl: '/api-docs#tag/Files/operation/crowdin.files',
|
|
57
59
|
}, {
|
|
58
|
-
key: '
|
|
60
|
+
key: 'file-translation-progress-api',
|
|
59
61
|
name: 'File Translation Progress',
|
|
60
62
|
url: '/file-progress',
|
|
61
63
|
method: types_1.RequestMethods.GET,
|
|
@@ -82,6 +84,20 @@ function getDefaultApiEndpointsManifest(config) {
|
|
|
82
84
|
method: types_1.RequestMethods.POST,
|
|
83
85
|
description: 'Update integration data',
|
|
84
86
|
documentationUrl: '/api-docs#tag/Files/operation/integration.update',
|
|
87
|
+
}, {
|
|
88
|
+
key: 'job-get-api',
|
|
89
|
+
name: 'Job Status',
|
|
90
|
+
url: '/jobs',
|
|
91
|
+
method: types_1.RequestMethods.GET,
|
|
92
|
+
description: 'Get Job Info',
|
|
93
|
+
documentationUrl: '/api-docs#tag/Jobs/operation/job.get',
|
|
94
|
+
}, {
|
|
95
|
+
key: 'job-cancel-api',
|
|
96
|
+
name: 'Job Status',
|
|
97
|
+
url: '/jobs',
|
|
98
|
+
method: types_1.RequestMethods.DELETE,
|
|
99
|
+
description: 'Cancel Job',
|
|
100
|
+
documentationUrl: '/api-docs#tag/Jobs/operation/job.cancel',
|
|
85
101
|
}, {
|
|
86
102
|
key: 'settings-api',
|
|
87
103
|
name: 'Get App Settings',
|
|
@@ -191,7 +207,7 @@ function addDefaultApiEndpoints(app, config) {
|
|
|
191
207
|
* name: fileId
|
|
192
208
|
* in: query
|
|
193
209
|
* required: true
|
|
194
|
-
* description: '
|
|
210
|
+
* description: 'Get via [List Crowdin Files](#operation/crowdin.files)'
|
|
195
211
|
* schema:
|
|
196
212
|
* type: integer
|
|
197
213
|
* example: 102
|
|
@@ -294,6 +310,62 @@ function addDefaultApiEndpoints(app, config) {
|
|
|
294
310
|
checkSubscriptionExpiration: true,
|
|
295
311
|
moduleKey: config.projectIntegration.key,
|
|
296
312
|
}), (0, integration_credentials_1.default)(config, config.projectIntegration), (0, integration_update_1.default)(config, config.projectIntegration));
|
|
313
|
+
/**
|
|
314
|
+
* @openapi
|
|
315
|
+
* /jobs:
|
|
316
|
+
* get:
|
|
317
|
+
* tags:
|
|
318
|
+
* - 'Jobs'
|
|
319
|
+
* summary: 'Get Job Info'
|
|
320
|
+
* operationId: job.get
|
|
321
|
+
* parameters:
|
|
322
|
+
* - $ref: '#/components/parameters/ProjectId'
|
|
323
|
+
* - name: jobId
|
|
324
|
+
* in: query
|
|
325
|
+
* required: false
|
|
326
|
+
* schema:
|
|
327
|
+
* type: string
|
|
328
|
+
* example: 067da473-fc0b-43e3-b0a2-09d26af130c1
|
|
329
|
+
* responses:
|
|
330
|
+
* 200:
|
|
331
|
+
* description: 'Job information retrieved successfully'
|
|
332
|
+
* content:
|
|
333
|
+
* application/json:
|
|
334
|
+
* schema:
|
|
335
|
+
* $ref: '#/components/schemas/JobResponse'
|
|
336
|
+
*/
|
|
337
|
+
app.get('/jobs', json_response_1.default, (0, crowdin_client_1.default)({
|
|
338
|
+
config,
|
|
339
|
+
optional: false,
|
|
340
|
+
checkSubscriptionExpiration: true,
|
|
341
|
+
moduleKey: config.projectIntegration.key,
|
|
342
|
+
}), (0, job_info_1.default)(config));
|
|
343
|
+
/**
|
|
344
|
+
* @openapi
|
|
345
|
+
* /jobs:
|
|
346
|
+
* delete:
|
|
347
|
+
* tags:
|
|
348
|
+
* - 'Jobs'
|
|
349
|
+
* summary: 'Cancel Job'
|
|
350
|
+
* operationId: job.cancel
|
|
351
|
+
* parameters:
|
|
352
|
+
* - $ref: '#/components/parameters/ProjectId'
|
|
353
|
+
* - name: jobId
|
|
354
|
+
* in: query
|
|
355
|
+
* required: false
|
|
356
|
+
* schema:
|
|
357
|
+
* type: string
|
|
358
|
+
* example: 067da473-fc0b-43e3-b0a2-09d26af130c1
|
|
359
|
+
* responses:
|
|
360
|
+
* 204:
|
|
361
|
+
* description: 'Job canceled successfully'
|
|
362
|
+
*/
|
|
363
|
+
app.delete('/jobs', json_response_1.default, (0, crowdin_client_1.default)({
|
|
364
|
+
config,
|
|
365
|
+
optional: false,
|
|
366
|
+
checkSubscriptionExpiration: true,
|
|
367
|
+
moduleKey: config.projectIntegration.key,
|
|
368
|
+
}), (0, job_cancel_1.default)(config));
|
|
297
369
|
/**
|
|
298
370
|
* @openapi
|
|
299
371
|
* /settings:
|
|
@@ -413,7 +485,7 @@ function addDefaultApiEndpoints(app, config) {
|
|
|
413
485
|
* operationId: integration.fields
|
|
414
486
|
* responses:
|
|
415
487
|
* 200:
|
|
416
|
-
* description: '
|
|
488
|
+
* description: 'Login Form Fields'
|
|
417
489
|
* content:
|
|
418
490
|
* application/json:
|
|
419
491
|
* schema:
|
|
@@ -70,25 +70,32 @@
|
|
|
70
70
|
* - files
|
|
71
71
|
* properties:
|
|
72
72
|
* projectId:
|
|
73
|
-
* description: 'Project
|
|
73
|
+
* description: 'Project Id. Get via [List Projects](https://developer.crowdin.com/api/v2/#operation/api.projects.getMany)'
|
|
74
74
|
* type: integer
|
|
75
75
|
* example: 12
|
|
76
76
|
* files:
|
|
77
|
+
* example: { 102: ['de', 'fr'], 999: ['uk'] }
|
|
77
78
|
* type: object
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
79
|
+
* description: |
|
|
80
|
+
* - **{fileId}** _(integer)_: Crowdin File Id. Get via [List Crowdin Files](#operation/crowdin.files)
|
|
81
|
+
* - **[{languageCode}]** _(array of strings)_: List Of Language Id. Get via [List Supported Languages](https://support.crowdin.com/developer/api/v2/#tag/Languages/operation/api.languages.getMany)
|
|
82
|
+
*
|
|
83
|
+
* **Example:**
|
|
84
|
+
* ```json
|
|
85
|
+
* {
|
|
86
|
+
* 102: ["de", "fr"],
|
|
87
|
+
* 999: ["uk"]
|
|
88
|
+
* }
|
|
89
|
+
* ```
|
|
84
90
|
* UpdateResponse:
|
|
85
91
|
* type: object
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
+
* properties:
|
|
93
|
+
* jobId:
|
|
94
|
+
* type: string
|
|
95
|
+
* example: '067da473-fc0b-43e3-b0a2-09d26af130c1'
|
|
96
|
+
* message:
|
|
97
|
+
* type: string
|
|
98
|
+
* example: 'Another sync is running'
|
|
92
99
|
* SettingsData:
|
|
93
100
|
* type: object
|
|
94
101
|
* example: {schedule: 0, condition: 0}
|
|
@@ -195,8 +202,17 @@
|
|
|
195
202
|
* type: integer
|
|
196
203
|
* example: 86
|
|
197
204
|
* LoginFieldsResponse:
|
|
198
|
-
*
|
|
199
|
-
*
|
|
205
|
+
* type: array
|
|
206
|
+
* items:
|
|
207
|
+
* type: object
|
|
208
|
+
* properties:
|
|
209
|
+
* key:
|
|
210
|
+
* type: string
|
|
211
|
+
* example: 'apiKey'
|
|
212
|
+
* name:
|
|
213
|
+
* type: string
|
|
214
|
+
* example: 'Service API key'
|
|
215
|
+
* example: [{ key: 'email', name: 'User email' }, { key: 'password', name: 'User password' }]
|
|
200
216
|
* Login:
|
|
201
217
|
* title: 'Login'
|
|
202
218
|
* required:
|
|
@@ -211,9 +227,34 @@
|
|
|
211
227
|
* $ref: '#/components/schemas/LoginData'
|
|
212
228
|
* LoginData:
|
|
213
229
|
* type: object
|
|
230
|
+
* description: 'Login Form Fields. Get via [Integration Login Form Fields](#operation/integration.fields)'
|
|
214
231
|
* example: { email: 'user@crowdin.com', password: 'password' }
|
|
215
|
-
*
|
|
216
|
-
*
|
|
232
|
+
* Job:
|
|
233
|
+
* type: object
|
|
234
|
+
* properties:
|
|
235
|
+
* id:
|
|
236
|
+
* type: string
|
|
237
|
+
* description: 'The Unique Identifier For The Job.'
|
|
238
|
+
* example: '067da473-fc0b-43e3-b0a2-09d26af130c1'
|
|
239
|
+
* progress:
|
|
240
|
+
* type: integer
|
|
241
|
+
* description: 'The Progress Of The Job.'
|
|
242
|
+
* example: 94
|
|
243
|
+
* status:
|
|
244
|
+
* type: string
|
|
245
|
+
* description: 'The Current Status Of The Job.'
|
|
246
|
+
* example: 'inProgress'
|
|
247
|
+
* title:
|
|
248
|
+
* type: string
|
|
249
|
+
* description: 'The Title Of The Job.'
|
|
250
|
+
* example: 'Sync files to Crowdin'
|
|
251
|
+
* JobResponse:
|
|
252
|
+
* type: object
|
|
253
|
+
* properties:
|
|
254
|
+
* data:
|
|
255
|
+
* type: array
|
|
256
|
+
* items:
|
|
257
|
+
* $ref: '#/components/schemas/Job'
|
|
217
258
|
*
|
|
218
259
|
* parameters:
|
|
219
260
|
* ProjectId:
|
|
@@ -71,25 +71,32 @@
|
|
|
71
71
|
* - files
|
|
72
72
|
* properties:
|
|
73
73
|
* projectId:
|
|
74
|
-
* description: 'Project
|
|
74
|
+
* description: 'Project Id. Get via [List Projects](https://developer.crowdin.com/api/v2/#operation/api.projects.getMany)'
|
|
75
75
|
* type: integer
|
|
76
76
|
* example: 12
|
|
77
77
|
* files:
|
|
78
|
+
* example: { 102: ['de', 'fr'], 999: ['uk'] }
|
|
78
79
|
* type: object
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
80
|
+
* description: |
|
|
81
|
+
* - **{fileId}** _(integer)_: Crowdin File Id. Get via [List Crowdin Files](#operation/crowdin.files)
|
|
82
|
+
* - **[{languageCode}]** _(array of strings)_: List Of Language Id. Get via [List Supported Languages](https://support.crowdin.com/developer/api/v2/#tag/Languages/operation/api.languages.getMany)
|
|
83
|
+
*
|
|
84
|
+
* **Example:**
|
|
85
|
+
* ```json
|
|
86
|
+
* {
|
|
87
|
+
* 102: ["de", "fr"],
|
|
88
|
+
* 999: ["uk"]
|
|
89
|
+
* }
|
|
90
|
+
* ```
|
|
85
91
|
* UpdateResponse:
|
|
86
92
|
* type: object
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
+
* properties:
|
|
94
|
+
* jobId:
|
|
95
|
+
* type: string
|
|
96
|
+
* example: '067da473-fc0b-43e3-b0a2-09d26af130c1'
|
|
97
|
+
* message:
|
|
98
|
+
* type: string
|
|
99
|
+
* example: 'Another sync is running'
|
|
93
100
|
* SettingsData:
|
|
94
101
|
* type: object
|
|
95
102
|
* example: {schedule: 0, condition: 0}
|
|
@@ -196,8 +203,17 @@
|
|
|
196
203
|
* type: integer
|
|
197
204
|
* example: 86
|
|
198
205
|
* LoginFieldsResponse:
|
|
199
|
-
*
|
|
200
|
-
*
|
|
206
|
+
* type: array
|
|
207
|
+
* items:
|
|
208
|
+
* type: object
|
|
209
|
+
* properties:
|
|
210
|
+
* key:
|
|
211
|
+
* type: string
|
|
212
|
+
* example: 'apiKey'
|
|
213
|
+
* name:
|
|
214
|
+
* type: string
|
|
215
|
+
* example: 'Service API key'
|
|
216
|
+
* example: [{ key: 'email', name: 'User email' }, { key: 'password', name: 'User password' }]
|
|
201
217
|
* Login:
|
|
202
218
|
* title: 'Login'
|
|
203
219
|
* required:
|
|
@@ -212,9 +228,34 @@
|
|
|
212
228
|
* $ref: '#/components/schemas/LoginData'
|
|
213
229
|
* LoginData:
|
|
214
230
|
* type: object
|
|
231
|
+
* description: 'Login Form Fields. Get via [Integration Login Form Fields](#operation/integration.fields)'
|
|
215
232
|
* example: { email: 'user@crowdin.com', password: 'password' }
|
|
216
|
-
*
|
|
217
|
-
*
|
|
233
|
+
* Job:
|
|
234
|
+
* type: object
|
|
235
|
+
* properties:
|
|
236
|
+
* id:
|
|
237
|
+
* type: string
|
|
238
|
+
* description: 'The Unique Identifier For The Job.'
|
|
239
|
+
* example: '067da473-fc0b-43e3-b0a2-09d26af130c1'
|
|
240
|
+
* progress:
|
|
241
|
+
* type: integer
|
|
242
|
+
* description: 'The Progress Of The Job.'
|
|
243
|
+
* example: 94
|
|
244
|
+
* status:
|
|
245
|
+
* type: string
|
|
246
|
+
* description: 'The Current Status Of The Job.'
|
|
247
|
+
* example: 'inProgress'
|
|
248
|
+
* title:
|
|
249
|
+
* type: string
|
|
250
|
+
* description: 'The Title Of The Job.'
|
|
251
|
+
* example: 'Sync files to Crowdin'
|
|
252
|
+
* JobResponse:
|
|
253
|
+
* type: object
|
|
254
|
+
* properties:
|
|
255
|
+
* data:
|
|
256
|
+
* type: array
|
|
257
|
+
* items:
|
|
258
|
+
* $ref: '#/components/schemas/Job'
|
|
218
259
|
*
|
|
219
260
|
* parameters:
|
|
220
261
|
* ProjectId:
|
|
@@ -12,6 +12,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
12
12
|
const util_1 = require("../../../util");
|
|
13
13
|
const defaults_1 = require("../util/defaults");
|
|
14
14
|
const logger_1 = require("../../../util/logger");
|
|
15
|
+
const files_1 = require("../util/files");
|
|
15
16
|
function handle(config, integration) {
|
|
16
17
|
return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
|
|
17
18
|
req.logInfo('Loading crowdin files');
|
|
@@ -19,9 +20,18 @@ function handle(config, integration) {
|
|
|
19
20
|
const rootFolder = yield (0, defaults_1.getRootFolder)(config, integration, req.crowdinApiClient, req.crowdinContext.jwtPayload.context.project_id);
|
|
20
21
|
req.logInfo(`Loading files ${rootFolder ? `from folder ${rootFolder.id}` : 'from root'}`);
|
|
21
22
|
try {
|
|
22
|
-
|
|
23
|
+
let files = integration.getCrowdinFiles
|
|
23
24
|
? yield integration.getCrowdinFiles(req.crowdinContext.jwtPayload.context.project_id, req.crowdinApiClient, rootFolder, req.integrationSettings)
|
|
24
25
|
: [];
|
|
26
|
+
req.logInfo('Marking files as unsynced');
|
|
27
|
+
if (files.length > 0) {
|
|
28
|
+
files = yield (0, files_1.markUnsyncedFiles)({
|
|
29
|
+
integrationId: req.crowdinContext.clientId,
|
|
30
|
+
crowdinId: req.crowdinContext.crowdinId,
|
|
31
|
+
client: req.crowdinApiClient,
|
|
32
|
+
files,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
25
35
|
req.logInfo(`Returning ${files.length} files`);
|
|
26
36
|
res.send(files);
|
|
27
37
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
/// <reference types="qs" />
|
|
2
2
|
import { Response } from 'express';
|
|
3
|
-
|
|
3
|
+
import { Config } from '../../../types';
|
|
4
|
+
export default function handle(config: Config): (req: import("../../../types").CrowdinClientRequest | import("express").Request<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>>, res: Response<any, Record<string, any>>, next: Function) => void;
|
|
@@ -12,9 +12,17 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
12
12
|
const types_1 = require("../util/types");
|
|
13
13
|
const util_1 = require("../../../util");
|
|
14
14
|
const storage_1 = require("../../../storage");
|
|
15
|
-
function handle() {
|
|
15
|
+
function handle(config) {
|
|
16
16
|
return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
|
|
17
|
-
|
|
17
|
+
var _a;
|
|
18
|
+
const id = req.query.jobId || req.body.jobId;
|
|
19
|
+
const isApi = (0, util_1.isApiRequest)(req, config);
|
|
20
|
+
if (isApi && !((_a = req.body) === null || _a === void 0 ? void 0 : _a.projectId)) {
|
|
21
|
+
res.send({
|
|
22
|
+
error: 'Project id is require',
|
|
23
|
+
});
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
18
26
|
if (!id) {
|
|
19
27
|
req.logInfo('Job id is absent');
|
|
20
28
|
res.status(400).send('Job id is required');
|
|
@@ -35,13 +35,33 @@ function getHumanETA(ms) {
|
|
|
35
35
|
}
|
|
36
36
|
function handle(config) {
|
|
37
37
|
return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
|
|
38
|
-
|
|
38
|
+
var _a;
|
|
39
|
+
const id = req.query.jobId || req.body.jobId;
|
|
40
|
+
const isApi = (0, util_1.isApiRequest)(req, config);
|
|
41
|
+
if (isApi && !((_a = req.body) === null || _a === void 0 ? void 0 : _a.projectId)) {
|
|
42
|
+
res.send({
|
|
43
|
+
error: 'Project id is require',
|
|
44
|
+
});
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
39
47
|
if (!id) {
|
|
40
48
|
req.logInfo('Get active jobs');
|
|
41
49
|
const jobs = yield (0, storage_1.getStorage)().getActiveJobs({
|
|
42
50
|
integrationId: req.crowdinContext.clientId,
|
|
43
51
|
crowdinId: req.crowdinContext.crowdinId,
|
|
44
52
|
});
|
|
53
|
+
if (isApi && jobs) {
|
|
54
|
+
const filteredJobs = jobs.map((job) => ({
|
|
55
|
+
id: job.id,
|
|
56
|
+
progress: job.progress,
|
|
57
|
+
status: job.status,
|
|
58
|
+
title: job.title,
|
|
59
|
+
}));
|
|
60
|
+
req.logInfo(`Returning active filtered jobs info ${JSON.stringify(filteredJobs, null, 2)}`);
|
|
61
|
+
res.send(filteredJobs);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
req.logInfo(`Returning active jobs info ${JSON.stringify(jobs, null, 2)}`);
|
|
45
65
|
res.send(jobs);
|
|
46
66
|
return;
|
|
47
67
|
}
|
|
@@ -51,6 +71,17 @@ function handle(config) {
|
|
|
51
71
|
job.eta = ((Date.now() - job.createdAt) / job.progress) * (100 - job.progress);
|
|
52
72
|
job.info = getHumanETA(job.eta) + (job.info ? `\n${job.info}` : '');
|
|
53
73
|
}
|
|
74
|
+
if (isApi && job) {
|
|
75
|
+
const filteredJob = {
|
|
76
|
+
id: job.id,
|
|
77
|
+
progress: job.progress,
|
|
78
|
+
status: job.status,
|
|
79
|
+
title: job.title,
|
|
80
|
+
};
|
|
81
|
+
req.logInfo(`Returning filtered job info ${JSON.stringify(filteredJob, null, 2)}`);
|
|
82
|
+
res.send([filteredJob]);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
54
85
|
if (job && (job === null || job === void 0 ? void 0 : job.updatedAt) && Date.now() - job.updatedAt >= MINUTES * 60 * 1000) {
|
|
55
86
|
const context = req.crowdinContext;
|
|
56
87
|
const projectId = context.jwtPayload.context.project_id;
|
|
@@ -15,10 +15,7 @@ const logger_1 = require("../../../util/logger");
|
|
|
15
15
|
function handle() {
|
|
16
16
|
return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
|
|
17
17
|
var _a;
|
|
18
|
-
|
|
19
|
-
if (!userTimezone) {
|
|
20
|
-
userTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
21
|
-
}
|
|
18
|
+
const userTimezone = (yield req.crowdinApiClient.usersApi.getAuthenticatedUser()).data.timezone;
|
|
22
19
|
req.logInfo(`Loading user errors for crowdinId ${req.crowdinContext.crowdinId} and integrationId ${req.crowdinContext.clientId}`);
|
|
23
20
|
const userErrors = (_a = (yield (0, storage_1.getStorage)().getAllUserErrors(req.crowdinContext.crowdinId, req.crowdinContext.clientId))) === null || _a === void 0 ? void 0 : _a.map((userError) => {
|
|
24
21
|
if (!(0, util_1.isJson)(userError.data)) {
|
|
@@ -28,16 +25,7 @@ function handle() {
|
|
|
28
25
|
});
|
|
29
26
|
}
|
|
30
27
|
const date = new Date(+userError.createdAt);
|
|
31
|
-
|
|
32
|
-
const formattedDate = new Intl.DateTimeFormat('en-US', {
|
|
33
|
-
year: 'numeric',
|
|
34
|
-
month: 'short',
|
|
35
|
-
day: 'numeric',
|
|
36
|
-
hour: '2-digit',
|
|
37
|
-
minute: '2-digit',
|
|
38
|
-
hour12: false,
|
|
39
|
-
timeZone: userTimezone,
|
|
40
|
-
}).format(date);
|
|
28
|
+
const formattedDate = (0, util_1.getFormattedDate)({ date, userTimezone });
|
|
41
29
|
return Object.assign(Object.assign({}, userError), { formattedDate });
|
|
42
30
|
});
|
|
43
31
|
req.logInfo(`Returning ${userErrors === null || userErrors === void 0 ? void 0 : userErrors.length} user errors`);
|
|
@@ -99,7 +99,7 @@ function register({ config, app }) {
|
|
|
99
99
|
optional: false,
|
|
100
100
|
checkSubscriptionExpiration: true,
|
|
101
101
|
moduleKey: integrationLogic.key,
|
|
102
|
-
}), (0, job_cancel_1.default)());
|
|
102
|
+
}), (0, job_cancel_1.default)(config));
|
|
103
103
|
app.post('/api/settings', (0, crowdin_client_1.default)({
|
|
104
104
|
config,
|
|
105
105
|
optional: false,
|
|
@@ -289,7 +289,8 @@ export interface File {
|
|
|
289
289
|
parentId?: string;
|
|
290
290
|
nodeType?: IntegrationTreeElementType;
|
|
291
291
|
customContent?: string;
|
|
292
|
-
labels?: LabelTreeElement;
|
|
292
|
+
labels?: LabelTreeElement[];
|
|
293
|
+
failed?: boolean;
|
|
293
294
|
}
|
|
294
295
|
export interface Folder {
|
|
295
296
|
id: string;
|
|
@@ -297,7 +298,7 @@ export interface Folder {
|
|
|
297
298
|
parentId?: string;
|
|
298
299
|
nodeType?: IntegrationTreeElementType;
|
|
299
300
|
customContent?: string;
|
|
300
|
-
labels?: LabelTreeElement;
|
|
301
|
+
labels?: LabelTreeElement[];
|
|
301
302
|
}
|
|
302
303
|
export type TreeItem = File | Folder;
|
|
303
304
|
/**
|
|
@@ -390,6 +391,7 @@ type LabelTreeElementType = 'primary' | 'secondary' | 'success' | 'warning' | 'i
|
|
|
390
391
|
export interface LabelTreeElement {
|
|
391
392
|
text: string;
|
|
392
393
|
type?: LabelTreeElementType;
|
|
394
|
+
tooltip?: string;
|
|
393
395
|
color?: string;
|
|
394
396
|
}
|
|
395
397
|
export declare enum Provider {
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import { ExtendedResult, IntegrationFile, IntegrationLogic, IntegrationRequest, SkipIntegrationNodes, TreeItem } from '../types';
|
|
2
2
|
import { JobClient } from './types';
|
|
3
|
+
import Crowdin from '@crowdin/crowdin-api-client';
|
|
3
4
|
export declare function skipFilesByRegex(files: TreeItem[] | undefined, skipIntegrationNodes?: SkipIntegrationNodes): TreeItem[];
|
|
4
5
|
export declare function expandFilesTree(nodes: IntegrationFile[], req: IntegrationRequest, integration: IntegrationLogic, job?: JobClient): Promise<IntegrationFile[]>;
|
|
5
6
|
export declare function isExtendedResultType<T>(data?: T | ExtendedResult<T>): data is ExtendedResult<T>;
|
|
7
|
+
export declare function markUnsyncedFiles({ integrationId, crowdinId, client, files, }: {
|
|
8
|
+
integrationId: string;
|
|
9
|
+
crowdinId: string;
|
|
10
|
+
client: Crowdin;
|
|
11
|
+
files?: TreeItem[];
|
|
12
|
+
}): Promise<TreeItem[]>;
|
|
@@ -9,8 +9,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.isExtendedResultType = exports.expandFilesTree = exports.skipFilesByRegex = void 0;
|
|
12
|
+
exports.markUnsyncedFiles = exports.isExtendedResultType = exports.expandFilesTree = exports.skipFilesByRegex = void 0;
|
|
13
13
|
const types_1 = require("./types");
|
|
14
|
+
const storage_1 = require("../../../storage");
|
|
15
|
+
const util_1 = require("../../../util");
|
|
14
16
|
function skipFilesByRegex(files, skipIntegrationNodes) {
|
|
15
17
|
if (!Array.isArray(files)) {
|
|
16
18
|
return [];
|
|
@@ -55,3 +57,44 @@ function isExtendedResultType(data) {
|
|
|
55
57
|
return !!dataTyped && !Array.isArray(dataTyped);
|
|
56
58
|
}
|
|
57
59
|
exports.isExtendedResultType = isExtendedResultType;
|
|
60
|
+
function isFileLeaf(file) {
|
|
61
|
+
return file.nodeType === '1' || !!file.type;
|
|
62
|
+
}
|
|
63
|
+
function markUnsyncedFiles({ integrationId, crowdinId, client, files, }) {
|
|
64
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
65
|
+
if (!Array.isArray(files)) {
|
|
66
|
+
return [];
|
|
67
|
+
}
|
|
68
|
+
const unsyncedFilesData = yield (0, storage_1.getStorage)().getUnsyncedFiles({ integrationId, crowdinId });
|
|
69
|
+
let unsyncedFiles = (unsyncedFilesData === null || unsyncedFilesData === void 0 ? void 0 : unsyncedFilesData.files)
|
|
70
|
+
? JSON.parse(unsyncedFilesData === null || unsyncedFilesData === void 0 ? void 0 : unsyncedFilesData.files)
|
|
71
|
+
: [];
|
|
72
|
+
const fileIds = files.filter((file) => isFileLeaf(file)).map((file) => `${file.id}`);
|
|
73
|
+
const idsToRemove = unsyncedFiles.filter((file) => !fileIds.includes(file.id)).map((file) => file.id);
|
|
74
|
+
unsyncedFiles = unsyncedFiles.filter((file) => !idsToRemove.includes(file.id));
|
|
75
|
+
const userTimezone = (yield client.usersApi.getAuthenticatedUser()).data.timezone;
|
|
76
|
+
yield (0, storage_1.getStorage)().updateUnsyncedFiles({
|
|
77
|
+
integrationId,
|
|
78
|
+
crowdinId,
|
|
79
|
+
files: JSON.stringify(unsyncedFiles),
|
|
80
|
+
});
|
|
81
|
+
files = files.map((file) => {
|
|
82
|
+
const unsynced = unsyncedFiles.find((unsyncedFile) => unsyncedFile.id === file.id);
|
|
83
|
+
if (unsynced && isFileLeaf(file)) {
|
|
84
|
+
const formattedDate = (0, util_1.getFormattedDate)({ date: new Date(+unsynced.updatedAt), userTimezone });
|
|
85
|
+
file.labels = [
|
|
86
|
+
{
|
|
87
|
+
text: `Sync failed ${formattedDate}`,
|
|
88
|
+
type: 'warning',
|
|
89
|
+
tooltip: unsynced === null || unsynced === void 0 ? void 0 : unsynced.message,
|
|
90
|
+
},
|
|
91
|
+
...(file.labels ? file.labels : []),
|
|
92
|
+
];
|
|
93
|
+
file.failed = true;
|
|
94
|
+
}
|
|
95
|
+
return file;
|
|
96
|
+
});
|
|
97
|
+
return files;
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
exports.markUnsyncedFiles = markUnsyncedFiles;
|
|
@@ -149,7 +149,6 @@ function runAsJob({ integrationId, crowdinId, type, title, payload, res, project
|
|
|
149
149
|
}
|
|
150
150
|
return translation;
|
|
151
151
|
}),
|
|
152
|
-
// translationUploaded: async ({ fileId, languageId, etag }) => {
|
|
153
152
|
translationUploaded: ({ fileId, translationParams }) => __awaiter(this, void 0, void 0, function* () {
|
|
154
153
|
if (!isDbStore) {
|
|
155
154
|
translationParams.forEach(({ languageId, etag }) => (store[integrationId][crowdinId][fileId][languageId] = { etag }));
|
|
@@ -176,6 +175,39 @@ function runAsJob({ integrationId, crowdinId, type, title, payload, res, project
|
|
|
176
175
|
}
|
|
177
176
|
yield Promise.all([...updates, ...inserts]);
|
|
178
177
|
}),
|
|
178
|
+
markFilesAsUnsynced: ({ files }) => __awaiter(this, void 0, void 0, function* () {
|
|
179
|
+
const unsyncedFilesData = yield storage.getUnsyncedFiles({ integrationId, crowdinId });
|
|
180
|
+
const updatedFileRecords = files.map((file) => ({
|
|
181
|
+
id: file.fileId,
|
|
182
|
+
message: file.error,
|
|
183
|
+
updatedAt: `${Date.now()}`,
|
|
184
|
+
}));
|
|
185
|
+
if (unsyncedFilesData) {
|
|
186
|
+
const existingUnsyncedFiles = (unsyncedFilesData === null || unsyncedFilesData === void 0 ? void 0 : unsyncedFilesData.files) ? JSON.parse(unsyncedFilesData.files) : [];
|
|
187
|
+
const uniqueFilesMap = new Map([...existingUnsyncedFiles, ...updatedFileRecords].map((file) => [file.id, file]));
|
|
188
|
+
const uniqueFiles = Array.from(uniqueFilesMap.values());
|
|
189
|
+
yield storage.updateUnsyncedFiles({ integrationId, crowdinId, files: JSON.stringify(uniqueFiles) });
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
yield storage.saveUnsyncedFiles({
|
|
193
|
+
integrationId,
|
|
194
|
+
crowdinId,
|
|
195
|
+
files: JSON.stringify(updatedFileRecords),
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
}),
|
|
199
|
+
unmarkFilesAsUnsynced: ({ files }) => __awaiter(this, void 0, void 0, function* () {
|
|
200
|
+
const unsyncedFilesData = yield storage.getUnsyncedFiles({ integrationId, crowdinId });
|
|
201
|
+
if (unsyncedFilesData) {
|
|
202
|
+
const existingUnsyncedFiles = (unsyncedFilesData === null || unsyncedFilesData === void 0 ? void 0 : unsyncedFilesData.files) ? JSON.parse(unsyncedFilesData.files) : [];
|
|
203
|
+
const updatedFiles = existingUnsyncedFiles.filter((file) => !files.some((unmarkFile) => unmarkFile.fileId === file.id));
|
|
204
|
+
yield storage.updateUnsyncedFiles({
|
|
205
|
+
integrationId,
|
|
206
|
+
crowdinId,
|
|
207
|
+
files: JSON.stringify(updatedFiles),
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
}),
|
|
179
211
|
};
|
|
180
212
|
try {
|
|
181
213
|
const data = yield jobCallback(job);
|