@crowdin/app-project-module 0.74.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.
@@ -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'>;
@@ -45,8 +45,8 @@ const connection_1 = require("../../../util/connection");
45
45
  const defaults_1 = require("./defaults");
46
46
  const index_1 = require("../../../util/index");
47
47
  const logger_1 = require("../../../util/logger");
48
- const prefetchCount = 20;
49
- const forceProcessDelay = 5000;
48
+ const prefetchCount = 10;
49
+ const forceProcessDelay = 10000;
50
50
  exports.HookEvents = {
51
51
  fileAdded: 'file.added',
52
52
  fileDeleted: 'file.deleted',
@@ -407,7 +407,7 @@ function consumer({ channel, config, integration, }) {
407
407
  webhooksInfo[clientId].data.push(data);
408
408
  }
409
409
  if (messagesCounter < prefetchCount) {
410
- // if all messages are not received, wait 5 seconds to force process messages
410
+ // if all messages are not received, wait 10 seconds to force process messages
411
411
  timeoutId = setTimeout(() => __awaiter(this, void 0, void 0, function* () {
412
412
  yield processMessages({
413
413
  webhooksData,
@@ -436,16 +436,22 @@ function consumer({ channel, config, integration, }) {
436
436
  }
437
437
  function processMessages({ channel, msg, webhooksData, webhooksInfo, }) {
438
438
  return __awaiter(this, void 0, void 0, function* () {
439
- yield Promise.all(webhooksData);
440
- for (const { data, integration, webhookData } of Object.values(webhooksInfo)) {
441
- if (webhookData && webhookData.crowdinClient) {
442
- yield updateCrowdinFromWebhookRequest({
443
- integration: integration,
444
- webhookData: webhookData,
445
- req: data,
446
- });
439
+ try {
440
+ yield Promise.all(webhooksData);
441
+ for (const { data, integration, webhookData } of Object.values(webhooksInfo)) {
442
+ if (webhookData && webhookData.crowdinClient) {
443
+ yield updateCrowdinFromWebhookRequest({
444
+ integration: integration,
445
+ webhookData: webhookData,
446
+ req: data,
447
+ });
448
+ }
447
449
  }
450
+ channel.ack(msg, true);
451
+ }
452
+ catch (e) {
453
+ (0, logger_1.logError)(e);
454
+ channel.nack(msg, false, false);
448
455
  }
449
- channel.ack(msg, true);
450
456
  });
451
457
  }
@@ -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,28 @@
1
1
  "use strict";
2
+ /* eslint-disable @typescript-eslint/camelcase */
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || function (mod) {
20
+ if (mod && mod.__esModule) return mod;
21
+ var result = {};
22
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
23
+ __setModuleDefault(result, mod);
24
+ return result;
25
+ };
2
26
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
27
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
28
  return new (P || (P = Promise))(function (resolve, reject) {
@@ -8,18 +32,44 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
32
  step((generator = generator.apply(thisArg, _arguments || [])).next());
9
33
  });
10
34
  };
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
11
38
  Object.defineProperty(exports, "__esModule", { value: true });
12
39
  exports.getStorage = exports.initialize = void 0;
40
+ const types_1 = require("../types");
13
41
  const logger_1 = require("../util/logger");
14
42
  const mysql_1 = require("./mysql");
15
43
  const postgre_1 = require("./postgre");
16
44
  const sqlite_1 = require("./sqlite");
45
+ const path_1 = __importStar(require("path"));
46
+ const fs_1 = __importDefault(require("fs"));
47
+ const child_process_1 = require("child_process");
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
+ };
17
62
  let storage;
18
63
  function initialize(config) {
19
64
  return __awaiter(this, void 0, void 0, function* () {
20
65
  if (config.postgreConfig) {
21
66
  (0, logger_1.log)('Using PostgreSQL database');
22
- storage = new postgre_1.PostgreStorage(config.postgreConfig);
67
+ let dumpDirectory = null;
68
+ if (config.migrateToPostgreFromSQLite) {
69
+ dumpDirectory = config.dbFolder;
70
+ createDumpForMigration(config);
71
+ }
72
+ storage = new postgre_1.PostgreStorage(config.postgreConfig, dumpDirectory);
23
73
  }
24
74
  else if (config.mysqlConfig) {
25
75
  (0, logger_1.log)('Using MySQL database');
@@ -27,6 +77,10 @@ function initialize(config) {
27
77
  }
28
78
  else {
29
79
  (0, logger_1.log)('Using SQLite database');
80
+ if (config.migrateToPostgreFromSQLite === false) {
81
+ (0, logger_1.log)('Try to get SQLite file from backup');
82
+ getSqLiteFileFromBackup(config);
83
+ }
30
84
  storage = new sqlite_1.SQLiteStorage({ dbFolder: config.dbFolder });
31
85
  }
32
86
  yield storage.migrate();
@@ -40,3 +94,45 @@ function getStorage() {
40
94
  return storage;
41
95
  }
42
96
  exports.getStorage = getStorage;
97
+ function createDumpForMigration(config) {
98
+ const sqliteFilePath = (0, path_1.join)(config.dbFolder, types_1.storageFiles.SQLITE);
99
+ const backupFilePath = (0, path_1.join)(config.dbFolder, types_1.storageFiles.SQLITE_BACKUP);
100
+ if (!fs_1.default.existsSync(sqliteFilePath)) {
101
+ (0, logger_1.log)('SQLite database not found, skipping migration dump creation');
102
+ return;
103
+ }
104
+ (0, logger_1.log)('Creating dump for migration from SQLite to PostgreSQL');
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
+ }
129
+ fs_1.default.renameSync(sqliteFilePath, backupFilePath);
130
+ }
131
+ function getSqLiteFileFromBackup(config) {
132
+ const sqliteFilePath = (0, path_1.join)(config.dbFolder, types_1.storageFiles.SQLITE);
133
+ const backupFilePath = (0, path_1.join)(config.dbFolder, types_1.storageFiles.SQLITE_BACKUP);
134
+ if (fs_1.default.existsSync(backupFilePath) && !fs_1.default.existsSync(sqliteFilePath)) {
135
+ (0, logger_1.log)('Restoring SQLite database from backup');
136
+ fs_1.default.renameSync(backupFilePath, sqliteFilePath);
137
+ }
138
+ }
@@ -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
  }
@@ -2,6 +2,7 @@
2
2
  /* eslint-disable no-unused-expressions */
3
3
  /* eslint-disable @typescript-eslint/no-var-requires */
4
4
  /* eslint-disable @typescript-eslint/ban-ts-ignore */
5
+ /* eslint-disable @typescript-eslint/camelcase */
5
6
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
6
7
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
7
8
  return new (P || (P = Promise))(function (resolve, reject) {
@@ -23,6 +24,100 @@ class MySQLStorage {
23
24
  this._res = res;
24
25
  this._rej = rej;
25
26
  });
27
+ this.tables = {
28
+ crowdin_credentials: `(
29
+ id varchar(255) primary key,
30
+ app_secret text,
31
+ domain varchar(255),
32
+ user_id varchar(255),
33
+ agent_id varchar(255),
34
+ organization_id varchar(255),
35
+ base_url varchar(255),
36
+ access_token text not null,
37
+ refresh_token text not null,
38
+ expire varchar(255) not null,
39
+ type varchar(255) not null
40
+ )`,
41
+ integration_credentials: `(
42
+ id varchar(255) primary key,
43
+ credentials text,
44
+ crowdin_id varchar(255) not null,
45
+ managers text
46
+ )`,
47
+ sync_settings: `(
48
+ id int auto_increment primary key,
49
+ files text,
50
+ integration_id varchar(255) not null,
51
+ crowdin_id varchar(255) not null,
52
+ type varchar(255) not null,
53
+ provider varchar(255) not null
54
+ )`,
55
+ app_metadata: `(
56
+ id varchar(255) primary key,
57
+ data text,
58
+ crowdin_id text
59
+ )`,
60
+ files_snapshot: `(
61
+ id int auto_increment primary key,
62
+ files text,
63
+ integration_id varchar(255) not null,
64
+ crowdin_id varchar(255) not null,
65
+ provider varchar(255) not null
66
+ )`,
67
+ webhooks: `(
68
+ id int auto_increment primary key,
69
+ file_id varchar(255) not null,
70
+ integration_id varchar(255) not null,
71
+ crowdin_id varchar(255) not null,
72
+ provider varchar(255) not null
73
+ )`,
74
+ user_errors: `(
75
+ id int auto_increment primary key,
76
+ action varchar(255) not null,
77
+ message varchar(255) not null,
78
+ data text,
79
+ created_at varchar(255) not null,
80
+ crowdin_id varchar(255) not null,
81
+ integration_id varchar(255)
82
+ )`,
83
+ integration_settings: `(
84
+ id int auto_increment primary key,
85
+ integration_id varchar(255) not null,
86
+ crowdin_id varchar(255) not null,
87
+ config text
88
+ )`,
89
+ job: `(
90
+ id varchar(255) not null primary key,
91
+ integration_id varchar(255) not null,
92
+ crowdin_id varchar(255) not null,
93
+ type varchar(255) not null,
94
+ payload text,
95
+ title text,
96
+ progress int 0,
97
+ status varchar(255) '${types_1.JobStatus.CREATED}',
98
+ payload text,
99
+ info text,
100
+ data text,
101
+ attempt int 0,
102
+ created_at varchar(255) not null,
103
+ updated_at varchar(255),
104
+ finished_at varchar(255)
105
+ )`,
106
+ translation_file_cache: `(
107
+ id int auto_increment primary key,
108
+ integration_id varchar(255) not null,
109
+ crowdin_id varchar(255) not null,
110
+ file_id int not null,
111
+ language_id varchar(255) not null,
112
+ etag varchar(255)
113
+ )`,
114
+ unsynced_files: `(
115
+ id int auto_increment primary key,
116
+ integration_id varchar(255) not null,
117
+ crowdin_id varchar(255) not null,
118
+ files text
119
+ )`,
120
+ };
26
121
  this.config = config;
27
122
  }
28
123
  executeQuery(command) {
@@ -52,7 +147,7 @@ class MySQLStorage {
52
147
  migrate() {
53
148
  return __awaiter(this, void 0, void 0, function* () {
54
149
  try {
55
- yield this.executeQuery(this.addTables);
150
+ yield this.executeQuery((connection) => this.addTables(connection));
56
151
  this._res && this._res();
57
152
  }
58
153
  catch (e) {
@@ -63,122 +158,9 @@ class MySQLStorage {
63
158
  }
64
159
  addTables(connection) {
65
160
  return __awaiter(this, void 0, void 0, function* () {
66
- yield connection.execute(`
67
- create table if not exists crowdin_credentials
68
- (
69
- id varchar(255) primary key,
70
- app_secret text,
71
- domain varchar(255),
72
- user_id varchar(255),
73
- agent_id varchar(255),
74
- organization_id varchar(255),
75
- base_url varchar(255),
76
- access_token text not null,
77
- refresh_token text not null,
78
- expire varchar(255) not null,
79
- type varchar(255) not null
80
- )
81
- `);
82
- yield connection.execute(`
83
- create table if not exists integration_credentials
84
- (
85
- id varchar(255) primary key,
86
- credentials text,
87
- crowdin_id varchar(255) not null,
88
- managers text
89
- )
90
- `);
91
- yield connection.execute(`
92
- create table if not exists sync_settings
93
- (
94
- id int auto_increment primary key,
95
- files text,
96
- integration_id varchar(255) not null,
97
- crowdin_id varchar(255) not null,
98
- type varchar(255) not null,
99
- provider varchar(255) not null
100
- )
101
- `);
102
- yield connection.execute(`
103
- create table if not exists app_metadata
104
- (
105
- id varchar(255) primary key,
106
- data text,
107
- crowdin_id text
108
- )
109
- `);
110
- yield connection.execute(`
111
- create table if not exists files_snapshot
112
- (
113
- id int auto_increment primary key,
114
- files text,
115
- integration_id varchar(255) not null,
116
- crowdin_id varchar(255) not null,
117
- provider varchar(255) not null
118
- )
119
- `);
120
- yield connection.execute(`
121
- create table if not exists webhooks
122
- (
123
- id int auto_increment primary key,
124
- file_id varchar(255) not null,
125
- integration_id varchar(255) not null,
126
- crowdin_id varchar(255) not null,
127
- provider varchar(255) not null
128
- )
129
- `);
130
- yield connection.execute(`
131
- create table if not exists user_errors
132
- (
133
- id int auto_increment primary key,
134
- action varchar(255) not null,
135
- message varchar(255) not null,
136
- data text,
137
- created_at varchar(255) not null,
138
- crowdin_id varchar(255) not null,
139
- integration_id varchar(255)
140
- )
141
- `);
142
- yield connection.execute(`
143
- create table if not exists integration_settings
144
- (
145
- id int auto_increment primary key,
146
- integration_id varchar(255) not null,
147
- crowdin_id varchar(255) not null,
148
- config text
149
- )
150
- `);
151
- yield connection.execute(`
152
- create table if not exists job
153
- (
154
- id varchar(255) not null primary key,
155
- integration_id varchar(255) not null,
156
- crowdin_id varchar(255) not null,
157
- type varchar(255) not null,
158
- payload text,
159
- title text,
160
- progress int 0,
161
- status varchar(255) '${types_1.JobStatus.CREATED}',
162
- payload text,
163
- info text,
164
- data text,
165
- attempt int 0,
166
- created_at varchar(255) not null,
167
- updated_at varchar(255),
168
- finished_at varchar(255)
169
- )
170
- `);
171
- yield connection.execute(`
172
- create table if not exists translation_file_cache
173
- (
174
- id int auto_increment primary key,
175
- integration_id varchar(255) not null,
176
- crowdin_id varchar(255) not null,
177
- file_id int not null,
178
- language_id varchar(255) not null,
179
- etag varchar(255)
180
- )
181
- `);
161
+ for (const [tableName, tableSchema] of Object.entries(this.tables)) {
162
+ yield connection.execute(`create table if not exists ${tableName} ${tableSchema}`);
163
+ }
182
164
  });
183
165
  }
184
166
  saveCrowdinCredentials(credentials) {
@@ -248,6 +230,7 @@ class MySQLStorage {
248
230
  yield connection.execute('DELETE FROM integration_settings WHERE crowdin_id = ?', [id]);
249
231
  yield connection.execute('DELETE FROM job WHERE crowdin_id = ?', [id]);
250
232
  yield connection.execute('DELETE FROM translation_file_cache WHERE crowdin_id = ?', [id]);
233
+ yield connection.execute('DELETE FROM unsynced_files WHERE crowdin_id = ?', [id]);
251
234
  }));
252
235
  });
253
236
  }
@@ -300,6 +283,7 @@ class MySQLStorage {
300
283
  yield connection.execute('DELETE FROM files_snapshot where integration_id = ?', [id]);
301
284
  yield connection.execute('DELETE FROM webhooks where integration_id = ?', [id]);
302
285
  yield connection.execute('DELETE FROM job where integration_id = ?', [id]);
286
+ yield connection.execute('DELETE FROM unsynced_files where integration_id = ?', [id]);
303
287
  }));
304
288
  });
305
289
  }
@@ -313,6 +297,7 @@ class MySQLStorage {
313
297
  yield connection.execute('DELETE FROM webhooks where crowdin_id = ?', [crowdinId]);
314
298
  yield connection.execute('DELETE FROM user_errors where crowdin_id = ?', [crowdinId]);
315
299
  yield connection.execute('DELETE FROM job where crowdin_id = ?', [crowdinId]);
300
+ yield connection.execute('DELETE FROM unsynced_files where crowdin_id = ?', [crowdinId]);
316
301
  }));
317
302
  });
318
303
  }
@@ -675,5 +660,37 @@ class MySQLStorage {
675
660
  `, [etag, integrationId, crowdinId, fileId, languageId]));
676
661
  });
677
662
  }
663
+ saveUnsyncedFiles({ integrationId, crowdinId, files }) {
664
+ return __awaiter(this, void 0, void 0, function* () {
665
+ yield this.dbPromise;
666
+ yield this.executeQuery((connection) => connection.execute(`
667
+ INSERT INTO unsynced_files(integration_id, crowdin_id, files)
668
+ VALUES (?, ?, ?,)
669
+ `, [integrationId, crowdinId, files]));
670
+ });
671
+ }
672
+ updateUnsyncedFiles({ integrationId, crowdinId, files }) {
673
+ return __awaiter(this, void 0, void 0, function* () {
674
+ yield this.dbPromise;
675
+ yield this.executeQuery((connection) => connection.execute(`
676
+ UPDATE unsynced_files
677
+ SET files = ?
678
+ WHERE integration_id = ? AND crowdin_id = ?
679
+ `, [files, integrationId, crowdinId]));
680
+ });
681
+ }
682
+ getUnsyncedFiles({ integrationId, crowdinId }) {
683
+ return __awaiter(this, void 0, void 0, function* () {
684
+ yield this.dbPromise;
685
+ return this.executeQuery((connection) => __awaiter(this, void 0, void 0, function* () {
686
+ const [rows] = yield connection.execute(`
687
+ SELECT fileIds
688
+ FROM unsynced_files
689
+ WHERE integration_id = ? AND crowdin_id = ?
690
+ `, [integrationId, crowdinId]);
691
+ return (rows || [])[0];
692
+ }));
693
+ });
694
+ }
678
695
  }
679
696
  exports.MySQLStorage = MySQLStorage;
@@ -2,7 +2,7 @@ import { Client } from 'pg';
2
2
  import { Storage } from '.';
3
3
  import { CrowdinCredentials } from '../types';
4
4
  import { IntegrationConfig, IntegrationCredentials, IntegrationFilesSnapshot, IntegrationSyncSettings, IntegrationWebhooks } from '../modules/integration/types';
5
- import { CreateJobParams, GetActiveJobsParams, GetJobParams, GetFileTranslationCacheByLanguageParams, Job, TranslationCache, UpdateJobParams, UpdateTranslationCacheParams, GetFileTranslationCache } from '../modules/integration/util/types';
5
+ import { CreateJobParams, GetActiveJobsParams, GetJobParams, GetFileTranslationCacheByLanguageParams, Job, TranslationCache, UpdateJobParams, UpdateTranslationCacheParams, GetFileTranslationCache, UnsyncedFiles, GetUnsyncedFiles } from '../modules/integration/util/types';
6
6
  import { UserErrors } from './types';
7
7
  export interface PostgreStorageConfig {
8
8
  host?: string;
@@ -18,10 +18,24 @@ export interface PostgreStorageConfig {
18
18
  }
19
19
  export declare class PostgreStorage implements Storage {
20
20
  private config;
21
+ private directoryPath;
21
22
  private _res?;
22
23
  private _rej?;
23
24
  private dbPromise;
24
- constructor(config: PostgreStorageConfig);
25
+ tables: {
26
+ crowdin_credentials: string;
27
+ integration_credentials: string;
28
+ sync_settings: string;
29
+ app_metadata: string;
30
+ files_snapshot: string;
31
+ webhooks: string;
32
+ user_errors: string;
33
+ integration_settings: string;
34
+ job: string;
35
+ translation_file_cache: string;
36
+ unsynced_files: string;
37
+ };
38
+ constructor(config: PostgreStorageConfig, directoryPath: string | null);
25
39
  executeQuery<T>(command: (client: Client) => Promise<T>): Promise<T>;
26
40
  migrate(): Promise<void>;
27
41
  alterTables(client: Client): Promise<void>;
@@ -75,4 +89,8 @@ export declare class PostgreStorage implements Storage {
75
89
  getFileTranslationCache({ integrationId, crowdinId, fileId, }: GetFileTranslationCache): Promise<TranslationCache[] | undefined>;
76
90
  getFileTranslationCacheByLanguage({ integrationId, crowdinId, fileId, languageId, }: GetFileTranslationCacheByLanguageParams): Promise<TranslationCache | undefined>;
77
91
  updateTranslationCache({ integrationId, crowdinId, fileId, languageId, etag, }: UpdateTranslationCacheParams): Promise<void>;
92
+ private migrateFromSqlite;
93
+ saveUnsyncedFiles({ integrationId, crowdinId, files }: UnsyncedFiles): Promise<void>;
94
+ updateUnsyncedFiles({ integrationId, crowdinId, files }: UnsyncedFiles): Promise<void>;
95
+ getUnsyncedFiles({ integrationId, crowdinId }: GetUnsyncedFiles): Promise<UnsyncedFiles | undefined>;
78
96
  }