@crowdin/app-project-module 0.37.0 → 0.39.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.
@@ -10,14 +10,26 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  const util_1 = require("../../util");
13
+ const logger_1 = require("../../util/logger");
13
14
  function handle(integration) {
14
15
  return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
15
16
  const fileId = Number(req.params.fileId || req.body.fileId);
16
17
  req.logInfo(`Loading translation progress for file ${fileId}`);
17
18
  if (integration.getFileProgress) {
18
- const progress = yield integration.getFileProgress(req.crowdinContext.jwtPayload.context.project_id, req.crowdinApiClient, fileId);
19
- req.logInfo(`Translation progress for file ${fileId} ${JSON.stringify(progress, null, 2)}`);
20
- res.send(progress);
19
+ try {
20
+ const progress = yield integration.getFileProgress(req.crowdinContext.jwtPayload.context.project_id, req.crowdinApiClient, fileId);
21
+ req.logInfo(`Translation progress for file ${fileId} ${JSON.stringify(progress, null, 2)}`);
22
+ res.send(progress);
23
+ }
24
+ catch (e) {
25
+ yield (0, logger_1.handleUserError)({
26
+ action: 'Get Crowdin files progress',
27
+ error: e,
28
+ crowdinId: req.crowdinContext.crowdinId,
29
+ clientId: req.crowdinContext.clientId,
30
+ });
31
+ throw e;
32
+ }
21
33
  }
22
34
  else {
23
35
  res.send({});
@@ -11,17 +11,29 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  const util_1 = require("../../util");
13
13
  const defaults_1 = require("../../util/defaults");
14
+ const logger_1 = require("../../util/logger");
14
15
  function handle(config, integration) {
15
16
  return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
16
17
  req.logInfo('Loading crowdin files');
17
18
  if (integration.getCrowdinFiles) {
18
19
  const rootFolder = yield (0, defaults_1.getRootFolder)(config, integration, req.crowdinApiClient, req.crowdinContext.jwtPayload.context.project_id);
19
20
  req.logInfo(`Loading files ${rootFolder ? `from folder ${rootFolder.id}` : 'from root'}`);
20
- const files = integration.getCrowdinFiles
21
- ? yield integration.getCrowdinFiles(req.crowdinContext.jwtPayload.context.project_id, req.crowdinApiClient, rootFolder, req.integrationSettings)
22
- : [];
23
- req.logInfo(`Returning ${files.length} files`);
24
- res.send(files);
21
+ try {
22
+ const files = integration.getCrowdinFiles
23
+ ? yield integration.getCrowdinFiles(req.crowdinContext.jwtPayload.context.project_id, req.crowdinApiClient, rootFolder, req.integrationSettings)
24
+ : [];
25
+ req.logInfo(`Returning ${files.length} files`);
26
+ res.send(files);
27
+ }
28
+ catch (e) {
29
+ yield (0, logger_1.handleUserError)({
30
+ action: 'Get Crowdin files',
31
+ error: e,
32
+ crowdinId: req.crowdinContext.crowdinId,
33
+ clientId: req.crowdinContext.clientId,
34
+ });
35
+ throw e;
36
+ }
25
37
  }
26
38
  else {
27
39
  res.send([]);
@@ -11,6 +11,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  const util_1 = require("../../util");
13
13
  const defaults_1 = require("../../util/defaults");
14
+ const logger_1 = require("../../util/logger");
14
15
  function handle(config, integration) {
15
16
  return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
16
17
  var _a, _b;
@@ -25,10 +26,21 @@ function handle(config, integration) {
25
26
  if (((_b = config.api) === null || _b === void 0 ? void 0 : _b.default) && req.body.files) {
26
27
  req.body = req.body.files;
27
28
  }
28
- const result = yield integration.updateCrowdin(projectId, req.crowdinApiClient, req.integrationCredentials, req.body, rootFolder, req.integrationSettings, uploadTranslations);
29
29
  let message;
30
- if ((0, util_1.isExtendedResultType)(result)) {
31
- message = result.message;
30
+ try {
31
+ const result = yield integration.updateCrowdin(projectId, req.crowdinApiClient, req.integrationCredentials, req.body, rootFolder, req.integrationSettings, uploadTranslations);
32
+ if ((0, util_1.isExtendedResultType)(result)) {
33
+ message = result.message;
34
+ }
35
+ }
36
+ catch (e) {
37
+ yield (0, logger_1.handleUserError)({
38
+ action: 'Sync files to Crowdin',
39
+ error: e,
40
+ crowdinId: req.crowdinContext.crowdinId,
41
+ clientId: req.crowdinContext.clientId,
42
+ });
43
+ throw e;
32
44
  }
33
45
  res.send({ message });
34
46
  }));
@@ -10,21 +10,33 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  const util_1 = require("../../util");
13
+ const logger_1 = require("../../util/logger");
13
14
  function handle(integration) {
14
15
  return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
15
16
  const { parent_id: parentId, search, page } = req.query;
16
17
  req.logInfo('Recieved request to get integration data');
17
- const result = yield integration.getIntegrationFiles(req.integrationCredentials, req.integrationSettings, parentId, search, page);
18
18
  let message;
19
19
  let stopPagination;
20
20
  let files;
21
- if ((0, util_1.isExtendedResultType)(result)) {
22
- files = result.data;
23
- message = result.message;
24
- stopPagination = result.stopPagination;
21
+ try {
22
+ const result = yield integration.getIntegrationFiles(req.integrationCredentials, req.integrationSettings, parentId, search, page);
23
+ if ((0, util_1.isExtendedResultType)(result)) {
24
+ files = result.data;
25
+ message = result.message;
26
+ stopPagination = result.stopPagination;
27
+ }
28
+ else {
29
+ files = result;
30
+ }
25
31
  }
26
- else {
27
- files = result;
32
+ catch (e) {
33
+ yield (0, logger_1.handleUserError)({
34
+ action: 'Get External Service data',
35
+ error: e,
36
+ crowdinId: req.crowdinContext.crowdinId,
37
+ clientId: req.crowdinContext.clientId,
38
+ });
39
+ throw e;
28
40
  }
29
41
  req.logInfo(`Integration data response ${JSON.stringify(files, null, 2)}`);
30
42
  res.send({ data: files, message, stopPagination });
@@ -11,12 +11,24 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  const storage_1 = require("../../storage");
13
13
  const util_1 = require("../../util");
14
+ const logger_1 = require("../../util/logger");
14
15
  function handle(config, integration) {
15
16
  return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
16
17
  req.logInfo('Received integration login request');
17
18
  if (integration.checkConnection) {
18
- req.logInfo('Checking the integration credentials');
19
- yield integration.checkConnection(req.body.credentials);
19
+ try {
20
+ req.logInfo('Checking the integration credentials');
21
+ yield integration.checkConnection(req.body.credentials);
22
+ }
23
+ catch (e) {
24
+ yield (0, logger_1.handleUserError)({
25
+ action: 'External Service login',
26
+ error: e,
27
+ crowdinId: req.crowdinContext.crowdinId,
28
+ clientId: req.crowdinContext.clientId,
29
+ });
30
+ throw e;
31
+ }
20
32
  }
21
33
  const existing = yield (0, storage_1.getStorage)().getIntegrationCredentials(req.crowdinContext.clientId);
22
34
  if (!!existing) {
@@ -13,12 +13,24 @@ const storage_1 = require("../../storage");
13
13
  const util_1 = require("../../util");
14
14
  const connection_1 = require("../../util/connection");
15
15
  const webhooks_1 = require("../../util/webhooks");
16
+ const logger_1 = require("../../util/logger");
16
17
  function handle(config, integration) {
17
18
  return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
18
19
  req.logInfo('Received integration logout request');
19
20
  if (integration.onLogout) {
20
- req.logInfo('Invoking onLogout hook');
21
- yield integration.onLogout(req.crowdinContext.jwtPayload.context.project_id, req.crowdinApiClient, req.integrationCredentials, req.integrationSettings);
21
+ try {
22
+ req.logInfo('Invoking onLogout hook');
23
+ yield integration.onLogout(req.crowdinContext.jwtPayload.context.project_id, req.crowdinApiClient, req.integrationCredentials, req.integrationSettings);
24
+ }
25
+ catch (e) {
26
+ yield (0, logger_1.handleUserError)({
27
+ action: 'External Service logout',
28
+ error: e,
29
+ crowdinId: req.crowdinContext.crowdinId,
30
+ clientId: req.crowdinContext.clientId,
31
+ });
32
+ throw e;
33
+ }
22
34
  }
23
35
  req.logInfo(`Deleting integration credentials for ${req.crowdinContext.clientId} client`);
24
36
  yield (0, storage_1.getStorage)().deleteIntegrationCredentials(req.crowdinContext.clientId);
@@ -11,6 +11,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  const util_1 = require("../../util");
13
13
  const defaults_1 = require("../../util/defaults");
14
+ const logger_1 = require("../../util/logger");
14
15
  function handle(config, integration) {
15
16
  return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
16
17
  var _a;
@@ -23,10 +24,21 @@ function handle(config, integration) {
23
24
  if (((_a = config.api) === null || _a === void 0 ? void 0 : _a.default) && req.body.files) {
24
25
  req.body = req.body.files;
25
26
  }
26
- const result = yield integration.updateIntegration(req.crowdinContext.jwtPayload.context.project_id, req.crowdinApiClient, req.integrationCredentials, req.body, rootFolder, req.integrationSettings);
27
27
  let message;
28
- if ((0, util_1.isExtendedResultType)(result)) {
29
- message = result.message;
28
+ try {
29
+ const result = yield integration.updateIntegration(req.crowdinContext.jwtPayload.context.project_id, req.crowdinApiClient, req.integrationCredentials, req.body, rootFolder, req.integrationSettings);
30
+ if ((0, util_1.isExtendedResultType)(result)) {
31
+ message = result.message;
32
+ }
33
+ }
34
+ catch (e) {
35
+ yield (0, logger_1.handleUserError)({
36
+ action: 'Sync files to External Service',
37
+ error: e,
38
+ crowdinId: req.crowdinContext.crowdinId,
39
+ clientId: req.crowdinContext.clientId,
40
+ });
41
+ throw e;
30
42
  }
31
43
  res.send({ message });
32
44
  }));
@@ -113,6 +113,18 @@ function handle(config) {
113
113
  },
114
114
  ];
115
115
  }
116
+ if (config.editorThemes) {
117
+ modules['editor-themes'] = [
118
+ {
119
+ key: config.identifier + '-editor-themes',
120
+ name: config.editorThemes.name || config.name,
121
+ logo: (0, util_1.getLogoUrl)(config.editorThemes, '/editor-themes'),
122
+ styles: config.editorThemes.styles,
123
+ modes: config.editorThemes.modes,
124
+ environments: config.editorThemes.environments,
125
+ },
126
+ ];
127
+ }
116
128
  if (config.projectMenu) {
117
129
  modules['project-menu'] = [
118
130
  {
@@ -0,0 +1,3 @@
1
+ /// <reference types="qs" />
2
+ import { Response } from 'express';
3
+ export default function handle(): (req: import("../models").CrowdinClientRequest | import("express").Request<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>>, res: Response<any, Record<string, any>>, next: Function) => void;
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ const storage_1 = require("../storage");
13
+ const util_1 = require("../util");
14
+ function handle() {
15
+ return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
16
+ var _a;
17
+ let userTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
18
+ if (req.crowdinContext.jwtPayload.context.user_id) {
19
+ userTimezone = (yield req.crowdinApiClient.usersApi.getUserInfo(req.crowdinContext.jwtPayload.context.user_id)).data.timezone;
20
+ }
21
+ req.logInfo(`Loading user errors for crowdinId ${req.crowdinContext.crowdinId} and integrationId ${req.crowdinContext.clientId}`);
22
+ const userErrors = (_a = (yield (0, storage_1.getStorage)().getAllUserErrors(req.crowdinContext.crowdinId, req.crowdinContext.clientId))) === null || _a === void 0 ? void 0 : _a.map((userError) => {
23
+ const date = new Date(+userError.createdAt);
24
+ // Format the date as 'MMM DD, YYYY HH:mm'
25
+ const formattedDate = new Intl.DateTimeFormat('en-US', {
26
+ year: 'numeric',
27
+ month: 'short',
28
+ day: 'numeric',
29
+ hour: '2-digit',
30
+ minute: '2-digit',
31
+ hour12: false,
32
+ timeZone: userTimezone,
33
+ }).format(date);
34
+ return Object.assign(Object.assign({}, userError), { createdAt: formattedDate });
35
+ });
36
+ req.logInfo(`Returning ${userErrors === null || userErrors === void 0 ? void 0 : userErrors.length} user errors`);
37
+ res.send(userErrors);
38
+ }));
39
+ }
40
+ exports.default = handle;
package/out/index.js CHANGED
@@ -67,6 +67,7 @@ const sync_settings_save_1 = __importDefault(require("./handlers/integration/syn
67
67
  const manifest_1 = __importDefault(require("./handlers/manifest"));
68
68
  const subscription_paid_1 = __importDefault(require("./handlers/subscription-paid"));
69
69
  const uninstall_1 = __importDefault(require("./handlers/uninstall"));
70
+ const user_errors_1 = __importDefault(require("./handlers/user-errors"));
70
71
  const crowdin_client_1 = __importStar(require("./middlewares/crowdin-client"));
71
72
  const integration_credentials_1 = __importDefault(require("./middlewares/integration-credentials"));
72
73
  const json_response_1 = __importDefault(require("./middlewares/json-response"));
@@ -184,6 +185,7 @@ function addCrowdinEndpoints(app, clientConfig) {
184
185
  (0, webhooks_1.listenQueueMessage)(config, integrationLogic, integrationLogic.webhooks.queueUrl, config.identifier);
185
186
  }
186
187
  }
188
+ app.get('/api/user-errors', json_response_1.default, (0, crowdin_client_1.default)(config), (0, integration_credentials_1.default)(config, integrationLogic), (0, user_errors_1.default)());
187
189
  }
188
190
  if (config.customFileFormat) {
189
191
  (0, defaults_1.applyFileProcessorsModuleDefaults)(config, config.customFileFormat);
@@ -225,6 +227,9 @@ function addCrowdinEndpoints(app, clientConfig) {
225
227
  if (config.editorRightPanel) {
226
228
  app.use('/editor-panels', (0, ui_module_1.default)(config, config.editorRightPanel.allowUnauthorized), (0, render_ui_module_1.default)(config.editorRightPanel));
227
229
  }
230
+ if (config.editorThemes) {
231
+ app.get((0, util_1.getLogoUrl)(config.editorThemes, '/editor-themes'), (req, res) => { var _a; return res.sendFile(((_a = config.editorThemes) === null || _a === void 0 ? void 0 : _a.imagePath) || config.imagePath); });
232
+ }
228
233
  if (config.projectMenu) {
229
234
  app.use('/project-menu', (0, ui_module_1.default)(config, config.projectMenu.allowUnauthorized), (0, render_ui_module_1.default)(config.projectMenu));
230
235
  }
@@ -94,6 +94,10 @@ export interface ClientConfig extends ImagePath {
94
94
  * editor-right-panel module
95
95
  */
96
96
  editorRightPanel?: EditorPanels & Environments;
97
+ /**
98
+ * editor-themes module
99
+ */
100
+ editorThemes?: EditorThemes & ImagePath & Environments;
97
101
  /**
98
102
  * project menu module
99
103
  */
@@ -754,9 +758,25 @@ export interface EditorPanels extends UiModule {
754
758
  /**
755
759
  * The Editor's mode list where the module will be available.
756
760
  */
757
- modes: EditorPanelsMode[];
761
+ modes: EditorMode[];
758
762
  }
759
- export declare enum EditorPanelsMode {
763
+ export interface EditorThemes {
764
+ /**
765
+ * Module name
766
+ */
767
+ name?: string;
768
+ /**
769
+ * Defines a set of CSS custom variables for theming purposes
770
+ */
771
+ styles: {
772
+ [key: string]: string;
773
+ };
774
+ /**
775
+ * The Editor's mode list where the module will be available.
776
+ */
777
+ modes: EditorMode[];
778
+ }
779
+ export declare enum EditorMode {
760
780
  ASSETS = "assets",
761
781
  REVIEW = "review",
762
782
  TRANSLATE = "TRANSLATE",
@@ -840,6 +860,15 @@ export interface IntegrationWebhooks {
840
860
  crowdinId: string;
841
861
  provider: Provider;
842
862
  }
863
+ export interface UserErrors {
864
+ id: number;
865
+ action: string;
866
+ message: string;
867
+ data: any;
868
+ createdAt: string;
869
+ crowdinId: string;
870
+ integrationId?: string;
871
+ }
843
872
  export interface ImagePath {
844
873
  /**
845
874
  * path to app logo (e.g. {@example join(__dirname, 'logo.png')})
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ProjectPermissions = exports.UserPermissions = exports.SyncSchedule = exports.SyncCondition = exports.RequestMethods = exports.Provider = exports.ContextOptionsTypes = exports.ContextOptionsLocations = exports.EditorPanelsMode = exports.ProcessFileJobType = exports.SubscriptionInfoType = exports.AccountType = exports.Scope = exports.AuthenticationType = void 0;
3
+ exports.ProjectPermissions = exports.UserPermissions = exports.SyncSchedule = exports.SyncCondition = exports.RequestMethods = exports.Provider = exports.ContextOptionsTypes = exports.ContextOptionsLocations = exports.EditorMode = exports.ProcessFileJobType = exports.SubscriptionInfoType = exports.AccountType = exports.Scope = exports.AuthenticationType = void 0;
4
4
  var AuthenticationType;
5
5
  (function (AuthenticationType) {
6
6
  AuthenticationType["CODE"] = "authorization_code";
@@ -44,13 +44,13 @@ var ProcessFileJobType;
44
44
  ProcessFileJobType["PRE_EXPORT"] = "pre-export-file";
45
45
  ProcessFileJobType["POST_EXPORT"] = "post-export-file";
46
46
  })(ProcessFileJobType = exports.ProcessFileJobType || (exports.ProcessFileJobType = {}));
47
- var EditorPanelsMode;
48
- (function (EditorPanelsMode) {
49
- EditorPanelsMode["ASSETS"] = "assets";
50
- EditorPanelsMode["REVIEW"] = "review";
51
- EditorPanelsMode["TRANSLATE"] = "TRANSLATE";
52
- EditorPanelsMode["PROOFREAD"] = "proofread";
53
- })(EditorPanelsMode = exports.EditorPanelsMode || (exports.EditorPanelsMode = {}));
47
+ var EditorMode;
48
+ (function (EditorMode) {
49
+ EditorMode["ASSETS"] = "assets";
50
+ EditorMode["REVIEW"] = "review";
51
+ EditorMode["TRANSLATE"] = "TRANSLATE";
52
+ EditorMode["PROOFREAD"] = "proofread";
53
+ })(EditorMode = exports.EditorMode || (exports.EditorMode = {}));
54
54
  var ContextOptionsLocations;
55
55
  (function (ContextOptionsLocations) {
56
56
  ContextOptionsLocations["TM"] = "tm";
@@ -161,4 +161,52 @@
161
161
 
162
162
  #form .MuiButtonBase-root[type="submit"]:hover {
163
163
  background: rgba(236, 239, 241, .54);
164
- }
164
+ }
165
+
166
+ #buttons {
167
+ display: flex;
168
+ justify-content: space-between;
169
+ width: 100%;
170
+ }
171
+
172
+ #buttons crowdin-button:nth-of-type(2) {
173
+ margin-right: auto;
174
+ }
175
+
176
+ #user-errors-table {
177
+ margin-top: 24px;
178
+ display: block;
179
+ }
180
+
181
+ .error-detail-table {
182
+ height: auto;
183
+ max-height: 579px;
184
+ margin: -24px;
185
+ }
186
+
187
+ .error-detail-table table {
188
+ border-spacing: unset;
189
+ width: 100%;
190
+ }
191
+
192
+ .error-detail-table tr:first-child td {
193
+ padding-top: 24px;
194
+ border-top-right-radius: 12px;
195
+ }
196
+
197
+ .error-detail-table tr td:first-child {
198
+ color: #263238;
199
+ font-weight: 500;
200
+ min-width: 200px;
201
+ vertical-align: top;
202
+ }
203
+
204
+ .error-detail-table tr td:last-child {
205
+ background: #f6f6f6;
206
+ color: rgba(38,50,56,.87);
207
+ word-break: break-all;
208
+ }
209
+
210
+ .error-detail-table tr td {
211
+ padding: 8px 24px;
212
+ }
@@ -1,4 +1,4 @@
1
- import { Config, CrowdinCredentials, IntegrationCredentials, IntegrationFilesSnapshot, IntegrationSyncSettings, IntegrationWebhooks, Provider } from '../models';
1
+ import { Config, CrowdinCredentials, IntegrationCredentials, IntegrationFilesSnapshot, IntegrationSyncSettings, IntegrationWebhooks, Provider, UserErrors } from '../models';
2
2
  export interface Storage {
3
3
  migrate(): Promise<void>;
4
4
  saveCrowdinCredentials(credentials: CrowdinCredentials): Promise<void>;
@@ -29,6 +29,9 @@ export interface Storage {
29
29
  getWebhooks(fileId: string, integrationId: string, crowdinId: string, provider: Provider): Promise<IntegrationWebhooks | undefined>;
30
30
  saveWebhooks(fileId: string, integrationId: string, crowdinId: string, provider: Provider): Promise<void>;
31
31
  deleteWebhooks(fileIds: any[], integrationId: string, crowdinId: string, provider: Provider): Promise<void>;
32
+ getAllUserErrors(crowdinId: string, integrationId?: string): Promise<UserErrors[] | undefined>;
33
+ saveUserError(action: string, message: string, data: any, createdAt: string, crowdinId: string, integrationId?: string): Promise<void>;
34
+ deleteUserErrors(date: string, crowdinId: string, integrationId?: string): Promise<void>;
32
35
  }
33
36
  export declare function initialize(config: Config): Promise<void>;
34
37
  export declare function getStorage(): Storage;
@@ -1,5 +1,5 @@
1
1
  import { Storage } from '.';
2
- import { CrowdinCredentials, IntegrationCredentials, IntegrationFilesSnapshot, IntegrationSyncSettings, IntegrationWebhooks } from '../models';
2
+ import { CrowdinCredentials, IntegrationCredentials, IntegrationFilesSnapshot, IntegrationSyncSettings, IntegrationWebhooks, UserErrors } from '../models';
3
3
  export interface MySQLStorageConfig {
4
4
  uri?: string;
5
5
  host?: string;
@@ -46,4 +46,7 @@ export declare class MySQLStorage implements Storage {
46
46
  getWebhooks(fileId: string, integrationId: string, crowdinId: string, provider: string): Promise<IntegrationWebhooks | undefined>;
47
47
  saveWebhooks(fileId: string, integrationId: string, crowdinId: string, provider: string): Promise<void>;
48
48
  deleteWebhooks(fileIds: any[], integrationId: string, crowdinId: string, provider: string): Promise<void>;
49
+ getAllUserErrors(crowdinId: string, integrationId?: string): Promise<UserErrors[] | undefined>;
50
+ saveUserError(action: string, message: string, data: any, createdAt: string, crowdinId: string, integrationId?: string): Promise<void>;
51
+ deleteUserErrors(createdAt: string, crowdinId: string, integrationId?: string): Promise<void>;
49
52
  }
@@ -124,6 +124,18 @@ class MySQLStorage {
124
124
  provider varchar(255) not null
125
125
  )
126
126
  `);
127
+ yield connection.execute(`
128
+ create table if not exists user_errors
129
+ (
130
+ id int auto_increment primary key,
131
+ action varchar(255) not null,
132
+ message varchar(255) not null,
133
+ data text,
134
+ created_at varchar(255) not null,
135
+ crowdin_id varchar(255) not null,
136
+ integration_id varchar(255)
137
+ )
138
+ `);
127
139
  });
128
140
  }
129
141
  saveCrowdinCredentials(credentials) {
@@ -187,6 +199,7 @@ class MySQLStorage {
187
199
  yield connection.execute('DELETE FROM app_metadata WHERE crowdin_id = ?', [id]);
188
200
  yield connection.execute('DELETE FROM files_snapshot WHERE crowdin_id = ?', [id]);
189
201
  yield connection.execute('DELETE FROM webhooks WHERE crowdin_id = ?', [id]);
202
+ yield connection.execute('DELETE FROM user_errors WHERE crowdin_id = ?', [id]);
190
203
  }));
191
204
  });
192
205
  }
@@ -249,6 +262,7 @@ class MySQLStorage {
249
262
  yield connection.execute('DELETE FROM sync_settings where crowdin_id = ?', [crowdinId]);
250
263
  yield connection.execute('DELETE FROM files_snapshot where crowdin_id = ?', [crowdinId]);
251
264
  yield connection.execute('DELETE FROM webhooks where crowdin_id = ?', [crowdinId]);
265
+ yield connection.execute('DELETE FROM user_errors where crowdin_id = ?', [crowdinId]);
252
266
  }));
253
267
  });
254
268
  }
@@ -380,5 +394,40 @@ class MySQLStorage {
380
394
  yield this.executeQuery((connection) => connection.execute(`DELETE FROM webhooks WHERE file_id IN (${placeholders}) AND integration_id = ? AND crowdin_id = ? AND provider = ?`, [...fileIds, integrationId, crowdinId, provider]));
381
395
  });
382
396
  }
397
+ getAllUserErrors(crowdinId, integrationId) {
398
+ return __awaiter(this, void 0, void 0, function* () {
399
+ yield this.dbPromise;
400
+ return this.executeQuery((connection) => __awaiter(this, void 0, void 0, function* () {
401
+ let whereIntegrationCondition = 'integration_id is NULL';
402
+ const params = [crowdinId];
403
+ if (integrationId) {
404
+ whereIntegrationCondition = 'integration_id = ?';
405
+ params.push(integrationId);
406
+ }
407
+ const [rows] = yield connection.execute(`SELECT id, action, message, data, created_at as "createdAt" FROM user_errors WHERE crowdin_id = ? AND ${whereIntegrationCondition}`, params);
408
+ return rows || [];
409
+ }));
410
+ });
411
+ }
412
+ saveUserError(action, message, data, createdAt, crowdinId, integrationId) {
413
+ return __awaiter(this, void 0, void 0, function* () {
414
+ yield this.dbPromise;
415
+ yield this.executeQuery((connection) => connection.execute('INSERT INTO user_errors(action, message, data, created_at, integration_id, crowdin_id) VALUES (?, ?, ?, ?, ?, ?)', [action, message, data, createdAt, integrationId, crowdinId]));
416
+ });
417
+ }
418
+ deleteUserErrors(createdAt, crowdinId, integrationId) {
419
+ return __awaiter(this, void 0, void 0, function* () {
420
+ yield this.dbPromise;
421
+ yield this.executeQuery((connection) => {
422
+ let whereIntegrationCondition = 'integration_id is NULL';
423
+ const params = [createdAt, crowdinId];
424
+ if (integrationId) {
425
+ whereIntegrationCondition = 'integration_id = ?';
426
+ params.push(integrationId);
427
+ }
428
+ return connection.execute(`DELETE FROM user_errors WHERE created_at < ? AND crowdin_id = ? AND ${whereIntegrationCondition}`, params);
429
+ });
430
+ });
431
+ }
383
432
  }
384
433
  exports.MySQLStorage = MySQLStorage;
@@ -1,6 +1,6 @@
1
1
  import { Client } from 'pg';
2
2
  import { Storage } from '.';
3
- import { CrowdinCredentials, IntegrationCredentials, IntegrationFilesSnapshot, IntegrationSyncSettings, IntegrationWebhooks } from '../models';
3
+ import { CrowdinCredentials, IntegrationCredentials, IntegrationFilesSnapshot, IntegrationSyncSettings, IntegrationWebhooks, UserErrors } from '../models';
4
4
  export interface PostgreStorageConfig {
5
5
  host?: string;
6
6
  connectionString?: string;
@@ -51,4 +51,7 @@ export declare class PostgreStorage implements Storage {
51
51
  getWebhooks(fileId: string, integrationId: string, crowdinId: string, provider: string): Promise<IntegrationWebhooks | undefined>;
52
52
  saveWebhooks(fileId: string, integrationId: string, crowdinId: string, provider: string): Promise<void>;
53
53
  deleteWebhooks(fileIds: any[], integrationId: string, crowdinId: string, provider: string): Promise<void>;
54
+ getAllUserErrors(crowdinId: string, integrationId?: string): Promise<UserErrors[] | undefined>;
55
+ saveUserError(action: string, message: string, data: any, createdAt: string, crowdinId: string, integrationId?: string): Promise<void>;
56
+ deleteUserErrors(createdAt: string, crowdinId: string, integrationId?: string): Promise<void>;
54
57
  }