@crowdin/app-project-module 0.53.0 → 0.54.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.
@@ -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
@@ -38,6 +38,7 @@ function handle(integration) {
38
38
  crowdinId: req.crowdinContext.crowdinId,
39
39
  clientId: req.crowdinContext.clientId,
40
40
  });
41
+ res.send({ data: [], message: (e === null || e === void 0 ? void 0 : e.message) || e });
41
42
  throw e;
42
43
  }
43
44
  req.logInfo(`Integration data response ${JSON.stringify(files, null, 2)}`);
@@ -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
  }
@@ -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
  /**
@@ -287,7 +299,7 @@ export interface FormField {
287
299
  helpText?: string;
288
300
  helpTextHtml?: string;
289
301
  label: string;
290
- type?: 'text' | 'password' | 'checkbox' | 'select' | 'textarea' | 'file';
302
+ type?: 'text' | 'password' | 'checkbox' | 'select' | 'textarea' | 'file' | 'notice';
291
303
  defaultValue?: any;
292
304
  /**
293
305
  * only for select
@@ -312,6 +324,14 @@ export interface FormField {
312
324
  * field dependency settings
313
325
  */
314
326
  dependencySettings?: string;
327
+ /**
328
+ * only for notice type
329
+ */
330
+ noticeType?: string;
331
+ /**
332
+ * only for notice type
333
+ */
334
+ noIcon?: boolean;
315
335
  }
316
336
  type NoticeType = 'info' | 'warning' | 'danger' | 'success' | 'error' | 'dataLostWarning';
317
337
  export interface IntegrationCredentials {
@@ -432,4 +452,9 @@ export interface AppSettings {
432
452
  'new-integration-files'?: boolean;
433
453
  [key: string]: any;
434
454
  }
455
+ interface LoginFormTokenResponse {
456
+ access_token: string;
457
+ expires_in: number;
458
+ refresh_token: string;
459
+ }
435
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,
@@ -215,7 +222,7 @@ function filesCron({ config, integration, period, }) {
215
222
  }
216
223
  catch (e) {
217
224
  (0, logger_1.logError)(e, context);
218
- throw e;
225
+ continue;
219
226
  }
220
227
  if (Object.keys(newFiles).length) {
221
228
  yield (0, storage_1.getStorage)().updateSyncSettings(JSON.stringify(Object.assign(Object.assign({}, files), newFiles)), syncSettings.integrationId, syncSettings.crowdinId, 'schedule', syncSettings.provider);
@@ -256,7 +263,7 @@ function filesCron({ config, integration, period, }) {
256
263
  }
257
264
  catch (e) {
258
265
  (0, logger_1.logError)(e, context);
259
- throw e;
266
+ continue;
260
267
  }
261
268
  if (Object.keys(newFiles).length) {
262
269
  const newSyncSettingsFields = allIntFiles.map((file) => (Object.assign(Object.assign({}, file), { schedule: true, sync: false })));
@@ -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
  }