@crowdin/app-project-module
Version:
Module that generates for you all common endpoints for serving standalone Crowdin App
965 lines (964 loc) • 49 kB
JavaScript
"use strict";
/* eslint-disable no-unused-expressions */
/* eslint-disable @typescript-eslint/camelcase */
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());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.PostgreStorage = void 0;
const pg_1 = require("pg");
const uuid_1 = require("uuid");
const types_1 = require("../types");
const types_2 = require("../modules/integration/util/types");
const util_1 = require("../util");
const fs_1 = __importDefault(require("fs"));
const path_1 = require("path");
class PostgreStorage {
constructor(config, directoryPath) {
this.dbPromise = new Promise((res, rej) => {
this._res = res;
this._rej = rej;
});
this.tables = {
crowdin_credentials: `(
id varchar primary key,
app_secret varchar,
domain varchar,
user_id varchar,
agent_id varchar,
organization_id varchar,
base_url varchar,
access_token varchar not null,
refresh_token varchar not null,
expire varchar not null,
type varchar not null
)`,
integration_credentials: `(
id varchar primary key,
credentials varchar,
crowdin_id varchar not null,
managers varchar
)`,
sync_settings: `(
id serial primary key,
files varchar,
integration_id varchar not null,
crowdin_id varchar not null,
type varchar not null,
provider varchar not null
)`,
app_metadata: `(
id varchar primary key,
data varchar,
crowdin_id varchar
)`,
files_snapshot: `(
id serial primary key,
integration_id varchar not null,
crowdin_id varchar not null,
files varchar,
provider varchar not null
)`,
webhooks: `(
id serial primary key,
file_id varchar not null,
integration_id varchar not null,
crowdin_id varchar not null,
provider varchar not null
)`,
user_errors: `(
id serial primary key,
action varchar not null,
message varchar not null,
data varchar,
created_at varchar not null,
crowdin_id varchar not null,
integration_id varchar
)`,
integration_settings: `(
id serial primary key,
integration_id varchar not null,
crowdin_id varchar not null,
config varchar
)`,
job: `(
id varchar not null primary key,
integration_id varchar not null,
crowdin_id varchar not null,
type varchar not null,
title varchar null,
progress int default 0,
status varchar default '${types_2.JobStatus.CREATED}',
payload varchar null,
info varchar null,
data varchar null,
attempt int default 0,
created_at varchar not null,
updated_at varchar null,
finished_at varchar null
)`,
translation_file_cache: `(
id serial primary key,
integration_id varchar not null,
crowdin_id varchar not null,
file_id int not null,
language_id varchar not null,
etag varchar
)`,
unsynced_files: `(
id serial primary key,
integration_id varchar not null,
crowdin_id varchar not null,
files varchar
)`,
synced_data: `(
id serial primary key,
files varchar,
integration_id varchar not null,
crowdin_id varchar not null,
type varchar not null,
updated_at varchar null
)`,
};
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) WHERE finished_at IS NULL',
idx_finished_at: '(finished_at) WHERE finished_at IS NOT NULL',
idx_status: '(status) WHERE finished_at IS NULL',
},
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.config = config;
this.directoryPath = directoryPath;
}
executeQuery(command) {
return __awaiter(this, void 0, void 0, function* () {
return (0, util_1.executeWithRetry)(() => __awaiter(this, void 0, void 0, function* () {
const client = new pg_1.Client(this.config);
try {
yield client.connect();
const res = yield command(client);
yield client.end();
return res;
}
catch (error) {
try {
yield client.end();
throw error;
}
catch (nestedError) {
throw nestedError;
}
}
}));
});
}
hasDumpFiles(directoryPath) {
if (!fs_1.default.existsSync(directoryPath)) {
return false;
}
const [name, extension] = types_1.storageFiles.DUMP.split('%s');
const files = fs_1.default.readdirSync(directoryPath).filter((file) => file.startsWith(name) && file.endsWith(extension));
return files.length > 0;
}
migrate() {
return __awaiter(this, void 0, void 0, function* () {
try {
if (this.directoryPath && this.hasDumpFiles(this.directoryPath)) {
yield this.migrateFromSqlite(this.directoryPath);
}
yield this.executeQuery((client) => this.addTables(client));
this._res && this._res();
// TODO: temporary code
yield this.executeQuery((client) => this.alterTables(client));
}
catch (e) {
console.error(e);
this._rej && this._rej();
}
});
}
alterTables(client) {
return __awaiter(this, void 0, void 0, function* () {
yield this.addColumns(client, ['crowdin_id'], 'app_metadata');
yield this.addColumns(client, ['agent_id'], 'crowdin_credentials');
yield this.addColumn(client, 'attempt', 'job', 'int default 0');
yield this.addColumn(client, 'managers', 'integration_credentials', 'varchar NULL');
});
}
addColumns(client, newColumns, tableName) {
return __awaiter(this, void 0, void 0, function* () {
const tableInfo = yield client.query('SELECT column_name FROM information_schema.columns WHERE table_name = $1', [tableName]);
tableInfo.rows.map((columnInfo) => __awaiter(this, void 0, void 0, function* () {
const index = newColumns.indexOf(columnInfo.column_name);
if (~index) {
newColumns.splice(index, 1);
}
}));
for (const column of newColumns) {
yield client.query(`ALTER TABLE ${tableName} ADD COLUMN ${column} varchar NULL;`);
}
});
}
addColumn(client, columnName, tableName, columnType) {
return __awaiter(this, void 0, void 0, function* () {
const tableInfo = yield client.query('SELECT column_name FROM information_schema.columns WHERE table_name = $1', [tableName]);
const exists = tableInfo.rows.some((columnInfo) => columnInfo.column_name === columnName);
if (!exists) {
yield client.query(`ALTER TABLE ${tableName} ADD COLUMN ${columnName} ${columnType};`);
}
});
}
addTables(client) {
return __awaiter(this, void 0, void 0, function* () {
for (const [tableName, tableSchema] of Object.entries(this.tables)) {
yield client.query(`create table if not exists ${tableName} ${tableSchema}`);
if (this.tableIndexes[tableName]) {
for (const [indexName, indexSchema] of Object.entries(this.tableIndexes[tableName])) {
yield client.query(`create index if not exists ${indexName} on ${tableName}${indexSchema}`);
}
}
}
});
}
saveCrowdinCredentials(credentials) {
return __awaiter(this, void 0, void 0, function* () {
yield this.dbPromise;
yield this.executeQuery((client) => client.query('INSERT INTO crowdin_credentials(id, app_secret, domain, user_id, agent_id, organization_id, base_url, access_token, refresh_token, expire, type) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)', [
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((client) => client.query('UPDATE crowdin_credentials SET app_secret = $1, domain = $2, user_id = $3, agent_id = $4, organization_id = $5, base_url = $6, access_token = $7, refresh_token = $8, expire = $9 WHERE id = $10', [
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((client) => __awaiter(this, void 0, void 0, function* () {
const res = yield client.query('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 = $1', [id]);
return res === null || res === void 0 ? void 0 : res.rows[0];
}));
});
}
getAllCrowdinCredentials() {
return __awaiter(this, void 0, void 0, function* () {
yield this.dbPromise;
return this.executeQuery((client) => __awaiter(this, void 0, void 0, function* () {
const res = yield client.query('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 (res === null || res === void 0 ? void 0 : res.rows) || [];
}));
});
}
deleteCrowdinCredentials(id) {
return __awaiter(this, void 0, void 0, function* () {
yield this.dbPromise;
yield this.executeQuery((client) => __awaiter(this, void 0, void 0, function* () {
yield client.query('DELETE FROM crowdin_credentials where id = $1', [id]);
yield client.query('DELETE FROM integration_credentials where crowdin_id = $1', [id]);
yield client.query('DELETE FROM sync_settings WHERE crowdin_id = $1', [id]);
yield client.query('DELETE FROM files_snapshot WHERE crowdin_id = $1', [id]);
yield client.query('DELETE FROM app_metadata WHERE crowdin_id = $1', [id]);
yield client.query('DELETE FROM webhooks WHERE crowdin_id = $1', [id]);
yield client.query('DELETE FROM user_errors WHERE crowdin_id = $1', [id]);
yield client.query('DELETE FROM integration_settings WHERE crowdin_id = $1', [id]);
yield client.query('DELETE FROM job WHERE crowdin_id = $1', [id]);
yield client.query('DELETE FROM translation_file_cache WHERE crowdin_id = $1', [id]);
yield client.query('DELETE FROM unsynced_files WHERE crowdin_id = $1', [id]);
}));
});
}
saveIntegrationCredentials(id, credentials, crowdinId) {
return __awaiter(this, void 0, void 0, function* () {
yield this.dbPromise;
yield this.executeQuery((client) => client.query('INSERT INTO integration_credentials(id, credentials, crowdin_id) VALUES ($1, $2, $3)', [
id,
credentials,
crowdinId,
]));
});
}
updateIntegrationCredentials(id, credentials) {
return __awaiter(this, void 0, void 0, function* () {
yield this.dbPromise;
yield this.executeQuery((client) => client.query('UPDATE integration_credentials SET credentials = $1 WHERE id = $2', [credentials, id]));
});
}
updateIntegrationManagers(id, managers) {
return __awaiter(this, void 0, void 0, function* () {
yield this.dbPromise;
yield this.executeQuery((client) => client.query('UPDATE integration_credentials SET managers = $1 WHERE id = $2', [managers, id]));
});
}
getIntegrationCredentials(id) {
return __awaiter(this, void 0, void 0, function* () {
yield this.dbPromise;
return this.executeQuery((client) => __awaiter(this, void 0, void 0, function* () {
const res = yield client.query('SELECT id, credentials, crowdin_id as "crowdinId", managers FROM integration_credentials WHERE id = $1', [id]);
return res === null || res === void 0 ? void 0 : res.rows[0];
}));
});
}
getAllIntegrationCredentials(crowdinId) {
return __awaiter(this, void 0, void 0, function* () {
yield this.dbPromise;
return this.executeQuery((client) => __awaiter(this, void 0, void 0, function* () {
const res = yield client.query('SELECT id, credentials, crowdin_id as "crowdinId", managers FROM integration_credentials WHERE crowdin_id = $1', [crowdinId]);
return (res === null || res === void 0 ? void 0 : res.rows) || [];
}));
});
}
deleteIntegrationCredentials(id) {
return __awaiter(this, void 0, void 0, function* () {
yield this.dbPromise;
yield this.executeQuery((client) => __awaiter(this, void 0, void 0, function* () {
yield client.query('DELETE FROM integration_credentials where id = $1', [id]);
yield client.query('DELETE FROM sync_settings where integration_id = $1', [id]);
yield client.query('DELETE FROM files_snapshot where integration_id = $1', [id]);
yield client.query('DELETE FROM webhooks where integration_id = $1', [id]);
yield client.query('DELETE FROM job where integration_id = $1', [id]);
yield client.query('DELETE FROM unsynced_files where integration_id = $1', [id]);
}));
});
}
deleteAllIntegrationCredentials(crowdinId) {
return __awaiter(this, void 0, void 0, function* () {
yield this.dbPromise;
yield this.executeQuery((client) => __awaiter(this, void 0, void 0, function* () {
yield client.query('DELETE FROM integration_credentials where crowdin_id = $1', [crowdinId]);
yield client.query('DELETE FROM sync_settings where crowdin_id = $1', [crowdinId]);
yield client.query('DELETE FROM files_snapshot where crowdin_id = $1', [crowdinId]);
yield client.query('DELETE FROM webhooks where crowdin_id = $1', [crowdinId]);
yield client.query('DELETE FROM user_errors where crowdin_id = $1', [crowdinId]);
yield client.query('DELETE FROM job where crowdin_id = $1', [crowdinId]);
yield client.query('DELETE FROM unsynced_files where crowdin_id = $1', [crowdinId]);
}));
});
}
saveMetadata(id, metadata, crowdinId) {
return __awaiter(this, void 0, void 0, function* () {
yield this.dbPromise;
yield this.executeQuery((client) => client.query('INSERT INTO app_metadata(id, data, crowdin_id) VALUES ($1, $2, $3)', [
id,
JSON.stringify(metadata),
crowdinId,
]));
});
}
updateMetadata(id, metadata, crowdinId) {
return __awaiter(this, void 0, void 0, function* () {
yield this.dbPromise;
yield this.executeQuery((client) => client.query('UPDATE app_metadata SET data = $1, crowdin_id = $2 WHERE id = $3', [
JSON.stringify(metadata),
crowdinId,
id,
]));
});
}
getMetadata(id) {
return __awaiter(this, void 0, void 0, function* () {
yield this.dbPromise;
return this.executeQuery((client) => __awaiter(this, void 0, void 0, function* () {
const res = yield client.query('SELECT data FROM app_metadata WHERE id = $1', [id]);
if (res === null || res === void 0 ? void 0 : res.rows[0]) {
return JSON.parse(res === null || res === void 0 ? void 0 : res.rows[0].data);
}
}));
});
}
getAllMetadata() {
return __awaiter(this, void 0, void 0, function* () {
yield this.dbPromise;
return this.executeQuery((client) => __awaiter(this, void 0, void 0, function* () {
const res = yield client.query('SELECT * FROM app_metadata');
return (res === null || res === void 0 ? void 0 : res.rows) || [];
}));
});
}
deleteMetadata(id) {
return __awaiter(this, void 0, void 0, function* () {
yield this.dbPromise;
yield this.executeQuery((client) => client.query('DELETE FROM app_metadata where id = $1', [id]));
});
}
getSyncSettingsByProvider(integrationId, provider) {
return __awaiter(this, void 0, void 0, function* () {
yield this.dbPromise;
return this.executeQuery((client) => __awaiter(this, void 0, void 0, function* () {
const res = yield client.query('SELECT id, files, integration_id as "integrationId", crowdin_id as "crowdinId", type, provider FROM sync_settings WHERE integration_id = $1 AND provider = $2', [integrationId, provider]);
return res === null || res === void 0 ? void 0 : res.rows[0];
}));
});
}
getSyncSettingsBySchedule(type, schedule) {
return __awaiter(this, void 0, void 0, function* () {
yield this.dbPromise;
return this.executeQuery((client) => __awaiter(this, void 0, void 0, function* () {
const res = yield client.query(`
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 = $1
AND CASE
WHEN i.config IS NULL THEN false
ELSE (i.config::json->>'schedule') = $2
END
`, [type, schedule]);
return (res === null || res === void 0 ? void 0 : res.rows) || [];
}));
});
}
saveSyncSettings(files, integrationId, crowdinId, type, provider) {
return __awaiter(this, void 0, void 0, function* () {
yield this.dbPromise;
yield this.executeQuery((client) => client.query('INSERT INTO sync_settings(files, integration_id, crowdin_id, type, provider) VALUES ($1, $2, $3, $4, $5)', [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((client) => client.query('UPDATE sync_settings SET files = $1 WHERE integration_id = $2 AND crowdin_id = $3 AND type = $4 AND provider = $5', [files, integrationId, crowdinId, type, provider]));
});
}
getSyncSettings(integrationId, crowdinId, type, provider) {
return __awaiter(this, void 0, void 0, function* () {
yield this.dbPromise;
return this.executeQuery((client) => __awaiter(this, void 0, void 0, function* () {
const res = yield client.query('SELECT id, files, integration_id as "integrationId", crowdin_id as "crowdinId", type, provider FROM sync_settings WHERE integration_id = $1 AND crowdin_id = $2 AND type = $3 AND provider = $4', [integrationId, crowdinId, type, provider]);
return res === null || res === void 0 ? void 0 : res.rows[0];
}));
});
}
saveFilesSnapshot(files, integrationId, crowdinId, provider) {
return __awaiter(this, void 0, void 0, function* () {
yield this.dbPromise;
yield this.executeQuery((client) => client.query('INSERT INTO files_snapshot(integration_id, crowdin_id, files, provider) VALUES ($1, $2, $3, $4)', [files, integrationId, crowdinId, provider]));
});
}
updateFilesSnapshot(files, integrationId, crowdinId, provider) {
return __awaiter(this, void 0, void 0, function* () {
yield this.dbPromise;
yield this.executeQuery((client) => client.query('UPDATE files_snapshot SET files = $1 WHERE integration_id = $2 AND crowdin_id = $3 AND provider = $4', [files, integrationId, crowdinId, provider]));
});
}
getFilesSnapshot(integrationId, crowdinId, provider) {
return __awaiter(this, void 0, void 0, function* () {
yield this.dbPromise;
return this.executeQuery((client) => __awaiter(this, void 0, void 0, function* () {
const res = yield client.query('SELECT id, files, integration_id as "integrationId", crowdin_id as "crowdinId" FROM files_snapshot WHERE integration_id = $1 AND crowdin_id = $2 AND provider = $3', [integrationId, crowdinId, provider]);
return res === null || res === void 0 ? void 0 : res.rows[0];
}));
});
}
getAllWebhooks(integrationId, crowdinId, provider) {
return __awaiter(this, void 0, void 0, function* () {
yield this.dbPromise;
return this.executeQuery((client) => __awaiter(this, void 0, void 0, function* () {
const res = yield client.query('SELECT id, file_id as "fileId", integration_id as "integrationId", crowdin_id as "crowdinId" FROM webhooks WHERE integration_id = $1 AND crowdin_id = $2 AND provider = $3', [integrationId, crowdinId, provider]);
return (res === null || res === void 0 ? void 0 : res.rows) || [];
}));
});
}
getWebhooks(fileId, integrationId, crowdinId, provider) {
return __awaiter(this, void 0, void 0, function* () {
yield this.dbPromise;
return this.executeQuery((client) => __awaiter(this, void 0, void 0, function* () {
const res = yield client.query('SELECT id, file_id as "fileId", integration_id as "integrationId", crowdin_id as "crowdinId" FROM webhooks WHERE file_id = $1 AND integration_id = $2 AND crowdin_id = $3 AND provider = $4', [fileId, integrationId, crowdinId, provider]);
return res === null || res === void 0 ? void 0 : res.rows[0];
}));
});
}
saveWebhooks(fileId, integrationId, crowdinId, provider) {
return __awaiter(this, void 0, void 0, function* () {
yield this.dbPromise;
yield this.executeQuery((client) => client.query('INSERT INTO webhooks(file_id, integration_id, crowdin_id, provider) VALUES ($1, $2, $3, $4)', [fileId, integrationId, crowdinId, provider]));
});
}
deleteWebhooks(fileIds, integrationId, crowdinId, provider) {
return __awaiter(this, void 0, void 0, function* () {
if (!fileIds.length) {
return;
}
let index = 0;
const placeholders = fileIds.map(() => `$${++index}`).join(',');
yield this.dbPromise;
yield this.executeQuery((client) => client.query(`DELETE FROM webhooks WHERE file_id IN (${placeholders}) AND integration_id = $${++index} AND crowdin_id = $${++index} AND provider = $${++index}`, [...fileIds, integrationId, crowdinId, provider]));
});
}
getAllUserErrors(crowdinId, integrationId) {
return __awaiter(this, void 0, void 0, function* () {
yield this.dbPromise;
return this.executeQuery((client) => __awaiter(this, void 0, void 0, function* () {
let whereIntegrationCondition = 'integration_id is NULL';
const params = [crowdinId];
if (integrationId) {
whereIntegrationCondition = 'integration_id = $2';
params.push(integrationId);
}
const res = yield client.query(`SELECT id, action, message, data, created_at as "createdAt", integration_id as "integrationId", crowdin_id as "crowdinId" FROM user_errors WHERE crowdin_id = $1 AND ${whereIntegrationCondition}`, params);
return (res === null || res === void 0 ? void 0 : res.rows) || [];
}));
});
}
saveUserError(action, message, data, createdAt, crowdinId, integrationId) {
return __awaiter(this, void 0, void 0, function* () {
yield this.dbPromise;
yield this.executeQuery((client) => client.query('INSERT INTO user_errors(action, message, data, created_at, integration_id, crowdin_id) VALUES ($1, $2, $3, $4, $5, $6)', [action, message, data, createdAt, integrationId, crowdinId]));
});
}
deleteUserErrors(createdAt, crowdinId, integrationId) {
return __awaiter(this, void 0, void 0, function* () {
yield this.dbPromise;
yield this.executeQuery((client) => {
let whereIntegrationCondition = 'integration_id is NULL';
const params = [createdAt, crowdinId];
if (integrationId) {
whereIntegrationCondition = 'integration_id = $3';
params.push(integrationId);
}
return client.query(`DELETE FROM user_errors WHERE created_at < $1 AND crowdin_id = $2 AND ${whereIntegrationCondition}`, params);
});
});
}
deleteAllUsersErrorsOlderThan(createdAt) {
return __awaiter(this, void 0, void 0, function* () {
yield this.dbPromise;
yield this.executeQuery((client) => {
return client.query('DELETE FROM user_errors WHERE created_at < $1', [createdAt]);
});
});
}
saveIntegrationConfig(integrationId, crowdinId, config) {
return __awaiter(this, void 0, void 0, function* () {
yield this.dbPromise;
yield this.executeQuery((client) => client.query('INSERT INTO integration_settings(integration_id, crowdin_id, config) VALUES ($1, $2, $3)', [
integrationId,
crowdinId,
config,
]));
});
}
getAllIntegrationConfigs(crowdinId) {
return __awaiter(this, void 0, void 0, function* () {
yield this.dbPromise;
return this.executeQuery((client) => __awaiter(this, void 0, void 0, function* () {
const res = yield client.query('SELECT config FROM integration_settings WHERE crowdin_id = $1', [crowdinId]);
return (res === null || res === void 0 ? void 0 : res.rows) || [];
}));
});
}
getIntegrationConfig(integrationId) {
return __awaiter(this, void 0, void 0, function* () {
yield this.dbPromise;
return this.executeQuery((client) => __awaiter(this, void 0, void 0, function* () {
const res = yield client.query('SELECT config FROM integration_settings WHERE integration_id = $1', [integrationId]);
return res === null || res === void 0 ? void 0 : res.rows[0];
}));
});
}
updateIntegrationConfig(integrationId, config) {
return __awaiter(this, void 0, void 0, function* () {
yield this.dbPromise;
yield this.executeQuery((client) => client.query('UPDATE integration_settings SET config = $1 WHERE integration_id = $2', [
config,
integrationId,
]));
});
}
createJob({ integrationId, crowdinId, type, payload, title }) {
return __awaiter(this, void 0, void 0, function* () {
const id = (0, uuid_1.v4)();
yield this.dbPromise;
yield this.executeQuery((client) => client.query(`
INSERT
INTO job(id, integration_id, crowdin_id, type, payload, title, created_at)
VALUES ($1, $2, $3, $4, $5, $6, $7)
`, [id, integrationId, crowdinId, type, payload, title, Date.now().toString()]));
return id;
});
}
updateJob({ id, progress, status, info, data, attempt }) {
return __awaiter(this, void 0, void 0, function* () {
let parametersPointer = 1;
const updateFields = [`updated_at = $${parametersPointer}`];
const updateParams = [Date.now().toString()];
if (progress) {
parametersPointer++;
updateFields.push('progress = $' + parametersPointer);
updateParams.push(Math.round(progress));
if (progress >= 100) {
parametersPointer++;
updateFields.push('finished_at = $' + parametersPointer);
updateParams.push(Date.now().toString());
}
}
if (status) {
parametersPointer++;
updateFields.push('status = $' + parametersPointer);
updateParams.push(status);
if ((!progress || progress <= 100) && [types_2.JobStatus.FAILED, types_2.JobStatus.CANCELED].includes(status)) {
parametersPointer++;
updateFields.push('finished_at = $' + parametersPointer);
updateParams.push(Date.now().toString());
}
}
if (data) {
parametersPointer++;
updateFields.push('data = $' + parametersPointer);
updateParams.push(data);
}
if (info) {
parametersPointer++;
updateFields.push('info = $' + parametersPointer);
updateParams.push(info);
}
if (attempt) {
parametersPointer++;
updateFields.push('attempt = $' + parametersPointer);
updateParams.push(attempt);
}
parametersPointer++;
updateParams.push(id);
yield this.dbPromise;
yield this.executeQuery((client) => client.query(`
UPDATE job
SET ${updateFields.join(', ')}
WHERE id = $${parametersPointer}
`, updateParams));
});
}
getJob({ id }) {
return __awaiter(this, void 0, void 0, function* () {
yield this.dbPromise;
return this.executeQuery((client) => __awaiter(this, void 0, void 0, function* () {
const res = yield client.query(`
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 id = $1
`, [id]);
return res === null || res === void 0 ? void 0 : res.rows[0];
}));
});
}
getActiveJobs({ integrationId, crowdinId }) {
return __awaiter(this, void 0, void 0, function* () {
yield this.dbPromise;
return this.executeQuery((client) => __awaiter(this, void 0, void 0, function* () {
const res = yield client.query(`
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 = $1 AND crowdin_id = $2 AND finished_at is NULL
`, [integrationId, crowdinId]);
return (res === null || res === void 0 ? void 0 : res.rows) || [];
}));
});
}
deleteFinishedJobs() {
return __awaiter(this, void 0, void 0, function* () {
yield this.dbPromise;
yield this.executeQuery((client) => client.query(' DELETE FROM job WHERE finished_at is not NULL', []));
});
}
getAllInProgressJobs() {
return __awaiter(this, void 0, void 0, function* () {
yield this.dbPromise;
return this.executeQuery((client) => __awaiter(this, void 0, void 0, function* () {
const res = yield client.query(`
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 status IN ($1, $2) AND finished_at is NULL
`, [types_2.JobStatus.IN_PROGRESS, types_2.JobStatus.CREATED]);
return (res === null || res === void 0 ? void 0 : res.rows) || [];
}));
});
}
saveTranslationCache({ integrationId, crowdinId, fileId, languageId, etag, }) {
return __awaiter(this, void 0, void 0, function* () {
yield this.dbPromise;
yield this.executeQuery((client) => client.query(`
INSERT
INTO translation_file_cache(integration_id, crowdin_id, file_id, language_id, etag)
VALUES ($1, $2, $3, $4, $5)
`, [integrationId, crowdinId, fileId, languageId, etag]));
});
}
getFileTranslationCache({ integrationId, crowdinId, fileId, }) {
return __awaiter(this, void 0, void 0, function* () {
yield this.dbPromise;
return this.executeQuery((client) => __awaiter(this, void 0, void 0, function* () {
const res = yield client.query(`
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 = $1 AND crowdin_id = $2 AND file_id = $3
`, [integrationId, crowdinId, fileId]);
return (res === null || res === void 0 ? void 0 : res.rows) || [];
}));
});
}
getFileTranslationCacheByLanguage({ integrationId, crowdinId, fileId, languageId, }) {
return __awaiter(this, void 0, void 0, function* () {
yield this.dbPromise;
return this.executeQuery((client) => __awaiter(this, void 0, void 0, function* () {
const res = yield client.query(`
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 = $1 AND crowdin_id = $2 AND file_id = $3 AND language_id = $4
`, [integrationId, crowdinId, fileId, languageId]);
return res === null || res === void 0 ? void 0 : res.rows[0];
}));
});
}
updateTranslationCache({ integrationId, crowdinId, fileId, languageId, etag, }) {
return __awaiter(this, void 0, void 0, function* () {
yield this.dbPromise;
yield this.executeQuery((client) => client.query(`
UPDATE translation_file_cache
SET etag = $1
WHERE integration_id = $2 AND crowdin_id = $3 AND file_id = $4 AND language_id = $5
`, [etag, integrationId, crowdinId, fileId, languageId]));
});
}
migrateFromSqlite(directoryPath) {
return __awaiter(this, void 0, void 0, function* () {
const [name, extension] = types_1.storageFiles.DUMP.split('%s');
const files = fs_1.default
.readdirSync(directoryPath)
.filter((file) => file.startsWith(name) && file.endsWith(extension))
.sort((a, b) => a.localeCompare(b, undefined, { numeric: true }));
for (const file of files) {
const filePath = (0, path_1.join)(directoryPath, file);
const sql = fs_1.default.readFileSync(filePath, 'utf8');
try {
yield this.executeQuery((client) => client.query(sql));
fs_1.default.unlinkSync(filePath);
}
catch (e) {
console.error('Error while executing', file);
console.error(e);
fs_1.default.renameSync(filePath, filePath.replace('dump_table_', 'error_dump_table_'));
}
}
// Reset sequences for tables with serial primary keys
yield this.resetSequences();
});
}
saveUnsyncedFiles({ integrationId, crowdinId, files }) {
return __awaiter(this, void 0, void 0, function* () {
yield this.dbPromise;
yield this.executeQuery((client) => client.query(`
INSERT
INTO unsynced_files(integration_id, crowdin_id, files)
VALUES ($1, $2, $3,)
`, [integrationId, crowdinId, files]));
});
}
updateUnsyncedFiles({ integrationId, crowdinId, files }) {
return __awaiter(this, void 0, void 0, function* () {
yield this.dbPromise;
yield this.executeQuery((client) => client.query(`
UPDATE unsynced_files
SET files = $1
WHERE integration_id = $2 AND crowdin_id = $3
`, [files, integrationId, crowdinId]));
});
}
getUnsyncedFiles({ integrationId, crowdinId }) {
return __awaiter(this, void 0, void 0, function* () {
yield this.dbPromise;
return this.executeQuery((client) => __awaiter(this, void 0, void 0, function* () {
const res = yield client.query(`
SELECT files
FROM unsynced_files
WHERE integration_id = $1 AND crowdin_id = $2
`, [integrationId, crowdinId]);
return res === null || res === void 0 ? void 0 : res.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((client) => __awaiter(this, void 0, void 0, function* () {
yield client.query(query);
return;
}));
});
}
insertRecord(tableName, data) {
return __awaiter(this, void 0, void 0, function* () {
const columns = Object.keys(data)
.map((col) => `"${col}"`)
.join(', ');
const placeholders = Object.keys(data)
.map((_, i) => `$${i + 1}`)
.join(', ');
const values = Object.values(data);
const query = `INSERT INTO "${tableName}" (${columns}) VALUES (${placeholders});`;
yield this.executeQuery((client) => __awaiter(this, void 0, void 0, function* () {
yield client.query(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}` : '';
const limitClause = options.limit ? ` LIMIT ${options.limit}` : '';
const query = `SELECT ${distinctKeyword}${columns} FROM ${tableName}${whereClause}${orderByClause}${limitClause};`;
return this.executeQuery((client) => __awaiter(this, void 0, void 0, function* () {
const res = yield client.query(query, params);
return res.rows;
}));
});
}
updateRecord(tableName, data, whereClause, params = []) {
return __awaiter(this, void 0, void 0, function* () {
const keys = Object.keys(data);
const setClause = keys.map((key, index) => `"${key}" = $${index + 1}`).join(', ');
const values = Object.values(data);
const query = `UPDATE "${tableName}" SET ${setClause} ${whereClause};`;
yield this.executeQuery((client) => __awaiter(this, void 0, void 0, function* () {
yield client.query(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((client) => __awaiter(this, void 0, void 0, function* () {
yield client.query(query, params);
return;
}));
});
}
saveSyncedData(files, integrationId, crowdinId, type) {
return __awaiter(this, void 0, void 0, function* () {
yield this.dbPromise;
yield this.executeQuery((client) => client.query('INSERT INTO synced_data(files, integration_id, crowdin_id, type, updated_at) VALUES ($1, $2, $3, $4, $5)', [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((client) => client.query('UPDATE synced_data SET files = $1, updated_at = $2 WHERE integration_id = $3 AND crowdin_id = $4 AND type = $5', [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((client) => __awaiter(this, void 0, void 0, function* () {
const res = yield client.query('SELECT id, files, integration_id as "integrationId", crowdin_id as "crowdinId", type, updated_at as "updatedAt" FROM synced_data WHERE integration_id = $1 AND crowdin_id = $2 AND type = $3', [integrationId, crowdinId, type]);
return res === null || res === void 0 ? void 0 : res.rows[0];
}));
});
}
resetSequences() {
return __awaiter(this, void 0, void 0, function* () {
yield this.executeQuery((client) => __awaiter(this, void 0, void 0, function* () {
const tables = Object.keys(this.tables);
for (const table of tables) {
try {
const primaryKeyResult = yield client.query(`
SELECT a.attname
FROM pg_index i
JOIN pg_attribute a ON a.attrelid = i.indrelid AND a.attnum = ANY(i.indkey)
WHERE i.indrelid = '${table}'::regclass
AND i.indisprimary;
`);
if (primaryKeyResult.rows.length === 0) {
continue;
}
const primaryKey = primaryKeyResult.rows[0].attname;
const maxIdResult = yield client.query(`SELECT MAX(${primaryKey}) FROM ${table}`);
const maxId = maxIdResult.rows[0].max;
// Skip if maxId is not a valid integer
if (maxId === null || isNaN(Number(maxId))) {
continue;
}
yield client.query(`SELECT setval('${table}_${primaryKey}_seq', ${Number(maxId)}, true)`);
}
catch (error) {
console.error(`Error resetting sequence for table ${table}:`, error);
}
}
}));
});
}
}
exports.PostgreStorage = PostgreStorage;