@crowdin/app-project-module 0.76.2 → 0.78.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.
@@ -83,10 +83,6 @@ function handle(config, integration, optional = false) {
83
83
  }
84
84
  return res.render('error', errorOptions);
85
85
  }
86
- const integrationConfig = yield (0, storage_1.getStorage)().getIntegrationConfig(clientId);
87
- if (integrationConfig === null || integrationConfig === void 0 ? void 0 : integrationConfig.config) {
88
- req.integrationSettings = JSON.parse(integrationConfig.config);
89
- }
90
86
  try {
91
87
  req.integrationCredentials = yield (0, connection_1.prepareIntegrationCredentials)(config, integration, integrationCredentials);
92
88
  }
@@ -94,6 +90,18 @@ function handle(config, integration, optional = false) {
94
90
  console.error(e);
95
91
  throw new util_1.CodeError('Credentials to integration either exprired or invalid', 401);
96
92
  }
93
+ const integrationConfig = yield (0, storage_1.getStorage)().getIntegrationConfig(clientId);
94
+ if (integrationConfig === null || integrationConfig === void 0 ? void 0 : integrationConfig.config) {
95
+ let integrationSettings = JSON.parse(integrationConfig.config) || {};
96
+ if (integration.normalizeSettings) {
97
+ integrationSettings = yield integration.normalizeSettings({
98
+ appSettings: integrationSettings,
99
+ apiCredentials: req.integrationCredentials,
100
+ client: req.crowdinApiClient,
101
+ });
102
+ }
103
+ req.integrationSettings = integrationSettings;
104
+ }
97
105
  next();
98
106
  }));
99
107
  }
@@ -19,17 +19,29 @@ function register({ config, app }) {
19
19
  app.use('/settings', (0, ui_module_1.default)({ config, allowUnauthorized: true, moduleType: config.aiProvider.key }), (0, render_ui_module_1.default)(config.aiProvider.settingsUiModule));
20
20
  }
21
21
  app.get((0, util_1.getLogoUrl)(config.aiProvider, '/aiprovider'), (req, res) => { var _a; return res.sendFile(((_a = config.aiProvider) === null || _a === void 0 ? void 0 : _a.imagePath) || config.imagePath); });
22
- app.get('/models', json_response_1.default, (0, crowdin_client_1.default)({
22
+ app.get('/ai-provider/models', json_response_1.default, (0, crowdin_client_1.default)({
23
23
  config,
24
24
  optional: false,
25
25
  checkSubscriptionExpiration: true,
26
26
  moduleKey: config.aiProvider.key,
27
27
  }), (0, get_model_list_1.default)(config.aiProvider));
28
- app.post('/completions', json_response_1.default, (0, crowdin_client_1.default)({
28
+ app.post('/ai-provider/completions', json_response_1.default, (0, crowdin_client_1.default)({
29
29
  config,
30
30
  optional: false,
31
31
  checkSubscriptionExpiration: true,
32
32
  moduleKey: config.aiProvider.key,
33
33
  }), (0, chat_completions_1.default)(config.aiProvider));
34
+ // TEMPORARY CODE: it needs to support old path
35
+ app.get('/models', json_response_1.default, (0, crowdin_client_1.default)({
36
+ config,
37
+ optional: false,
38
+ checkSubscriptionExpiration: true,
39
+ }), (0, get_model_list_1.default)(config.aiProvider));
40
+ app.post('/completions', json_response_1.default, (0, crowdin_client_1.default)({
41
+ config,
42
+ optional: false,
43
+ checkSubscriptionExpiration: true,
44
+ }), (0, chat_completions_1.default)(config.aiProvider));
45
+ // END TEMPORARY CODE
34
46
  }
35
47
  exports.register = register;
@@ -12,11 +12,18 @@ function register({ config, app }) {
12
12
  return;
13
13
  }
14
14
  app.get((0, util_1.getLogoUrl)(config.customMT, '/mt'), (req, res) => { var _a; return res.sendFile(((_a = config.customMT) === null || _a === void 0 ? void 0 : _a.imagePath) || config.imagePath); });
15
- app.post('/translate', (0, crowdin_client_1.default)({
15
+ app.post('/mt/translate', (0, crowdin_client_1.default)({
16
16
  config,
17
17
  optional: false,
18
18
  checkSubscriptionExpiration: true,
19
19
  moduleKey: config.customMT.key,
20
20
  }), (0, translate_1.default)(config.customMT));
21
+ // TEMPORARY CODE: it needs to support old path
22
+ app.post('/translate', (0, crowdin_client_1.default)({
23
+ config,
24
+ optional: false,
25
+ checkSubscriptionExpiration: true,
26
+ }), (0, translate_1.default)(config.customMT));
27
+ // END TEMPORARY CODE
21
28
  }
22
29
  exports.register = register;
@@ -17,26 +17,48 @@ function register({ config, app }) {
17
17
  }
18
18
  if ((0, util_1.isAuthorizedConfig)(config)) {
19
19
  if (config.customSpellchecker.settingsUiModule) {
20
- app.use('/settings', (0, ui_module_1.default)({ config, moduleType: config.customSpellchecker.key }), (0, render_ui_module_1.default)(config.customSpellchecker.settingsUiModule));
20
+ app.use('/spellchecker/settings', (0, ui_module_1.default)({ config, moduleType: config.customSpellchecker.key }), (0, render_ui_module_1.default)(config.customSpellchecker.settingsUiModule));
21
+ // TEMPORARY CODE: it needs to support old path
22
+ app.use('/settings', (0, ui_module_1.default)({ config }), (0, render_ui_module_1.default)(config.customSpellchecker.settingsUiModule));
23
+ // END TEMPORARY CODE
21
24
  }
22
- app.get('/languages', json_response_1.default, (0, crowdin_client_1.default)({
25
+ app.get('/spellchecker/languages', json_response_1.default, (0, crowdin_client_1.default)({
23
26
  config,
24
27
  optional: false,
25
28
  checkSubscriptionExpiration: true,
26
29
  moduleKey: config.customSpellchecker.key,
27
30
  }), (0, get_languages_list_1.default)(config.customSpellchecker));
28
- app.post('/spellcheck', json_response_1.default, (0, crowdin_client_1.default)({
31
+ app.post('/spellchecker/spellcheck', json_response_1.default, (0, crowdin_client_1.default)({
29
32
  config,
30
33
  optional: false,
31
34
  checkSubscriptionExpiration: true,
32
35
  moduleKey: config.customSpellchecker.key,
33
36
  }), (0, spell_check_1.default)(config.customSpellchecker));
37
+ // TEMPORARY CODE: it needs to support old path
38
+ app.get('/languages', json_response_1.default, (0, crowdin_client_1.default)({
39
+ config,
40
+ optional: false,
41
+ checkSubscriptionExpiration: true,
42
+ }), (0, get_languages_list_1.default)(config.customSpellchecker));
43
+ app.post('/spellcheck', json_response_1.default, (0, crowdin_client_1.default)({
44
+ config,
45
+ optional: false,
46
+ checkSubscriptionExpiration: true,
47
+ }), (0, spell_check_1.default)(config.customSpellchecker));
48
+ // END TEMPORARY CODE
34
49
  return;
35
50
  }
36
51
  if (config.customSpellchecker.settingsUiModule) {
37
- app.use('/settings', (0, ui_module_1.default)({ config, allowUnauthorized: true, moduleType: config.customSpellchecker.key }), (0, render_ui_module_1.default)(config.customSpellchecker.settingsUiModule));
52
+ app.use('/spellchecker/settings', (0, ui_module_1.default)({ config, allowUnauthorized: true, moduleType: config.customSpellchecker.key }), (0, render_ui_module_1.default)(config.customSpellchecker.settingsUiModule));
53
+ // TEMPORARY CODE: it needs to support old path
54
+ app.use('/settings', (0, ui_module_1.default)({ config, allowUnauthorized: true }), (0, render_ui_module_1.default)(config.customSpellchecker.settingsUiModule));
55
+ // END TEMPORARY CODE
38
56
  }
57
+ app.get('/spellchecker/languages', json_response_1.default, (0, get_languages_list_1.default)(config.customSpellchecker));
58
+ app.post('/spellchecker/spellcheck', json_response_1.default, (0, spell_check_1.default)(config.customSpellchecker));
59
+ // TEMPORARY CODE: it needs to support old path
39
60
  app.get('/languages', json_response_1.default, (0, get_languages_list_1.default)(config.customSpellchecker));
40
61
  app.post('/spellcheck', json_response_1.default, (0, spell_check_1.default)(config.customSpellchecker));
62
+ // END TEMPORARY CODE
41
63
  }
42
64
  exports.register = register;
@@ -15,18 +15,33 @@ function register({ config, app }) {
15
15
  return;
16
16
  }
17
17
  if (qaCheck === null || qaCheck === void 0 ? void 0 : qaCheck.batchSize) {
18
+ app.use('/qa-check/batch-size', json_response_1.default, (req, res) => {
19
+ res.send({ data: { size: qaCheck.batchSize } });
20
+ });
21
+ // TEMPORARY CODE: it needs to support old path
18
22
  app.use('/batch-size', json_response_1.default, (req, res) => {
19
23
  res.send({ data: { size: qaCheck.batchSize } });
20
24
  });
25
+ // END TEMPORARY CODE
21
26
  }
22
- app.use('/validate', json_response_1.default, (0, crowdin_client_1.default)({
27
+ app.use('/qa-check/validate', json_response_1.default, (0, crowdin_client_1.default)({
23
28
  config,
24
29
  optional: false,
25
30
  checkSubscriptionExpiration: true,
26
31
  moduleKey: qaCheck.key,
27
32
  }), (0, validate_1.default)(qaCheck));
33
+ // TEMPORARY CODE: it needs to support old path
34
+ app.use('/validate', json_response_1.default, (0, crowdin_client_1.default)({
35
+ config,
36
+ optional: false,
37
+ checkSubscriptionExpiration: true,
38
+ }), (0, validate_1.default)(qaCheck));
39
+ // END TEMPORARY CODE
28
40
  if (qaCheck.settingsUiModule) {
29
- app.use('/settings', (0, ui_module_1.default)({ config, allowUnauthorized: true, moduleType: qaCheck.key }), (0, render_ui_module_1.default)(qaCheck.settingsUiModule));
41
+ app.use('/qa-check/settings', (0, ui_module_1.default)({ config, allowUnauthorized: true, moduleType: qaCheck.key }), (0, render_ui_module_1.default)(qaCheck.settingsUiModule));
42
+ // TEMPORARY CODE: it needs to support old path
43
+ app.use('/settings', (0, ui_module_1.default)({ config, allowUnauthorized: true }), (0, render_ui_module_1.default)(qaCheck.settingsUiModule));
44
+ // END TEMPORARY CODE
30
45
  }
31
46
  }
32
47
  exports.register = register;
@@ -15,12 +15,19 @@ function registerCustomFileFormat({ config, app }) {
15
15
  return;
16
16
  }
17
17
  (0, defaults_1.applyFileProcessorsModuleDefaults)(config, config.customFileFormat);
18
- app.post('/process', (0, crowdin_client_1.default)({
18
+ app.post('/file/process', (0, crowdin_client_1.default)({
19
19
  config,
20
20
  optional: false,
21
21
  checkSubscriptionExpiration: true,
22
22
  moduleKey: config.customFileFormat.key,
23
23
  }), (0, custom_file_format_1.default)(config.baseUrl, config.customFileFormat.filesFolder || config.dbFolder, config.customFileFormat));
24
+ // TEMPORARY CODE: it needs to support old path
25
+ app.post('/process', (0, crowdin_client_1.default)({
26
+ config,
27
+ optional: false,
28
+ checkSubscriptionExpiration: true,
29
+ }), (0, custom_file_format_1.default)(config.baseUrl, config.customFileFormat.filesFolder || config.dbFolder, config.customFileFormat));
30
+ // END TEMPORARY CODE
24
31
  app.get('/file/download', (0, file_download_1.default)(config, config.customFileFormat, 'custom-file-format'));
25
32
  }
26
33
  exports.registerCustomFileFormat = registerCustomFileFormat;
@@ -21,7 +21,7 @@ const job_1 = require("../util/job");
21
21
  const files_1 = require("../util/files");
22
22
  function handle(config, integration) {
23
23
  return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
24
- var _a, _b;
24
+ var _a, _b, _c;
25
25
  const projectId = req.crowdinContext.jwtPayload.context.project_id || (req === null || req === void 0 ? void 0 : req.body.projectId);
26
26
  const uploadTranslations = req.query.uploadTranslations === 'true' || ((_a = req.body) === null || _a === void 0 ? void 0 : _a.uploadTranslations);
27
27
  req.logInfo(`Updating crowdin project ${projectId}`);
@@ -33,6 +33,11 @@ function handle(config, integration) {
33
33
  if (((_b = config.api) === null || _b === void 0 ? void 0 : _b.default) && req.body.files) {
34
34
  req.body = req.body.files;
35
35
  }
36
+ const excludedTargetLanguages = yield (0, files_1.getExcludedTargetLanguages)({
37
+ client: req.crowdinApiClient,
38
+ projectId,
39
+ languages: ((_c = req.query.languages) === null || _c === void 0 ? void 0 : _c.split(',')) || [],
40
+ });
36
41
  yield (0, job_1.runAsJob)({
37
42
  integrationId: req.crowdinContext.clientId,
38
43
  crowdinId: req.crowdinContext.crowdinId,
@@ -40,13 +45,13 @@ function handle(config, integration) {
40
45
  title: 'Sync files to Crowdin',
41
46
  payload: req.body,
42
47
  res,
43
- projectId: projectId,
48
+ projectId,
44
49
  client: req.crowdinApiClient,
45
50
  jobType: types_1.JobClientType.MANUAL,
46
51
  jobStoreType: integration.jobStoreType,
47
52
  jobCallback: (job) => __awaiter(this, void 0, void 0, function* () {
48
- var _c;
49
- if (req.body && ((_c = req.body) === null || _c === void 0 ? void 0 : _c.length)) {
53
+ var _d;
54
+ if (req.body && ((_d = req.body) === null || _d === void 0 ? void 0 : _d.length)) {
50
55
  req.body = yield (0, files_1.expandFilesTree)(req.body, req, integration, job);
51
56
  req.body = (0, lodash_uniqby_1.default)(req.body, 'id');
52
57
  }
@@ -59,6 +64,7 @@ function handle(config, integration) {
59
64
  appSettings: req.integrationSettings,
60
65
  uploadTranslations,
61
66
  job,
67
+ excludedTargetLanguages: req.query.languages ? excludedTargetLanguages : undefined,
62
68
  });
63
69
  let message;
64
70
  if ((0, files_1.isExtendedResultType)(result)) {
@@ -19,7 +19,9 @@ function handle(config, integration) {
19
19
  return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
20
20
  var _a;
21
21
  req.logInfo('Updating integration data');
22
- const rootFolder = yield (0, defaults_1.getRootFolder)(config, integration, req.crowdinApiClient, req.crowdinContext.jwtPayload.context.project_id);
22
+ const client = req.crowdinApiClient;
23
+ const projectId = req.crowdinContext.jwtPayload.context.project_id;
24
+ const rootFolder = yield (0, defaults_1.getRootFolder)(config, integration, req.crowdinApiClient, projectId);
23
25
  if (rootFolder) {
24
26
  req.logInfo(`Updating integration data for crowding root folder ${rootFolder.id}`);
25
27
  }
@@ -27,23 +29,35 @@ function handle(config, integration) {
27
29
  if (((_a = config.api) === null || _a === void 0 ? void 0 : _a.default) && req.body.files) {
28
30
  req.body = req.body.files;
29
31
  }
32
+ let payload = req.body;
33
+ if (integration.excludedTargetLanguages) {
34
+ let options = {};
35
+ if (rootFolder) {
36
+ options = {
37
+ directoryId: rootFolder.id,
38
+ recursion: 'true',
39
+ };
40
+ }
41
+ const files = (yield client.sourceFilesApi.withFetchAll().listProjectFiles(projectId, options)).data.map((d) => d.data);
42
+ payload = (0, files_1.filterLanguages)(payload, files);
43
+ }
30
44
  yield (0, job_1.runAsJob)({
31
45
  integrationId: req.crowdinContext.clientId,
32
46
  crowdinId: req.crowdinContext.crowdinId,
33
47
  type: types_1.JobType.UPDATE_TO_INTEGRATION,
34
48
  title: 'Sync files to ' + config.name,
35
- payload: req.body,
49
+ payload,
36
50
  res,
37
- projectId: req.crowdinContext.jwtPayload.context.project_id,
38
- client: req.crowdinApiClient,
51
+ projectId,
52
+ client,
39
53
  jobType: types_1.JobClientType.MANUAL,
40
54
  jobStoreType: integration.jobStoreType,
41
55
  jobCallback: (job) => __awaiter(this, void 0, void 0, function* () {
42
56
  const result = yield integration.updateIntegration({
43
- projectId: req.crowdinContext.jwtPayload.context.project_id,
44
- client: req.crowdinApiClient,
57
+ projectId,
58
+ client,
45
59
  credentials: req.integrationCredentials,
46
- request: req.body,
60
+ request: payload,
47
61
  rootFolder,
48
62
  appSettings: req.integrationSettings,
49
63
  job,
@@ -94,6 +94,7 @@ function handle(config, integration) {
94
94
  options.integrationSearchListener = integration.integrationSearchListener;
95
95
  options.checkSubscription = !(0, subscription_1.isAppFree)(config);
96
96
  options.uploadTranslations = integration.uploadTranslations;
97
+ options.excludedTargetLanguages = integration.excludedTargetLanguages;
97
98
  options.sentryData = process.env.SENTRY_DSN
98
99
  ? {
99
100
  dsn: process.env.SENTRY_DSN,
@@ -34,7 +34,7 @@ export interface IntegrationLogic extends ModuleKey {
34
34
  /**
35
35
  * function to update crowdin files (e.g. pull integration data to crowdin source files)
36
36
  */
37
- updateCrowdin: ({ projectId, client, credentials, request, rootFolder, appSettings, uploadTranslations, job, }: {
37
+ updateCrowdin: ({ projectId, client, credentials, request, rootFolder, appSettings, uploadTranslations, job, excludedTargetLanguages, }: {
38
38
  projectId: number;
39
39
  client: Crowdin;
40
40
  credentials: any;
@@ -43,6 +43,7 @@ export interface IntegrationLogic extends ModuleKey {
43
43
  appSettings?: any;
44
44
  uploadTranslations?: boolean;
45
45
  job: JobClient;
46
+ excludedTargetLanguages?: string[];
46
47
  }) => Promise<void | ExtendedResult<void>>;
47
48
  /**
48
49
  * function to update integration content (e.g. load crowdin translations and push them to integration service)
@@ -64,6 +65,14 @@ export interface IntegrationLogic extends ModuleKey {
64
65
  * function to define configuration(settings) modal for you app (by default app will not have any custom settings)
65
66
  */
66
67
  getConfiguration?: (projectId: number, client: Crowdin, apiCredentials: any) => Promise<FormEntity[]>;
68
+ /**
69
+ * function to normalize saved settings
70
+ */
71
+ normalizeSettings?: ({ appSettings, apiCredentials, client, }: {
72
+ appSettings: FormEntity[];
73
+ apiCredentials: any;
74
+ client?: Crowdin;
75
+ }) => Promise<FormEntity[]>;
67
76
  /**
68
77
  * Logout hook for cleanup logic
69
78
  */
@@ -117,6 +126,10 @@ export interface IntegrationLogic extends ModuleKey {
117
126
  * Enable the option to upload translations to crowdin that are already present in the integration.
118
127
  */
119
128
  uploadTranslations?: boolean;
129
+ /**
130
+ * Enable the option to upload file for translation into selected languages.
131
+ */
132
+ excludedTargetLanguages?: boolean;
120
133
  /**
121
134
  * function to get crowdin file translation progress
122
135
  */
@@ -291,6 +304,7 @@ export interface File {
291
304
  customContent?: string;
292
305
  labels?: LabelTreeElement[];
293
306
  failed?: boolean;
307
+ excludedTargetLanguages?: string[];
294
308
  }
295
309
  export interface Folder {
296
310
  id: string;
@@ -116,6 +116,7 @@ function applyIntegrationModuleDefaults(config, integration) {
116
116
  parentId: parentId ? parentId.toString() : undefined,
117
117
  name: e.title || e.name,
118
118
  type: e.type,
119
+ excludedTargetLanguages: e.excludedTargetLanguages,
119
120
  });
120
121
  });
121
122
  return res;
@@ -1,6 +1,7 @@
1
- import { ExtendedResult, IntegrationFile, IntegrationLogic, IntegrationRequest, SkipIntegrationNodes, TreeItem } from '../types';
1
+ import { ExtendedResult, IntegrationFile, IntegrationLogic, IntegrationRequest, SkipIntegrationNodes, TreeItem, UpdateIntegrationRequest } from '../types';
2
2
  import { JobClient } from './types';
3
3
  import Crowdin from '@crowdin/crowdin-api-client';
4
+ import { SourceFilesModel } from '@crowdin/crowdin-api-client';
4
5
  export declare function skipFilesByRegex(files: TreeItem[] | undefined, skipIntegrationNodes?: SkipIntegrationNodes): TreeItem[];
5
6
  export declare function expandFilesTree(nodes: IntegrationFile[], req: IntegrationRequest, integration: IntegrationLogic, job?: JobClient): Promise<IntegrationFile[]>;
6
7
  export declare function isExtendedResultType<T>(data?: T | ExtendedResult<T>): data is ExtendedResult<T>;
@@ -10,3 +11,9 @@ export declare function markUnsyncedFiles({ integrationId, crowdinId, client, fi
10
11
  client: Crowdin;
11
12
  files?: TreeItem[];
12
13
  }): Promise<TreeItem[]>;
14
+ export declare function getExcludedTargetLanguages({ client, projectId, languages, }: {
15
+ client: Crowdin;
16
+ projectId: number;
17
+ languages: string[];
18
+ }): Promise<string[]>;
19
+ export declare function filterLanguages(request: UpdateIntegrationRequest, files: SourceFilesModel.File[]): UpdateIntegrationRequest;
@@ -9,7 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  });
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.markUnsyncedFiles = exports.isExtendedResultType = exports.expandFilesTree = exports.skipFilesByRegex = void 0;
12
+ exports.filterLanguages = exports.getExcludedTargetLanguages = exports.markUnsyncedFiles = exports.isExtendedResultType = exports.expandFilesTree = exports.skipFilesByRegex = void 0;
13
13
  const types_1 = require("./types");
14
14
  const storage_1 = require("../../../storage");
15
15
  const util_1 = require("../../../util");
@@ -98,3 +98,26 @@ function markUnsyncedFiles({ integrationId, crowdinId, client, files, }) {
98
98
  });
99
99
  }
100
100
  exports.markUnsyncedFiles = markUnsyncedFiles;
101
+ function getExcludedTargetLanguages({ client, projectId, languages, }) {
102
+ return __awaiter(this, void 0, void 0, function* () {
103
+ const projectData = yield client.projectsGroupsApi.getProject(projectId);
104
+ const targetLanguages = projectData.data.targetLanguageIds;
105
+ return targetLanguages.filter((language) => !languages.includes(language));
106
+ });
107
+ }
108
+ exports.getExcludedTargetLanguages = getExcludedTargetLanguages;
109
+ function filterLanguages(request, files) {
110
+ const result = {};
111
+ for (const fileId in request) {
112
+ const file = files.find((f) => f.id === parseInt(fileId, 10));
113
+ if (file) {
114
+ const excludedLanguages = new Set((file === null || file === void 0 ? void 0 : file.excludedTargetLanguages) || []);
115
+ result[fileId] = request[fileId].filter((lang) => !excludedLanguages.has(lang));
116
+ }
117
+ else {
118
+ result[fileId] = request[fileId];
119
+ }
120
+ }
121
+ return result;
122
+ }
123
+ exports.filterLanguages = filterLanguages;