UNPKG

@crowdin/app-project-module

Version:

Module that generates for you all common endpoints for serving standalone Crowdin App

975 lines (974 loc) 47.1 kB
"use strict"; /* eslint-disable no-unused-expressions */ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.MySQLStorage = void 0; const uuid_1 = require("uuid"); const types_1 = require("../modules/integration/util/types"); const util_1 = require("../util"); class MySQLStorage { constructor(config) { this.mysql = require('mysql2/promise'); this.dbPromise = new Promise((res, rej) => { this._res = res; this._rej = rej; }); this.tableIndexes = { integration_credentials: { idx_crowdin: '(crowdin_id)', }, sync_settings: { idx_integration_provider: '(integration_id, provider)', idx_crowdin: '(crowdin_id)', idx_type: '(type)', }, files_snapshot: { idx_integration: '(integration_id)', idx_crowdin: '(crowdin_id)', }, webhooks: { idx_integration_crowdin: '(integration_id, crowdin_id)', idx_file_provider: '(file_id, provider)', }, user_errors: { idx_integration_crowdin: '(integration_id, crowdin_id)', idx_crowdin: '(crowdin_id)', idx_created_at: '(created_at)', }, integration_settings: { idx_integration: '(integration_id)', idx_crowdin: '(crowdin_id)', }, job: { idx_integration_crowdin: '(integration_id, crowdin_id)', idx_finished_at: '(finished_at)', idx_status: '(status)', }, translation_file_cache: { idx_integration_crowdin_file_language: '(integration_id, crowdin_id, file_id, language_id)', idx_crowdin: '(crowdin_id)', }, unsynced_files: { idx_integration_crowdin: '(integration_id, crowdin_id)', idx_crowdin: '(crowdin_id)', }, }; this.tables = { crowdin_credentials: `( id varchar(255) primary key, app_secret text, domain varchar(255), user_id varchar(255), agent_id varchar(255), organization_id varchar(255), base_url varchar(255), access_token text not null, refresh_token text not null, expire varchar(255) not null, type varchar(255) not null )`, integration_credentials: `( id varchar(255) primary key, credentials text, crowdin_id varchar(255) not null, managers text )`, sync_settings: `( id int auto_increment primary key, files text, integration_id varchar(255) not null, crowdin_id varchar(255) not null, type varchar(255) not null, provider varchar(255) not null )`, app_metadata: `( id varchar(255) primary key, data text, crowdin_id text )`, files_snapshot: `( id int auto_increment primary key, files text, integration_id varchar(255) not null, crowdin_id varchar(255) not null, provider varchar(255) not null )`, webhooks: `( id int auto_increment primary key, file_id varchar(255) not null, integration_id varchar(255) not null, crowdin_id varchar(255) not null, provider varchar(255) not null )`, user_errors: `( id int auto_increment primary key, action varchar(255) not null, message varchar(255) not null, data text, created_at varchar(255) not null, crowdin_id varchar(255) not null, integration_id varchar(255) )`, integration_settings: `( id int auto_increment primary key, integration_id varchar(255) not null, crowdin_id varchar(255) not null, config text )`, job: `( id varchar(255) not null primary key, integration_id varchar(255) not null, crowdin_id varchar(255) not null, type varchar(255) not null, payload text, title text, progress int 0, status varchar(255) '${types_1.JobStatus.CREATED}', payload text, info text, data text, attempt int 0, errors text, processed_entities text, created_at varchar(255) not null, updated_at varchar(255), finished_at varchar(255) )`, translation_file_cache: `( id int auto_increment primary key, integration_id varchar(255) not null, crowdin_id varchar(255) not null, file_id int not null, language_id varchar(255) not null, etag varchar(255) )`, unsynced_files: `( id int auto_increment primary key, integration_id varchar(255) not null, crowdin_id varchar(255) not null, files text )`, synced_data: `( id int auto_increment primary key, files text, integration_id varchar(255) not null, crowdin_id varchar(255) not null, type varchar(255) not null, updated_at varchar(255) null, )`, }; this.config = config; } executeQuery(command) { return __awaiter(this, void 0, void 0, function* () { return (0, util_1.executeWithRetry)(() => __awaiter(this, void 0, void 0, function* () { let connection; try { connection = yield this.mysql.createConnection(this.config); const res = yield command(connection); yield connection.end(); return res; } catch (error) { if (connection) { try { yield connection.end(); } catch (nestedError) { throw nestedError; } } throw error; } })); }); } migrate() { return __awaiter(this, void 0, void 0, function* () { try { yield this.executeQuery((connection) => this.addTables(connection)); this._res && this._res(); } catch (e) { console.error(e); this._rej && this._rej(); } }); } addTables(connection) { return __awaiter(this, void 0, void 0, function* () { for (const [tableName, tableSchema] of Object.entries(this.tables)) { yield connection.execute(`create table if not exists ${tableName} ${tableSchema}`); if (this.tableIndexes[tableName]) { for (const [indexName, indexSchema] of Object.entries(this.tableIndexes[tableName])) { // For MySQL, we need to handle partial indexes differently since MySQL doesn't support WHERE clauses in indexes // We'll create the basic index without the WHERE clause const indexColumns = indexSchema.replace(/WHERE.*$/, '').trim(); yield connection.execute(`create index if not exists ${indexName} on ${tableName}${indexColumns}`); } } } }); } saveCrowdinCredentials(credentials) { return __awaiter(this, void 0, void 0, function* () { yield this.dbPromise; yield this.executeQuery((connection) => connection.execute('INSERT INTO crowdin_credentials(id, app_secret, domain, user_id, agent_id, organization_id, base_url, access_token, refresh_token, expire, type) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', [ credentials.id, credentials.appSecret, credentials.domain, credentials.userId, credentials.agentId, credentials.organizationId, credentials.baseUrl, credentials.accessToken, credentials.refreshToken, credentials.expire, credentials.type, ])); }); } updateCrowdinCredentials(credentials) { return __awaiter(this, void 0, void 0, function* () { yield this.dbPromise; yield this.executeQuery((connection) => connection.execute('UPDATE crowdin_credentials SET app_secret = ?, domain = ?, user_id = ?, agent_id = ?, organization_id = ?, base_url = ?, access_token = ?, refresh_token = ?, expire = ? WHERE id = ?', [ credentials.appSecret, credentials.domain, credentials.userId, credentials.agentId, credentials.organizationId, credentials.baseUrl, credentials.accessToken, credentials.refreshToken, credentials.expire, credentials.id, ])); }); } getCrowdinCredentials(id) { return __awaiter(this, void 0, void 0, function* () { yield this.dbPromise; return this.executeQuery((connection) => __awaiter(this, void 0, void 0, function* () { const [rows] = yield connection.execute('SELECT id, app_secret as "appSecret", domain, user_id as "userId", agent_id as "agentId", organization_id as "organizationId", base_url as "baseUrl", access_token as "accessToken", refresh_token as "refreshToken", expire, type FROM crowdin_credentials WHERE id = ?', [id]); return (rows || [])[0]; })); }); } getAllCrowdinCredentials() { return __awaiter(this, void 0, void 0, function* () { yield this.dbPromise; return this.executeQuery((connection) => __awaiter(this, void 0, void 0, function* () { const [rows] = yield connection.execute('SELECT id, app_secret as "appSecret", domain, user_id as "userId", agent_id as "agentId", organization_id as "organizationId", base_url as "baseUrl", access_token as "accessToken", refresh_token as "refreshToken", expire, type FROM crowdin_credentials', []); return rows || []; })); }); } deleteCrowdinCredentials(id) { return __awaiter(this, void 0, void 0, function* () { yield this.dbPromise; yield this.executeQuery((connection) => __awaiter(this, void 0, void 0, function* () { yield connection.execute('DELETE FROM crowdin_credentials where id = ?', [id]); yield connection.execute('DELETE FROM integration_credentials where crowdin_id = ?', [id]); yield connection.execute('DELETE FROM sync_settings WHERE crowdin_id = ?', [id]); yield connection.execute('DELETE FROM app_metadata WHERE crowdin_id = ?', [id]); yield connection.execute('DELETE FROM files_snapshot WHERE crowdin_id = ?', [id]); yield connection.execute('DELETE FROM webhooks WHERE crowdin_id = ?', [id]); yield connection.execute('DELETE FROM user_errors WHERE crowdin_id = ?', [id]); yield connection.execute('DELETE FROM integration_settings WHERE crowdin_id = ?', [id]); yield connection.execute('DELETE FROM job WHERE crowdin_id = ?', [id]); yield connection.execute('DELETE FROM translation_file_cache WHERE crowdin_id = ?', [id]); yield connection.execute('DELETE FROM unsynced_files WHERE crowdin_id = ?', [id]); yield connection.execute('DELETE FROM synced_data WHERE crowdin_id = ?', [id]); })); }); } saveIntegrationCredentials(id, credentials, crowdinId) { return __awaiter(this, void 0, void 0, function* () { yield this.dbPromise; yield this.executeQuery((connection) => connection.execute('INSERT INTO integration_credentials(id, credentials, crowdin_id) VALUES (?, ?, ?)', [ id, credentials, crowdinId, ])); }); } updateIntegrationCredentials(id, credentials) { return __awaiter(this, void 0, void 0, function* () { yield this.dbPromise; yield this.executeQuery((connection) => connection.execute('UPDATE integration_credentials SET credentials = ? WHERE id = ?', [credentials, id])); }); } updateIntegrationManagers(id, managers) { return __awaiter(this, void 0, void 0, function* () { yield this.dbPromise; yield this.executeQuery((connection) => connection.execute('UPDATE integration_credentials SET managers = ? WHERE id = ?', [managers, id])); }); } getIntegrationCredentials(id) { return __awaiter(this, void 0, void 0, function* () { yield this.dbPromise; return this.executeQuery((connection) => __awaiter(this, void 0, void 0, function* () { const [rows] = yield connection.execute('SELECT id, credentials, crowdin_id as "crowdinId", managers FROM integration_credentials WHERE id = ?', [id]); return (rows || [])[0]; })); }); } getAllIntegrationCredentials(crowdinId) { return __awaiter(this, void 0, void 0, function* () { yield this.dbPromise; return this.executeQuery((connection) => __awaiter(this, void 0, void 0, function* () { const [rows] = yield connection.execute('SELECT id, credentials, crowdin_id as "crowdinId", managers FROM integration_credentials WHERE crowdin_id = ?', [crowdinId]); return rows || []; })); }); } deleteIntegrationCredentials(id) { return __awaiter(this, void 0, void 0, function* () { yield this.dbPromise; yield this.executeQuery((connection) => __awaiter(this, void 0, void 0, function* () { yield connection.execute('DELETE FROM integration_credentials where id = ?', [id]); yield connection.execute('DELETE FROM sync_settings where integration_id = ?', [id]); yield connection.execute('DELETE FROM files_snapshot where integration_id = ?', [id]); yield connection.execute('DELETE FROM webhooks where integration_id = ?', [id]); yield connection.execute('DELETE FROM job where integration_id = ?', [id]); yield connection.execute('DELETE FROM unsynced_files where integration_id = ?', [id]); yield connection.execute('DELETE FROM synced_data where integration_id = ?', [id]); })); }); } deleteAllIntegrationCredentials(crowdinId) { return __awaiter(this, void 0, void 0, function* () { yield this.dbPromise; yield this.executeQuery((connection) => __awaiter(this, void 0, void 0, function* () { yield connection.execute('DELETE FROM integration_credentials where crowdin_id = ?', [crowdinId]); yield connection.execute('DELETE FROM sync_settings where crowdin_id = ?', [crowdinId]); yield connection.execute('DELETE FROM files_snapshot where crowdin_id = ?', [crowdinId]); yield connection.execute('DELETE FROM webhooks where crowdin_id = ?', [crowdinId]); yield connection.execute('DELETE FROM user_errors where crowdin_id = ?', [crowdinId]); yield connection.execute('DELETE FROM job where crowdin_id = ?', [crowdinId]); yield connection.execute('DELETE FROM unsynced_files where crowdin_id = ?', [crowdinId]); yield connection.execute('DELETE FROM synced_data where crowdin_id = ?', [crowdinId]); })); }); } saveMetadata(id, metadata, crowdinId) { return __awaiter(this, void 0, void 0, function* () { yield this.dbPromise; yield this.executeQuery((connection) => connection.execute('INSERT INTO app_metadata(id, data, crowdin_id) VALUES (?, ?, ?)', [ id, JSON.stringify(metadata), crowdinId, ])); }); } updateMetadata(id, metadata, crowdinId) { return __awaiter(this, void 0, void 0, function* () { yield this.dbPromise; yield this.executeQuery((connection) => connection.execute('UPDATE app_metadata SET data = ?, crowdin_id = ? WHERE id = ?', [ JSON.stringify(metadata), crowdinId, id, ])); }); } getMetadata(id) { return __awaiter(this, void 0, void 0, function* () { yield this.dbPromise; return this.executeQuery((connection) => __awaiter(this, void 0, void 0, function* () { const [rows] = yield connection.execute('SELECT data FROM app_metadata WHERE id = ?', [id]); if (rows && rows[0]) { return JSON.parse(rows[0].data); } })); }); } getAllMetadata() { return __awaiter(this, void 0, void 0, function* () { yield this.dbPromise; return this.executeQuery((connection) => __awaiter(this, void 0, void 0, function* () { const [rows] = yield connection.execute('SELECT * FROM app_metadata'); return rows || []; })); }); } deleteMetadata(id) { return __awaiter(this, void 0, void 0, function* () { yield this.dbPromise; yield this.executeQuery((connection) => connection.execute('DELETE FROM app_metadata where id = ?', [id])); }); } getSyncSettingsByProvider(integrationId, provider) { return __awaiter(this, void 0, void 0, function* () { yield this.dbPromise; return this.executeQuery((connection) => __awaiter(this, void 0, void 0, function* () { const [rows] = yield connection.execute('SELECT id, files, integration_id as "integrationId", crowdin_id as "crowdinId", type, provider FROM sync_settings WHERE integration_id = ? AND provider = ?', [integrationId, provider]); return (rows || [])[0]; })); }); } getSyncSettingsBySchedule(type, schedule) { return __awaiter(this, void 0, void 0, function* () { yield this.dbPromise; return this.executeQuery((connection) => __awaiter(this, void 0, void 0, function* () { const [rows] = yield connection.execute(` SELECT s.id, s.files, s.integration_id as "integrationId", s.crowdin_id as "crowdinId", s.type, s.provider FROM sync_settings s INNER JOIN integration_settings i ON s.integration_id = i.integration_id WHERE s.type = ? AND CASE WHEN i.config IS NULL THEN false ELSE JSON_EXTRACT(i.config, '$.schedule') = ? END `, [type, schedule]); return rows || []; })); }); } saveSyncSettings(files, integrationId, crowdinId, type, provider) { return __awaiter(this, void 0, void 0, function* () { yield this.dbPromise; yield this.executeQuery((connection) => connection.execute('INSERT INTO sync_settings(files, integration_id, crowdin_id, type, provider) VALUES (?, ?, ?, ?, ?)', [files, integrationId, crowdinId, type, provider])); }); } updateSyncSettings(files, integrationId, crowdinId, type, provider) { return __awaiter(this, void 0, void 0, function* () { yield this.dbPromise; yield this.executeQuery((connection) => connection.execute('UPDATE sync_settings SET files = ? WHERE integration_id = ? AND crowdin_id = ? AND type = ? AND provider = ?', [files, integrationId, crowdinId, type, provider])); }); } getSyncSettings(integrationId, crowdinId, type, provider) { return __awaiter(this, void 0, void 0, function* () { yield this.dbPromise; return this.executeQuery((connection) => __awaiter(this, void 0, void 0, function* () { const [rows] = yield connection.execute('SELECT id, files, integration_id as "integrationId", crowdin_id as "crowdinId", type, provider FROM sync_settings WHERE integration_id = ? AND crowdin_id = ? AND type = ? AND provider = ?', [integrationId, crowdinId, type, provider]); return (rows || [])[0]; })); }); } saveFilesSnapshot(files, integrationId, crowdinId, provider) { return __awaiter(this, void 0, void 0, function* () { yield this.dbPromise; yield this.executeQuery((connection) => connection.execute('INSERT INTO files_snapshot(files, integration_id, crowdin_id, provider) VALUES (?, ?, ?, ?)', [files, integrationId, crowdinId, provider])); }); } updateFilesSnapshot(files, integrationId, crowdinId, provider) { return __awaiter(this, void 0, void 0, function* () { yield this.dbPromise; yield this.executeQuery((connection) => connection.execute('UPDATE files_snapshot SET files = ? WHERE integration_id = ? AND crowdin_id = ? AND provider = ?', [files, integrationId, crowdinId, provider])); }); } getFilesSnapshot(integrationId, crowdinId, provider) { return __awaiter(this, void 0, void 0, function* () { yield this.dbPromise; return this.executeQuery((connection) => __awaiter(this, void 0, void 0, function* () { const [rows] = yield connection.execute('SELECT id, files, integration_id as "integrationId", crowdin_id as "crowdinId" FROM files_snapshot WHERE integration_id = ? AND crowdin_id = ? AND provider = ?', [integrationId, crowdinId, provider]); return (rows || [])[0]; })); }); } getAllWebhooks(integrationId, crowdinId, provider) { return __awaiter(this, void 0, void 0, function* () { yield this.dbPromise; return this.executeQuery((connection) => __awaiter(this, void 0, void 0, function* () { const [rows] = yield connection.execute('SELECT id, file_id as "fileId", integration_id as "integrationId", crowdin_id as "crowdinId" FROM webhooks WHERE integration_id = ? AND crowdin_id = ? AND provider = ?', [integrationId, crowdinId, provider]); return rows || []; })); }); } getWebhooks(fileId, integrationId, crowdinId, provider) { return __awaiter(this, void 0, void 0, function* () { yield this.dbPromise; return this.executeQuery((connection) => __awaiter(this, void 0, void 0, function* () { const [rows] = yield connection.execute('SELECT id, file_id as "fileId", integration_id as "integrationId", crowdin_id as "crowdinId" FROM webhooks WHERE file_id = ? AND integration_id = ? AND crowdin_id = ? AND provider = ?', [fileId, integrationId, crowdinId, provider]); return (rows || [])[0]; })); }); } saveWebhooks(fileId, integrationId, crowdinId, provider) { return __awaiter(this, void 0, void 0, function* () { yield this.dbPromise; yield this.executeQuery((connection) => connection.execute('INSERT INTO webhooks(file_id, integration_id, crowdin_id, provider) VALUES (?, ?, ?, ?)', [fileId, integrationId, crowdinId, provider])); }); } deleteWebhooks(fileIds, integrationId, crowdinId, provider) { return __awaiter(this, void 0, void 0, function* () { if (!fileIds.length) { return; } yield this.dbPromise; const placeholders = fileIds.map(() => '?').join(','); 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])); }); } getAllUserErrors(crowdinId, integrationId) { return __awaiter(this, void 0, void 0, function* () { yield this.dbPromise; return this.executeQuery((connection) => __awaiter(this, void 0, void 0, function* () { let whereIntegrationCondition = 'integration_id is NULL'; const params = [crowdinId]; if (integrationId) { whereIntegrationCondition = 'integration_id = ?'; params.push(integrationId); } const [rows] = yield connection.execute(`SELECT id, action, message, data, created_at as "createdAt" FROM user_errors WHERE crowdin_id = ? AND ${whereIntegrationCondition}`, params); return rows || []; })); }); } saveUserError(action, message, data, createdAt, crowdinId, integrationId) { return __awaiter(this, void 0, void 0, function* () { yield this.dbPromise; 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])); }); } deleteUserErrors(createdAt, crowdinId, integrationId) { return __awaiter(this, void 0, void 0, function* () { yield this.dbPromise; yield this.executeQuery((connection) => { let whereIntegrationCondition = 'integration_id is NULL'; const params = [createdAt, crowdinId]; if (integrationId) { whereIntegrationCondition = 'integration_id = ?'; params.push(integrationId); } return connection.execute(`DELETE FROM user_errors WHERE created_at < ? AND crowdin_id = ? AND ${whereIntegrationCondition}`, params); }); }); } deleteAllUsersErrorsOlderThan(createdAt) { return __awaiter(this, void 0, void 0, function* () { yield this.dbPromise; yield this.executeQuery((connection) => connection.execute('DELETE FROM user_errors WHERE created_at < ?', [createdAt])); }); } saveIntegrationConfig(integrationId, crowdinId, config) { return __awaiter(this, void 0, void 0, function* () { yield this.dbPromise; yield this.executeQuery((connection) => connection.execute('INSERT INTO integration_settings(integrationId, crowdin_id, config) VALUES (?, ?, ?)', [ integrationId, crowdinId, config, ])); }); } getAllIntegrationConfigs(crowdinId) { return __awaiter(this, void 0, void 0, function* () { yield this.dbPromise; return this.executeQuery((connection) => __awaiter(this, void 0, void 0, function* () { const [rows] = yield connection.execute('SELECT config FROM integration_settings WHERE crowdin_id = ?', [ crowdinId, ]); return rows || []; })); }); } getIntegrationConfig(integrationId) { return __awaiter(this, void 0, void 0, function* () { yield this.dbPromise; return this.executeQuery((connection) => __awaiter(this, void 0, void 0, function* () { const [rows] = yield connection.execute('SELECT config FROM integration_settings WHERE integration_id = ?', [integrationId]); return (rows || [])[0]; })); }); } updateIntegrationConfig(integrationId, config) { return __awaiter(this, void 0, void 0, function* () { yield this.dbPromise; yield this.executeQuery((connection) => connection.execute('UPDATE integration_settings SET config = ? WHERE id = ?', [config, integrationId])); }); } createJob({ integrationId, crowdinId, type, title, payload }) { return __awaiter(this, void 0, void 0, function* () { const id = (0, uuid_1.v4)(); yield this.dbPromise; yield this.executeQuery((connection) => connection.execute(` INSERT INTO job(id, integration_id, crowdin_id, type, payload, title, created_at) VALUES (?, ?, ?, ?, ?, ?, ?) `, [id, integrationId, crowdinId, type, payload, title, Date.now().toString()])); return id; }); } updateJob({ id, progress, status, info, data, attempt, errors, processedEntities, }) { return __awaiter(this, void 0, void 0, function* () { const updateFields = ['updated_at']; const updateParams = [Date.now().toString()]; if (progress) { updateFields.push('progress = ?'); updateParams.push(Math.round(progress)); if (progress >= 100) { updateFields.push('finished_at = ?'); updateParams.push(Date.now().toString()); } } if (status) { updateFields.push('status = ?'); updateParams.push(status); if (!updateFields.includes('finished_at = ?') && [types_1.JobStatus.FAILED, types_1.JobStatus.CANCELED].includes(status)) { updateFields.push('finished_at = ?'); updateParams.push(Date.now().toString()); } } if (data) { updateFields.push('data = ?'); updateParams.push(data); } if (info) { updateFields.push('info = ?'); updateParams.push(info); } if (attempt) { updateFields.push('attempt = ?'); updateParams.push(attempt); } if (errors) { updateFields.push('errors = ?'); updateParams.push(JSON.stringify(errors)); } if (processedEntities) { updateFields.push('processed_entities = ?'); updateParams.push(processedEntities); } updateParams.push(id); yield this.dbPromise; yield this.executeQuery((connection) => connection.execute(` UPDATE job SET ${updateFields.join(', ')} WHERE id = ? `, updateParams)); }); } getJob({ id }) { return __awaiter(this, void 0, void 0, function* () { yield this.dbPromise; return this.executeQuery((connection) => __awaiter(this, void 0, void 0, function* () { const [rows] = yield connection.execute(` SELECT id, integration_id as "integrationId", crowdin_id as "crowdinId", type, payload, progress, status, title, info, data, attempt, errors, processed_entities as "processedEntities", created_at as "createdAt", updated_at as "updatedAt", finished_at as "finishedAt" FROM job WHERE id = ? `, [id]); return (rows || [])[0]; })); }); } getActiveJobs({ integrationId, crowdinId }) { return __awaiter(this, void 0, void 0, function* () { yield this.dbPromise; return this.executeQuery((connection) => __awaiter(this, void 0, void 0, function* () { const [rows] = yield connection.execute(` SELECT id, integration_id as "integrationId", crowdin_id as "crowdinId", type, payload, progress, status, title, info, data, attempt, errors, processed_entities as "processedEntities", created_at as "createdAt", updated_at as "updatedAt", finished_at as "finishedAt" FROM job WHERE integration_id = ? AND crowdin_id = ? AND finished_at is NULL `, [integrationId, crowdinId]); return rows || []; })); }); } deleteFinishedJobs() { return __awaiter(this, void 0, void 0, function* () { yield this.dbPromise; yield this.executeQuery((connection) => connection.execute('DELETE FROM job WHERE finished_at is not NULL', [])); }); } getAllInProgressJobs() { return __awaiter(this, void 0, void 0, function* () { yield this.dbPromise; return this.executeQuery((connection) => __awaiter(this, void 0, void 0, function* () { const [rows] = yield connection.execute(` SELECT id, integration_id as "integrationId", crowdin_id as "crowdinId", type, payload, progress, status, title, info, data, attempt, errors, processed_entities as "processedEntities", created_at as "createdAt", updated_at as "updatedAt", finished_at as "finishedAt" FROM job WHERE status IN (?, ?) AND finished_at is NULL `, [types_1.JobStatus.IN_PROGRESS, types_1.JobStatus.CREATED]); return rows || []; })); }); } getAllJobs({ integrationId, crowdinId, limit, offset }) { return __awaiter(this, void 0, void 0, function* () { yield this.dbPromise; return this.executeQuery((connection) => __awaiter(this, void 0, void 0, function* () { const [rows] = yield connection.execute(` SELECT id, integration_id as "integrationId", crowdin_id as "crowdinId", type, payload, progress, status, title, info, data, attempt, created_at as "createdAt", updated_at as "updatedAt", finished_at as "finishedAt" FROM job WHERE integration_id = ? AND crowdin_id = ? ORDER BY created_at DESC LIMIT ? OFFSET ? `, [integrationId, crowdinId, limit, offset]); return rows || []; })); }); } saveTranslationCache({ integrationId, crowdinId, fileId, languageId, etag, }) { return __awaiter(this, void 0, void 0, function* () { yield this.dbPromise; yield this.executeQuery((connection) => connection.execute(` INSERT INTO translation_file_cache(integration_id, crowdin_id, file_id, language_id, etag) VALUES (?, ?, ?, ?, ?) `, [integrationId, crowdinId, fileId, languageId, etag])); }); } getFileTranslationCache({ integrationId, crowdinId, fileId, }) { return __awaiter(this, void 0, void 0, function* () { yield this.dbPromise; return this.executeQuery((connection) => __awaiter(this, void 0, void 0, function* () { const [rows] = yield connection.execute(` SELECT integration_id as "integrationId", crowdin_id as "crowdinId", file_id as "fileId", language_id as "languageId", etag FROM translation_file_cache WHERE integration_id = ? AND crowdin_id = ? AND file_id = ? `, [integrationId, crowdinId, fileId]); return rows || []; })); }); } getFileTranslationCacheByLanguage({ integrationId, crowdinId, fileId, languageId, }) { return __awaiter(this, void 0, void 0, function* () { yield this.dbPromise; return this.executeQuery((connection) => __awaiter(this, void 0, void 0, function* () { const [rows] = yield connection.execute(` SELECT integration_id as "integrationId", crowdin_id as "crowdinId", file_id as "fileId", language_id as "languageId", etag FROM translation_file_cache WHERE integration_id = ? AND crowdin_id = ? AND file_id = ? AND language_id = ? `, [integrationId, crowdinId, fileId, languageId]); return (rows || [])[0]; })); }); } updateTranslationCache({ integrationId, crowdinId, fileId, languageId, etag, }) { return __awaiter(this, void 0, void 0, function* () { yield this.dbPromise; yield this.executeQuery((connection) => connection.execute(` UPDATE translation_file_cache SET etag = ? WHERE integration_id = ? AND crowdin_id = ? AND file_id = ? AND language_id = ? `, [etag, integrationId, crowdinId, fileId, languageId])); }); } saveUnsyncedFiles({ integrationId, crowdinId, files }) { return __awaiter(this, void 0, void 0, function* () { yield this.dbPromise; yield this.executeQuery((connection) => connection.execute(` INSERT INTO unsynced_files(integration_id, crowdin_id, files) VALUES (?, ?, ?,) `, [integrationId, crowdinId, files])); }); } updateUnsyncedFiles({ integrationId, crowdinId, files }) { return __awaiter(this, void 0, void 0, function* () { yield this.dbPromise; yield this.executeQuery((connection) => connection.execute(` UPDATE unsynced_files SET files = ? WHERE integration_id = ? AND crowdin_id = ? `, [files, integrationId, crowdinId])); }); } getUnsyncedFiles({ integrationId, crowdinId }) { return __awaiter(this, void 0, void 0, function* () { yield this.dbPromise; return this.executeQuery((connection) => __awaiter(this, void 0, void 0, function* () { const [rows] = yield connection.execute(` SELECT fileIds FROM unsynced_files WHERE integration_id = ? AND crowdin_id = ? `, [integrationId, crowdinId]); return (rows || [])[0]; })); }); } registerCustomTable(tableName, schema) { return __awaiter(this, void 0, void 0, function* () { const columns = Object.entries(schema) .map(([col, def]) => `${col} ${def}`) .join(', '); const query = `CREATE TABLE IF NOT EXISTS ${tableName} ( ${columns} );`; yield this.executeQuery((connection) => __awaiter(this, void 0, void 0, function* () { yield connection.execute(query); return; })); }); } insertRecord(tableName, data) { return __awaiter(this, void 0, void 0, function* () { const columns = Object.keys(data).join(', '); const placeholders = Object.keys(data) .map(() => '?') .join(', '); const values = Object.values(data); const query = `INSERT INTO ${tableName} (${columns}) VALUES (${placeholders});`; yield this.executeQuery((connection) => __awaiter(this, void 0, void 0, function* () { yield connection.execute(query, values); return; })); }); } selectRecords(tableName, options = {}, params = []) { var _a; return __awaiter(this, void 0, void 0, function* () { const columns = ((_a = options.columns) === null || _a === void 0 ? void 0 : _a.length) ? options.columns.join(', ') : '*'; const distinctKeyword = options.distinct ? 'DISTINCT ' : ''; const whereClause = options.whereClause ? ` ${options.whereClause}` : ''; const orderByClause = options.orderBy ? ` ORDER BY ${options.orderBy}` : ''; let limitClause = options.limit ? ` LIMIT ${options.limit}` : ''; if (!options.limit && options.offset) { throw new Error('[selectRecords] Limit is required for pagination with MySQL'); } else if (options.limit && options.offset) { limitClause += ` OFFSET ${options.offset}`; } const query = `SELECT ${distinctKeyword}${columns} FROM ${tableName}${whereClause}${orderByClause}${limitClause};`; return this.executeQuery((connection) => __awaiter(this, void 0, void 0, function* () { const [rows] = yield connection.execute(query, params); return rows; })); }); } updateRecord(tableName, data, whereClause, params = []) { return __awaiter(this, void 0, void 0, function* () { const setClause = Object.keys(data) .map((key) => `${key} = ?`) .join(', '); const values = Object.values(data); const query = `UPDATE ${tableName} SET ${setClause} ${whereClause};`; yield this.executeQuery((connection) => __awaiter(this, void 0, void 0, function* () { yield connection.execute(query, [...values, ...params]); return; })); }); } deleteRecord(tableName, whereClause, params = []) { return __awaiter(this, void 0, void 0, function* () { const query = `DELETE FROM ${tableName} ${whereClause};`; yield this.executeQuery((connection) => __awaiter(this, void 0, void 0, function* () { yield connection.execute(query, params); return; })); }); } saveSyncedData(files, integrationId, crowdinId, type) { return __awaiter(this, void 0, void 0, function* () { yield this.dbPromise; yield this.executeQuery((connection) => connection.execute('INSERT INTO synced_data(files, integration_id, crowdin_id, type, updated_at) VALUES (?, ?, ?, ?, ?)', [files, integrationId, crowdinId, type, Date.now().toString()])); }); } updateSyncedData(files, integrationId, crowdinId, type) { return __awaiter(this, void 0, void 0, function* () { yield this.dbPromise; yield this.executeQuery((connection) => connection.execute('UPDATE synced_data SET files = ?, updated_at = ? WHERE integration_id = ? AND crowdin_id = ? AND type = ?', [files, Date.now().toString(), integrationId, crowdinId, type])); }); } getSyncedData(integrationId, crowdinId, type) { return __awaiter(this, void 0, void 0, function* () { yield this.dbPromise; return this.executeQuery((connection) => __awaiter(this, void 0, void 0, function* () { const [rows] = yield connection.execute('SELECT id, files, integration_id as "integrationId", crowdin_id as "crowdinId", type, updated_at as "updatedAt" FROM synced_data WHERE integration_id = ? AND crowdin_id = ? AND type = ?', [integrationId, crowdinId, type]); return (rows || [])[0]; })); }); } } exports.MySQLStorage = MySQLStorage;