wip commit

This commit is contained in:
Charlie Kolb 2025-07-31 11:15:58 +02:00
parent 01900d57f3
commit 331fc9b1bd
No known key found for this signature in database
9 changed files with 79 additions and 50 deletions

View File

@ -2,6 +2,4 @@ import { Z } from 'zod-class';
import { dataStoreCreateColumnSchema } from '../../schemas/data-store.schema';
export class AddDataStoreColumnDto extends Z.class({
column: dataStoreCreateColumnSchema,
}) {}
export class AddDataStoreColumnDto extends Z.class(dataStoreCreateColumnSchema.shape) {}

View File

@ -2,7 +2,7 @@ import { z } from 'zod';
import { Z } from 'zod-class';
import { dataStoreColumnNameSchema } from '../../schemas/data-store.schema';
import { paginationSchema } from 'dto/pagination/pagination.dto';
import { paginationSchema } from '../pagination/pagination.dto';
const FilterConditionSchema = z.union([z.literal('eq'), z.literal('neq')]);
export type ListDataStoreContentFilterConditionType = z.infer<typeof FilterConditionSchema>;

View File

@ -1,8 +1,9 @@
import { paginationSchema } from 'dto/pagination/pagination.dto';
import { jsonParse } from 'n8n-workflow';
import { z } from 'zod';
import { Z } from 'zod-class';
import { paginationSchema } from '../pagination/pagination.dto';
const VALID_SORT_OPTIONS = [
'name:asc',
'name:desc',

View File

@ -19,6 +19,7 @@ export const dataStoreColumnTypeSchema = z.enum(['string', 'number', 'boolean',
export const dataStoreCreateColumnSchema = z.object({
name: dataStoreColumnNameSchema,
type: dataStoreColumnTypeSchema,
columnIndex: z.number().optional(),
});
export type DataStoreCreateColumnSchema = z.infer<typeof dataStoreCreateColumnSchema>;

View File

@ -24,6 +24,7 @@ export class CreateDataStoreTables1747814180618 implements ReversibleMigration {
column('id').varchar(36).primary.notNull,
column('name').varchar(128).notNull,
column('type').varchar(32).notNull,
column('columnIndex').int.notNull,
column('dataStoreId').varchar(36).notNull,
)
.withForeignKey('dataStoreId', {

View File

@ -154,15 +154,14 @@ describe('dataStore', () => {
it('should fail with adding two columns of the same name', async () => {
// ARRANGE
await dataStoreService.addColumn(dataStore1.id, {
column: { name: 'myColumn1', type: 'string' },
name: 'myColumn1',
type: 'string',
});
// ACT
const result = await dataStoreService.addColumn(dataStore1.id, {
column: {
name: 'myColumn1',
type: 'number',
},
name: 'myColumn1',
type: 'number',
});
// ASSERT
@ -172,10 +171,8 @@ describe('dataStore', () => {
it('should fail with adding column of non-existent table', async () => {
// ACT
const result = await dataStoreService.addColumn('this is not an id', {
column: {
name: 'myColumn1',
type: 'number',
},
name: 'myColumn1',
type: 'number',
});
// ASSERT
@ -186,7 +183,8 @@ describe('dataStore', () => {
it('should succeed with deleting a column', async () => {
// ARRANGE
await dataStoreService.addColumn(dataStore1.id, {
column: { name: 'myColumn1', type: 'string' },
name: 'myColumn1',
type: 'string',
});
// ACT
@ -200,7 +198,8 @@ describe('dataStore', () => {
it('should fail when deleting unknown column', async () => {
// ARRANGE
await dataStoreService.addColumn(dataStore1.id, {
column: { name: 'myColumn1', type: 'string' },
name: 'myColumn1',
type: 'string',
});
// ACT
@ -214,7 +213,8 @@ describe('dataStore', () => {
it('should fail when deleting column from unknown table', async () => {
// ARRANGE
await dataStoreService.addColumn(dataStore1.id, {
column: { name: 'myColumn1', type: 'string' },
name: 'myColumn1',
type: 'string',
});
// ACT
@ -436,10 +436,10 @@ describe('dataStore', () => {
describe('appendRows', () => {
it('appends a row to an existing table', async () => {
// ARRANGE
await dataStoreService.addColumn(dataStore1.id, { column: { name: 'c1', type: 'number' } });
await dataStoreService.addColumn(dataStore1.id, { column: { name: 'c2', type: 'boolean' } });
await dataStoreService.addColumn(dataStore1.id, { column: { name: 'c3', type: 'date' } });
await dataStoreService.addColumn(dataStore1.id, { column: { name: 'c4', type: 'string' } });
await dataStoreService.addColumn(dataStore1.id, { name: 'c1', type: 'number' });
await dataStoreService.addColumn(dataStore1.id, { name: 'c2', type: 'boolean' });
await dataStoreService.addColumn(dataStore1.id, { name: 'c3', type: 'date' });
await dataStoreService.addColumn(dataStore1.id, { name: 'c4', type: 'string' });
// ACT
const result = await dataStoreService.appendRows(dataStore1.id, [
@ -454,10 +454,10 @@ describe('dataStore', () => {
it('rejects a mismatched row with extra column', async () => {
// ARRANGE
await dataStoreService.addColumn(dataStore1.id, { column: { name: 'c1', type: 'number' } });
await dataStoreService.addColumn(dataStore1.id, { column: { name: 'c2', type: 'boolean' } });
await dataStoreService.addColumn(dataStore1.id, { column: { name: 'c3', type: 'date' } });
await dataStoreService.addColumn(dataStore1.id, { column: { name: 'c4', type: 'string' } });
await dataStoreService.addColumn(dataStore1.id, { name: 'c1', type: 'number' });
await dataStoreService.addColumn(dataStore1.id, { name: 'c2', type: 'boolean' });
await dataStoreService.addColumn(dataStore1.id, { name: 'c3', type: 'date' });
await dataStoreService.addColumn(dataStore1.id, { name: 'c4', type: 'string' });
// ACT
const result = await dataStoreService.appendRows(dataStore1.id, [
@ -470,10 +470,10 @@ describe('dataStore', () => {
});
it('rejects a mismatched row with missing column', async () => {
// ARRANGE
await dataStoreService.addColumn(dataStore1.id, { column: { name: 'c1', type: 'number' } });
await dataStoreService.addColumn(dataStore1.id, { column: { name: 'c2', type: 'boolean' } });
await dataStoreService.addColumn(dataStore1.id, { column: { name: 'c3', type: 'date' } });
await dataStoreService.addColumn(dataStore1.id, { column: { name: 'c4', type: 'string' } });
await dataStoreService.addColumn(dataStore1.id, { name: 'c1', type: 'number' });
await dataStoreService.addColumn(dataStore1.id, { name: 'c2', type: 'boolean' });
await dataStoreService.addColumn(dataStore1.id, { name: 'c3', type: 'date' });
await dataStoreService.addColumn(dataStore1.id, { name: 'c4', type: 'string' });
// ACT
const result = await dataStoreService.appendRows(dataStore1.id, [
@ -486,10 +486,10 @@ describe('dataStore', () => {
});
it('rejects a mismatched row with replaced column', async () => {
// ARRANGE
await dataStoreService.addColumn(dataStore1.id, { column: { name: 'c1', type: 'number' } });
await dataStoreService.addColumn(dataStore1.id, { column: { name: 'c2', type: 'boolean' } });
await dataStoreService.addColumn(dataStore1.id, { column: { name: 'c3', type: 'date' } });
await dataStoreService.addColumn(dataStore1.id, { column: { name: 'c4', type: 'string' } });
await dataStoreService.addColumn(dataStore1.id, { name: 'c1', type: 'number' });
await dataStoreService.addColumn(dataStore1.id, { name: 'c2', type: 'boolean' });
await dataStoreService.addColumn(dataStore1.id, { name: 'c3', type: 'date' });
await dataStoreService.addColumn(dataStore1.id, { name: 'c4', type: 'string' });
// ACT
const result = await dataStoreService.appendRows(dataStore1.id, [
@ -502,10 +502,10 @@ describe('dataStore', () => {
});
it('rejects unknown data store id', async () => {
// ARRANGE
await dataStoreService.addColumn(dataStore1.id, { column: { name: 'c1', type: 'number' } });
await dataStoreService.addColumn(dataStore1.id, { column: { name: 'c2', type: 'boolean' } });
await dataStoreService.addColumn(dataStore1.id, { column: { name: 'c3', type: 'date' } });
await dataStoreService.addColumn(dataStore1.id, { column: { name: 'c4', type: 'string' } });
await dataStoreService.addColumn(dataStore1.id, { name: 'c1', type: 'number' });
await dataStoreService.addColumn(dataStore1.id, { name: 'c2', type: 'boolean' });
await dataStoreService.addColumn(dataStore1.id, { name: 'c3', type: 'date' });
await dataStoreService.addColumn(dataStore1.id, { name: 'c4', type: 'string' });
// ACT
const result = await dataStoreService.appendRows('this is not an id', [
@ -530,10 +530,10 @@ describe('dataStore', () => {
describe('getManyRowsAndCount', () => {
it('retrieves rows correctly', async () => {
// ARRANGE
await dataStoreService.addColumn(dataStore1.id, { column: { name: 'c1', type: 'number' } });
await dataStoreService.addColumn(dataStore1.id, { column: { name: 'c2', type: 'boolean' } });
await dataStoreService.addColumn(dataStore1.id, { column: { name: 'c3', type: 'date' } });
await dataStoreService.addColumn(dataStore1.id, { column: { name: 'c4', type: 'string' } });
await dataStoreService.addColumn(dataStore1.id, { name: 'c1', type: 'number' });
await dataStoreService.addColumn(dataStore1.id, { name: 'c2', type: 'boolean' });
await dataStoreService.addColumn(dataStore1.id, { name: 'c3', type: 'date' });
await dataStoreService.addColumn(dataStore1.id, { name: 'c4', type: 'string' });
await dataStoreService.appendRows(dataStore1.id, [
{ c1: 3, c2: true, c3: new Date(0), c4: 'hello?' },

View File

@ -1,8 +1,8 @@
import { DataStoreCreateColumnSchema } from '@n8n/api-types/src/schemas/data-store.schema';
import { WithStringId } from '@n8n/db';
import { Column, Entity, Index, JoinColumn, ManyToOne } from '@n8n/typeorm';
import { type DataStoreEntity } from './data-store.entity';
import { DataStoreColumnType } from './data-store.types';
@Entity()
@Index(['dataStoreId', 'name'], { unique: true })
@ -11,10 +11,13 @@ export class DataStoreColumnEntity extends WithStringId {
dataStoreId: string;
@Column()
name: string;
name: DataStoreCreateColumnSchema['name'];
@Column({ type: 'varchar' })
type: DataStoreColumnType;
type: DataStoreCreateColumnSchema['type'];
@Column({ type: 'int' })
columnIndex: number;
@ManyToOne('DataStoreEntity', 'columns')
@JoinColumn({ name: 'dataStoreId' })

View File

@ -25,4 +25,17 @@ export class DataStoreColumnRepository extends Repository<DataStoreColumnEntity>
async deleteColumn(dataStoreId: DataStoreUserTableName, column: string) {
await this.manager.query(deleteColumnQuery(dataStoreId, column));
}
async shiftColumns(dataStoreId: string, lowestIndex: number, delta: -1 | 1) {
await this.createQueryBuilder()
.update()
.set({
columnIndex: () => `columnIndex + ${delta}`,
})
.where('dataStoreId = :dataStoreId AND columnIndex > :thresholdValue', {
dataStoreId,
thresholdValue: lowestIndex,
})
.execute();
}
}

View File

@ -145,13 +145,8 @@ export class DataStoreService {
return 'tried to add column to non-existent table';
}
const column = this.dataStoreColumnRepository.create({
...dto.column,
dataStoreId,
});
const existingColumnMatch = await this.dataStoreColumnRepository.findBy({
name: column.name,
name: dto.name,
dataStoreId,
});
@ -159,6 +154,18 @@ export class DataStoreService {
return 'tried to add column with name already present in this data store';
}
if (dto.columnIndex === undefined) {
const columns = await this.dataStoreColumnRepository.getColumns(dataStoreId);
dto.columnIndex = columns.length;
} else {
await this.dataStoreColumnRepository.shiftColumns(dataStoreId, dto.columnIndex, 1);
}
const column = this.dataStoreColumnRepository.create({
...dto,
dataStoreId,
});
await this.dataStoreColumnRepository.insert(column);
await this.dataStoreColumnRepository.addColumn(toTableName(dataStoreId), column);
@ -185,6 +192,11 @@ export class DataStoreService {
await this.dataStoreColumnRepository.remove(existingColumnMatch);
await this.dataStoreColumnRepository.deleteColumn(toTableName(dataStoreId), dto.columnName);
await this.dataStoreColumnRepository.shiftColumns(
dataStoreId,
existingColumnMatch[0].columnIndex,
-1,
);
// should we update the main table entry's `updatedAt` field here?
return true;