@crowdin/app-project-module 0.38.0 → 0.39.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
  }));
@@ -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 = (yield req.crowdinApiClient.usersApi.getAuthenticatedUser()).data.timezone;
18
+ if (!userTimezone) {
19
+ userTimezone = Intl.DateTimeFormat().resolvedOptions().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);
@@ -860,6 +860,15 @@ export interface IntegrationWebhooks {
860
860
  crowdinId: string;
861
861
  provider: Provider;
862
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
+ }
863
872
  export interface ImagePath {
864
873
  /**
865
874
  * path to app logo (e.g. {@example join(__dirname, 'logo.png')})
@@ -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-bottom: 12px;
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
+ }