@crowdin/app-project-module 0.75.0 → 0.76.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.
@@ -108,6 +108,8 @@ const updateCrowdinTest = ({ appConfig, integrationTestConfig, }) => __awaiter(v
108
108
  type: types_1.JobClientType.MANUAL,
109
109
  fetchTranslation: jest.fn(),
110
110
  translationUploaded: jest.fn(),
111
+ markFilesAsUnsynced: jest.fn(),
112
+ unmarkFilesAsUnsynced: jest.fn(),
111
113
  },
112
114
  });
113
115
  }, 'Fail to run method updateCrowdin()');
@@ -65,6 +65,8 @@ const updateIntegrationTest = ({ appConfig, integrationTestConfig, }) => __await
65
65
  type: types_1.JobClientType.MANUAL,
66
66
  fetchTranslation: jest.fn(),
67
67
  translationUploaded: jest.fn(),
68
+ markFilesAsUnsynced: jest.fn(),
69
+ unmarkFilesAsUnsynced: jest.fn(),
68
70
  },
69
71
  });
70
72
  }, 'Fail to run method updateIntegration()');
@@ -12,6 +12,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
12
12
  const util_1 = require("../../../util");
13
13
  const defaults_1 = require("../util/defaults");
14
14
  const logger_1 = require("../../../util/logger");
15
+ const files_1 = require("../util/files");
15
16
  function handle(config, integration) {
16
17
  return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
17
18
  req.logInfo('Loading crowdin files');
@@ -19,9 +20,18 @@ function handle(config, integration) {
19
20
  const rootFolder = yield (0, defaults_1.getRootFolder)(config, integration, req.crowdinApiClient, req.crowdinContext.jwtPayload.context.project_id);
20
21
  req.logInfo(`Loading files ${rootFolder ? `from folder ${rootFolder.id}` : 'from root'}`);
21
22
  try {
22
- const files = integration.getCrowdinFiles
23
+ let files = integration.getCrowdinFiles
23
24
  ? yield integration.getCrowdinFiles(req.crowdinContext.jwtPayload.context.project_id, req.crowdinApiClient, rootFolder, req.integrationSettings)
24
25
  : [];
26
+ req.logInfo('Marking files as unsynced');
27
+ if (files.length > 0) {
28
+ files = yield (0, files_1.markUnsyncedFiles)({
29
+ integrationId: req.crowdinContext.clientId,
30
+ crowdinId: req.crowdinContext.crowdinId,
31
+ client: req.crowdinApiClient,
32
+ files,
33
+ });
34
+ }
25
35
  req.logInfo(`Returning ${files.length} files`);
26
36
  res.send(files);
27
37
  }
@@ -15,10 +15,7 @@ const logger_1 = require("../../../util/logger");
15
15
  function handle() {
16
16
  return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
17
17
  var _a;
18
- let userTimezone = (yield req.crowdinApiClient.usersApi.getAuthenticatedUser()).data.timezone;
19
- if (!userTimezone) {
20
- userTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
21
- }
18
+ const userTimezone = (yield req.crowdinApiClient.usersApi.getAuthenticatedUser()).data.timezone;
22
19
  req.logInfo(`Loading user errors for crowdinId ${req.crowdinContext.crowdinId} and integrationId ${req.crowdinContext.clientId}`);
23
20
  const userErrors = (_a = (yield (0, storage_1.getStorage)().getAllUserErrors(req.crowdinContext.crowdinId, req.crowdinContext.clientId))) === null || _a === void 0 ? void 0 : _a.map((userError) => {
24
21
  if (!(0, util_1.isJson)(userError.data)) {
@@ -28,16 +25,7 @@ function handle() {
28
25
  });
29
26
  }
30
27
  const date = new Date(+userError.createdAt);
31
- // Format the date as 'MMM DD, YYYY HH:mm'
32
- const formattedDate = new Intl.DateTimeFormat('en-US', {
33
- year: 'numeric',
34
- month: 'short',
35
- day: 'numeric',
36
- hour: '2-digit',
37
- minute: '2-digit',
38
- hour12: false,
39
- timeZone: userTimezone,
40
- }).format(date);
28
+ const formattedDate = (0, util_1.getFormattedDate)({ date, userTimezone });
41
29
  return Object.assign(Object.assign({}, userError), { formattedDate });
42
30
  });
43
31
  req.logInfo(`Returning ${userErrors === null || userErrors === void 0 ? void 0 : userErrors.length} user errors`);
@@ -289,7 +289,8 @@ export interface File {
289
289
  parentId?: string;
290
290
  nodeType?: IntegrationTreeElementType;
291
291
  customContent?: string;
292
- labels?: LabelTreeElement;
292
+ labels?: LabelTreeElement[];
293
+ failed?: boolean;
293
294
  }
294
295
  export interface Folder {
295
296
  id: string;
@@ -297,7 +298,7 @@ export interface Folder {
297
298
  parentId?: string;
298
299
  nodeType?: IntegrationTreeElementType;
299
300
  customContent?: string;
300
- labels?: LabelTreeElement;
301
+ labels?: LabelTreeElement[];
301
302
  }
302
303
  export type TreeItem = File | Folder;
303
304
  /**
@@ -390,6 +391,7 @@ type LabelTreeElementType = 'primary' | 'secondary' | 'success' | 'warning' | 'i
390
391
  export interface LabelTreeElement {
391
392
  text: string;
392
393
  type?: LabelTreeElementType;
394
+ tooltip?: string;
393
395
  color?: string;
394
396
  }
395
397
  export declare enum Provider {
@@ -1,5 +1,12 @@
1
1
  import { ExtendedResult, IntegrationFile, IntegrationLogic, IntegrationRequest, SkipIntegrationNodes, TreeItem } from '../types';
2
2
  import { JobClient } from './types';
3
+ import Crowdin from '@crowdin/crowdin-api-client';
3
4
  export declare function skipFilesByRegex(files: TreeItem[] | undefined, skipIntegrationNodes?: SkipIntegrationNodes): TreeItem[];
4
5
  export declare function expandFilesTree(nodes: IntegrationFile[], req: IntegrationRequest, integration: IntegrationLogic, job?: JobClient): Promise<IntegrationFile[]>;
5
6
  export declare function isExtendedResultType<T>(data?: T | ExtendedResult<T>): data is ExtendedResult<T>;
7
+ export declare function markUnsyncedFiles({ integrationId, crowdinId, client, files, }: {
8
+ integrationId: string;
9
+ crowdinId: string;
10
+ client: Crowdin;
11
+ files?: TreeItem[];
12
+ }): Promise<TreeItem[]>;
@@ -9,8 +9,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  });
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.isExtendedResultType = exports.expandFilesTree = exports.skipFilesByRegex = void 0;
12
+ exports.markUnsyncedFiles = exports.isExtendedResultType = exports.expandFilesTree = exports.skipFilesByRegex = void 0;
13
13
  const types_1 = require("./types");
14
+ const storage_1 = require("../../../storage");
15
+ const util_1 = require("../../../util");
14
16
  function skipFilesByRegex(files, skipIntegrationNodes) {
15
17
  if (!Array.isArray(files)) {
16
18
  return [];
@@ -55,3 +57,44 @@ function isExtendedResultType(data) {
55
57
  return !!dataTyped && !Array.isArray(dataTyped);
56
58
  }
57
59
  exports.isExtendedResultType = isExtendedResultType;
60
+ function isFileLeaf(file) {
61
+ return file.nodeType === '1' || !!file.type;
62
+ }
63
+ function markUnsyncedFiles({ integrationId, crowdinId, client, files, }) {
64
+ return __awaiter(this, void 0, void 0, function* () {
65
+ if (!Array.isArray(files)) {
66
+ return [];
67
+ }
68
+ const unsyncedFilesData = yield (0, storage_1.getStorage)().getUnsyncedFiles({ integrationId, crowdinId });
69
+ let unsyncedFiles = (unsyncedFilesData === null || unsyncedFilesData === void 0 ? void 0 : unsyncedFilesData.files)
70
+ ? JSON.parse(unsyncedFilesData === null || unsyncedFilesData === void 0 ? void 0 : unsyncedFilesData.files)
71
+ : [];
72
+ const fileIds = files.filter((file) => isFileLeaf(file)).map((file) => `${file.id}`);
73
+ const idsToRemove = unsyncedFiles.filter((file) => !fileIds.includes(file.id)).map((file) => file.id);
74
+ unsyncedFiles = unsyncedFiles.filter((file) => !idsToRemove.includes(file.id));
75
+ const userTimezone = (yield client.usersApi.getAuthenticatedUser()).data.timezone;
76
+ yield (0, storage_1.getStorage)().updateUnsyncedFiles({
77
+ integrationId,
78
+ crowdinId,
79
+ files: JSON.stringify(unsyncedFiles),
80
+ });
81
+ files = files.map((file) => {
82
+ const unsynced = unsyncedFiles.find((unsyncedFile) => unsyncedFile.id === file.id);
83
+ if (unsynced && isFileLeaf(file)) {
84
+ const formattedDate = (0, util_1.getFormattedDate)({ date: new Date(+unsynced.updatedAt), userTimezone });
85
+ file.labels = [
86
+ {
87
+ text: `Sync failed ${formattedDate}`,
88
+ type: 'warning',
89
+ tooltip: unsynced === null || unsynced === void 0 ? void 0 : unsynced.message,
90
+ },
91
+ ...(file.labels ? file.labels : []),
92
+ ];
93
+ file.failed = true;
94
+ }
95
+ return file;
96
+ });
97
+ return files;
98
+ });
99
+ }
100
+ exports.markUnsyncedFiles = markUnsyncedFiles;
@@ -149,7 +149,6 @@ function runAsJob({ integrationId, crowdinId, type, title, payload, res, project
149
149
  }
150
150
  return translation;
151
151
  }),
152
- // translationUploaded: async ({ fileId, languageId, etag }) => {
153
152
  translationUploaded: ({ fileId, translationParams }) => __awaiter(this, void 0, void 0, function* () {
154
153
  if (!isDbStore) {
155
154
  translationParams.forEach(({ languageId, etag }) => (store[integrationId][crowdinId][fileId][languageId] = { etag }));
@@ -176,6 +175,39 @@ function runAsJob({ integrationId, crowdinId, type, title, payload, res, project
176
175
  }
177
176
  yield Promise.all([...updates, ...inserts]);
178
177
  }),
178
+ markFilesAsUnsynced: ({ files }) => __awaiter(this, void 0, void 0, function* () {
179
+ const unsyncedFilesData = yield storage.getUnsyncedFiles({ integrationId, crowdinId });
180
+ const updatedFileRecords = files.map((file) => ({
181
+ id: file.fileId,
182
+ message: file.error,
183
+ updatedAt: `${Date.now()}`,
184
+ }));
185
+ if (unsyncedFilesData) {
186
+ const existingUnsyncedFiles = (unsyncedFilesData === null || unsyncedFilesData === void 0 ? void 0 : unsyncedFilesData.files) ? JSON.parse(unsyncedFilesData.files) : [];
187
+ const uniqueFilesMap = new Map([...existingUnsyncedFiles, ...updatedFileRecords].map((file) => [file.id, file]));
188
+ const uniqueFiles = Array.from(uniqueFilesMap.values());
189
+ yield storage.updateUnsyncedFiles({ integrationId, crowdinId, files: JSON.stringify(uniqueFiles) });
190
+ }
191
+ else {
192
+ yield storage.saveUnsyncedFiles({
193
+ integrationId,
194
+ crowdinId,
195
+ files: JSON.stringify(updatedFileRecords),
196
+ });
197
+ }
198
+ }),
199
+ unmarkFilesAsUnsynced: ({ files }) => __awaiter(this, void 0, void 0, function* () {
200
+ const unsyncedFilesData = yield storage.getUnsyncedFiles({ integrationId, crowdinId });
201
+ if (unsyncedFilesData) {
202
+ const existingUnsyncedFiles = (unsyncedFilesData === null || unsyncedFilesData === void 0 ? void 0 : unsyncedFilesData.files) ? JSON.parse(unsyncedFilesData.files) : [];
203
+ const updatedFiles = existingUnsyncedFiles.filter((file) => !files.some((unmarkFile) => unmarkFile.fileId === file.id));
204
+ yield storage.updateUnsyncedFiles({
205
+ integrationId,
206
+ crowdinId,
207
+ files: JSON.stringify(updatedFiles),
208
+ });
209
+ }
210
+ }),
179
211
  };
180
212
  try {
181
213
  const data = yield jobCallback(job);
@@ -3,6 +3,8 @@ import { Config } from '../../../types';
3
3
  import { IntegrationLogic, IntegrationSyncSettings } from '../types';
4
4
  import { ResponseObject } from '@crowdin/crowdin-api-client/out/core';
5
5
  import { TranslationsModel } from '@crowdin/crowdin-api-client/out/translations';
6
+ import { AxiosError } from 'axios';
7
+ import { AppModuleError, AppUserModuleError } from '../../../util/logger';
6
8
  export declare enum JobType {
7
9
  UPDATE_TO_CROWDIN = "updateCrowdin",
8
10
  UPDATE_TO_INTEGRATION = "updateIntegration",
@@ -56,6 +58,8 @@ export type JobClient = {
56
58
  type: JobClientType;
57
59
  translationUploaded: SaveUploadedFileTranslation;
58
60
  fetchTranslation: FetchTranslation;
61
+ markFilesAsUnsynced: MarkUnsyncedFiles;
62
+ unmarkFilesAsUnsynced: UnmarkUnsyncedFiles;
59
63
  };
60
64
  export type UpdateJobProgress = ({ progress, status, info, data, attempt, }: Omit<UpdateJobParams, 'id'>) => Promise<{
61
65
  isCanceled: boolean;
@@ -92,3 +96,20 @@ export interface TranslationCache {
92
96
  export type GetFileTranslationCache = Pick<TranslationCache, 'integrationId' | 'crowdinId' | 'fileId'>;
93
97
  export type GetFileTranslationCacheByLanguageParams = Pick<TranslationCache, 'integrationId' | 'crowdinId' | 'fileId' | 'languageId'>;
94
98
  export type UpdateTranslationCacheParams = Pick<TranslationCache, 'integrationId' | 'crowdinId' | 'fileId' | 'languageId' | 'etag'>;
99
+ export type MarkUnsyncedFiles = ({ files, }: {
100
+ files: {
101
+ fileId: number;
102
+ error: AxiosError | AppModuleError | AppUserModuleError | Error | string;
103
+ }[];
104
+ }) => Promise<void>;
105
+ export type UnmarkUnsyncedFiles = ({ files }: {
106
+ files: {
107
+ fileId: number;
108
+ }[];
109
+ }) => Promise<void>;
110
+ export interface UnsyncedFiles {
111
+ integrationId: string;
112
+ crowdinId: string;
113
+ files: string;
114
+ }
115
+ export type GetUnsyncedFiles = Pick<UnsyncedFiles, 'integrationId' | 'crowdinId'>;
@@ -1,8 +1,22 @@
1
1
  import { IntegrationConfig, IntegrationCredentials, IntegrationFilesSnapshot, IntegrationSyncSettings, IntegrationWebhooks, Provider } from '../modules/integration/types';
2
2
  import { Config, CrowdinCredentials, UnauthorizedConfig } from '../types';
3
- import { CreateJobParams, GetActiveJobsParams, GetJobParams, GetFileTranslationCacheByLanguageParams, Job, TranslationCache, UpdateJobParams, UpdateTranslationCacheParams, GetFileTranslationCache } from '../modules/integration/util/types';
3
+ import { CreateJobParams, GetActiveJobsParams, GetJobParams, GetFileTranslationCacheByLanguageParams, Job, TranslationCache, UpdateJobParams, UpdateTranslationCacheParams, GetFileTranslationCache, UnsyncedFiles, GetUnsyncedFiles } from '../modules/integration/util/types';
4
4
  import { UserErrors } from './types';
5
+ declare const TABLES: {
6
+ crowdin_credentials: string;
7
+ integration_credentials: string;
8
+ sync_settings: string;
9
+ app_metadata: string;
10
+ files_snapshot: string;
11
+ webhooks: string;
12
+ user_errors: string;
13
+ integration_settings: string;
14
+ job: string;
15
+ translation_file_cache: string;
16
+ unsynced_files: string;
17
+ };
5
18
  export interface Storage {
19
+ tables: typeof TABLES;
6
20
  migrate(): Promise<void>;
7
21
  saveCrowdinCredentials(credentials: CrowdinCredentials): Promise<void>;
8
22
  updateCrowdinCredentials(credentials: CrowdinCredentials): Promise<void>;
@@ -51,6 +65,10 @@ export interface Storage {
51
65
  getFileTranslationCache(params: GetFileTranslationCache): Promise<TranslationCache[] | undefined>;
52
66
  getFileTranslationCacheByLanguage(params: GetFileTranslationCacheByLanguageParams): Promise<TranslationCache | undefined>;
53
67
  updateTranslationCache(params: UpdateTranslationCacheParams): Promise<void>;
68
+ saveUnsyncedFiles(params: UnsyncedFiles): Promise<void>;
69
+ getUnsyncedFiles(params: GetUnsyncedFiles): Promise<UnsyncedFiles | undefined>;
70
+ updateUnsyncedFiles(params: UnsyncedFiles): Promise<void>;
54
71
  }
55
72
  export declare function initialize(config: Config | UnauthorizedConfig): Promise<void>;
56
73
  export declare function getStorage(): Storage;
74
+ export {};
@@ -1,4 +1,5 @@
1
1
  "use strict";
2
+ /* eslint-disable @typescript-eslint/camelcase */
2
3
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
4
  if (k2 === undefined) k2 = k;
4
5
  var desc = Object.getOwnPropertyDescriptor(m, k);
@@ -45,6 +46,19 @@ const path_1 = __importStar(require("path"));
45
46
  const fs_1 = __importDefault(require("fs"));
46
47
  const child_process_1 = require("child_process");
47
48
  const util = __importStar(require("node:util"));
49
+ const TABLES = {
50
+ crowdin_credentials: 'crowdin_credentials',
51
+ integration_credentials: 'integration_credentials',
52
+ sync_settings: 'sync_settings',
53
+ app_metadata: 'app_metadata',
54
+ files_snapshot: 'files_snapshot',
55
+ webhooks: 'webhooks',
56
+ user_errors: 'user_errors',
57
+ integration_settings: 'integration_settings',
58
+ job: 'job',
59
+ translation_file_cache: 'translation_file_cache',
60
+ unsynced_files: 'unsynced_files',
61
+ };
48
62
  let storage;
49
63
  function initialize(config) {
50
64
  return __awaiter(this, void 0, void 0, function* () {
@@ -83,46 +97,36 @@ exports.getStorage = getStorage;
83
97
  function createDumpForMigration(config) {
84
98
  const sqliteFilePath = (0, path_1.join)(config.dbFolder, types_1.storageFiles.SQLITE);
85
99
  const backupFilePath = (0, path_1.join)(config.dbFolder, types_1.storageFiles.SQLITE_BACKUP);
86
- const dumpFilePath = (0, path_1.join)(config.dbFolder, 'dump_sqlite.sql');
87
100
  if (!fs_1.default.existsSync(sqliteFilePath)) {
88
101
  (0, logger_1.log)('SQLite database not found, skipping migration dump creation');
89
102
  return;
90
103
  }
91
104
  (0, logger_1.log)('Creating dump for migration from SQLite to PostgreSQL');
92
- (0, child_process_1.execSync)(`sqlite3 ${sqliteFilePath} .dump > ${dumpFilePath}`);
105
+ for (const tableName in TABLES) {
106
+ (0, logger_1.log)(`Creating dump for table ${tableName}`);
107
+ const dumpFileName = util.format(types_1.storageFiles.DUMP, tableName);
108
+ const dumpFilePath = path_1.default.join(config.dbFolder, dumpFileName);
109
+ (0, child_process_1.execSync)(`sqlite3 ${sqliteFilePath} ".output ${dumpFilePath}" ".dump ${tableName}" ".output stdout"`);
110
+ let modifiedContent = fs_1.default.readFileSync(dumpFilePath).toString();
111
+ // 1. Remove SQLite-specific PRAGMA statements
112
+ modifiedContent = modifiedContent.replace(/PRAGMA foreign_keys=OFF;\n/g, '');
113
+ // 2. Adjust transaction syntax for PostgreSQL
114
+ modifiedContent = modifiedContent.replace(/BEGIN TRANSACTION;\n/g, '');
115
+ modifiedContent = modifiedContent.replace(/COMMIT TRANSACTION;\n/g, '');
116
+ // 3. Ensure tables are only created if they don't already exist
117
+ modifiedContent = modifiedContent
118
+ .replace(/CREATE TABLE IF NOT EXISTS/g, 'CREATE TABLE') // Remove duplicate IF NOT EXISTS
119
+ .replace(/CREATE TABLE/g, 'CREATE TABLE IF NOT EXISTS'); // Add IF NOT EXISTS if missing
120
+ // 4. Add `ON CONFLICT DO NOTHING` to INSERT statements to handle conflicts gracefully
121
+ modifiedContent = modifiedContent.replace(/(INSERT INTO [^;]+)(;)/g, '$1 ON CONFLICT DO NOTHING;');
122
+ // 5. Convert SQLite-specific data types to PostgreSQL equivalents
123
+ modifiedContent = modifiedContent.replace(/integer not null primary key autoincrement/gi, 'serial primary key');
124
+ modifiedContent = modifiedContent.replace(/varchar not null primary key/gi, 'varchar primary key');
125
+ // 6. Remove SQLite-specific function replace()
126
+ modifiedContent = modifiedContent.replace(/replace\s*\('([^']+?)',\s*'?\\[rn]'?\s*,\s*char\s*\([0-9]+\)\s*\)/gi, "'$1'");
127
+ fs_1.default.writeFileSync(dumpFilePath, modifiedContent, { encoding: 'utf8', flag: 'w' });
128
+ }
93
129
  fs_1.default.renameSync(sqliteFilePath, backupFilePath);
94
- let modifiedContent = fs_1.default.readFileSync(dumpFilePath).toString();
95
- // 1. Remove SQLite-specific PRAGMA statements
96
- modifiedContent = modifiedContent.replace(/PRAGMA foreign_keys=OFF;\n/g, '');
97
- // 2. Adjust transaction syntax for PostgreSQL
98
- modifiedContent = modifiedContent.replace(/BEGIN TRANSACTION;\n/g, '');
99
- modifiedContent = modifiedContent.replace(/COMMIT TRANSACTION;\n/g, '');
100
- // 3. Ensure tables are only created if they don't already exist
101
- modifiedContent = modifiedContent
102
- .replace(/CREATE TABLE IF NOT EXISTS/g, 'CREATE TABLE') // Remove duplicate IF NOT EXISTS
103
- .replace(/CREATE TABLE/g, 'CREATE TABLE IF NOT EXISTS'); // Add IF NOT EXISTS if missing
104
- // 4. Add `ON CONFLICT DO NOTHING` to INSERT statements to handle conflicts gracefully
105
- modifiedContent = modifiedContent.replace(/(INSERT INTO [^;]+)(;)/g, '$1 ON CONFLICT DO NOTHING;');
106
- // 5. Convert SQLite-specific data types to PostgreSQL equivalents
107
- modifiedContent = modifiedContent.replace(/integer not null primary key autoincrement/gi, 'serial primary key');
108
- modifiedContent = modifiedContent.replace(/varchar not null primary key/gi, 'varchar primary key');
109
- // 6. Remove SQLite-specific function replace()
110
- modifiedContent = modifiedContent.replace(/replace\s*\('([^']+?)',\s*'?\\[rn]'?\s*,\s*char\s*\([0-9]+\)\s*\)/gi, "'$1'");
111
- // 7. Remove SQLite-specific sequences
112
- modifiedContent = modifiedContent.replace(/DELETE FROM sqlite_sequence;\n/g, '');
113
- modifiedContent = modifiedContent.replace(/INSERT INTO sqlite_sequence .*;\n/g, '');
114
- // 8. Split the dump into separate statements
115
- const tables = modifiedContent.split(/(?=CREATE TABLE)/g);
116
- tables.forEach((tableSql, index) => {
117
- const match = tableSql.match(/CREATE TABLE IF NOT EXISTS (\w+)/);
118
- if (match) {
119
- const dumpFileName = util.format(types_1.storageFiles.DUMP_CHUNK, index);
120
- (0, logger_1.log)(`Creating dump chunk ${dumpFileName} file for table ${match[1]}`);
121
- const outputFilePath = path_1.default.join(config.dbFolder, dumpFileName);
122
- fs_1.default.writeFileSync(outputFilePath, tableSql);
123
- }
124
- });
125
- fs_1.default.unlinkSync(dumpFilePath);
126
130
  }
127
131
  function getSqLiteFileFromBackup(config) {
128
132
  const sqliteFilePath = (0, path_1.join)(config.dbFolder, types_1.storageFiles.SQLITE);
@@ -1,6 +1,6 @@
1
1
  import { Storage } from '.';
2
2
  import { CrowdinCredentials } from '../types';
3
- import { CreateJobParams, GetActiveJobsParams, GetJobParams, GetFileTranslationCacheByLanguageParams, Job, TranslationCache, UpdateJobParams, UpdateTranslationCacheParams, GetFileTranslationCache } from '../modules/integration/util/types';
3
+ import { CreateJobParams, GetActiveJobsParams, GetJobParams, GetFileTranslationCacheByLanguageParams, Job, TranslationCache, UpdateJobParams, UpdateTranslationCacheParams, GetFileTranslationCache, UnsyncedFiles, GetUnsyncedFiles } from '../modules/integration/util/types';
4
4
  import { IntegrationConfig, IntegrationCredentials, IntegrationFilesSnapshot, IntegrationSyncSettings, IntegrationWebhooks } from '../modules/integration/types';
5
5
  import { UserErrors } from './types';
6
6
  export interface MySQLStorageConfig {
@@ -17,6 +17,19 @@ export declare class MySQLStorage implements Storage {
17
17
  private _rej?;
18
18
  private dbPromise;
19
19
  private config;
20
+ tables: {
21
+ crowdin_credentials: string;
22
+ integration_credentials: string;
23
+ sync_settings: string;
24
+ app_metadata: string;
25
+ files_snapshot: string;
26
+ webhooks: string;
27
+ user_errors: string;
28
+ integration_settings: string;
29
+ job: string;
30
+ translation_file_cache: string;
31
+ unsynced_files: string;
32
+ };
20
33
  constructor(config: MySQLStorageConfig);
21
34
  executeQuery<T>(command: (connection: any) => Promise<T>): Promise<T>;
22
35
  migrate(): Promise<void>;
@@ -68,4 +81,7 @@ export declare class MySQLStorage implements Storage {
68
81
  getFileTranslationCache({ integrationId, crowdinId, fileId, }: GetFileTranslationCache): Promise<TranslationCache[] | undefined>;
69
82
  getFileTranslationCacheByLanguage({ integrationId, crowdinId, fileId, languageId, }: GetFileTranslationCacheByLanguageParams): Promise<TranslationCache | undefined>;
70
83
  updateTranslationCache({ integrationId, crowdinId, fileId, languageId, etag, }: UpdateTranslationCacheParams): Promise<void>;
84
+ saveUnsyncedFiles({ integrationId, crowdinId, files }: UnsyncedFiles): Promise<void>;
85
+ updateUnsyncedFiles({ integrationId, crowdinId, files }: UnsyncedFiles): Promise<void>;
86
+ getUnsyncedFiles({ integrationId, crowdinId }: GetUnsyncedFiles): Promise<UnsyncedFiles | undefined>;
71
87
  }