mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 01:56:46 +00:00
feat(core): Add createdAt and updatedAt to data store user tables (no-changelog) (#18723)
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||
import type { DataStore, DataStoreCreateColumnSchema } from '@n8n/api-types';
|
||||
import {
|
||||
createTeamProject,
|
||||
@@ -271,7 +273,7 @@ describe('GET /projects/:projectId/data-stores', () => {
|
||||
|
||||
expect(response.body.data.count).toBe(2);
|
||||
expect(response.body.data.data).toHaveLength(2);
|
||||
expect(response.body.data.data.map((f: any) => f.name).sort()).toEqual(
|
||||
expect((response.body.data.data as DataStore[]).map((f) => f.name).sort()).toEqual(
|
||||
['Personal Data Store 1', 'Personal Data Store 2'].sort(),
|
||||
);
|
||||
});
|
||||
@@ -288,7 +290,7 @@ describe('GET /projects/:projectId/data-stores', () => {
|
||||
|
||||
expect(response.body.data.count).toBe(2);
|
||||
expect(response.body.data.data).toHaveLength(2);
|
||||
expect(response.body.data.data.map((f: any) => f.name).sort()).toEqual(
|
||||
expect((response.body.data.data as DataStore[]).map((f) => f.name).sort()).toEqual(
|
||||
['Test Data Store 1', 'Test Data Store 2'].sort(),
|
||||
);
|
||||
});
|
||||
@@ -305,7 +307,7 @@ describe('GET /projects/:projectId/data-stores', () => {
|
||||
|
||||
expect(response.body.data.count).toBe(2);
|
||||
expect(response.body.data.data).toHaveLength(2);
|
||||
expect(response.body.data.data.map((f: any) => f.name).sort()).toEqual(
|
||||
expect((response.body.data.data as DataStore[]).map((f) => f.name).sort()).toEqual(
|
||||
['Test Data Store', 'Test Something Else'].sort(),
|
||||
);
|
||||
});
|
||||
@@ -357,7 +359,7 @@ describe('GET /projects/:projectId/data-stores', () => {
|
||||
|
||||
expect(response.body.data.count).toBe(5); // Total count should be 5
|
||||
expect(response.body.data.data).toHaveLength(3); // But only 3 returned
|
||||
expect(response.body.data.data.map((store: DataStore) => store.name)).toEqual([
|
||||
expect((response.body.data.data as DataStore[]).map((store) => store.name)).toEqual([
|
||||
'Data Store 5',
|
||||
'Data Store 4',
|
||||
'Data Store 3',
|
||||
@@ -381,7 +383,7 @@ describe('GET /projects/:projectId/data-stores', () => {
|
||||
|
||||
expect(response.body.data.count).toBe(5);
|
||||
expect(response.body.data.data).toHaveLength(3);
|
||||
expect(response.body.data.data.map((store: DataStore) => store.name)).toEqual([
|
||||
expect((response.body.data.data as DataStore[]).map((store) => store.name)).toEqual([
|
||||
'Data Store 3',
|
||||
'Data Store 2',
|
||||
'Data Store 1',
|
||||
@@ -405,7 +407,7 @@ describe('GET /projects/:projectId/data-stores', () => {
|
||||
|
||||
expect(response.body.data.count).toBe(5);
|
||||
expect(response.body.data.data).toHaveLength(2);
|
||||
expect(response.body.data.data.map((store: DataStore) => store.name)).toEqual([
|
||||
expect((response.body.data.data as DataStore[]).map((store) => store.name)).toEqual([
|
||||
'Data Store 4',
|
||||
'Data Store 3',
|
||||
]);
|
||||
@@ -421,7 +423,7 @@ describe('GET /projects/:projectId/data-stores', () => {
|
||||
.query({ sortBy: 'name:asc' })
|
||||
.expect(200);
|
||||
|
||||
expect(response.body.data.data.map((store: DataStore) => store.name)).toEqual([
|
||||
expect((response.body.data.data as DataStore[]).map((store) => store.name)).toEqual([
|
||||
'A Data Store',
|
||||
'M Data Store',
|
||||
'Z Data Store',
|
||||
@@ -438,7 +440,7 @@ describe('GET /projects/:projectId/data-stores', () => {
|
||||
.query({ sortBy: 'name:desc' })
|
||||
.expect(200);
|
||||
|
||||
expect(response.body.data.data.map((f: DataStore) => f.name)).toEqual([
|
||||
expect((response.body.data.data as DataStore[]).map((f) => f.name)).toEqual([
|
||||
'Z Data Store',
|
||||
'M Data Store',
|
||||
'A Data Store',
|
||||
@@ -464,7 +466,7 @@ describe('GET /projects/:projectId/data-stores', () => {
|
||||
.query({ sortBy: 'updatedAt:desc' })
|
||||
.expect(200);
|
||||
|
||||
expect(response.body.data.data.map((f: DataStore) => f.name)).toEqual([
|
||||
expect((response.body.data.data as DataStore[]).map((f) => f.name)).toEqual([
|
||||
'Newest Data Store',
|
||||
'Middle Data Store',
|
||||
'Older Data Store',
|
||||
@@ -1582,7 +1584,7 @@ describe('GET /projects/:projectId/data-stores/:dataStoreId/rows', () => {
|
||||
.get(`/projects/${project.id}/data-stores/${dataStore.id}/rows`)
|
||||
.expect(200);
|
||||
|
||||
expect(response.body.data).toEqual({
|
||||
expect(response.body.data).toMatchObject({
|
||||
count: 1,
|
||||
data: [
|
||||
{
|
||||
@@ -1621,7 +1623,7 @@ describe('GET /projects/:projectId/data-stores/:dataStoreId/rows', () => {
|
||||
.get(`/projects/${project.id}/data-stores/${dataStore.id}/rows`)
|
||||
.expect(200);
|
||||
|
||||
expect(response.body.data).toEqual({
|
||||
expect(response.body.data).toMatchObject({
|
||||
count: 1,
|
||||
data: [
|
||||
{
|
||||
@@ -1660,7 +1662,7 @@ describe('GET /projects/:projectId/data-stores/:dataStoreId/rows', () => {
|
||||
.get(`/projects/${project.id}/data-stores/${dataStore.id}/rows`)
|
||||
.expect(200);
|
||||
|
||||
expect(response.body.data).toEqual({
|
||||
expect(response.body.data).toMatchObject({
|
||||
count: 1,
|
||||
data: [
|
||||
{
|
||||
@@ -1696,7 +1698,7 @@ describe('GET /projects/:projectId/data-stores/:dataStoreId/rows', () => {
|
||||
.get(`/projects/${memberProject.id}/data-stores/${dataStore.id}/rows`)
|
||||
.expect(200);
|
||||
|
||||
expect(response.body.data).toEqual({
|
||||
expect(response.body.data).toMatchObject({
|
||||
count: 1,
|
||||
data: [
|
||||
{
|
||||
@@ -3053,7 +3055,11 @@ describe('PATCH /projects/:projectId/data-stores/:dataStoreId/rows', () => {
|
||||
.expect(200);
|
||||
|
||||
expect(readResponse.body.data.count).toBe(1);
|
||||
expect(readResponse.body.data.data[0]).toMatchObject({ id: 1, name: 'Alice', age: 31 });
|
||||
expect(readResponse.body.data.data[0]).toMatchObject({
|
||||
id: 1,
|
||||
name: 'Alice',
|
||||
age: 31,
|
||||
});
|
||||
});
|
||||
|
||||
test('should update row in personal project', async () => {
|
||||
@@ -3080,7 +3086,11 @@ describe('PATCH /projects/:projectId/data-stores/:dataStoreId/rows', () => {
|
||||
.expect(200);
|
||||
|
||||
expect(readResponse.body.data.count).toBe(1);
|
||||
expect(readResponse.body.data.data[0]).toMatchObject({ id: 1, name: 'Alice', age: 31 });
|
||||
expect(readResponse.body.data.data[0]).toMatchObject({
|
||||
id: 1,
|
||||
name: 'Alice',
|
||||
age: 31,
|
||||
});
|
||||
});
|
||||
|
||||
test('should update row by id filter', async () => {
|
||||
@@ -3112,8 +3122,16 @@ describe('PATCH /projects/:projectId/data-stores/:dataStoreId/rows', () => {
|
||||
expect(readResponse.body.data.count).toBe(2);
|
||||
expect(readResponse.body.data.data).toEqual(
|
||||
expect.arrayContaining([
|
||||
{ id: 1, name: 'Alice', age: 31 },
|
||||
{ id: 2, name: 'Bob', age: 25 },
|
||||
expect.objectContaining({
|
||||
id: 1,
|
||||
name: 'Alice',
|
||||
age: 31,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: 2,
|
||||
name: 'Bob',
|
||||
age: 25,
|
||||
}),
|
||||
]),
|
||||
);
|
||||
});
|
||||
@@ -3149,9 +3167,24 @@ describe('PATCH /projects/:projectId/data-stores/:dataStoreId/rows', () => {
|
||||
expect(readResponse.body.data.count).toBe(3);
|
||||
expect(readResponse.body.data.data).toEqual(
|
||||
expect.arrayContaining([
|
||||
{ id: 1, name: 'Alice', age: 30, department: 'Management' },
|
||||
{ id: 2, name: 'Alice', age: 25, department: 'Marketing' },
|
||||
{ id: 3, name: 'Bob', age: 30, department: 'Engineering' },
|
||||
expect.objectContaining({
|
||||
id: 1,
|
||||
name: 'Alice',
|
||||
age: 30,
|
||||
department: 'Management',
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: 2,
|
||||
name: 'Alice',
|
||||
age: 25,
|
||||
department: 'Marketing',
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: 3,
|
||||
name: 'Bob',
|
||||
age: 30,
|
||||
department: 'Engineering',
|
||||
}),
|
||||
]),
|
||||
);
|
||||
});
|
||||
@@ -3182,7 +3215,10 @@ describe('PATCH /projects/:projectId/data-stores/:dataStoreId/rows', () => {
|
||||
.expect(200);
|
||||
|
||||
expect(readResponse.body.data.count).toBe(1);
|
||||
expect(readResponse.body.data.data[0]).toMatchObject({ name: 'Alice', age: 30 });
|
||||
expect(readResponse.body.data.data[0]).toMatchObject({
|
||||
name: 'Alice',
|
||||
age: 30,
|
||||
});
|
||||
});
|
||||
|
||||
test('should fail when filter is empty', async () => {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||
import type { AddDataStoreColumnDto, CreateDataStoreColumnDto } from '@n8n/api-types';
|
||||
import { createTeamProject, testDb, testModules } from '@n8n/backend-test-utils';
|
||||
import { Project } from '@n8n/db';
|
||||
@@ -62,11 +63,8 @@ describe('dataStore', () => {
|
||||
type: 'string',
|
||||
index: 0,
|
||||
dataStoreId,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
id: expect.any(String),
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
createdAt: expect.any(Date),
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
updatedAt: expect.any(Date),
|
||||
},
|
||||
]);
|
||||
@@ -101,7 +99,7 @@ describe('dataStore', () => {
|
||||
const table = await queryRunner.getTable(userTableName);
|
||||
const columnNames = table?.columns.map((col) => col.name);
|
||||
|
||||
expect(columnNames).toEqual(['id']);
|
||||
expect(columnNames).toEqual(expect.arrayContaining(['id', 'createdAt', 'updatedAt']));
|
||||
} finally {
|
||||
await queryRunner.release();
|
||||
}
|
||||
@@ -272,66 +270,36 @@ describe('dataStore', () => {
|
||||
}
|
||||
const columnResult = await dataStoreService.getColumns(dataStoreId, project1.id);
|
||||
expect(columnResult).toEqual([
|
||||
{
|
||||
expect.objectContaining({
|
||||
index: 0,
|
||||
dataStoreId,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
id: expect.any(String),
|
||||
name: 'myColumn0',
|
||||
type: 'string',
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
createdAt: expect.any(Date),
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
updatedAt: expect.any(Date),
|
||||
},
|
||||
{
|
||||
}),
|
||||
expect.objectContaining({
|
||||
index: 1,
|
||||
dataStoreId,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
id: expect.any(String),
|
||||
name: 'myColumn1',
|
||||
type: 'string',
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
createdAt: expect.any(Date),
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
updatedAt: expect.any(Date),
|
||||
},
|
||||
{
|
||||
}),
|
||||
expect.objectContaining({
|
||||
index: 2,
|
||||
dataStoreId,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
id: expect.any(String),
|
||||
name: 'myColumn2',
|
||||
type: 'number',
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
createdAt: expect.any(Date),
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
updatedAt: expect.any(Date),
|
||||
},
|
||||
{
|
||||
}),
|
||||
expect.objectContaining({
|
||||
index: 3,
|
||||
dataStoreId,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
id: expect.any(String),
|
||||
name: 'myColumn3',
|
||||
type: 'number',
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
createdAt: expect.any(Date),
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
updatedAt: expect.any(Date),
|
||||
},
|
||||
{
|
||||
}),
|
||||
expect.objectContaining({
|
||||
index: 4,
|
||||
dataStoreId,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
id: expect.any(String),
|
||||
name: 'myColumn4',
|
||||
type: 'date',
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
createdAt: expect.any(Date),
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
updatedAt: expect.any(Date),
|
||||
},
|
||||
}),
|
||||
]);
|
||||
|
||||
const userTableName = dataStoreRowsRepository.toTableName(dataStoreId);
|
||||
@@ -343,6 +311,8 @@ describe('dataStore', () => {
|
||||
expect(columnNames).toEqual(
|
||||
expect.arrayContaining([
|
||||
'id',
|
||||
'createdAt',
|
||||
'updatedAt',
|
||||
'myColumn0',
|
||||
'myColumn1',
|
||||
'myColumn2',
|
||||
@@ -375,30 +345,18 @@ describe('dataStore', () => {
|
||||
expect(columnResult.length).toBe(2);
|
||||
|
||||
expect(columnResult).toEqual([
|
||||
{
|
||||
expect.objectContaining({
|
||||
index: 0,
|
||||
dataStoreId,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
id: expect.any(String),
|
||||
name: 'myColumn0',
|
||||
type: 'string',
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
createdAt: expect.any(Date),
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
updatedAt: expect.any(Date),
|
||||
},
|
||||
{
|
||||
}),
|
||||
expect.objectContaining({
|
||||
index: 1,
|
||||
dataStoreId,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
id: expect.any(String),
|
||||
name: 'myColumn1',
|
||||
type: 'number',
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
createdAt: expect.any(Date),
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
updatedAt: expect.any(Date),
|
||||
},
|
||||
}),
|
||||
]);
|
||||
|
||||
const userTableName = dataStoreRowsRepository.toTableName(dataStoreId);
|
||||
@@ -407,7 +365,9 @@ describe('dataStore', () => {
|
||||
const table = await queryRunner.getTable(userTableName);
|
||||
const columnNames = table?.columns.map((col) => col.name);
|
||||
|
||||
expect(columnNames).toEqual(expect.arrayContaining(['id', 'myColumn0', 'myColumn1']));
|
||||
expect(columnNames).toEqual(
|
||||
expect.arrayContaining(['id', 'createdAt', 'updatedAt', 'myColumn0', 'myColumn1']),
|
||||
);
|
||||
} finally {
|
||||
await queryRunner.release();
|
||||
}
|
||||
@@ -492,9 +452,24 @@ describe('dataStore', () => {
|
||||
const updatedData = await dataStoreService.getManyRowsAndCount(dataStoreId, project1.id, {});
|
||||
expect(updatedData.count).toBe(3);
|
||||
expect(updatedData.data).toEqual([
|
||||
{ id: 1, name: 'Alice', age: 30, email: null },
|
||||
{ id: 2, name: 'Bob', age: 25, email: null },
|
||||
{ id: 3, name: 'Charlie', age: 35, email: null },
|
||||
expect.objectContaining({
|
||||
id: 1,
|
||||
name: 'Alice',
|
||||
age: 30,
|
||||
email: null,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: 2,
|
||||
name: 'Bob',
|
||||
age: 25,
|
||||
email: null,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: 3,
|
||||
name: 'Charlie',
|
||||
age: 35,
|
||||
email: null,
|
||||
}),
|
||||
]);
|
||||
|
||||
// Verify we can insert new rows with the new column
|
||||
@@ -510,10 +485,30 @@ describe('dataStore', () => {
|
||||
const finalData = await dataStoreService.getManyRowsAndCount(dataStoreId, project1.id, {});
|
||||
expect(finalData.count).toBe(4);
|
||||
expect(finalData.data).toEqual([
|
||||
{ id: 1, name: 'Alice', age: 30, email: null },
|
||||
{ id: 2, name: 'Bob', age: 25, email: null },
|
||||
{ id: 3, name: 'Charlie', age: 35, email: null },
|
||||
{ id: 4, name: 'David', age: 28, email: 'david@example.com' },
|
||||
expect.objectContaining({
|
||||
id: 1,
|
||||
name: 'Alice',
|
||||
age: 30,
|
||||
email: null,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: 2,
|
||||
name: 'Bob',
|
||||
age: 25,
|
||||
email: null,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: 3,
|
||||
name: 'Charlie',
|
||||
age: 35,
|
||||
email: null,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: 4,
|
||||
name: 'David',
|
||||
age: 28,
|
||||
email: 'david@example.com',
|
||||
}),
|
||||
]);
|
||||
});
|
||||
});
|
||||
@@ -550,7 +545,6 @@ describe('dataStore', () => {
|
||||
name: 'myColumn2',
|
||||
type: 'number',
|
||||
createdAt: c2.createdAt,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
updatedAt: expect.any(Date),
|
||||
},
|
||||
]);
|
||||
@@ -656,7 +650,6 @@ describe('dataStore', () => {
|
||||
expect(result.data).toHaveLength(1);
|
||||
expect(result.data[0]).toEqual({
|
||||
...dataStore,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
project: expect.any(Project),
|
||||
});
|
||||
expect(result.data[0].project).toEqual({
|
||||
@@ -689,12 +682,10 @@ describe('dataStore', () => {
|
||||
expect(result.data).toHaveLength(2);
|
||||
expect(result.data).toContainEqual({
|
||||
...dataStore2,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
project: expect.any(Project),
|
||||
});
|
||||
expect(result.data).toContainEqual({
|
||||
...dataStore1,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
project: expect.any(Project),
|
||||
});
|
||||
expect(result.count).toEqual(2);
|
||||
@@ -803,46 +794,34 @@ describe('dataStore', () => {
|
||||
expect(resultColumns).toEqual(
|
||||
expect.arrayContaining([
|
||||
{
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
id: expect.any(String),
|
||||
name: 'myColumn1',
|
||||
type: 'string',
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
createdAt: expect.any(Date),
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
updatedAt: expect.any(Date),
|
||||
index: 0,
|
||||
},
|
||||
{
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
id: expect.any(String),
|
||||
name: 'myColumn2',
|
||||
type: 'number',
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
createdAt: expect.any(Date),
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
updatedAt: expect.any(Date),
|
||||
index: 1,
|
||||
},
|
||||
{
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
id: expect.any(String),
|
||||
name: 'myColumn3',
|
||||
type: 'number',
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
createdAt: expect.any(Date),
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
updatedAt: expect.any(Date),
|
||||
index: 2,
|
||||
},
|
||||
{
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
id: expect.any(String),
|
||||
name: 'myColumn4',
|
||||
type: 'date',
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
createdAt: expect.any(Date),
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
updatedAt: expect.any(Date),
|
||||
index: 3,
|
||||
},
|
||||
@@ -998,14 +977,16 @@ describe('dataStore', () => {
|
||||
);
|
||||
expect(count).toEqual(4);
|
||||
expect(data).toEqual(
|
||||
rows.map((row, i) => ({
|
||||
...row,
|
||||
id: i + 1,
|
||||
c1: row.c1,
|
||||
c2: row.c2,
|
||||
c3: row.c3 instanceof Date ? row.c3.toISOString() : row.c3,
|
||||
c4: row.c4,
|
||||
})),
|
||||
rows.map(
|
||||
(row) =>
|
||||
expect.objectContaining({
|
||||
...row,
|
||||
c1: row.c1,
|
||||
c2: row.c2,
|
||||
c3: row.c3 instanceof Date ? row.c3.toISOString() : row.c3,
|
||||
c4: row.c4,
|
||||
}) as Record<string, unknown>,
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1037,8 +1018,16 @@ describe('dataStore', () => {
|
||||
|
||||
expect(count).toEqual(2);
|
||||
expect(data).toEqual([
|
||||
{ c1: 1, c2: 'foo', id: 1 },
|
||||
{ c1: 1, c2: 'foo', id: 2 },
|
||||
expect.objectContaining({
|
||||
c1: 1,
|
||||
c2: 'foo',
|
||||
id: 1,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
c1: 1,
|
||||
c2: 'foo',
|
||||
id: 2,
|
||||
}),
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -1074,9 +1063,21 @@ describe('dataStore', () => {
|
||||
|
||||
expect(count).toEqual(3);
|
||||
expect(data).toEqual([
|
||||
{ c1: 2, c2: 'bar', id: 2 },
|
||||
{ c1: 1, c2: 'baz', id: 3 },
|
||||
{ c1: 2, c2: 'faz', id: 4 },
|
||||
expect.objectContaining({
|
||||
c1: 2,
|
||||
c2: 'bar',
|
||||
id: 2,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
c1: 1,
|
||||
c2: 'baz',
|
||||
id: 3,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
c1: 2,
|
||||
c2: 'faz',
|
||||
id: 4,
|
||||
}),
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -1271,7 +1272,23 @@ describe('dataStore', () => {
|
||||
{},
|
||||
);
|
||||
expect(count).toEqual(3);
|
||||
expect(data).toEqual([{ id: 1 }, { id: 2 }, { id: 3 }]);
|
||||
expect(data).toEqual([
|
||||
{
|
||||
id: 1,
|
||||
createdAt: expect.any(String),
|
||||
updatedAt: expect.any(String),
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
createdAt: expect.any(String),
|
||||
updatedAt: expect.any(String),
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
createdAt: expect.any(String),
|
||||
updatedAt: expect.any(String),
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1316,9 +1333,21 @@ describe('dataStore', () => {
|
||||
expect(count).toEqual(3);
|
||||
expect(data).toEqual(
|
||||
expect.arrayContaining([
|
||||
{ pid: '1995-111a', name: 'Alicia', age: 31, id: 1 },
|
||||
{ pid: '1994-222a', name: 'John', age: 32, id: 2 },
|
||||
{ pid: '1993-333a', name: 'Paul', age: 32, id: 3 }, // unchanged
|
||||
expect.objectContaining({
|
||||
pid: '1995-111a',
|
||||
name: 'Alicia',
|
||||
age: 31,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
pid: '1994-222a',
|
||||
name: 'John',
|
||||
age: 32,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
pid: '1993-333a',
|
||||
name: 'Paul',
|
||||
age: 32,
|
||||
}), // unchanged
|
||||
]),
|
||||
);
|
||||
});
|
||||
@@ -1361,9 +1390,21 @@ describe('dataStore', () => {
|
||||
expect(count).toEqual(3);
|
||||
expect(data).toEqual(
|
||||
expect.arrayContaining([
|
||||
{ city: 'Berlin', age: 30, isEligible: true, id: 1 },
|
||||
{ city: 'Amsterdam', age: 32, isEligible: true, id: 2 },
|
||||
{ city: 'Oslo', age: 28, isEligible: false, id: 3 },
|
||||
expect.objectContaining({
|
||||
city: 'Berlin',
|
||||
age: 30,
|
||||
isEligible: true,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
city: 'Amsterdam',
|
||||
age: 32,
|
||||
isEligible: true,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
city: 'Oslo',
|
||||
age: 28,
|
||||
isEligible: false,
|
||||
}),
|
||||
]),
|
||||
);
|
||||
});
|
||||
@@ -1402,8 +1443,16 @@ describe('dataStore', () => {
|
||||
|
||||
expect(count).toEqual(2);
|
||||
expect(data).toEqual([
|
||||
{ name: 'Alice', age: 30, id: 1, pid: '1995-111a' },
|
||||
{ name: 'Alice', age: 30, id: 2, pid: '1992-222b' },
|
||||
expect.objectContaining({
|
||||
name: 'Alice',
|
||||
age: 30,
|
||||
pid: '1995-111a',
|
||||
}),
|
||||
expect.objectContaining({
|
||||
name: 'Alice',
|
||||
age: 30,
|
||||
pid: '1992-222b',
|
||||
}),
|
||||
]);
|
||||
});
|
||||
});
|
||||
@@ -1428,10 +1477,6 @@ describe('dataStore', () => {
|
||||
]);
|
||||
expect(ids).toEqual([{ id: 1 }, { id: 2 }, { id: 3 }]);
|
||||
|
||||
// Get initial data to find row IDs
|
||||
const initialData = await dataStoreService.getManyRowsAndCount(dataStoreId, project1.id, {});
|
||||
expect(initialData.count).toBe(3);
|
||||
|
||||
// ACT - Delete first and third rows
|
||||
const result = await dataStoreService.deleteRows(dataStoreId, project1.id, [1, 3]);
|
||||
|
||||
@@ -1440,7 +1485,12 @@ describe('dataStore', () => {
|
||||
|
||||
const rows = await dataStoreService.getManyRowsAndCount(dataStoreId, project1.id, {});
|
||||
expect(rows.count).toBe(1);
|
||||
expect(rows.data).toEqual([{ name: 'Bob', age: 25, id: 2 }]);
|
||||
expect(rows.data).toEqual([
|
||||
expect.objectContaining({
|
||||
name: 'Bob',
|
||||
age: 25,
|
||||
}),
|
||||
]);
|
||||
});
|
||||
|
||||
it('returns true when deleting empty list of IDs', async () => {
|
||||
@@ -1517,8 +1567,18 @@ describe('dataStore', () => {
|
||||
const { data } = await dataStoreService.getManyRowsAndCount(dataStoreId, project1.id, {});
|
||||
expect(data).toEqual(
|
||||
expect.arrayContaining([
|
||||
{ id: 1, name: 'Alice', age: 31, active: false },
|
||||
{ id: 2, name: 'Bob', age: 25, active: false },
|
||||
expect.objectContaining({
|
||||
id: 1,
|
||||
name: 'Alice',
|
||||
age: 31,
|
||||
active: false,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: 2,
|
||||
name: 'Bob',
|
||||
age: 25,
|
||||
active: false,
|
||||
}),
|
||||
]),
|
||||
);
|
||||
});
|
||||
@@ -1535,8 +1595,16 @@ describe('dataStore', () => {
|
||||
});
|
||||
|
||||
await dataStoreService.insertRows(dataStoreId, project1.id, [
|
||||
{ name: 'Alice', age: 30, active: true },
|
||||
{ name: 'Bob', age: 25, active: false },
|
||||
{
|
||||
name: 'Alice',
|
||||
age: 30,
|
||||
active: true,
|
||||
},
|
||||
{
|
||||
name: 'Bob',
|
||||
age: 25,
|
||||
active: false,
|
||||
},
|
||||
]);
|
||||
|
||||
// ACT
|
||||
@@ -1551,12 +1619,72 @@ describe('dataStore', () => {
|
||||
const { data } = await dataStoreService.getManyRowsAndCount(dataStoreId, project1.id, {});
|
||||
expect(data).toEqual(
|
||||
expect.arrayContaining([
|
||||
{ id: 1, name: 'Alicia', age: 31, active: false },
|
||||
{ id: 2, name: 'Bob', age: 25, active: false },
|
||||
expect.objectContaining({
|
||||
id: 1,
|
||||
name: 'Alicia',
|
||||
age: 31,
|
||||
active: false,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: 2,
|
||||
name: 'Bob',
|
||||
age: 25,
|
||||
active: false,
|
||||
}),
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
it('should update the updatedAt', async () => {
|
||||
// ARRANGE
|
||||
const { id: dataStoreId } = await dataStoreService.createDataStore(project1.id, {
|
||||
name: 'dataStore',
|
||||
columns: [
|
||||
{ name: 'name', type: 'string' },
|
||||
{ name: 'age', type: 'number' },
|
||||
{ name: 'active', type: 'boolean' },
|
||||
],
|
||||
});
|
||||
|
||||
await dataStoreService.insertRows(dataStoreId, project1.id, [
|
||||
{ name: 'Alice', age: 30, active: true },
|
||||
]);
|
||||
|
||||
const { data: initialRows } = await dataStoreService.getManyRowsAndCount(
|
||||
dataStoreId,
|
||||
project1.id,
|
||||
{},
|
||||
);
|
||||
|
||||
// Wait to ensure different timestamps
|
||||
await new Promise((resolve) => setTimeout(resolve, 10));
|
||||
|
||||
// ACT
|
||||
const result = await dataStoreService.updateRow(dataStoreId, project1.id, {
|
||||
filter: { name: 'Alice' },
|
||||
data: { age: 31, active: false },
|
||||
});
|
||||
|
||||
// ASSERT
|
||||
expect(result).toBe(true);
|
||||
|
||||
const { data: updatedRows } = await dataStoreService.getManyRowsAndCount(
|
||||
dataStoreId,
|
||||
project1.id,
|
||||
{},
|
||||
);
|
||||
|
||||
expect(updatedRows[0].createdAt).not.toBeNull();
|
||||
expect(updatedRows[0].updatedAt).not.toBeNull();
|
||||
expect(initialRows[0].updatedAt).not.toBeNull();
|
||||
expect(new Date(updatedRows[0].updatedAt as string).getTime()).toBeGreaterThan(
|
||||
new Date(initialRows[0].updatedAt as string).getTime(),
|
||||
);
|
||||
expect(new Date(updatedRows[0].updatedAt as string).getTime()).toBeGreaterThan(
|
||||
new Date(updatedRows[0].createdAt as string).getTime(),
|
||||
);
|
||||
});
|
||||
|
||||
it('should update row with multiple filter conditions', async () => {
|
||||
// ARRANGE
|
||||
const { id: dataStoreId } = await dataStoreService.createDataStore(project1.id, {
|
||||
@@ -1586,9 +1714,24 @@ describe('dataStore', () => {
|
||||
const { data } = await dataStoreService.getManyRowsAndCount(dataStoreId, project1.id, {});
|
||||
expect(data).toEqual(
|
||||
expect.arrayContaining([
|
||||
{ id: 1, name: 'Alice', age: 30, department: 'Management' },
|
||||
{ id: 2, name: 'Alice', age: 25, department: 'Marketing' },
|
||||
{ id: 3, name: 'Bob', age: 30, department: 'Engineering' },
|
||||
expect.objectContaining({
|
||||
id: 1,
|
||||
name: 'Alice',
|
||||
age: 30,
|
||||
department: 'Management',
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: 2,
|
||||
name: 'Alice',
|
||||
age: 25,
|
||||
department: 'Marketing',
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: 3,
|
||||
name: 'Bob',
|
||||
age: 30,
|
||||
department: 'Engineering',
|
||||
}),
|
||||
]),
|
||||
);
|
||||
});
|
||||
@@ -1615,7 +1758,12 @@ describe('dataStore', () => {
|
||||
expect(result).toBe(true);
|
||||
|
||||
const { data } = await dataStoreService.getManyRowsAndCount(dataStoreId, project1.id, {});
|
||||
expect(data).toEqual([{ id: 1, name: 'Alice', age: 30 }]);
|
||||
expect(data).toEqual([
|
||||
expect.objectContaining({
|
||||
name: 'Alice',
|
||||
age: 30,
|
||||
}),
|
||||
]);
|
||||
});
|
||||
|
||||
it('should throw validation error when filters are empty', async () => {
|
||||
@@ -1642,7 +1790,12 @@ describe('dataStore', () => {
|
||||
);
|
||||
|
||||
const { data } = await dataStoreService.getManyRowsAndCount(dataStoreId, project1.id, {});
|
||||
expect(data).toEqual([{ id: 1, name: 'Alice', age: 30 }]);
|
||||
expect(data).toEqual([
|
||||
expect.objectContaining({
|
||||
name: 'Alice',
|
||||
age: 30,
|
||||
}),
|
||||
]);
|
||||
});
|
||||
|
||||
it('should throw validation error when data is empty', async () => {
|
||||
@@ -1669,7 +1822,12 @@ describe('dataStore', () => {
|
||||
);
|
||||
|
||||
const { data } = await dataStoreService.getManyRowsAndCount(dataStoreId, project1.id, {});
|
||||
expect(data).toEqual([{ id: 1, name: 'Alice', age: 30 }]);
|
||||
expect(data).toEqual([
|
||||
expect.objectContaining({
|
||||
name: 'Alice',
|
||||
age: 30,
|
||||
}),
|
||||
]);
|
||||
});
|
||||
|
||||
it('should fail when data store does not exist', async () => {
|
||||
@@ -1764,7 +1922,13 @@ describe('dataStore', () => {
|
||||
expect(result).toBe(true);
|
||||
|
||||
const { data } = await dataStoreService.getManyRowsAndCount(dataStoreId, project1.id, {});
|
||||
expect(data).toEqual([{ id: 1, name: 'Alice', age: 31, active: true }]);
|
||||
expect(data).toEqual([
|
||||
expect.objectContaining({
|
||||
name: 'Alice',
|
||||
age: 31,
|
||||
active: true,
|
||||
}),
|
||||
]);
|
||||
});
|
||||
|
||||
it('should handle date column updates correctly', async () => {
|
||||
@@ -1793,7 +1957,12 @@ describe('dataStore', () => {
|
||||
expect(result).toBe(true);
|
||||
|
||||
const { data } = await dataStoreService.getManyRowsAndCount(dataStoreId, project1.id, {});
|
||||
expect(data).toEqual([{ id: 1, name: 'Alice', birthDate: newDate.toISOString() }]);
|
||||
expect(data).toEqual([
|
||||
expect.objectContaining({
|
||||
name: 'Alice',
|
||||
birthDate: newDate.toISOString(),
|
||||
}),
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1811,9 +1980,24 @@ describe('dataStore', () => {
|
||||
});
|
||||
|
||||
const rows = [
|
||||
{ c1: 3, c2: true, c3: new Date(0), c4: 'hello?' },
|
||||
{ c1: 4, c2: false, c3: new Date(1), c4: 'hello!' },
|
||||
{ c1: 5, c2: true, c3: new Date(2), c4: 'hello.' },
|
||||
{
|
||||
c1: 3,
|
||||
c2: true,
|
||||
c3: new Date(0),
|
||||
c4: 'hello?',
|
||||
},
|
||||
{
|
||||
c1: 4,
|
||||
c2: false,
|
||||
c3: new Date(1),
|
||||
c4: 'hello!',
|
||||
},
|
||||
{
|
||||
c1: 5,
|
||||
c2: true,
|
||||
c3: new Date(2),
|
||||
c4: 'hello.',
|
||||
},
|
||||
];
|
||||
|
||||
const ids = await dataStoreService.insertRows(dataStoreId, project1.id, rows);
|
||||
@@ -1826,9 +2010,33 @@ describe('dataStore', () => {
|
||||
expect(result.count).toEqual(3);
|
||||
// Assuming IDs are auto-incremented starting from 1
|
||||
expect(result.data).toEqual([
|
||||
{ c1: rows[0].c1, c2: rows[0].c2, c3: '1970-01-01T00:00:00.000Z', c4: rows[0].c4, id: 1 },
|
||||
{ c1: rows[1].c1, c2: rows[1].c2, c3: '1970-01-01T00:00:00.001Z', c4: rows[1].c4, id: 2 },
|
||||
{ c1: rows[2].c1, c2: rows[2].c2, c3: '1970-01-01T00:00:00.002Z', c4: rows[2].c4, id: 3 },
|
||||
{
|
||||
c1: rows[0].c1,
|
||||
c2: rows[0].c2,
|
||||
c3: '1970-01-01T00:00:00.000Z',
|
||||
c4: rows[0].c4,
|
||||
id: 1,
|
||||
createdAt: expect.any(String),
|
||||
updatedAt: expect.any(String),
|
||||
},
|
||||
{
|
||||
c1: rows[1].c1,
|
||||
c2: rows[1].c2,
|
||||
c3: '1970-01-01T00:00:00.001Z',
|
||||
c4: rows[1].c4,
|
||||
id: 2,
|
||||
createdAt: expect.any(String),
|
||||
updatedAt: expect.any(String),
|
||||
},
|
||||
{
|
||||
c1: rows[2].c1,
|
||||
c2: rows[2].c2,
|
||||
c3: '1970-01-01T00:00:00.002Z',
|
||||
c4: rows[2].c4,
|
||||
id: 3,
|
||||
createdAt: expect.any(String),
|
||||
updatedAt: expect.any(String),
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -177,6 +177,9 @@ export class DataStoreRowsRepository {
|
||||
setValues[key] = normalizeValue(value, columnTypeMap[key], dbType);
|
||||
}
|
||||
|
||||
// Always update the updatedAt timestamp
|
||||
setValues.updatedAt = normalizeValue(new Date(), 'date', dbType);
|
||||
|
||||
queryBuilder.set(setValues);
|
||||
|
||||
const normalizedWhereData: Record<string, DataStoreColumnJsType | null> = {};
|
||||
@@ -208,8 +211,9 @@ export class DataStoreRowsRepository {
|
||||
queryRunner: QueryRunner,
|
||||
) {
|
||||
const dslColumns = [new DslColumn('id').int.autoGenerate2.primary, ...toDslColumns(columns)];
|
||||
const createTable = new CreateTable(this.toTableName(dataStoreId), '', queryRunner);
|
||||
createTable.withColumns.apply(createTable, dslColumns);
|
||||
const createTable = new CreateTable(this.toTableName(dataStoreId), '', queryRunner).withColumns(
|
||||
...dslColumns,
|
||||
).withTimestamps;
|
||||
|
||||
await createTable.execute(queryRunner);
|
||||
}
|
||||
|
||||
@@ -193,7 +193,13 @@ export function extractInsertedIds(raw: unknown, dbType: DataSourceOptions['type
|
||||
}
|
||||
|
||||
export function normalizeRows(rows: DataStoreRows, columns: DataStoreColumn[]) {
|
||||
const typeMap = new Map(columns.map((col) => [col.name, col.type]));
|
||||
// we need to normalize system dates as well
|
||||
const systemColumns = [
|
||||
{ name: 'createdAt', type: 'date' },
|
||||
{ name: 'updatedAt', type: 'date' },
|
||||
];
|
||||
|
||||
const typeMap = new Map([...columns, ...systemColumns].map((col) => [col.name, col.type]));
|
||||
return rows.map((row) => {
|
||||
const normalized = { ...row };
|
||||
for (const [key, value] of Object.entries(row)) {
|
||||
|
||||
Reference in New Issue
Block a user