@crowdin/app-project-module 0.53.1 → 0.54.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -37,7 +37,11 @@ function filterSyncFiles(args) {
37
37
  if (!projectData) {
38
38
  projectData = yield crowdinClient.projectsGroupsApi.getProject(projectId);
39
39
  }
40
- addFileToResponse(fileId, projectData.data.targetLanguageIds);
40
+ const projectLanguages = projectData.data.targetLanguageIds;
41
+ if (projectData.data.inContext) {
42
+ projectLanguages.push(projectData.data.inContextPseudoLanguageId);
43
+ }
44
+ addFileToResponse(fileId, projectLanguages);
41
45
  continue;
42
46
  }
43
47
  // sync file that was selected for sync schedule
@@ -14,11 +14,18 @@ const util_1 = require("../../../util");
14
14
  const logger_1 = require("../../../util/logger");
15
15
  function handle(config, integration) {
16
16
  return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
17
+ var _a;
17
18
  req.logInfo('Received integration login request');
19
+ let credentials = req.body.credentials;
20
+ if ((_a = integration.loginForm) === null || _a === void 0 ? void 0 : _a.performGetTokenRequest) {
21
+ req.logInfo('Performing custom get bearer token request');
22
+ const tokenCredentials = yield integration.loginForm.performGetTokenRequest(credentials);
23
+ credentials = Object.assign(Object.assign({}, credentials), { expireIn: Number(tokenCredentials.expires_in) + Date.now() / 1000, accessToken: tokenCredentials.access_token, refreshToken: tokenCredentials.refresh_token });
24
+ }
18
25
  if (integration.checkConnection) {
19
26
  try {
20
27
  req.logInfo('Checking the integration credentials');
21
- yield integration.checkConnection(req.body.credentials);
28
+ yield integration.checkConnection(credentials);
22
29
  }
23
30
  catch (e) {
24
31
  yield (0, logger_1.handleUserError)({
@@ -35,7 +42,7 @@ function handle(config, integration) {
35
42
  req.logInfo('Deleting old credentials');
36
43
  yield (0, storage_1.getStorage)().deleteIntegrationCredentials(req.crowdinContext.clientId);
37
44
  }
38
- yield (0, storage_1.getStorage)().saveIntegrationCredentials(req.crowdinContext.clientId, (0, util_1.encryptData)(config, JSON.stringify(req.body.credentials)), req.crowdinContext.crowdinId);
45
+ yield (0, storage_1.getStorage)().saveIntegrationCredentials(req.crowdinContext.clientId, (0, util_1.encryptData)(config, JSON.stringify(credentials)), req.crowdinContext.crowdinId);
39
46
  res.status(204).end();
40
47
  }));
41
48
  }
@@ -25,11 +25,11 @@ function handle(config, integration) {
25
25
  webhookUrlParam,
26
26
  provider: types_1.Provider.INTEGRATION,
27
27
  });
28
- if (!webhookData.crowdinClient) {
28
+ if (!(webhookData === null || webhookData === void 0 ? void 0 : webhookData.crowdinClient)) {
29
29
  (0, logger_1.temporaryErrorDebug)('Access denied: integration-webhooks !webhookData.crowdinClient', req);
30
30
  return res.status(403).send({ error: 'Access denied' });
31
31
  }
32
- if (!webhookData.syncSettings) {
32
+ if (!(webhookData === null || webhookData === void 0 ? void 0 : webhookData.syncSettings)) {
33
33
  return res.status(200).send({ message: 'Sync is not configured' });
34
34
  }
35
35
  yield (0, webhooks_1.updateCrowdinFromWebhookRequest)({ integration, webhookData, req: [req] });
@@ -145,6 +145,18 @@ export interface IntegrationLogic {
145
145
  }
146
146
  export interface LoginForm {
147
147
  fields: FormEntity[];
148
+ /**
149
+ * Override to implement request for retrieving access token (and refresh token if 'refresh' is enabled)
150
+ */
151
+ performGetTokenRequest?: (fieldsCredentials: any) => Promise<LoginFormTokenResponse>;
152
+ /**
153
+ * Override to implement request for refreshing token (only if 'refresh' is enabled)
154
+ */
155
+ performRefreshTokenRequest?: (currentCredentials: any) => Promise<LoginFormTokenResponse>;
156
+ /**
157
+ * default 'false' which means that the access token has no expiration date
158
+ */
159
+ refresh?: boolean;
148
160
  }
149
161
  export interface OAuthLogin {
150
162
  /**
@@ -440,4 +452,9 @@ export interface AppSettings {
440
452
  'new-integration-files'?: boolean;
441
453
  [key: string]: any;
442
454
  }
455
+ interface LoginFormTokenResponse {
456
+ access_token: string;
457
+ expires_in: number;
458
+ refresh_token: string;
459
+ }
443
460
  export {};
@@ -131,6 +131,10 @@ function filesCron({ config, integration, period, }) {
131
131
  (0, logger_1.log)(`Subscription expired. Skipping job [${period}] for organization ${crowdinCredentials.id}`);
132
132
  continue;
133
133
  }
134
+ const projectData = (yield crowdinClient.projectsGroupsApi.getProject(projectId))
135
+ .data;
136
+ // eslint-disable-next-line @typescript-eslint/camelcase
137
+ context.jwtPayload.context.project_identifier = projectData.identifier;
134
138
  const rootFolder = yield (0, defaults_1.getRootFolder)(config, integration, crowdinClient, projectId);
135
139
  const apiCredentials = yield (0, connection_1.prepareIntegrationCredentials)(config, integration, integrationCredentials);
136
140
  if (!integration.webhooks &&
@@ -139,13 +143,13 @@ function filesCron({ config, integration, period, }) {
139
143
  newFiles = yield getAllNewFiles({
140
144
  config,
141
145
  integration,
146
+ projectData,
147
+ syncSettings,
142
148
  crowdinApiClient: crowdinClient,
143
149
  crowdinId: crowdinCredentials.id,
144
150
  integrationCredentials: apiCredentials,
145
151
  integrationId: integrationCredentials.id,
146
- projectId,
147
152
  integrationSettings: intConfig,
148
- syncSettings,
149
153
  });
150
154
  }
151
155
  if (integration.webhooks) {
@@ -192,6 +196,9 @@ function filesCron({ config, integration, period, }) {
192
196
  }
193
197
  }
194
198
  const apiCredentials = yield (0, connection_1.prepareIntegrationCredentials)(config, integration, integrationCredentials);
199
+ if (!(intConfig === null || intConfig === void 0 ? void 0 : intConfig.inContext)) {
200
+ removeInContextLanguage(filesToProcess, projectData);
201
+ }
195
202
  try {
196
203
  yield (0, job_1.runAsJob)({
197
204
  integrationId: syncSettings.integrationId,
@@ -276,12 +283,12 @@ function getFileDiff(currentFiles, savedFiles) {
276
283
  }
277
284
  function getAllNewFiles(args) {
278
285
  return __awaiter(this, void 0, void 0, function* () {
279
- const { config, integration, crowdinApiClient, crowdinId, integrationCredentials, integrationId, projectId, integrationSettings, syncSettings, } = args;
286
+ const { config, integration, crowdinApiClient, crowdinId, integrationCredentials, integrationId, projectData, integrationSettings, syncSettings, } = args;
280
287
  let currentFileSnapshot = [];
281
288
  const fileSnapshotData = yield (0, storage_1.getStorage)().getFilesSnapshot(integrationId, crowdinId, syncSettings.provider);
282
289
  const snapshotFiles = (fileSnapshotData === null || fileSnapshotData === void 0 ? void 0 : fileSnapshotData.files) ? JSON.parse(fileSnapshotData.files) : [];
283
290
  if (syncSettings.provider === types_1.Provider.CROWDIN) {
284
- currentFileSnapshot = yield (0, snapshot_1.getCrowdinSnapshot)(config, integration, crowdinApiClient, projectId, integrationSettings);
291
+ currentFileSnapshot = yield (0, snapshot_1.getCrowdinSnapshot)(config, integration, crowdinApiClient, projectData.id, integrationSettings);
285
292
  }
286
293
  else {
287
294
  currentFileSnapshot = yield (0, snapshot_1.getIntegrationSnapshot)(integration, integrationCredentials, integrationSettings);
@@ -298,8 +305,10 @@ function getAllNewFiles(args) {
298
305
  }
299
306
  else {
300
307
  const files = {};
301
- const projectData = yield crowdinApiClient.projectsGroupsApi.getProject(projectId);
302
- const targetLanguages = projectData.data.targetLanguageIds;
308
+ const targetLanguages = projectData.targetLanguageIds;
309
+ if (projectData.inContext) {
310
+ targetLanguages.push(projectData.inContextPseudoLanguageId);
311
+ }
303
312
  if (integrationSettings[`new-${syncSettings.provider}-files`]) {
304
313
  for (const file of onlyFiles) {
305
314
  files[file.id] = targetLanguages;
@@ -429,3 +438,13 @@ function removeFinishedJobs() {
429
438
  });
430
439
  }
431
440
  exports.removeFinishedJobs = removeFinishedJobs;
441
+ function removeInContextLanguage(filesToProcess, projectData) {
442
+ (0, logger_1.log)('Removing in-context language from files to process');
443
+ if (!projectData.inContext) {
444
+ return;
445
+ }
446
+ for (const fileId in filesToProcess) {
447
+ filesToProcess[fileId] = filesToProcess[fileId].filter((language) => language !== projectData.inContextPseudoLanguageId);
448
+ }
449
+ (0, logger_1.log)('In-context language(' + projectData.inContextPseudoLanguageId + ') removed from files to process');
450
+ }
@@ -138,15 +138,24 @@ function applyIntegrationModuleDefaults(config, integration) {
138
138
  ],
139
139
  };
140
140
  }
141
- if (integration.withCronSync || integration.webhooks) {
142
- const getUserSettings = integration.getConfiguration;
143
- integration.getConfiguration = (projectId, crowdinClient, integrationCredentials) => __awaiter(this, void 0, void 0, function* () {
144
- var _c, _d;
145
- let fields = [];
146
- if (getUserSettings) {
147
- fields = yield getUserSettings(projectId, crowdinClient, integrationCredentials);
148
- }
149
- const defaultSettings = [];
141
+ const getUserSettings = integration.getConfiguration;
142
+ integration.getConfiguration = (projectId, crowdinClient, integrationCredentials) => __awaiter(this, void 0, void 0, function* () {
143
+ var _c, _d;
144
+ let fields = [];
145
+ const project = (yield crowdinClient.projectsGroupsApi.getProject(projectId));
146
+ if (getUserSettings) {
147
+ fields = yield getUserSettings(projectId, crowdinClient, integrationCredentials);
148
+ }
149
+ const defaultSettings = [];
150
+ if (project.data.inContext) {
151
+ defaultSettings.push({
152
+ key: 'inContext',
153
+ label: 'Show In-Context Pseudo Language',
154
+ type: 'checkbox',
155
+ defaultValue: 'false',
156
+ });
157
+ }
158
+ if (integration.withCronSync || integration.webhooks) {
150
159
  defaultSettings.push({
151
160
  key: 'schedule',
152
161
  label: 'Auto sync',
@@ -211,33 +220,30 @@ function applyIntegrationModuleDefaults(config, integration) {
211
220
  type: 'checkbox',
212
221
  });
213
222
  }
214
- return [
215
- ...defaultSettings,
216
- {
217
- key: 'condition',
218
- label: 'Files export settings',
219
- type: 'select',
220
- defaultValue: '0',
221
- dependencySettings: JSON.stringify([{ '#schedule-settings': { type: '!equal', value: ['0'] } }]),
222
- options: [
223
- {
224
- value: '0',
225
- label: 'Export all',
226
- },
227
- {
228
- value: '1',
229
- label: 'Export translated only',
230
- },
231
- {
232
- value: '2',
233
- label: 'Export approved only',
234
- },
235
- ],
236
- },
237
- ...fields,
238
- ];
239
- });
240
- }
223
+ defaultSettings.push({
224
+ key: 'condition',
225
+ label: 'Files export settings',
226
+ type: 'select',
227
+ defaultValue: '0',
228
+ dependencySettings: JSON.stringify([{ '#schedule-settings': { type: '!equal', value: ['0'] } }]),
229
+ options: [
230
+ {
231
+ value: '0',
232
+ label: 'Export all',
233
+ },
234
+ {
235
+ value: '1',
236
+ label: 'Export translated only',
237
+ },
238
+ {
239
+ value: '2',
240
+ label: 'Export approved only',
241
+ },
242
+ ],
243
+ });
244
+ }
245
+ return [...defaultSettings, ...fields];
246
+ });
241
247
  if (!integration.checkConnection) {
242
248
  integration.checkConnection = (apiCredentials) => __awaiter(this, void 0, void 0, function* () {
243
249
  yield integration.getIntegrationFiles(apiCredentials);
@@ -1,4 +1,4 @@
1
- import Crowdin from '@crowdin/crowdin-api-client';
1
+ import Crowdin, { ProjectsGroupsModel } from '@crowdin/crowdin-api-client';
2
2
  import { Config } from '../../../types';
3
3
  import { IntegrationLogic, IntegrationSyncSettings } from '../types';
4
4
  export declare enum JobType {
@@ -59,7 +59,7 @@ export interface GetAllNewFilesArgs {
59
59
  crowdinId: string;
60
60
  integrationCredentials: any;
61
61
  integrationId: string;
62
- projectId: number;
62
+ projectData: ProjectsGroupsModel.ProjectSettings;
63
63
  integrationSettings: any;
64
64
  syncSettings: IntegrationSyncSettings;
65
65
  }
@@ -255,6 +255,7 @@ function prepareWebhookData({ config, integration, provider, webhookUrlParam, })
255
255
  return __awaiter(this, void 0, void 0, function* () {
256
256
  let rootFolder = undefined;
257
257
  let syncSettings = null;
258
+ let preparedIntegrationCredentials = null;
258
259
  let appSettings = {};
259
260
  const { projectId, crowdinId, clientId } = decodedUrlParam(config, webhookUrlParam);
260
261
  const crowdinCredentials = yield (0, storage_1.getStorage)().getCrowdinCredentials(crowdinId);
@@ -277,7 +278,10 @@ function prepareWebhookData({ config, integration, provider, webhookUrlParam, })
277
278
  credentials: crowdinCredentials,
278
279
  context,
279
280
  });
280
- const preparedIntegrationCredentials = yield (0, connection_1.prepareIntegrationCredentials)(config, integration, integrationCredentials);
281
+ if (!integrationCredentials) {
282
+ return { projectId, crowdinClient, rootFolder, appSettings, syncSettings, preparedIntegrationCredentials };
283
+ }
284
+ preparedIntegrationCredentials = yield (0, connection_1.prepareIntegrationCredentials)(config, integration, integrationCredentials);
281
285
  if (integrationConfig === null || integrationConfig === void 0 ? void 0 : integrationConfig.config) {
282
286
  appSettings = JSON.parse(integrationConfig.config);
283
287
  const isWebhookSync = Number(appSettings.schedule) !== types_1.SyncSchedule.DISABLED;
@@ -387,7 +391,9 @@ function consumer({ channel, config, integration, }) {
387
391
  webhookUrlParam,
388
392
  provider: types_1.Provider.INTEGRATION,
389
393
  }).then((res) => {
390
- webhooksInfo[clientId].webhookData = res;
394
+ if (res) {
395
+ webhooksInfo[clientId].webhookData = res;
396
+ }
391
397
  }));
392
398
  }
393
399
  else {
@@ -425,11 +431,13 @@ function processMessages({ channel, msg, webhooksData, webhooksInfo, }) {
425
431
  return __awaiter(this, void 0, void 0, function* () {
426
432
  yield Promise.all(webhooksData);
427
433
  for (const { data, integration, webhookData } of Object.values(webhooksInfo)) {
428
- yield updateCrowdinFromWebhookRequest({
429
- integration: integration,
430
- webhookData: webhookData,
431
- req: data,
432
- });
434
+ if (webhookData) {
435
+ yield updateCrowdinFromWebhookRequest({
436
+ integration: integration,
437
+ webhookData: webhookData,
438
+ req: data,
439
+ });
440
+ }
433
441
  }
434
442
  channel.ack(msg, true);
435
443
  });