@crowdin/app-project-module 0.23.5 → 0.24.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/README.md CHANGED
@@ -833,6 +833,12 @@ configuration.jwtValidationOptions = {
833
833
  };
834
834
  ```
835
835
 
836
+ ### App authentication type
837
+
838
+ ```js
839
+ configuration.authenticationType = 'authorization_code'; //default is "crowdin_app"
840
+ ```
841
+
836
842
  ## Contributing
837
843
 
838
844
  If you want to contribute please read the [Contributing](/CONTRIBUTING.md) guidelines.
@@ -16,7 +16,7 @@ function handle(config) {
16
16
  (0, util_1.log)(`Loading translation progress for file ${fileId}`, config.logger);
17
17
  const progress = yield req.crowdinApiClient.translationStatusApi.getFileProgress(req.crowdinContext.jwtPayload.context.project_id, fileId);
18
18
  (0, util_1.log)(`Translation progress for file ${fileId} ${JSON.stringify(progress.data, null, 2)}`, config.logger);
19
- res.send({ [fileId]: progress.data.map(e => e.data) });
19
+ res.send({ [fileId]: progress.data.map((e) => e.data) });
20
20
  }), config.onError);
21
21
  }
22
22
  exports.default = handle;
@@ -135,15 +135,15 @@ function handleParseFile(baseUrl, dataFolder, config, req, client, context, proj
135
135
  if (res.strings) {
136
136
  let strings = res.strings;
137
137
  if (config.autoUploadTranslations) {
138
- strings = strings.map(string => {
138
+ strings = strings.map((string) => {
139
139
  const translations = {};
140
140
  req.targetLanguages
141
- .map(targetLanguage => targetLanguage.id)
142
- .forEach(targetLanguage => (translations[targetLanguage] = { text: string.text }));
141
+ .map((targetLanguage) => targetLanguage.id)
142
+ .forEach((targetLanguage) => (translations[targetLanguage] = { text: string.text }));
143
143
  return Object.assign(Object.assign({}, string), { translations });
144
144
  });
145
145
  }
146
- const stringsNDJson = strings.map(s => JSON.stringify(s)).join('\n\r');
146
+ const stringsNDJson = strings.map((s) => JSON.stringify(s)).join('\n\r');
147
147
  if (Buffer.byteLength(stringsNDJson, 'utf8') < maxSize) {
148
148
  response.strings = strings;
149
149
  }
@@ -164,7 +164,7 @@ function handleParseFile(baseUrl, dataFolder, config, req, client, context, proj
164
164
  }
165
165
  function storeFile(fileContent, folder) {
166
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 => {
167
+ return new Promise((res, rej) => fs_1.default.writeFile(path_1.default.join(folder, 'custom-file-format', fileName), fileContent, (err) => {
168
168
  if (err) {
169
169
  rej(err);
170
170
  }
@@ -15,7 +15,7 @@ function handle(baseConfig, config) {
15
15
  const source = req.query.source;
16
16
  const target = req.query.target;
17
17
  const body = req.body;
18
- (0, util_1.log)('Recieved request for custom mt', baseConfig.logger);
18
+ (0, util_1.log)('Received request for custom mt', baseConfig.logger);
19
19
  (0, util_1.log)(`Source language ${source}, target language ${target}`, baseConfig.logger);
20
20
  (0, util_1.log)(`Payload ${JSON.stringify(body, null, 2)}`, baseConfig.logger);
21
21
  const projectId = Number(req.query.project_id);
@@ -13,25 +13,6 @@ const crowdin_apps_functions_1 = require("@crowdin/crowdin-apps-functions");
13
13
  const models_1 = require("../models");
14
14
  const storage_1 = require("../storage");
15
15
  const util_1 = require("../util");
16
- function fetchToken(config, event) {
17
- var _a, _b;
18
- return __awaiter(this, void 0, void 0, function* () {
19
- if (config.authenticationType === models_1.AuthenticationType.APP) {
20
- const token = yield (0, crowdin_apps_functions_1.fetchAppToken)(config.identifier, event.appSecret, config.clientId, config.clientSecret, event.domain || '', event.userId, (_a = config.crowdinUrls) === null || _a === void 0 ? void 0 : _a.accountUrl);
21
- return {
22
- accessToken: (0, util_1.encryptData)(config, token.accessToken),
23
- expiresIn: token.expiresIn,
24
- refreshToken: '',
25
- };
26
- }
27
- const token = yield (0, crowdin_apps_functions_1.generateOAuthToken)(config.clientId, config.clientSecret, event.code || '', (_b = config.crowdinUrls) === null || _b === void 0 ? void 0 : _b.accountUrl);
28
- return {
29
- accessToken: (0, util_1.encryptData)(config, token.accessToken),
30
- refreshToken: (0, util_1.encryptData)(config, token.refreshToken),
31
- expiresIn: token.expiresIn,
32
- };
33
- });
34
- }
35
16
  function handle(config) {
36
17
  return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
37
18
  const event = req.body;
@@ -45,7 +26,7 @@ function handle(config) {
45
26
  baseUrl: event.baseUrl,
46
27
  accessToken: token.accessToken,
47
28
  refreshToken: token.refreshToken,
48
- expire: (new Date().getTime() / 1000 + token.expiresIn).toString(),
29
+ expire: (Date.now() / 1000 + token.expiresIn).toString(),
49
30
  type: event.domain ? models_1.AccountType.ENTERPRISE : models_1.AccountType.NORMAL,
50
31
  };
51
32
  const existingCredentials = yield (0, storage_1.getStorage)().getCrowdinCredentials(credentials.id);
@@ -61,3 +42,22 @@ function handle(config) {
61
42
  }), config.onError);
62
43
  }
63
44
  exports.default = handle;
45
+ function fetchToken(config, event) {
46
+ var _a, _b;
47
+ return __awaiter(this, void 0, void 0, function* () {
48
+ if (config.authenticationType === models_1.AuthenticationType.CODE) {
49
+ const token = yield (0, crowdin_apps_functions_1.generateOAuthToken)(config.clientId, config.clientSecret, event.code || '', (_a = config.crowdinUrls) === null || _a === void 0 ? void 0 : _a.accountUrl);
50
+ return {
51
+ accessToken: (0, util_1.encryptData)(config, token.accessToken),
52
+ refreshToken: (0, util_1.encryptData)(config, token.refreshToken),
53
+ expiresIn: token.expiresIn,
54
+ };
55
+ }
56
+ const token = yield (0, crowdin_apps_functions_1.fetchAppToken)(config.identifier, event.appSecret, config.clientId, config.clientSecret, event.domain || '', event.userId, (_b = config.crowdinUrls) === null || _b === void 0 ? void 0 : _b.accountUrl);
57
+ return {
58
+ accessToken: (0, util_1.encryptData)(config, token.accessToken),
59
+ expiresIn: token.expiresIn,
60
+ refreshToken: '',
61
+ };
62
+ });
63
+ }
@@ -13,7 +13,7 @@ const storage_1 = require("../storage");
13
13
  const util_1 = require("../util");
14
14
  function handle(config, integration) {
15
15
  return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
16
- (0, util_1.log)('Recieved integration login request', config.logger);
16
+ (0, util_1.log)('Received integration login request', config.logger);
17
17
  if (integration.checkConnection) {
18
18
  (0, util_1.log)('Checking the integration credentials', config.logger);
19
19
  yield integration.checkConnection(req.body.credentials);
@@ -14,7 +14,7 @@ const util_1 = require("../util");
14
14
  const connection_1 = require("../util/connection");
15
15
  function handle(config, integration) {
16
16
  return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
17
- (0, util_1.log)('Recieved integration logout request', config.logger);
17
+ (0, util_1.log)('Received integration logout request', config.logger);
18
18
  if (integration.onLogout) {
19
19
  (0, util_1.log)('Invoking onLogout hook', config.logger);
20
20
  yield integration.onLogout(req.crowdinContext.jwtPayload.context.project_id, req.crowdinApiClient, req.integrationCredentials, req.integrationSettings);
@@ -13,10 +13,10 @@ const util_1 = require("../util");
13
13
  const defaults_1 = require("../util/defaults");
14
14
  function handle(config, integration) {
15
15
  return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
16
- (0, util_1.log)('Upading integratino data', config.logger);
16
+ (0, util_1.log)('Updating integration data', config.logger);
17
17
  const rootFolder = yield (0, defaults_1.getRootFolder)(config, integration, req.crowdinApiClient, req.crowdinContext.jwtPayload.context.project_id);
18
18
  if (rootFolder) {
19
- (0, util_1.log)(`Upading integration data for crowding root folder ${rootFolder.id}`, config.logger);
19
+ (0, util_1.log)(`Updating integration data for crowding root folder ${rootFolder.id}`, config.logger);
20
20
  }
21
21
  const result = yield integration.updateIntegration(req.crowdinContext.jwtPayload.context.project_id, req.crowdinApiClient, req.integrationCredentials, req.body, rootFolder, req.integrationSettings);
22
22
  let message;
@@ -123,7 +123,7 @@ function handle(config) {
123
123
  logo: '/logo.png',
124
124
  baseUrl: config.baseUrl,
125
125
  authentication: {
126
- type: config.authenticationType || models_1.AuthenticationType.CODE,
126
+ type: config.authenticationType || models_1.AuthenticationType.APP,
127
127
  clientId: config.clientId,
128
128
  },
129
129
  events,
@@ -22,7 +22,7 @@ function handle(config, integration) {
22
22
  uid: 'oauth_popup',
23
23
  };
24
24
  const code = req.query[((_b = (_a = integration.oauthLogin) === null || _a === void 0 ? void 0 : _a.fieldsMapping) === null || _b === void 0 ? void 0 : _b.code) || 'code'];
25
- (0, util_1.log)(`Recieved request from OAuth login callback. Code ${code}`, config.logger);
25
+ (0, util_1.log)(`Received request from OAuth login callback. Code ${code}`, config.logger);
26
26
  try {
27
27
  const oauthLogin = integration.oauthLogin;
28
28
  let credentials;
@@ -13,7 +13,7 @@ const util_1 = require("../util");
13
13
  const defaults_1 = require("../util/defaults");
14
14
  function handle(config, integration) {
15
15
  return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
16
- (0, util_1.log)('Recieved OAuth login url request', config.logger);
16
+ (0, util_1.log)('Received OAuth login url request', config.logger);
17
17
  const { oauthLogin } = integration;
18
18
  if (!oauthLogin) {
19
19
  (0, util_1.log)('OAuth login url request is not supported', config.logger);
@@ -14,7 +14,7 @@ const connection_1 = require("../util/connection");
14
14
  function handle(config) {
15
15
  return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
16
16
  const organizationId = (req.query || {})['organization_id'] || (req.body || {})['organization_id'];
17
- (0, util_1.log)(`Recieved subscription paid request for organization ${organizationId}`, config.logger);
17
+ (0, util_1.log)(`Received subscription paid request for organization ${organizationId}`, config.logger);
18
18
  (0, connection_1.clearCache)(organizationId);
19
19
  res.status(204).end();
20
20
  }), config.onError);
@@ -15,7 +15,7 @@ const connection_1 = require("../util/connection");
15
15
  function handle(config) {
16
16
  return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
17
17
  const event = req.body;
18
- (0, util_1.log)(`Recieved uninstall request ${JSON.stringify(event, null, 2)}`, config.logger);
18
+ (0, util_1.log)(`Received uninstall request ${JSON.stringify(event, null, 2)}`, config.logger);
19
19
  const projectIntegration = config.projectIntegration;
20
20
  const organization = (event.domain || event.organizationId).toString();
21
21
  if (config.onUninstall) {
package/out/index.js CHANGED
@@ -121,7 +121,7 @@ function addCrowdinEndpoints(app, config) {
121
121
  app.post('/api/oauth-url', json_response_1.default, (0, crowdin_client_1.default)(config, false, false), (0, oauth_url_1.default)(config, integrationLogic));
122
122
  }
123
123
  if (integrationLogic.cronJobs) {
124
- integrationLogic.cronJobs.forEach(job => {
124
+ integrationLogic.cronJobs.forEach((job) => {
125
125
  cron.schedule(job.expression, () => (0, cron_1.runJob)(config, integrationLogic, job).catch(console.error));
126
126
  });
127
127
  }
@@ -184,7 +184,7 @@ function addCrowdinEndpoints(app, config) {
184
184
  return JSON.parse(integrationCredentials.config);
185
185
  }
186
186
  }),
187
- establishCrowdinConnection: jwtToken => (0, crowdin_client_1.prepareCrowdinRequest)(jwtToken, config),
187
+ establishCrowdinConnection: (jwtToken) => (0, crowdin_client_1.prepareCrowdinRequest)(jwtToken, config),
188
188
  };
189
189
  }
190
190
  exports.addCrowdinEndpoints = addCrowdinEndpoints;
@@ -5,7 +5,7 @@ import { MySQLStorageConfig } from '../storage/mysql';
5
5
  import { PostgreStorageConfig } from '../storage/postgre';
6
6
  export interface Config extends ImagePath {
7
7
  /**
8
- * Authentication Crowdin App type: "authorization_code", "crowdin_app". Default: "authorization_code"
8
+ * Authentication Crowdin App type: "authorization_code", "crowdin_app". Default: "crowdin_app"
9
9
  */
10
10
  authenticationType?: AuthenticationType;
11
11
  /**
@@ -589,7 +589,7 @@ export interface IntegrationSyncSettings {
589
589
  files?: any;
590
590
  integrationId: string;
591
591
  crowdinId: string;
592
- provider: string;
592
+ provider: 'integration' | 'crowdin';
593
593
  }
594
594
  interface ImagePath {
595
595
  /**
@@ -108,7 +108,7 @@ class MySQLStorage {
108
108
  saveCrowdinCredentials(credentials) {
109
109
  return __awaiter(this, void 0, void 0, function* () {
110
110
  yield this.dbPromise;
111
- yield this.executeQuery(connection => connection.execute('INSERT INTO crowdin_credentials(id, app_secret, domain, user_id, organization_id, base_url, access_token, refresh_token, expire, type) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', [
111
+ yield this.executeQuery((connection) => connection.execute('INSERT INTO crowdin_credentials(id, app_secret, domain, user_id, organization_id, base_url, access_token, refresh_token, expire, type) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', [
112
112
  credentials.id,
113
113
  credentials.appSecret,
114
114
  credentials.domain,
@@ -125,7 +125,7 @@ class MySQLStorage {
125
125
  updateCrowdinCredentials(credentials) {
126
126
  return __awaiter(this, void 0, void 0, function* () {
127
127
  yield this.dbPromise;
128
- yield this.executeQuery(connection => connection.execute('UPDATE crowdin_credentials SET app_secret = ?, domain = ?, user_id = ?, organization_id = ?, base_url = ?, access_token = ?, refresh_token = ?, expire = ? WHERE id = ?', [
128
+ yield this.executeQuery((connection) => connection.execute('UPDATE crowdin_credentials SET app_secret = ?, domain = ?, user_id = ?, organization_id = ?, base_url = ?, access_token = ?, refresh_token = ?, expire = ? WHERE id = ?', [
129
129
  credentials.appSecret,
130
130
  credentials.domain,
131
131
  credentials.userId,
@@ -142,7 +142,7 @@ class MySQLStorage {
142
142
  return __awaiter(this, void 0, void 0, function* () {
143
143
  yield this.dbPromise;
144
144
  return this.executeQuery((connection) => __awaiter(this, void 0, void 0, function* () {
145
- const [rows,] = yield connection.execute('SELECT id, app_secret as "appSecret", domain, user_id as "userId", organization_id as "organizationId", base_url as "baseUrl", access_token as "accessToken", refresh_token as "refreshToken", expire, type FROM crowdin_credentials WHERE id = ?', [id]);
145
+ const [rows] = yield connection.execute('SELECT id, app_secret as "appSecret", domain, user_id as "userId", organization_id as "organizationId", base_url as "baseUrl", access_token as "accessToken", refresh_token as "refreshToken", expire, type FROM crowdin_credentials WHERE id = ?', [id]);
146
146
  return (rows || [])[0];
147
147
  }));
148
148
  });
@@ -169,7 +169,7 @@ class MySQLStorage {
169
169
  saveIntegrationCredentials(id, credentials, crowdinId) {
170
170
  return __awaiter(this, void 0, void 0, function* () {
171
171
  yield this.dbPromise;
172
- yield this.executeQuery(connection => connection.execute('INSERT INTO integration_credentials(id, credentials, crowdin_id) VALUES (?, ?, ?)', [
172
+ yield this.executeQuery((connection) => connection.execute('INSERT INTO integration_credentials(id, credentials, crowdin_id) VALUES (?, ?, ?)', [
173
173
  id,
174
174
  credentials,
175
175
  crowdinId,
@@ -179,20 +179,20 @@ class MySQLStorage {
179
179
  updateIntegrationCredentials(id, credentials) {
180
180
  return __awaiter(this, void 0, void 0, function* () {
181
181
  yield this.dbPromise;
182
- yield this.executeQuery(connection => connection.execute('UPDATE integration_credentials SET credentials = ? WHERE id = ?', [credentials, id]));
182
+ yield this.executeQuery((connection) => connection.execute('UPDATE integration_credentials SET credentials = ? WHERE id = ?', [credentials, id]));
183
183
  });
184
184
  }
185
185
  updateIntegrationConfig(id, config) {
186
186
  return __awaiter(this, void 0, void 0, function* () {
187
187
  yield this.dbPromise;
188
- yield this.executeQuery(connection => connection.execute('UPDATE integration_credentials SET config = ? WHERE id = ?', [config, id]));
188
+ yield this.executeQuery((connection) => connection.execute('UPDATE integration_credentials SET config = ? WHERE id = ?', [config, id]));
189
189
  });
190
190
  }
191
191
  getIntegrationCredentials(id) {
192
192
  return __awaiter(this, void 0, void 0, function* () {
193
193
  yield this.dbPromise;
194
194
  return this.executeQuery((connection) => __awaiter(this, void 0, void 0, function* () {
195
- const [rows,] = yield connection.execute('SELECT id, credentials, config, crowdin_id as "crowdinId" FROM integration_credentials WHERE id = ?', [id]);
195
+ const [rows] = yield connection.execute('SELECT id, credentials, config, crowdin_id as "crowdinId" FROM integration_credentials WHERE id = ?', [id]);
196
196
  return (rows || [])[0];
197
197
  }));
198
198
  });
@@ -201,7 +201,7 @@ class MySQLStorage {
201
201
  return __awaiter(this, void 0, void 0, function* () {
202
202
  yield this.dbPromise;
203
203
  return this.executeQuery((connection) => __awaiter(this, void 0, void 0, function* () {
204
- const [rows,] = yield connection.execute('SELECT id, credentials, config, crowdin_id as "crowdinId" FROM integration_credentials WHERE crowdin_id = ?', [crowdinId]);
204
+ const [rows] = yield connection.execute('SELECT id, credentials, config, crowdin_id as "crowdinId" FROM integration_credentials WHERE crowdin_id = ?', [crowdinId]);
205
205
  return rows || [];
206
206
  }));
207
207
  });
@@ -227,13 +227,13 @@ class MySQLStorage {
227
227
  saveMetadata(id, metadata) {
228
228
  return __awaiter(this, void 0, void 0, function* () {
229
229
  yield this.dbPromise;
230
- yield this.executeQuery(connection => connection.execute('INSERT INTO app_metadata(id, data) VALUES (?, ?)', [id, JSON.stringify(metadata)]));
230
+ yield this.executeQuery((connection) => connection.execute('INSERT INTO app_metadata(id, data) VALUES (?, ?)', [id, JSON.stringify(metadata)]));
231
231
  });
232
232
  }
233
233
  updateMetadata(id, metadata) {
234
234
  return __awaiter(this, void 0, void 0, function* () {
235
235
  yield this.dbPromise;
236
- yield this.executeQuery(connection => connection.execute('UPDATE app_metadata SET data = ? WHERE id = ?', [JSON.stringify(metadata), id]));
236
+ yield this.executeQuery((connection) => connection.execute('UPDATE app_metadata SET data = ? WHERE id = ?', [JSON.stringify(metadata), id]));
237
237
  });
238
238
  }
239
239
  getMetadata(id) {
@@ -250,14 +250,14 @@ class MySQLStorage {
250
250
  deleteMetadata(id) {
251
251
  return __awaiter(this, void 0, void 0, function* () {
252
252
  yield this.dbPromise;
253
- yield this.executeQuery(connection => connection.execute('DELETE FROM app_metadata where id = ?', [id]));
253
+ yield this.executeQuery((connection) => connection.execute('DELETE FROM app_metadata where id = ?', [id]));
254
254
  });
255
255
  }
256
256
  getSyncSettingsByProvider(integrationId, provider) {
257
257
  return __awaiter(this, void 0, void 0, function* () {
258
258
  yield this.dbPromise;
259
259
  return this.executeQuery((connection) => __awaiter(this, void 0, void 0, function* () {
260
- const [rows,] = yield connection.execute('SELECT id, files, integration_id as "integrationId", crowdin_id as "crowdinId", type, provider FROM sync_settings WHERE integration_id = ? AND provider = ?', [integrationId, provider]);
260
+ const [rows] = yield connection.execute('SELECT id, files, integration_id as "integrationId", crowdin_id as "crowdinId", type, provider FROM sync_settings WHERE integration_id = ? AND provider = ?', [integrationId, provider]);
261
261
  return (rows || [])[0];
262
262
  }));
263
263
  });
@@ -266,7 +266,7 @@ class MySQLStorage {
266
266
  return __awaiter(this, void 0, void 0, function* () {
267
267
  yield this.dbPromise;
268
268
  return this.executeQuery((connection) => __awaiter(this, void 0, void 0, function* () {
269
- const [rows,] = yield connection.execute('SELECT id, files, integration_id as "integrationId", crowdin_id as "crowdinId", type, provider FROM sync_settings WHERE type = ?', [type]);
269
+ const [rows] = yield connection.execute('SELECT id, files, integration_id as "integrationId", crowdin_id as "crowdinId", type, provider FROM sync_settings WHERE type = ?', [type]);
270
270
  return rows || [];
271
271
  }));
272
272
  });
@@ -274,20 +274,20 @@ class MySQLStorage {
274
274
  saveSyncSettings(files, integrationId, crowdinId, type, provider) {
275
275
  return __awaiter(this, void 0, void 0, function* () {
276
276
  yield this.dbPromise;
277
- yield this.executeQuery(connection => connection.execute('INSERT INTO sync_settings(files, integration_id, crowdin_id, type, provider) VALUES (?, , ?, ?, ?)', [files, integrationId, crowdinId, type, provider]));
277
+ yield this.executeQuery((connection) => connection.execute('INSERT INTO sync_settings(files, integration_id, crowdin_id, type, provider) VALUES (?, , ?, ?, ?)', [files, integrationId, crowdinId, type, provider]));
278
278
  });
279
279
  }
280
280
  updateSyncSettings(files, integrationId, crowdinId, type, provider) {
281
281
  return __awaiter(this, void 0, void 0, function* () {
282
282
  yield this.dbPromise;
283
- yield this.executeQuery(connection => connection.execute('UPDATE sync_settings SET files = ? WHERE integration_id = ? AND crowdin_id = ? AND type = ? AND provider = ?', [files, integrationId, crowdinId, type, provider]));
283
+ yield this.executeQuery((connection) => connection.execute('UPDATE sync_settings SET files = ? WHERE integration_id = ? AND crowdin_id = ? AND type = ? AND provider = ?', [files, integrationId, crowdinId, type, provider]));
284
284
  });
285
285
  }
286
286
  getSyncSettings(integrationId, crowdinId, type, provider) {
287
287
  return __awaiter(this, void 0, void 0, function* () {
288
288
  yield this.dbPromise;
289
289
  return this.executeQuery((connection) => __awaiter(this, void 0, void 0, function* () {
290
- const [rows,] = yield connection.execute('SELECT id, files, integration_id as "integrationId", crowdin_id as "crowdinId", type FROM sync_settings WHERE integration_id = ? AND crowdin_id = ? AND type = ? AND provider = ?', [integrationId, crowdinId, type, provider]);
290
+ const [rows] = yield connection.execute('SELECT id, files, integration_id as "integrationId", crowdin_id as "crowdinId", type FROM sync_settings WHERE integration_id = ? AND crowdin_id = ? AND type = ? AND provider = ?', [integrationId, crowdinId, type, provider]);
291
291
  return (rows || [])[0];
292
292
  }));
293
293
  });
@@ -104,7 +104,7 @@ class PostgreStorage {
104
104
  saveCrowdinCredentials(credentials) {
105
105
  return __awaiter(this, void 0, void 0, function* () {
106
106
  yield this.dbPromise;
107
- yield this.executeQuery(client => client.query('INSERT INTO crowdin_credentials(id, app_secret, domain, user_id, organization_id, base_url, access_token, refresh_token, expire, type) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)', [
107
+ yield this.executeQuery((client) => client.query('INSERT INTO crowdin_credentials(id, app_secret, domain, user_id, organization_id, base_url, access_token, refresh_token, expire, type) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)', [
108
108
  credentials.id,
109
109
  credentials.appSecret,
110
110
  credentials.domain,
@@ -121,7 +121,7 @@ class PostgreStorage {
121
121
  updateCrowdinCredentials(credentials) {
122
122
  return __awaiter(this, void 0, void 0, function* () {
123
123
  yield this.dbPromise;
124
- yield this.executeQuery(client => client.query('UPDATE crowdin_credentials SET app_secret = $1, domain = $2, user_id = $3, organization_id = $4, base_url = $5, access_token = $6, refresh_token = $7, expire = $8 WHERE id = $9', [
124
+ yield this.executeQuery((client) => client.query('UPDATE crowdin_credentials SET app_secret = $1, domain = $2, user_id = $3, organization_id = $4, base_url = $5, access_token = $6, refresh_token = $7, expire = $8 WHERE id = $9', [
125
125
  credentials.appSecret,
126
126
  credentials.domain,
127
127
  credentials.userId,
@@ -165,7 +165,7 @@ class PostgreStorage {
165
165
  saveIntegrationCredentials(id, credentials, crowdinId) {
166
166
  return __awaiter(this, void 0, void 0, function* () {
167
167
  yield this.dbPromise;
168
- yield this.executeQuery(client => client.query('INSERT INTO integration_credentials(id, credentials, crowdin_id) VALUES ($1, $2, $3)', [
168
+ yield this.executeQuery((client) => client.query('INSERT INTO integration_credentials(id, credentials, crowdin_id) VALUES ($1, $2, $3)', [
169
169
  id,
170
170
  credentials,
171
171
  crowdinId,
@@ -175,13 +175,13 @@ class PostgreStorage {
175
175
  updateIntegrationCredentials(id, credentials) {
176
176
  return __awaiter(this, void 0, void 0, function* () {
177
177
  yield this.dbPromise;
178
- yield this.executeQuery(client => client.query('UPDATE integration_credentials SET credentials = $1 WHERE id = $2', [credentials, id]));
178
+ yield this.executeQuery((client) => client.query('UPDATE integration_credentials SET credentials = $1 WHERE id = $2', [credentials, id]));
179
179
  });
180
180
  }
181
181
  updateIntegrationConfig(id, config) {
182
182
  return __awaiter(this, void 0, void 0, function* () {
183
183
  yield this.dbPromise;
184
- yield this.executeQuery(client => client.query('UPDATE integration_credentials SET config = $1 WHERE id = $2', [config, id]));
184
+ yield this.executeQuery((client) => client.query('UPDATE integration_credentials SET config = $1 WHERE id = $2', [config, id]));
185
185
  });
186
186
  }
187
187
  getIntegrationCredentials(id) {
@@ -223,13 +223,13 @@ class PostgreStorage {
223
223
  saveMetadata(id, metadata) {
224
224
  return __awaiter(this, void 0, void 0, function* () {
225
225
  yield this.dbPromise;
226
- yield this.executeQuery(client => client.query('INSERT INTO app_metadata(id, data) VALUES ($1, $2)', [id, JSON.stringify(metadata)]));
226
+ yield this.executeQuery((client) => client.query('INSERT INTO app_metadata(id, data) VALUES ($1, $2)', [id, JSON.stringify(metadata)]));
227
227
  });
228
228
  }
229
229
  updateMetadata(id, metadata) {
230
230
  return __awaiter(this, void 0, void 0, function* () {
231
231
  yield this.dbPromise;
232
- yield this.executeQuery(client => client.query('UPDATE app_metadata SET data = $1 WHERE id = $2', [JSON.stringify(metadata), id]));
232
+ yield this.executeQuery((client) => client.query('UPDATE app_metadata SET data = $1 WHERE id = $2', [JSON.stringify(metadata), id]));
233
233
  });
234
234
  }
235
235
  getMetadata(id) {
@@ -246,7 +246,7 @@ class PostgreStorage {
246
246
  deleteMetadata(id) {
247
247
  return __awaiter(this, void 0, void 0, function* () {
248
248
  yield this.dbPromise;
249
- yield this.executeQuery(client => client.query('DELETE FROM app_metadata where id = $1', [id]));
249
+ yield this.executeQuery((client) => client.query('DELETE FROM app_metadata where id = $1', [id]));
250
250
  });
251
251
  }
252
252
  getSyncSettingsByProvider(integrationId, provider) {
@@ -270,13 +270,13 @@ class PostgreStorage {
270
270
  saveSyncSettings(files, integrationId, crowdinId, type, provider) {
271
271
  return __awaiter(this, void 0, void 0, function* () {
272
272
  yield this.dbPromise;
273
- yield this.executeQuery(client => client.query('INSERT INTO sync_settings(files, integration_id, crowdin_id, type, provider) VALUES ($1, $2, $3, $4, $5)', [files, integrationId, crowdinId, type, provider]));
273
+ yield this.executeQuery((client) => client.query('INSERT INTO sync_settings(files, integration_id, crowdin_id, type, provider) VALUES ($1, $2, $3, $4, $5)', [files, integrationId, crowdinId, type, provider]));
274
274
  });
275
275
  }
276
276
  updateSyncSettings(files, integrationId, crowdinId, type, provider) {
277
277
  return __awaiter(this, void 0, void 0, function* () {
278
278
  yield this.dbPromise;
279
- yield this.executeQuery(client => client.query('UPDATE sync_settings SET files = $1 WHERE integration_id = $2 AND crowdin_id = $3 AND type = $4 AND provider = $5', [files, integrationId, crowdinId, type, provider]));
279
+ yield this.executeQuery((client) => client.query('UPDATE sync_settings SET files = $1 WHERE integration_id = $2 AND crowdin_id = $3 AND type = $4 AND provider = $5', [files, integrationId, crowdinId, type, provider]));
280
280
  });
281
281
  }
282
282
  getSyncSettings(integrationId, crowdinId, type, provider) {
@@ -28,7 +28,7 @@ class SQLiteStorage {
28
28
  yield new Promise((res, rej) => {
29
29
  var _a;
30
30
  //@ts-ignore
31
- (_a = this.db) === null || _a === void 0 ? void 0 : _a.run(query, params, err => {
31
+ (_a = this.db) === null || _a === void 0 ? void 0 : _a.run(query, params, (err) => {
32
32
  if (err) {
33
33
  rej(err);
34
34
  }
@@ -45,7 +45,7 @@ class SQLiteStorage {
45
45
  yield new Promise((res, rej) => {
46
46
  var _a;
47
47
  //@ts-ignore
48
- (_a = this.db) === null || _a === void 0 ? void 0 : _a.run(query, params, err => {
48
+ (_a = this.db) === null || _a === void 0 ? void 0 : _a.run(query, params, (err) => {
49
49
  if (err) {
50
50
  rej(err);
51
51
  }
@@ -118,7 +118,7 @@ class SQLiteStorage {
118
118
  });
119
119
  const sqlite = require('sqlite3');
120
120
  //@ts-ignore
121
- this.db = new sqlite.Database((0, path_1.join)(this.config.dbFolder, 'app.sqlite'), error => {
121
+ this.db = new sqlite.Database((0, path_1.join)(this.config.dbFolder, 'app.sqlite'), (error) => {
122
122
  if (error) {
123
123
  _connection_rej(error);
124
124
  }
@@ -22,26 +22,27 @@ const storage_1 = require("../storage");
22
22
  function refreshToken(config, credentials) {
23
23
  var _a, _b;
24
24
  return __awaiter(this, void 0, void 0, function* () {
25
- if (config.authenticationType === models_1.AuthenticationType.APP) {
26
- const token = yield (0, crowdin_apps_functions_1.fetchAppToken)(config.identifier, credentials.appSecret, config.clientId, config.clientSecret, credentials.domain || '', credentials.userId, (_a = config.crowdinUrls) === null || _a === void 0 ? void 0 : _a.accountUrl);
25
+ if (config.authenticationType === models_1.AuthenticationType.CODE) {
26
+ const token = yield (0, crowdin_apps_functions_1.refreshOAuthToken)(config.clientId, config.clientSecret, (0, _1.decryptData)(config, credentials.refreshToken), (_a = config.crowdinUrls) === null || _a === void 0 ? void 0 : _a.accountUrl);
27
27
  return {
28
28
  accessToken: (0, _1.encryptData)(config, token.accessToken),
29
+ refreshToken: (0, _1.encryptData)(config, token.refreshToken),
29
30
  expiresIn: token.expiresIn,
30
- refreshToken: '',
31
31
  };
32
32
  }
33
- const token = yield (0, crowdin_apps_functions_1.refreshOAuthToken)(config.clientId, config.clientSecret, (0, _1.decryptData)(config, credentials.refreshToken), (_b = config.crowdinUrls) === null || _b === void 0 ? void 0 : _b.accountUrl);
33
+ const token = yield (0, crowdin_apps_functions_1.fetchAppToken)(config.identifier, credentials.appSecret, config.clientId, config.clientSecret, credentials.domain || '', credentials.userId, (_b = config.crowdinUrls) === null || _b === void 0 ? void 0 : _b.accountUrl);
34
34
  return {
35
35
  accessToken: (0, _1.encryptData)(config, token.accessToken),
36
- refreshToken: (0, _1.encryptData)(config, token.refreshToken),
37
36
  expiresIn: token.expiresIn,
37
+ refreshToken: '',
38
38
  };
39
39
  });
40
40
  }
41
41
  function prepareCrowdinClient(config, credentials) {
42
42
  var _a, _b;
43
43
  return __awaiter(this, void 0, void 0, function* () {
44
- const isExpired = +credentials.expire < +new Date().getTime() / 1000;
44
+ //2 min as an extra buffer
45
+ const isExpired = +credentials.expire - 120 < Date.now() / 1000;
45
46
  const organization = credentials.type === models_1.AccountType.ENTERPRISE ? credentials.id : undefined;
46
47
  if (!isExpired) {
47
48
  const token = (0, _1.decryptData)(config, credentials.accessToken);
@@ -68,7 +69,7 @@ function prepareCrowdinClient(config, credentials) {
68
69
  const token = (0, _1.decryptData)(config, credentials.accessToken);
69
70
  return {
70
71
  client: new crowdin_api_client_1.default({ token, organization, baseUrl: (_b = config.crowdinUrls) === null || _b === void 0 ? void 0 : _b.apiUrl }),
71
- token: token,
72
+ token,
72
73
  };
73
74
  });
74
75
  }
@@ -117,14 +118,14 @@ function prepareIntegrationCredentials(config, integration, integrationCredentia
117
118
  }
118
119
  exports.prepareIntegrationCredentials = prepareIntegrationCredentials;
119
120
  const subscriptionCache = {};
120
- function addToCache(organization, appIdenfifier, validUntil, type, cachingSeconds, subscribeLink) {
121
+ function addToCache(organization, appIdentifier, validUntil, type, cachingSeconds, subscribeLink) {
121
122
  if (!cachingSeconds) {
122
123
  return;
123
124
  }
124
125
  const orgCache = subscriptionCache[organization] || {};
125
126
  const now = new Date();
126
127
  now.setSeconds(now.getSeconds() + cachingSeconds);
127
- orgCache[appIdenfifier] = {
128
+ orgCache[appIdentifier] = {
128
129
  cacheValidUntil: now,
129
130
  validUntil,
130
131
  subscribeLink,
@@ -132,8 +133,8 @@ function addToCache(organization, appIdenfifier, validUntil, type, cachingSecond
132
133
  };
133
134
  subscriptionCache[organization] = orgCache;
134
135
  }
135
- function getFromCache(organization, appIdenfifier) {
136
- return (subscriptionCache[organization] || {})[appIdenfifier];
136
+ function getFromCache(organization, appIdentifier) {
137
+ return (subscriptionCache[organization] || {})[appIdentifier];
137
138
  }
138
139
  function clearCache(organization) {
139
140
  delete subscriptionCache[organization];
package/out/util/cron.js CHANGED
@@ -45,24 +45,24 @@ function runJob(config, integration, job) {
45
45
  (0, _1.log)(`Starting cron job with expression [${job.expression}]`, config.logger);
46
46
  const crowdinCredentialsList = yield (0, storage_1.getStorage)().getAllCrowdinCredentials();
47
47
  yield Promise.all(crowdinCredentialsList.map((crowdinCredentials) => __awaiter(this, void 0, void 0, function* () {
48
- const { client: crowdinClient, token } = yield (0, connection_1.prepareCrowdinClient)(config, crowdinCredentials);
48
+ const { token } = yield (0, connection_1.prepareCrowdinClient)(config, crowdinCredentials);
49
49
  const { expired } = yield (0, connection_1.checkSubscription)(config, token, crowdinCredentials.id, crowdinCredentials.type);
50
50
  if (expired) {
51
51
  (0, _1.log)(`Subscription expired. Skipping job [${job.expression}] for organization ${crowdinCredentials.id}`);
52
52
  return;
53
53
  }
54
54
  const integrationCredentialsList = yield (0, storage_1.getStorage)().getAllIntegrationCredentials(crowdinCredentials.id);
55
- yield Promise.all(integrationCredentialsList.map((integrationCredentials) => __awaiter(this, void 0, void 0, function* () {
55
+ //executing in non-parallel way to properly prepare crowdin client (avoid expriration issues)
56
+ for (const integrationCredentials of integrationCredentialsList) {
57
+ const { client: crowdinClient } = yield (0, connection_1.prepareCrowdinClient)(config, crowdinCredentials);
56
58
  const projectId = crowdinAppFunctions.getProjectId(integrationCredentials.id);
57
59
  const apiCredentials = yield (0, connection_1.prepareIntegrationCredentials)(config, integration, integrationCredentials);
58
60
  const rootFolder = yield (0, defaults_1.getRootFolder)(config, integration, crowdinClient, projectId);
59
- const intConfig = integrationCredentials.config
60
- ? JSON.parse(integrationCredentials.config)
61
- : undefined;
61
+ const intConfig = integrationCredentials.config ? JSON.parse(integrationCredentials.config) : undefined;
62
62
  (0, _1.log)(`Executing task for cron job with expression [${job.expression}] for project ${projectId}`, config.logger);
63
63
  yield job.task(projectId, crowdinClient, apiCredentials, rootFolder, intConfig);
64
64
  (0, _1.log)(`Task for cron job with expression [${job.expression}] for project ${projectId} completed`, config.logger);
65
- })));
65
+ }
66
66
  })));
67
67
  (0, _1.log)(`Cron job with expression [${job.expression}] completed`, config.logger);
68
68
  });
@@ -76,7 +76,7 @@ function filesCron(config, integration, period) {
76
76
  const files = JSON.parse(syncSettings.files);
77
77
  const crowdinCredentials = yield (0, storage_1.getStorage)().getCrowdinCredentials(syncSettings.crowdinId);
78
78
  const integrationCredentials = yield (0, storage_1.getStorage)().getIntegrationCredentials(syncSettings.integrationId);
79
- if (!crowdinCredentials || !integrationCredentials || !files) {
79
+ if (!crowdinCredentials || !integrationCredentials) {
80
80
  return;
81
81
  }
82
82
  const intConfig = integrationCredentials.config
@@ -92,31 +92,44 @@ function filesCron(config, integration, period) {
92
92
  (0, _1.log)(`Subscription expired. Skipping job [${period}] for organization ${crowdinCredentials.id}`);
93
93
  return;
94
94
  }
95
- const apiCredentials = yield (0, connection_1.prepareIntegrationCredentials)(config, integration, integrationCredentials);
96
95
  const rootFolder = yield (0, defaults_1.getRootFolder)(config, integration, crowdinClient, projectId);
96
+ //split invocation into chunks to avoid expiration issues
97
+ const chunkSize = 10;
97
98
  if (syncSettings.provider === 'crowdin') {
98
99
  const crowdinFiles = files;
99
100
  const onlyTranslated = intConfig.condition === SyncCondition.TRANSLATED;
100
101
  const onlyApproved = intConfig.condition === SyncCondition.APPROVED;
101
102
  const all = intConfig.condition === SyncCondition.ALL || intConfig.condition === undefined;
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) {
103
+ const filesToProcess = all
104
+ ? crowdinFiles
105
+ : yield getOnlyTranslatedOrApprovedFiles(config, projectId, crowdinFiles, crowdinClient, onlyApproved, onlyTranslated);
106
+ (0, _1.log)(`Executing updateIntegration task for files cron job with period [${period}] for project ${projectId}.Files ${Object.keys(filesToProcess).length}`, config.logger);
107
+ if (!all) {
108
+ if (Object.keys(filesToProcess).length === 0) {
109
109
  (0, _1.log)(`There is no ${onlyApproved ? 'approved' : 'translated'} file`, config.logger);
110
110
  return;
111
111
  }
112
- yield integration.updateIntegration(projectId, crowdinClient, apiCredentials, filteredFiles, rootFolder, intConfig);
112
+ }
113
+ const filesArr = Object.keys(filesToProcess);
114
+ for (let i = 0; i < filesArr.length; i += chunkSize) {
115
+ const chunk = filesArr.slice(i, i + chunkSize);
116
+ const filesChunk = {};
117
+ chunk.forEach((f) => (filesChunk[f] = filesToProcess[f]));
118
+ const apiCredentials = yield (0, connection_1.prepareIntegrationCredentials)(config, integration, integrationCredentials);
119
+ const { client } = yield (0, connection_1.prepareCrowdinClient)(config, crowdinCredentials);
120
+ yield integration.updateIntegration(projectId, client, apiCredentials, filesChunk, rootFolder, intConfig);
113
121
  }
114
122
  (0, _1.log)(`updateIntegration task for files cron job with period [${period}] for project ${projectId} completed`, config.logger);
115
123
  }
116
124
  else {
117
125
  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);
126
+ (0, _1.log)(`Executing updateCrowdin task for files cron job with period [${period}] for project ${projectId}. Files ${intFiles.length}`, config.logger);
127
+ for (let i = 0; i < intFiles.length; i += chunkSize) {
128
+ const chunk = intFiles.slice(i, i + chunkSize);
129
+ const apiCredentials = yield (0, connection_1.prepareIntegrationCredentials)(config, integration, integrationCredentials);
130
+ const { client } = yield (0, connection_1.prepareCrowdinClient)(config, crowdinCredentials);
131
+ yield integration.updateCrowdin(projectId, client, apiCredentials, chunk, rootFolder, intConfig);
132
+ }
120
133
  (0, _1.log)(`updateCrowdin task for files cron job with period [${period}] for project ${projectId} completed`, config.logger);
121
134
  }
122
135
  })));
@@ -133,18 +146,18 @@ function getOnlyTranslatedOrApprovedFiles(config, projectId, crowdinFiles, crowd
133
146
  .getFileProgress(projectId, Number(fileId));
134
147
  return {
135
148
  id: fileId,
136
- info: res.data.map(e => e.data),
149
+ info: res.data.map((e) => e.data),
137
150
  };
138
151
  })));
139
152
  const filteredFiles = {};
140
- Object.keys(crowdinFiles).forEach(fileId => {
141
- const fileInfo = filesInfo.find(info => info.id === fileId);
153
+ Object.keys(crowdinFiles).forEach((fileId) => {
154
+ const fileInfo = filesInfo.find((info) => info.id === fileId);
142
155
  if (!fileInfo) {
143
156
  return;
144
157
  }
145
158
  const languages = crowdinFiles[fileId];
146
- languages.forEach(language => {
147
- const languageInfo = fileInfo.info.find(info => info.languageId === language);
159
+ languages.forEach((language) => {
160
+ const languageInfo = fileInfo.info.find((info) => info.languageId === language);
148
161
  if (!languageInfo) {
149
162
  return;
150
163
  }
@@ -36,7 +36,7 @@ function getRootFolder(config, integration, client, projectId) {
36
36
  return;
37
37
  }
38
38
  const folder = integration.appFolderName || config.name;
39
- const directories = (yield client.sourceFilesApi.withFetchAll().listProjectDirectories(projectId)).data.map(d => d.data);
39
+ const directories = (yield client.sourceFilesApi.withFetchAll().listProjectDirectories(projectId)).data.map((d) => d.data);
40
40
  const { folder: rootFolder } = yield crowdinAppFunctions.getOrCreateFolder(directories, client, projectId, folder);
41
41
  return rootFolder;
42
42
  });
@@ -55,18 +55,18 @@ function applyDefaults(config, integration) {
55
55
  allDirectories = (yield client.sourceFilesApi.withFetchAll().listProjectDirectories(projectId, {
56
56
  directoryId: rootFolder.id,
57
57
  recursion: 'true',
58
- })).data.map(d => d.data);
58
+ })).data.map((d) => d.data);
59
59
  }
60
60
  else {
61
- allDirectories = (yield client.sourceFilesApi.withFetchAll().listProjectDirectories(projectId)).data.map(d => d.data);
61
+ allDirectories = (yield client.sourceFilesApi.withFetchAll().listProjectDirectories(projectId)).data.map((d) => d.data);
62
62
  }
63
- const directoryIds = allDirectories.map(d => d.id);
64
- let files = (yield client.sourceFilesApi.withFetchAll().listProjectFiles(projectId)).data.map(d => d.data);
65
- files = files.filter(f => (rootFolder && f.directoryId === rootFolder.id) ||
63
+ const directoryIds = allDirectories.map((d) => d.id);
64
+ let files = (yield client.sourceFilesApi.withFetchAll().listProjectFiles(projectId)).data.map((d) => d.data);
65
+ files = files.filter((f) => (rootFolder && f.directoryId === rootFolder.id) ||
66
66
  directoryIds.includes(f.directoryId) ||
67
67
  (!rootFolder && !f.directoryId));
68
68
  const res = [];
69
- allDirectories.forEach(e => {
69
+ allDirectories.forEach((e) => {
70
70
  const parentId = rootFolder && e.directoryId === rootFolder.id ? undefined : e.directoryId;
71
71
  res.push({
72
72
  id: e.id.toString(),
@@ -74,7 +74,7 @@ function applyDefaults(config, integration) {
74
74
  name: e.name,
75
75
  });
76
76
  });
77
- files.forEach(e => {
77
+ files.forEach((e) => {
78
78
  const parentId = rootFolder && e.directoryId === rootFolder.id ? undefined : e.directoryId;
79
79
  res.push({
80
80
  id: e.id.toString(),
@@ -143,10 +143,10 @@
143
143
  )
144
144
  .then(checkResponse)
145
145
  .then(reloadLocation)
146
- .catch(e => catchRejection(e, 'Credentials are not stored'))
147
- .finally(() => {
146
+ .catch(e => {
148
147
  loginButton.setAttribute('disabled', false);
149
148
  loginButton.setAttribute('is-loading', false);
149
+ catchRejection(e, 'Credentials are not stored');
150
150
  });
151
151
  }
152
152
 
package/package.json CHANGED
@@ -1,15 +1,16 @@
1
1
  {
2
2
  "name": "@crowdin/app-project-module",
3
- "version": "0.23.5",
3
+ "version": "0.24.0",
4
4
  "description": "Module that generates for you all common endpoints for serving standalone Crowdin App",
5
5
  "main": "out/index.js",
6
6
  "types": "out/index.d.ts",
7
7
  "scripts": {
8
8
  "build": "tsc -p ./ && cp -R views out && cp -R static out && cp logo.png out",
9
9
  "lint": "eslint --fix \"{src,tests}/**/*.{js,ts}\"",
10
+ "prettier": "prettier --config .prettierrc src/**/*.ts --write",
10
11
  "lint-ci": "eslint \"{src,tests}/**/*.{js,ts}\"",
11
- "test-coverage": "echo \"test-coverage not implemented\"",
12
- "test": "echo \"test not implemented\""
12
+ "test-coverage": "jest --config jestconfig.json --ci --reporters=jest-junit --reporters=default --coverage --coverageReporters=cobertura --coverageReporters=html",
13
+ "test": "jest --config jestconfig.json"
13
14
  },
14
15
  "dependencies": {
15
16
  "@crowdin/crowdin-apps-functions": "~0.2.1",
@@ -27,6 +28,7 @@
27
28
  "@types/crypto-js": "^4.0.0",
28
29
  "@types/express": "4.17.13",
29
30
  "@types/express-handlebars": "^5.3.1",
31
+ "@types/jest": "^29.2.5",
30
32
  "@types/node": "^12.0.10",
31
33
  "@types/node-cron": "^3.0.0",
32
34
  "@typescript-eslint/eslint-plugin": "^2.3.1",
@@ -34,8 +36,10 @@
34
36
  "eslint": "^6.4.0",
35
37
  "eslint-config-prettier": "^6.3.0",
36
38
  "eslint-plugin-prettier": "^3.1.1",
37
- "lint-staged": ">=8",
38
- "prettier": "^1.18.2",
39
+ "jest": "^29.3.1",
40
+ "jest-junit": "^15.0.0",
41
+ "prettier": "^2.8.1",
42
+ "ts-jest": "^29.0.3",
39
43
  "typescript": "^4.4.4"
40
44
  },
41
45
  "repository": {
@@ -48,6 +52,12 @@
48
52
  "Applications",
49
53
  "App"
50
54
  ],
55
+ "jest": {
56
+ "coverageReporters": [
57
+ "text",
58
+ "cobertura"
59
+ ]
60
+ },
51
61
  "license": "MIT",
52
62
  "bugs": {
53
63
  "url": "https://github.com/crowdin/app-project-module/issues"