@crowdin/app-project-module 0.36.0 → 0.38.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.
@@ -53,7 +53,6 @@ function catchRejection(e, message) {
53
53
  subscriptionModal.open();
54
54
  return;
55
55
  }
56
- console.error(e.message || message);
57
56
  showToast(e.message || message);
58
57
  }
59
58
 
@@ -1,9 +1,10 @@
1
1
  import Crowdin from '@crowdin/crowdin-api-client';
2
- import { AccountType, Config, CrowdinCredentials, IntegrationCredentials, IntegrationLogic, SubscriptionInfo } from '../models';
3
- export declare function prepareCrowdinClient({ config, credentials, autoRenew, }: {
2
+ import { AccountType, Config, CrowdinContextInfo, CrowdinCredentials, IntegrationCredentials, IntegrationLogic, SubscriptionInfo } from '../models';
3
+ export declare function prepareCrowdinClient({ config, credentials, autoRenew, context, }: {
4
4
  config: Config;
5
5
  credentials: CrowdinCredentials;
6
6
  autoRenew?: boolean;
7
+ context?: CrowdinContextInfo;
7
8
  }): Promise<{
8
9
  client: Crowdin;
9
10
  token: string;
@@ -54,7 +54,7 @@ function refreshToken(config, credentials) {
54
54
  };
55
55
  });
56
56
  }
57
- function prepareCrowdinClient({ config, credentials, autoRenew = false, }) {
57
+ function prepareCrowdinClient({ config, credentials, autoRenew = false, context, }) {
58
58
  var _a, _b;
59
59
  return __awaiter(this, void 0, void 0, function* () {
60
60
  //2 min as an extra buffer
@@ -114,6 +114,7 @@ function prepareCrowdinClient({ config, credentials, autoRenew = false, }) {
114
114
  const res = yield axiosCustom.get(url, getHttpConfig(httpConfig));
115
115
  return res.data;
116
116
  }
117
+ (0, logger_1.logError)(e, context);
117
118
  throw e;
118
119
  }
119
120
  });
@@ -130,6 +131,7 @@ function prepareCrowdinClient({ config, credentials, autoRenew = false, }) {
130
131
  const res = yield axiosCustom.delete(url, getHttpConfig(httpConfig));
131
132
  return res.data;
132
133
  }
134
+ (0, logger_1.logError)(e, context);
133
135
  throw e;
134
136
  }
135
137
  });
@@ -146,6 +148,7 @@ function prepareCrowdinClient({ config, credentials, autoRenew = false, }) {
146
148
  const res = yield axiosCustom.head(url, getHttpConfig(httpConfig));
147
149
  return res.data;
148
150
  }
151
+ (0, logger_1.logError)(e, context);
149
152
  throw e;
150
153
  }
151
154
  });
@@ -162,6 +165,7 @@ function prepareCrowdinClient({ config, credentials, autoRenew = false, }) {
162
165
  const res = yield axiosCustom.patch(url, data, getHttpConfig(httpConfig));
163
166
  return res.data;
164
167
  }
168
+ (0, logger_1.logError)(e, context);
165
169
  throw e;
166
170
  }
167
171
  });
@@ -178,6 +182,7 @@ function prepareCrowdinClient({ config, credentials, autoRenew = false, }) {
178
182
  const res = yield axiosCustom.post(url, data, getHttpConfig(httpConfig));
179
183
  return res.data;
180
184
  }
185
+ (0, logger_1.logError)(e, context);
181
186
  throw e;
182
187
  }
183
188
  });
@@ -194,6 +199,7 @@ function prepareCrowdinClient({ config, credentials, autoRenew = false, }) {
194
199
  const res = yield axiosCustom.put(url, data, getHttpConfig(httpConfig));
195
200
  return res.data;
196
201
  }
202
+ (0, logger_1.logError)(e, context);
197
203
  throw e;
198
204
  }
199
205
  });
package/out/util/cron.js CHANGED
@@ -95,10 +95,25 @@ function filesCron(config, integration, period) {
95
95
  return;
96
96
  }
97
97
  const projectId = crowdinAppFunctions.getProjectId(integrationCredentials.id);
98
+ const context = {
99
+ jwtPayload: {
100
+ context: {
101
+ // eslint-disable-next-line @typescript-eslint/camelcase
102
+ project_id: projectId,
103
+ // eslint-disable-next-line @typescript-eslint/camelcase
104
+ organization_id: crowdinCredentials.organizationId,
105
+ // eslint-disable-next-line @typescript-eslint/camelcase
106
+ organization_domain: crowdinCredentials.domain,
107
+ // eslint-disable-next-line @typescript-eslint/camelcase
108
+ user_id: crowdinCredentials.userId,
109
+ },
110
+ },
111
+ };
98
112
  const { client: crowdinClient, token } = yield (0, connection_1.prepareCrowdinClient)({
99
113
  config,
100
114
  credentials: crowdinCredentials,
101
115
  autoRenew: true,
116
+ context,
102
117
  });
103
118
  const { expired } = yield (0, connection_1.checkSubscription)({
104
119
  config,
@@ -159,7 +174,13 @@ function filesCron(config, integration, period) {
159
174
  }
160
175
  }
161
176
  const apiCredentials = yield (0, connection_1.prepareIntegrationCredentials)(config, integration, integrationCredentials);
162
- yield integration.updateIntegration(projectId, crowdinClient, apiCredentials, filesToProcess, rootFolder, intConfig);
177
+ try {
178
+ yield integration.updateIntegration(projectId, crowdinClient, apiCredentials, filesToProcess, rootFolder, intConfig);
179
+ }
180
+ catch (e) {
181
+ (0, logger_1.logError)(e, context);
182
+ throw e;
183
+ }
163
184
  if (Object.keys(newFiles).length) {
164
185
  yield (0, storage_1.getStorage)().updateSyncSettings(JSON.stringify(Object.assign(Object.assign({}, files), newFiles)), syncSettings.integrationId, syncSettings.crowdinId, 'schedule', syncSettings.provider);
165
186
  const currentFileSnapshot = yield (0, file_snapshot_1.getCrowdinSnapshot)(config, integration, crowdinClient, projectId, intConfig);
@@ -176,7 +197,13 @@ function filesCron(config, integration, period) {
176
197
  const intFiles = allIntFiles.filter((file) => 'type' in file);
177
198
  (0, logger_1.log)(`Executing updateCrowdin task for files cron job with period [${period}] for project ${projectId}. Files ${intFiles.length}`);
178
199
  const apiCredentials = yield (0, connection_1.prepareIntegrationCredentials)(config, integration, integrationCredentials);
179
- yield integration.updateCrowdin(projectId, crowdinClient, apiCredentials, intFiles, rootFolder, intConfig);
200
+ try {
201
+ yield integration.updateCrowdin(projectId, crowdinClient, apiCredentials, intFiles, rootFolder, intConfig);
202
+ }
203
+ catch (e) {
204
+ (0, logger_1.logError)(e, context);
205
+ throw e;
206
+ }
180
207
  if (Object.keys(newFiles).length) {
181
208
  const newSyncSettingsFields = allIntFiles.map((file) => (Object.assign(Object.assign({}, file), { schedule: true, sync: false })));
182
209
  yield (0, storage_1.getStorage)().updateSyncSettings(JSON.stringify(newSyncSettingsFields), syncSettings.integrationId, syncSettings.crowdinId, 'schedule', syncSettings.provider);
@@ -1,3 +1,4 @@
1
+ import { AxiosError } from 'axios';
1
2
  import { Config, CrowdinContextInfo } from '../models';
2
3
  export type LogFunction = (message: string) => void;
3
4
  export type LogErrorFunction = (error: any) => void;
@@ -12,4 +13,18 @@ export declare function withContext(context: CrowdinContextInfo): LogFunction;
12
13
  export declare function withContextError(context: CrowdinContextInfo): LogFunction;
13
14
  export declare function log(message: string, context?: CrowdinContextInfo): void;
14
15
  export declare function logError(e: any, context?: CrowdinContextInfo): void;
16
+ export declare function isAxiosError(e: any): e is CustomAxiosError;
15
17
  export declare function getErrorMessage(err: any): any;
18
+ interface CustomAxiosError extends AxiosError {
19
+ data?: any;
20
+ }
21
+ export declare class AppModuleError extends Error {
22
+ data: any;
23
+ isAxiosError: boolean;
24
+ constructor(error: CustomAxiosError | Error | string, data?: any);
25
+ }
26
+ export declare class AppModuleAggregateError extends Error {
27
+ errors: Error[] | AppModuleError[];
28
+ constructor(errors: Error[] | AppModuleError[], message: string);
29
+ }
30
+ export {};
@@ -23,7 +23,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
23
23
  return result;
24
24
  };
25
25
  Object.defineProperty(exports, "__esModule", { value: true });
26
- exports.getErrorMessage = exports.logError = exports.log = exports.withContextError = exports.withContext = exports.prepareContext = exports.initialize = void 0;
26
+ exports.AppModuleAggregateError = exports.AppModuleError = exports.getErrorMessage = exports.isAxiosError = exports.logError = exports.log = exports.withContextError = exports.withContext = exports.prepareContext = exports.initialize = void 0;
27
27
  const logsFormatter = __importStar(require("@crowdin/logs-formatter"));
28
28
  let logConfig;
29
29
  let onError;
@@ -49,6 +49,7 @@ function withContextError(context) {
49
49
  }
50
50
  exports.withContextError = withContextError;
51
51
  function log(message, context) {
52
+ var _a, _b, _c;
52
53
  if (logConfig === null || logConfig === void 0 ? void 0 : logConfig.enabled) {
53
54
  if (logConfig.log) {
54
55
  logConfig.log(message, context);
@@ -59,11 +60,20 @@ function log(message, context) {
59
60
  logsFormatter.setContext({
60
61
  project: {
61
62
  id: context.jwtPayload.context.project_id,
63
+ identifier: (_a = context.jwtPayload.context.project_identifier) !== null && _a !== void 0 ? _a : '',
62
64
  // eslint-disable-next-line @typescript-eslint/camelcase
63
65
  organization_id: context.jwtPayload.context.organization_id,
64
66
  // eslint-disable-next-line @typescript-eslint/camelcase
65
67
  user_id: context.jwtPayload.context.user_id,
66
68
  },
69
+ user: {
70
+ id: context.jwtPayload.context.user_id,
71
+ login: (_b = context.jwtPayload.context.user_login) !== null && _b !== void 0 ? _b : '',
72
+ },
73
+ organization: {
74
+ id: context.jwtPayload.context.organization_id,
75
+ domain: (_c = context.jwtPayload.context.organization_domain) !== null && _c !== void 0 ? _c : '',
76
+ },
67
77
  });
68
78
  prefix += ` Context [${JSON.stringify(prepareContext(context))}]`;
69
79
  }
@@ -74,37 +84,71 @@ function log(message, context) {
74
84
  }
75
85
  exports.log = log;
76
86
  function logError(e, context) {
87
+ var _a, _b, _c;
77
88
  if (onError) {
78
89
  onError(e, context);
79
90
  }
80
91
  else {
81
- let message = `[${new Date().toISOString()}] Error.`;
82
92
  if (context) {
83
93
  logsFormatter.setContext({
84
94
  project: {
85
95
  id: context.jwtPayload.context.project_id,
96
+ identifier: (_a = context.jwtPayload.context.project_identifier) !== null && _a !== void 0 ? _a : '',
86
97
  // eslint-disable-next-line @typescript-eslint/camelcase
87
98
  organization_id: context.jwtPayload.context.organization_id,
88
99
  // eslint-disable-next-line @typescript-eslint/camelcase
89
100
  user_id: context.jwtPayload.context.user_id,
90
101
  },
102
+ user: {
103
+ id: context.jwtPayload.context.user_id,
104
+ login: (_b = context.jwtPayload.context.user_login) !== null && _b !== void 0 ? _b : '',
105
+ },
106
+ organization: {
107
+ id: context.jwtPayload.context.organization_id,
108
+ domain: (_c = context.jwtPayload.context.organization_domain) !== null && _c !== void 0 ? _c : '',
109
+ },
91
110
  });
92
- message += ` Context ${JSON.stringify(prepareContext(context))}`;
93
111
  }
94
- if (isAxiosError(e)) {
95
- const request = e.config ? { url: e.config.url, method: e.config.method, data: e.config.data } : {};
96
- const response = e.response ? { data: e.response.data, status: e.response.status } : {};
97
- console.error(`${message} Axios error. Request ${JSON.stringify(request)}. Response ${JSON.stringify(response)}. Message ${e.message}`);
112
+ if (e instanceof AppModuleAggregateError) {
113
+ if (e.errors && e.errors.length > 0) {
114
+ for (const error of e.errors) {
115
+ errorOutputByType(error);
116
+ }
117
+ }
98
118
  }
99
119
  else {
100
- console.error(message, e);
120
+ errorOutputByType(e);
101
121
  }
102
122
  }
103
123
  }
104
124
  exports.logError = logError;
125
+ function errorOutputByType(error) {
126
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
127
+ const message = error.message || error;
128
+ if (isAxiosError(error)) {
129
+ const request = error.config || error.data.config
130
+ ? {
131
+ url: ((_a = error.config) === null || _a === void 0 ? void 0 : _a.url) || ((_b = error.data) === null || _b === void 0 ? void 0 : _b.config.url),
132
+ method: ((_c = error.config) === null || _c === void 0 ? void 0 : _c.method) || ((_d = error.data) === null || _d === void 0 ? void 0 : _d.config.method),
133
+ data: ((_e = error.config) === null || _e === void 0 ? void 0 : _e.data) || ((_f = error.data) === null || _f === void 0 ? void 0 : _f.config.data),
134
+ }
135
+ : {};
136
+ const response = error.response || error.data.response
137
+ ? {
138
+ data: ((_g = error.response) === null || _g === void 0 ? void 0 : _g.data) || ((_h = error.data) === null || _h === void 0 ? void 0 : _h.response.data),
139
+ status: ((_j = error.response) === null || _j === void 0 ? void 0 : _j.status) || ((_k = error.data) === null || _k === void 0 ? void 0 : _k.response.status),
140
+ }
141
+ : {};
142
+ console.error(message, { attributes: { request, response } }, { backtrace: error.stack });
143
+ }
144
+ else {
145
+ console.error(message, error.data ? { attributes: error.data } : '', error.stack ? { backtrace: error.stack } : '');
146
+ }
147
+ }
105
148
  function isAxiosError(e) {
106
149
  return !!e.isAxiosError;
107
150
  }
151
+ exports.isAxiosError = isAxiosError;
108
152
  function getErrorMessage(err) {
109
153
  let message;
110
154
  if (typeof err === 'string') {
@@ -119,3 +163,24 @@ function getErrorMessage(err) {
119
163
  return message;
120
164
  }
121
165
  exports.getErrorMessage = getErrorMessage;
166
+ class AppModuleError extends Error {
167
+ constructor(error, data) {
168
+ super(typeof error === 'string' ? error : error.message);
169
+ this.isAxiosError = false;
170
+ this.name = 'AppModuleError';
171
+ this.data = typeof error === 'string' ? Object.assign({}, data) : Object.assign(Object.assign({}, error), data);
172
+ this.stack = typeof error === 'string' ? this.stack : error.stack;
173
+ if (isAxiosError(error)) {
174
+ this.isAxiosError = true;
175
+ }
176
+ }
177
+ }
178
+ exports.AppModuleError = AppModuleError;
179
+ class AppModuleAggregateError extends Error {
180
+ constructor(errors, message) {
181
+ super(message);
182
+ this.name = 'AppModuleAggregateError';
183
+ this.errors = errors;
184
+ }
185
+ }
186
+ exports.AppModuleAggregateError = AppModuleAggregateError;
@@ -91,7 +91,7 @@ function registerWebhooks(config, integration, client, crowdinContext, apiCreden
91
91
  const webhookName = `${config.name} application hook ${crowdinContext.jwtPayload.sub}`;
92
92
  const webhookUrl = makeCrowdinWebhookUrl(config, integration, crowdinContext);
93
93
  const syncCondition = models_1.SyncCondition[Number(appSettings.condition)];
94
- const events = HookConditionEvents[syncCondition];
94
+ const events = [...HookConditionEvents[syncCondition]];
95
95
  const webhook = yield getCrowdinProjectWebhook(config, client, projectId, webhookName);
96
96
  if (appSettings['new-crowdin-files']) {
97
97
  events.push(exports.HookEvents.fileAdded);
@@ -226,8 +226,24 @@ function prepareWebhookData(config, integration, webhookUrlParam, provider) {
226
226
  let appSettings = {};
227
227
  const { projectId, crowdinId, clientId } = decodedUrlParam(config, webhookUrlParam);
228
228
  const crowdinCredentials = yield (0, storage_1.getStorage)().getCrowdinCredentials(crowdinId);
229
- const crowdinClient = yield (0, connection_1.prepareCrowdinClient)({ config, credentials: crowdinCredentials });
230
229
  const integrationCredentials = yield (0, storage_1.getStorage)().getIntegrationCredentials(clientId);
230
+ const context = {
231
+ jwtPayload: {
232
+ context: {
233
+ // eslint-disable-next-line @typescript-eslint/camelcase
234
+ project_id: projectId,
235
+ // eslint-disable-next-line @typescript-eslint/camelcase
236
+ organization_id: crowdinCredentials === null || crowdinCredentials === void 0 ? void 0 : crowdinCredentials.organizationId,
237
+ // eslint-disable-next-line @typescript-eslint/camelcase
238
+ user_id: crowdinCredentials === null || crowdinCredentials === void 0 ? void 0 : crowdinCredentials.userId,
239
+ },
240
+ },
241
+ };
242
+ const crowdinClient = yield (0, connection_1.prepareCrowdinClient)({
243
+ config,
244
+ credentials: crowdinCredentials,
245
+ context,
246
+ });
231
247
  const preparedIntegrationCredentials = yield (0, connection_1.prepareIntegrationCredentials)(config, integration, integrationCredentials);
232
248
  if (integrationCredentials === null || integrationCredentials === void 0 ? void 0 : integrationCredentials.config) {
233
249
  appSettings = JSON.parse(integrationCredentials.config);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crowdin/app-project-module",
3
- "version": "0.36.0",
3
+ "version": "0.38.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",
@@ -16,8 +16,8 @@
16
16
  "dependencies": {
17
17
  "@aws-sdk/client-s3": "^3.423.0",
18
18
  "@aws-sdk/s3-request-presigner": "^3.423.0",
19
- "@crowdin/crowdin-apps-functions": "0.5.1",
20
- "@crowdin/logs-formatter": "^2.0.3",
19
+ "@crowdin/crowdin-apps-functions": "0.6.0",
20
+ "@crowdin/logs-formatter": "^2.0.5",
21
21
  "@godaddy/terminus": "^4.12.1",
22
22
  "@types/pg": "^8.10.3",
23
23
  "amqplib": "^0.10.3",