n8n
Version:
n8n Workflow Automation Tool
314 lines • 14 kB
JavaScript
;
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.DataTableRepository = void 0;
const api_types_1 = require("@n8n/api-types");
const backend_common_1 = require("@n8n/backend-common");
const config_1 = require("@n8n/config");
const db_1 = require("@n8n/db");
const di_1 = require("@n8n/di");
const typeorm_1 = require("@n8n/typeorm");
const n8n_workflow_1 = require("n8n-workflow");
const data_table_column_entity_1 = require("./data-table-column.entity");
const data_table_ddl_service_1 = require("./data-table-ddl.service");
const data_table_entity_1 = require("./data-table.entity");
const data_table_name_conflict_error_1 = require("./errors/data-table-name-conflict.error");
const data_table_validation_error_1 = require("./errors/data-table-validation.error");
const sql_utils_1 = require("./utils/sql-utils");
let DataTableRepository = class DataTableRepository extends typeorm_1.Repository {
constructor(dataSource, ddlService, globalConfig, logger) {
super(data_table_entity_1.DataTable, dataSource.manager);
this.ddlService = ddlService;
this.globalConfig = globalConfig;
this.logger = logger;
this.parseSize = (bytes) => bytes === null ? 0 : typeof bytes === 'string' ? parseInt(bytes, 10) : bytes;
}
async touchUpdatedAt(dataTableId, trx) {
await (0, db_1.withTransaction)(this.manager, trx, async (em) => {
await em.update(data_table_entity_1.DataTable, { id: dataTableId }, { updatedAt: new Date() });
}).catch((error) => {
this.logger.warn('Failed to update DataTable timestamp', { dataTableId, error });
});
}
async createDataTable(projectId, name, columns, trx) {
return await (0, db_1.withTransaction)(this.manager, trx, async (em) => {
if (columns.some((c) => !(0, sql_utils_1.isValidColumnName)(c.name))) {
throw new data_table_validation_error_1.DataTableValidationError(api_types_1.DATA_TABLE_COLUMN_ERROR_MESSAGE);
}
const dataTable = em.create(data_table_entity_1.DataTable, { name, columns, projectId });
await em.insert(data_table_entity_1.DataTable, dataTable);
const dataTableId = dataTable.id;
const columnEntities = columns.map((col, index) => em.create(data_table_column_entity_1.DataTableColumn, {
dataTableId,
name: col.name,
type: col.type,
index: col.index ?? index,
}));
if (columnEntities.length > 0) {
await em.insert(data_table_column_entity_1.DataTableColumn, columnEntities);
}
await this.ddlService.createTableWithColumns(dataTableId, columnEntities, em);
if (!dataTableId) {
throw new n8n_workflow_1.UnexpectedError('Data table creation failed');
}
const createdDataTable = await em.findOneOrFail(data_table_entity_1.DataTable, {
where: { id: dataTableId },
relations: ['project', 'columns'],
});
return createdDataTable;
});
}
async deleteDataTable(dataTableId, trx) {
return await (0, db_1.withTransaction)(this.manager, trx, async (em) => {
await em.delete(data_table_entity_1.DataTable, { id: dataTableId });
await this.ddlService.dropTable(dataTableId, em);
return true;
});
}
async transferDataTableByProjectId(fromProjectId, toProjectId, trx) {
if (fromProjectId === toProjectId)
return false;
return await (0, db_1.withTransaction)(this.manager, trx, async (em) => {
const existingTables = await em.findBy(data_table_entity_1.DataTable, { projectId: fromProjectId });
let transferred = false;
for (const existing of existingTables) {
let name = existing.name;
const hasNameClash = await em.existsBy(data_table_entity_1.DataTable, {
name,
projectId: toProjectId,
});
if (hasNameClash) {
const project = await em.findOneByOrFail(db_1.Project, { id: fromProjectId });
name = `${existing.name} (${project.name})`;
const stillHasNameClash = await em.existsBy(data_table_entity_1.DataTable, {
name,
projectId: toProjectId,
});
if (stillHasNameClash) {
throw new data_table_name_conflict_error_1.DataTableNameConflictError(`Failed to transfer data table "${existing.name}" to the target project "${toProjectId}". A data table with the same name already exists in the target project.`);
}
}
await em.update(data_table_entity_1.DataTable, { id: existing.id }, { name, projectId: toProjectId });
transferred = true;
}
return transferred;
});
}
async deleteDataTableByProjectId(projectId, trx) {
return await (0, db_1.withTransaction)(this.manager, trx, async (em) => {
const existingTables = await em.findBy(data_table_entity_1.DataTable, { projectId });
let changed = false;
for (const match of existingTables) {
const result = await this.deleteDataTable(match.id, em);
changed = changed || result;
}
return changed;
});
}
async deleteDataTableAll(trx) {
return await (0, db_1.withTransaction)(this.manager, trx, async (em) => {
const existingTables = await em.findBy(data_table_entity_1.DataTable, {});
let changed = false;
for (const match of existingTables) {
const result = await em.delete(data_table_entity_1.DataTable, { id: match.id });
await this.ddlService.dropTable(match.id, em);
changed = changed || (result.affected ?? 0) > 0;
}
return changed;
});
}
async getManyAndCount(options) {
const query = this.getManyQuery(options);
const [data, count] = await query.getManyAndCount();
return { count, data };
}
async getMany(options) {
const query = this.getManyQuery(options);
return await query.getMany();
}
getManyQuery(options) {
const query = this.createQueryBuilder('dataTable');
this.applySelections(query);
this.applyFilters(query, options.filter);
this.applySorting(query, options.sortBy);
this.applyPagination(query, options);
return query;
}
applySelections(query) {
this.applyDefaultSelect(query);
}
applyFilters(query, filter) {
for (const x of ['id', 'projectId']) {
const content = [filter?.[x]].flat().filter((x) => x !== undefined);
if (content.length === 0)
continue;
query.andWhere(`dataTable.${x} IN (:...${x}s)`, {
[x + 's']: content.length > 0 ? content : [''],
});
}
if (filter?.name) {
const nameFilters = Array.isArray(filter.name) ? filter.name : [filter.name];
nameFilters.forEach((name, i) => {
query.andWhere(`LOWER(dataTable.name) LIKE LOWER(:name${i})`, {
['name' + i]: `%${name}%`,
});
});
}
}
applySorting(query, sortBy) {
if (!sortBy) {
query.orderBy('dataTable.updatedAt', 'DESC');
return;
}
const [field, order] = this.parseSortingParams(sortBy);
this.applySortingByField(query, field, order);
}
parseSortingParams(sortBy) {
const [field, order] = sortBy.split(':');
return [field, order?.toLowerCase() === 'desc' ? 'DESC' : 'ASC'];
}
applySortingByField(query, field, direction) {
if (field === 'name') {
query
.addSelect('LOWER(dataTable.name)', 'datatable_name_lower')
.orderBy('datatable_name_lower', direction);
}
else if (['createdAt', 'updatedAt'].includes(field)) {
query.orderBy(`dataTable.${field}`, direction);
}
}
applyPagination(query, options) {
query.skip(options.skip ?? 0);
if (options.take !== undefined)
query.take(options.take);
}
applyDefaultSelect(query) {
query
.leftJoinAndSelect('dataTable.project', 'project')
.leftJoinAndSelect('dataTable.columns', 'data_table_column')
.select([
'dataTable',
...this.getDataTableColumnFields('data_table_column'),
...this.getProjectFields('project'),
]);
}
getDataTableColumnFields(alias) {
return [
`${alias}.id`,
`${alias}.name`,
`${alias}.type`,
`${alias}.createdAt`,
`${alias}.updatedAt`,
`${alias}.index`,
];
}
getProjectFields(alias) {
return [`${alias}.id`, `${alias}.name`, `${alias}.type`, `${alias}.icon`];
}
async findDataTablesSize() {
const sizeMap = await this.getAllDataTablesSizeMap();
const totalBytes = Array.from(sizeMap.values()).reduce((sum, size) => sum + size, 0);
const query = this.createQueryBuilder('dt')
.leftJoinAndSelect('dt.project', 'p')
.select(['dt.id', 'dt.name', 'p.id', 'p.name']);
const dataTablesWithProjects = await query.getMany();
const dataTables = {};
for (const dt of dataTablesWithProjects) {
const sizeBytes = sizeMap.get(dt.id) ?? 0;
dataTables[dt.id] = {
id: dt.id,
name: dt.name,
projectId: dt.project.id,
projectName: dt.project.name,
sizeBytes,
};
}
return {
totalBytes,
dataTables,
};
}
async getAllDataTablesSizeMap() {
const dbType = this.globalConfig.database.type;
const tablePattern = (0, sql_utils_1.toTableName)('%');
let sql = '';
switch (dbType) {
case 'sqlite':
sql = `
WITH data_table_names(name) AS (
SELECT name
FROM sqlite_schema
WHERE type = 'table' AND name GLOB '${(0, sql_utils_1.toTableName)('*')}'
)
SELECT t.name AS table_name, (SELECT SUM(pgsize) FROM dbstat WHERE name = t.name) AS table_bytes
FROM data_table_names AS t
`;
break;
case 'postgresdb': {
const schemaName = this.globalConfig.database.postgresdb?.schema;
sql = `
SELECT c.relname AS table_name, pg_relation_size(c.oid) AS table_bytes
FROM pg_class c
JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE n.nspname = '${schemaName}'
AND c.relname LIKE '${tablePattern}'
AND c.relkind IN ('r', 'm', 'p')
`;
break;
}
case 'mysqldb':
case 'mariadb': {
const databaseName = this.globalConfig.database.mysqldb.database;
const isMariaDb = dbType === 'mariadb';
const innodbTables = isMariaDb ? 'INNODB_SYS_TABLES' : 'INNODB_TABLES';
const innodbTablespaces = isMariaDb ? 'INNODB_SYS_TABLESPACES' : 'INNODB_TABLESPACES';
sql = `
SELECT t.TABLE_NAME AS table_name,
COALESCE(
(
SELECT SUM(ists.ALLOCATED_SIZE)
FROM information_schema.${innodbTables} ist
JOIN information_schema.${innodbTablespaces} ists
ON ists.SPACE = ist.SPACE
WHERE ist.NAME = CONCAT(t.TABLE_SCHEMA, '/', t.TABLE_NAME)
),
(t.DATA_LENGTH + t.INDEX_LENGTH)
) AS table_bytes
FROM information_schema.TABLES t
WHERE t.TABLE_SCHEMA = '${databaseName}'
AND t.TABLE_NAME LIKE '${tablePattern}'
`;
break;
}
default:
return new Map();
}
const result = (await this.query(sql));
const sizeMap = new Map();
for (const row of result) {
if (row.table_bytes !== null && row.table_name) {
const dataTableId = (0, sql_utils_1.toTableId)(row.table_name);
const sizeBytes = this.parseSize(row.table_bytes);
sizeMap.set(dataTableId, (sizeMap.get(dataTableId) ?? 0) + sizeBytes);
}
}
return sizeMap;
}
};
exports.DataTableRepository = DataTableRepository;
exports.DataTableRepository = DataTableRepository = __decorate([
(0, di_1.Service)(),
__metadata("design:paramtypes", [typeorm_1.DataSource,
data_table_ddl_service_1.DataTableDDLService,
config_1.GlobalConfig,
backend_common_1.Logger])
], DataTableRepository);
//# sourceMappingURL=data-table.repository.js.map