@getanthill/datastore
Version:
Event-Sourced Datastore
130 lines • 5.42 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const pg_1 = require("pg");
const lodash_1 = require("lodash");
class PostgreSQLClient {
constructor(config) {
this._config = config;
this._client = new pg_1.Client(config.client);
}
static async init(config) {
const url = new URL(config.client.connectionString);
const dbName = url.pathname.slice(1);
const client = new pg_1.Client({
...config.client,
connectionString: config.client.connectionString.replace(`/${dbName}`, ''),
});
try {
await client.connect();
await client.query(`CREATE DATABASE ${dbName}`);
}
catch (err) {
// ...
}
finally {
await client.end();
}
}
connect() {
return this._client.connect();
}
disconnect() {
return this._client.end();
}
static getTableBaseName(modelConfig, namespace) {
const modelName = modelConfig.name;
return `${namespace ? namespace + '_' : ''}${modelName}`;
}
static getSqlSchemaForModelDeletion(modelConfig, namespace) {
const tableBaseName = PostgreSQLClient.getTableBaseName(modelConfig, namespace);
return [
`DROP TABLE IF EXISTS "${tableBaseName}";`,
`DROP TABLE IF EXISTS "${tableBaseName}_events";`,
`DROP TABLE IF EXISTS "${tableBaseName}_snapshots";`,
];
}
static getSqlSchemaForModel(modelConfig, namespace) {
const sql = [];
const correlationField = modelConfig.correlation_field;
const tableBaseName = PostgreSQLClient.getTableBaseName(modelConfig, namespace);
const modelDescription = modelConfig.description ?? '';
sql.push(`CREATE TABLE IF NOT EXISTS "${tableBaseName}" (
"${correlationField}" TEXT,
"created_at" TIMESTAMP WITH TIME ZONE NOT NULL,
"updated_at" TIMESTAMP WITH TIME ZONE NOT NULL,
"version" BIGINT,
"entity" JSON,
PRIMARY KEY ("${correlationField}")
);`);
sql.push(`CREATE TABLE IF NOT EXISTS "${tableBaseName}_events" (
"${correlationField}" TEXT,
"created_at" TIMESTAMP WITH TIME ZONE NOT NULL,
"version" BIGINT,
"type" VARCHAR(256),
"event" JSON,
PRIMARY KEY ("${correlationField}", "version")
);`);
sql.push(`CREATE TABLE IF NOT EXISTS "${tableBaseName}_snapshots" (
"${correlationField}" TEXT,
"created_at" TIMESTAMP WITH TIME ZONE NOT NULL,
"updated_at" TIMESTAMP WITH TIME ZONE NOT NULL,
"version" BIGINT,
"entity" JSON,
PRIMARY KEY ("${correlationField}")
);`);
sql.push(`COMMENT ON TABLE "${tableBaseName}" IS ${modelDescription ? "'[entities] " + modelDescription + "'" : 'NULL'}`);
sql.push(`COMMENT ON TABLE "${tableBaseName}_events" IS ${modelDescription ? "'[events] " + modelDescription + "'" : 'NULL'}`);
sql.push(`COMMENT ON TABLE "${tableBaseName}_snapshots" IS ${modelDescription ? "'[snapshots] " + modelDescription + "'" : 'NULL'}`);
return sql;
}
static getSqlSchemaForModels(modelConfigs, mustClean = false, namespace) {
const sql = [];
for (const modelConfig of modelConfigs) {
sql.push(...(mustClean === true
? PostgreSQLClient.getSqlSchemaForModelDeletion(modelConfig, namespace)
: []), ...PostgreSQLClient.getSqlSchemaForModel(modelConfig, namespace));
}
return sql;
}
query(q) {
return this._client.query(q);
}
async queryAll(queries) {
const results = [];
for (const q of queries) {
const result = await this.query(q);
results.push(result);
}
return results;
}
insert(modelConfig, source, data, options) {
let safeData = data;
if (options?.with_encrypted_data !== true) {
safeData = (0, lodash_1.cloneDeep)(data);
(modelConfig.encrypted_fields ?? []).forEach((path) => (0, lodash_1.unset)(safeData, `${path}.encrypted`));
}
const json = JSON.stringify(safeData).replace(/'/g, "''");
const tableBaseName = PostgreSQLClient.getTableBaseName(modelConfig, this._config.namespace);
const tableName = `${tableBaseName}${source === 'events' ? '_events' : ''}`;
const sql = `INSERT INTO "${tableName}" (
${modelConfig.correlation_field},
created_at,${source === 'entities' ? ' updated_at,' : ''}
version, ${source === 'events' ? ' type,' : ''}
${source === 'events' ? 'event' : 'entity'}
) VALUES (
'${safeData[modelConfig.correlation_field]}',
'${safeData.created_at}',${source === 'entities' ? "'" + safeData.updated_at + "'," : ''}
${safeData.version},${source === 'events' ? "'" + safeData.type + "'," : ''}
'${json}'
) ON CONFLICT ON CONSTRAINT ${tableName}_pkey DO UPDATE SET
"version" = ${safeData.version},
${source === 'entities'
? '"updated_at" = \'' + safeData.updated_at + "',"
: '"created_at" = \'' + safeData.created_at + "',"}
"${source === 'events' ? 'event' : 'entity'}" = '${json}'`;
return this.query(sql);
}
}
PostgreSQLClient.ERRORS = {};
exports.default = PostgreSQLClient;
//# sourceMappingURL=pg.js.map