diff --git a/packages/@n8n/api-types/src/dto/data-store/add-data-store-column.dto.ts b/packages/@n8n/api-types/src/dto/data-store/add-data-store-column.dto.ts deleted file mode 100644 index 37e089b4d53..00000000000 --- a/packages/@n8n/api-types/src/dto/data-store/add-data-store-column.dto.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { Z } from 'zod-class'; - -import { dataStoreCreateColumnSchema } from '../../schemas/data-store.schema'; - -export class AddDataStoreColumnDto extends Z.class(dataStoreCreateColumnSchema.shape) {} diff --git a/packages/@n8n/api-types/src/dto/data-store/add-data-store-rows.dto.ts b/packages/@n8n/api-types/src/dto/data-store/add-data-store-rows.dto.ts deleted file mode 100644 index 094564b05c0..00000000000 --- a/packages/@n8n/api-types/src/dto/data-store/add-data-store-rows.dto.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { z } from 'zod'; -import { Z } from 'zod-class'; - -import { - dataStoreColumnNameSchema, - dataStoreColumnValueSchema, - insertRowReturnType, -} from '../../schemas/data-store.schema'; - -export class AddDataStoreRowsDto extends Z.class({ - data: z.array(z.record(dataStoreColumnNameSchema, dataStoreColumnValueSchema)), - returnType: insertRowReturnType, -}) {} diff --git a/packages/@n8n/api-types/src/dto/data-store/create-data-store-column.dto.ts b/packages/@n8n/api-types/src/dto/data-store/create-data-store-column.dto.ts deleted file mode 100644 index 2246589ad53..00000000000 --- a/packages/@n8n/api-types/src/dto/data-store/create-data-store-column.dto.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Z } from 'zod-class'; - -import { - dataStoreColumnNameSchema, - dataStoreColumnTypeSchema, -} from '../../schemas/data-store.schema'; - -export class CreateDataStoreColumnDto extends Z.class({ - name: dataStoreColumnNameSchema, - type: dataStoreColumnTypeSchema, -}) {} diff --git a/packages/@n8n/api-types/src/dto/data-store/create-data-store.dto.ts b/packages/@n8n/api-types/src/dto/data-store/create-data-store.dto.ts deleted file mode 100644 index f6b1298ebcc..00000000000 --- a/packages/@n8n/api-types/src/dto/data-store/create-data-store.dto.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { z } from 'zod'; -import { Z } from 'zod-class'; - -import { CreateDataStoreColumnDto } from './create-data-store-column.dto'; -import { dataStoreNameSchema } from '../../schemas/data-store.schema'; - -export class CreateDataStoreDto extends Z.class({ - name: dataStoreNameSchema, - columns: z.array(CreateDataStoreColumnDto), -}) {} diff --git a/packages/@n8n/api-types/src/dto/data-store/update-data-store.dto.ts b/packages/@n8n/api-types/src/dto/data-store/update-data-store.dto.ts deleted file mode 100644 index 7821f9dd43a..00000000000 --- a/packages/@n8n/api-types/src/dto/data-store/update-data-store.dto.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { Z } from 'zod-class'; - -import { dataStoreNameSchema } from '../../schemas/data-store.schema'; - -export class UpdateDataStoreDto extends Z.class({ - name: dataStoreNameSchema, -}) {} diff --git a/packages/@n8n/api-types/src/dto/data-table/add-data-table-column.dto.ts b/packages/@n8n/api-types/src/dto/data-table/add-data-table-column.dto.ts new file mode 100644 index 00000000000..23981a9a4c2 --- /dev/null +++ b/packages/@n8n/api-types/src/dto/data-table/add-data-table-column.dto.ts @@ -0,0 +1,5 @@ +import { Z } from 'zod-class'; + +import { dataTableCreateColumnSchema } from '../../schemas/data-table.schema'; + +export class AddDataTableColumnDto extends Z.class(dataTableCreateColumnSchema.shape) {} diff --git a/packages/@n8n/api-types/src/dto/data-table/add-data-table-rows.dto.ts b/packages/@n8n/api-types/src/dto/data-table/add-data-table-rows.dto.ts new file mode 100644 index 00000000000..d8d2a984fdf --- /dev/null +++ b/packages/@n8n/api-types/src/dto/data-table/add-data-table-rows.dto.ts @@ -0,0 +1,13 @@ +import { z } from 'zod'; +import { Z } from 'zod-class'; + +import { + dataTableColumnNameSchema, + dataTableColumnValueSchema, + insertRowReturnType, +} from '../../schemas/data-table.schema'; + +export class AddDataTableRowsDto extends Z.class({ + data: z.array(z.record(dataTableColumnNameSchema, dataTableColumnValueSchema)), + returnType: insertRowReturnType, +}) {} diff --git a/packages/@n8n/api-types/src/dto/data-table/create-data-table-column.dto.ts b/packages/@n8n/api-types/src/dto/data-table/create-data-table-column.dto.ts new file mode 100644 index 00000000000..abf689cee43 --- /dev/null +++ b/packages/@n8n/api-types/src/dto/data-table/create-data-table-column.dto.ts @@ -0,0 +1,11 @@ +import { Z } from 'zod-class'; + +import { + dataTableColumnNameSchema, + dataTableColumnTypeSchema, +} from '../../schemas/data-table.schema'; + +export class CreateDataTableColumnDto extends Z.class({ + name: dataTableColumnNameSchema, + type: dataTableColumnTypeSchema, +}) {} diff --git a/packages/@n8n/api-types/src/dto/data-table/create-data-table.dto.ts b/packages/@n8n/api-types/src/dto/data-table/create-data-table.dto.ts new file mode 100644 index 00000000000..bb59069566f --- /dev/null +++ b/packages/@n8n/api-types/src/dto/data-table/create-data-table.dto.ts @@ -0,0 +1,10 @@ +import { z } from 'zod'; +import { Z } from 'zod-class'; + +import { CreateDataTableColumnDto } from './create-data-table-column.dto'; +import { dataTableNameSchema } from '../../schemas/data-table.schema'; + +export class CreateDataTableDto extends Z.class({ + name: dataTableNameSchema, + columns: z.array(CreateDataTableColumnDto), +}) {} diff --git a/packages/@n8n/api-types/src/dto/data-store/delete-data-table-rows.dto.ts b/packages/@n8n/api-types/src/dto/data-table/delete-data-table-rows.dto.ts similarity index 100% rename from packages/@n8n/api-types/src/dto/data-store/delete-data-table-rows.dto.ts rename to packages/@n8n/api-types/src/dto/data-table/delete-data-table-rows.dto.ts diff --git a/packages/@n8n/api-types/src/dto/data-store/list-data-store-content-query.dto.ts b/packages/@n8n/api-types/src/dto/data-table/list-data-table-content-query.dto.ts similarity index 90% rename from packages/@n8n/api-types/src/dto/data-store/list-data-store-content-query.dto.ts rename to packages/@n8n/api-types/src/dto/data-table/list-data-table-content-query.dto.ts index 37f1a101ca9..2229de7df39 100644 --- a/packages/@n8n/api-types/src/dto/data-store/list-data-store-content-query.dto.ts +++ b/packages/@n8n/api-types/src/dto/data-table/list-data-table-content-query.dto.ts @@ -2,8 +2,8 @@ import { jsonParse } from 'n8n-workflow'; import { z } from 'zod'; import { Z } from 'zod-class'; -import { dataStoreColumnNameSchema } from '../../schemas/data-store.schema'; import { dataTableFilterSchema } from '../../schemas/data-table-filter.schema'; +import { dataTableColumnNameSchema } from '../../schemas/data-table.schema'; import { paginationSchema } from '../pagination/pagination.dto'; const filterValidator = z @@ -51,7 +51,7 @@ const sortByValidator = z let [column, direction] = val.split(':'); try { - column = dataStoreColumnNameSchema.parse(column); + column = dataTableColumnNameSchema.parse(column); } catch { ctx.addIssue({ code: z.ZodIssueCode.custom, @@ -74,7 +74,7 @@ const sortByValidator = z return [column, direction] as const; }); -export class ListDataStoreContentQueryDto extends Z.class({ +export class ListDataTableContentQueryDto extends Z.class({ take: paginationSchema.take.optional(), skip: paginationSchema.skip.optional(), filter: filterValidator.optional(), diff --git a/packages/@n8n/api-types/src/dto/data-store/list-data-store-query.dto.ts b/packages/@n8n/api-types/src/dto/data-table/list-data-table-query.dto.ts similarity index 93% rename from packages/@n8n/api-types/src/dto/data-store/list-data-store-query.dto.ts rename to packages/@n8n/api-types/src/dto/data-table/list-data-table-query.dto.ts index 36296d46adc..6037ae8cd9b 100644 --- a/packages/@n8n/api-types/src/dto/data-store/list-data-store-query.dto.ts +++ b/packages/@n8n/api-types/src/dto/data-table/list-data-table-query.dto.ts @@ -15,7 +15,7 @@ const VALID_SORT_OPTIONS = [ 'sizeBytes:desc', ] as const; -export type ListDataStoreQuerySortOptions = (typeof VALID_SORT_OPTIONS)[number]; +export type ListDataTableQuerySortOptions = (typeof VALID_SORT_OPTIONS)[number]; const FILTER_OPTIONS = { id: z.union([z.string(), z.array(z.string())]).optional(), @@ -63,7 +63,7 @@ const sortByValidator = z .enum(VALID_SORT_OPTIONS, { message: `sortBy must be one of: ${VALID_SORT_OPTIONS.join(', ')}` }) .optional(); -export class ListDataStoreQueryDto extends Z.class({ +export class ListDataTableQueryDto extends Z.class({ ...paginationSchema, filter: filterValidator, sortBy: sortByValidator, diff --git a/packages/@n8n/api-types/src/dto/data-store/move-data-store-column.dto.ts b/packages/@n8n/api-types/src/dto/data-table/move-data-table-column.dto.ts similarity index 66% rename from packages/@n8n/api-types/src/dto/data-store/move-data-store-column.dto.ts rename to packages/@n8n/api-types/src/dto/data-table/move-data-table-column.dto.ts index 94f34a612d8..baba2c14920 100644 --- a/packages/@n8n/api-types/src/dto/data-store/move-data-store-column.dto.ts +++ b/packages/@n8n/api-types/src/dto/data-table/move-data-table-column.dto.ts @@ -1,6 +1,6 @@ import { z } from 'zod'; import { Z } from 'zod-class'; -export class MoveDataStoreColumnDto extends Z.class({ +export class MoveDataTableColumnDto extends Z.class({ targetIndex: z.number().int().nonnegative(), }) {} diff --git a/packages/@n8n/api-types/src/dto/data-store/update-data-store-row.dto.ts b/packages/@n8n/api-types/src/dto/data-table/update-data-table-row.dto.ts similarity index 79% rename from packages/@n8n/api-types/src/dto/data-store/update-data-store-row.dto.ts rename to packages/@n8n/api-types/src/dto/data-table/update-data-table-row.dto.ts index 4569f3c9ff5..37e3ad25cd6 100644 --- a/packages/@n8n/api-types/src/dto/data-store/update-data-store-row.dto.ts +++ b/packages/@n8n/api-types/src/dto/data-table/update-data-table-row.dto.ts @@ -1,11 +1,11 @@ import { z } from 'zod'; import { Z } from 'zod-class'; -import { - dataStoreColumnNameSchema, - dataStoreColumnValueSchema, -} from '../../schemas/data-store.schema'; import { dataTableFilterSchema } from '../../schemas/data-table-filter.schema'; +import { + dataTableColumnNameSchema, + dataTableColumnValueSchema, +} from '../../schemas/data-table.schema'; const updateFilterSchema = dataTableFilterSchema.refine((filter) => filter.filters.length > 0, { message: 'filter must not be empty', @@ -14,7 +14,7 @@ const updateFilterSchema = dataTableFilterSchema.refine((filter) => filter.filte const updateDataTableRowShape = { filter: updateFilterSchema, data: z - .record(dataStoreColumnNameSchema, dataStoreColumnValueSchema) + .record(dataTableColumnNameSchema, dataTableColumnValueSchema) .refine((obj) => Object.keys(obj).length > 0, { message: 'data must not be empty', }), diff --git a/packages/@n8n/api-types/src/dto/data-table/update-data-table.dto.ts b/packages/@n8n/api-types/src/dto/data-table/update-data-table.dto.ts new file mode 100644 index 00000000000..1e7227d9626 --- /dev/null +++ b/packages/@n8n/api-types/src/dto/data-table/update-data-table.dto.ts @@ -0,0 +1,7 @@ +import { Z } from 'zod-class'; + +import { dataTableNameSchema } from '../../schemas/data-table.schema'; + +export class UpdateDataTableDto extends Z.class({ + name: dataTableNameSchema, +}) {} diff --git a/packages/@n8n/api-types/src/dto/data-store/upsert-data-store-row.dto.ts b/packages/@n8n/api-types/src/dto/data-table/upsert-data-table-row.dto.ts similarity index 65% rename from packages/@n8n/api-types/src/dto/data-store/upsert-data-store-row.dto.ts rename to packages/@n8n/api-types/src/dto/data-table/upsert-data-table-row.dto.ts index c691b2c3c6d..b27d2bb6ad1 100644 --- a/packages/@n8n/api-types/src/dto/data-store/upsert-data-store-row.dto.ts +++ b/packages/@n8n/api-types/src/dto/data-table/upsert-data-table-row.dto.ts @@ -1,20 +1,20 @@ import { z } from 'zod'; import { Z } from 'zod-class'; -import { - dataStoreColumnNameSchema, - dataStoreColumnValueSchema, -} from '../../schemas/data-store.schema'; import { dataTableFilterSchema } from '../../schemas/data-table-filter.schema'; +import { + dataTableColumnNameSchema, + dataTableColumnValueSchema, +} from '../../schemas/data-table.schema'; const upsertFilterSchema = dataTableFilterSchema.refine((filter) => filter.filters.length > 0, { message: 'filter must not be empty', }); -const upsertDataStoreRowShape = { +const upsertDataTableRowShape = { filter: upsertFilterSchema, data: z - .record(dataStoreColumnNameSchema, dataStoreColumnValueSchema) + .record(dataTableColumnNameSchema, dataTableColumnValueSchema) .refine((obj) => Object.keys(obj).length > 0, { message: 'data must not be empty', }), @@ -22,4 +22,4 @@ const upsertDataStoreRowShape = { dryRun: z.boolean().optional().default(false), }; -export class UpsertDataStoreRowDto extends Z.class(upsertDataStoreRowShape) {} +export class UpsertDataTableRowDto extends Z.class(upsertDataTableRowShape) {} diff --git a/packages/@n8n/api-types/src/dto/index.ts b/packages/@n8n/api-types/src/dto/index.ts index cc604724a5d..80d524a324e 100644 --- a/packages/@n8n/api-types/src/dto/index.ts +++ b/packages/@n8n/api-types/src/dto/index.ts @@ -86,14 +86,14 @@ export { RoleGetQueryDto } from './roles/role-get-query.dto'; export { OidcConfigDto } from './oidc/config.dto'; -export { CreateDataStoreDto } from './data-store/create-data-store.dto'; -export { UpdateDataStoreDto } from './data-store/update-data-store.dto'; -export { UpdateDataTableRowDto } from './data-store/update-data-store-row.dto'; -export { DeleteDataTableRowsDto } from './data-store/delete-data-table-rows.dto'; -export { UpsertDataStoreRowDto } from './data-store/upsert-data-store-row.dto'; -export { ListDataStoreQueryDto } from './data-store/list-data-store-query.dto'; -export { ListDataStoreContentQueryDto } from './data-store/list-data-store-content-query.dto'; -export { CreateDataStoreColumnDto } from './data-store/create-data-store-column.dto'; -export { AddDataStoreRowsDto } from './data-store/add-data-store-rows.dto'; -export { AddDataStoreColumnDto } from './data-store/add-data-store-column.dto'; -export { MoveDataStoreColumnDto } from './data-store/move-data-store-column.dto'; +export { CreateDataTableDto } from './data-table/create-data-table.dto'; +export { UpdateDataTableDto } from './data-table/update-data-table.dto'; +export { UpdateDataTableRowDto } from './data-table/update-data-table-row.dto'; +export { DeleteDataTableRowsDto } from './data-table/delete-data-table-rows.dto'; +export { UpsertDataTableRowDto } from './data-table/upsert-data-table-row.dto'; +export { ListDataTableQueryDto } from './data-table/list-data-table-query.dto'; +export { ListDataTableContentQueryDto } from './data-table/list-data-table-content-query.dto'; +export { CreateDataTableColumnDto } from './data-table/create-data-table-column.dto'; +export { AddDataTableRowsDto } from './data-table/add-data-table-rows.dto'; +export { AddDataTableColumnDto } from './data-table/add-data-table-column.dto'; +export { MoveDataTableColumnDto } from './data-table/move-data-table-column.dto'; diff --git a/packages/@n8n/api-types/src/index.ts b/packages/@n8n/api-types/src/index.ts index df4cf043206..517a5bd5af0 100644 --- a/packages/@n8n/api-types/src/index.ts +++ b/packages/@n8n/api-types/src/index.ts @@ -50,17 +50,17 @@ export { } from './schemas/user.schema'; export { - DATA_STORE_COLUMN_REGEX, - DATA_STORE_COLUMN_MAX_LENGTH, - DATA_STORE_COLUMN_ERROR_MESSAGE, - type DataStore, - type DataStoreColumn, - type DataStoreCreateColumnSchema, - type DataStoreListFilter, - type DataStoreListOptions, + DATA_TABLE_COLUMN_REGEX, + DATA_TABLE_COLUMN_MAX_LENGTH, + DATA_TABLE_COLUMN_ERROR_MESSAGE, + type DataTable, + type DataTableColumn, + type DataTableCreateColumnSchema, + type DataTableListFilter, + type DataTableListOptions, dateTimeSchema, - dataStoreColumnNameSchema, -} from './schemas/data-store.schema'; + dataTableColumnNameSchema, +} from './schemas/data-table.schema'; export type { DataTableFilter, diff --git a/packages/@n8n/api-types/src/schemas/data-store.schema.ts b/packages/@n8n/api-types/src/schemas/data-store.schema.ts deleted file mode 100644 index 2a0bbf34020..00000000000 --- a/packages/@n8n/api-types/src/schemas/data-store.schema.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { z } from 'zod'; - -import type { ListDataStoreQueryDto } from '../dto'; - -export const insertRowReturnType = z.union([z.literal('all'), z.literal('count'), z.literal('id')]); - -export const dataStoreNameSchema = z.string().trim().min(1).max(128); -export const dataStoreIdSchema = z.string().max(36); - -// Postgres does not allow leading numbers or - -export const DATA_STORE_COLUMN_REGEX = /^[a-zA-Z][a-zA-Z0-9_]*$/; -export const DATA_STORE_COLUMN_MAX_LENGTH = 63; // Postgres has a maximum of 63 characters -export const DATA_STORE_COLUMN_ERROR_MESSAGE = - 'Only alphabetical characters and non-leading numbers and underscores are allowed for column names, and the maximum length is 63 characters.'; - -export const dataStoreColumnNameSchema = z - .string() - .trim() - .min(1) - .max(DATA_STORE_COLUMN_MAX_LENGTH) // Postgres has a maximum of 63 characters - .regex(DATA_STORE_COLUMN_REGEX, DATA_STORE_COLUMN_ERROR_MESSAGE); -export const dataStoreColumnTypeSchema = z.enum(['string', 'number', 'boolean', 'date']); - -export const dataStoreCreateColumnSchema = z.object({ - name: dataStoreColumnNameSchema, - type: dataStoreColumnTypeSchema, - index: z.number().optional(), -}); -export type DataStoreCreateColumnSchema = z.infer; - -export const dataStoreColumnSchema = dataStoreCreateColumnSchema.extend({ - dataStoreId: dataStoreIdSchema, -}); - -export const dataStoreSchema = z.object({ - id: dataStoreIdSchema, - name: dataStoreNameSchema, - columns: z.array(dataStoreColumnSchema), - createdAt: z.string().datetime(), - updatedAt: z.string().datetime(), -}); -export type DataStore = z.infer; -export type DataStoreColumn = z.infer; - -export type DataStoreListFilter = { - id?: string | string[]; - projectId?: string | string[]; - name?: string; -}; - -export type DataStoreListOptions = Partial & { - filter: { projectId: string }; -}; - -export const dateTimeSchema = z - .string() - .datetime({ offset: true }) - .transform((s) => new Date(s)) - .pipe(z.date()); - -export const dataStoreColumnValueSchema = z.union([ - z.string(), - z.number(), - z.boolean(), - z.null(), - z.date(), -]); diff --git a/packages/@n8n/api-types/src/schemas/data-table-filter.schema.ts b/packages/@n8n/api-types/src/schemas/data-table-filter.schema.ts index 1e8dac9463b..31d236d4a44 100644 --- a/packages/@n8n/api-types/src/schemas/data-table-filter.schema.ts +++ b/packages/@n8n/api-types/src/schemas/data-table-filter.schema.ts @@ -1,6 +1,6 @@ import { z } from 'zod'; -import { dataStoreColumnNameSchema } from './data-store.schema'; +import { dataTableColumnNameSchema } from './data-table.schema'; export const FilterConditionSchema = z.union([ z.literal('eq'), @@ -16,7 +16,7 @@ export const FilterConditionSchema = z.union([ export type DataTableFilterConditionType = z.infer; export const dataTableFilterRecordSchema = z.object({ - columnName: dataStoreColumnNameSchema, + columnName: dataTableColumnNameSchema, condition: FilterConditionSchema.default('eq'), value: z.union([z.string(), z.number(), z.boolean(), z.date(), z.null()]), }); diff --git a/packages/@n8n/api-types/src/schemas/data-table.schema.ts b/packages/@n8n/api-types/src/schemas/data-table.schema.ts new file mode 100644 index 00000000000..f9790f74730 --- /dev/null +++ b/packages/@n8n/api-types/src/schemas/data-table.schema.ts @@ -0,0 +1,67 @@ +import { z } from 'zod'; + +import type { ListDataTableQueryDto } from '../dto'; + +export const insertRowReturnType = z.union([z.literal('all'), z.literal('count'), z.literal('id')]); + +export const dataTableNameSchema = z.string().trim().min(1).max(128); +export const dataTableIdSchema = z.string().max(36); + +// Postgres does not allow leading numbers or - +export const DATA_TABLE_COLUMN_REGEX = /^[a-zA-Z][a-zA-Z0-9_]*$/; +export const DATA_TABLE_COLUMN_MAX_LENGTH = 63; // Postgres has a maximum of 63 characters +export const DATA_TABLE_COLUMN_ERROR_MESSAGE = + 'Only alphabetical characters and non-leading numbers and underscores are allowed for column names, and the maximum length is 63 characters.'; + +export const dataTableColumnNameSchema = z + .string() + .trim() + .min(1) + .max(DATA_TABLE_COLUMN_MAX_LENGTH) // Postgres has a maximum of 63 characters + .regex(DATA_TABLE_COLUMN_REGEX, DATA_TABLE_COLUMN_ERROR_MESSAGE); +export const dataTableColumnTypeSchema = z.enum(['string', 'number', 'boolean', 'date']); + +export const dataTableCreateColumnSchema = z.object({ + name: dataTableColumnNameSchema, + type: dataTableColumnTypeSchema, + index: z.number().optional(), +}); +export type DataTableCreateColumnSchema = z.infer; + +export const dataTableColumnSchema = dataTableCreateColumnSchema.extend({ + dataTableId: dataTableIdSchema, +}); + +export const dataTableSchema = z.object({ + id: dataTableIdSchema, + name: dataTableNameSchema, + columns: z.array(dataTableColumnSchema), + createdAt: z.string().datetime(), + updatedAt: z.string().datetime(), +}); +export type DataTable = z.infer; +export type DataTableColumn = z.infer; + +export type DataTableListFilter = { + id?: string | string[]; + projectId?: string | string[]; + name?: string; +}; + +export type DataTableListOptions = Partial & { + filter: { projectId: string }; +}; + +export const dateTimeSchema = z + .string() + .datetime({ offset: true }) + .transform((s) => new Date(s)) + .pipe(z.date()); + +export const dataTableColumnValueSchema = z.union([ + z.string(), + z.number(), + z.boolean(), + z.null(), + z.date(), +]); diff --git a/packages/cli/src/__tests__/workflow-execute-additional-data.test.ts b/packages/cli/src/__tests__/workflow-execute-additional-data.test.ts index 93147ead05b..db6a3edb77d 100644 --- a/packages/cli/src/__tests__/workflow-execute-additional-data.test.ts +++ b/packages/cli/src/__tests__/workflow-execute-additional-data.test.ts @@ -24,7 +24,7 @@ import { SubworkflowPolicyChecker, } from '@/executions/pre-execution-checks'; import { ExternalHooks } from '@/external-hooks'; -import { DataStoreProxyService } from '@/modules/data-table/data-store-proxy.service'; +import { DataTableProxyService } from '@/modules/data-table/data-table-proxy.service'; import { UrlService } from '@/services/url.service'; import { WorkflowStatisticsService } from '@/services/workflow-statistics.service'; import { Telemetry } from '@/telemetry'; @@ -99,7 +99,7 @@ describe('WorkflowExecuteAdditionalData', () => { mockInstance(CredentialsPermissionChecker); mockInstance(SubworkflowPolicyChecker); mockInstance(WorkflowStatisticsService); - mockInstance(DataStoreProxyService); + mockInstance(DataTableProxyService); const urlService = mockInstance(UrlService); Container.set(UrlService, urlService); diff --git a/packages/cli/src/modules/data-table/__tests__/data-store-aggregate.controller.integration.test.ts b/packages/cli/src/modules/data-table/__tests__/data-store-aggregate.controller.integration.test.ts index cd19de085b8..7f8262f058a 100644 --- a/packages/cli/src/modules/data-table/__tests__/data-store-aggregate.controller.integration.test.ts +++ b/packages/cli/src/modules/data-table/__tests__/data-store-aggregate.controller.integration.test.ts @@ -1,4 +1,4 @@ -import type { DataStore } from '@n8n/api-types'; +import type { DataTable } from '@n8n/api-types'; import { createTeamProject, getPersonalProject, @@ -8,7 +8,7 @@ import { import type { Project, User } from '@n8n/db'; import { DateTime } from 'luxon'; -import { createDataStore } from '@test-integration/db/data-stores'; +import { createDataTable } from '@test-integration/db/data-tables'; import { createOwner, createMember, createAdmin } from '@test-integration/db/users'; import type { SuperAgentTest } from '@test-integration/types'; import * as utils from '@test-integration/utils'; @@ -51,67 +51,66 @@ afterAll(async () => { }); describe('GET /data-tables-global', () => { - test('should not list data stores when no data stores exist', async () => { + test('should not list data tables when no data tables exist', async () => { const response = await authOwnerAgent.get('/data-tables-global').expect(200); expect(response.body.data.count).toBe(0); expect(response.body.data.data).toHaveLength(0); }); - test('should not list data stores from projects member has no access to', async () => { + test('should not list data tables from projects member has no access to', async () => { const project = await createTeamProject('test project', owner); - await createDataStore(project, { name: 'Test Data Store' }); - + await createDataTable(project, { name: 'Test Data Table' }); const response = await authMemberAgent.get('/data-tables-global').expect(200); expect(response.body.data.count).toBe(0); expect(response.body.data.data).toHaveLength(0); }); - test('should not list data stores from projects admin has no access to', async () => { + test('should not list data tables from projects admin has no access to', async () => { const project = await createTeamProject('test project', owner); - await createDataStore(project, { name: 'Test Data Store' }); + await createDataTable(project, { name: 'Test Data Table' }); const response = await authAdminAgent.get('/data-tables-global').expect(200); expect(response.body.data.count).toBe(0); expect(response.body.data.data).toHaveLength(0); }); - test("should not list data stores from another user's personal project", async () => { - await createDataStore(ownerProject, { name: 'Personal Data Store' }); + test("should not list data tables from another user's personal project", async () => { + await createDataTable(ownerProject, { name: 'Personal Data Table' }); const response = await authAdminAgent.get('/data-tables-global').expect(200); expect(response.body.data.count).toBe(0); expect(response.body.data.data).toHaveLength(0); }); - test('should list data stores from team projects where user has project:viewer role', async () => { + test('should list data tables from team projects where user has project:viewer role', async () => { const project = await createTeamProject('test project', owner); await linkUserToProject(member, project, 'project:viewer'); - await createDataStore(project, { name: 'Test Data Store' }); + await createDataTable(project, { name: 'Test Data Table' }); const response = await authMemberAgent.get('/data-tables-global').expect(200); expect(response.body.data.count).toBe(1); expect(response.body.data.data).toHaveLength(1); - expect(response.body.data.data[0].name).toBe('Test Data Store'); + expect(response.body.data.data[0].name).toBe('Test Data Table'); }); - test("should list data stores from user's own personal project", async () => { - await createDataStore(ownerProject, { name: 'Personal Data Store 1' }); - await createDataStore(ownerProject, { name: 'Personal Data Store 2' }); + test("should list data tables from user's own personal project", async () => { + await createDataTable(ownerProject, { name: 'Personal Data Table 1' }); + await createDataTable(ownerProject, { name: 'Personal Data Table 2' }); const response = await authOwnerAgent.get('/data-tables-global').expect(200); expect(response.body.data.count).toBe(2); expect(response.body.data.data).toHaveLength(2); - expect(response.body.data.data.map((f: DataStore) => f.name).sort()).toEqual( - ['Personal Data Store 1', 'Personal Data Store 2'].sort(), + expect(response.body.data.data.map((f: DataTable) => f.name).sort()).toEqual( + ['Personal Data Table 1', 'Personal Data Table 2'].sort(), ); }); - test('should filter data stores by projectId', async () => { - await createDataStore(ownerProject, { name: 'Test Data Store 1' }); - await createDataStore(ownerProject, { name: 'Test Data Store 2' }); - await createDataStore(memberProject, { name: 'Another Data Store' }); + test('should filter data tables by projectId', async () => { + await createDataTable(ownerProject, { name: 'Test Data Table 1' }); + await createDataTable(ownerProject, { name: 'Test Data Table 2' }); + await createDataTable(memberProject, { name: 'Another Data Table' }); const response = await authOwnerAgent .get('/data-tables-global') @@ -120,15 +119,15 @@ describe('GET /data-tables-global', () => { expect(response.body.data.count).toBe(2); expect(response.body.data.data).toHaveLength(2); - expect(response.body.data.data.map((f: DataStore) => f.name).sort()).toEqual( - ['Test Data Store 1', 'Test Data Store 2'].sort(), + expect(response.body.data.data.map((f: DataTable) => f.name).sort()).toEqual( + ['Test Data Table 1', 'Test Data Table 2'].sort(), ); }); test('should not list projects the user cant access even with project filters', async () => { - await createDataStore(ownerProject, { name: 'Test Data Store 1' }); - await createDataStore(ownerProject, { name: 'Test Data Store 2' }); - await createDataStore(memberProject, { name: 'Another Data Store' }); + await createDataTable(ownerProject, { name: 'Test Data Table 1' }); + await createDataTable(ownerProject, { name: 'Test Data Table 2' }); + await createDataTable(memberProject, { name: 'Another Data Table' }); const response = await authMemberAgent .get('/data-tables-global') @@ -139,12 +138,12 @@ describe('GET /data-tables-global', () => { expect(response.body.data.data).toHaveLength(0); }); - test('should filter data stores by name', async () => { + test('should filter data tables by name', async () => { const project = await createTeamProject('test project', owner); - await createDataStore(ownerProject, { name: 'Test Data Store' }); - await createDataStore(ownerProject, { name: 'Another Data Store' }); - await createDataStore(project, { name: 'Test Something Else' }); + await createDataTable(ownerProject, { name: 'Test Data Table' }); + await createDataTable(ownerProject, { name: 'Another Data Table' }); + await createDataTable(project, { name: 'Test Something Else' }); const response = await authOwnerAgent .get('/data-tables-global') @@ -154,14 +153,14 @@ describe('GET /data-tables-global', () => { 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( - ['Test Data Store', 'Test Something Else'].sort(), + ['Test Data Table', 'Test Something Else'].sort(), ); }); - test('should filter data stores by id', async () => { - const dataStore1 = await createDataStore(ownerProject, { name: 'Data Store 1' }); - await createDataStore(ownerProject, { name: 'Data Store 2' }); - await createDataStore(ownerProject, { name: 'Data Store 3' }); + test('should filter data tables by id', async () => { + const dataStore1 = await createDataTable(ownerProject, { name: 'Data Table 1' }); + await createDataTable(ownerProject, { name: 'Data Table 2' }); + await createDataTable(ownerProject, { name: 'Data Table 3' }); const response = await authOwnerAgent .get('/data-tables-global') @@ -170,15 +169,15 @@ describe('GET /data-tables-global', () => { expect(response.body.data.count).toBe(1); expect(response.body.data.data).toHaveLength(1); - expect(response.body.data.data[0].name).toBe('Data Store 1'); + expect(response.body.data.data[0].name).toBe('Data Table 1'); }); - test('should filter data stores by multiple names (AND operator)', async () => { + test('should filter data tables by multiple names (AND operator)', async () => { const project = await createTeamProject('test project', owner); - await createDataStore(ownerProject, { name: 'Data Store' }); - await createDataStore(ownerProject, { name: 'Test Store' }); - await createDataStore(project, { name: 'Another Store' }); + await createDataTable(ownerProject, { name: 'Data Table' }); + await createDataTable(ownerProject, { name: 'Test Store' }); + await createDataTable(project, { name: 'Another Store' }); const response = await authOwnerAgent .get('/data-tables-global') @@ -193,8 +192,8 @@ describe('GET /data-tables-global', () => { test('should apply pagination with take parameter', async () => { const project = await createTeamProject('test project', owner); for (let i = 1; i <= 5; i++) { - await createDataStore(i % 2 ? ownerProject : project, { - name: `Data Store ${i}`, + await createDataTable(i % 2 ? ownerProject : project, { + name: `Data Table ${i}`, updatedAt: DateTime.now() .minus({ minutes: 6 - i }) .toJSDate(), @@ -205,18 +204,18 @@ describe('GET /data-tables-global', () => { 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([ - 'Data Store 5', - 'Data Store 4', - 'Data Store 3', + expect(response.body.data.data.map((store: DataTable) => store.name)).toEqual([ + 'Data Table 5', + 'Data Table 4', + 'Data Table 3', ]); }); test('should apply pagination with skip parameter', async () => { const project = await createTeamProject('test project', owner); for (let i = 1; i <= 5; i++) { - await createDataStore(i % 2 ? ownerProject : project, { - name: `Data Store ${i}`, + await createDataTable(i % 2 ? ownerProject : project, { + name: `Data Table ${i}`, updatedAt: DateTime.now() .minus({ minutes: 6 - i }) .toJSDate(), @@ -227,18 +226,18 @@ describe('GET /data-tables-global', () => { 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([ - 'Data Store 3', - 'Data Store 2', - 'Data Store 1', + expect(response.body.data.data.map((store: DataTable) => store.name)).toEqual([ + 'Data Table 3', + 'Data Table 2', + 'Data Table 1', ]); }); test('should apply combined skip and take parameters', async () => { const project = await createTeamProject('test project', owner); for (let i = 1; i <= 5; i++) { - await createDataStore(i % 2 ? ownerProject : project, { - name: `Data Store ${i}`, + await createDataTable(i % 2 ? ownerProject : project, { + name: `Data Table ${i}`, updatedAt: DateTime.now() .minus({ minutes: 6 - i }) .toJSDate(), @@ -252,57 +251,57 @@ describe('GET /data-tables-global', () => { 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([ - 'Data Store 4', - 'Data Store 3', + expect(response.body.data.data.map((store: DataTable) => store.name)).toEqual([ + 'Data Table 4', + 'Data Table 3', ]); }); - test('should sort data stores by name ascending', async () => { - await createDataStore(ownerProject, { name: 'Z Data Store' }); - await createDataStore(ownerProject, { name: 'A Data Store' }); - await createDataStore(ownerProject, { name: 'M Data Store' }); + test('should sort data tables by name ascending', async () => { + await createDataTable(ownerProject, { name: 'Z Data Table' }); + await createDataTable(ownerProject, { name: 'A Data Table' }); + await createDataTable(ownerProject, { name: 'M Data Table' }); const response = await authOwnerAgent .get('/data-tables-global') .query({ sortBy: 'name:asc' }) .expect(200); - expect(response.body.data.data.map((store: DataStore) => store.name)).toEqual([ - 'A Data Store', - 'M Data Store', - 'Z Data Store', + expect(response.body.data.data.map((store: DataTable) => store.name)).toEqual([ + 'A Data Table', + 'M Data Table', + 'Z Data Table', ]); }); - test('should sort data stores by name descending', async () => { - await createDataStore(ownerProject, { name: 'Z Data Store' }); - await createDataStore(ownerProject, { name: 'A Data Store' }); - await createDataStore(ownerProject, { name: 'M Data Store' }); + test('should sort data tables by name descending', async () => { + await createDataTable(ownerProject, { name: 'Z Data Table' }); + await createDataTable(ownerProject, { name: 'A Data Table' }); + await createDataTable(ownerProject, { name: 'M Data Table' }); const response = await authOwnerAgent .get('/data-tables-global') .query({ sortBy: 'name:desc' }) .expect(200); - expect(response.body.data.data.map((f: DataStore) => f.name)).toEqual([ - 'Z Data Store', - 'M Data Store', - 'A Data Store', + expect(response.body.data.data.map((f: DataTable) => f.name)).toEqual([ + 'Z Data Table', + 'M Data Table', + 'A Data Table', ]); }); - test('should sort data stores by updatedAt', async () => { - await createDataStore(ownerProject, { - name: 'Older Data Store', + test('should sort data tables by updatedAt', async () => { + await createDataTable(ownerProject, { + name: 'Older Data Table', updatedAt: DateTime.now().minus({ days: 2 }).toJSDate(), }); - await createDataStore(ownerProject, { - name: 'Newest Data Store', + await createDataTable(ownerProject, { + name: 'Newest Data Table', updatedAt: DateTime.now().toJSDate(), }); - await createDataStore(ownerProject, { - name: 'Middle Data Store', + await createDataTable(ownerProject, { + name: 'Middle Data Table', updatedAt: DateTime.now().minus({ days: 1 }).toJSDate(), }); @@ -311,16 +310,16 @@ describe('GET /data-tables-global', () => { .query({ sortBy: 'updatedAt:desc' }) .expect(200); - expect(response.body.data.data.map((f: DataStore) => f.name)).toEqual([ - 'Newest Data Store', - 'Middle Data Store', - 'Older Data Store', + expect(response.body.data.data.map((f: DataTable) => f.name)).toEqual([ + 'Newest Data Table', + 'Middle Data Table', + 'Older Data Table', ]); }); test('should combine multiple query parameters correctly', async () => { - const dataStore1 = await createDataStore(ownerProject, { name: 'Test Data Store' }); - await createDataStore(ownerProject, { name: 'Another Data Store' }); + const dataStore1 = await createDataTable(ownerProject, { name: 'Test Data Table' }); + await createDataTable(ownerProject, { name: 'Another Data Table' }); const response = await authOwnerAgent .get('/data-tables-global') @@ -329,12 +328,12 @@ describe('GET /data-tables-global', () => { expect(response.body.data.count).toBe(1); expect(response.body.data.data).toHaveLength(1); - expect(response.body.data.data[0].name).toBe('Test Data Store'); + expect(response.body.data.data[0].name).toBe('Test Data Table'); }); test('should include columns', async () => { - await createDataStore(ownerProject, { - name: 'Test Data Store', + await createDataTable(ownerProject, { + name: 'Test Data Table', columns: [ { name: 'test_column_1', diff --git a/packages/cli/src/modules/data-table/__tests__/data-store-aggregate.service.integration.test.ts b/packages/cli/src/modules/data-table/__tests__/data-store-aggregate.service.integration.test.ts index a0f13d47744..740bba67dde 100644 --- a/packages/cli/src/modules/data-table/__tests__/data-store-aggregate.service.integration.test.ts +++ b/packages/cli/src/modules/data-table/__tests__/data-store-aggregate.service.integration.test.ts @@ -14,8 +14,8 @@ import { mock } from 'jest-mock-extended'; import { createUser } from '@test-integration/db/users'; -import { DataStoreAggregateService } from '../data-store-aggregate.service'; -import { DataStoreService } from '../data-store.service'; +import { DataTableAggregateService } from '../data-table-aggregate.service'; +import { DataTableService } from '../data-table.service'; beforeAll(async () => { await testModules.loadModules(['data-table']); @@ -31,15 +31,15 @@ afterAll(async () => { }); describe('dataStoreAggregate', () => { - let dataStoreService: DataStoreService; - let dataStoreAggregateService: DataStoreAggregateService; + let dataStoreService: DataTableService; + let dataStoreAggregateService: DataTableAggregateService; const manager = mock(); const projectRelationRepository = mock({ manager }); beforeAll(() => { Container.set(ProjectRelationRepository, projectRelationRepository); - dataStoreAggregateService = Container.get(DataStoreAggregateService); - dataStoreService = Container.get(DataStoreService); + dataStoreAggregateService = Container.get(DataTableAggregateService); + dataStoreService = Container.get(DataTableService); }); let user: User; @@ -53,18 +53,18 @@ describe('dataStoreAggregate', () => { }); afterEach(async () => { - // Clean up any created user data stores - await dataStoreService.deleteDataStoreAll(); + // Clean up any created user data tables + await dataStoreService.deleteDataTableAll(); }); describe('getManyAndCount', () => { - it('should return the correct data stores for the user', async () => { + it('should return the correct data tables for the user', async () => { // ARRANGE - const ds1 = await dataStoreService.createDataStore(project1.id, { + const ds1 = await dataStoreService.createDataTable(project1.id, { name: 'store1', columns: [], }); - const ds2 = await dataStoreService.createDataStore(project1.id, { + const ds2 = await dataStoreService.createDataTable(project1.id, { name: 'store2', columns: [], }); @@ -92,7 +92,7 @@ describe('dataStoreAggregate', () => { }, ]); - await dataStoreService.createDataStore(project2.id, { + await dataStoreService.createDataTable(project2.id, { name: 'store3', columns: [], }); @@ -118,7 +118,7 @@ describe('dataStoreAggregate', () => { // ARRANGE const currentUser = await createUser({ role: GLOBAL_MEMBER_ROLE }); - await dataStoreService.createDataStore(project1.id, { + await dataStoreService.createDataTable(project1.id, { name: 'store1', columns: [], }); @@ -135,13 +135,13 @@ describe('dataStoreAggregate', () => { expect(result.count).toBe(0); }); - it('should return only the data store matching the given data store id filter', async () => { + it('should return only the data table matching the given data table id filter', async () => { // ARRANGE - await dataStoreService.createDataStore(project1.id, { + await dataStoreService.createDataTable(project1.id, { name: 'store1', columns: [], }); - const ds2 = await dataStoreService.createDataStore(project1.id, { + const ds2 = await dataStoreService.createDataTable(project1.id, { name: 'store2', columns: [], }); @@ -182,15 +182,15 @@ describe('dataStoreAggregate', () => { it('should respect pagination (skip/take)', async () => { // ARRANGE - const ds1 = await dataStoreService.createDataStore(project1.id, { + const ds1 = await dataStoreService.createDataTable(project1.id, { name: 'store1', columns: [], }); - const ds2 = await dataStoreService.createDataStore(project1.id, { + const ds2 = await dataStoreService.createDataTable(project1.id, { name: 'store2', columns: [], }); - const ds3 = await dataStoreService.createDataStore(project1.id, { + const ds3 = await dataStoreService.createDataTable(project1.id, { name: 'store3', columns: [], }); diff --git a/packages/cli/src/modules/data-table/__tests__/data-store-filters.integration.test.ts b/packages/cli/src/modules/data-table/__tests__/data-store-filters.integration.test.ts index 4378383a85f..fd4cf12654a 100644 --- a/packages/cli/src/modules/data-table/__tests__/data-store-filters.integration.test.ts +++ b/packages/cli/src/modules/data-table/__tests__/data-store-filters.integration.test.ts @@ -4,14 +4,14 @@ import { createTeamProject, testDb, testModules } from '@n8n/backend-test-utils' import { Project } from '@n8n/db'; import { Container } from '@n8n/di'; -import { mockDataStoreSizeValidator } from './test-helpers'; -import { DataStoreService } from '../data-store.service'; -import { DataStoreValidationError } from '../errors/data-store-validation.error'; +import { mockDataTableSizeValidator } from './test-helpers'; +import { DataTableService } from '../data-table.service'; +import { DataTableValidationError } from '../errors/data-table-validation.error'; beforeAll(async () => { await testModules.loadModules(['data-table']); await testDb.init(); - mockDataStoreSizeValidator(); + mockDataTableSizeValidator(); }); beforeEach(async () => { @@ -23,10 +23,10 @@ afterAll(async () => { }); describe('dataStore filters', () => { - let dataStoreService: DataStoreService; + let dataTableService: DataTableService; beforeAll(() => { - dataStoreService = Container.get(DataStoreService); + dataTableService = Container.get(DataTableService); }); let project: Project; @@ -36,20 +36,20 @@ describe('dataStore filters', () => { }); afterEach(async () => { - // Clean up any created user data stores - await dataStoreService.deleteDataStoreAll(); + // Clean up any created user data tables + await dataTableService.deleteDataTableAll(); }); describe('getManyAndCount', () => { it('should retrieve by name', async () => { // ARRANGE - const dataStore = await dataStoreService.createDataStore(project.id, { + const dataStore = await dataTableService.createDataTable(project.id, { name: 'dataStore', columns: [], }); // ACT - const result = await dataStoreService.getManyAndCount({ + const result = await dataTableService.getManyAndCount({ filter: { projectId: project.id, name: dataStore.name }, }); @@ -70,18 +70,18 @@ describe('dataStore filters', () => { it('should retrieve by ids', async () => { // ARRANGE - const dataStore1 = await dataStoreService.createDataStore(project.id, { - name: 'myDataStore1', + const dataStore1 = await dataTableService.createDataTable(project.id, { + name: 'myDataTable1', columns: [], }); - const dataStore2 = await dataStoreService.createDataStore(project.id, { - name: 'myDataStore2', + const dataStore2 = await dataTableService.createDataTable(project.id, { + name: 'myDataTable2', columns: [], }); // ACT - const result = await dataStoreService.getManyAndCount({ + const result = await dataTableService.getManyAndCount({ filter: { projectId: project.id, id: [dataStore1.id, dataStore2.id] }, }); @@ -100,21 +100,21 @@ describe('dataStore filters', () => { it('should retrieve by projectId', async () => { // ARRANGE - const dataStore = await dataStoreService.createDataStore(project.id, { - name: 'myDataStore', + const dataStore = await dataTableService.createDataTable(project.id, { + name: 'myDataTable', columns: [], }); const names = [dataStore.name]; for (let i = 0; i < 10; ++i) { - const ds = await dataStoreService.createDataStore(project.id, { - name: `anotherDataStore${i}`, + const ds = await dataTableService.createDataTable(project.id, { + name: `anotherDataTable${i}`, columns: [], }); names.push(ds.name); } // ACT - const result = await dataStoreService.getManyAndCount({ + const result = await dataTableService.getManyAndCount({ filter: { projectId: project.id }, }); @@ -125,32 +125,32 @@ describe('dataStore filters', () => { it('should retrieve by id with pagination', async () => { // ARRANGE - const dataStore = await dataStoreService.createDataStore(project.id, { - name: 'myDataStore', + const dataStore = await dataTableService.createDataTable(project.id, { + name: 'myDataTable', columns: [], }); const names = [dataStore.name]; for (let i = 0; i < 10; ++i) { - const ds = await dataStoreService.createDataStore(project.id, { - name: `anotherDataStore${i}`, + const ds = await dataTableService.createDataTable(project.id, { + name: `anotherDataTable${i}`, columns: [], }); names.push(ds.name); } // ACT - const p0 = await dataStoreService.getManyAndCount({ + const p0 = await dataTableService.getManyAndCount({ filter: { projectId: project.id }, skip: 0, take: 3, }); - const p1 = await dataStoreService.getManyAndCount({ + const p1 = await dataTableService.getManyAndCount({ filter: { projectId: project.id }, skip: 3, take: 3, }); - const rest = await dataStoreService.getManyAndCount({ + const rest = await dataTableService.getManyAndCount({ filter: { projectId: project.id }, skip: 6, take: 10, @@ -180,7 +180,7 @@ describe('dataStore filters', () => { describe('equals and not equals filters', () => { it("retrieves rows with 'equals' filter correctly", async () => { // ARRANGE - const { id: dataStoreId } = await dataStoreService.createDataStore(project.id, { + const { id: dataTableId } = await dataTableService.createDataTable(project.id, { name: 'dataStore', columns: [ { name: 'name', type: 'string' }, @@ -198,10 +198,10 @@ describe('dataStore filters', () => { { name: 'Jack', age: 35, birthday: new Date('1988-12-05T00:00:00.000Z'), isActive: true }, ]; - await dataStoreService.insertRows(dataStoreId, project.id, rows); + await dataTableService.insertRows(dataTableId, project.id, rows); // ACT - const result = await dataStoreService.getManyRowsAndCount(dataStoreId, project.id, { + const result = await dataTableService.getManyRowsAndCount(dataTableId, project.id, { filter: { type: 'and', filters: [ @@ -227,7 +227,7 @@ describe('dataStore filters', () => { it("retrieves rows with 'not equals' filter correctly", async () => { // ARRANGE - const { id: dataStoreId } = await dataStoreService.createDataStore(project.id, { + const { id: dataTableId } = await dataTableService.createDataTable(project.id, { name: 'dataStore', columns: [ { name: 'name', type: 'string' }, @@ -241,10 +241,10 @@ describe('dataStore filters', () => { { name: 'Jack', age: 35 }, ]; - await dataStoreService.insertRows(dataStoreId, project.id, rows); + await dataTableService.insertRows(dataTableId, project.id, rows); // ACT - const result = await dataStoreService.getManyRowsAndCount(dataStoreId, project.id, { + const result = await dataTableService.getManyRowsAndCount(dataTableId, project.id, { filter: { type: 'and', filters: [{ columnName: 'name', value: 'Mary', condition: 'neq' }], @@ -261,7 +261,7 @@ describe('dataStore filters', () => { it('supports filter by null', async () => { // ARRANGE - const { id: dataStoreId } = await dataStoreService.createDataStore(project.id, { + const { id: dataTableId } = await dataTableService.createDataTable(project.id, { name: 'dataStore', columns: [ { name: 'c1', type: 'string' }, @@ -276,10 +276,10 @@ describe('dataStore filters', () => { { c1: 'Polo', c2: false }, ]; - await dataStoreService.insertRows(dataStoreId, project.id, rows); + await dataTableService.insertRows(dataTableId, project.id, rows); // ACT - const result = await dataStoreService.getManyRowsAndCount(dataStoreId, project.id, { + const result = await dataTableService.getManyRowsAndCount(dataTableId, project.id, { filter: { type: 'and', filters: [{ columnName: 'c1', condition: 'eq', value: null }], @@ -296,7 +296,7 @@ describe('dataStore filters', () => { it('supports filter by not null', async () => { // ARRANGE - const { id: dataStoreId } = await dataStoreService.createDataStore(project.id, { + const { id: dataTableId } = await dataTableService.createDataTable(project.id, { name: 'dataStore', columns: [ { name: 'c1', type: 'string' }, @@ -311,10 +311,10 @@ describe('dataStore filters', () => { { c1: 'Polo', c2: false }, ]; - await dataStoreService.insertRows(dataStoreId, project.id, rows); + await dataTableService.insertRows(dataTableId, project.id, rows); // ACT - const result = await dataStoreService.getManyRowsAndCount(dataStoreId, project.id, { + const result = await dataTableService.getManyRowsAndCount(dataTableId, project.id, { filter: { type: 'and', filters: [{ columnName: 'c1', condition: 'neq', value: null }], @@ -331,7 +331,7 @@ describe('dataStore filters', () => { it('includes null values when using neq with specific value', async () => { // ARRANGE - const { id: dataStoreId } = await dataStoreService.createDataStore(project.id, { + const { id: dataTableId } = await dataTableService.createDataTable(project.id, { name: 'dataStore', columns: [ { name: 'name', type: 'string' }, @@ -347,10 +347,10 @@ describe('dataStore filters', () => { { name: 'Bob', category: null }, ]; - await dataStoreService.insertRows(dataStoreId, project.id, rows); + await dataTableService.insertRows(dataTableId, project.id, rows); // ACT - const result = await dataStoreService.getManyRowsAndCount(dataStoreId, project.id, { + const result = await dataTableService.getManyRowsAndCount(dataTableId, project.id, { filter: { type: 'and', filters: [{ columnName: 'category', condition: 'neq', value: 'A' }], @@ -369,15 +369,15 @@ describe('dataStore filters', () => { }); it('should accept a valid numeric string', async () => { - const { id: dataStoreId } = await dataStoreService.createDataStore(project.id, { + const { id: dataTableId } = await dataTableService.createDataTable(project.id, { name: 'dataStore', columns: [{ name: 'age', type: 'number' }], }); - await dataStoreService.insertRows(dataStoreId, project.id, [{ age: null }, { age: 30 }]); + await dataTableService.insertRows(dataTableId, project.id, [{ age: null }, { age: 30 }]); // ACT - const result = await dataStoreService.getManyRowsAndCount(dataStoreId, project.id, { + const result = await dataTableService.getManyRowsAndCount(dataTableId, project.id, { filter: { type: 'and', filters: [{ columnName: 'age', condition: 'eq', value: '30' }], @@ -390,15 +390,15 @@ describe('dataStore filters', () => { }); it('should throw on invalid numeric string', async () => { - const { id: dataStoreId } = await dataStoreService.createDataStore(project.id, { + const { id: dataTableId } = await dataTableService.createDataTable(project.id, { name: 'dataStore', columns: [{ name: 'age', type: 'number' }], }); - await dataStoreService.insertRows(dataStoreId, project.id, [{ age: null }, { age: 30 }]); + await dataTableService.insertRows(dataTableId, project.id, [{ age: null }, { age: 30 }]); // ACT - const result = dataStoreService.getManyRowsAndCount(dataStoreId, project.id, { + const result = dataTableService.getManyRowsAndCount(dataTableId, project.id, { filter: { type: 'and', filters: [{ columnName: 'age', condition: 'eq', value: '30dfddf' }], @@ -406,7 +406,7 @@ describe('dataStore filters', () => { }); // ASSERT - await expect(result).rejects.toThrow(DataStoreValidationError); + await expect(result).rejects.toThrow(DataTableValidationError); await expect(result).rejects.toThrow("value '30dfddf' does not match column type 'number'"); }); }); @@ -414,7 +414,7 @@ describe('dataStore filters', () => { describe('LIKE filters', () => { it("retrieves rows with 'contains sensitive' filter correctly", async () => { // ARRANGE - const { id: dataStoreId } = await dataStoreService.createDataStore(project.id, { + const { id: dataTableId } = await dataTableService.createDataTable(project.id, { name: 'dataStore', columns: [ { name: 'name', type: 'string' }, @@ -428,10 +428,10 @@ describe('dataStore filters', () => { { name: 'Charlie', age: 35 }, ]; - await dataStoreService.insertRows(dataStoreId, project.id, rows); + await dataTableService.insertRows(dataTableId, project.id, rows); // ACT - const result = await dataStoreService.getManyRowsAndCount(dataStoreId, project.id, { + const result = await dataTableService.getManyRowsAndCount(dataTableId, project.id, { filter: { type: 'and', filters: [{ columnName: 'name', value: '%ar%', condition: 'like' }], @@ -448,7 +448,7 @@ describe('dataStore filters', () => { it("retrieves rows with 'contains insensitive' filter correctly", async () => { // ARRANGE - const { id: dataStoreId } = await dataStoreService.createDataStore(project.id, { + const { id: dataTableId } = await dataTableService.createDataTable(project.id, { name: 'dataStore', columns: [ { name: 'name', type: 'string' }, @@ -463,10 +463,10 @@ describe('dataStore filters', () => { { name: 'Taj', age: 35 }, ]; - await dataStoreService.insertRows(dataStoreId, project.id, rows); + await dataTableService.insertRows(dataTableId, project.id, rows); // ACT - const result = await dataStoreService.getManyRowsAndCount(dataStoreId, project.id, { + const result = await dataTableService.getManyRowsAndCount(dataTableId, project.id, { filter: { type: 'and', filters: [{ columnName: 'name', value: '%J%', condition: 'ilike' }], @@ -484,7 +484,7 @@ describe('dataStore filters', () => { it("retrieves rows with 'starts with' filter correctly", async () => { // ARRANGE - const { id: dataStoreId } = await dataStoreService.createDataStore(project.id, { + const { id: dataTableId } = await dataTableService.createDataTable(project.id, { name: 'dataStore', columns: [ { name: 'name', type: 'string' }, @@ -498,10 +498,10 @@ describe('dataStore filters', () => { { name: 'Charlie', age: 35 }, ]; - await dataStoreService.insertRows(dataStoreId, project.id, rows); + await dataTableService.insertRows(dataTableId, project.id, rows); // ACT - const result = await dataStoreService.getManyRowsAndCount(dataStoreId, project.id, { + const result = await dataTableService.getManyRowsAndCount(dataTableId, project.id, { filter: { type: 'and', filters: [{ columnName: 'name', value: 'Ar%', condition: 'ilike' }], @@ -515,7 +515,7 @@ describe('dataStore filters', () => { it("retrieves rows with 'ends with' filter correctly", async () => { // ARRANGE - const { id: dataStoreId } = await dataStoreService.createDataStore(project.id, { + const { id: dataTableId } = await dataTableService.createDataTable(project.id, { name: 'dataStore', columns: [ { name: 'name', type: 'string' }, @@ -530,10 +530,10 @@ describe('dataStore filters', () => { { name: 'Harold', age: 40 }, ]; - await dataStoreService.insertRows(dataStoreId, project.id, rows); + await dataTableService.insertRows(dataTableId, project.id, rows); // ACT - const result = await dataStoreService.getManyRowsAndCount(dataStoreId, project.id, { + const result = await dataTableService.getManyRowsAndCount(dataTableId, project.id, { filter: { type: 'and', filters: [{ columnName: 'name', value: '%old', condition: 'ilike' }], @@ -553,7 +553,7 @@ describe('dataStore filters', () => { (condition) => { it(`throws error when '${condition}' filter value is null`, async () => { // ARRANGE - const { id: dataStoreId } = await dataStoreService.createDataStore(project.id, { + const { id: dataTableId } = await dataTableService.createDataTable(project.id, { name: 'dataStore', columns: [ { name: 'name', type: 'string' }, @@ -566,10 +566,10 @@ describe('dataStore filters', () => { { name: 'Mary', age: 25 }, ]; - await dataStoreService.insertRows(dataStoreId, project.id, rows); + await dataTableService.insertRows(dataTableId, project.id, rows); // ACT - const result = dataStoreService.getManyRowsAndCount(dataStoreId, project.id, { + const result = dataTableService.getManyRowsAndCount(dataTableId, project.id, { filter: { type: 'and', filters: [{ columnName: 'name', value: null, condition }], @@ -578,7 +578,7 @@ describe('dataStore filters', () => { // ASSERT await expect(result).rejects.toThrow( - new DataStoreValidationError( + new DataTableValidationError( `${condition.toUpperCase()} filter value cannot be null or undefined`, ), ); @@ -586,7 +586,7 @@ describe('dataStore filters', () => { it(`throws error when '${condition}' filter value is not a string`, async () => { // ARRANGE - const { id: dataStoreId } = await dataStoreService.createDataStore(project.id, { + const { id: dataTableId } = await dataTableService.createDataTable(project.id, { name: 'dataStore', columns: [ { name: 'name', type: 'string' }, @@ -599,10 +599,10 @@ describe('dataStore filters', () => { { name: 'Mary', age: 25 }, ]; - await dataStoreService.insertRows(dataStoreId, project.id, rows); + await dataTableService.insertRows(dataTableId, project.id, rows); // ACT - const result = dataStoreService.getManyRowsAndCount(dataStoreId, project.id, { + const result = dataTableService.getManyRowsAndCount(dataTableId, project.id, { filter: { type: 'and', filters: [{ columnName: 'age', value: 123, condition }], @@ -611,7 +611,7 @@ describe('dataStore filters', () => { // ASSERT await expect(result).rejects.toThrow( - new DataStoreValidationError( + new DataTableValidationError( `${condition.toUpperCase()} filter value must be a string`, ), ); @@ -620,19 +620,19 @@ describe('dataStore filters', () => { ); describe('like filter with special characters', () => { - let dataStoreId: string; + let dataTableId: string; beforeEach(async () => { - const { id } = await dataStoreService.createDataStore(project.id, { + const { id } = await dataTableService.createDataTable(project.id, { name: 'dataStore', columns: [{ name: 'text', type: 'string' }], }); - dataStoreId = id; + dataTableId = id; }); it('should treat square brackets literally in like patterns', async () => { // ARRANGE - await dataStoreService.insertRows(dataStoreId, project.id, [ + await dataTableService.insertRows(dataTableId, project.id, [ { text: 'test[abc]data' }, { text: 'Test[abc]Data' }, { text: 'testAdata' }, @@ -640,7 +640,7 @@ describe('dataStore filters', () => { ]); // ACT - const result = await dataStoreService.getManyRowsAndCount(dataStoreId, project.id, { + const result = await dataTableService.getManyRowsAndCount(dataTableId, project.id, { filter: { type: 'and', filters: [{ columnName: 'text', value: 'test%[abc]%', condition: 'like' }], @@ -654,7 +654,7 @@ describe('dataStore filters', () => { it('should treat asterisk literally in like patterns', async () => { // ARRANGE - await dataStoreService.insertRows(dataStoreId, project.id, [ + await dataTableService.insertRows(dataTableId, project.id, [ { text: 'test*data' }, { text: 'Test*Data' }, { text: 'testAdata' }, @@ -662,7 +662,7 @@ describe('dataStore filters', () => { ]); // ACT - const result = await dataStoreService.getManyRowsAndCount(dataStoreId, project.id, { + const result = await dataTableService.getManyRowsAndCount(dataTableId, project.id, { filter: { type: 'and', filters: [{ columnName: 'text', value: 'test%*%', condition: 'like' }], @@ -676,7 +676,7 @@ describe('dataStore filters', () => { it('should treat question mark literally in like patterns', async () => { // ARRANGE - await dataStoreService.insertRows(dataStoreId, project.id, [ + await dataTableService.insertRows(dataTableId, project.id, [ { text: 'test?data' }, { text: 'Test?Data' }, { text: 'testAdata' }, @@ -684,7 +684,7 @@ describe('dataStore filters', () => { ]); // ACT - const result = await dataStoreService.getManyRowsAndCount(dataStoreId, project.id, { + const result = await dataTableService.getManyRowsAndCount(dataTableId, project.id, { filter: { type: 'and', filters: [{ columnName: 'text', value: 'test%?%', condition: 'like' }], @@ -698,7 +698,7 @@ describe('dataStore filters', () => { it('should convert LIKE % wildcard to match zero or more characters', async () => { // ARRANGE - await dataStoreService.insertRows(dataStoreId, project.id, [ + await dataTableService.insertRows(dataTableId, project.id, [ { text: 'data%more' }, { text: 'Data%More' }, { text: 'datamore' }, @@ -707,7 +707,7 @@ describe('dataStore filters', () => { ]); // ACT - const result = await dataStoreService.getManyRowsAndCount(dataStoreId, project.id, { + const result = await dataTableService.getManyRowsAndCount(dataTableId, project.id, { filter: { type: 'and', filters: [{ columnName: 'text', value: 'data%more', condition: 'like' }], @@ -727,7 +727,7 @@ describe('dataStore filters', () => { it('should treat underscore literally in like patterns', async () => { // ARRANGE - await dataStoreService.insertRows(dataStoreId, project.id, [ + await dataTableService.insertRows(dataTableId, project.id, [ { text: 'prefix_suffix' }, { text: 'Prefix_Suffix' }, { text: 'prefix\\_suffix' }, @@ -736,7 +736,7 @@ describe('dataStore filters', () => { ]); // ACT - const result = await dataStoreService.getManyRowsAndCount(dataStoreId, project.id, { + const result = await dataTableService.getManyRowsAndCount(dataTableId, project.id, { filter: { type: 'and', filters: [{ columnName: 'text', value: 'prefix_suffix', condition: 'like' }], @@ -750,7 +750,7 @@ describe('dataStore filters', () => { it('should handle multiple special characters', async () => { // ARRANGE - await dataStoreService.insertRows(dataStoreId, project.id, [ + await dataTableService.insertRows(dataTableId, project.id, [ { text: 'test[*?]data' }, { text: 'Test[*?]Data' }, { text: 'testOtherData' }, @@ -758,7 +758,7 @@ describe('dataStore filters', () => { ]); // ACT - const result = await dataStoreService.getManyRowsAndCount(dataStoreId, project.id, { + const result = await dataTableService.getManyRowsAndCount(dataTableId, project.id, { filter: { type: 'and', filters: [{ columnName: 'text', value: 'test%[*?]%', condition: 'like' }], @@ -772,19 +772,19 @@ describe('dataStore filters', () => { }); describe('ilike filter with special characters (case-insensitive)', () => { - let dataStoreId: string; + let dataTableId: string; beforeEach(async () => { - const { id } = await dataStoreService.createDataStore(project.id, { + const { id } = await dataTableService.createDataTable(project.id, { name: 'dataStore', columns: [{ name: 'text', type: 'string' }], }); - dataStoreId = id; + dataTableId = id; }); it('should treat square brackets literally', async () => { // ARRANGE - await dataStoreService.insertRows(dataStoreId, project.id, [ + await dataTableService.insertRows(dataTableId, project.id, [ { text: 'test[abc]data' }, { text: 'Test[ABC]Data' }, { text: 'testAdata' }, @@ -792,7 +792,7 @@ describe('dataStore filters', () => { ]); // ACT - const result = await dataStoreService.getManyRowsAndCount(dataStoreId, project.id, { + const result = await dataTableService.getManyRowsAndCount(dataTableId, project.id, { filter: { type: 'and', filters: [{ columnName: 'text', value: '%[abc]%', condition: 'ilike' }], @@ -811,7 +811,7 @@ describe('dataStore filters', () => { it('should treat asterisk literally', async () => { // ARRANGE - await dataStoreService.insertRows(dataStoreId, project.id, [ + await dataTableService.insertRows(dataTableId, project.id, [ { text: 'test*data' }, { text: 'Test*Data' }, { text: 'testOtherData' }, @@ -819,7 +819,7 @@ describe('dataStore filters', () => { ]); // ACT - const result = await dataStoreService.getManyRowsAndCount(dataStoreId, project.id, { + const result = await dataTableService.getManyRowsAndCount(dataTableId, project.id, { filter: { type: 'and', filters: [{ columnName: 'text', value: '%*%', condition: 'ilike' }], @@ -838,7 +838,7 @@ describe('dataStore filters', () => { it('should treat question mark literally', async () => { // ARRANGE - await dataStoreService.insertRows(dataStoreId, project.id, [ + await dataTableService.insertRows(dataTableId, project.id, [ { text: 'test?data' }, { text: 'Test?Data' }, { text: 'testSingleChar' }, @@ -846,7 +846,7 @@ describe('dataStore filters', () => { ]); // ACT - const result = await dataStoreService.getManyRowsAndCount(dataStoreId, project.id, { + const result = await dataTableService.getManyRowsAndCount(dataTableId, project.id, { filter: { type: 'and', filters: [{ columnName: 'text', value: '%?%', condition: 'ilike' }], @@ -865,7 +865,7 @@ describe('dataStore filters', () => { it('should convert % wildcard to match zero or more characters', async () => { // ARRANGE - await dataStoreService.insertRows(dataStoreId, project.id, [ + await dataTableService.insertRows(dataTableId, project.id, [ { text: 'data%more' }, { text: 'Data%More' }, { text: 'datamore' }, @@ -876,7 +876,7 @@ describe('dataStore filters', () => { ]); // ACT - const result = await dataStoreService.getManyRowsAndCount(dataStoreId, project.id, { + const result = await dataTableService.getManyRowsAndCount(dataTableId, project.id, { filter: { type: 'and', filters: [{ columnName: 'text', value: 'data%more', condition: 'ilike' }], @@ -899,7 +899,7 @@ describe('dataStore filters', () => { it('should treat underscore literally', async () => { // ARRANGE - await dataStoreService.insertRows(dataStoreId, project.id, [ + await dataTableService.insertRows(dataTableId, project.id, [ { text: 'prefix_suffix' }, { text: 'Prefix_Suffix' }, { text: 'Prefix\\_Suffix' }, @@ -908,7 +908,7 @@ describe('dataStore filters', () => { ]); // ACT - const result = await dataStoreService.getManyRowsAndCount(dataStoreId, project.id, { + const result = await dataTableService.getManyRowsAndCount(dataTableId, project.id, { filter: { type: 'and', filters: [{ columnName: 'text', value: 'prefix_suffix', condition: 'ilike' }], @@ -927,7 +927,7 @@ describe('dataStore filters', () => { it('should handle multiple special characters', async () => { // ARRANGE - await dataStoreService.insertRows(dataStoreId, project.id, [ + await dataTableService.insertRows(dataTableId, project.id, [ { text: 'test[*?]data' }, { text: 'Test[*?]Data' }, { text: 'testOtherData' }, @@ -935,7 +935,7 @@ describe('dataStore filters', () => { ]); // ACT - const result = await dataStoreService.getManyRowsAndCount(dataStoreId, project.id, { + const result = await dataTableService.getManyRowsAndCount(dataTableId, project.id, { filter: { type: 'and', filters: [{ columnName: 'text', value: '%[*?]%', condition: 'ilike' }], @@ -956,17 +956,17 @@ describe('dataStore filters', () => { describe('greater than and less than filters', () => { describe('number comparisons', () => { - let dataStoreId: string; + let dataTableId: string; beforeEach(async () => { - const { id } = await dataStoreService.createDataStore(project.id, { + const { id } = await dataTableService.createDataTable(project.id, { name: 'dataStore', columns: [ { name: 'name', type: 'string' }, { name: 'age', type: 'number' }, ], }); - dataStoreId = id; + dataTableId = id; const rows = [ { name: 'John', age: 25 }, @@ -975,12 +975,12 @@ describe('dataStore filters', () => { { name: 'Alice', age: 40 }, ]; - await dataStoreService.insertRows(dataStoreId, project.id, rows); + await dataTableService.insertRows(dataTableId, project.id, rows); }); it("retrieves rows with 'greater than' filter correctly", async () => { // ACT - const result = await dataStoreService.getManyRowsAndCount(dataStoreId, project.id, { + const result = await dataTableService.getManyRowsAndCount(dataTableId, project.id, { filter: { type: 'and', filters: [{ columnName: 'age', value: 30, condition: 'gt' }], @@ -997,7 +997,7 @@ describe('dataStore filters', () => { it("retrieves rows with 'greater than or equal' filter correctly", async () => { // ACT - const result = await dataStoreService.getManyRowsAndCount(dataStoreId, project.id, { + const result = await dataTableService.getManyRowsAndCount(dataTableId, project.id, { filter: { type: 'and', filters: [{ columnName: 'age', value: 30, condition: 'gte' }], @@ -1015,7 +1015,7 @@ describe('dataStore filters', () => { it("retrieves rows with 'less than' filter correctly", async () => { // ACT - const result = await dataStoreService.getManyRowsAndCount(dataStoreId, project.id, { + const result = await dataTableService.getManyRowsAndCount(dataTableId, project.id, { filter: { type: 'and', filters: [{ columnName: 'age', value: 35, condition: 'lt' }], @@ -1032,7 +1032,7 @@ describe('dataStore filters', () => { it("retrieves rows with 'less than or equal' filter correctly", async () => { // ACT - const result = await dataStoreService.getManyRowsAndCount(dataStoreId, project.id, { + const result = await dataTableService.getManyRowsAndCount(dataTableId, project.id, { filter: { type: 'and', filters: [{ columnName: 'age', value: 35, condition: 'lte' }], @@ -1050,17 +1050,17 @@ describe('dataStore filters', () => { }); describe('string comparisons', () => { - let dataStoreId: string; + let dataTableId: string; beforeEach(async () => { - const { id } = await dataStoreService.createDataStore(project.id, { + const { id } = await dataTableService.createDataTable(project.id, { name: 'dataStore', columns: [ { name: 'name', type: 'string' }, { name: 'category', type: 'string' }, ], }); - dataStoreId = id; + dataTableId = id; const rows = [ { name: 'Alice', category: 'Alpha' }, @@ -1069,12 +1069,12 @@ describe('dataStore filters', () => { { name: 'David', category: 'Delta' }, ]; - await dataStoreService.insertRows(dataStoreId, project.id, rows); + await dataTableService.insertRows(dataTableId, project.id, rows); }); it("retrieves rows with 'greater than' string filter correctly", async () => { // ACT - const result = await dataStoreService.getManyRowsAndCount(dataStoreId, project.id, { + const result = await dataTableService.getManyRowsAndCount(dataTableId, project.id, { filter: { type: 'and', filters: [{ columnName: 'category', value: 'Beta', condition: 'gt' }], @@ -1091,7 +1091,7 @@ describe('dataStore filters', () => { it("retrieves rows with 'less than' string filter correctly", async () => { // ACT - const result = await dataStoreService.getManyRowsAndCount(dataStoreId, project.id, { + const result = await dataTableService.getManyRowsAndCount(dataTableId, project.id, { filter: { type: 'and', filters: [{ columnName: 'category', value: 'Delta', condition: 'lt' }], @@ -1108,17 +1108,17 @@ describe('dataStore filters', () => { }); describe('date comparisons', () => { - let dataStoreId: string; + let dataTableId: string; beforeEach(async () => { - const { id } = await dataStoreService.createDataStore(project.id, { + const { id } = await dataTableService.createDataTable(project.id, { name: 'dataStore', columns: [ { name: 'name', type: 'string' }, { name: 'registeredAt', type: 'date' }, ], }); - dataStoreId = id; + dataTableId = id; const rows = [ { name: 'Task1', registeredAt: new Date('2023-12-31') }, @@ -1127,13 +1127,13 @@ describe('dataStore filters', () => { { name: 'Task4', registeredAt: new Date('2024-01-03') }, ]; - await dataStoreService.insertRows(dataStoreId, project.id, rows); + await dataTableService.insertRows(dataTableId, project.id, rows); }); it("retrieves rows with 'greater than' date filter correctly", async () => { // ACT const baseDate = new Date('2024-01-01'); - const result = await dataStoreService.getManyRowsAndCount(dataStoreId, project.id, { + const result = await dataTableService.getManyRowsAndCount(dataTableId, project.id, { filter: { type: 'and', filters: [{ columnName: 'registeredAt', value: baseDate, condition: 'gt' }], @@ -1151,7 +1151,7 @@ describe('dataStore filters', () => { it("retrieves rows with 'less than or equal' date filter correctly", async () => { // ACT const baseDate = new Date('2024-01-02'); - const result = await dataStoreService.getManyRowsAndCount(dataStoreId, project.id, { + const result = await dataTableService.getManyRowsAndCount(dataTableId, project.id, { filter: { type: 'and', filters: [{ columnName: 'registeredAt', value: baseDate, condition: 'lte' }], @@ -1169,17 +1169,17 @@ describe('dataStore filters', () => { }); describe('null value validation', () => { - let dataStoreId: string; + let dataTableId: string; beforeEach(async () => { - const { id } = await dataStoreService.createDataStore(project.id, { + const { id } = await dataTableService.createDataTable(project.id, { name: 'dataStore', columns: [ { name: 'name', type: 'string' }, { name: 'age', type: 'number' }, ], }); - dataStoreId = id; + dataTableId = id; const rows = [ { name: 'John', age: 31 }, @@ -1187,7 +1187,7 @@ describe('dataStore filters', () => { { name: 'Mary', age: null }, ]; - await dataStoreService.insertRows(dataStoreId, project.id, rows); + await dataTableService.insertRows(dataTableId, project.id, rows); }); describe.each(['gt', 'gte', 'lt', 'lte'] as const)( @@ -1195,7 +1195,7 @@ describe('dataStore filters', () => { (condition) => { it(`throws error when '${condition}' filter value is null`, async () => { // ACT - const result = dataStoreService.getManyRowsAndCount(dataStoreId, project.id, { + const result = dataTableService.getManyRowsAndCount(dataTableId, project.id, { filter: { type: 'and', filters: [{ columnName: 'age', value: null, condition }], @@ -1204,7 +1204,7 @@ describe('dataStore filters', () => { // ASSERT await expect(result).rejects.toThrow( - new DataStoreValidationError( + new DataTableValidationError( `${condition.toUpperCase()} filter value cannot be null or undefined`, ), ); @@ -1212,7 +1212,7 @@ describe('dataStore filters', () => { it(`handles null values in data correctly with '${condition}' filter`, async () => { // ACT - const result = await dataStoreService.getManyRowsAndCount(dataStoreId, project.id, { + const result = await dataTableService.getManyRowsAndCount(dataTableId, project.id, { filter: { type: 'and', filters: [{ columnName: 'age', value: 30, condition }], @@ -1232,7 +1232,7 @@ describe('dataStore filters', () => { describe('AND filters', () => { it('retrieves rows matching all conditions in AND filter', async () => { // ARRANGE - const { id } = await dataStoreService.createDataStore(project.id, { + const { id } = await dataTableService.createDataTable(project.id, { name: 'dataStore', columns: [ { name: 'name', type: 'string' }, @@ -1241,7 +1241,7 @@ describe('dataStore filters', () => { ], }); - await dataStoreService.insertRows(id, project.id, [ + await dataTableService.insertRows(id, project.id, [ { name: 'John', age: 25, isActive: true }, { name: 'Mary', age: 30, isActive: true }, { name: 'Jack', age: 35, isActive: true }, @@ -1250,7 +1250,7 @@ describe('dataStore filters', () => { ]); // ACT - const result = await dataStoreService.getManyRowsAndCount(id, project.id, { + const result = await dataTableService.getManyRowsAndCount(id, project.id, { filter: { type: 'and', filters: [ @@ -1269,7 +1269,7 @@ describe('dataStore filters', () => { it('returns empty result when no conditions match', async () => { // ARRANGE - const { id } = await dataStoreService.createDataStore(project.id, { + const { id } = await dataTableService.createDataTable(project.id, { name: 'dataStore', columns: [ { name: 'name', type: 'string' }, @@ -1278,7 +1278,7 @@ describe('dataStore filters', () => { ], }); - await dataStoreService.insertRows(id, project.id, [ + await dataTableService.insertRows(id, project.id, [ { name: 'John', age: 25, isActive: true }, { name: 'Mary', age: 30, isActive: false }, { name: 'Jack', age: 35, isActive: true }, @@ -1287,7 +1287,7 @@ describe('dataStore filters', () => { ]); // ACT - const result = await dataStoreService.getManyRowsAndCount(id, project.id, { + const result = await dataTableService.getManyRowsAndCount(id, project.id, { filter: { type: 'and', filters: [ @@ -1306,7 +1306,7 @@ describe('dataStore filters', () => { describe('OR filters', () => { it('retrieves rows matching any condition in OR filter', async () => { // ARRANGE - const { id } = await dataStoreService.createDataStore(project.id, { + const { id } = await dataTableService.createDataTable(project.id, { name: 'dataStore', columns: [ { name: 'name', type: 'string' }, @@ -1315,7 +1315,7 @@ describe('dataStore filters', () => { ], }); - await dataStoreService.insertRows(id, project.id, [ + await dataTableService.insertRows(id, project.id, [ { name: 'John', age: 25, isActive: true }, { name: 'Mary', age: 30, isActive: false }, { name: 'Jack', age: 35, isActive: true }, @@ -1324,7 +1324,7 @@ describe('dataStore filters', () => { ]); // ACT - const result = await dataStoreService.getManyRowsAndCount(id, project.id, { + const result = await dataTableService.getManyRowsAndCount(id, project.id, { filter: { type: 'or', filters: [ @@ -1346,7 +1346,7 @@ describe('dataStore filters', () => { it('retrieves rows when multiple conditions match the same row', async () => { // ARRANGE - const { id } = await dataStoreService.createDataStore(project.id, { + const { id } = await dataTableService.createDataTable(project.id, { name: 'dataStore', columns: [ { name: 'name', type: 'string' }, @@ -1355,7 +1355,7 @@ describe('dataStore filters', () => { ], }); - await dataStoreService.insertRows(id, project.id, [ + await dataTableService.insertRows(id, project.id, [ { name: 'John', age: 25, isActive: false }, { name: 'Mary', age: 30, isActive: true }, { name: 'Arnold', age: 35, isActive: false }, @@ -1364,7 +1364,7 @@ describe('dataStore filters', () => { ]); // ACT - const result = await dataStoreService.getManyRowsAndCount(id, project.id, { + const result = await dataTableService.getManyRowsAndCount(id, project.id, { filter: { type: 'or', filters: [ @@ -1383,7 +1383,7 @@ describe('dataStore filters', () => { it('returns empty result when no conditions match', async () => { // ARRANGE - const { id } = await dataStoreService.createDataStore(project.id, { + const { id } = await dataTableService.createDataTable(project.id, { name: 'dataStore', columns: [ { name: 'name', type: 'string' }, @@ -1393,7 +1393,7 @@ describe('dataStore filters', () => { }); // ACT - const result = await dataStoreService.getManyRowsAndCount(id, project.id, { + const result = await dataTableService.getManyRowsAndCount(id, project.id, { filter: { type: 'or', filters: [ @@ -1414,7 +1414,7 @@ describe('dataStore filters', () => { describe('equals and not equals filters', () => { it("updates rows with 'equals' filter correctly", async () => { // ARRANGE - const { id: dataStoreId } = await dataStoreService.createDataStore(project.id, { + const { id: dataTableId } = await dataTableService.createDataTable(project.id, { name: 'dataStore', columns: [ { name: 'name', type: 'string' }, @@ -1426,15 +1426,15 @@ describe('dataStore filters', () => { const maryBirthday = new Date('1998-08-25T14:30:00.000Z'); - await dataStoreService.insertRows(dataStoreId, project.id, [ + await dataTableService.insertRows(dataTableId, project.id, [ { name: 'John', age: 30, birthday: new Date('1994-05-15T12:00:00.000Z'), isActive: true }, { name: 'Mary', age: 25, birthday: maryBirthday, isActive: false }, { name: 'Jack', age: 35, birthday: new Date('1988-12-05T10:00:00.000Z'), isActive: true }, ]); // ACT - await dataStoreService.updateRows( - dataStoreId, + await dataTableService.updateRows( + dataTableId, project.id, { filter: { @@ -1452,7 +1452,7 @@ describe('dataStore filters', () => { ); // ASSERT - const result = await dataStoreService.getManyRowsAndCount(dataStoreId, project.id, { + const result = await dataTableService.getManyRowsAndCount(dataTableId, project.id, { filter: { type: 'and', filters: [{ columnName: 'name', value: 'Mary', condition: 'eq' }], @@ -1471,7 +1471,7 @@ describe('dataStore filters', () => { it("updates rows with 'not equals' filter correctly", async () => { // ARRANGE - const { id: dataStoreId } = await dataStoreService.createDataStore(project.id, { + const { id: dataTableId } = await dataTableService.createDataTable(project.id, { name: 'dataStore', columns: [ { name: 'name', type: 'string' }, @@ -1479,14 +1479,14 @@ describe('dataStore filters', () => { ], }); - await dataStoreService.insertRows(dataStoreId, project.id, [ + await dataTableService.insertRows(dataTableId, project.id, [ { name: 'John', age: 30 }, { name: 'Mary', age: 25 }, { name: 'Jack', age: 35 }, ]); // ACT - await dataStoreService.updateRows(dataStoreId, project.id, { + await dataTableService.updateRows(dataTableId, project.id, { filter: { type: 'and', filters: [{ columnName: 'name', value: 'Mary', condition: 'neq' }], @@ -1495,7 +1495,7 @@ describe('dataStore filters', () => { }); // ASSERT - const result = await dataStoreService.getManyRowsAndCount(dataStoreId, project.id, {}); + const result = await dataTableService.getManyRowsAndCount(dataTableId, project.id, {}); expect(result.count).toEqual(3); expect(result.data).toEqual( expect.arrayContaining([ @@ -1508,7 +1508,7 @@ describe('dataStore filters', () => { it('updates rows with filter by null', async () => { // ARRANGE - const { id: dataStoreId } = await dataStoreService.createDataStore(project.id, { + const { id: dataTableId } = await dataTableService.createDataTable(project.id, { name: 'dataStore', columns: [ { name: 'name', type: 'string' }, @@ -1516,7 +1516,7 @@ describe('dataStore filters', () => { ], }); - await dataStoreService.insertRows(dataStoreId, project.id, [ + await dataTableService.insertRows(dataTableId, project.id, [ { name: null, active: true }, { name: 'Marco', active: true }, { name: null, active: false }, @@ -1524,7 +1524,7 @@ describe('dataStore filters', () => { ]); // ACT - await dataStoreService.updateRows(dataStoreId, project.id, { + await dataTableService.updateRows(dataTableId, project.id, { filter: { type: 'and', filters: [{ columnName: 'name', condition: 'eq', value: null }], @@ -1533,7 +1533,7 @@ describe('dataStore filters', () => { }); // ASSERT - const result = await dataStoreService.getManyRowsAndCount(dataStoreId, project.id, {}); + const result = await dataTableService.getManyRowsAndCount(dataTableId, project.id, {}); expect(result.count).toEqual(4); expect(result.data).toEqual( expect.arrayContaining([ @@ -1547,7 +1547,7 @@ describe('dataStore filters', () => { it('updates rows with filter by not null', async () => { // ARRANGE - const { id: dataStoreId } = await dataStoreService.createDataStore(project.id, { + const { id: dataTableId } = await dataTableService.createDataTable(project.id, { name: 'dataStore', columns: [ { name: 'name', type: 'string' }, @@ -1555,7 +1555,7 @@ describe('dataStore filters', () => { ], }); - await dataStoreService.insertRows(dataStoreId, project.id, [ + await dataTableService.insertRows(dataTableId, project.id, [ { name: null, active: true }, { name: 'Marco', active: true }, { name: null, active: false }, @@ -1563,7 +1563,7 @@ describe('dataStore filters', () => { ]); // ACT - await dataStoreService.updateRows(dataStoreId, project.id, { + await dataTableService.updateRows(dataTableId, project.id, { filter: { type: 'and', filters: [{ columnName: 'name', condition: 'neq', value: null }], @@ -1572,7 +1572,7 @@ describe('dataStore filters', () => { }); // ASSERT - const result = await dataStoreService.getManyRowsAndCount(dataStoreId, project.id, {}); + const result = await dataTableService.getManyRowsAndCount(dataTableId, project.id, {}); expect(result.count).toEqual(4); expect(result.data).toEqual( expect.arrayContaining([ @@ -1588,7 +1588,7 @@ describe('dataStore filters', () => { describe('LIKE filters', () => { it("updates rows with 'contains sensitive' filter correctly", async () => { // ARRANGE - const { id: dataStoreId } = await dataStoreService.createDataStore(project.id, { + const { id: dataTableId } = await dataTableService.createDataTable(project.id, { name: 'dataStore', columns: [ { name: 'name', type: 'string' }, @@ -1596,14 +1596,14 @@ describe('dataStore filters', () => { ], }); - await dataStoreService.insertRows(dataStoreId, project.id, [ + await dataTableService.insertRows(dataTableId, project.id, [ { name: 'Arnold', age: 30 }, { name: 'Mary', age: 25 }, { name: 'Charlie', age: 35 }, ]); // ACT - await dataStoreService.updateRows(dataStoreId, project.id, { + await dataTableService.updateRows(dataTableId, project.id, { filter: { type: 'and', filters: [{ columnName: 'name', value: '%ar%', condition: 'like' }], @@ -1612,7 +1612,7 @@ describe('dataStore filters', () => { }); // ASSERT - const result = await dataStoreService.getManyRowsAndCount(dataStoreId, project.id, { + const result = await dataTableService.getManyRowsAndCount(dataTableId, project.id, { filter: { type: 'and', filters: [{ columnName: 'age', value: 50, condition: 'eq' }], @@ -1627,7 +1627,7 @@ describe('dataStore filters', () => { it("updates rows with 'contains insensitive' filter correctly", async () => { // ARRANGE - const { id: dataStoreId } = await dataStoreService.createDataStore(project.id, { + const { id: dataTableId } = await dataTableService.createDataTable(project.id, { name: 'dataStore', columns: [ { name: 'name', type: 'string' }, @@ -1641,10 +1641,10 @@ describe('dataStore filters', () => { { name: 'Charlie', age: 35 }, ]; - await dataStoreService.insertRows(dataStoreId, project.id, rows); + await dataTableService.insertRows(dataTableId, project.id, rows); // ACT - await dataStoreService.updateRows(dataStoreId, project.id, { + await dataTableService.updateRows(dataTableId, project.id, { filter: { type: 'and', filters: [{ columnName: 'name', value: '%ar%', condition: 'ilike' }], @@ -1653,7 +1653,7 @@ describe('dataStore filters', () => { }); // ASSERT - const result = await dataStoreService.getManyRowsAndCount(dataStoreId, project.id, {}); + const result = await dataTableService.getManyRowsAndCount(dataTableId, project.id, {}); expect(result.count).toEqual(3); expect(result.data).toEqual( expect.arrayContaining([ @@ -1666,7 +1666,7 @@ describe('dataStore filters', () => { it("updates rows with 'starts with' filter correctly", async () => { // ARRANGE - const { id: dataStoreId } = await dataStoreService.createDataStore(project.id, { + const { id: dataTableId } = await dataTableService.createDataTable(project.id, { name: 'dataStore', columns: [ { name: 'name', type: 'string' }, @@ -1680,10 +1680,10 @@ describe('dataStore filters', () => { { name: 'Charlie', age: 35 }, ]; - await dataStoreService.insertRows(dataStoreId, project.id, rows); + await dataTableService.insertRows(dataTableId, project.id, rows); // ACT - await dataStoreService.updateRows(dataStoreId, project.id, { + await dataTableService.updateRows(dataTableId, project.id, { filter: { type: 'and', filters: [{ columnName: 'name', value: 'Ar%', condition: 'ilike' }], @@ -1692,7 +1692,7 @@ describe('dataStore filters', () => { }); // ASSERT - const result = await dataStoreService.getManyRowsAndCount(dataStoreId, project.id, { + const result = await dataTableService.getManyRowsAndCount(dataTableId, project.id, { filter: { type: 'and', filters: [{ columnName: 'age', value: 60, condition: 'eq' }], @@ -1704,7 +1704,7 @@ describe('dataStore filters', () => { it("updates rows with 'ends with' filter correctly", async () => { // ARRANGE - const { id: dataStoreId } = await dataStoreService.createDataStore(project.id, { + const { id: dataTableId } = await dataTableService.createDataTable(project.id, { name: 'dataStore', columns: [ { name: 'name', type: 'string' }, @@ -1712,7 +1712,7 @@ describe('dataStore filters', () => { ], }); - await dataStoreService.insertRows(dataStoreId, project.id, [ + await dataTableService.insertRows(dataTableId, project.id, [ { name: 'Arnold', age: 30 }, { name: 'Mary', age: 25 }, { name: 'Charlie', age: 35 }, @@ -1720,7 +1720,7 @@ describe('dataStore filters', () => { ]); // ACT - await dataStoreService.updateRows(dataStoreId, project.id, { + await dataTableService.updateRows(dataTableId, project.id, { filter: { type: 'and', filters: [{ columnName: 'name', value: '%old', condition: 'ilike' }], @@ -1729,7 +1729,7 @@ describe('dataStore filters', () => { }); // ASSERT - const result = await dataStoreService.getManyRowsAndCount(dataStoreId, project.id, { + const result = await dataTableService.getManyRowsAndCount(dataTableId, project.id, { filter: { type: 'and', filters: [{ columnName: 'age', value: 65, condition: 'eq' }], @@ -1745,10 +1745,10 @@ describe('dataStore filters', () => { describe('greater than and less than filters', () => { describe('number comparisons', () => { - let dataStoreId: string; + let dataTableId: string; beforeEach(async () => { - const { id } = await dataStoreService.createDataStore(project.id, { + const { id } = await dataTableService.createDataTable(project.id, { name: 'dataStore', columns: [ { name: 'name', type: 'string' }, @@ -1756,12 +1756,12 @@ describe('dataStore filters', () => { { name: 'position', type: 'string' }, ], }); - dataStoreId = id; + dataTableId = id; }); it("updates rows with 'greater than' filter correctly", async () => { // ARRANGE - await dataStoreService.insertRows(dataStoreId, project.id, [ + await dataTableService.insertRows(dataTableId, project.id, [ { name: 'John', age: 25 }, { name: 'Mary', age: 30 }, { name: 'Jack', age: 35 }, @@ -1769,7 +1769,7 @@ describe('dataStore filters', () => { ]); // ACT - await dataStoreService.updateRows(dataStoreId, project.id, { + await dataTableService.updateRows(dataTableId, project.id, { filter: { type: 'and', filters: [{ columnName: 'age', value: 30, condition: 'gt' }], @@ -1778,7 +1778,7 @@ describe('dataStore filters', () => { }); // ASSERT - const result = await dataStoreService.getManyRowsAndCount(dataStoreId, project.id, { + const result = await dataTableService.getManyRowsAndCount(dataTableId, project.id, { filter: { type: 'and', filters: [{ columnName: 'position', value: 'senior', condition: 'eq' }], @@ -1793,7 +1793,7 @@ describe('dataStore filters', () => { it("updates rows with 'greater than or equal' filter correctly", async () => { // ARRANGE - await dataStoreService.insertRows(dataStoreId, project.id, [ + await dataTableService.insertRows(dataTableId, project.id, [ { name: 'John', age: 25 }, { name: 'Mary', age: 30 }, { name: 'Jack', age: 35 }, @@ -1801,7 +1801,7 @@ describe('dataStore filters', () => { ]); // ACT - await dataStoreService.updateRows(dataStoreId, project.id, { + await dataTableService.updateRows(dataTableId, project.id, { filter: { type: 'and', filters: [{ columnName: 'age', value: 30, condition: 'gte' }], @@ -1810,7 +1810,7 @@ describe('dataStore filters', () => { }); // ASSERT - const result = await dataStoreService.getManyRowsAndCount(dataStoreId, project.id, { + const result = await dataTableService.getManyRowsAndCount(dataTableId, project.id, { filter: { type: 'and', filters: [{ columnName: 'position', value: 'experienced', condition: 'eq' }], @@ -1828,7 +1828,7 @@ describe('dataStore filters', () => { it("updates rows with 'less than' filter correctly", async () => { // ARRANGE - await dataStoreService.insertRows(dataStoreId, project.id, [ + await dataTableService.insertRows(dataTableId, project.id, [ { name: 'John', age: 25 }, { name: 'Mary', age: 30 }, { name: 'Jack', age: 35 }, @@ -1836,7 +1836,7 @@ describe('dataStore filters', () => { ]); // ACT - await dataStoreService.updateRows(dataStoreId, project.id, { + await dataTableService.updateRows(dataTableId, project.id, { filter: { type: 'and', filters: [{ columnName: 'age', value: 35, condition: 'lt' }], @@ -1845,7 +1845,7 @@ describe('dataStore filters', () => { }); // ASSERT - const result = await dataStoreService.getManyRowsAndCount(dataStoreId, project.id, { + const result = await dataTableService.getManyRowsAndCount(dataTableId, project.id, { filter: { type: 'and', filters: [{ columnName: 'position', value: 'junior', condition: 'eq' }], @@ -1862,7 +1862,7 @@ describe('dataStore filters', () => { it("updates rows with 'less than or equal' filter correctly", async () => { // ARRANGE - await dataStoreService.insertRows(dataStoreId, project.id, [ + await dataTableService.insertRows(dataTableId, project.id, [ { name: 'John', age: 25 }, { name: 'Mary', age: 30 }, { name: 'Jack', age: 35 }, @@ -1870,7 +1870,7 @@ describe('dataStore filters', () => { ]); // ACT - await dataStoreService.updateRows(dataStoreId, project.id, { + await dataTableService.updateRows(dataTableId, project.id, { filter: { type: 'and', filters: [{ columnName: 'age', value: 35, condition: 'lte' }], @@ -1879,7 +1879,7 @@ describe('dataStore filters', () => { }); // ASSERT - const result = await dataStoreService.getManyRowsAndCount(dataStoreId, project.id, { + const result = await dataTableService.getManyRowsAndCount(dataTableId, project.id, { filter: { type: 'and', filters: [{ columnName: 'position', value: 'junior', condition: 'eq' }], @@ -1897,10 +1897,10 @@ describe('dataStore filters', () => { }); describe('string comparisons', () => { - let dataStoreId: string; + let dataTableId: string; beforeEach(async () => { - const { id } = await dataStoreService.createDataStore(project.id, { + const { id } = await dataTableService.createDataTable(project.id, { name: 'dataStore', columns: [ { name: 'name', type: 'string' }, @@ -1908,12 +1908,12 @@ describe('dataStore filters', () => { { name: 'startDate', type: 'date' }, ], }); - dataStoreId = id; + dataTableId = id; }); it("updates rows with 'greater than' string filter correctly", async () => { // ARRANGE - await dataStoreService.insertRows(dataStoreId, project.id, [ + await dataTableService.insertRows(dataTableId, project.id, [ { name: 'Alice', category: 'A', startDate: new Date('2023-01-01T12:00:00Z') }, { name: 'Bob', category: 'B', startDate: new Date('2023-01-02T12:00:00Z') }, { name: 'Charlie', category: 'C', startDate: new Date('2023-01-03T12:00:00Z') }, @@ -1922,7 +1922,7 @@ describe('dataStore filters', () => { // ACT const newStartDate = new Date('2024-01-01T12:00:00Z'); - await dataStoreService.updateRows(dataStoreId, project.id, { + await dataTableService.updateRows(dataTableId, project.id, { filter: { type: 'and', filters: [{ columnName: 'category', value: 'C', condition: 'gt' }], @@ -1931,7 +1931,7 @@ describe('dataStore filters', () => { }); // ASSERT - const result = await dataStoreService.getManyRowsAndCount(dataStoreId, project.id, { + const result = await dataTableService.getManyRowsAndCount(dataTableId, project.id, { filter: { type: 'and', filters: [{ columnName: 'startDate', value: newStartDate, condition: 'eq' }], @@ -1945,7 +1945,7 @@ describe('dataStore filters', () => { it("updates rows with 'less than' string filter correctly", async () => { // ARRANGE - await dataStoreService.insertRows(dataStoreId, project.id, [ + await dataTableService.insertRows(dataTableId, project.id, [ { name: 'Alice', category: 'A', startDate: new Date('2023-01-01T12:00:00Z') }, { name: 'Bob', category: 'B', startDate: new Date('2023-01-02T12:00:00Z') }, { name: 'Charlie', category: 'C', startDate: new Date('2023-01-03T12:00:00Z') }, @@ -1954,7 +1954,7 @@ describe('dataStore filters', () => { // ACT const newStartDate = new Date('2024-01-01T12:00:00Z'); - await dataStoreService.updateRows(dataStoreId, project.id, { + await dataTableService.updateRows(dataTableId, project.id, { filter: { type: 'and', filters: [{ columnName: 'category', value: 'C', condition: 'lt' }], @@ -1963,7 +1963,7 @@ describe('dataStore filters', () => { }); // ASSERT - const result = await dataStoreService.getManyRowsAndCount(dataStoreId, project.id, { + const result = await dataTableService.getManyRowsAndCount(dataTableId, project.id, { filter: { type: 'and', filters: [{ columnName: 'startDate', value: newStartDate, condition: 'eq' }], @@ -1980,22 +1980,22 @@ describe('dataStore filters', () => { }); describe('date comparisons', () => { - let dataStoreId: string; + let dataTableId: string; beforeEach(async () => { - const { id } = await dataStoreService.createDataStore(project.id, { + const { id } = await dataTableService.createDataTable(project.id, { name: 'dataStore', columns: [ { name: 'name', type: 'string' }, { name: 'registeredAt', type: 'date' }, ], }); - dataStoreId = id; + dataTableId = id; }); it("updates rows with 'greater than' date filter correctly", async () => { // ARRANGE - await dataStoreService.insertRows(dataStoreId, project.id, [ + await dataTableService.insertRows(dataTableId, project.id, [ { name: 'Task1', registeredAt: new Date('2023-12-31') }, { name: 'Task2', registeredAt: new Date('2024-01-01') }, { name: 'Task3', registeredAt: new Date('2024-01-02') }, @@ -2004,7 +2004,7 @@ describe('dataStore filters', () => { // ACT const baseDate = new Date('2024-01-01'); - await dataStoreService.updateRows(dataStoreId, project.id, { + await dataTableService.updateRows(dataTableId, project.id, { filter: { type: 'and', filters: [{ columnName: 'registeredAt', value: baseDate, condition: 'gt' }], @@ -2013,7 +2013,7 @@ describe('dataStore filters', () => { }); // ASSERT - const result = await dataStoreService.getManyRowsAndCount(dataStoreId, project.id, { + const result = await dataTableService.getManyRowsAndCount(dataTableId, project.id, { filter: { type: 'and', filters: [{ columnName: 'name', value: 'RECENT', condition: 'eq' }], @@ -2028,7 +2028,7 @@ describe('dataStore filters', () => { it("updates rows with 'less than or equal' date filter correctly", async () => { // ARRANGE - await dataStoreService.insertRows(dataStoreId, project.id, [ + await dataTableService.insertRows(dataTableId, project.id, [ { name: 'Task1', registeredAt: new Date('2023-12-31') }, { name: 'Task2', registeredAt: new Date('2024-01-01') }, { name: 'Task3', registeredAt: new Date('2024-01-02') }, @@ -2037,7 +2037,7 @@ describe('dataStore filters', () => { // ACT const baseDate = new Date('2024-01-02'); - await dataStoreService.updateRows(dataStoreId, project.id, { + await dataTableService.updateRows(dataTableId, project.id, { filter: { type: 'and', filters: [{ columnName: 'registeredAt', value: baseDate, condition: 'lte' }], @@ -2046,7 +2046,7 @@ describe('dataStore filters', () => { }); // ASSERT - const result = await dataStoreService.getManyRowsAndCount(dataStoreId, project.id, { + const result = await dataTableService.getManyRowsAndCount(dataTableId, project.id, { filter: { type: 'and', filters: [{ columnName: 'name', value: 'OLD', condition: 'eq' }], @@ -2065,7 +2065,7 @@ describe('dataStore filters', () => { describe('AND filters', () => { it('updates rows matching all conditions in AND filter', async () => { // ARRANGE - const { id } = await dataStoreService.createDataStore(project.id, { + const { id } = await dataTableService.createDataTable(project.id, { name: 'dataStore', columns: [ { name: 'name', type: 'string' }, @@ -2074,7 +2074,7 @@ describe('dataStore filters', () => { ], }); - await dataStoreService.insertRows(id, project.id, [ + await dataTableService.insertRows(id, project.id, [ { name: 'John', age: 25, isActive: true }, { name: 'Mary', age: 30, isActive: true }, { name: 'Jack', age: 35, isActive: true }, @@ -2083,7 +2083,7 @@ describe('dataStore filters', () => { ]); // ACT - await dataStoreService.updateRows(id, project.id, { + await dataTableService.updateRows(id, project.id, { filter: { type: 'and', filters: [ @@ -2095,7 +2095,7 @@ describe('dataStore filters', () => { }); // ASSERT - const result = await dataStoreService.getManyRowsAndCount(id, project.id, { + const result = await dataTableService.getManyRowsAndCount(id, project.id, { filter: { type: 'and', filters: [{ columnName: 'age', value: 100, condition: 'eq' }], @@ -2111,7 +2111,7 @@ describe('dataStore filters', () => { describe('OR filters', () => { it('updates rows matching any condition in OR filter', async () => { // ARRANGE - const { id } = await dataStoreService.createDataStore(project.id, { + const { id } = await dataTableService.createDataTable(project.id, { name: 'dataStore', columns: [ { name: 'name', type: 'string' }, @@ -2120,7 +2120,7 @@ describe('dataStore filters', () => { ], }); - await dataStoreService.insertRows(id, project.id, [ + await dataTableService.insertRows(id, project.id, [ { name: 'John', age: 25, isActive: true }, { name: 'Mary', age: 30, isActive: false }, { name: 'Jack', age: 35, isActive: true }, @@ -2129,7 +2129,7 @@ describe('dataStore filters', () => { ]); // ACT - await dataStoreService.updateRows(id, project.id, { + await dataTableService.updateRows(id, project.id, { filter: { type: 'or', filters: [ @@ -2141,7 +2141,7 @@ describe('dataStore filters', () => { }); // ASSERT - const result = await dataStoreService.getManyRowsAndCount(id, project.id, { + const result = await dataTableService.getManyRowsAndCount(id, project.id, { filter: { type: 'and', filters: [{ columnName: 'age', value: 99, condition: 'eq' }], @@ -2158,7 +2158,7 @@ describe('dataStore filters', () => { it('updates rows when multiple conditions match the same row', async () => { // ARRANGE - const { id } = await dataStoreService.createDataStore(project.id, { + const { id } = await dataTableService.createDataTable(project.id, { name: 'dataStore', columns: [ { name: 'name', type: 'string' }, @@ -2167,7 +2167,7 @@ describe('dataStore filters', () => { ], }); - await dataStoreService.insertRows(id, project.id, [ + await dataTableService.insertRows(id, project.id, [ { name: 'John', age: 25, isActive: false }, { name: 'Mary', age: 30, isActive: true }, { name: 'Arnold', age: 35, isActive: false }, @@ -2176,7 +2176,7 @@ describe('dataStore filters', () => { ]); // ACT - await dataStoreService.updateRows(id, project.id, { + await dataTableService.updateRows(id, project.id, { filter: { type: 'or', filters: [ @@ -2188,7 +2188,7 @@ describe('dataStore filters', () => { }); // ASSERT - const result = await dataStoreService.getManyRowsAndCount(id, project.id, { + const result = await dataTableService.getManyRowsAndCount(id, project.id, { filter: { type: 'and', filters: [{ columnName: 'age', value: 88, condition: 'eq' }], diff --git a/packages/cli/src/modules/data-table/__tests__/data-store-proxy.service.integration.test.ts b/packages/cli/src/modules/data-table/__tests__/data-store-proxy.service.integration.test.ts index 169ef09dcea..b8332a1b647 100644 --- a/packages/cli/src/modules/data-table/__tests__/data-store-proxy.service.integration.test.ts +++ b/packages/cli/src/modules/data-table/__tests__/data-store-proxy.service.integration.test.ts @@ -3,18 +3,18 @@ import { testDb, testModules } from '@n8n/backend-test-utils'; import type { Project } from '@n8n/db'; import { mock } from 'jest-mock-extended'; import type { - AddDataStoreColumnOptions, + AddDataTableColumnOptions, INode, - ListDataStoreRowsOptions, - MoveDataStoreColumnOptions, - UpsertDataStoreRowOptions, + ListDataTableRowsOptions, + MoveDataTableColumnOptions, + UpsertDataTableRowOptions, Workflow, } from 'n8n-workflow'; import type { OwnershipService } from '@/services/ownership.service'; -import { DataStoreProxyService } from '../data-store-proxy.service'; -import type { DataStoreService } from '../data-store.service'; +import { DataTableProxyService } from '../data-table-proxy.service'; +import type { DataTableService } from '../data-table.service'; const PROJECT_ID = 'project-id'; @@ -22,23 +22,23 @@ beforeAll(async () => { await testModules.loadModules(['data-table']); await testDb.init(); }); -describe('DataStoreProxyService', () => { - let dataStoreServiceMock = mock(); +describe('DataTableProxyService', () => { + let dataTableServiceMock = mock(); let ownershipServiceMock = mock(); let loggerMock = mock(); - let dataStoreProxyService: DataStoreProxyService; + let dataStoreProxyService: DataTableProxyService; let workflow: Workflow; let node: INode; let project: Project; beforeEach(() => { - dataStoreServiceMock = mock(); + dataTableServiceMock = mock(); ownershipServiceMock = mock(); loggerMock = mock(); - dataStoreProxyService = new DataStoreProxyService( - dataStoreServiceMock, + dataStoreProxyService = new DataTableProxyService( + dataTableServiceMock, ownershipServiceMock, loggerMock, ); @@ -60,103 +60,103 @@ describe('DataStoreProxyService', () => { it('should call getManyAndCount with correct parameters', async () => { const options = { filter: { name: 'test' } }; - const aggregateOperations = await dataStoreProxyService.getDataStoreAggregateProxy( + const aggregateOperations = await dataStoreProxyService.getDataTableAggregateProxy( workflow, node, ); await aggregateOperations.getManyAndCount(options); - expect(dataStoreServiceMock.getManyAndCount).toBeCalledWith({ + expect(dataTableServiceMock.getManyAndCount).toBeCalledWith({ filter: { name: 'test', projectId: PROJECT_ID }, }); }); - it('should call createDataStore with correct parameters', async () => { - const options = { name: 'newDataStore', columns: [] }; + it('should call createDataTable with correct parameters', async () => { + const options = { name: 'newDataTable', columns: [] }; - const aggregateOperations = await dataStoreProxyService.getDataStoreAggregateProxy( + const aggregateOperations = await dataStoreProxyService.getDataTableAggregateProxy( workflow, node, ); - await aggregateOperations.createDataStore(options); + await aggregateOperations.createDataTable(options); - expect(dataStoreServiceMock.createDataStore).toBeCalledWith(PROJECT_ID, options); + expect(dataTableServiceMock.createDataTable).toBeCalledWith(PROJECT_ID, options); }); - it('should call deleteDataStoreByProject when proxy calls deleteDataStoreAll', async () => { - const aggregateOperations = await dataStoreProxyService.getDataStoreAggregateProxy( + it('should call deleteDataTableByProject when proxy calls deleteDataTableAll', async () => { + const aggregateOperations = await dataStoreProxyService.getDataTableAggregateProxy( workflow, node, ); - await aggregateOperations.deleteDataStoreAll(); + await aggregateOperations.deleteDataTableAll(); - expect(dataStoreServiceMock.deleteDataStoreByProjectId).toBeCalledWith(PROJECT_ID); + expect(dataTableServiceMock.deleteDataTableByProjectId).toBeCalledWith(PROJECT_ID); }); }); - it('should call updateDataStore with correct parameters', async () => { - const options = { name: 'updatedDataStore' }; + it('should call updateDataTable with correct parameters', async () => { + const options = { name: 'updatedDataTable' }; - const dataStoreOperations = await dataStoreProxyService.getDataStoreProxy( + const dataStoreOperations = await dataStoreProxyService.getDataTableProxy( workflow, node, 'dataStore-id', ); - await dataStoreOperations.updateDataStore(options); + await dataStoreOperations.updateDataTable(options); - expect(dataStoreServiceMock.updateDataStore).toBeCalledWith( + expect(dataTableServiceMock.updateDataTable).toBeCalledWith( 'dataStore-id', PROJECT_ID, options, ); }); - it('should call deleteDataStore with correct parameters', async () => { - const dataStoreOperations = await dataStoreProxyService.getDataStoreProxy( + it('should call deleteDataTable with correct parameters', async () => { + const dataStoreOperations = await dataStoreProxyService.getDataTableProxy( workflow, node, 'dataStore-id', ); - await dataStoreOperations.deleteDataStore(); + await dataStoreOperations.deleteDataTable(); - expect(dataStoreServiceMock.deleteDataStore).toBeCalledWith('dataStore-id', PROJECT_ID); + expect(dataTableServiceMock.deleteDataTable).toBeCalledWith('dataStore-id', PROJECT_ID); }); it('should call getColumns with correct parameters', async () => { - const dataStoreOperations = await dataStoreProxyService.getDataStoreProxy( + const dataStoreOperations = await dataStoreProxyService.getDataTableProxy( workflow, node, 'dataStore-id', ); await dataStoreOperations.getColumns(); - expect(dataStoreServiceMock.getColumns).toBeCalledWith('dataStore-id', PROJECT_ID); + expect(dataTableServiceMock.getColumns).toBeCalledWith('dataStore-id', PROJECT_ID); }); it('should call addColumn with correct parameters', async () => { - const options: AddDataStoreColumnOptions = { name: 'newColumn', type: 'string' }; + const options: AddDataTableColumnOptions = { name: 'newColumn', type: 'string' }; - const dataStoreOperations = await dataStoreProxyService.getDataStoreProxy( + const dataStoreOperations = await dataStoreProxyService.getDataTableProxy( workflow, node, 'dataStore-id', ); await dataStoreOperations.addColumn(options); - expect(dataStoreServiceMock.addColumn).toBeCalledWith('dataStore-id', PROJECT_ID, options); + expect(dataTableServiceMock.addColumn).toBeCalledWith('dataStore-id', PROJECT_ID, options); }); it('should call moveColumn with correct parameters', async () => { const columnId = 'column-id'; - const options: MoveDataStoreColumnOptions = { targetIndex: 1 }; + const options: MoveDataTableColumnOptions = { targetIndex: 1 }; - const dataStoreOperations = await dataStoreProxyService.getDataStoreProxy( + const dataStoreOperations = await dataStoreProxyService.getDataTableProxy( workflow, node, 'dataStore-id', ); await dataStoreOperations.moveColumn(columnId, options); - expect(dataStoreServiceMock.moveColumn).toBeCalledWith( + expect(dataTableServiceMock.moveColumn).toBeCalledWith( 'dataStore-id', PROJECT_ID, columnId, @@ -167,32 +167,32 @@ describe('DataStoreProxyService', () => { it('should call deleteColumn with correct parameters', async () => { const columnId = 'column-id'; - const dataStoreOperations = await dataStoreProxyService.getDataStoreProxy( + const dataStoreOperations = await dataStoreProxyService.getDataTableProxy( workflow, node, 'dataStore-id', ); await dataStoreOperations.deleteColumn(columnId); - expect(dataStoreServiceMock.deleteColumn).toBeCalledWith('dataStore-id', PROJECT_ID, columnId); + expect(dataTableServiceMock.deleteColumn).toBeCalledWith('dataStore-id', PROJECT_ID, columnId); }); it('should call getManyRowsAndCount with correct parameters', async () => { - const options: ListDataStoreRowsOptions = { + const options: ListDataTableRowsOptions = { filter: { filters: [{ columnName: 'x', condition: 'eq', value: 'testRow' }], type: 'and', }, }; - const dataStoreOperations = await dataStoreProxyService.getDataStoreProxy( + const dataStoreOperations = await dataStoreProxyService.getDataTableProxy( workflow, node, 'dataStore-id', ); await dataStoreOperations.getManyRowsAndCount(options); - expect(dataStoreServiceMock.getManyRowsAndCount).toBeCalledWith( + expect(dataTableServiceMock.getManyRowsAndCount).toBeCalledWith( 'dataStore-id', PROJECT_ID, options, @@ -202,14 +202,14 @@ describe('DataStoreProxyService', () => { it('should call insertRows with correct parameters', async () => { const rows = [{ id: 1, name: 'row1' }]; - const dataStoreOperations = await dataStoreProxyService.getDataStoreProxy( + const dataStoreOperations = await dataStoreProxyService.getDataTableProxy( workflow, node, 'dataStore-id', ); await dataStoreOperations.insertRows(rows, 'count'); - expect(dataStoreServiceMock.insertRows).toBeCalledWith( + expect(dataTableServiceMock.insertRows).toBeCalledWith( 'dataStore-id', PROJECT_ID, rows, @@ -218,7 +218,7 @@ describe('DataStoreProxyService', () => { }); it('should call upsertRow with correct parameters', async () => { - const options: UpsertDataStoreRowOptions = { + const options: UpsertDataTableRowOptions = { filter: { filters: [{ columnName: 'name', condition: 'eq', value: 'test' }], type: 'and', @@ -226,14 +226,14 @@ describe('DataStoreProxyService', () => { data: { name: 'newName' }, }; - const dataStoreOperations = await dataStoreProxyService.getDataStoreProxy( + const dataStoreOperations = await dataStoreProxyService.getDataTableProxy( workflow, node, 'dataStore-id', ); await dataStoreOperations.upsertRow(options); - expect(dataStoreServiceMock.upsertRow).toBeCalledWith( + expect(dataTableServiceMock.upsertRow).toBeCalledWith( 'dataStore-id', PROJECT_ID, options, @@ -243,7 +243,7 @@ describe('DataStoreProxyService', () => { }); it('should call upsertRow dry run with correct parameters', async () => { - const options: UpsertDataStoreRowOptions = { + const options: UpsertDataTableRowOptions = { filter: { filters: [{ columnName: 'name', condition: 'eq', value: 'test' }], type: 'and', @@ -252,14 +252,14 @@ describe('DataStoreProxyService', () => { dryRun: true, }; - const dataStoreOperations = await dataStoreProxyService.getDataStoreProxy( + const dataStoreOperations = await dataStoreProxyService.getDataTableProxy( workflow, node, 'dataStore-id', ); await dataStoreOperations.upsertRow(options); - expect(dataStoreServiceMock.upsertRow).toBeCalledWith( + expect(dataTableServiceMock.upsertRow).toBeCalledWith( 'dataStore-id', PROJECT_ID, options, diff --git a/packages/cli/src/modules/data-table/__tests__/data-store-size-validator.service.test.ts b/packages/cli/src/modules/data-table/__tests__/data-store-size-validator.service.test.ts index af9addb2e1d..af5110a168d 100644 --- a/packages/cli/src/modules/data-table/__tests__/data-store-size-validator.service.test.ts +++ b/packages/cli/src/modules/data-table/__tests__/data-store-size-validator.service.test.ts @@ -1,10 +1,10 @@ import { mockInstance } from '@n8n/backend-test-utils'; import { GlobalConfig } from '@n8n/config'; -import { DataStoreSizeValidator } from '../data-store-size-validator.service'; +import { DataTableSizeValidator } from '../data-table-size-validator.service'; -describe('DataStoreSizeValidator', () => { - let validator: DataStoreSizeValidator; +describe('DataTableSizeValidator', () => { + let validator: DataTableSizeValidator; let fetchSizeFn: jest.Mock; const globalConfig = mockInstance(GlobalConfig, { dataTable: { @@ -14,7 +14,7 @@ describe('DataStoreSizeValidator', () => { }, }); beforeEach(() => { - validator = new DataStoreSizeValidator(globalConfig); + validator = new DataTableSizeValidator(globalConfig); fetchSizeFn = jest.fn(); }); @@ -40,7 +40,7 @@ describe('DataStoreSizeValidator', () => { await expect( validator.validateSize(fetchSizeFn, new Date('2024-01-01T00:00:00Z')), - ).rejects.toThrow('Data store size limit exceeded: 150MB used, limit is 100MB'); + ).rejects.toThrow('Data table size limit exceeded: 150MB used, limit is 100MB'); }); it('should throw error when size equals limit', async () => { @@ -48,7 +48,7 @@ describe('DataStoreSizeValidator', () => { await expect( validator.validateSize(fetchSizeFn, new Date('2024-01-01T00:00:00Z')), - ).rejects.toThrow('Data store size limit exceeded: 100MB used, limit is 100MB'); + ).rejects.toThrow('Data table size limit exceeded: 100MB used, limit is 100MB'); }); }); @@ -95,13 +95,13 @@ describe('DataStoreSizeValidator', () => { const time1 = new Date('2024-01-01T00:00:00Z'); await expect(validator.validateSize(fetchSizeFn, time1)).rejects.toThrow( - 'Data store size limit exceeded: 100MB used, limit is 100MB', + 'Data table size limit exceeded: 100MB used, limit is 100MB', ); // Subsequent calls within cache duration should also fail const time2 = new Date('2024-01-01T00:00:00.500Z'); await expect(validator.validateSize(fetchSizeFn, time2)).rejects.toThrow( - 'Data store size limit exceeded: 100MB used, limit is 100MB', + 'Data table size limit exceeded: 100MB used, limit is 100MB', ); // Size was only fetched once @@ -161,13 +161,13 @@ describe('DataStoreSizeValidator', () => { // All should fail with the same error await expect(promise1).rejects.toThrow( - 'Data store size limit exceeded: 150MB used, limit is 100MB', + 'Data table size limit exceeded: 150MB used, limit is 100MB', ); await expect(promise2).rejects.toThrow( - 'Data store size limit exceeded: 150MB used, limit is 100MB', + 'Data table size limit exceeded: 150MB used, limit is 100MB', ); await expect(promise3).rejects.toThrow( - 'Data store size limit exceeded: 150MB used, limit is 100MB', + 'Data table size limit exceeded: 150MB used, limit is 100MB', ); // Should only fetch once @@ -219,13 +219,13 @@ describe('DataStoreSizeValidator', () => { fetchSizeFn.mockResolvedValueOnce({ totalBytes: 100 * 1024 * 1024, dataTables: {} }); const time3 = new Date('2024-01-01T00:00:01.001Z'); await expect(validator.validateSize(fetchSizeFn, time3)).rejects.toThrow( - 'Data store size limit exceeded: 100MB used, limit is 100MB', + 'Data table size limit exceeded: 100MB used, limit is 100MB', ); // Subsequent calls use the cached "full" state and continue to fail correctly const time4 = new Date('2024-01-01T00:00:01.500Z'); await expect(validator.validateSize(fetchSizeFn, time4)).rejects.toThrow( - 'Data store size limit exceeded: 100MB used, limit is 100MB', + 'Data table size limit exceeded: 100MB used, limit is 100MB', ); expect(fetchSizeFn).toHaveBeenCalledTimes(2); diff --git a/packages/cli/src/modules/data-table/__tests__/data-store-size.integration.test.ts b/packages/cli/src/modules/data-table/__tests__/data-store-size.integration.test.ts index 5ce167c0716..f2f6af2efbd 100644 --- a/packages/cli/src/modules/data-table/__tests__/data-store-size.integration.test.ts +++ b/packages/cli/src/modules/data-table/__tests__/data-store-size.integration.test.ts @@ -5,10 +5,10 @@ import { Container } from '@n8n/di'; import { createUser } from '@test-integration/db/users'; -import { DataStoreSizeValidator } from '../data-store-size-validator.service'; -import { DataStoreRepository } from '../data-store.repository'; -import { DataStoreService } from '../data-store.service'; -import { DataStoreValidationError } from '../errors/data-store-validation.error'; +import { DataTableSizeValidator } from '../data-table-size-validator.service'; +import { DataTableRepository } from '../data-table.repository'; +import { DataTableService } from '../data-table.service'; +import { DataTableValidationError } from '../errors/data-table-validation.error'; beforeAll(async () => { await testModules.loadModules(['data-table']); @@ -16,11 +16,11 @@ beforeAll(async () => { }); beforeEach(async () => { - const dataStoreService = Container.get(DataStoreService); - await dataStoreService.deleteDataStoreAll(); + const dataStoreService = Container.get(DataTableService); + await dataStoreService.deleteDataTableAll(); await testDb.truncate(['DataTable', 'DataTableColumn']); - const dataStoreSizeValidator = Container.get(DataStoreSizeValidator); + const dataStoreSizeValidator = Container.get(DataTableSizeValidator); dataStoreSizeValidator.reset(); }); @@ -28,13 +28,13 @@ afterAll(async () => { await testDb.terminate(); }); -describe('Data Store Size Tests', () => { - let dataStoreService: DataStoreService; - let dataStoreRepository: DataStoreRepository; +describe('Data Table Size Tests', () => { + let dataStoreService: DataTableService; + let dataStoreRepository: DataTableRepository; beforeAll(() => { - dataStoreService = Container.get(DataStoreService); - dataStoreRepository = Container.get(DataStoreRepository); + dataStoreService = Container.get(DataTableService); + dataStoreRepository = Container.get(DataTableRepository); }); let project1: Project; @@ -46,12 +46,12 @@ describe('Data Store Size Tests', () => { describe('size validation', () => { it('should prevent insertRows when size limit exceeded', async () => { // ARRANGE - const dataStoreSizeValidator = Container.get(DataStoreSizeValidator); + const dataStoreSizeValidator = Container.get(DataTableSizeValidator); dataStoreSizeValidator.reset(); const maxSize = Container.get(GlobalConfig).dataTable.maxSize; - const { id: dataStoreId } = await dataStoreService.createDataStore(project1.id, { + const { id: dataStoreId } = await dataStoreService.createDataTable(project1.id, { name: 'dataStore', columns: [{ name: 'data', type: 'string' }], }); @@ -63,7 +63,7 @@ describe('Data Store Size Tests', () => { // ACT & ASSERT await expect( dataStoreService.insertRows(dataStoreId, project1.id, [{ data: 'test' }]), - ).rejects.toThrow(DataStoreValidationError); + ).rejects.toThrow(DataTableValidationError); expect(mockFindDataTablesSize).toHaveBeenCalled(); mockFindDataTablesSize.mockRestore(); @@ -71,12 +71,12 @@ describe('Data Store Size Tests', () => { it('should prevent updateRows when size limit exceeded', async () => { // ARRANGE - const dataStoreSizeValidator = Container.get(DataStoreSizeValidator); + const dataStoreSizeValidator = Container.get(DataTableSizeValidator); dataStoreSizeValidator.reset(); const maxSize = Container.get(GlobalConfig).dataTable.maxSize; - const { id: dataStoreId } = await dataStoreService.createDataStore(project1.id, { + const { id: dataStoreId } = await dataStoreService.createDataTable(project1.id, { name: 'dataStore', columns: [{ name: 'data', type: 'string' }], }); @@ -95,7 +95,7 @@ describe('Data Store Size Tests', () => { }, data: { data: 'updated' }, }), - ).rejects.toThrow(DataStoreValidationError); + ).rejects.toThrow(DataTableValidationError); expect(mockFindDataTablesSize).toHaveBeenCalled(); mockFindDataTablesSize.mockRestore(); @@ -106,7 +106,7 @@ describe('Data Store Size Tests', () => { const maxSize = Container.get(GlobalConfig).dataTable.maxSize; - const { id: dataStoreId } = await dataStoreService.createDataStore(project1.id, { + const { id: dataStoreId } = await dataStoreService.createDataTable(project1.id, { name: 'dataStore', columns: [{ name: 'data', type: 'string' }], }); @@ -124,7 +124,7 @@ describe('Data Store Size Tests', () => { }, data: { data: 'new' }, }), - ).rejects.toThrow(DataStoreValidationError); + ).rejects.toThrow(DataTableValidationError); expect(mockFindDataTablesSize).toHaveBeenCalled(); mockFindDataTablesSize.mockRestore(); @@ -150,12 +150,12 @@ describe('Data Store Size Tests', () => { it('should return all data tables for admin user', async () => { // ARRANGE - const dataStore1 = await dataStoreService.createDataStore(project1.id, { + const dataStore1 = await dataStoreService.createDataTable(project1.id, { name: 'project1-dataStore', columns: [{ name: 'data', type: 'string' }], }); - const dataStore2 = await dataStoreService.createDataStore(project2.id, { + const dataStore2 = await dataStoreService.createDataTable(project2.id, { name: 'project2-dataStore', columns: [{ name: 'data', type: 'string' }], }); @@ -201,12 +201,12 @@ describe('Data Store Size Tests', () => { // ARRANGE await linkUserToProject(regularUser, project1, 'project:viewer'); - const dataStore1 = await dataStoreService.createDataStore(project1.id, { + const dataStore1 = await dataStoreService.createDataTable(project1.id, { name: 'accessible-dataStore', columns: [{ name: 'data', type: 'string' }], }); - const dataStore2 = await dataStoreService.createDataStore(project2.id, { + const dataStore2 = await dataStoreService.createDataTable(project2.id, { name: 'inaccessible-dataStore', columns: [{ name: 'data', type: 'string' }], }); @@ -233,12 +233,12 @@ describe('Data Store Size Tests', () => { it('should return empty dataTables but full totalBytes when user has no project access', async () => { // ARRANGE - const dataStore1 = await dataStoreService.createDataStore(project1.id, { + const dataStore1 = await dataStoreService.createDataTable(project1.id, { name: 'inaccessible-dataStore1', columns: [{ name: 'data', type: 'string' }], }); - const dataStore2 = await dataStoreService.createDataStore(project2.id, { + const dataStore2 = await dataStoreService.createDataTable(project2.id, { name: 'inaccessible-dataStore2', columns: [{ name: 'data', type: 'string' }], }); @@ -271,8 +271,8 @@ describe('Data Store Size Tests', () => { it('should handle data tables with no rows', async () => { // ARRANGE - const dataStore = await dataStoreService.createDataStore(project1.id, { - name: 'emptyDataStore', + const dataStore = await dataStoreService.createDataTable(project1.id, { + name: 'emptyDataTable', columns: [{ name: 'data', type: 'string' }], }); @@ -285,7 +285,7 @@ describe('Data Store Size Tests', () => { expect(result.dataTables).toBeDefined(); expect(result.dataTables[dataStore.id]).toBeDefined(); expect(result.dataTables[dataStore.id].sizeBytes).toBeGreaterThanOrEqual(0); - expect(result.dataTables[dataStore.id].name).toBe('emptyDataStore'); + expect(result.dataTables[dataStore.id].name).toBe('emptyDataTable'); }); }); @@ -303,7 +303,7 @@ describe('Data Store Size Tests', () => { it('should cache data globally and filter based on user permissions', async () => { // ARRANGE - const dataStore = await dataStoreService.createDataStore(project1.id, { + const dataStore = await dataStoreService.createDataTable(project1.id, { name: 'test-dataStore', columns: [{ name: 'data', type: 'string' }], }); @@ -332,8 +332,8 @@ describe('Data Store Size Tests', () => { it('should use global cache for both data fetching and size validation', async () => { // ARRANGE - const dataStoreSizeValidator = Container.get(DataStoreSizeValidator); - const dataStore = await dataStoreService.createDataStore(project1.id, { + const dataStoreSizeValidator = Container.get(DataTableSizeValidator); + const dataStore = await dataStoreService.createDataTable(project1.id, { name: 'test-dataStore', columns: [{ name: 'data', type: 'string' }], }); @@ -366,8 +366,8 @@ describe('Data Store Size Tests', () => { it('should invalidate cache on data modifications', async () => { // ARRANGE - const dataStoreSizeValidator = Container.get(DataStoreSizeValidator); - const dataStore = await dataStoreService.createDataStore(project1.id, { + const dataStoreSizeValidator = Container.get(DataTableSizeValidator); + const dataStore = await dataStoreService.createDataTable(project1.id, { name: 'test-dataStore', columns: [{ name: 'data', type: 'string' }], }); diff --git a/packages/cli/src/modules/data-table/__tests__/data-store.controller.integration.test.ts b/packages/cli/src/modules/data-table/__tests__/data-store.controller.integration.test.ts index 14928ecec23..403274ee95c 100644 --- a/packages/cli/src/modules/data-table/__tests__/data-store.controller.integration.test.ts +++ b/packages/cli/src/modules/data-table/__tests__/data-store.controller.integration.test.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ -import type { DataStore, DataStoreCreateColumnSchema } from '@n8n/api-types'; +import type { DataTable, DataTableCreateColumnSchema } from '@n8n/api-types'; import { createTeamProject, getPersonalProject, @@ -11,17 +11,17 @@ import type { Project, User } from '@n8n/db'; import { ProjectRepository, QueryFailedError } from '@n8n/db'; import { Container } from '@n8n/di'; import { DateTime } from 'luxon'; -import type { DataStoreRow } from 'n8n-workflow'; +import type { DataTableRow } from 'n8n-workflow'; -import { createDataStore } from '@test-integration/db/data-stores'; +import { createDataTable } from '@test-integration/db/data-tables'; import { createOwner, createMember, createAdmin } from '@test-integration/db/users'; import type { SuperAgentTest } from '@test-integration/types'; import * as utils from '@test-integration/utils'; -import { DataStoreColumnRepository } from '../data-store-column.repository'; -import { DataStoreRowsRepository } from '../data-store-rows.repository'; -import { DataStoreRepository } from '../data-store.repository'; -import { mockDataStoreSizeValidator } from './test-helpers'; +import { DataTableColumnRepository } from '../data-table-column.repository'; +import { DataTableRowsRepository } from '../data-table-rows.repository'; +import { DataTableRepository } from '../data-table.repository'; +import { mockDataTableSizeValidator } from './test-helpers'; let owner: User; let member: User; @@ -37,17 +37,17 @@ const testServer = utils.setupTestServer({ modules: ['data-table'], }); let projectRepository: ProjectRepository; -let dataStoreRepository: DataStoreRepository; -let dataStoreColumnRepository: DataStoreColumnRepository; -let dataStoreRowsRepository: DataStoreRowsRepository; +let dataTableRepository: DataTableRepository; +let dataTableColumnRepository: DataTableColumnRepository; +let dataTableRowsRepository: DataTableRowsRepository; beforeAll(async () => { - mockDataStoreSizeValidator(); + mockDataTableSizeValidator(); projectRepository = Container.get(ProjectRepository); - dataStoreRepository = Container.get(DataStoreRepository); - dataStoreColumnRepository = Container.get(DataStoreColumnRepository); - dataStoreRowsRepository = Container.get(DataStoreRowsRepository); + dataTableRepository = Container.get(DataTableRepository); + dataTableColumnRepository = Container.get(DataTableColumnRepository); + dataTableRowsRepository = Container.get(DataTableRowsRepository); owner = await createOwner(); member = await createMember(); @@ -66,9 +66,9 @@ beforeEach(async () => { }); describe('POST /projects/:projectId/data-tables', () => { - test('should not create data store when project does not exist', async () => { + test('should not create data table when project does not exist', async () => { const payload = { - name: 'Test Data Store', + name: 'Test Data Table', columns: [ { name: 'test_ccolumn', @@ -82,7 +82,7 @@ describe('POST /projects/:projectId/data-tables', () => { await authOwnerAgent.post('/projects/non-existing-id/data-tables').send(payload).expect(403); }); - test('should not create data store when name is empty', async () => { + test('should not create data table when name is empty', async () => { const project = await createTeamProject(undefined, owner); const payload = { name: '', @@ -97,12 +97,12 @@ describe('POST /projects/:projectId/data-tables', () => { await authOwnerAgent.post(`/projects/${project.id}/data-tables`).send(payload).expect(400); }); - test('should not create data store if user has project:viewer role in team project', async () => { + test('should not create data table if user has project:viewer role in team project', async () => { const project = await createTeamProject(undefined, owner); await linkUserToProject(member, project, 'project:viewer'); const payload = { - name: 'Test Data Store', + name: 'Test Data Table', columns: [ { name: 'test_ccolumn', @@ -113,13 +113,13 @@ describe('POST /projects/:projectId/data-tables', () => { await authMemberAgent.post(`/projects/${project.id}/data-tables`).send(payload).expect(403); - const dataStoresInDb = await dataStoreRepository.find(); - expect(dataStoresInDb).toHaveLength(0); + const dataTablesInDb = await dataTableRepository.find(); + expect(dataTablesInDb).toHaveLength(0); }); - test("should not create data store in another user's personal project", async () => { + test("should not create data table in another user's personal project", async () => { const payload = { - name: 'Test Data Store', + name: 'Test Data Table', columns: [ { name: 'test_ccolumn', @@ -134,12 +134,12 @@ describe('POST /projects/:projectId/data-tables', () => { .expect(403); }); - test('should create data store if user has project:editor role in team project', async () => { + test('should create data table if user has project:editor role in team project', async () => { const project = await createTeamProject(undefined, owner); await linkUserToProject(member, project, 'project:editor'); const payload = { - name: 'Test Data Store', + name: 'Test Data Table', columns: [ { name: 'test_ccolumn', @@ -150,16 +150,16 @@ describe('POST /projects/:projectId/data-tables', () => { await authMemberAgent.post(`/projects/${project.id}/data-tables`).send(payload).expect(200); - const dataStoresInDb = await dataStoreRepository.find(); - expect(dataStoresInDb).toHaveLength(1); + const dataTablesInDb = await dataTableRepository.find(); + expect(dataTablesInDb).toHaveLength(1); }); - test('should create data store if user has project:admin role in team project', async () => { + test('should create data table if user has project:admin role in team project', async () => { const project = await createTeamProject(undefined, owner); await linkUserToProject(admin, project, 'project:admin'); const payload = { - name: 'Test Data Store', + name: 'Test Data Table', columns: [ { name: 'test_ccolumn', @@ -170,15 +170,15 @@ describe('POST /projects/:projectId/data-tables', () => { await authAdminAgent.post(`/projects/${project.id}/data-tables`).send(payload).expect(200); - const dataStoresInDb = await dataStoreRepository.find(); - expect(dataStoresInDb).toHaveLength(1); + const dataTablesInDb = await dataTableRepository.find(); + expect(dataTablesInDb).toHaveLength(1); }); - test('should create data store if user is owner in team project', async () => { + test('should create data table if user is owner in team project', async () => { const project = await createTeamProject(undefined, owner); const payload = { - name: 'Test Data Store', + name: 'Test Data Table', columns: [ { name: 'test_ccolumn', @@ -189,14 +189,14 @@ describe('POST /projects/:projectId/data-tables', () => { await authOwnerAgent.post(`/projects/${project.id}/data-tables`).send(payload).expect(200); - const dataStoresInDb = await dataStoreRepository.find(); - expect(dataStoresInDb).toHaveLength(1); + const dataTablesInDb = await dataTableRepository.find(); + expect(dataTablesInDb).toHaveLength(1); }); - test('should create data store in personal project', async () => { + test('should create data table in personal project', async () => { const personalProject = await projectRepository.getPersonalProjectForUserOrFail(owner.id); const payload = { - name: 'Test Data Store', + name: 'Test Data Table', columns: [ { name: 'test_ccolumn', @@ -220,50 +220,50 @@ describe('POST /projects/:projectId/data-tables', () => { }), ); - const dataStoreInDb = await dataStoreRepository.findOneBy({ id: response.body.data.id }); - expect(dataStoreInDb).toBeDefined(); - expect(dataStoreInDb?.name).toBe(payload.name); + const dataTableInDb = await dataTableRepository.findOneBy({ id: response.body.data.id }); + expect(dataTableInDb).toBeDefined(); + expect(dataTableInDb?.name).toBe(payload.name); }); }); describe('GET /projects/:projectId/data-tables', () => { - test('should not list data stores when project does not exist', async () => { + test('should not list data tables when project does not exist', async () => { await authMemberAgent.get('/projects/non-existing-id/data-tables').expect(403); await authAdminAgent.get('/projects/non-existing-id/data-tables').expect(403); await authOwnerAgent.get('/projects/non-existing-id/data-tables').expect(403); }); - test('should not list data stores if user has no access to project', async () => { + test('should not list data tables if user has no access to project', async () => { const project = await createTeamProject('test project', owner); await authMemberAgent.get(`/projects/${project.id}/data-tables`).expect(403); }); - test('should not list data stores if admin has no access to project', async () => { + test('should not list data tables if admin has no access to project', async () => { const project = await createTeamProject('test project', owner); await authAdminAgent.get(`/projects/${project.id}/data-tables`).expect(403); }); - test("should not list data stores from another user's personal project", async () => { + test("should not list data tables from another user's personal project", async () => { await authMemberAgent.get(`/projects/${ownerProject.id}/data-tables`).expect(403); }); - test('should list data stores if user has project:viewer role in team project', async () => { + test('should list data tables if user has project:viewer role in team project', async () => { const project = await createTeamProject('test project', owner); await linkUserToProject(member, project, 'project:viewer'); - await createDataStore(project, { name: 'Test Data Store' }); + await createDataTable(project, { name: 'Test Data Table' }); const response = await authMemberAgent.get(`/projects/${project.id}/data-tables`).expect(200); expect(response.body.data.count).toBe(1); expect(response.body.data.data).toHaveLength(1); - expect(response.body.data.data[0].name).toBe('Test Data Store'); + expect(response.body.data.data[0].name).toBe('Test Data Table'); }); - test('should list data stores from personal project', async () => { - await createDataStore(ownerProject, { name: 'Personal Data Store 1' }); - await createDataStore(ownerProject, { name: 'Personal Data Store 2' }); + test('should list data tables from personal project', async () => { + await createDataTable(ownerProject, { name: 'Personal Data Table 1' }); + await createDataTable(ownerProject, { name: 'Personal Data Table 2' }); const response = await authOwnerAgent .get(`/projects/${ownerProject.id}/data-tables`) @@ -271,15 +271,15 @@ describe('GET /projects/:projectId/data-tables', () => { expect(response.body.data.count).toBe(2); expect(response.body.data.data).toHaveLength(2); - expect((response.body.data.data as DataStore[]).map((f) => f.name).sort()).toEqual( - ['Personal Data Store 1', 'Personal Data Store 2'].sort(), + expect((response.body.data.data as DataTable[]).map((f) => f.name).sort()).toEqual( + ['Personal Data Table 1', 'Personal Data Table 2'].sort(), ); }); - test('should filter data stores by projectId', async () => { - await createDataStore(ownerProject, { name: 'Test Data Store 1' }); - await createDataStore(ownerProject, { name: 'Test Data Store 2' }); - await createDataStore(memberProject, { name: 'Another Data Store' }); + test('should filter data tables by projectId', async () => { + await createDataTable(ownerProject, { name: 'Test Data Table 1' }); + await createDataTable(ownerProject, { name: 'Test Data Table 2' }); + await createDataTable(memberProject, { name: 'Another Data Table' }); const response = await authOwnerAgent .get(`/projects/${ownerProject.id}/data-tables`) @@ -288,15 +288,15 @@ describe('GET /projects/:projectId/data-tables', () => { expect(response.body.data.count).toBe(2); expect(response.body.data.data).toHaveLength(2); - expect((response.body.data.data as DataStore[]).map((f) => f.name).sort()).toEqual( - ['Test Data Store 1', 'Test Data Store 2'].sort(), + expect((response.body.data.data as DataTable[]).map((f) => f.name).sort()).toEqual( + ['Test Data Table 1', 'Test Data Table 2'].sort(), ); }); - test('should filter data stores by name', async () => { - await createDataStore(ownerProject, { name: 'Test Data Store' }); - await createDataStore(ownerProject, { name: 'Another Data Store' }); - await createDataStore(ownerProject, { name: 'Test Something Else' }); + test('should filter data tables by name', async () => { + await createDataTable(ownerProject, { name: 'Test Data Table' }); + await createDataTable(ownerProject, { name: 'Another Data Table' }); + await createDataTable(ownerProject, { name: 'Test Something Else' }); const response = await authOwnerAgent .get(`/projects/${ownerProject.id}/data-tables`) @@ -305,45 +305,45 @@ describe('GET /projects/:projectId/data-tables', () => { expect(response.body.data.count).toBe(2); expect(response.body.data.data).toHaveLength(2); - expect((response.body.data.data as DataStore[]).map((f) => f.name).sort()).toEqual( - ['Test Data Store', 'Test Something Else'].sort(), + expect((response.body.data.data as DataTable[]).map((f) => f.name).sort()).toEqual( + ['Test Data Table', 'Test Something Else'].sort(), ); }); - test('should filter data stores by id', async () => { - const dataStore1 = await createDataStore(ownerProject, { name: 'Data Store 1' }); - await createDataStore(ownerProject, { name: 'Data Store 2' }); - await createDataStore(ownerProject, { name: 'Data Store 3' }); + test('should filter data tables by id', async () => { + const dataTable1 = await createDataTable(ownerProject, { name: 'Data Table 1' }); + await createDataTable(ownerProject, { name: 'Data Table 2' }); + await createDataTable(ownerProject, { name: 'Data Table 3' }); const response = await authOwnerAgent .get(`/projects/${ownerProject.id}/data-tables`) - .query({ filter: JSON.stringify({ id: dataStore1.id }) }) + .query({ filter: JSON.stringify({ id: dataTable1.id }) }) .expect(200); expect(response.body.data.count).toBe(1); expect(response.body.data.data).toHaveLength(1); - expect(response.body.data.data[0].name).toBe('Data Store 1'); + expect(response.body.data.data[0].name).toBe('Data Table 1'); }); - test('should filter data stores by multiple names (AND operator)', async () => { - await createDataStore(ownerProject, { name: 'Data Store' }); - await createDataStore(ownerProject, { name: 'Test Store' }); - await createDataStore(ownerProject, { name: 'Another Store' }); + test('should filter data tables by multiple names (AND operator)', async () => { + await createDataTable(ownerProject, { name: 'Data Table' }); + await createDataTable(ownerProject, { name: 'Test Table' }); + await createDataTable(ownerProject, { name: 'Another Table' }); const response = await authOwnerAgent .get(`/projects/${ownerProject.id}/data-tables`) - .query({ filter: JSON.stringify({ name: ['Store', 'Test'] }) }) + .query({ filter: JSON.stringify({ name: ['Table', 'Test'] }) }) .expect(200); expect(response.body.data.count).toBe(1); expect(response.body.data.data).toHaveLength(1); - expect(response.body.data.data[0].name).toBe('Test Store'); + expect(response.body.data.data[0].name).toBe('Test Table'); }); test('should apply pagination with take parameter', async () => { for (let i = 1; i <= 5; i++) { - await createDataStore(ownerProject, { - name: `Data Store ${i}`, + await createDataTable(ownerProject, { + name: `Data Table ${i}`, updatedAt: DateTime.now() .minus({ minutes: 6 - i }) .toJSDate(), @@ -357,17 +357,17 @@ describe('GET /projects/:projectId/data-tables', () => { 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 as DataStore[]).map((store) => store.name)).toEqual([ - 'Data Store 5', - 'Data Store 4', - 'Data Store 3', + expect((response.body.data.data as DataTable[]).map((store) => store.name)).toEqual([ + 'Data Table 5', + 'Data Table 4', + 'Data Table 3', ]); }); test('should apply pagination with skip parameter', async () => { for (let i = 1; i <= 5; i++) { - await createDataStore(ownerProject, { - name: `Data Store ${i}`, + await createDataTable(ownerProject, { + name: `Data Table ${i}`, updatedAt: DateTime.now() .minus({ minutes: 6 - i }) .toJSDate(), @@ -381,17 +381,17 @@ describe('GET /projects/:projectId/data-tables', () => { expect(response.body.data.count).toBe(5); expect(response.body.data.data).toHaveLength(3); - expect((response.body.data.data as DataStore[]).map((store) => store.name)).toEqual([ - 'Data Store 3', - 'Data Store 2', - 'Data Store 1', + expect((response.body.data.data as DataTable[]).map((store) => store.name)).toEqual([ + 'Data Table 3', + 'Data Table 2', + 'Data Table 1', ]); }); test('should apply combined skip and take parameters', async () => { for (let i = 1; i <= 5; i++) { - await createDataStore(ownerProject, { - name: `Data Store ${i}`, + await createDataTable(ownerProject, { + name: `Data Table ${i}`, updatedAt: DateTime.now() .minus({ minutes: 6 - i }) .toJSDate(), @@ -405,57 +405,57 @@ describe('GET /projects/:projectId/data-tables', () => { expect(response.body.data.count).toBe(5); expect(response.body.data.data).toHaveLength(2); - expect((response.body.data.data as DataStore[]).map((store) => store.name)).toEqual([ - 'Data Store 4', - 'Data Store 3', + expect((response.body.data.data as DataTable[]).map((store) => store.name)).toEqual([ + 'Data Table 4', + 'Data Table 3', ]); }); - test('should sort data stores by name ascending', async () => { - await createDataStore(ownerProject, { name: 'Z Data Store' }); - await createDataStore(ownerProject, { name: 'A Data Store' }); - await createDataStore(ownerProject, { name: 'M Data Store' }); + test('should sort data tables by name ascending', async () => { + await createDataTable(ownerProject, { name: 'Z Data Table' }); + await createDataTable(ownerProject, { name: 'A Data Table' }); + await createDataTable(ownerProject, { name: 'M Data Table' }); const response = await authOwnerAgent .get(`/projects/${ownerProject.id}/data-tables`) .query({ sortBy: 'name:asc' }) .expect(200); - expect((response.body.data.data as DataStore[]).map((store) => store.name)).toEqual([ - 'A Data Store', - 'M Data Store', - 'Z Data Store', + expect((response.body.data.data as DataTable[]).map((store) => store.name)).toEqual([ + 'A Data Table', + 'M Data Table', + 'Z Data Table', ]); }); - test('should sort data stores by name descending', async () => { - await createDataStore(ownerProject, { name: 'Z Data Store' }); - await createDataStore(ownerProject, { name: 'A Data Store' }); - await createDataStore(ownerProject, { name: 'M Data Store' }); + test('should sort data tables by name descending', async () => { + await createDataTable(ownerProject, { name: 'Z Data Table' }); + await createDataTable(ownerProject, { name: 'A Data Table' }); + await createDataTable(ownerProject, { name: 'M Data Table' }); const response = await authOwnerAgent .get(`/projects/${ownerProject.id}/data-tables`) .query({ sortBy: 'name:desc' }) .expect(200); - expect((response.body.data.data as DataStore[]).map((f) => f.name)).toEqual([ - 'Z Data Store', - 'M Data Store', - 'A Data Store', + expect((response.body.data.data as DataTable[]).map((f) => f.name)).toEqual([ + 'Z Data Table', + 'M Data Table', + 'A Data Table', ]); }); - test('should sort data stores by updatedAt', async () => { - await createDataStore(ownerProject, { - name: 'Older Data Store', + test('should sort data tables by updatedAt', async () => { + await createDataTable(ownerProject, { + name: 'Older Data Table', updatedAt: DateTime.now().minus({ days: 2 }).toJSDate(), }); - await createDataStore(ownerProject, { - name: 'Newest Data Store', + await createDataTable(ownerProject, { + name: 'Newest Data Table', updatedAt: DateTime.now().toJSDate(), }); - await createDataStore(ownerProject, { - name: 'Middle Data Store', + await createDataTable(ownerProject, { + name: 'Middle Data Table', updatedAt: DateTime.now().minus({ days: 1 }).toJSDate(), }); @@ -464,30 +464,30 @@ describe('GET /projects/:projectId/data-tables', () => { .query({ sortBy: 'updatedAt:desc' }) .expect(200); - expect((response.body.data.data as DataStore[]).map((f) => f.name)).toEqual([ - 'Newest Data Store', - 'Middle Data Store', - 'Older Data Store', + expect((response.body.data.data as DataTable[]).map((f) => f.name)).toEqual([ + 'Newest Data Table', + 'Middle Data Table', + 'Older Data Table', ]); }); test('should combine multiple query parameters correctly', async () => { - const dataStore1 = await createDataStore(ownerProject, { name: 'Test Data Store' }); - await createDataStore(ownerProject, { name: 'Another Data Store' }); + const dataTable1 = await createDataTable(ownerProject, { name: 'Test Data Table' }); + await createDataTable(ownerProject, { name: 'Another Data Table' }); const response = await authOwnerAgent .get(`/projects/${ownerProject.id}/data-tables`) - .query({ filter: JSON.stringify({ name: 'data', id: dataStore1.id }), sortBy: 'name:asc' }) + .query({ filter: JSON.stringify({ name: 'data', id: dataTable1.id }), sortBy: 'name:asc' }) .expect(200); expect(response.body.data.count).toBe(1); expect(response.body.data.data).toHaveLength(1); - expect(response.body.data.data[0].name).toBe('Test Data Store'); + expect(response.body.data.data[0].name).toBe('Test Data Table'); }); test('should include columns', async () => { - await createDataStore(ownerProject, { - name: 'Test Data Store', + await createDataTable(ownerProject, { + name: 'Test Data Table', columns: [ { name: 'test_column_1', @@ -511,10 +511,10 @@ describe('GET /projects/:projectId/data-tables', () => { }); }); -describe('PATCH /projects/:projectId/data-tables/:dataStoreId', () => { - test('should not update data store when project does not exist', async () => { +describe('PATCH /projects/:projectId/data-tables/:dataTableId', () => { + test('should not update data table when project does not exist', async () => { const payload = { - name: 'Updated Data Store Name', + name: 'Updated Data Table Name', }; await authOwnerAgent @@ -523,11 +523,11 @@ describe('PATCH /projects/:projectId/data-tables/:dataStoreId', () => { .expect(403); }); - test('should not update data store when data store does not exist', async () => { + test('should not update data table when data table does not exist', async () => { const project = await createTeamProject('test project', owner); const payload = { - name: 'Updated Data Store Name', + name: 'Updated Data Table Name', }; await authOwnerAgent @@ -536,137 +536,137 @@ describe('PATCH /projects/:projectId/data-tables/:dataStoreId', () => { .expect(404); }); - test('should not update data store when name is empty', async () => { + test('should not update data table when name is empty', async () => { const project = await createTeamProject(undefined, owner); - const dataStore = await createDataStore(project, { name: 'Original Name' }); + const dataTable = await createDataTable(project, { name: 'Original Name' }); const payload = { name: '', }; await authOwnerAgent - .patch(`/projects/${project.id}/data-tables/${dataStore.id}`) + .patch(`/projects/${project.id}/data-tables/${dataTable.id}`) .send(payload) .expect(400); - const dataStoreInDb = await dataStoreRepository.findOneBy({ id: dataStore.id }); - expect(dataStoreInDb?.name).toBe('Original Name'); + const dataTableInDb = await dataTableRepository.findOneBy({ id: dataTable.id }); + expect(dataTableInDb?.name).toBe('Original Name'); }); - test('should not update data store if user has project:viewer role in team project', async () => { + test('should not update data table if user has project:viewer role in team project', async () => { const project = await createTeamProject(undefined, owner); - const dataStore = await createDataStore(project, { name: 'Original Name' }); + const dataTable = await createDataTable(project, { name: 'Original Name' }); await linkUserToProject(member, project, 'project:viewer'); const payload = { - name: 'Updated Data Store Name', + name: 'Updated Data Table Name', }; await authMemberAgent - .patch(`/projects/${project.id}/data-tables/${dataStore.id}`) + .patch(`/projects/${project.id}/data-tables/${dataTable.id}`) .send(payload) .expect(403); - const dataStoreInDb = await dataStoreRepository.findOneBy({ id: dataStore.id }); - expect(dataStoreInDb?.name).toBe('Original Name'); + const dataTableInDb = await dataTableRepository.findOneBy({ id: dataTable.id }); + expect(dataTableInDb?.name).toBe('Original Name'); }); - test("should not update data store in another user's personal project", async () => { - const dataStore = await createDataStore(ownerProject, { name: 'Original Name' }); + test("should not update data table in another user's personal project", async () => { + const dataTable = await createDataTable(ownerProject, { name: 'Original Name' }); const payload = { - name: 'Updated Data Store Name', + name: 'Updated Data Table Name', }; await authMemberAgent - .patch(`/projects/${ownerProject.id}/data-tables/${dataStore.id}`) + .patch(`/projects/${ownerProject.id}/data-tables/${dataTable.id}`) .send(payload) .expect(403); - const dataStoreInDb = await dataStoreRepository.findOneBy({ id: dataStore.id }); - expect(dataStoreInDb?.name).toBe('Original Name'); + const dataTableInDb = await dataTableRepository.findOneBy({ id: dataTable.id }); + expect(dataTableInDb?.name).toBe('Original Name'); }); - test('should update data store if user has project:editor role in team project', async () => { + test('should update data table if user has project:editor role in team project', async () => { const project = await createTeamProject(undefined, owner); - const dataStore = await createDataStore(project, { name: 'Original Name' }); + const dataTable = await createDataTable(project, { name: 'Original Name' }); await linkUserToProject(member, project, 'project:editor'); const payload = { - name: 'Updated Data Store Name', + name: 'Updated Data Table Name', }; await authMemberAgent - .patch(`/projects/${project.id}/data-tables/${dataStore.id}`) + .patch(`/projects/${project.id}/data-tables/${dataTable.id}`) .send(payload) .expect(200); - const dataStoreInDb = await dataStoreRepository.findOneBy({ id: dataStore.id }); - expect(dataStoreInDb?.name).toBe('Updated Data Store Name'); + const dataTableInDb = await dataTableRepository.findOneBy({ id: dataTable.id }); + expect(dataTableInDb?.name).toBe('Updated Data Table Name'); }); - test('should update data store if user has project:admin role in team project', async () => { + test('should update data table if user has project:admin role in team project', async () => { const project = await createTeamProject(undefined, owner); - const dataStore = await createDataStore(project, { name: 'Original Name' }); + const dataTable = await createDataTable(project, { name: 'Original Name' }); await linkUserToProject(admin, project, 'project:admin'); const payload = { - name: 'Updated Data Store Name', + name: 'Updated Data Table Name', }; await authAdminAgent - .patch(`/projects/${project.id}/data-tables/${dataStore.id}`) + .patch(`/projects/${project.id}/data-tables/${dataTable.id}`) .send(payload) .expect(200); - const dataStoreInDb = await dataStoreRepository.findOneBy({ id: dataStore.id }); - expect(dataStoreInDb?.name).toBe('Updated Data Store Name'); + const dataTableInDb = await dataTableRepository.findOneBy({ id: dataTable.id }); + expect(dataTableInDb?.name).toBe('Updated Data Table Name'); }); - test('should update data store if user is owner in team project', async () => { + test('should update data table if user is owner in team project', async () => { const project = await createTeamProject(undefined, owner); - const dataStore = await createDataStore(project, { name: 'Original Name' }); + const dataTable = await createDataTable(project, { name: 'Original Name' }); const payload = { - name: 'Updated Data Store Name', + name: 'Updated Data Table Name', }; await authOwnerAgent - .patch(`/projects/${project.id}/data-tables/${dataStore.id}`) + .patch(`/projects/${project.id}/data-tables/${dataTable.id}`) .send(payload) .expect(200); - const dataStoreInDb = await dataStoreRepository.findOneBy({ id: dataStore.id }); - expect(dataStoreInDb?.name).toBe('Updated Data Store Name'); + const dataTableInDb = await dataTableRepository.findOneBy({ id: dataTable.id }); + expect(dataTableInDb?.name).toBe('Updated Data Table Name'); }); - test('should update data store in personal project', async () => { + test('should update data table in personal project', async () => { const personalProject = await projectRepository.getPersonalProjectForUserOrFail(owner.id); - const dataStore = await createDataStore(personalProject, { name: 'Original Name' }); + const dataTable = await createDataTable(personalProject, { name: 'Original Name' }); const payload = { - name: 'Updated Data Store Name', + name: 'Updated Data Table Name', }; await authOwnerAgent - .patch(`/projects/${personalProject.id}/data-tables/${dataStore.id}`) + .patch(`/projects/${personalProject.id}/data-tables/${dataTable.id}`) .send(payload) .expect(200); - const dataStoreInDb = await dataStoreRepository.findOneBy({ id: dataStore.id }); - expect(dataStoreInDb?.name).toBe('Updated Data Store Name'); + const dataTableInDb = await dataTableRepository.findOneBy({ id: dataTable.id }); + expect(dataTableInDb?.name).toBe('Updated Data Table Name'); }); }); -describe('DELETE /projects/:projectId/data-tables/:dataStoreId', () => { - test('should not delete data store when project does not exist', async () => { +describe('DELETE /projects/:projectId/data-tables/:dataTableId', () => { + test('should not delete data table when project does not exist', async () => { await authOwnerAgent .delete('/projects/non-existing-id/data-tables/some-data-store-id') .send({}) .expect(403); }); - test('should not delete data store when data store does not exist', async () => { + test('should not delete data table when data table does not exist', async () => { const project = await createTeamProject('test project', owner); await authOwnerAgent @@ -675,90 +675,90 @@ describe('DELETE /projects/:projectId/data-tables/:dataStoreId', () => { .expect(404); }); - test('should not delete data store if user has project:viewer role in team project', async () => { + test('should not delete data table if user has project:viewer role in team project', async () => { const project = await createTeamProject(undefined, owner); - const dataStore = await createDataStore(project); + const dataTable = await createDataTable(project); await linkUserToProject(member, project, 'project:viewer'); await authMemberAgent - .delete(`/projects/${project.id}/data-tables/${dataStore.id}`) + .delete(`/projects/${project.id}/data-tables/${dataTable.id}`) .send({}) .expect(403); - const dataStoreInDb = await dataStoreRepository.findOneBy({ id: dataStore.id }); - expect(dataStoreInDb).toBeDefined(); + const dataTableInDb = await dataTableRepository.findOneBy({ id: dataTable.id }); + expect(dataTableInDb).toBeDefined(); }); - test("should not delete data store in another user's personal project", async () => { - const dataStore = await createDataStore(ownerProject); + test("should not delete data table in another user's personal project", async () => { + const dataTable = await createDataTable(ownerProject); await authMemberAgent - .delete(`/projects/${ownerProject.id}/data-tables/${dataStore.id}`) + .delete(`/projects/${ownerProject.id}/data-tables/${dataTable.id}`) .send({}) .expect(403); - const dataStoreInDb = await dataStoreRepository.findOneBy({ id: dataStore.id }); - expect(dataStoreInDb).toBeDefined(); + const dataTableInDb = await dataTableRepository.findOneBy({ id: dataTable.id }); + expect(dataTableInDb).toBeDefined(); }); - test('should delete data store if user has project:editor role in team project', async () => { + test('should delete data table if user has project:editor role in team project', async () => { const project = await createTeamProject(undefined, owner); - const dataStore = await createDataStore(project); + const dataTable = await createDataTable(project); await linkUserToProject(member, project, 'project:editor'); await authMemberAgent - .delete(`/projects/${project.id}/data-tables/${dataStore.id}`) + .delete(`/projects/${project.id}/data-tables/${dataTable.id}`) .send({}) .expect(200); - const dataStoreInDb = await dataStoreRepository.findOneBy({ id: dataStore.id }); - expect(dataStoreInDb).toBeNull(); + const dataTableInDb = await dataTableRepository.findOneBy({ id: dataTable.id }); + expect(dataTableInDb).toBeNull(); }); - test('should delete data store if user has project:admin role in team project', async () => { + test('should delete data table if user has project:admin role in team project', async () => { const project = await createTeamProject(undefined, owner); - const dataStore = await createDataStore(project); + const dataTable = await createDataTable(project); await linkUserToProject(admin, project, 'project:admin'); await authAdminAgent - .delete(`/projects/${project.id}/data-tables/${dataStore.id}`) + .delete(`/projects/${project.id}/data-tables/${dataTable.id}`) .send({}) .expect(200); - const dataStoreInDb = await dataStoreRepository.findOneBy({ id: dataStore.id }); - expect(dataStoreInDb).toBeNull(); + const dataTableInDb = await dataTableRepository.findOneBy({ id: dataTable.id }); + expect(dataTableInDb).toBeNull(); }); - test('should delete data store if user is owner in team project', async () => { + test('should delete data table if user is owner in team project', async () => { const project = await createTeamProject(undefined, owner); - const dataStore = await createDataStore(project); + const dataTable = await createDataTable(project); await authOwnerAgent - .delete(`/projects/${project.id}/data-tables/${dataStore.id}`) + .delete(`/projects/${project.id}/data-tables/${dataTable.id}`) .send({}) .expect(200); - const dataStoreInDb = await dataStoreRepository.findOneBy({ id: dataStore.id }); - expect(dataStoreInDb).toBeNull(); + const dataTableInDb = await dataTableRepository.findOneBy({ id: dataTable.id }); + expect(dataTableInDb).toBeNull(); }); - test('should delete data store in personal project', async () => { + test('should delete data table in personal project', async () => { const personalProject = await projectRepository.getPersonalProjectForUserOrFail(owner.id); - const dataStore = await createDataStore(personalProject); + const dataTable = await createDataTable(personalProject); await authOwnerAgent - .delete(`/projects/${personalProject.id}/data-tables/${dataStore.id}`) + .delete(`/projects/${personalProject.id}/data-tables/${dataTable.id}`) .send({}) .expect(200); - const dataStoreInDb = await dataStoreRepository.findOneBy({ id: dataStore.id }); - expect(dataStoreInDb).toBeNull(); + const dataTableInDb = await dataTableRepository.findOneBy({ id: dataTable.id }); + expect(dataTableInDb).toBeNull(); }); test("should delete data from 'data_table', 'data_table_column' tables and drop 'data_table_user_' table", async () => { const personalProject = await projectRepository.getPersonalProjectForUserOrFail(owner.id); - const dataStore = await createDataStore(personalProject, { - name: 'Test Data Store', + const dataTable = await createDataTable(personalProject, { + name: 'Test Data Table', columns: [ { name: 'test', @@ -768,25 +768,25 @@ describe('DELETE /projects/:projectId/data-tables/:dataStoreId', () => { }); await authOwnerAgent - .delete(`/projects/${personalProject.id}/data-tables/${dataStore.id}`) + .delete(`/projects/${personalProject.id}/data-tables/${dataTable.id}`) .send({}) .expect(200); - const dataStoreInDb = await dataStoreRepository.findOneBy({ id: dataStore.id }); - expect(dataStoreInDb).toBeNull(); + const dataTableInDb = await dataTableRepository.findOneBy({ id: dataTable.id }); + expect(dataTableInDb).toBeNull(); - const dataStoreColumnInDb = await dataStoreColumnRepository.findOneBy({ - dataTableId: dataStore.id, + const dataTableColumnInDb = await dataTableColumnRepository.findOneBy({ + dataTableId: dataTable.id, }); - expect(dataStoreColumnInDb).toBeNull(); + expect(dataTableColumnInDb).toBeNull(); - await expect(dataStoreRowsRepository.getManyAndCount(dataStore.id, {})).rejects.toThrow( + await expect(dataTableRowsRepository.getManyAndCount(dataTable.id, {})).rejects.toThrow( QueryFailedError, ); }); }); -describe('GET /projects/:projectId/data-tables/:dataStoreId/columns', () => { +describe('GET /projects/:projectId/data-tables/:dataTableId/columns', () => { test('should not list columns when project does not exist', async () => { await authOwnerAgent .get('/projects/non-existing-id/data-tables/non-existing-id/columns') @@ -795,18 +795,18 @@ describe('GET /projects/:projectId/data-tables/:dataStoreId/columns', () => { test('should not list columns if user has no access to project', async () => { const project = await createTeamProject('test project', owner); - const dataStore = await createDataStore(project); + const dataTable = await createDataTable(project); await authMemberAgent - .get(`/projects/${project.id}/data-tables/${dataStore.id}/columns`) + .get(`/projects/${project.id}/data-tables/${dataTable.id}/columns`) .expect(403); }); - test("should not list columns from data stores in another user's personal project", async () => { + test("should not list columns from data tables in another user's personal project", async () => { await authMemberAgent.get(`/projects/${ownerProject.id}/data-tables`).expect(403); }); - test('should not list columns when data store does not exist', async () => { + test('should not list columns when data table does not exist', async () => { const project = await createTeamProject('test project', owner); await authOwnerAgent @@ -817,7 +817,7 @@ describe('GET /projects/:projectId/data-tables/:dataStoreId/columns', () => { test('should list columns if user has project:viewer role in team project', async () => { const project = await createTeamProject('test project', owner); await linkUserToProject(member, project, 'project:viewer'); - const dataStore = await createDataStore(project, { + const dataTable = await createDataTable(project, { columns: [ { name: 'test_column', @@ -831,7 +831,7 @@ describe('GET /projects/:projectId/data-tables/:dataStoreId/columns', () => { }); const response = await authMemberAgent - .get(`/projects/${project.id}/data-tables/${dataStore.id}/columns`) + .get(`/projects/${project.id}/data-tables/${dataTable.id}/columns`) .expect(200); expect(response.body.data).toHaveLength(2); @@ -842,7 +842,7 @@ describe('GET /projects/:projectId/data-tables/:dataStoreId/columns', () => { test('should list columns if user has project:editor role in team project', async () => { const project = await createTeamProject('test project', owner); await linkUserToProject(member, project, 'project:editor'); - const dataStore = await createDataStore(project, { + const dataTable = await createDataTable(project, { columns: [ { name: 'test_column', @@ -852,16 +852,16 @@ describe('GET /projects/:projectId/data-tables/:dataStoreId/columns', () => { }); const response = await authMemberAgent - .get(`/projects/${project.id}/data-tables/${dataStore.id}/columns`) + .get(`/projects/${project.id}/data-tables/${dataTable.id}/columns`) .expect(200); expect(response.body.data).toHaveLength(1); expect(response.body.data[0].name).toBe('test_column'); }); - test('should list columns from personal project data store', async () => { - const dataStore = await createDataStore(memberProject, { - name: 'Personal Data Store 1', + test('should list columns from personal project data table', async () => { + const dataTable = await createDataTable(memberProject, { + name: 'Personal Data Table 1', columns: [ { name: 'test_column', @@ -871,7 +871,7 @@ describe('GET /projects/:projectId/data-tables/:dataStoreId/columns', () => { }); const response = await authMemberAgent - .get(`/projects/${memberProject.id}/data-tables/${dataStore.id}/columns`) + .get(`/projects/${memberProject.id}/data-tables/${dataTable.id}/columns`) .expect(200); expect(response.body.data).toHaveLength(1); @@ -879,7 +879,7 @@ describe('GET /projects/:projectId/data-tables/:dataStoreId/columns', () => { }); }); -describe('POST /projects/:projectId/data-tables/:dataStoreId/columns', () => { +describe('POST /projects/:projectId/data-tables/:dataTableId/columns', () => { test('should not create column when project does not exist', async () => { const payload = { name: 'Test Column', @@ -892,7 +892,7 @@ describe('POST /projects/:projectId/data-tables/:dataStoreId/columns', () => { .expect(403); }); - test('should not create column when data store does not exist', async () => { + test('should not create column when data table does not exist', async () => { const project = await createTeamProject('test project', owner); const payload = { @@ -909,7 +909,7 @@ describe('POST /projects/:projectId/data-tables/:dataStoreId/columns', () => { test('should not create column when name is empty', async () => { const project = await createTeamProject(undefined, owner); - const dataStore = await createDataStore(project); + const dataTable = await createDataTable(project); const payload = { name: '', @@ -917,17 +917,17 @@ describe('POST /projects/:projectId/data-tables/:dataStoreId/columns', () => { }; await authOwnerAgent - .post(`/projects/${project.id}/data-tables/${dataStore.id}/columns`) + .post(`/projects/${project.id}/data-tables/${dataTable.id}/columns`) .send(payload) .expect(400); - const columnsInDb = await dataStoreColumnRepository.findBy({ dataTableId: dataStore.id }); + const columnsInDb = await dataTableColumnRepository.findBy({ dataTableId: dataTable.id }); expect(columnsInDb).toHaveLength(0); }); test("should not create column when name isn't valid", async () => { const project = await createTeamProject(undefined, owner); - const dataStore = await createDataStore(project); + const dataTable = await createDataTable(project); const payload = { name: 'invalid name', @@ -935,16 +935,16 @@ describe('POST /projects/:projectId/data-tables/:dataStoreId/columns', () => { }; await authOwnerAgent - .post(`/projects/${project.id}/data-tables/${dataStore.id}/columns`) + .post(`/projects/${project.id}/data-tables/${dataTable.id}/columns`) .send(payload) .expect(400); - const columnsInDb = await dataStoreColumnRepository.findBy({ dataTableId: dataStore.id }); + const columnsInDb = await dataTableColumnRepository.findBy({ dataTableId: dataTable.id }); expect(columnsInDb).toHaveLength(0); }); - test("should not create column in another user's personal project data store", async () => { - const dataStore = await createDataStore(ownerProject, { + test("should not create column in another user's personal project data table", async () => { + const dataTable = await createDataTable(ownerProject, { columns: [ { name: 'test_column', @@ -954,14 +954,14 @@ describe('POST /projects/:projectId/data-tables/:dataStoreId/columns', () => { }); await authMemberAgent - .post(`/projects/${ownerProject.id}/data-tables/${dataStore.id}/columns`) + .post(`/projects/${ownerProject.id}/data-tables/${dataTable.id}/columns`) .send({ name: 'new_column', type: 'string', }) .expect(403); - const columnsInDb = await dataStoreColumnRepository.findBy({ dataTableId: dataStore.id }); + const columnsInDb = await dataTableColumnRepository.findBy({ dataTableId: dataTable.id }); expect(columnsInDb).toHaveLength(1); expect(columnsInDb[0].name).toBe('test_column'); }); @@ -969,7 +969,7 @@ describe('POST /projects/:projectId/data-tables/:dataStoreId/columns', () => { test('should not create column if user has project:viewer role in team project', async () => { const project = await createTeamProject(undefined, owner); await linkUserToProject(member, project, 'project:viewer'); - const dataStore = await createDataStore(project); + const dataTable = await createDataTable(project); const payload = { name: 'test_column', @@ -977,18 +977,18 @@ describe('POST /projects/:projectId/data-tables/:dataStoreId/columns', () => { }; await authMemberAgent - .post(`/projects/${project.id}/data-tables/${dataStore.id}/columns`) + .post(`/projects/${project.id}/data-tables/${dataTable.id}/columns`) .send(payload) .expect(403); - const columnsInDb = await dataStoreColumnRepository.findBy({ dataTableId: dataStore.id }); + const columnsInDb = await dataTableColumnRepository.findBy({ dataTableId: dataTable.id }); expect(columnsInDb).toHaveLength(0); }); test('should create column if user has project:editor role in team project', async () => { const project = await createTeamProject(undefined, owner); await linkUserToProject(member, project, 'project:editor'); - const dataStore = await createDataStore(project, { + const dataTable = await createDataTable(project, { columns: [ { name: 'test_column', @@ -1004,11 +1004,11 @@ describe('POST /projects/:projectId/data-tables/:dataStoreId/columns', () => { }; await authMemberAgent - .post(`/projects/${project.id}/data-tables/${dataStore.id}/columns`) + .post(`/projects/${project.id}/data-tables/${dataTable.id}/columns`) .send(payload) .expect(200); - const columnsInDb = await dataStoreColumnRepository.findBy({ dataTableId: dataStore.id }); + const columnsInDb = await dataTableColumnRepository.findBy({ dataTableId: dataTable.id }); expect(columnsInDb).toHaveLength(2); expect(columnsInDb[0].name).toBe('new_column'); expect(columnsInDb[0].type).toBe('string'); @@ -1017,7 +1017,7 @@ describe('POST /projects/:projectId/data-tables/:dataStoreId/columns', () => { test('should create column if user has project:admin role in team project', async () => { const project = await createTeamProject(undefined, owner); await linkUserToProject(admin, project, 'project:admin'); - const dataStore = await createDataStore(project, { + const dataTable = await createDataTable(project, { columns: [ { name: 'test_column', @@ -1033,11 +1033,11 @@ describe('POST /projects/:projectId/data-tables/:dataStoreId/columns', () => { }; await authAdminAgent - .post(`/projects/${project.id}/data-tables/${dataStore.id}/columns`) + .post(`/projects/${project.id}/data-tables/${dataTable.id}/columns`) .send(payload) .expect(200); - const columnsInDb = await dataStoreColumnRepository.findBy({ dataTableId: dataStore.id }); + const columnsInDb = await dataTableColumnRepository.findBy({ dataTableId: dataTable.id }); expect(columnsInDb).toHaveLength(2); expect(columnsInDb[0].name).toBe('new_column'); expect(columnsInDb[0].type).toBe('boolean'); @@ -1047,7 +1047,7 @@ describe('POST /projects/:projectId/data-tables/:dataStoreId/columns', () => { test('should create column if user has is owner in team project', async () => { const project = await createTeamProject(undefined, owner); - const dataStore = await createDataStore(project, { + const dataTable = await createDataTable(project, { columns: [ { name: 'test_column', @@ -1063,11 +1063,11 @@ describe('POST /projects/:projectId/data-tables/:dataStoreId/columns', () => { }; await authOwnerAgent - .post(`/projects/${project.id}/data-tables/${dataStore.id}/columns`) + .post(`/projects/${project.id}/data-tables/${dataTable.id}/columns`) .send(payload) .expect(200); - const columnsInDb = await dataStoreColumnRepository.findBy({ dataTableId: dataStore.id }); + const columnsInDb = await dataTableColumnRepository.findBy({ dataTableId: dataTable.id }); expect(columnsInDb).toHaveLength(2); expect(columnsInDb[0].name).toBe('new_column'); expect(columnsInDb[0].type).toBe('boolean'); @@ -1077,7 +1077,7 @@ describe('POST /projects/:projectId/data-tables/:dataStoreId/columns', () => { test('should place the column in correct index', async () => { const project = await createTeamProject(undefined, owner); - const dataStore = await createDataStore(project, { + const dataTable = await createDataTable(project, { columns: [ { name: 'test_column_1', @@ -1090,18 +1090,18 @@ describe('POST /projects/:projectId/data-tables/:dataStoreId/columns', () => { ], }); - const payload: DataStoreCreateColumnSchema = { + const payload: DataTableCreateColumnSchema = { name: 'new_column', type: 'boolean', index: 1, }; await authOwnerAgent - .post(`/projects/${project.id}/data-tables/${dataStore.id}/columns`) + .post(`/projects/${project.id}/data-tables/${dataTable.id}/columns`) .send(payload) .expect(200); - const columns = await dataStoreColumnRepository.getColumns(dataStore.id); + const columns = await dataTableColumnRepository.getColumns(dataTable.id); expect(columns).toHaveLength(3); expect(columns[0].name).toBe('test_column_1'); @@ -1110,7 +1110,7 @@ describe('POST /projects/:projectId/data-tables/:dataStoreId/columns', () => { }); }); -describe('DELETE /projects/:projectId/data-tables/:dataStoreId/columns/:columnId', () => { +describe('DELETE /projects/:projectId/data-tables/:dataTableId/columns/:columnId', () => { test('should not delete column when project does not exist', async () => { await authOwnerAgent .delete('/projects/non-existing-id/data-tables/some-data-store-id/columns/some-column-id') @@ -1118,7 +1118,7 @@ describe('DELETE /projects/:projectId/data-tables/:dataStoreId/columns/:columnId .expect(403); }); - test('should not delete column when data store does not exist', async () => { + test('should not delete column when data table does not exist', async () => { const project = await createTeamProject('test project', owner); await authOwnerAgent @@ -1129,7 +1129,7 @@ describe('DELETE /projects/:projectId/data-tables/:dataStoreId/columns/:columnId test('should not delete column when column does not exist', async () => { const project = await createTeamProject('test project', owner); - const dataStore = await createDataStore(project, { + const dataTable = await createDataTable(project, { columns: [ { name: 'test_column', @@ -1139,13 +1139,13 @@ describe('DELETE /projects/:projectId/data-tables/:dataStoreId/columns/:columnId }); await authOwnerAgent - .delete(`/projects/${project.id}/data-tables/${dataStore.id}/columns/non-existing-id`) + .delete(`/projects/${project.id}/data-tables/${dataTable.id}/columns/non-existing-id`) .send() .expect(404); }); - test("should not delete column in another user's personal project data store", async () => { - const dataStore = await createDataStore(ownerProject, { + test("should not delete column in another user's personal project data table", async () => { + const dataTable = await createDataTable(ownerProject, { columns: [ { name: 'test_column', @@ -1155,12 +1155,12 @@ describe('DELETE /projects/:projectId/data-tables/:dataStoreId/columns/:columnId }); await authMemberAgent - .delete(`/projects/${ownerProject.id}/data-tables/${dataStore.id}/columns/test_column`) + .delete(`/projects/${ownerProject.id}/data-tables/${dataTable.id}/columns/test_column`) .send() .expect(403); - const columnInDb = await dataStoreColumnRepository.findOneBy({ - dataTableId: dataStore.id, + const columnInDb = await dataTableColumnRepository.findOneBy({ + dataTableId: dataTable.id, name: 'test-column', }); expect(columnInDb).toBeDefined(); @@ -1168,7 +1168,7 @@ describe('DELETE /projects/:projectId/data-tables/:dataStoreId/columns/:columnId test('should not delete column if user has project:viewer role in team project', async () => { const project = await createTeamProject(undefined, owner); - const dataStore = await createDataStore(project, { + const dataTable = await createDataTable(project, { columns: [ { name: 'test_column', @@ -1179,12 +1179,12 @@ describe('DELETE /projects/:projectId/data-tables/:dataStoreId/columns/:columnId await linkUserToProject(member, project, 'project:viewer'); await authMemberAgent - .delete(`/projects/${project.id}/data-tables/${dataStore.id}/columns/test_column`) + .delete(`/projects/${project.id}/data-tables/${dataTable.id}/columns/test_column`) .send() .expect(403); - const columnInDb = await dataStoreColumnRepository.findOneBy({ - dataTableId: dataStore.id, + const columnInDb = await dataTableColumnRepository.findOneBy({ + dataTableId: dataTable.id, name: 'test-column', }); expect(columnInDb).toBeDefined(); @@ -1194,7 +1194,7 @@ describe('DELETE /projects/:projectId/data-tables/:dataStoreId/columns/:columnId const project = await createTeamProject(undefined, owner); await linkUserToProject(member, project, 'project:editor'); - const dataStore = await createDataStore(project, { + const dataTable = await createDataTable(project, { columns: [ { name: 'test_column', @@ -1205,13 +1205,13 @@ describe('DELETE /projects/:projectId/data-tables/:dataStoreId/columns/:columnId await authOwnerAgent .delete( - `/projects/${project.id}/data-tables/${dataStore.id}/columns/${dataStore.columns[0].id}`, + `/projects/${project.id}/data-tables/${dataTable.id}/columns/${dataTable.columns[0].id}`, ) .send() .expect(200); - const columnInDb = await dataStoreColumnRepository.findOneBy({ - dataTableId: dataStore.id, + const columnInDb = await dataTableColumnRepository.findOneBy({ + dataTableId: dataTable.id, name: 'test-column', }); expect(columnInDb).toBeNull(); @@ -1220,7 +1220,7 @@ describe('DELETE /projects/:projectId/data-tables/:dataStoreId/columns/:columnId test('should delete column if user has project:admin role in team project', async () => { const project = await createTeamProject(undefined, owner); await linkUserToProject(admin, project, 'project:admin'); - const dataStore = await createDataStore(project, { + const dataTable = await createDataTable(project, { columns: [ { name: 'test_column', @@ -1231,13 +1231,13 @@ describe('DELETE /projects/:projectId/data-tables/:dataStoreId/columns/:columnId await authAdminAgent .delete( - `/projects/${project.id}/data-tables/${dataStore.id}/columns/${dataStore.columns[0].id}`, + `/projects/${project.id}/data-tables/${dataTable.id}/columns/${dataTable.columns[0].id}`, ) .send() .expect(200); - const columnInDb = await dataStoreColumnRepository.findOneBy({ - dataTableId: dataStore.id, + const columnInDb = await dataTableColumnRepository.findOneBy({ + dataTableId: dataTable.id, name: 'test-column', }); expect(columnInDb).toBeNull(); @@ -1245,7 +1245,7 @@ describe('DELETE /projects/:projectId/data-tables/:dataStoreId/columns/:columnId test('should delete column if user is owner in team project', async () => { const project = await createTeamProject(undefined, owner); - const dataStore = await createDataStore(project, { + const dataTable = await createDataTable(project, { columns: [ { name: 'test_column', @@ -1256,20 +1256,20 @@ describe('DELETE /projects/:projectId/data-tables/:dataStoreId/columns/:columnId await authOwnerAgent .delete( - `/projects/${project.id}/data-tables/${dataStore.id}/columns/${dataStore.columns[0].id}`, + `/projects/${project.id}/data-tables/${dataTable.id}/columns/${dataTable.columns[0].id}`, ) .send() .expect(200); - const columnInDb = await dataStoreColumnRepository.findOneBy({ - dataTableId: dataStore.id, + const columnInDb = await dataTableColumnRepository.findOneBy({ + dataTableId: dataTable.id, name: 'test-column', }); expect(columnInDb).toBeNull(); }); test('should delete column in personal project', async () => { - const dataStore = await createDataStore(memberProject, { + const dataTable = await createDataTable(memberProject, { columns: [ { name: 'test_column', @@ -1280,20 +1280,20 @@ describe('DELETE /projects/:projectId/data-tables/:dataStoreId/columns/:columnId await authMemberAgent .delete( - `/projects/${memberProject.id}/data-tables/${dataStore.id}/columns/${dataStore.columns[0].id}`, + `/projects/${memberProject.id}/data-tables/${dataTable.id}/columns/${dataTable.columns[0].id}`, ) .send() .expect(200); - const columnInDb = await dataStoreColumnRepository.findOneBy({ - dataTableId: dataStore.id, + const columnInDb = await dataTableColumnRepository.findOneBy({ + dataTableId: dataTable.id, name: 'test-column', }); expect(columnInDb).toBeNull(); }); }); -describe('PATCH /projects/:projectId/data-tables/:dataStoreId/columns/:columnId/move', () => { +describe('PATCH /projects/:projectId/data-tables/:dataTableId/columns/:columnId/move', () => { test('should not move column when project does not exist', async () => { const payload = { index: 1, @@ -1305,7 +1305,7 @@ describe('PATCH /projects/:projectId/data-tables/:dataStoreId/columns/:columnId/ .expect(403); }); - test('should not move column when data store does not exist', async () => { + test('should not move column when data table does not exist', async () => { const project = await createTeamProject('test project', owner); const payload = { targetIndex: 1, @@ -1321,7 +1321,7 @@ describe('PATCH /projects/:projectId/data-tables/:dataStoreId/columns/:columnId/ test('should not move column when column does not exist', async () => { const project = await createTeamProject('test project', owner); - const dataStore = await createDataStore(project, { + const dataTable = await createDataTable(project, { columns: [ { name: 'test_column', @@ -1334,13 +1334,13 @@ describe('PATCH /projects/:projectId/data-tables/:dataStoreId/columns/:columnId/ }; await authOwnerAgent - .patch(`/projects/${project.id}/data-tables/${dataStore.id}/columns/some-column-id/move`) + .patch(`/projects/${project.id}/data-tables/${dataTable.id}/columns/some-column-id/move`) .send(payload) .expect(404); }); - test("should not move column in another user's personal project data store", async () => { - const dataStore = await createDataStore(ownerProject, { + test("should not move column in another user's personal project data table", async () => { + const dataTable = await createDataTable(ownerProject, { columns: [ { name: 'test_column', @@ -1355,13 +1355,13 @@ describe('PATCH /projects/:projectId/data-tables/:dataStoreId/columns/:columnId/ await authMemberAgent .patch( - `/projects/${ownerProject.id}/data-tables/${dataStore.id}/columns/${dataStore.columns[0].id}/move`, + `/projects/${ownerProject.id}/data-tables/${dataTable.id}/columns/${dataTable.columns[0].id}/move`, ) .send({ targetIndex: 1 }) .expect(403); - const columnInDb = await dataStoreColumnRepository.findOneBy({ - dataTableId: dataStore.id, + const columnInDb = await dataTableColumnRepository.findOneBy({ + dataTableId: dataTable.id, name: 'test-column', index: 0, }); @@ -1371,7 +1371,7 @@ describe('PATCH /projects/:projectId/data-tables/:dataStoreId/columns/:columnId/ test('should not move column if user has project:viewer role in team project', async () => { const project = await createTeamProject('test project', owner); await linkUserToProject(member, project, 'project:viewer'); - const dataStore = await createDataStore(project, { + const dataTable = await createDataTable(project, { columns: [ { name: 'test_column', @@ -1386,13 +1386,13 @@ describe('PATCH /projects/:projectId/data-tables/:dataStoreId/columns/:columnId/ await authMemberAgent .patch( - `/projects/${project.id}/data-tables/${dataStore.id}/columns/${dataStore.columns[0].id}/move`, + `/projects/${project.id}/data-tables/${dataTable.id}/columns/${dataTable.columns[0].id}/move`, ) .send({ targetIndex: 1 }) .expect(403); - const columnInDb = await dataStoreColumnRepository.findOneBy({ - dataTableId: dataStore.id, + const columnInDb = await dataTableColumnRepository.findOneBy({ + dataTableId: dataTable.id, name: 'test-column', index: 0, }); @@ -1402,7 +1402,7 @@ describe('PATCH /projects/:projectId/data-tables/:dataStoreId/columns/:columnId/ test('should move column if user has project:editor role in team project', async () => { const project = await createTeamProject('test project', owner); await linkUserToProject(member, project, 'project:editor'); - const dataStore = await createDataStore(project, { + const dataTable = await createDataTable(project, { columns: [ { name: 'test_column', @@ -1417,13 +1417,13 @@ describe('PATCH /projects/:projectId/data-tables/:dataStoreId/columns/:columnId/ await authMemberAgent .patch( - `/projects/${project.id}/data-tables/${dataStore.id}/columns/${dataStore.columns[0].id}/move`, + `/projects/${project.id}/data-tables/${dataTable.id}/columns/${dataTable.columns[0].id}/move`, ) .send({ targetIndex: 1 }) .expect(200); - const columnInDb = await dataStoreColumnRepository.findOneBy({ - dataTableId: dataStore.id, + const columnInDb = await dataTableColumnRepository.findOneBy({ + dataTableId: dataTable.id, name: 'test-column', index: 1, }); @@ -1433,7 +1433,7 @@ describe('PATCH /projects/:projectId/data-tables/:dataStoreId/columns/:columnId/ test('should move column if user has project:admin role in team project', async () => { const project = await createTeamProject('test project', owner); await linkUserToProject(admin, project, 'project:admin'); - const dataStore = await createDataStore(project, { + const dataTable = await createDataTable(project, { columns: [ { name: 'test_column', @@ -1448,13 +1448,13 @@ describe('PATCH /projects/:projectId/data-tables/:dataStoreId/columns/:columnId/ await authAdminAgent .patch( - `/projects/${project.id}/data-tables/${dataStore.id}/columns/${dataStore.columns[0].id}/move`, + `/projects/${project.id}/data-tables/${dataTable.id}/columns/${dataTable.columns[0].id}/move`, ) .send({ targetIndex: 1 }) .expect(200); - const columnInDb = await dataStoreColumnRepository.findOneBy({ - dataTableId: dataStore.id, + const columnInDb = await dataTableColumnRepository.findOneBy({ + dataTableId: dataTable.id, name: 'test-column', index: 1, }); @@ -1464,7 +1464,7 @@ describe('PATCH /projects/:projectId/data-tables/:dataStoreId/columns/:columnId/ test('should move column if user is owner in team project', async () => { const project = await createTeamProject('test project', owner); - const dataStore = await createDataStore(project, { + const dataTable = await createDataTable(project, { columns: [ { name: 'test_column', @@ -1479,13 +1479,13 @@ describe('PATCH /projects/:projectId/data-tables/:dataStoreId/columns/:columnId/ await authOwnerAgent .patch( - `/projects/${project.id}/data-tables/${dataStore.id}/columns/${dataStore.columns[0].id}/move`, + `/projects/${project.id}/data-tables/${dataTable.id}/columns/${dataTable.columns[0].id}/move`, ) .send({ targetIndex: 1 }) .expect(200); - const columnInDb = await dataStoreColumnRepository.findOneBy({ - dataTableId: dataStore.id, + const columnInDb = await dataTableColumnRepository.findOneBy({ + dataTableId: dataTable.id, name: 'test-column', index: 1, }); @@ -1493,7 +1493,7 @@ describe('PATCH /projects/:projectId/data-tables/:dataStoreId/columns/:columnId/ }); test('should move column in personal project', async () => { - const dataStore = await createDataStore(memberProject, { + const dataTable = await createDataTable(memberProject, { columns: [ { name: 'test_column', @@ -1508,13 +1508,13 @@ describe('PATCH /projects/:projectId/data-tables/:dataStoreId/columns/:columnId/ await authMemberAgent .patch( - `/projects/${memberProject.id}/data-tables/${dataStore.id}/columns/${dataStore.columns[0].id}/move`, + `/projects/${memberProject.id}/data-tables/${dataTable.id}/columns/${dataTable.columns[0].id}/move`, ) .send({ targetIndex: 1 }) .expect(200); - const columnInDb = await dataStoreColumnRepository.findOneBy({ - dataTableId: dataStore.id, + const columnInDb = await dataTableColumnRepository.findOneBy({ + dataTableId: dataTable.id, name: 'test-column', index: 1, }); @@ -1522,22 +1522,22 @@ describe('PATCH /projects/:projectId/data-tables/:dataStoreId/columns/:columnId/ }); }); -describe('GET /projects/:projectId/data-tables/:dataStoreId/rows', () => { +describe('GET /projects/:projectId/data-tables/:dataTableId/rows', () => { test('should not list rows when project does not exist', async () => { await authOwnerAgent .get('/projects/non-existing-id/data-tables/some-data-store-id/rows') .expect(403); }); - test('should not list rows when data store does not exist', async () => { + test('should not list rows when data table does not exist', async () => { const project = await createTeamProject('test project', owner); await authOwnerAgent .get(`/projects/${project.id}/data-tables/non-existing-id/rows`) .expect(404); }); - test("should not list rows in another user's personal project data store", async () => { - const dataStore = await createDataStore(ownerProject, { + test("should not list rows in another user's personal project data table", async () => { + const dataTable = await createDataTable(ownerProject, { columns: [ { name: 'test_column', @@ -1551,7 +1551,7 @@ describe('GET /projects/:projectId/data-tables/:dataStoreId/rows', () => { }); await authMemberAgent - .get(`/projects/${ownerProject.id}/data-tables/${dataStore.id}/rows`) + .get(`/projects/${ownerProject.id}/data-tables/${dataTable.id}/rows`) .expect(403); }); @@ -1559,7 +1559,7 @@ describe('GET /projects/:projectId/data-tables/:dataStoreId/rows', () => { const project = await createTeamProject('test project', owner); await linkUserToProject(member, project, 'project:viewer'); - const dataStore = await createDataStore(project, { + const dataTable = await createDataTable(project, { columns: [ { name: 'first', @@ -1579,7 +1579,7 @@ describe('GET /projects/:projectId/data-tables/:dataStoreId/rows', () => { }); const response = await authMemberAgent - .get(`/projects/${project.id}/data-tables/${dataStore.id}/rows`) + .get(`/projects/${project.id}/data-tables/${dataTable.id}/rows`) .expect(200); expect(response.body.data).toMatchObject({ @@ -1598,7 +1598,7 @@ describe('GET /projects/:projectId/data-tables/:dataStoreId/rows', () => { const project = await createTeamProject('test project', owner); await linkUserToProject(member, project, 'project:editor'); - const dataStore = await createDataStore(project, { + const dataTable = await createDataTable(project, { columns: [ { name: 'first', @@ -1618,7 +1618,7 @@ describe('GET /projects/:projectId/data-tables/:dataStoreId/rows', () => { }); const response = await authMemberAgent - .get(`/projects/${project.id}/data-tables/${dataStore.id}/rows`) + .get(`/projects/${project.id}/data-tables/${dataTable.id}/rows`) .expect(200); expect(response.body.data).toMatchObject({ @@ -1637,7 +1637,7 @@ describe('GET /projects/:projectId/data-tables/:dataStoreId/rows', () => { const project = await createTeamProject('test project', owner); await linkUserToProject(admin, project, 'project:admin'); - const dataStore = await createDataStore(project, { + const dataTable = await createDataTable(project, { columns: [ { name: 'first', @@ -1657,7 +1657,7 @@ describe('GET /projects/:projectId/data-tables/:dataStoreId/rows', () => { }); const response = await authAdminAgent - .get(`/projects/${project.id}/data-tables/${dataStore.id}/rows`) + .get(`/projects/${project.id}/data-tables/${dataTable.id}/rows`) .expect(200); expect(response.body.data).toMatchObject({ @@ -1673,7 +1673,7 @@ describe('GET /projects/:projectId/data-tables/:dataStoreId/rows', () => { }); test('should list rows in personal project', async () => { - const dataStore = await createDataStore(memberProject, { + const dataTable = await createDataTable(memberProject, { columns: [ { name: 'first', @@ -1693,7 +1693,7 @@ describe('GET /projects/:projectId/data-tables/:dataStoreId/rows', () => { }); const response = await authMemberAgent - .get(`/projects/${memberProject.id}/data-tables/${dataStore.id}/rows`) + .get(`/projects/${memberProject.id}/data-tables/${dataTable.id}/rows`) .expect(200); expect(response.body.data).toMatchObject({ @@ -1709,7 +1709,7 @@ describe('GET /projects/:projectId/data-tables/:dataStoreId/rows', () => { }); test("should parse 'eq' filters correctly", async () => { - const dataStore = await createDataStore(memberProject, { + const dataTable = await createDataTable(memberProject, { columns: [ { name: 'name', @@ -1737,7 +1737,7 @@ describe('GET /projects/:projectId/data-tables/:dataStoreId/rows', () => { ); const response = await authMemberAgent - .get(`/projects/${memberProject.id}/data-tables/${dataStore.id}/rows?filter=${filterParam}`) + .get(`/projects/${memberProject.id}/data-tables/${dataTable.id}/rows?filter=${filterParam}`) .expect(200); expect(response.body.data).toEqual({ @@ -1751,7 +1751,7 @@ describe('GET /projects/:projectId/data-tables/:dataStoreId/rows', () => { }); test("should parse 'like' filters correctly", async () => { - const dataStore = await createDataStore(memberProject, { + const dataTable = await createDataTable(memberProject, { columns: [ { name: 'name', @@ -1779,7 +1779,7 @@ describe('GET /projects/:projectId/data-tables/:dataStoreId/rows', () => { ); const response = await authMemberAgent - .get(`/projects/${memberProject.id}/data-tables/${dataStore.id}/rows?filter=${filterParam}`) + .get(`/projects/${memberProject.id}/data-tables/${dataTable.id}/rows?filter=${filterParam}`) .expect(200); expect(response.body.data).toEqual({ @@ -1803,7 +1803,7 @@ describe('GET /projects/:projectId/data-tables/:dataStoreId/rows', () => { ])( 'should filter rows using %s (%s) condition correctly', async (condition, _operator, value, expectedNames, excludedNames) => { - const dataStore = await createDataStore(memberProject, { + const dataTable = await createDataTable(memberProject, { columns: [ { name: 'name', @@ -1837,11 +1837,11 @@ describe('GET /projects/:projectId/data-tables/:dataStoreId/rows', () => { }), ); const response = await authMemberAgent - .get(`/projects/${memberProject.id}/data-tables/${dataStore.id}/rows?filter=${filterParam}`) + .get(`/projects/${memberProject.id}/data-tables/${dataTable.id}/rows?filter=${filterParam}`) .expect(200); expect(response.body.data.count).toBe(expectedNames.length); - const returnedNames = (response.body.data.data as DataStoreRow[]).map((row) => row.name); + const returnedNames = (response.body.data.data as DataTableRow[]).map((row) => row.name); for (const expectedName of expectedNames) { expect(returnedNames).toContain(expectedName); @@ -1856,7 +1856,7 @@ describe('GET /projects/:projectId/data-tables/:dataStoreId/rows', () => { test.each(['like', 'ilike'])( 'should auto-wrap %s filters if no wildcard is present', async (condition) => { - const dataStore = await createDataStore(memberProject, { + const dataTable = await createDataTable(memberProject, { columns: [ { name: 'name', @@ -1883,7 +1883,7 @@ describe('GET /projects/:projectId/data-tables/:dataStoreId/rows', () => { }), ); const response = await authMemberAgent - .get(`/projects/${memberProject.id}/data-tables/${dataStore.id}/rows?filter=${filterParam}`) + .get(`/projects/${memberProject.id}/data-tables/${dataTable.id}/rows?filter=${filterParam}`) .expect(200); expect(response.body.data.count).toBe(1); @@ -1892,7 +1892,7 @@ describe('GET /projects/:projectId/data-tables/:dataStoreId/rows', () => { ); }); -describe('POST /projects/:projectId/data-tables/:dataStoreId/insert', () => { +describe('POST /projects/:projectId/data-tables/:dataTableId/insert', () => { test('should not insert rows when project does not exist', async () => { const payload = { data: [ @@ -1910,7 +1910,7 @@ describe('POST /projects/:projectId/data-tables/:dataStoreId/insert', () => { .expect(403); }); - test('should not insert rows when data store does not exist', async () => { + test('should not insert rows when data table does not exist', async () => { const project = await createTeamProject('test project', owner); const payload = { data: [ @@ -1928,8 +1928,8 @@ describe('POST /projects/:projectId/data-tables/:dataStoreId/insert', () => { .expect(404); }); - test("should not insert rows in another user's personal project data store", async () => { - const dataStore = await createDataStore(ownerProject, { + test("should not insert rows in another user's personal project data table", async () => { + const dataTable = await createDataTable(ownerProject, { columns: [ { name: 'first', @@ -1953,7 +1953,7 @@ describe('POST /projects/:projectId/data-tables/:dataStoreId/insert', () => { }; await authMemberAgent - .post(`/projects/${ownerProject.id}/data-tables/${dataStore.id}/insert`) + .post(`/projects/${ownerProject.id}/data-tables/${dataTable.id}/insert`) .send(payload) .expect(403); }); @@ -1961,7 +1961,7 @@ describe('POST /projects/:projectId/data-tables/:dataStoreId/insert', () => { test('should not insert rows if user has project:viewer role in team project', async () => { const project = await createTeamProject('test project', owner); await linkUserToProject(member, project, 'project:viewer'); - const dataStore = await createDataStore(project, { + const dataTable = await createDataTable(project, { columns: [ { name: 'first', @@ -1985,7 +1985,7 @@ describe('POST /projects/:projectId/data-tables/:dataStoreId/insert', () => { }; await authMemberAgent - .post(`/projects/${project.id}/data-tables/${dataStore.id}/insert`) + .post(`/projects/${project.id}/data-tables/${dataTable.id}/insert`) .send(payload) .expect(403); }); @@ -1994,7 +1994,7 @@ describe('POST /projects/:projectId/data-tables/:dataStoreId/insert', () => { const project = await createTeamProject('test project', owner); await linkUserToProject(member, project, 'project:editor'); - const dataStore = await createDataStore(project, { + const dataTable = await createDataTable(project, { columns: [ { name: 'first', @@ -2018,7 +2018,7 @@ describe('POST /projects/:projectId/data-tables/:dataStoreId/insert', () => { }; const response = await authMemberAgent - .post(`/projects/${project.id}/data-tables/${dataStore.id}/insert`) + .post(`/projects/${project.id}/data-tables/${dataTable.id}/insert`) .send(payload) .expect(200); @@ -2026,7 +2026,7 @@ describe('POST /projects/:projectId/data-tables/:dataStoreId/insert', () => { data: [{ id: 1 }], }); - const rowsInDb = await dataStoreRowsRepository.getManyAndCount(dataStore.id, {}); + const rowsInDb = await dataTableRowsRepository.getManyAndCount(dataTable.id, {}); expect(rowsInDb.count).toBe(1); expect(rowsInDb.data[0]).toMatchObject(payload.data[0]); }); @@ -2035,7 +2035,7 @@ describe('POST /projects/:projectId/data-tables/:dataStoreId/insert', () => { const project = await createTeamProject('test project', owner); await linkUserToProject(admin, project, 'project:admin'); - const dataStore = await createDataStore(project, { + const dataTable = await createDataTable(project, { columns: [ { name: 'first', @@ -2059,7 +2059,7 @@ describe('POST /projects/:projectId/data-tables/:dataStoreId/insert', () => { }; const response = await authAdminAgent - .post(`/projects/${project.id}/data-tables/${dataStore.id}/insert`) + .post(`/projects/${project.id}/data-tables/${dataTable.id}/insert`) .send(payload) .expect(200); @@ -2067,13 +2067,13 @@ describe('POST /projects/:projectId/data-tables/:dataStoreId/insert', () => { data: [{ id: 1 }], }); - const rowsInDb = await dataStoreRowsRepository.getManyAndCount(dataStore.id, {}); + const rowsInDb = await dataTableRowsRepository.getManyAndCount(dataTable.id, {}); expect(rowsInDb.count).toBe(1); expect(rowsInDb.data[0]).toMatchObject(payload.data[0]); }); test('should insert rows in personal project', async () => { - const dataStore = await createDataStore(memberProject, { + const dataTable = await createDataTable(memberProject, { columns: [ { name: 'first', @@ -2097,7 +2097,7 @@ describe('POST /projects/:projectId/data-tables/:dataStoreId/insert', () => { }; const response = await authMemberAgent - .post(`/projects/${memberProject.id}/data-tables/${dataStore.id}/insert`) + .post(`/projects/${memberProject.id}/data-tables/${dataTable.id}/insert`) .send(payload) .expect(200); @@ -2105,13 +2105,13 @@ describe('POST /projects/:projectId/data-tables/:dataStoreId/insert', () => { data: [{ id: 1 }], }); - const rowsInDb = await dataStoreRowsRepository.getManyAndCount(dataStore.id, {}); + const rowsInDb = await dataTableRowsRepository.getManyAndCount(dataTable.id, {}); expect(rowsInDb.count).toBe(1); expect(rowsInDb.data[0]).toMatchObject(payload.data[0]); }); test('should return inserted data if returnData is set', async () => { - const dataStore = await createDataStore(memberProject, { + const dataTable = await createDataTable(memberProject, { columns: [ { name: 'first', @@ -2139,7 +2139,7 @@ describe('POST /projects/:projectId/data-tables/:dataStoreId/insert', () => { }; const response = await authMemberAgent - .post(`/projects/${memberProject.id}/data-tables/${dataStore.id}/insert`) + .post(`/projects/${memberProject.id}/data-tables/${dataTable.id}/insert`) .send(payload) .expect(200); @@ -2162,13 +2162,13 @@ describe('POST /projects/:projectId/data-tables/:dataStoreId/insert', () => { ], }); - const rowsInDb = await dataStoreRowsRepository.getManyAndCount(dataStore.id, {}); + const rowsInDb = await dataTableRowsRepository.getManyAndCount(dataTable.id, {}); expect(rowsInDb.count).toBe(2); expect(rowsInDb.data[0]).toMatchObject(payload.data[0]); }); test('should not insert rows when column does not exist', async () => { - const dataStore = await createDataStore(memberProject, { + const dataTable = await createDataTable(memberProject, { columns: [ { name: 'first', @@ -2192,17 +2192,17 @@ describe('POST /projects/:projectId/data-tables/:dataStoreId/insert', () => { }; const response = await authMemberAgent - .post(`/projects/${memberProject.id}/data-tables/${dataStore.id}/insert`) + .post(`/projects/${memberProject.id}/data-tables/${dataTable.id}/insert`) .send(payload) .expect(400); expect(response.body.message).toContain('unknown column'); - const rowsInDb = await dataStoreRowsRepository.getManyAndCount(dataStore.id, {}); + const rowsInDb = await dataTableRowsRepository.getManyAndCount(dataTable.id, {}); expect(rowsInDb.count).toBe(0); }); test('should insert columns with dates', async () => { - const dataStore = await createDataStore(memberProject, { + const dataTable = await createDataTable(memberProject, { columns: [ { name: 'a', @@ -2226,7 +2226,7 @@ describe('POST /projects/:projectId/data-tables/:dataStoreId/insert', () => { }; const response = await authMemberAgent - .post(`/projects/${memberProject.id}/data-tables/${dataStore.id}/insert`) + .post(`/projects/${memberProject.id}/data-tables/${dataTable.id}/insert`) .send(payload) .expect(200); @@ -2235,7 +2235,7 @@ describe('POST /projects/:projectId/data-tables/:dataStoreId/insert', () => { }); const readResponse = await authMemberAgent - .get(`/projects/${memberProject.id}/data-tables/${dataStore.id}/rows`) + .get(`/projects/${memberProject.id}/data-tables/${dataTable.id}/rows`) .expect(200); expect(readResponse.body.data.count).toBe(1); @@ -2246,7 +2246,7 @@ describe('POST /projects/:projectId/data-tables/:dataStoreId/insert', () => { }); test('should insert columns with strings', async () => { - const dataStore = await createDataStore(memberProject, { + const dataTable = await createDataTable(memberProject, { columns: [ { name: 'a', @@ -2275,7 +2275,7 @@ describe('POST /projects/:projectId/data-tables/:dataStoreId/insert', () => { }; const response = await authMemberAgent - .post(`/projects/${memberProject.id}/data-tables/${dataStore.id}/insert`) + .post(`/projects/${memberProject.id}/data-tables/${dataTable.id}/insert`) .send(payload) .expect(200); @@ -2284,7 +2284,7 @@ describe('POST /projects/:projectId/data-tables/:dataStoreId/insert', () => { }); const readResponse = await authMemberAgent - .get(`/projects/${memberProject.id}/data-tables/${dataStore.id}/rows`) + .get(`/projects/${memberProject.id}/data-tables/${dataTable.id}/rows`) .expect(200); expect(readResponse.body.data.count).toBe(1); @@ -2292,7 +2292,7 @@ describe('POST /projects/:projectId/data-tables/:dataStoreId/insert', () => { }); test('should insert columns with booleans', async () => { - const dataStore = await createDataStore(memberProject, { + const dataTable = await createDataTable(memberProject, { columns: [ { name: 'a', @@ -2316,7 +2316,7 @@ describe('POST /projects/:projectId/data-tables/:dataStoreId/insert', () => { }; const response = await authMemberAgent - .post(`/projects/${memberProject.id}/data-tables/${dataStore.id}/insert`) + .post(`/projects/${memberProject.id}/data-tables/${dataTable.id}/insert`) .send(payload) .expect(200); @@ -2325,7 +2325,7 @@ describe('POST /projects/:projectId/data-tables/:dataStoreId/insert', () => { }); const readResponse = await authMemberAgent - .get(`/projects/${memberProject.id}/data-tables/${dataStore.id}/rows`) + .get(`/projects/${memberProject.id}/data-tables/${dataTable.id}/rows`) .expect(200); expect(readResponse.body.data.count).toBe(1); @@ -2333,7 +2333,7 @@ describe('POST /projects/:projectId/data-tables/:dataStoreId/insert', () => { }); test('should insert columns with numbers', async () => { - const dataStore = await createDataStore(memberProject, { + const dataTable = await createDataTable(memberProject, { columns: [ { name: 'a', @@ -2372,7 +2372,7 @@ describe('POST /projects/:projectId/data-tables/:dataStoreId/insert', () => { }; const response = await authMemberAgent - .post(`/projects/${memberProject.id}/data-tables/${dataStore.id}/insert`) + .post(`/projects/${memberProject.id}/data-tables/${dataTable.id}/insert`) .send(payload) .expect(200); @@ -2381,7 +2381,7 @@ describe('POST /projects/:projectId/data-tables/:dataStoreId/insert', () => { }); const readResponse = await authMemberAgent - .get(`/projects/${memberProject.id}/data-tables/${dataStore.id}/rows`) + .get(`/projects/${memberProject.id}/data-tables/${dataTable.id}/rows`) .expect(200); expect(readResponse.body.data.count).toBe(1); @@ -2389,7 +2389,7 @@ describe('POST /projects/:projectId/data-tables/:dataStoreId/insert', () => { }); test('should insert columns with null values', async () => { - const dataStore = await createDataStore(memberProject, { + const dataTable = await createDataTable(memberProject, { columns: [ { name: 'a', @@ -2423,7 +2423,7 @@ describe('POST /projects/:projectId/data-tables/:dataStoreId/insert', () => { }; const response = await authMemberAgent - .post(`/projects/${memberProject.id}/data-tables/${dataStore.id}/insert`) + .post(`/projects/${memberProject.id}/data-tables/${dataTable.id}/insert`) .send(payload) .expect(200); @@ -2432,7 +2432,7 @@ describe('POST /projects/:projectId/data-tables/:dataStoreId/insert', () => { }); const readResponse = await authMemberAgent - .get(`/projects/${memberProject.id}/data-tables/${dataStore.id}/rows`) + .get(`/projects/${memberProject.id}/data-tables/${dataTable.id}/rows`) .expect(200); expect(readResponse.body.data.count).toBe(1); @@ -2440,7 +2440,7 @@ describe('POST /projects/:projectId/data-tables/:dataStoreId/insert', () => { }); test('should insert multiple rows', async () => { - const dataStore = await createDataStore(memberProject, { + const dataTable = await createDataTable(memberProject, { columns: [ { name: 'a', @@ -2472,7 +2472,7 @@ describe('POST /projects/:projectId/data-tables/:dataStoreId/insert', () => { }; const first = await authMemberAgent - .post(`/projects/${memberProject.id}/data-tables/${dataStore.id}/insert`) + .post(`/projects/${memberProject.id}/data-tables/${dataTable.id}/insert`) .send(payload) .expect(200); @@ -2481,7 +2481,7 @@ describe('POST /projects/:projectId/data-tables/:dataStoreId/insert', () => { }); const second = await authMemberAgent - .post(`/projects/${memberProject.id}/data-tables/${dataStore.id}/insert`) + .post(`/projects/${memberProject.id}/data-tables/${dataTable.id}/insert`) .send(payload) .expect(200); @@ -2490,7 +2490,7 @@ describe('POST /projects/:projectId/data-tables/:dataStoreId/insert', () => { }); const readResponse = await authMemberAgent - .get(`/projects/${memberProject.id}/data-tables/${dataStore.id}/rows`) + .get(`/projects/${memberProject.id}/data-tables/${dataTable.id}/rows`) .expect(200); expect(readResponse.body.data.count).toBe(6); @@ -2498,7 +2498,7 @@ describe('POST /projects/:projectId/data-tables/:dataStoreId/insert', () => { }); }); -describe('DELETE /projects/:projectId/data-tables/:dataStoreId/rows', () => { +describe('DELETE /projects/:projectId/data-tables/:dataTableId/rows', () => { test('should not delete rows when project does not exist', async () => { await authOwnerAgent .delete('/projects/non-existing-id/data-tables/some-data-store-id/rows') @@ -2511,7 +2511,7 @@ describe('DELETE /projects/:projectId/data-tables/:dataStoreId/rows', () => { .expect(403); }); - test('should not delete rows when data store does not exist', async () => { + test('should not delete rows when data table does not exist', async () => { const project = await createTeamProject('test project', owner); await authOwnerAgent @@ -2527,7 +2527,7 @@ describe('DELETE /projects/:projectId/data-tables/:dataStoreId/rows', () => { test('should not delete rows when no filter is provided', async () => { const project = await createTeamProject('test project', owner); - const dataStore = await createDataStore(project, { + const dataTable = await createDataTable(project, { columns: [ { name: 'first', @@ -2537,13 +2537,13 @@ describe('DELETE /projects/:projectId/data-tables/:dataStoreId/rows', () => { }); await authOwnerAgent - .delete(`/projects/${project.id}/data-tables/${dataStore.id}/rows`) + .delete(`/projects/${project.id}/data-tables/${dataTable.id}/rows`) .expect(400); }); test('should not delete rows when filter has empty filters array', async () => { const project = await createTeamProject('test project', owner); - const dataStore = await createDataStore(project, { + const dataTable = await createDataTable(project, { columns: [ { name: 'first', @@ -2553,7 +2553,7 @@ describe('DELETE /projects/:projectId/data-tables/:dataStoreId/rows', () => { }); await authOwnerAgent - .delete(`/projects/${project.id}/data-tables/${dataStore.id}/rows`) + .delete(`/projects/${project.id}/data-tables/${dataTable.id}/rows`) .query({ filter: { type: 'and', @@ -2563,8 +2563,8 @@ describe('DELETE /projects/:projectId/data-tables/:dataStoreId/rows', () => { .expect(400); }); - test("should not delete rows in another user's personal project data store", async () => { - const dataStore = await createDataStore(ownerProject, { + test("should not delete rows in another user's personal project data table", async () => { + const dataTable = await createDataTable(ownerProject, { columns: [ { name: 'first', @@ -2584,7 +2584,7 @@ describe('DELETE /projects/:projectId/data-tables/:dataStoreId/rows', () => { }); await authMemberAgent - .delete(`/projects/${ownerProject.id}/data-tables/${dataStore.id}/rows`) + .delete(`/projects/${ownerProject.id}/data-tables/${dataTable.id}/rows`) .query({ filter: JSON.stringify({ type: 'and', @@ -2593,14 +2593,14 @@ describe('DELETE /projects/:projectId/data-tables/:dataStoreId/rows', () => { }) .expect(403); - const rowsInDb = await dataStoreRowsRepository.getManyAndCount(dataStore.id, {}); + const rowsInDb = await dataTableRowsRepository.getManyAndCount(dataTable.id, {}); expect(rowsInDb.count).toBe(1); }); test('should not delete rows if user has project:viewer role in team project', async () => { const project = await createTeamProject('test project', owner); await linkUserToProject(member, project, 'project:viewer'); - const dataStore = await createDataStore(project, { + const dataTable = await createDataTable(project, { columns: [ { name: 'first', @@ -2620,7 +2620,7 @@ describe('DELETE /projects/:projectId/data-tables/:dataStoreId/rows', () => { }); await authMemberAgent - .delete(`/projects/${project.id}/data-tables/${dataStore.id}/rows`) + .delete(`/projects/${project.id}/data-tables/${dataTable.id}/rows`) .query({ filter: JSON.stringify({ type: 'and', @@ -2629,7 +2629,7 @@ describe('DELETE /projects/:projectId/data-tables/:dataStoreId/rows', () => { }) .expect(403); - const rowsInDb = await dataStoreRowsRepository.getManyAndCount(dataStore.id, {}); + const rowsInDb = await dataTableRowsRepository.getManyAndCount(dataTable.id, {}); expect(rowsInDb.count).toBe(1); }); @@ -2637,7 +2637,7 @@ describe('DELETE /projects/:projectId/data-tables/:dataStoreId/rows', () => { const project = await createTeamProject('test project', owner); await linkUserToProject(member, project, 'project:editor'); - const dataStore = await createDataStore(project, { + const dataTable = await createDataTable(project, { columns: [ { name: 'first', @@ -2665,7 +2665,7 @@ describe('DELETE /projects/:projectId/data-tables/:dataStoreId/rows', () => { }); await authMemberAgent - .delete(`/projects/${project.id}/data-tables/${dataStore.id}/rows`) + .delete(`/projects/${project.id}/data-tables/${dataTable.id}/rows`) .query({ filter: JSON.stringify({ type: 'or', @@ -2677,7 +2677,7 @@ describe('DELETE /projects/:projectId/data-tables/:dataStoreId/rows', () => { }) .expect(200); - const rowsInDb = await dataStoreRowsRepository.getManyAndCount(dataStore.id, {}); + const rowsInDb = await dataTableRowsRepository.getManyAndCount(dataTable.id, {}); expect(rowsInDb.count).toBe(1); expect(rowsInDb.data[0]).toMatchObject({ first: 'test value 2', @@ -2689,7 +2689,7 @@ describe('DELETE /projects/:projectId/data-tables/:dataStoreId/rows', () => { const project = await createTeamProject('test project', owner); await linkUserToProject(admin, project, 'project:admin'); - const dataStore = await createDataStore(project, { + const dataTable = await createDataTable(project, { columns: [ { name: 'first', @@ -2713,7 +2713,7 @@ describe('DELETE /projects/:projectId/data-tables/:dataStoreId/rows', () => { }); await authAdminAgent - .delete(`/projects/${project.id}/data-tables/${dataStore.id}/rows`) + .delete(`/projects/${project.id}/data-tables/${dataTable.id}/rows`) .query({ filter: JSON.stringify({ type: 'and', @@ -2722,7 +2722,7 @@ describe('DELETE /projects/:projectId/data-tables/:dataStoreId/rows', () => { }) .expect(200); - const rowsInDb = await dataStoreRowsRepository.getManyAndCount(dataStore.id, {}); + const rowsInDb = await dataTableRowsRepository.getManyAndCount(dataTable.id, {}); expect(rowsInDb.count).toBe(1); expect(rowsInDb.data[0]).toMatchObject({ first: 'test value 1', @@ -2733,7 +2733,7 @@ describe('DELETE /projects/:projectId/data-tables/:dataStoreId/rows', () => { test('should delete rows if user is owner in team project', async () => { const project = await createTeamProject('test project', owner); - const dataStore = await createDataStore(project, { + const dataTable = await createDataTable(project, { columns: [ { name: 'first', @@ -2757,7 +2757,7 @@ describe('DELETE /projects/:projectId/data-tables/:dataStoreId/rows', () => { }); await authOwnerAgent - .delete(`/projects/${project.id}/data-tables/${dataStore.id}/rows`) + .delete(`/projects/${project.id}/data-tables/${dataTable.id}/rows`) .query({ filter: JSON.stringify({ type: 'and', @@ -2766,13 +2766,13 @@ describe('DELETE /projects/:projectId/data-tables/:dataStoreId/rows', () => { }) .expect(200); - const rowsInDb = await dataStoreRowsRepository.getManyAndCount(dataStore.id, {}); + const rowsInDb = await dataTableRowsRepository.getManyAndCount(dataTable.id, {}); expect(rowsInDb.count).toBe(1); expect(rowsInDb.data.map((r) => r.first)).toEqual(['test value 1']); }); test('should delete rows in personal project', async () => { - const dataStore = await createDataStore(memberProject, { + const dataTable = await createDataTable(memberProject, { columns: [ { name: 'first', @@ -2800,7 +2800,7 @@ describe('DELETE /projects/:projectId/data-tables/:dataStoreId/rows', () => { }); await authMemberAgent - .delete(`/projects/${memberProject.id}/data-tables/${dataStore.id}/rows`) + .delete(`/projects/${memberProject.id}/data-tables/${dataTable.id}/rows`) .query({ filter: JSON.stringify({ type: 'and', @@ -2809,13 +2809,13 @@ describe('DELETE /projects/:projectId/data-tables/:dataStoreId/rows', () => { }) .expect(200); - const rowsInDb = await dataStoreRowsRepository.getManyAndCount(dataStore.id, {}); + const rowsInDb = await dataTableRowsRepository.getManyAndCount(dataTable.id, {}); expect(rowsInDb.count).toBe(2); expect(rowsInDb.data.map((r) => r.first).sort()).toEqual(['test value 1', 'test value 3']); }); test('should return full deleted data if returnData is set', async () => { - const dataStore = await createDataStore(memberProject, { + const dataTable = await createDataTable(memberProject, { columns: [ { name: 'first', @@ -2843,7 +2843,7 @@ describe('DELETE /projects/:projectId/data-tables/:dataStoreId/rows', () => { }); const result = await authMemberAgent - .delete(`/projects/${memberProject.id}/data-tables/${dataStore.id}/rows`) + .delete(`/projects/${memberProject.id}/data-tables/${dataTable.id}/rows`) .query({ filter: JSON.stringify({ type: 'and', @@ -2864,7 +2864,7 @@ describe('DELETE /projects/:projectId/data-tables/:dataStoreId/rows', () => { }); }); -describe('POST /projects/:projectId/data-tables/:dataStoreId/upsert', () => { +describe('POST /projects/:projectId/data-tables/:dataTableId/upsert', () => { test('should not upsert rows when project does not exist', async () => { const payload = { filter: { type: 'and', filters: [{ columnName: 'name', condition: 'eq', value: 'Alice' }] }, @@ -2877,7 +2877,7 @@ describe('POST /projects/:projectId/data-tables/:dataStoreId/upsert', () => { .expect(403); }); - test('should not upsert rows when data store does not exist', async () => { + test('should not upsert rows when data table does not exist', async () => { const project = await createTeamProject('test project', owner); const payload = { filter: { type: 'and', filters: [{ columnName: 'name', condition: 'eq', value: 'Alice' }] }, @@ -2890,8 +2890,8 @@ describe('POST /projects/:projectId/data-tables/:dataStoreId/upsert', () => { .expect(404); }); - test("should not upsert rows in another user's personal project data store", async () => { - const dataStore = await createDataStore(ownerProject, { + test("should not upsert rows in another user's personal project data table", async () => { + const dataTable = await createDataTable(ownerProject, { columns: [ { name: 'name', @@ -2910,7 +2910,7 @@ describe('POST /projects/:projectId/data-tables/:dataStoreId/upsert', () => { }; await authMemberAgent - .post(`/projects/${ownerProject.id}/data-tables/${dataStore.id}/upsert`) + .post(`/projects/${ownerProject.id}/data-tables/${dataTable.id}/upsert`) .send(payload) .expect(403); }); @@ -2918,7 +2918,7 @@ describe('POST /projects/:projectId/data-tables/:dataStoreId/upsert', () => { test('should not upsert rows if user has project:viewer role in team project', async () => { const project = await createTeamProject('test project', owner); await linkUserToProject(member, project, 'project:viewer'); - const dataStore = await createDataStore(project, { + const dataTable = await createDataTable(project, { columns: [ { name: 'name', @@ -2937,7 +2937,7 @@ describe('POST /projects/:projectId/data-tables/:dataStoreId/upsert', () => { }; await authMemberAgent - .post(`/projects/${project.id}/data-tables/${dataStore.id}/upsert`) + .post(`/projects/${project.id}/data-tables/${dataTable.id}/upsert`) .send(payload) .expect(403); }); @@ -2946,7 +2946,7 @@ describe('POST /projects/:projectId/data-tables/:dataStoreId/upsert', () => { const project = await createTeamProject('test project', owner); await linkUserToProject(member, project, 'project:editor'); - const dataStore = await createDataStore(project, { + const dataTable = await createDataTable(project, { columns: [ { name: 'name', @@ -2965,11 +2965,11 @@ describe('POST /projects/:projectId/data-tables/:dataStoreId/upsert', () => { }; await authMemberAgent - .post(`/projects/${project.id}/data-tables/${dataStore.id}/upsert`) + .post(`/projects/${project.id}/data-tables/${dataTable.id}/upsert`) .send(payload) .expect(200); - const rowsInDb = await dataStoreRowsRepository.getManyAndCount(dataStore.id, {}); + const rowsInDb = await dataTableRowsRepository.getManyAndCount(dataTable.id, {}); expect(rowsInDb.count).toBe(1); expect(rowsInDb.data[0]).toMatchObject(payload.data); }); @@ -2978,7 +2978,7 @@ describe('POST /projects/:projectId/data-tables/:dataStoreId/upsert', () => { const project = await createTeamProject('test project', owner); await linkUserToProject(admin, project, 'project:admin'); - const dataStore = await createDataStore(project, { + const dataTable = await createDataTable(project, { columns: [ { name: 'name', @@ -2997,17 +2997,17 @@ describe('POST /projects/:projectId/data-tables/:dataStoreId/upsert', () => { }; await authAdminAgent - .post(`/projects/${project.id}/data-tables/${dataStore.id}/upsert`) + .post(`/projects/${project.id}/data-tables/${dataTable.id}/upsert`) .send(payload) .expect(200); - const rowsInDb = await dataStoreRowsRepository.getManyAndCount(dataStore.id, {}); + const rowsInDb = await dataTableRowsRepository.getManyAndCount(dataTable.id, {}); expect(rowsInDb.count).toBe(1); expect(rowsInDb.data[0]).toMatchObject(payload.data); }); test('should upsert rows in personal project', async () => { - const dataStore = await createDataStore(memberProject, { + const dataTable = await createDataTable(memberProject, { columns: [ { name: 'name', @@ -3026,17 +3026,17 @@ describe('POST /projects/:projectId/data-tables/:dataStoreId/upsert', () => { }; await authMemberAgent - .post(`/projects/${memberProject.id}/data-tables/${dataStore.id}/upsert`) + .post(`/projects/${memberProject.id}/data-tables/${dataTable.id}/upsert`) .send(payload) .expect(200); - const rowsInDb = await dataStoreRowsRepository.getManyAndCount(dataStore.id, {}); + const rowsInDb = await dataTableRowsRepository.getManyAndCount(dataTable.id, {}); expect(rowsInDb.count).toBe(1); expect(rowsInDb.data[0]).toMatchObject(payload.data); }); test('should not upsert rows when column does not exist', async () => { - const dataStore = await createDataStore(memberProject, { + const dataTable = await createDataTable(memberProject, { columns: [ { name: 'name', @@ -3055,17 +3055,17 @@ describe('POST /projects/:projectId/data-tables/:dataStoreId/upsert', () => { }; const response = await authMemberAgent - .post(`/projects/${memberProject.id}/data-tables/${dataStore.id}/upsert`) + .post(`/projects/${memberProject.id}/data-tables/${dataTable.id}/upsert`) .send(payload) .expect(400); expect(response.body.message).toContain('unknown column'); - const rowsInDb = await dataStoreRowsRepository.getManyAndCount(dataStore.id, {}); + const rowsInDb = await dataTableRowsRepository.getManyAndCount(dataTable.id, {}); expect(rowsInDb.count).toBe(0); }); test('should return updated row if returnData is set', async () => { - const dataStore = await createDataStore(memberProject, { + const dataTable = await createDataTable(memberProject, { columns: [ { name: 'name', @@ -3095,7 +3095,7 @@ describe('POST /projects/:projectId/data-tables/:dataStoreId/upsert', () => { }; const result = await authMemberAgent - .post(`/projects/${memberProject.id}/data-tables/${dataStore.id}/upsert`) + .post(`/projects/${memberProject.id}/data-tables/${dataTable.id}/upsert`) .send(payload) .expect(200); @@ -3111,7 +3111,7 @@ describe('POST /projects/:projectId/data-tables/:dataStoreId/upsert', () => { }); }); -describe('PATCH /projects/:projectId/data-tables/:dataStoreId/rows', () => { +describe('PATCH /projects/:projectId/data-tables/:dataTableId/rows', () => { test('should not update row when project does not exist', async () => { const payload = { filter: { name: 'Alice' }, @@ -3124,7 +3124,7 @@ describe('PATCH /projects/:projectId/data-tables/:dataStoreId/rows', () => { .expect(403); }); - test('should not update row when data store does not exist', async () => { + test('should not update row when data table does not exist', async () => { const project = await createTeamProject('test project', owner); const payload = { filter: { type: 'and', filters: [{ columnName: 'name', condition: 'eq', value: 'Alice' }] }, @@ -3137,8 +3137,8 @@ describe('PATCH /projects/:projectId/data-tables/:dataStoreId/rows', () => { .expect(404); }); - test("should not update row in another user's personal project data store", async () => { - const dataStore = await createDataStore(ownerProject, { + test("should not update row in another user's personal project data table", async () => { + const dataTable = await createDataTable(ownerProject, { columns: [ { name: 'name', type: 'string' }, { name: 'age', type: 'number' }, @@ -3152,7 +3152,7 @@ describe('PATCH /projects/:projectId/data-tables/:dataStoreId/rows', () => { }; await authMemberAgent - .patch(`/projects/${ownerProject.id}/data-tables/${dataStore.id}/rows`) + .patch(`/projects/${ownerProject.id}/data-tables/${dataTable.id}/rows`) .send(payload) .expect(403); }); @@ -3160,7 +3160,7 @@ describe('PATCH /projects/:projectId/data-tables/:dataStoreId/rows', () => { test('should not update row if user has project:viewer role in team project', async () => { const project = await createTeamProject('test project', owner); await linkUserToProject(member, project, 'project:viewer'); - const dataStore = await createDataStore(project, { + const dataTable = await createDataTable(project, { columns: [ { name: 'name', type: 'string' }, { name: 'age', type: 'number' }, @@ -3174,7 +3174,7 @@ describe('PATCH /projects/:projectId/data-tables/:dataStoreId/rows', () => { }; await authMemberAgent - .patch(`/projects/${project.id}/data-tables/${dataStore.id}/rows`) + .patch(`/projects/${project.id}/data-tables/${dataTable.id}/rows`) .send(payload) .expect(403); }); @@ -3182,7 +3182,7 @@ describe('PATCH /projects/:projectId/data-tables/:dataStoreId/rows', () => { test('should update row if user has project:editor role in team project', async () => { const project = await createTeamProject('test project', owner); await linkUserToProject(member, project, 'project:editor'); - const dataStore = await createDataStore(project, { + const dataTable = await createDataTable(project, { columns: [ { name: 'name', type: 'string' }, { name: 'age', type: 'number' }, @@ -3198,14 +3198,14 @@ describe('PATCH /projects/:projectId/data-tables/:dataStoreId/rows', () => { }; const result = await authMemberAgent - .patch(`/projects/${project.id}/data-tables/${dataStore.id}/rows`) + .patch(`/projects/${project.id}/data-tables/${dataTable.id}/rows`) .send(payload) .expect(200); expect(result.body.data).toBe(true); const readResponse = await authMemberAgent - .get(`/projects/${project.id}/data-tables/${dataStore.id}/rows`) + .get(`/projects/${project.id}/data-tables/${dataTable.id}/rows`) .expect(200); expect(readResponse.body.data.count).toBe(1); @@ -3221,7 +3221,7 @@ describe('PATCH /projects/:projectId/data-tables/:dataStoreId/rows', () => { test('should update row if user has project:admin role in team project', async () => { const project = await createTeamProject('test project', owner); await linkUserToProject(admin, project, 'project:admin'); - const dataStore = await createDataStore(project, { + const dataTable = await createDataTable(project, { columns: [ { name: 'name', type: 'string' }, { name: 'age', type: 'number' }, @@ -3235,12 +3235,12 @@ describe('PATCH /projects/:projectId/data-tables/:dataStoreId/rows', () => { }; await authAdminAgent - .patch(`/projects/${project.id}/data-tables/${dataStore.id}/rows`) + .patch(`/projects/${project.id}/data-tables/${dataTable.id}/rows`) .send(payload) .expect(200); const readResponse = await authAdminAgent - .get(`/projects/${project.id}/data-tables/${dataStore.id}/rows`) + .get(`/projects/${project.id}/data-tables/${dataTable.id}/rows`) .expect(200); expect(readResponse.body.data.count).toBe(1); @@ -3249,7 +3249,7 @@ describe('PATCH /projects/:projectId/data-tables/:dataStoreId/rows', () => { test('should update row if user is owner in team project', async () => { const project = await createTeamProject('test project', owner); - const dataStore = await createDataStore(project, { + const dataTable = await createDataTable(project, { columns: [ { name: 'name', type: 'string' }, { name: 'age', type: 'number' }, @@ -3263,12 +3263,12 @@ describe('PATCH /projects/:projectId/data-tables/:dataStoreId/rows', () => { }; await authOwnerAgent - .patch(`/projects/${project.id}/data-tables/${dataStore.id}/rows`) + .patch(`/projects/${project.id}/data-tables/${dataTable.id}/rows`) .send(payload) .expect(200); const readResponse = await authOwnerAgent - .get(`/projects/${project.id}/data-tables/${dataStore.id}/rows`) + .get(`/projects/${project.id}/data-tables/${dataTable.id}/rows`) .expect(200); expect(readResponse.body.data.count).toBe(1); @@ -3280,7 +3280,7 @@ describe('PATCH /projects/:projectId/data-tables/:dataStoreId/rows', () => { }); test('should update row in personal project', async () => { - const dataStore = await createDataStore(memberProject, { + const dataTable = await createDataTable(memberProject, { columns: [ { name: 'name', type: 'string' }, { name: 'age', type: 'number' }, @@ -3294,12 +3294,12 @@ describe('PATCH /projects/:projectId/data-tables/:dataStoreId/rows', () => { }; await authMemberAgent - .patch(`/projects/${memberProject.id}/data-tables/${dataStore.id}/rows`) + .patch(`/projects/${memberProject.id}/data-tables/${dataTable.id}/rows`) .send(payload) .expect(200); const readResponse = await authMemberAgent - .get(`/projects/${memberProject.id}/data-tables/${dataStore.id}/rows`) + .get(`/projects/${memberProject.id}/data-tables/${dataTable.id}/rows`) .expect(200); expect(readResponse.body.data.count).toBe(1); @@ -3311,7 +3311,7 @@ describe('PATCH /projects/:projectId/data-tables/:dataStoreId/rows', () => { }); test('should update row by id filter', async () => { - const dataStore = await createDataStore(memberProject, { + const dataTable = await createDataTable(memberProject, { columns: [ { name: 'name', type: 'string' }, { name: 'age', type: 'number' }, @@ -3328,12 +3328,12 @@ describe('PATCH /projects/:projectId/data-tables/:dataStoreId/rows', () => { }; await authMemberAgent - .patch(`/projects/${memberProject.id}/data-tables/${dataStore.id}/rows`) + .patch(`/projects/${memberProject.id}/data-tables/${dataTable.id}/rows`) .send(payload) .expect(200); const readResponse = await authMemberAgent - .get(`/projects/${memberProject.id}/data-tables/${dataStore.id}/rows`) + .get(`/projects/${memberProject.id}/data-tables/${dataTable.id}/rows`) .expect(200); expect(readResponse.body.data.count).toBe(2); @@ -3354,7 +3354,7 @@ describe('PATCH /projects/:projectId/data-tables/:dataStoreId/rows', () => { }); test('should update row with multiple filter conditions', async () => { - const dataStore = await createDataStore(memberProject, { + const dataTable = await createDataTable(memberProject, { columns: [ { name: 'name', type: 'string' }, { name: 'age', type: 'number' }, @@ -3379,12 +3379,12 @@ describe('PATCH /projects/:projectId/data-tables/:dataStoreId/rows', () => { }; await authMemberAgent - .patch(`/projects/${memberProject.id}/data-tables/${dataStore.id}/rows`) + .patch(`/projects/${memberProject.id}/data-tables/${dataTable.id}/rows`) .send(payload) .expect(200); const readResponse = await authMemberAgent - .get(`/projects/${memberProject.id}/data-tables/${dataStore.id}/rows`) + .get(`/projects/${memberProject.id}/data-tables/${dataTable.id}/rows`) .expect(200); expect(readResponse.body.data.count).toBe(3); @@ -3413,7 +3413,7 @@ describe('PATCH /projects/:projectId/data-tables/:dataStoreId/rows', () => { }); test('should return true when no rows match the filter', async () => { - const dataStore = await createDataStore(memberProject, { + const dataTable = await createDataTable(memberProject, { columns: [ { name: 'name', type: 'string' }, { name: 'age', type: 'number' }, @@ -3427,14 +3427,14 @@ describe('PATCH /projects/:projectId/data-tables/:dataStoreId/rows', () => { }; const response = await authMemberAgent - .patch(`/projects/${memberProject.id}/data-tables/${dataStore.id}/rows`) + .patch(`/projects/${memberProject.id}/data-tables/${dataTable.id}/rows`) .send(payload) .expect(200); expect(response.body.data).toEqual(true); const readResponse = await authMemberAgent - .get(`/projects/${memberProject.id}/data-tables/${dataStore.id}/rows`) + .get(`/projects/${memberProject.id}/data-tables/${dataTable.id}/rows`) .expect(200); expect(readResponse.body.data.count).toBe(1); @@ -3445,7 +3445,7 @@ describe('PATCH /projects/:projectId/data-tables/:dataStoreId/rows', () => { }); test('should fail when filter is empty', async () => { - const dataStore = await createDataStore(memberProject, { + const dataTable = await createDataTable(memberProject, { columns: [{ name: 'name', type: 'string' }], }); @@ -3455,7 +3455,7 @@ describe('PATCH /projects/:projectId/data-tables/:dataStoreId/rows', () => { }; const response = await authMemberAgent - .patch(`/projects/${memberProject.id}/data-tables/${dataStore.id}/rows`) + .patch(`/projects/${memberProject.id}/data-tables/${dataTable.id}/rows`) .send(payload) .expect(400); @@ -3463,7 +3463,7 @@ describe('PATCH /projects/:projectId/data-tables/:dataStoreId/rows', () => { }); test('should fail when data is empty', async () => { - const dataStore = await createDataStore(memberProject, { + const dataTable = await createDataTable(memberProject, { columns: [{ name: 'name', type: 'string' }], }); @@ -3473,7 +3473,7 @@ describe('PATCH /projects/:projectId/data-tables/:dataStoreId/rows', () => { }; const response = await authMemberAgent - .patch(`/projects/${memberProject.id}/data-tables/${dataStore.id}/rows`) + .patch(`/projects/${memberProject.id}/data-tables/${dataTable.id}/rows`) .send(payload) .expect(400); @@ -3481,7 +3481,7 @@ describe('PATCH /projects/:projectId/data-tables/:dataStoreId/rows', () => { }); test('should fail when data contains invalid column names', async () => { - const dataStore = await createDataStore(memberProject, { + const dataTable = await createDataTable(memberProject, { columns: [{ name: 'name', type: 'string' }], data: [{ name: 'Alice' }], }); @@ -3492,7 +3492,7 @@ describe('PATCH /projects/:projectId/data-tables/:dataStoreId/rows', () => { }; const response = await authMemberAgent - .patch(`/projects/${memberProject.id}/data-tables/${dataStore.id}/rows`) + .patch(`/projects/${memberProject.id}/data-tables/${dataTable.id}/rows`) .send(payload) .expect(400); @@ -3500,7 +3500,7 @@ describe('PATCH /projects/:projectId/data-tables/:dataStoreId/rows', () => { }); test('should fail when filter contains invalid column names', async () => { - const dataStore = await createDataStore(memberProject, { + const dataTable = await createDataTable(memberProject, { columns: [{ name: 'name', type: 'string' }], data: [{ name: 'Alice' }], }); @@ -3514,7 +3514,7 @@ describe('PATCH /projects/:projectId/data-tables/:dataStoreId/rows', () => { }; const response = await authMemberAgent - .patch(`/projects/${memberProject.id}/data-tables/${dataStore.id}/rows`) + .patch(`/projects/${memberProject.id}/data-tables/${dataTable.id}/rows`) .send(payload) .expect(400); @@ -3522,7 +3522,7 @@ describe('PATCH /projects/:projectId/data-tables/:dataStoreId/rows', () => { }); test('should validate data types in filter', async () => { - const dataStore = await createDataStore(memberProject, { + const dataTable = await createDataTable(memberProject, { columns: [ { name: 'name', type: 'string' }, { name: 'age', type: 'number' }, @@ -3539,7 +3539,7 @@ describe('PATCH /projects/:projectId/data-tables/:dataStoreId/rows', () => { }; const response = await authMemberAgent - .patch(`/projects/${memberProject.id}/data-tables/${dataStore.id}/rows`) + .patch(`/projects/${memberProject.id}/data-tables/${dataTable.id}/rows`) .send(payload) .expect(400); @@ -3547,7 +3547,7 @@ describe('PATCH /projects/:projectId/data-tables/:dataStoreId/rows', () => { }); test('should validate data types in data', async () => { - const dataStore = await createDataStore(memberProject, { + const dataTable = await createDataTable(memberProject, { columns: [ { name: 'name', type: 'string' }, { name: 'age', type: 'number' }, @@ -3561,7 +3561,7 @@ describe('PATCH /projects/:projectId/data-tables/:dataStoreId/rows', () => { }; const response = await authMemberAgent - .patch(`/projects/${memberProject.id}/data-tables/${dataStore.id}/rows`) + .patch(`/projects/${memberProject.id}/data-tables/${dataTable.id}/rows`) .send(payload) .expect(400); @@ -3569,7 +3569,7 @@ describe('PATCH /projects/:projectId/data-tables/:dataStoreId/rows', () => { }); test('should allow partial updates', async () => { - const dataStore = await createDataStore(memberProject, { + const dataTable = await createDataTable(memberProject, { columns: [ { name: 'name', type: 'string' }, { name: 'age', type: 'number' }, @@ -3584,12 +3584,12 @@ describe('PATCH /projects/:projectId/data-tables/:dataStoreId/rows', () => { }; await authMemberAgent - .patch(`/projects/${memberProject.id}/data-tables/${dataStore.id}/rows`) + .patch(`/projects/${memberProject.id}/data-tables/${dataTable.id}/rows`) .send(payload) .expect(200); const readResponse = await authMemberAgent - .get(`/projects/${memberProject.id}/data-tables/${dataStore.id}/rows`) + .get(`/projects/${memberProject.id}/data-tables/${dataTable.id}/rows`) .expect(200); expect(readResponse.body.data.count).toBe(1); @@ -3601,7 +3601,7 @@ describe('PATCH /projects/:projectId/data-tables/:dataStoreId/rows', () => { }); test('should handle date values in updates', async () => { - const dataStore = await createDataStore(memberProject, { + const dataTable = await createDataTable(memberProject, { columns: [ { name: 'name', type: 'string' }, { name: 'birthdate', type: 'date' }, @@ -3615,12 +3615,12 @@ describe('PATCH /projects/:projectId/data-tables/:dataStoreId/rows', () => { }; await authMemberAgent - .patch(`/projects/${memberProject.id}/data-tables/${dataStore.id}/rows`) + .patch(`/projects/${memberProject.id}/data-tables/${dataTable.id}/rows`) .send(payload) .expect(200); const readResponse = await authMemberAgent - .get(`/projects/${memberProject.id}/data-tables/${dataStore.id}/rows`) + .get(`/projects/${memberProject.id}/data-tables/${dataTable.id}/rows`) .expect(200); expect(readResponse.body.data.count).toBe(1); @@ -3631,7 +3631,7 @@ describe('PATCH /projects/:projectId/data-tables/:dataStoreId/rows', () => { }); test('should return updated data if returnData is set', async () => { - const dataStore = await createDataStore(memberProject, { + const dataTable = await createDataTable(memberProject, { columns: [ { name: 'name', type: 'string' }, { name: 'age', type: 'number' }, @@ -3651,7 +3651,7 @@ describe('PATCH /projects/:projectId/data-tables/:dataStoreId/rows', () => { }; const result = await authMemberAgent - .patch(`/projects/${memberProject.id}/data-tables/${dataStore.id}/rows`) + .patch(`/projects/${memberProject.id}/data-tables/${dataTable.id}/rows`) .send(payload) .expect(200); @@ -3676,7 +3676,7 @@ describe('PATCH /projects/:projectId/data-tables/:dataStoreId/rows', () => { test.each(['like', 'ilike'])( 'should auto-wrap %s filters if no wildcard is present', async (condition) => { - const dataStore = await createDataStore(memberProject, { + const dataTable = await createDataTable(memberProject, { columns: [ { name: 'name', @@ -3693,7 +3693,7 @@ describe('PATCH /projects/:projectId/data-tables/:dataStoreId/rows', () => { }; const result = await authMemberAgent - .patch(`/projects/${memberProject.id}/data-tables/${dataStore.id}/rows`) + .patch(`/projects/${memberProject.id}/data-tables/${dataTable.id}/rows`) .send(payload) .expect(200); diff --git a/packages/cli/src/modules/data-table/__tests__/data-store.service.integration.test.ts b/packages/cli/src/modules/data-table/__tests__/data-store.service.integration.test.ts index 4f6962cf922..bb0776dcde6 100644 --- a/packages/cli/src/modules/data-table/__tests__/data-store.service.integration.test.ts +++ b/packages/cli/src/modules/data-table/__tests__/data-store.service.integration.test.ts @@ -1,25 +1,25 @@ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ -import type { AddDataStoreColumnDto, CreateDataStoreColumnDto } from '@n8n/api-types'; +import type { AddDataTableColumnDto, CreateDataTableColumnDto } from '@n8n/api-types'; import { createTeamProject, testDb, testModules } from '@n8n/backend-test-utils'; import type { Project } from '@n8n/db'; import { Container } from '@n8n/di'; -import type { DataStoreRow } from 'n8n-workflow'; +import type { DataTableRow } from 'n8n-workflow'; -import { DataStoreRowsRepository } from '../data-store-rows.repository'; -import { DataStoreRepository } from '../data-store.repository'; -import { DataStoreService } from '../data-store.service'; -import { mockDataStoreSizeValidator } from './test-helpers'; -import { DataStoreColumnNameConflictError } from '../errors/data-store-column-name-conflict.error'; -import { DataStoreColumnNotFoundError } from '../errors/data-store-column-not-found.error'; -import { DataStoreNameConflictError } from '../errors/data-store-name-conflict.error'; -import { DataStoreNotFoundError } from '../errors/data-store-not-found.error'; -import { DataStoreValidationError } from '../errors/data-store-validation.error'; +import { DataTableRowsRepository } from '../data-table-rows.repository'; +import { DataTableRepository } from '../data-table.repository'; +import { DataTableService } from '../data-table.service'; +import { mockDataTableSizeValidator } from './test-helpers'; +import { DataTableColumnNameConflictError } from '../errors/data-table-column-name-conflict.error'; +import { DataTableColumnNotFoundError } from '../errors/data-table-column-not-found.error'; +import { DataTableNameConflictError } from '../errors/data-table-name-conflict.error'; +import { DataTableNotFoundError } from '../errors/data-table-not-found.error'; +import { DataTableValidationError } from '../errors/data-table-validation.error'; import { toTableName } from '../utils/sql-utils'; beforeAll(async () => { await testModules.loadModules(['data-table']); await testDb.init(); - mockDataStoreSizeValidator(); + mockDataTableSizeValidator(); }); beforeEach(async () => { @@ -30,15 +30,15 @@ afterAll(async () => { await testDb.terminate(); }); -describe('dataStore', () => { - let dataStoreService: DataStoreService; - let dataStoreRepository: DataStoreRepository; - let dataStoreRowsRepository: DataStoreRowsRepository; +describe('dataTable', () => { + let dataTableService: DataTableService; + let dataTableRepository: DataTableRepository; + let dataTableRowsRepository: DataTableRowsRepository; beforeAll(() => { - dataStoreService = Container.get(DataStoreService); - dataStoreRepository = Container.get(DataStoreRepository); - dataStoreRowsRepository = Container.get(DataStoreRowsRepository); + dataTableService = Container.get(DataTableService); + dataTableRepository = Container.get(DataTableRepository); + dataTableRowsRepository = Container.get(DataTableRowsRepository); }); let project1: Project; @@ -50,17 +50,17 @@ describe('dataStore', () => { }); afterEach(async () => { - await dataStoreService.deleteDataStoreAll(); + await dataTableService.deleteDataTableAll(); }); - describe('createDataStore', () => { + describe('createDataTable', () => { it('should create a columns table and a user table if columns are provided', async () => { - const { id: dataTableId } = await dataStoreService.createDataStore(project1.id, { - name: 'dataStoreWithColumns', + const { id: dataTableId } = await dataTableService.createDataTable(project1.id, { + name: 'dataTableWithColumns', columns: [{ name: 'foo', type: 'string' }], }); - await expect(dataStoreService.getColumns(dataTableId, project1.id)).resolves.toEqual([ + await expect(dataTableService.getColumns(dataTableId, project1.id)).resolves.toEqual([ { name: 'foo', type: 'string', @@ -74,7 +74,7 @@ describe('dataStore', () => { // Select the column from user table to check for its existence const userTableName = toTableName(dataTableId); - const rows = await dataStoreRepository.manager + const rows = await dataTableRepository.manager .createQueryBuilder() .select('foo') .from(userTableName, userTableName) @@ -85,19 +85,19 @@ describe('dataStore', () => { }); it('should create a user table and a columns table even if columns are not provided', async () => { - const name = 'dataStore'; + const name = 'dataTable'; // ACT - const result = await dataStoreService.createDataStore(project1.id, { + const result = await dataTableService.createDataTable(project1.id, { name, columns: [], }); - const { id: dataStoreId } = result; + const { id: dataTableId } = result; - await expect(dataStoreService.getColumns(dataStoreId, project1.id)).resolves.toEqual([]); + await expect(dataTableService.getColumns(dataTableId, project1.id)).resolves.toEqual([]); - const userTableName = toTableName(dataStoreId); - const queryRunner = dataStoreRepository.manager.connection.createQueryRunner(); + const userTableName = toTableName(dataTableId); + const queryRunner = dataTableRepository.manager.connection.createQueryRunner(); try { const table = await queryRunner.getTable(userTableName); const columnNames = table?.columns.map((col) => col.name); @@ -109,28 +109,28 @@ describe('dataStore', () => { }); it('should succeed even if the name exists in a different project', async () => { - const name = 'dataStore'; + const name = 'dataTable'; - await dataStoreService.createDataStore(project2.id, { + await dataTableService.createDataTable(project2.id, { name, columns: [], }); // ACT - const { id: dataStoreId } = await dataStoreService.createDataStore(project1.id, { + const { id: dataTableId } = await dataTableService.createDataTable(project1.id, { name, columns: [], }); - const created = await dataStoreRepository.findOneBy({ name, projectId: project1.id }); - expect(created?.id).toBe(dataStoreId); + const created = await dataTableRepository.findOneBy({ name, projectId: project1.id }); + expect(created?.id).toBe(dataTableId); }); - it('should populate the project relation when creating a data store', async () => { - const name = 'dataStore'; + it('should populate the project relation when creating a data table', async () => { + const name = 'dataTable'; // ACT - const { project } = await dataStoreService.createDataStore(project1.id, { + const { project } = await dataTableService.createDataTable(project1.id, { name, columns: [], }); @@ -141,30 +141,30 @@ describe('dataStore', () => { }); it('should return an error if name/project combination already exists', async () => { - const name = 'dataStore'; + const name = 'dataTable'; // ARRANGE - await dataStoreService.createDataStore(project1.id, { + await dataTableService.createDataTable(project1.id, { name, columns: [], }); // ACT - const result = dataStoreService.createDataStore(project1.id, { + const result = dataTableService.createDataTable(project1.id, { name, columns: [], }); // ASSERT - await expect(result).rejects.toThrow(DataStoreNameConflictError); + await expect(result).rejects.toThrow(DataTableNameConflictError); }); }); - describe('updateDataStore', () => { + describe('updateDataTable', () => { it('should succeed when renaming to an available name', async () => { // ARRANGE - const { id: dataStoreId, updatedAt } = await dataStoreService.createDataStore(project1.id, { - name: 'myDataStore1', + const { id: dataTableId, updatedAt } = await dataTableService.createDataTable(project1.id, { + name: 'myDataTable1', columns: [], }); @@ -172,68 +172,68 @@ describe('dataStore', () => { // Wait to get second difference await new Promise((resolve) => setTimeout(resolve, 1001)); - const result = await dataStoreService.updateDataStore(dataStoreId, project1.id, { + const result = await dataTableService.updateDataTable(dataTableId, project1.id, { name: 'aNewName', }); // ASSERT expect(result).toEqual(true); - const updated = await dataStoreRepository.findOneBy({ id: dataStoreId }); + const updated = await dataTableRepository.findOneBy({ id: dataTableId }); expect(updated?.name).toBe('aNewName'); expect(updated?.updatedAt.getTime()).toBeGreaterThan(updatedAt.getTime()); }); - it('should fail when renaming a non-existent data store', async () => { + it('should fail when renaming a non-existent data table', async () => { // ACT - const result = dataStoreService.updateDataStore('this is not an id', project1.id, { + const result = dataTableService.updateDataTable('this is not an id', project1.id, { name: 'aNewName', }); // ASSERT - await expect(result).rejects.toThrow(DataStoreNotFoundError); + await expect(result).rejects.toThrow(DataTableNotFoundError); }); it('should fail when renaming to a taken name', async () => { // ARRANGE - const name = 'myDataStoreOld'; - await dataStoreService.createDataStore(project1.id, { + const name = 'myDataTableOld'; + await dataTableService.createDataTable(project1.id, { name, columns: [], }); - const { id: dataStoreNewId } = await dataStoreService.createDataStore(project1.id, { - name: 'myDataStoreNew', + const { id: dataTableNewId } = await dataTableService.createDataTable(project1.id, { + name: 'myDataTableNew', columns: [], }); // ACT - const result = dataStoreService.updateDataStore(dataStoreNewId, project1.id, { name }); + const result = dataTableService.updateDataTable(dataTableNewId, project1.id, { name }); // ASSERT - await expect(result).rejects.toThrow(DataStoreNameConflictError); + await expect(result).rejects.toThrow(DataTableNameConflictError); }); }); - describe('deleteDataStore', () => { + describe('deleteDataTable', () => { it('should succeed with deleting a store', async () => { // ARRANGE - const { id: dataStoreId } = await dataStoreService.createDataStore(project1.id, { - name: 'myDataStore1', + const { id: dataTableId } = await dataTableService.createDataTable(project1.id, { + name: 'myDataTable1', columns: [], }); // ACT - const result = await dataStoreService.deleteDataStore(dataStoreId, project1.id); - const userTableName = toTableName(dataStoreId); + const result = await dataTableService.deleteDataTable(dataTableId, project1.id); + const userTableName = toTableName(dataTableId); // ASSERT expect(result).toEqual(true); - const deletedDatastore = await dataStoreRepository.findOneBy({ id: dataStoreId }); + const deletedDatastore = await dataTableRepository.findOneBy({ id: dataTableId }); expect(deletedDatastore).toBeNull(); - const queryUserTable = dataStoreRepository.manager + const queryUserTable = dataTableRepository.manager .createQueryBuilder() .select() .from(userTableName, userTableName) @@ -243,23 +243,23 @@ describe('dataStore', () => { it('should fail when deleting a non-existent id', async () => { // ACT - const result = dataStoreService.deleteDataStore('this is not an id', project1.id); + const result = dataTableService.deleteDataTable('this is not an id', project1.id); // ASSERT - await expect(result).rejects.toThrow(DataStoreNotFoundError); + await expect(result).rejects.toThrow(DataTableNotFoundError); }); }); describe('addColumn', () => { it('should succeed with adding columns to a non-empty table as well as to a user table', async () => { - const existingColumns: CreateDataStoreColumnDto[] = [{ name: 'myColumn0', type: 'string' }]; + const existingColumns: CreateDataTableColumnDto[] = [{ name: 'myColumn0', type: 'string' }]; - const { id: dataTableId } = await dataStoreService.createDataStore(project1.id, { - name: 'dataStoreWithColumns', + const { id: dataTableId } = await dataTableService.createDataTable(project1.id, { + name: 'dataTableWithColumns', columns: existingColumns, }); - const columns: AddDataStoreColumnDto[] = [ + const columns: AddDataTableColumnDto[] = [ { name: 'myColumn1', type: 'string' }, { name: 'myColumn2', type: 'number' }, { name: 'myColumn3', type: 'number' }, @@ -267,11 +267,11 @@ describe('dataStore', () => { ]; for (const column of columns) { // ACT - const result = await dataStoreService.addColumn(dataTableId, project1.id, column); + const result = await dataTableService.addColumn(dataTableId, project1.id, column); // ASSERT expect(result).toMatchObject(column); } - const columnResult = await dataStoreService.getColumns(dataTableId, project1.id); + const columnResult = await dataTableService.getColumns(dataTableId, project1.id); expect(columnResult).toEqual([ expect.objectContaining({ index: 0, @@ -306,7 +306,7 @@ describe('dataStore', () => { ]); const userTableName = toTableName(dataTableId); - const queryRunner = dataStoreRepository.manager.connection.createQueryRunner(); + const queryRunner = dataTableRepository.manager.connection.createQueryRunner(); try { const table = await queryRunner.getTable(userTableName); const columnNames = table?.columns.map((col) => col.name); @@ -329,22 +329,22 @@ describe('dataStore', () => { }); it('should succeed with adding columns to an empty table', async () => { - const { id: dataTableId } = await dataStoreService.createDataStore(project1.id, { - name: 'dataStore', + const { id: dataTableId } = await dataTableService.createDataTable(project1.id, { + name: 'dataTable', columns: [], }); - const columns: AddDataStoreColumnDto[] = [ + const columns: AddDataTableColumnDto[] = [ { name: 'myColumn0', type: 'string' }, { name: 'myColumn1', type: 'number' }, ]; for (const column of columns) { // ACT - const result = await dataStoreService.addColumn(dataTableId, project1.id, column); + const result = await dataTableService.addColumn(dataTableId, project1.id, column); // ASSERT expect(result).toMatchObject(column); } - const columnResult = await dataStoreService.getColumns(dataTableId, project1.id); + const columnResult = await dataTableService.getColumns(dataTableId, project1.id); expect(columnResult.length).toBe(2); expect(columnResult).toEqual([ @@ -363,7 +363,7 @@ describe('dataStore', () => { ]); const userTableName = toTableName(dataTableId); - const queryRunner = dataStoreRepository.manager.connection.createQueryRunner(); + const queryRunner = dataTableRepository.manager.connection.createQueryRunner(); try { const table = await queryRunner.getTable(userTableName); const columnNames = table?.columns.map((col) => col.name); @@ -378,8 +378,8 @@ describe('dataStore', () => { it('should fail with adding two columns of the same name', async () => { // ARRANGE - const { id: dataStoreId } = await dataStoreService.createDataStore(project1.id, { - name: 'myDataStore', + const { id: dataTableId } = await dataTableService.createDataTable(project1.id, { + name: 'myDataTable', columns: [ { name: 'myColumn1', @@ -389,38 +389,38 @@ describe('dataStore', () => { }); // ACT - const result = dataStoreService.addColumn(dataStoreId, project1.id, { + const result = dataTableService.addColumn(dataTableId, project1.id, { name: 'myColumn1', type: 'number', }); // ASSERT - await expect(result).rejects.toThrow(DataStoreColumnNameConflictError); + await expect(result).rejects.toThrow(DataTableColumnNameConflictError); }); it('should fail with adding column of non-existent table', async () => { // ACT - const result = dataStoreService.addColumn('this is not an id', project1.id, { + const result = dataTableService.addColumn('this is not an id', project1.id, { name: 'myColumn1', type: 'number', }); // ASSERT - await expect(result).rejects.toThrow(DataStoreNotFoundError); + await expect(result).rejects.toThrow(DataTableNotFoundError); }); it('should succeed with adding column to table that already has rows and set null values for existing rows', async () => { // ARRANGE - const { id: dataStoreId } = await dataStoreService.createDataStore(project1.id, { - name: 'dataStore', + const { id: dataTableId } = await dataTableService.createDataTable(project1.id, { + name: 'dataTable', columns: [ { name: 'name', type: 'string' }, { name: 'age', type: 'number' }, ], }); - const results = await dataStoreService.insertRows( - dataStoreId, + const results = await dataTableService.insertRows( + dataTableId, project1.id, [ { name: 'Alice', age: 30 }, @@ -443,7 +443,7 @@ describe('dataStore', () => { ]); // ACT - const newColumn = await dataStoreService.addColumn(dataStoreId, project1.id, { + const newColumn = await dataTableService.addColumn(dataTableId, project1.id, { name: 'email', type: 'string', }); @@ -452,12 +452,12 @@ describe('dataStore', () => { expect(newColumn).toMatchObject({ name: 'email', type: 'string' }); // Verify the column was added to the metadata - const columns = await dataStoreService.getColumns(dataStoreId, project1.id); + const columns = await dataTableService.getColumns(dataTableId, project1.id); expect(columns).toHaveLength(3); expect(columns.map((c) => c.name)).toEqual(['name', 'age', 'email']); // Verify existing rows now have null values for the new column - const updatedData = await dataStoreService.getManyRowsAndCount(dataStoreId, project1.id, {}); + const updatedData = await dataTableService.getManyRowsAndCount(dataTableId, project1.id, {}); expect(updatedData.count).toBe(3); expect(updatedData.data).toEqual([ expect.objectContaining({ @@ -481,8 +481,8 @@ describe('dataStore', () => { ]); // Verify we can insert new rows with the new column - const newRow = await dataStoreService.insertRows( - dataStoreId, + const newRow = await dataTableService.insertRows( + dataTableId, project1.id, [{ name: 'David', age: 28, email: 'david@example.com' }], 'id', @@ -493,7 +493,7 @@ describe('dataStore', () => { }, ]); - const finalData = await dataStoreService.getManyRowsAndCount(dataStoreId, project1.id, {}); + const finalData = await dataTableService.getManyRowsAndCount(dataTableId, project1.id, {}); expect(finalData.count).toBe(4); expect(finalData.data).toEqual([ expect.objectContaining({ @@ -527,27 +527,27 @@ describe('dataStore', () => { describe('deleteColumn', () => { it('should succeed with deleting a column', async () => { // ARRANGE - const { id: dataTableId } = await dataStoreService.createDataStore(project1.id, { - name: 'dataStore', + const { id: dataTableId } = await dataTableService.createDataTable(project1.id, { + name: 'dataTable', columns: [], }); - const c1 = await dataStoreService.addColumn(dataTableId, project1.id, { + const c1 = await dataTableService.addColumn(dataTableId, project1.id, { name: 'myColumn1', type: 'string', }); - const c2 = await dataStoreService.addColumn(dataTableId, project1.id, { + const c2 = await dataTableService.addColumn(dataTableId, project1.id, { name: 'myColumn2', type: 'number', }); // ACT - const result = await dataStoreService.deleteColumn(dataTableId, project1.id, c1.id); + const result = await dataTableService.deleteColumn(dataTableId, project1.id, c1.id); // ASSERT expect(result).toEqual(true); - const columns = await dataStoreService.getColumns(dataTableId, project1.id); + const columns = await dataTableService.getColumns(dataTableId, project1.id); expect(columns).toEqual([ { index: 0, @@ -563,8 +563,8 @@ describe('dataStore', () => { it('should fail when deleting unknown column', async () => { // ARRANGE - const { id: dataStoreId } = await dataStoreService.createDataStore(project1.id, { - name: 'dataStore', + const { id: dataTableId } = await dataTableService.createDataTable(project1.id, { + name: 'dataTable', columns: [ { name: 'myColumn1', @@ -574,57 +574,57 @@ describe('dataStore', () => { }); // ACT - const result = dataStoreService.deleteColumn(dataStoreId, project1.id, 'thisIsNotAnId'); + const result = dataTableService.deleteColumn(dataTableId, project1.id, 'thisIsNotAnId'); // ASSERT - await expect(result).rejects.toThrow(DataStoreColumnNotFoundError); + await expect(result).rejects.toThrow(DataTableColumnNotFoundError); }); it('should fail when deleting column from unknown table', async () => { // ARRANGE - const { id: dataStoreId } = await dataStoreService.createDataStore(project1.id, { - name: 'dataStore', + const { id: dataTableId } = await dataTableService.createDataTable(project1.id, { + name: 'dataTable', columns: [], }); - const c1 = await dataStoreService.addColumn(dataStoreId, project1.id, { + const c1 = await dataTableService.addColumn(dataTableId, project1.id, { name: 'myColumn1', type: 'string', }); // ACT - const result = dataStoreService.deleteColumn('this is not an id', project1.id, c1.id); + const result = dataTableService.deleteColumn('this is not an id', project1.id, c1.id); // ASSERT - await expect(result).rejects.toThrow(DataStoreNotFoundError); + await expect(result).rejects.toThrow(DataTableNotFoundError); }); }); describe('moveColumn', () => { it('should succeed with moving a column', async () => { // ARRANGE - const { id: dataTableId } = await dataStoreService.createDataStore(project1.id, { - name: 'dataStore', + const { id: dataTableId } = await dataTableService.createDataTable(project1.id, { + name: 'dataTable', columns: [], }); - const c1 = await dataStoreService.addColumn(dataTableId, project1.id, { + const c1 = await dataTableService.addColumn(dataTableId, project1.id, { name: 'myColumn1', type: 'string', }); - const c2 = await dataStoreService.addColumn(dataTableId, project1.id, { + const c2 = await dataTableService.addColumn(dataTableId, project1.id, { name: 'myColumn2', type: 'number', }); // ACT - const result = await dataStoreService.moveColumn(dataTableId, project1.id, c2.id, { + const result = await dataTableService.moveColumn(dataTableId, project1.id, c2.id, { targetIndex: 0, }); // ASSERT expect(result).toEqual(true); - const columns = await dataStoreService.getColumns(dataTableId, project1.id); + const columns = await dataTableService.getColumns(dataTableId, project1.id); expect(columns).toMatchObject([ { index: 0, @@ -647,20 +647,20 @@ describe('dataStore', () => { describe('getManyAndCount', () => { it('correctly joins columns', async () => { // ARRANGE - const columns: CreateDataStoreColumnDto[] = [ + const columns: CreateDataTableColumnDto[] = [ { name: 'myColumn1', type: 'string' }, { name: 'myColumn2', type: 'number' }, { name: 'myColumn3', type: 'number' }, { name: 'myColumn4', type: 'date' }, ]; - const { id: dataStoreId } = await dataStoreService.createDataStore(project1.id, { - name: 'myDataStore', + const { id: dataTableId } = await dataTableService.createDataTable(project1.id, { + name: 'myDataTable', columns, }); // ACT - const result = await dataStoreService.getManyAndCount({ - filter: { projectId: project1.id, id: dataStoreId }, + const result = await dataTableService.getManyAndCount({ + filter: { projectId: project1.id, id: dataTableId }, }); // ASSERT @@ -715,27 +715,27 @@ describe('dataStore', () => { describe('sorts as expected', () => { it('sorts by name', async () => { // ARRANGE - await dataStoreService.createDataStore(project1.id, { + await dataTableService.createDataTable(project1.id, { name: 'ds2', columns: [], }); - await dataStoreService.createDataStore(project1.id, { + await dataTableService.createDataTable(project1.id, { name: 'ds1', columns: [], }); - await dataStoreService.createDataStore(project1.id, { + await dataTableService.createDataTable(project1.id, { name: 'ds3', columns: [], }); // ACT - const nameAsc = await dataStoreService.getManyAndCount({ + const nameAsc = await dataTableService.getManyAndCount({ filter: { projectId: project1.id }, sortBy: 'name:asc', }); - const nameDesc = await dataStoreService.getManyAndCount({ + const nameDesc = await dataTableService.getManyAndCount({ filter: { projectId: project1.id }, sortBy: 'name:desc', }); @@ -747,31 +747,31 @@ describe('dataStore', () => { it('sorts by createdAt', async () => { // ARRANGE - await dataStoreService.createDataStore(project1.id, { + await dataTableService.createDataTable(project1.id, { name: 'ds0', columns: [], }); // Wait to get seconds difference await new Promise((resolve) => setTimeout(resolve, 1001)); - await dataStoreService.createDataStore(project1.id, { + await dataTableService.createDataTable(project1.id, { name: 'ds1', columns: [], }); // Wait to get seconds difference await new Promise((resolve) => setTimeout(resolve, 1001)); - await dataStoreService.createDataStore(project1.id, { + await dataTableService.createDataTable(project1.id, { name: 'ds2', columns: [], }); // ACT - const createdAsc = await dataStoreService.getManyAndCount({ + const createdAsc = await dataTableService.getManyAndCount({ filter: { projectId: project1.id }, sortBy: 'createdAt:asc', }); - const createdDesc = await dataStoreService.getManyAndCount({ + const createdDesc = await dataTableService.getManyAndCount({ filter: { projectId: project1.id }, sortBy: 'createdAt:desc', }); @@ -783,29 +783,29 @@ describe('dataStore', () => { it('sorts by updatedAt', async () => { // ARRANGE - const { id: dataStoreId } = await dataStoreService.createDataStore(project1.id, { + const { id: dataTableId } = await dataTableService.createDataTable(project1.id, { name: 'ds1', columns: [], }); // Wait to get seconds difference await new Promise((resolve) => setTimeout(resolve, 1001)); - await dataStoreService.createDataStore(project1.id, { + await dataTableService.createDataTable(project1.id, { name: 'ds2', columns: [], }); // Wait to get seconds difference await new Promise((resolve) => setTimeout(resolve, 1001)); - await dataStoreService.updateDataStore(dataStoreId, project1.id, { name: 'ds1Updated' }); + await dataTableService.updateDataTable(dataTableId, project1.id, { name: 'ds1Updated' }); // ACT - const updatedAsc = await dataStoreService.getManyAndCount({ + const updatedAsc = await dataTableService.getManyAndCount({ filter: { projectId: project1.id }, sortBy: 'updatedAt:asc', }); - const updatedDesc = await dataStoreService.getManyAndCount({ + const updatedDesc = await dataTableService.getManyAndCount({ filter: { projectId: project1.id }, sortBy: 'updatedAt:desc', }); @@ -819,72 +819,72 @@ describe('dataStore', () => { describe('filters as expected', () => { it('filters by name', async () => { // ARRANGE - await dataStoreService.createDataStore(project1.id, { - name: 'dataStore1', + await dataTableService.createDataTable(project1.id, { + name: 'dataTable1', columns: [], }); - await dataStoreService.createDataStore(project1.id, { - name: 'dataStore2', + await dataTableService.createDataTable(project1.id, { + name: 'dataTable2', columns: [], }); - await dataStoreService.createDataStore(project1.id, { - name: 'dataStore3', + await dataTableService.createDataTable(project1.id, { + name: 'dataTable3', columns: [], }); // ACT - const filtered = await dataStoreService.getManyAndCount({ - filter: { projectId: project1.id, name: 'dataStore2' }, + const filtered = await dataTableService.getManyAndCount({ + filter: { projectId: project1.id, name: 'dataTable2' }, }); // ASSERT expect(filtered.count).toBe(1); - expect(filtered.data[0].name).toBe('dataStore2'); + expect(filtered.data[0].name).toBe('dataTable2'); }); it('filters by multiple names (AND)', async () => { // ARRANGE - await dataStoreService.createDataStore(project1.id, { - name: 'dataStore1', + await dataTableService.createDataTable(project1.id, { + name: 'dataTable1', columns: [], }); - await dataStoreService.createDataStore(project1.id, { - name: 'dataStore2', + await dataTableService.createDataTable(project1.id, { + name: 'dataTable2', columns: [], }); - await dataStoreService.createDataStore(project1.id, { - name: 'dataStore3', + await dataTableService.createDataTable(project1.id, { + name: 'dataTable3', columns: [], }); // ACT - const filtered = await dataStoreService.getManyAndCount({ - filter: { projectId: project1.id, name: ['Store3', 'data'] }, + const filtered = await dataTableService.getManyAndCount({ + filter: { projectId: project1.id, name: ['Table3', 'data'] }, }); // ASSERT expect(filtered.count).toBe(1); - expect(filtered.data.map((x) => x.name).sort()).toEqual(['dataStore3']); + expect(filtered.data.map((x) => x.name).sort()).toEqual(['dataTable3']); }); it('filters by id', async () => { // ARRANGE - const { id: ds1Id } = await dataStoreService.createDataStore(project1.id, { - name: 'dataStore1', + const { id: ds1Id } = await dataTableService.createDataTable(project1.id, { + name: 'dataTable1', columns: [], }); - await dataStoreService.createDataStore(project1.id, { - name: 'dataStore2', + await dataTableService.createDataTable(project1.id, { + name: 'dataTable2', columns: [], }); // ACT - const filtered = await dataStoreService.getManyAndCount({ + const filtered = await dataTableService.getManyAndCount({ filter: { projectId: project1.id, id: ds1Id }, }); @@ -895,21 +895,21 @@ describe('dataStore', () => { it('filters by multiple ids (OR)', async () => { // ARRANGE - const { id: ds1Id } = await dataStoreService.createDataStore(project1.id, { - name: 'dataStore1', + const { id: ds1Id } = await dataTableService.createDataTable(project1.id, { + name: 'dataTable1', columns: [], }); - const { id: ds2Id } = await dataStoreService.createDataStore(project1.id, { - name: 'dataStore2', + const { id: ds2Id } = await dataTableService.createDataTable(project1.id, { + name: 'dataTable2', columns: [], }); - await dataStoreService.createDataStore(project1.id, { - name: 'dataStore3', + await dataTableService.createDataTable(project1.id, { + name: 'dataTable3', columns: [], }); // ACT - const filtered = await dataStoreService.getManyAndCount({ + const filtered = await dataTableService.getManyAndCount({ filter: { projectId: project1.id, id: [ds1Id, ds2Id] }, }); @@ -920,29 +920,29 @@ describe('dataStore', () => { it('filters by projectId', async () => { // ARRANGE - await dataStoreService.createDataStore(project1.id, { - name: 'dataStore1', + await dataTableService.createDataTable(project1.id, { + name: 'dataTable1', columns: [], }); - await dataStoreService.createDataStore(project2.id, { - name: 'dataStore2', + await dataTableService.createDataTable(project2.id, { + name: 'dataTable2', columns: [], }); // ACT - const filtered1 = await dataStoreService.getManyAndCount({ + const filtered1 = await dataTableService.getManyAndCount({ filter: { projectId: project1.id }, }); - const filtered2 = await dataStoreService.getManyAndCount({ + const filtered2 = await dataTableService.getManyAndCount({ filter: { projectId: project2.id }, }); // ASSERT expect(filtered1.count).toBe(1); - expect(filtered1.data[0].name).toBe('dataStore1'); + expect(filtered1.data[0].name).toBe('dataTable1'); expect(filtered2.count).toBe(1); - expect(filtered2.data[0].name).toBe('dataStore2'); + expect(filtered2.data[0].name).toBe('dataTable2'); }); }); }); @@ -950,8 +950,8 @@ describe('dataStore', () => { describe('insertRows', () => { it('inserts rows into an existing table', async () => { // ARRANGE - const { id: dataStoreId } = await dataStoreService.createDataStore(project1.id, { - name: 'dataStore', + const { id: dataTableId } = await dataTableService.createDataTable(project1.id, { + name: 'dataTable', columns: [ { name: 'c1', type: 'number' }, { name: 'c2', type: 'boolean' }, @@ -972,13 +972,13 @@ describe('dataStore', () => { c4: 'iso 8601 date strings are okay too', }, ]; - const result = await dataStoreService.insertRows(dataStoreId, project1.id, rows, 'id'); + const result = await dataTableService.insertRows(dataTableId, project1.id, rows, 'id'); // ASSERT expect(result).toEqual([{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }]); - const { count, data } = await dataStoreService.getManyRowsAndCount( - dataStoreId, + const { count, data } = await dataTableService.getManyRowsAndCount( + dataTableId, project1.id, {}, ); @@ -986,7 +986,7 @@ describe('dataStore', () => { const expected = rows.map( (row, i) => - expect.objectContaining({ + expect.objectContaining({ ...row, id: i + 1, c3: typeof row.c3 === 'string' ? new Date(row.c3) : row.c3, @@ -998,8 +998,8 @@ describe('dataStore', () => { it('inserts a row even if it matches with the existing one', async () => { // ARRANGE - const { id: dataStoreId } = await dataStoreService.createDataStore(project1.id, { - name: 'myDataStore', + const { id: dataTableId } = await dataTableService.createDataTable(project1.id, { + name: 'myDataTable', columns: [ { name: 'c1', type: 'number' }, { name: 'c2', type: 'string' }, @@ -1007,8 +1007,8 @@ describe('dataStore', () => { }); // Insert initial row - const initial = await dataStoreService.insertRows( - dataStoreId, + const initial = await dataTableService.insertRows( + dataTableId, project1.id, [{ c1: 1, c2: 'foo' }], 'id', @@ -1016,8 +1016,8 @@ describe('dataStore', () => { expect(initial).toEqual([{ id: 1 }]); // Attempt to insert a row with the same primary key - const result = await dataStoreService.insertRows( - dataStoreId, + const result = await dataTableService.insertRows( + dataTableId, project1.id, [{ c1: 1, c2: 'foo' }], 'id', @@ -1026,7 +1026,7 @@ describe('dataStore', () => { // ASSERT expect(result).toEqual([{ id: 2 }]); - const { count, data } = await dataStoreRowsRepository.getManyAndCount(dataStoreId, {}); + const { count, data } = await dataTableRowsRepository.getManyAndCount(dataTableId, {}); expect(count).toEqual(2); expect(data).toEqual([ @@ -1045,8 +1045,8 @@ describe('dataStore', () => { it('return correct IDs even after deletions', async () => { // ARRANGE - const { id: dataStoreId } = await dataStoreService.createDataStore(project1.id, { - name: 'myDataStore', + const { id: dataTableId } = await dataTableService.createDataTable(project1.id, { + name: 'myDataTable', columns: [ { name: 'c1', type: 'number' }, { name: 'c2', type: 'string' }, @@ -1054,8 +1054,8 @@ describe('dataStore', () => { }); // Insert initial row - const ids = await dataStoreService.insertRows( - dataStoreId, + const ids = await dataTableService.insertRows( + dataTableId, project1.id, [ { c1: 1, c2: 'foo' }, @@ -1065,7 +1065,7 @@ describe('dataStore', () => { ); expect(ids).toEqual([{ id: 1 }, { id: 2 }]); - await dataStoreService.deleteRows(dataStoreId, project1.id, { + await dataTableService.deleteRows(dataTableId, project1.id, { filter: { type: 'and', filters: [{ columnName: 'id', condition: 'eq', value: ids[0].id }], @@ -1073,8 +1073,8 @@ describe('dataStore', () => { }); // Insert a new row - const result = await dataStoreService.insertRows( - dataStoreId, + const result = await dataTableService.insertRows( + dataTableId, project1.id, [ { c1: 1, c2: 'baz' }, @@ -1086,7 +1086,7 @@ describe('dataStore', () => { // ASSERT expect(result).toEqual([{ id: 3 }, { id: 4 }]); - const { count, data } = await dataStoreRowsRepository.getManyAndCount(dataStoreId, {}); + const { count, data } = await dataTableRowsRepository.getManyAndCount(dataTableId, {}); expect(count).toEqual(3); expect(data).toEqual([ @@ -1110,8 +1110,8 @@ describe('dataStore', () => { it('return full inserted data if returnData is set', async () => { // ARRANGE - const { id: dataStoreId } = await dataStoreService.createDataStore(project1.id, { - name: 'myDataStore', + const { id: dataTableId } = await dataTableService.createDataTable(project1.id, { + name: 'myDataTable', columns: [ { name: 'c1', type: 'number' }, { name: 'c2', type: 'string' }, @@ -1123,8 +1123,8 @@ describe('dataStore', () => { const now = new Date(); // Insert initial row - const ids = await dataStoreService.insertRows( - dataStoreId, + const ids = await dataTableService.insertRows( + dataTableId, project1.id, [ { c1: 1, c2: 'foo', c3: true, c4: now }, @@ -1166,8 +1166,8 @@ describe('dataStore', () => { it('inserts in correct order even with different column order', async () => { // ARRANGE - const { id: dataStoreId } = await dataStoreService.createDataStore(project1.id, { - name: 'myDataStore', + const { id: dataTableId } = await dataTableService.createDataTable(project1.id, { + name: 'myDataTable', columns: [ { name: 'c4', type: 'date' }, { name: 'c3', type: 'boolean' }, @@ -1179,8 +1179,8 @@ describe('dataStore', () => { const now = new Date(); // Insert initial row - const ids = await dataStoreService.insertRows( - dataStoreId, + const ids = await dataTableService.insertRows( + dataTableId, project1.id, [ { c1: 1, c2: 'foo', c3: true, c4: now }, @@ -1222,8 +1222,8 @@ describe('dataStore', () => { it('rejects a mismatched row with unknown column', async () => { // ARRANGE - const { id: dataStoreId } = await dataStoreService.createDataStore(project1.id, { - name: 'dataStore', + const { id: dataTableId } = await dataTableService.createDataTable(project1.id, { + name: 'dataTable', columns: [ { name: 'c1', type: 'number' }, { name: 'c2', type: 'boolean' }, @@ -1233,8 +1233,8 @@ describe('dataStore', () => { }); // ACT - const result = dataStoreService.insertRows( - dataStoreId, + const result = dataTableService.insertRows( + dataTableId, project1.id, [ { c1: 3, c2: true, c3: new Date(), c4: 'hello?' }, @@ -1245,14 +1245,14 @@ describe('dataStore', () => { // ASSERT await expect(result).rejects.toThrow( - new DataStoreValidationError("unknown column name 'cWrong'"), + new DataTableValidationError("unknown column name 'cWrong'"), ); }); it('inserts rows with partial data (some columns missing)', async () => { // ARRANGE - const { id: dataStoreId } = await dataStoreService.createDataStore(project1.id, { - name: 'dataStore', + const { id: dataTableId } = await dataTableService.createDataTable(project1.id, { + name: 'dataTable', columns: [ { name: 'name', type: 'string' }, { name: 'age', type: 'number' }, @@ -1262,8 +1262,8 @@ describe('dataStore', () => { }); // ACT - await dataStoreService.insertRows( - dataStoreId, + await dataTableService.insertRows( + dataTableId, project1.id, [ { name: 'Mary', age: 20, email: 'mary@example.com', active: true }, // full row @@ -1274,8 +1274,8 @@ describe('dataStore', () => { 'id', ); - const { count, data } = await dataStoreService.getManyRowsAndCount( - dataStoreId, + const { count, data } = await dataTableService.getManyRowsAndCount( + dataTableId, project1.id, {}, ); @@ -1310,8 +1310,8 @@ describe('dataStore', () => { it('rejects a mismatched row with replaced column', async () => { // ARRANGE - const { id: dataStoreId } = await dataStoreService.createDataStore(project1.id, { - name: 'dataStore', + const { id: dataTableId } = await dataTableService.createDataTable(project1.id, { + name: 'dataTable', columns: [ { name: 'c1', type: 'number' }, { name: 'c2', type: 'boolean' }, @@ -1321,8 +1321,8 @@ describe('dataStore', () => { }); // ACT - const result = dataStoreService.insertRows( - dataStoreId, + const result = dataTableService.insertRows( + dataTableId, project1.id, [ { c1: 3, c2: true, c3: new Date(), c4: 'hello?' }, @@ -1333,36 +1333,36 @@ describe('dataStore', () => { // ASSERT await expect(result).rejects.toThrow( - new DataStoreValidationError("unknown column name 'cWrong'"), + new DataTableValidationError("unknown column name 'cWrong'"), ); }); it('rejects an invalid date string to date column', async () => { // ARRANGE - const { id: dataStoreId } = await dataStoreService.createDataStore(project1.id, { - name: 'dataStore', + const { id: dataTableId } = await dataTableService.createDataTable(project1.id, { + name: 'dataTable', columns: [{ name: 'c1', type: 'date' }], }); // ACT - const result = dataStoreService.insertRows( - dataStoreId, + const result = dataTableService.insertRows( + dataTableId, project1.id, [{ c1: '2025-99-15T09:48:14.259Z' }], 'id', ); // ASSERT - await expect(result).rejects.toThrow(DataStoreValidationError); + await expect(result).rejects.toThrow(DataTableValidationError); await expect(result).rejects.toThrow( "value '2025-99-15T09:48:14.259Z' does not match column type 'date'", ); }); - it('rejects unknown data store id', async () => { + it('rejects unknown data table id', async () => { // ARRANGE - await dataStoreService.createDataStore(project1.id, { - name: 'dataStore', + await dataTableService.createDataTable(project1.id, { + name: 'dataTable', columns: [ { name: 'c1', type: 'number' }, { name: 'c2', type: 'boolean' }, @@ -1372,7 +1372,7 @@ describe('dataStore', () => { }); // ACT - const result = dataStoreService.insertRows( + const result = dataTableService.insertRows( 'this is not an id', project1.id, [ @@ -1383,27 +1383,27 @@ describe('dataStore', () => { ); // ASSERT - await expect(result).rejects.toThrow(DataStoreNotFoundError); + await expect(result).rejects.toThrow(DataTableNotFoundError); }); it('fails on type mismatch', async () => { // ARRANGE - const { id: dataStoreId } = await dataStoreService.createDataStore(project1.id, { - name: 'dataStore', + const { id: dataTableId } = await dataTableService.createDataTable(project1.id, { + name: 'dataTable', columns: [{ name: 'c1', type: 'number' }], }); // ACT const wrongValue = new Date().toISOString(); - const result = dataStoreService.insertRows( - dataStoreId, + const result = dataTableService.insertRows( + dataTableId, project1.id, [{ c1: 3 }, { c1: wrongValue }], 'id', ); // ASSERT - await expect(result).rejects.toThrow(DataStoreValidationError); + await expect(result).rejects.toThrow(DataTableValidationError); await expect(result).rejects.toThrow( `value '${wrongValue}' does not match column type 'number'`, ); @@ -1411,20 +1411,20 @@ describe('dataStore', () => { it('inserts rows into an existing table with no columns', async () => { // ARRANGE - const { id: dataStoreId } = await dataStoreService.createDataStore(project1.id, { - name: 'dataStore', + const { id: dataTableId } = await dataTableService.createDataTable(project1.id, { + name: 'dataTable', columns: [], }); // ACT const rows = [{}, {}, {}]; - const result = await dataStoreService.insertRows(dataStoreId, project1.id, rows, 'id'); + const result = await dataTableService.insertRows(dataTableId, project1.id, rows, 'id'); // ASSERT expect(result).toEqual([{ id: 1 }, { id: 2 }, { id: 3 }]); - const { count, data } = await dataStoreService.getManyRowsAndCount( - dataStoreId, + const { count, data } = await dataTableService.getManyRowsAndCount( + dataTableId, project1.id, {}, ); @@ -1450,20 +1450,20 @@ describe('dataStore', () => { describe('bulk', () => { it('handles single empty row correctly in bulk mode', async () => { // ARRANGE - const { id: dataStoreId } = await dataStoreService.createDataStore(project1.id, { - name: 'dataStore', + const { id: dataTableId } = await dataTableService.createDataTable(project1.id, { + name: 'dataTable', columns: [], }); // ACT const rows = [{}]; - const result = await dataStoreService.insertRows(dataStoreId, project1.id, rows); + const result = await dataTableService.insertRows(dataTableId, project1.id, rows); // ASSERT expect(result).toEqual({ success: true, insertedRows: 1 }); - const { count, data } = await dataStoreService.getManyRowsAndCount( - dataStoreId, + const { count, data } = await dataTableService.getManyRowsAndCount( + dataTableId, project1.id, {}, ); @@ -1474,19 +1474,19 @@ describe('dataStore', () => { it('bulk insert should work with multiple empty rows', async () => { // ARRANGE - const { id: dataStoreId } = await dataStoreService.createDataStore(project1.id, { - name: 'dataStore', + const { id: dataTableId } = await dataTableService.createDataTable(project1.id, { + name: 'dataTable', columns: [], }); // ACT - const result = await dataStoreService.insertRows(dataStoreId, project1.id, [{}, {}]); + const result = await dataTableService.insertRows(dataTableId, project1.id, [{}, {}]); // ASSERT expect(result).toEqual({ success: true, insertedRows: 2 }); - const { count, data } = await dataStoreService.getManyRowsAndCount( - dataStoreId, + const { count, data } = await dataTableService.getManyRowsAndCount( + dataTableId, project1.id, {}, ); @@ -1500,8 +1500,8 @@ describe('dataStore', () => { it('handles multi-batch bulk correctly in bulk mode', async () => { // ARRANGE - const { id: dataStoreId } = await dataStoreService.createDataStore(project1.id, { - name: 'dataStore', + const { id: dataTableId } = await dataTableService.createDataTable(project1.id, { + name: 'dataTable', columns: [ { name: 'c1', type: 'number' }, { name: 'c2', type: 'boolean' }, @@ -1515,13 +1515,13 @@ describe('dataStore', () => { c2: index % 2 === 0, c3: `index ${index}`, })); - const result = await dataStoreService.insertRows(dataStoreId, project1.id, rows); + const result = await dataTableService.insertRows(dataTableId, project1.id, rows); // ASSERT expect(result).toEqual({ success: true, insertedRows: rows.length }); - const { count, data } = await dataStoreService.getManyRowsAndCount( - dataStoreId, + const { count, data } = await dataTableService.getManyRowsAndCount( + dataTableId, project1.id, {}, ); @@ -1529,7 +1529,7 @@ describe('dataStore', () => { const expected = rows.map( (row, i) => - expect.objectContaining({ + expect.objectContaining({ ...row, id: i + 1, }) as jest.AsymmetricMatcher, @@ -1542,8 +1542,8 @@ describe('dataStore', () => { describe('upsertRow', () => { it('should update a row if filter matches', async () => { // ARRANGE - const { id: dataStoreId } = await dataStoreService.createDataStore(project1.id, { - name: 'dataStore', + const { id: dataTableId } = await dataTableService.createDataTable(project1.id, { + name: 'dataTable', columns: [ { name: 'pid', type: 'string' }, { name: 'name', type: 'string' }, @@ -1551,8 +1551,8 @@ describe('dataStore', () => { ], }); - const ids = await dataStoreService.insertRows( - dataStoreId, + const ids = await dataTableService.insertRows( + dataTableId, project1.id, [ { pid: '1995-111a', name: 'Alice', age: 30 }, @@ -1565,7 +1565,7 @@ describe('dataStore', () => { expect(ids).toEqual([{ id: 1 }, { id: 2 }, { id: 3 }]); // ACT - const result = await dataStoreService.upsertRow(dataStoreId, project1.id, { + const result = await dataTableService.upsertRow(dataTableId, project1.id, { filter: { type: 'and', filters: [{ columnName: 'pid', value: '1995-111a', condition: 'eq' }], @@ -1576,8 +1576,8 @@ describe('dataStore', () => { // ASSERT expect(result).toBe(true); - const { count, data } = await dataStoreService.getManyRowsAndCount( - dataStoreId, + const { count, data } = await dataTableService.getManyRowsAndCount( + dataTableId, project1.id, {}, ); @@ -1606,8 +1606,8 @@ describe('dataStore', () => { it('should insert a row if filter does not match', async () => { // ARRANGE - const { id: dataStoreId } = await dataStoreService.createDataStore(project1.id, { - name: 'dataStore', + const { id: dataTableId } = await dataTableService.createDataTable(project1.id, { + name: 'dataTable', columns: [ { name: 'pid', type: 'string' }, { name: 'name', type: 'string' }, @@ -1616,8 +1616,8 @@ describe('dataStore', () => { }); // Insert initial row - const ids = await dataStoreService.insertRows( - dataStoreId, + const ids = await dataTableService.insertRows( + dataTableId, project1.id, [{ pid: '1995-111a', name: 'Alice', age: 30 }], 'id', @@ -1625,7 +1625,7 @@ describe('dataStore', () => { expect(ids).toEqual([{ id: 1 }]); // ACT - const result = await dataStoreService.upsertRow(dataStoreId, project1.id, { + const result = await dataTableService.upsertRow(dataTableId, project1.id, { filter: { type: 'and', filters: [{ columnName: 'pid', value: '1995-222b', condition: 'eq' }], @@ -1636,8 +1636,8 @@ describe('dataStore', () => { // ASSERT expect(result).toBe(true); - const { count, data } = await dataStoreService.getManyRowsAndCount( - dataStoreId, + const { count, data } = await dataTableService.getManyRowsAndCount( + dataTableId, project1.id, {}, ); @@ -1661,8 +1661,8 @@ describe('dataStore', () => { it('should return full inserted row if returnData is set', async () => { // ARRANGE - const { id: dataStoreId } = await dataStoreService.createDataStore(project1.id, { - name: 'dataStore', + const { id: dataTableId } = await dataTableService.createDataTable(project1.id, { + name: 'dataTable', columns: [ { name: 'fullName', type: 'string' }, { name: 'age', type: 'number' }, @@ -1671,8 +1671,8 @@ describe('dataStore', () => { }); // Insert initial row - const ids = await dataStoreService.insertRows( - dataStoreId, + const ids = await dataTableService.insertRows( + dataTableId, project1.id, [{ fullName: 'Alice Cooper', age: 30, birthday: new Date('1995-01-01') }], 'id', @@ -1680,8 +1680,8 @@ describe('dataStore', () => { expect(ids).toEqual([{ id: 1 }]); // ACT - const result = await dataStoreService.upsertRow( - dataStoreId, + const result = await dataTableService.upsertRow( + dataTableId, project1.id, { filter: { @@ -1708,8 +1708,8 @@ describe('dataStore', () => { it('should return full updated row if returnData is set', async () => { // ARRANGE - const { id: dataStoreId } = await dataStoreService.createDataStore(project1.id, { - name: 'dataStore', + const { id: dataTableId } = await dataTableService.createDataTable(project1.id, { + name: 'dataTable', columns: [ { name: 'fullName', type: 'string' }, { name: 'age', type: 'number' }, @@ -1718,8 +1718,8 @@ describe('dataStore', () => { }); // Insert initial row - const ids = await dataStoreService.insertRows( - dataStoreId, + const ids = await dataTableService.insertRows( + dataTableId, project1.id, [{ fullName: 'Alice Cooper', age: 30, birthday: new Date('1995-01-01') }], 'id', @@ -1727,8 +1727,8 @@ describe('dataStore', () => { expect(ids).toEqual([{ id: 1 }]); // ACT - const result = await dataStoreService.upsertRow( - dataStoreId, + const result = await dataTableService.upsertRow( + dataTableId, project1.id, { filter: { @@ -1755,24 +1755,24 @@ describe('dataStore', () => { it('should simulate update without persisting when dryRun is true', async () => { // ARRANGE - const { id: dataStoreId } = await dataStoreService.createDataStore(project1.id, { - name: 'dataStore', + const { id: dataTableId } = await dataTableService.createDataTable(project1.id, { + name: 'dataTable', columns: [ { name: 'name', type: 'string' }, { name: 'age', type: 'number' }, ], }); - await dataStoreService.insertRows( - dataStoreId, + await dataTableService.insertRows( + dataTableId, project1.id, [{ name: 'Alice', age: 30 }], 'id', ); // ACT - const result = await dataStoreService.upsertRow( - dataStoreId, + const result = await dataTableService.upsertRow( + dataTableId, project1.id, { filter: { @@ -1800,7 +1800,7 @@ describe('dataStore', () => { ]); // Verify data was NOT persisted - const { data } = await dataStoreService.getManyRowsAndCount(dataStoreId, project1.id, {}); + const { data } = await dataTableService.getManyRowsAndCount(dataTableId, project1.id, {}); expect(data).toEqual([ expect.objectContaining({ @@ -1812,24 +1812,24 @@ describe('dataStore', () => { it('should simulate insert without persisting when dryRun is true and no match', async () => { // ARRANGE - const { id: dataStoreId } = await dataStoreService.createDataStore(project1.id, { - name: 'dataStore', + const { id: dataTableId } = await dataTableService.createDataTable(project1.id, { + name: 'dataTable', columns: [ { name: 'name', type: 'string' }, { name: 'age', type: 'number' }, ], }); - await dataStoreService.insertRows( - dataStoreId, + await dataTableService.insertRows( + dataTableId, project1.id, [{ name: 'Alice', age: 30 }], 'id', ); // ACT - const result = await dataStoreService.upsertRow( - dataStoreId, + const result = await dataTableService.upsertRow( + dataTableId, project1.id, { filter: { @@ -1859,8 +1859,8 @@ describe('dataStore', () => { ]); // Verify data was NOT persisted - const { count, data } = await dataStoreService.getManyRowsAndCount( - dataStoreId, + const { count, data } = await dataTableService.getManyRowsAndCount( + dataTableId, project1.id, {}, ); @@ -1878,19 +1878,19 @@ describe('dataStore', () => { describe('deleteRows', () => { it('should delete rows by filter condition', async () => { // ARRANGE - const dataStore = await dataStoreService.createDataStore(project1.id, { - name: 'dataStore', + const dataTable = await dataTableService.createDataTable(project1.id, { + name: 'dataTable', columns: [ { name: 'name', type: 'string' }, { name: 'age', type: 'number' }, { name: 'active', type: 'boolean' }, ], }); - const { id: dataStoreId } = dataStore; + const { id: dataTableId } = dataTable; // Insert test rows - await dataStoreService.insertRows( - dataStoreId, + await dataTableService.insertRows( + dataTableId, project1.id, [ { name: 'Alice', age: 30, active: true }, @@ -1901,7 +1901,7 @@ describe('dataStore', () => { ); // ACT - const result = await dataStoreService.deleteRows(dataStoreId, project1.id, { + const result = await dataTableService.deleteRows(dataTableId, project1.id, { filter: { type: 'and', filters: [{ columnName: 'active', condition: 'eq', value: true }], @@ -1911,7 +1911,7 @@ describe('dataStore', () => { // ASSERT expect(result).toBe(true); - const rows = await dataStoreService.getManyRowsAndCount(dataStoreId, project1.id, {}); + const rows = await dataTableService.getManyRowsAndCount(dataTableId, project1.id, {}); expect(rows.count).toBe(1); expect(rows.data).toEqual([ expect.objectContaining({ @@ -1924,18 +1924,18 @@ describe('dataStore', () => { it('should delete rows by ids', async () => { // ARRANGE - const dataStore = await dataStoreService.createDataStore(project1.id, { - name: 'dataStore', + const dataTable = await dataTableService.createDataTable(project1.id, { + name: 'dataTable', columns: [ { name: 'name', type: 'string' }, { name: 'age', type: 'number' }, ], }); - const { id: dataStoreId } = dataStore; + const { id: dataTableId } = dataTable; // Insert test rows - const insertedIds = await dataStoreService.insertRows( - dataStoreId, + const insertedIds = await dataTableService.insertRows( + dataTableId, project1.id, [ { name: 'Alice', age: 30 }, @@ -1950,7 +1950,7 @@ describe('dataStore', () => { .map(({ id }) => ({ columnName: 'id', condition: 'eq' as const, value: id })); // ACT - const result = await dataStoreService.deleteRows(dataStoreId, project1.id, { + const result = await dataTableService.deleteRows(dataTableId, project1.id, { filter: { type: 'or', filters, @@ -1960,7 +1960,7 @@ describe('dataStore', () => { // ASSERT expect(result).toBe(true); - const rows = await dataStoreService.getManyRowsAndCount(dataStoreId, project1.id, {}); + const rows = await dataTableService.getManyRowsAndCount(dataTableId, project1.id, {}); expect(rows.count).toBe(1); expect(rows.data).toEqual([ expect.objectContaining({ @@ -1971,17 +1971,17 @@ describe('dataStore', () => { it('should delete only one row with OR filter', async () => { // ARRANGE - const dataStore = await dataStoreService.createDataStore(project1.id, { - name: 'dataStore', + const dataTable = await dataTableService.createDataTable(project1.id, { + name: 'dataTable', columns: [ { name: 'name', type: 'string' }, { name: 'age', type: 'number' }, ], }); - const { id: dataStoreId } = dataStore; + const { id: dataTableId } = dataTable; - await dataStoreService.insertRows( - dataStoreId, + await dataTableService.insertRows( + dataTableId, project1.id, [ { name: 'Alice', age: 30 }, @@ -1991,7 +1991,7 @@ describe('dataStore', () => { ); // ACT - const result = await dataStoreService.deleteRows(dataStoreId, project1.id, { + const result = await dataTableService.deleteRows(dataTableId, project1.id, { filter: { type: 'or', filters: [{ columnName: 'name', condition: 'eq', value: 'Alice' }], @@ -2001,7 +2001,7 @@ describe('dataStore', () => { // ASSERT expect(result).toBe(true); - const rows = await dataStoreService.getManyRowsAndCount(dataStoreId, project1.id, {}); + const rows = await dataTableService.getManyRowsAndCount(dataTableId, project1.id, {}); expect(rows.count).toBe(1); expect(rows.data).toEqual([ expect.objectContaining({ @@ -2013,25 +2013,25 @@ describe('dataStore', () => { it('return full deleted data if returnData is set', async () => { // ARRANGE - const dataStore = await dataStoreService.createDataStore(project1.id, { - name: 'dataStore', + const dataTable = await dataTableService.createDataTable(project1.id, { + name: 'dataTable', columns: [ { name: 'name', type: 'string' }, { name: 'age', type: 'number' }, ], }); - const { id: dataStoreId } = dataStore; + const { id: dataTableId } = dataTable; - await dataStoreService.insertRows( - dataStoreId, + await dataTableService.insertRows( + dataTableId, project1.id, [{ name: 'Alice', age: 30 }], 'id', ); // ACT - const result = await dataStoreService.deleteRows( - dataStoreId, + const result = await dataTableService.deleteRows( + dataTableId, project1.id, { filter: { @@ -2056,17 +2056,17 @@ describe('dataStore', () => { it('should simulate deletion with dryRun=true without actually deleting rows', async () => { // ARRANGE - const dataStore = await dataStoreService.createDataStore(project1.id, { - name: 'dataStore', + const dataTable = await dataTableService.createDataTable(project1.id, { + name: 'dataTable', columns: [ { name: 'name', type: 'string' }, { name: 'age', type: 'number' }, ], }); - const { id: dataStoreId } = dataStore; + const { id: dataTableId } = dataTable; - await dataStoreService.insertRows( - dataStoreId, + await dataTableService.insertRows( + dataTableId, project1.id, [ { name: 'Alice', age: 30 }, @@ -2076,8 +2076,8 @@ describe('dataStore', () => { ); // ACT - const result = await dataStoreService.deleteRows( - dataStoreId, + const result = await dataTableService.deleteRows( + dataTableId, project1.id, { filter: { @@ -2110,7 +2110,7 @@ describe('dataStore', () => { ]); // Should not actually delete the rows - const rows = await dataStoreService.getManyRowsAndCount(dataStoreId, project1.id, {}); + const rows = await dataTableService.getManyRowsAndCount(dataTableId, project1.id, {}); expect(rows.count).toBe(2); expect(rows.data).toEqual([ expect.objectContaining({ name: 'Alice', age: 30 }), @@ -2120,17 +2120,17 @@ describe('dataStore', () => { it('should return data with dryRun=true even if returnData=false', async () => { // ARRANGE - const dataStore = await dataStoreService.createDataStore(project1.id, { - name: 'dataStore', + const dataTable = await dataTableService.createDataTable(project1.id, { + name: 'dataTable', columns: [ { name: 'name', type: 'string' }, { name: 'age', type: 'number' }, ], }); - const { id: dataStoreId } = dataStore; + const { id: dataTableId } = dataTable; - await dataStoreService.insertRows( - dataStoreId, + await dataTableService.insertRows( + dataTableId, project1.id, [ { name: 'Alice', age: 30 }, @@ -2140,8 +2140,8 @@ describe('dataStore', () => { ); // ACT - const result = await dataStoreService.deleteRows( - dataStoreId, + const result = await dataTableService.deleteRows( + dataTableId, project1.id, { filter: { @@ -2174,7 +2174,7 @@ describe('dataStore', () => { ]); // Should not actually delete the rows - const rows = await dataStoreService.getManyRowsAndCount(dataStoreId, project1.id, {}); + const rows = await dataTableService.getManyRowsAndCount(dataTableId, project1.id, {}); expect(rows.count).toBe(2); expect(rows.data).toEqual([ expect.objectContaining({ name: 'Alice', age: 30 }), @@ -2184,25 +2184,25 @@ describe('dataStore', () => { it('should return empty array with dryRun=true when no rows match filter', async () => { // ARRANGE - const dataStore = await dataStoreService.createDataStore(project1.id, { - name: 'dataStore', + const dataTable = await dataTableService.createDataTable(project1.id, { + name: 'dataTable', columns: [ { name: 'name', type: 'string' }, { name: 'age', type: 'number' }, ], }); - const { id: dataStoreId } = dataStore; + const { id: dataTableId } = dataTable; - await dataStoreService.insertRows( - dataStoreId, + await dataTableService.insertRows( + dataTableId, project1.id, [{ name: 'Alice', age: 30 }], 'id', ); // ACT - const result = await dataStoreService.deleteRows( - dataStoreId, + const result = await dataTableService.deleteRows( + dataTableId, project1.id, { filter: { @@ -2217,34 +2217,34 @@ describe('dataStore', () => { // ASSERT expect(result).toEqual([]); - const rows = await dataStoreService.getManyRowsAndCount(dataStoreId, project1.id, {}); + const rows = await dataTableService.getManyRowsAndCount(dataTableId, project1.id, {}); expect(rows.count).toBe(1); }); - it('fails when trying to delete from non-existent data store', async () => { + it('fails when trying to delete from non-existent data table', async () => { // ACT & ASSERT - const result = dataStoreService.deleteRows('non-existent-id', project1.id, { + const result = dataTableService.deleteRows('non-existent-id', project1.id, { filter: { type: 'and', filters: [{ columnName: 'id', condition: 'eq', value: 1 }], }, }); - await expect(result).rejects.toThrow(DataStoreNotFoundError); + await expect(result).rejects.toThrow(DataTableNotFoundError); }); it('should return true and do nothing when no rows match the filter', async () => { // ARRANGE - const dataStore = await dataStoreService.createDataStore(project1.id, { - name: 'dataStore', + const dataTable = await dataTableService.createDataTable(project1.id, { + name: 'dataTable', columns: [{ name: 'name', type: 'string' }], }); - const { id: dataStoreId } = dataStore; + const { id: dataTableId } = dataTable; - await dataStoreService.insertRows(dataStoreId, project1.id, [{ name: 'Alice' }]); + await dataTableService.insertRows(dataTableId, project1.id, [{ name: 'Alice' }]); // ACT - const result = await dataStoreService.deleteRows(dataStoreId, project1.id, { + const result = await dataTableService.deleteRows(dataTableId, project1.id, { filter: { type: 'and', filters: [{ columnName: 'name', condition: 'eq', value: 'Charlie' }], @@ -2254,32 +2254,32 @@ describe('dataStore', () => { // ASSERT expect(result).toBe(true); - const { count } = await dataStoreService.getManyRowsAndCount(dataStoreId, project1.id, {}); + const { count } = await dataTableService.getManyRowsAndCount(dataTableId, project1.id, {}); expect(count).toBe(1); }); it('should not delete all rows when no filter is provided', async () => { // ARRANGE - const dataStore = await dataStoreService.createDataStore(project1.id, { - name: 'dataStore', + const dataTable = await dataTableService.createDataTable(project1.id, { + name: 'dataTable', columns: [{ name: 'name', type: 'string' }], }); - const { id: dataStoreId } = dataStore; + const { id: dataTableId } = dataTable; - await dataStoreService.insertRows(dataStoreId, project1.id, [ + await dataTableService.insertRows(dataTableId, project1.id, [ { name: 'Alice' }, { name: 'Bob' }, { name: 'Charlie' }, ]); // ACT - const result = dataStoreService.deleteRows(dataStoreId, project1.id, { + const result = dataTableService.deleteRows(dataTableId, project1.id, { // eslint-disable-next-line @typescript-eslint/no-explicit-any filter: undefined as any, }); await expect(result).rejects.toThrow( - new DataStoreValidationError( + new DataTableValidationError( 'Filter is required for delete operations to prevent accidental deletion of all data', ), ); @@ -2287,25 +2287,25 @@ describe('dataStore', () => { it('should not delete all rows when no filter is empty', async () => { // ARRANGE - const dataStore = await dataStoreService.createDataStore(project1.id, { - name: 'dataStore', + const dataTable = await dataTableService.createDataTable(project1.id, { + name: 'dataTable', columns: [{ name: 'name', type: 'string' }], }); - const { id: dataStoreId } = dataStore; + const { id: dataTableId } = dataTable; - await dataStoreService.insertRows(dataStoreId, project1.id, [ + await dataTableService.insertRows(dataTableId, project1.id, [ { name: 'Alice' }, { name: 'Bob' }, { name: 'Charlie' }, ]); // ACT - const result = dataStoreService.deleteRows(dataStoreId, project1.id, { + const result = dataTableService.deleteRows(dataTableId, project1.id, { filter: { type: 'and', filters: [] }, }); await expect(result).rejects.toThrow( - new DataStoreValidationError( + new DataTableValidationError( 'Filter is required for delete operations to prevent accidental deletion of all data', ), ); @@ -2313,17 +2313,17 @@ describe('dataStore', () => { it('should delete empty rows containing only system columns', async () => { // ARRANGE - const { id: dataStoreId } = await dataStoreService.createDataStore(project1.id, { - name: 'dataStore', + const { id: dataTableId } = await dataTableService.createDataTable(project1.id, { + name: 'dataTable', columns: [], }); // Insert empty rows - await dataStoreService.insertRows(dataStoreId, project1.id, [{}, {}]); + await dataTableService.insertRows(dataTableId, project1.id, [{}, {}]); // Verify rows exist with only system columns - const { count: initialCount, data: initialData } = await dataStoreService.getManyRowsAndCount( - dataStoreId, + const { count: initialCount, data: initialData } = await dataTableService.getManyRowsAndCount( + dataTableId, project1.id, {}, ); @@ -2334,7 +2334,7 @@ describe('dataStore', () => { ]); // ACT - const result = await dataStoreService.deleteRows(dataStoreId, project1.id, { + const result = await dataTableService.deleteRows(dataTableId, project1.id, { filter: { type: 'and', filters: [{ columnName: 'id', condition: 'eq', value: 1 }], @@ -2345,8 +2345,8 @@ describe('dataStore', () => { expect(result).toEqual(true); // Verify only one row remains - const { count: finalCount, data: finalData } = await dataStoreService.getManyRowsAndCount( - dataStoreId, + const { count: finalCount, data: finalData } = await dataTableService.getManyRowsAndCount( + dataTableId, project1.id, {}, ); @@ -2360,8 +2360,8 @@ describe('dataStore', () => { describe('updateRows', () => { it('should update an existing row with matching filter', async () => { // ARRANGE - const { id: dataStoreId } = await dataStoreService.createDataStore(project1.id, { - name: 'dataStore', + const { id: dataTableId } = await dataTableService.createDataTable(project1.id, { + name: 'dataTable', columns: [ { name: 'name', type: 'string' }, { name: 'age', type: 'number' }, @@ -2370,13 +2370,13 @@ describe('dataStore', () => { ], }); - await dataStoreService.insertRows(dataStoreId, project1.id, [ + await dataTableService.insertRows(dataTableId, project1.id, [ { name: 'Alice', age: 30, active: true, birthday: new Date('1990-01-01') }, { name: 'Bob', age: 25, active: false, birthday: new Date('1995-01-01') }, ]); // ACT - const result = await dataStoreService.updateRows(dataStoreId, project1.id, { + const result = await dataTableService.updateRows(dataTableId, project1.id, { filter: { type: 'and', filters: [{ columnName: 'name', condition: 'eq', value: 'Alice' }] }, data: { name: 'Alicia', age: 31, active: false, birthday: new Date('1990-01-02') }, }); @@ -2384,7 +2384,7 @@ describe('dataStore', () => { // ASSERT expect(result).toEqual(true); - const { data } = await dataStoreService.getManyRowsAndCount(dataStoreId, project1.id, {}); + const { data } = await dataTableService.getManyRowsAndCount(dataTableId, project1.id, {}); expect(data).toEqual( expect.arrayContaining([ expect.objectContaining({ @@ -2407,8 +2407,8 @@ describe('dataStore', () => { it('should be able to update by id', async () => { // ARRANGE - const { id: dataStoreId } = await dataStoreService.createDataStore(project1.id, { - name: 'dataStore', + const { id: dataTableId } = await dataTableService.createDataTable(project1.id, { + name: 'dataTable', columns: [ { name: 'name', type: 'string' }, { name: 'age', type: 'number' }, @@ -2416,7 +2416,7 @@ describe('dataStore', () => { ], }); - await dataStoreService.insertRows(dataStoreId, project1.id, [ + await dataTableService.insertRows(dataTableId, project1.id, [ { name: 'Alice', age: 30, @@ -2430,7 +2430,7 @@ describe('dataStore', () => { ]); // ACT - const result = await dataStoreService.updateRows(dataStoreId, project1.id, { + const result = await dataTableService.updateRows(dataTableId, project1.id, { filter: { type: 'and', filters: [{ columnName: 'id', condition: 'eq', value: 1 }] }, data: { name: 'Alicia', age: 31, active: false }, }); @@ -2438,7 +2438,7 @@ describe('dataStore', () => { // ASSERT expect(result).toEqual(true); - const { data } = await dataStoreService.getManyRowsAndCount(dataStoreId, project1.id, {}); + const { data } = await dataTableService.getManyRowsAndCount(dataTableId, project1.id, {}); expect(data).toEqual( expect.arrayContaining([ expect.objectContaining({ @@ -2459,8 +2459,8 @@ describe('dataStore', () => { it('should update the updatedAt', async () => { // ARRANGE - const { id: dataStoreId } = await dataStoreService.createDataStore(project1.id, { - name: 'dataStore', + const { id: dataTableId } = await dataTableService.createDataTable(project1.id, { + name: 'dataTable', columns: [ { name: 'name', type: 'string' }, { name: 'age', type: 'number' }, @@ -2468,12 +2468,12 @@ describe('dataStore', () => { ], }); - await dataStoreService.insertRows(dataStoreId, project1.id, [ + await dataTableService.insertRows(dataTableId, project1.id, [ { name: 'Alice', age: 30, active: true }, ]); - const { data: initialRows } = await dataStoreService.getManyRowsAndCount( - dataStoreId, + const { data: initialRows } = await dataTableService.getManyRowsAndCount( + dataTableId, project1.id, {}, ); @@ -2482,7 +2482,7 @@ describe('dataStore', () => { await new Promise((resolve) => setTimeout(resolve, 10)); // ACT - const result = await dataStoreService.updateRows(dataStoreId, project1.id, { + const result = await dataTableService.updateRows(dataTableId, project1.id, { filter: { type: 'and', filters: [{ columnName: 'name', condition: 'eq', value: 'Alice' }] }, data: { age: 31, active: false }, }); @@ -2490,8 +2490,8 @@ describe('dataStore', () => { // ASSERT expect(result).toBe(true); - const { data: updatedRows } = await dataStoreService.getManyRowsAndCount( - dataStoreId, + const { data: updatedRows } = await dataTableService.getManyRowsAndCount( + dataTableId, project1.id, {}, ); @@ -2509,8 +2509,8 @@ describe('dataStore', () => { it('should be able to update by string column', async () => { // ARRANGE - const { id: dataStoreId } = await dataStoreService.createDataStore(project1.id, { - name: 'dataStore', + const { id: dataTableId } = await dataTableService.createDataTable(project1.id, { + name: 'dataTable', columns: [ { name: 'name', type: 'string' }, { name: 'age', type: 'number' }, @@ -2519,13 +2519,13 @@ describe('dataStore', () => { ], }); - await dataStoreService.insertRows(dataStoreId, project1.id, [ + await dataTableService.insertRows(dataTableId, project1.id, [ { name: 'Alice', age: 30, active: true, birthday: new Date('1990-01-01') }, { name: 'Bob', age: 25, active: false, birthday: new Date('1995-01-01') }, ]); // ACT - const result = await dataStoreService.updateRows(dataStoreId, project1.id, { + const result = await dataTableService.updateRows(dataTableId, project1.id, { filter: { type: 'and', filters: [{ columnName: 'name', condition: 'eq', value: 'Alice' }] }, data: { name: 'Alicia' }, }); @@ -2533,7 +2533,7 @@ describe('dataStore', () => { // ASSERT expect(result).toEqual(true); - const { data } = await dataStoreService.getManyRowsAndCount(dataStoreId, project1.id, {}); + const { data } = await dataTableService.getManyRowsAndCount(dataTableId, project1.id, {}); expect(data).toEqual( expect.arrayContaining([ expect.objectContaining({ @@ -2556,8 +2556,8 @@ describe('dataStore', () => { it('should be able to update by number column', async () => { // ARRANGE - const { id: dataStoreId } = await dataStoreService.createDataStore(project1.id, { - name: 'dataStore', + const { id: dataTableId } = await dataTableService.createDataTable(project1.id, { + name: 'dataTable', columns: [ { name: 'name', type: 'string' }, { name: 'age', type: 'number' }, @@ -2566,13 +2566,13 @@ describe('dataStore', () => { ], }); - await dataStoreService.insertRows(dataStoreId, project1.id, [ + await dataTableService.insertRows(dataTableId, project1.id, [ { name: 'Alice', age: 30, active: true, birthday: new Date('1990-01-01') }, { name: 'Bob', age: 25, active: false, birthday: new Date('1995-01-01') }, ]); // ACT - const result = await dataStoreService.updateRows(dataStoreId, project1.id, { + const result = await dataTableService.updateRows(dataTableId, project1.id, { filter: { type: 'and', filters: [{ columnName: 'age', condition: 'eq', value: 30 }] }, data: { age: 31 }, }); @@ -2580,7 +2580,7 @@ describe('dataStore', () => { // ASSERT expect(result).toEqual(true); - const { data } = await dataStoreService.getManyRowsAndCount(dataStoreId, project1.id, {}); + const { data } = await dataTableService.getManyRowsAndCount(dataTableId, project1.id, {}); expect(data).toEqual( expect.arrayContaining([ expect.objectContaining({ @@ -2603,15 +2603,15 @@ describe('dataStore', () => { it('should be able to update by numeric string', async () => { // ARRANGE - const { id: dataStoreId } = await dataStoreService.createDataStore(project1.id, { - name: 'dataStore', + const { id: dataTableId } = await dataTableService.createDataTable(project1.id, { + name: 'dataTable', columns: [{ name: 'age', type: 'number' }], }); - await dataStoreService.insertRows(dataStoreId, project1.id, [{ age: 30 }]); + await dataTableService.insertRows(dataTableId, project1.id, [{ age: 30 }]); // ACT - const result = await dataStoreService.updateRows(dataStoreId, project1.id, { + const result = await dataTableService.updateRows(dataTableId, project1.id, { filter: { type: 'and', filters: [{ columnName: 'age', condition: 'eq', value: '30' }] }, data: { age: '31' }, }); @@ -2619,21 +2619,21 @@ describe('dataStore', () => { // ASSERT expect(result).toEqual(true); - const { data } = await dataStoreService.getManyRowsAndCount(dataStoreId, project1.id, {}); + const { data } = await dataTableService.getManyRowsAndCount(dataTableId, project1.id, {}); expect(data).toEqual([expect.objectContaining({ age: 31 })]); }); it('should throw on invalid numeric string', async () => { // ARRANGE - const { id: dataStoreId } = await dataStoreService.createDataStore(project1.id, { - name: 'dataStore', + const { id: dataTableId } = await dataTableService.createDataTable(project1.id, { + name: 'dataTable', columns: [{ name: 'age', type: 'number' }], }); - await dataStoreService.insertRows(dataStoreId, project1.id, [{ age: 30 }]); + await dataTableService.insertRows(dataTableId, project1.id, [{ age: 30 }]); // ACT - const result = dataStoreService.updateRows(dataStoreId, project1.id, { + const result = dataTableService.updateRows(dataTableId, project1.id, { filter: { type: 'and', filters: [{ columnName: 'age', condition: 'eq', value: '30dfddf' }], @@ -2642,14 +2642,14 @@ describe('dataStore', () => { }); // ASSERT - await expect(result).rejects.toThrow(DataStoreValidationError); + await expect(result).rejects.toThrow(DataTableValidationError); await expect(result).rejects.toThrow("value '30dfddf' does not match column type 'number'"); }); it('should be able to update by boolean column', async () => { // ARRANGE - const { id: dataStoreId } = await dataStoreService.createDataStore(project1.id, { - name: 'dataStore', + const { id: dataTableId } = await dataTableService.createDataTable(project1.id, { + name: 'dataTable', columns: [ { name: 'name', type: 'string' }, { name: 'age', type: 'number' }, @@ -2658,13 +2658,13 @@ describe('dataStore', () => { ], }); - await dataStoreService.insertRows(dataStoreId, project1.id, [ + await dataTableService.insertRows(dataTableId, project1.id, [ { name: 'Alice', age: 30, active: true, birthday: new Date('1990-01-01') }, { name: 'Bob', age: 25, active: false, birthday: new Date('1995-01-01') }, ]); // ACT - const result = await dataStoreService.updateRows(dataStoreId, project1.id, { + const result = await dataTableService.updateRows(dataTableId, project1.id, { filter: { type: 'and', filters: [{ columnName: 'active', condition: 'eq', value: true }] }, data: { active: false }, }); @@ -2672,7 +2672,7 @@ describe('dataStore', () => { // ASSERT expect(result).toEqual(true); - const { data } = await dataStoreService.getManyRowsAndCount(dataStoreId, project1.id, {}); + const { data } = await dataTableService.getManyRowsAndCount(dataTableId, project1.id, {}); expect(data).toEqual( expect.arrayContaining([ expect.objectContaining({ @@ -2698,8 +2698,8 @@ describe('dataStore', () => { const aliceBirthday = new Date('1990-01-02'); const bobBirthday = new Date('1995-01-01'); - const { id: dataStoreId } = await dataStoreService.createDataStore(project1.id, { - name: 'dataStore', + const { id: dataTableId } = await dataTableService.createDataTable(project1.id, { + name: 'dataTable', columns: [ { name: 'name', type: 'string' }, { name: 'age', type: 'number' }, @@ -2708,14 +2708,14 @@ describe('dataStore', () => { ], }); - await dataStoreService.insertRows(dataStoreId, project1.id, [ + await dataTableService.insertRows(dataTableId, project1.id, [ { name: 'Alice', age: 30, active: true, birthday: aliceBirthday }, { name: 'Bob', age: 25, active: false, birthday: bobBirthday }, ]); // ACT const newBirthday = new Date('1990-01-03'); - const result = await dataStoreService.updateRows(dataStoreId, project1.id, { + const result = await dataTableService.updateRows(dataTableId, project1.id, { filter: { type: 'and', filters: [{ columnName: 'birthday', condition: 'eq', value: aliceBirthday }], @@ -2726,7 +2726,7 @@ describe('dataStore', () => { // ASSERT expect(result).toEqual(true); - const { data } = await dataStoreService.getManyRowsAndCount(dataStoreId, project1.id, {}); + const { data } = await dataTableService.getManyRowsAndCount(dataTableId, project1.id, {}); expect(data).toEqual( expect.arrayContaining([ expect.objectContaining({ @@ -2749,8 +2749,8 @@ describe('dataStore', () => { it('should update row with multiple filter conditions', async () => { // ARRANGE - const { id: dataStoreId } = await dataStoreService.createDataStore(project1.id, { - name: 'dataStore', + const { id: dataTableId } = await dataTableService.createDataTable(project1.id, { + name: 'dataTable', columns: [ { name: 'name', type: 'string' }, { name: 'age', type: 'number' }, @@ -2758,14 +2758,14 @@ describe('dataStore', () => { ], }); - await dataStoreService.insertRows(dataStoreId, project1.id, [ + await dataTableService.insertRows(dataTableId, project1.id, [ { name: 'Alice', age: 30, department: 'Engineering' }, { name: 'Alice', age: 25, department: 'Marketing' }, { name: 'Bob', age: 30, department: 'Engineering' }, ]); // ACT - const result = await dataStoreService.updateRows(dataStoreId, project1.id, { + const result = await dataTableService.updateRows(dataTableId, project1.id, { filter: { type: 'and', filters: [ @@ -2779,7 +2779,7 @@ describe('dataStore', () => { // ASSERT expect(result).toEqual(true); - const { data } = await dataStoreService.getManyRowsAndCount(dataStoreId, project1.id, {}); + const { data } = await dataTableService.getManyRowsAndCount(dataTableId, project1.id, {}); expect(data).toEqual( expect.arrayContaining([ expect.objectContaining({ @@ -2806,18 +2806,18 @@ describe('dataStore', () => { it('should return true when no rows match the filter', async () => { // ARRANGE - const { id: dataStoreId } = await dataStoreService.createDataStore(project1.id, { - name: 'dataStore', + const { id: dataTableId } = await dataTableService.createDataTable(project1.id, { + name: 'dataTable', columns: [ { name: 'name', type: 'string' }, { name: 'age', type: 'number' }, ], }); - await dataStoreService.insertRows(dataStoreId, project1.id, [{ name: 'Alice', age: 30 }]); + await dataTableService.insertRows(dataTableId, project1.id, [{ name: 'Alice', age: 30 }]); // ACT - const result = await dataStoreService.updateRows(dataStoreId, project1.id, { + const result = await dataTableService.updateRows(dataTableId, project1.id, { filter: { type: 'and', filters: [{ columnName: 'name', condition: 'eq', value: 'Charlie' }], @@ -2828,7 +2828,7 @@ describe('dataStore', () => { // ASSERT expect(result).toEqual(true); - const { data } = await dataStoreService.getManyRowsAndCount(dataStoreId, project1.id, {}); + const { data } = await dataTableService.getManyRowsAndCount(dataTableId, project1.id, {}); expect(data).toEqual([ expect.objectContaining({ name: 'Alice', @@ -2839,28 +2839,28 @@ describe('dataStore', () => { it('should throw validation error when filters are empty', async () => { // ARRANGE - const { id: dataStoreId } = await dataStoreService.createDataStore(project1.id, { - name: 'dataStore', + const { id: dataTableId } = await dataTableService.createDataTable(project1.id, { + name: 'dataTable', columns: [ { name: 'name', type: 'string' }, { name: 'age', type: 'number' }, ], }); - await dataStoreService.insertRows(dataStoreId, project1.id, [{ name: 'Alice', age: 30 }]); + await dataTableService.insertRows(dataTableId, project1.id, [{ name: 'Alice', age: 30 }]); // ACT - const result = dataStoreService.updateRows(dataStoreId, project1.id, { + const result = dataTableService.updateRows(dataTableId, project1.id, { filter: { type: 'and', filters: [] }, data: { name: 'Alice', age: 31 }, }); // ASSERT await expect(result).rejects.toThrow( - new DataStoreValidationError('Filter must not be empty'), + new DataTableValidationError('Filter must not be empty'), ); - const { data } = await dataStoreService.getManyRowsAndCount(dataStoreId, project1.id, {}); + const { data } = await dataTableService.getManyRowsAndCount(dataTableId, project1.id, {}); expect(data).toEqual([ expect.objectContaining({ name: 'Alice', @@ -2871,28 +2871,28 @@ describe('dataStore', () => { it('should throw validation error when data is empty', async () => { // ARRANGE - const { id: dataStoreId } = await dataStoreService.createDataStore(project1.id, { - name: 'dataStore', + const { id: dataTableId } = await dataTableService.createDataTable(project1.id, { + name: 'dataTable', columns: [ { name: 'name', type: 'string' }, { name: 'age', type: 'number' }, ], }); - await dataStoreService.insertRows(dataStoreId, project1.id, [{ name: 'Alice', age: 30 }]); + await dataTableService.insertRows(dataTableId, project1.id, [{ name: 'Alice', age: 30 }]); // ACT - const result = dataStoreService.updateRows(dataStoreId, project1.id, { + const result = dataTableService.updateRows(dataTableId, project1.id, { filter: { type: 'and', filters: [{ columnName: 'name', condition: 'eq', value: 'Alice' }] }, data: {}, }); // ASSERT await expect(result).rejects.toThrow( - new DataStoreValidationError('Data columns must not be empty'), + new DataTableValidationError('Data columns must not be empty'), ); - const { data } = await dataStoreService.getManyRowsAndCount(dataStoreId, project1.id, {}); + const { data } = await dataTableService.getManyRowsAndCount(dataTableId, project1.id, {}); expect(data).toEqual([ expect.objectContaining({ name: 'Alice', @@ -2901,45 +2901,45 @@ describe('dataStore', () => { ]); }); - it('should fail when data store does not exist', async () => { + it('should fail when data table does not exist', async () => { // ACT & ASSERT - const result = dataStoreService.updateRows('non-existent-id', project1.id, { + const result = dataTableService.updateRows('non-existent-id', project1.id, { filter: { type: 'and', filters: [{ columnName: 'name', condition: 'eq', value: 'Alice' }] }, data: { age: 25 }, }); - await expect(result).rejects.toThrow(DataStoreNotFoundError); + await expect(result).rejects.toThrow(DataTableNotFoundError); }); it('should fail when data contains invalid column names', async () => { // ARRANGE - const { id: dataStoreId } = await dataStoreService.createDataStore(project1.id, { - name: 'dataStore', + const { id: dataTableId } = await dataTableService.createDataTable(project1.id, { + name: 'dataTable', columns: [{ name: 'name', type: 'string' }], }); - await dataStoreService.insertRows(dataStoreId, project1.id, [{ name: 'Alice' }]); + await dataTableService.insertRows(dataTableId, project1.id, [{ name: 'Alice' }]); // ACT & ASSERT - const result = dataStoreService.updateRows(dataStoreId, project1.id, { + const result = dataTableService.updateRows(dataTableId, project1.id, { filter: { type: 'and', filters: [{ columnName: 'name', condition: 'eq', value: 'Alice' }] }, data: { invalidColumn: 'value' }, }); - await expect(result).rejects.toThrow(DataStoreValidationError); + await expect(result).rejects.toThrow(DataTableValidationError); }); it('should fail when filter contains invalid column names', async () => { // ARRANGE - const { id: dataStoreId } = await dataStoreService.createDataStore(project1.id, { - name: 'dataStore', + const { id: dataTableId } = await dataTableService.createDataTable(project1.id, { + name: 'dataTable', columns: [{ name: 'name', type: 'string' }], }); - await dataStoreService.insertRows(dataStoreId, project1.id, [{ name: 'Alice' }]); + await dataTableService.insertRows(dataTableId, project1.id, [{ name: 'Alice' }]); // ACT & ASSERT - const result = dataStoreService.updateRows(dataStoreId, project1.id, { + const result = dataTableService.updateRows(dataTableId, project1.id, { filter: { type: 'and', filters: [{ columnName: 'invalidColumn', condition: 'eq', value: 'Alice' }], @@ -2947,34 +2947,34 @@ describe('dataStore', () => { data: { name: 'Bob' }, }); - await expect(result).rejects.toThrow(DataStoreValidationError); + await expect(result).rejects.toThrow(DataTableValidationError); }); it('should fail when data contains invalid type values', async () => { // ARRANGE - const { id: dataStoreId } = await dataStoreService.createDataStore(project1.id, { - name: 'dataStore', + const { id: dataTableId } = await dataTableService.createDataTable(project1.id, { + name: 'dataTable', columns: [ { name: 'name', type: 'string' }, { name: 'age', type: 'number' }, ], }); - await dataStoreService.insertRows(dataStoreId, project1.id, [{ name: 'Alice', age: 30 }]); + await dataTableService.insertRows(dataTableId, project1.id, [{ name: 'Alice', age: 30 }]); // ACT & ASSERT - const result = dataStoreService.updateRows(dataStoreId, project1.id, { + const result = dataTableService.updateRows(dataTableId, project1.id, { filter: { type: 'and', filters: [{ columnName: 'name', condition: 'eq', value: 'Alice' }] }, data: { age: 'not-a-number' }, }); - await expect(result).rejects.toThrow(DataStoreValidationError); + await expect(result).rejects.toThrow(DataTableValidationError); }); it('should allow partial data updates', async () => { // ARRANGE - const { id: dataStoreId } = await dataStoreService.createDataStore(project1.id, { - name: 'dataStore', + const { id: dataTableId } = await dataTableService.createDataTable(project1.id, { + name: 'dataTable', columns: [ { name: 'name', type: 'string' }, { name: 'age', type: 'number' }, @@ -2982,12 +2982,12 @@ describe('dataStore', () => { ], }); - await dataStoreService.insertRows(dataStoreId, project1.id, [ + await dataTableService.insertRows(dataTableId, project1.id, [ { name: 'Alice', age: 30, active: true }, ]); // ACT - only update age, leaving name and active unchanged - const result = await dataStoreService.updateRows(dataStoreId, project1.id, { + const result = await dataTableService.updateRows(dataTableId, project1.id, { filter: { type: 'and', filters: [{ columnName: 'name', condition: 'eq', value: 'Alice' }] }, data: { age: 31 }, }); @@ -2995,7 +2995,7 @@ describe('dataStore', () => { // ASSERT expect(result).toEqual(true); - const { data } = await dataStoreService.getManyRowsAndCount(dataStoreId, project1.id, {}); + const { data } = await dataTableService.getManyRowsAndCount(dataTableId, project1.id, {}); expect(data).toEqual([ expect.objectContaining({ name: 'Alice', @@ -3007,8 +3007,8 @@ describe('dataStore', () => { it('should handle date column updates correctly', async () => { // ARRANGE - const { id: dataStoreId } = await dataStoreService.createDataStore(project1.id, { - name: 'dataStore', + const { id: dataTableId } = await dataTableService.createDataTable(project1.id, { + name: 'dataTable', columns: [ { name: 'name', type: 'string' }, { name: 'birthDate', type: 'date' }, @@ -3016,13 +3016,13 @@ describe('dataStore', () => { }); const initialDate = new Date('1990-01-01'); - await dataStoreService.insertRows(dataStoreId, project1.id, [ + await dataTableService.insertRows(dataTableId, project1.id, [ { name: 'Alice', birthDate: initialDate }, ]); // ACT const newDate = new Date('1991-02-02'); - const result = await dataStoreService.updateRows(dataStoreId, project1.id, { + const result = await dataTableService.updateRows(dataTableId, project1.id, { filter: { type: 'and', filters: [{ columnName: 'name', condition: 'eq', value: 'Alice' }] }, data: { birthDate: newDate.toISOString() }, }); @@ -3030,15 +3030,15 @@ describe('dataStore', () => { // ASSERT expect(result).toEqual(true); - const { data } = await dataStoreService.getManyRowsAndCount(dataStoreId, project1.id, {}); + const { data } = await dataTableService.getManyRowsAndCount(dataTableId, project1.id, {}); expect(data).toEqual([expect.objectContaining({ id: 1, name: 'Alice', birthDate: newDate })]); }); it('should return full updated rows if returnData is set', async () => { // ARRANGE - const { id: dataStoreId } = await dataStoreService.createDataStore(project1.id, { - name: 'dataStore', + const { id: dataTableId } = await dataTableService.createDataTable(project1.id, { + name: 'dataTable', columns: [ { name: 'name', type: 'string' }, { name: 'age', type: 'number' }, @@ -3048,7 +3048,7 @@ describe('dataStore', () => { }); const now = new Date(); - await dataStoreService.insertRows(dataStoreId, project1.id, [ + await dataTableService.insertRows(dataTableId, project1.id, [ { name: 'Alice', age: 30, active: true, timestamp: now }, { name: 'Bob', age: 25, active: false, timestamp: now }, ]); @@ -3057,8 +3057,8 @@ describe('dataStore', () => { soon.setDate(now.getDate() + 1); // ACT - const result = await dataStoreService.updateRows( - dataStoreId, + const result = await dataTableService.updateRows( + dataTableId, project1.id, { filter: { @@ -3078,22 +3078,22 @@ describe('dataStore', () => { it('should simulate update with dryRun=true and return before/after pairs', async () => { // ARRANGE - const { id: dataStoreId } = await dataStoreService.createDataStore(project1.id, { - name: 'dataStore', + const { id: dataTableId } = await dataTableService.createDataTable(project1.id, { + name: 'dataTable', columns: [ { name: 'name', type: 'string' }, { name: 'age', type: 'number' }, ], }); - await dataStoreService.insertRows(dataStoreId, project1.id, [ + await dataTableService.insertRows(dataTableId, project1.id, [ { name: 'Alice', age: 30 }, { name: 'Bob', age: 25 }, ]); // ACT - const result = await dataStoreService.updateRows( - dataStoreId, + const result = await dataTableService.updateRows( + dataTableId, project1.id, { filter: { @@ -3135,26 +3135,26 @@ describe('dataStore', () => { ); // Should not actually update the rows - const { data } = await dataStoreService.getManyRowsAndCount(dataStoreId, project1.id, {}); + const { data } = await dataTableService.getManyRowsAndCount(dataTableId, project1.id, {}); const aliceRow = data.find((row) => row.name === 'Alice'); expect(aliceRow?.age).toBe(30); // Should still be original value }); it('should return empty array with dryRun=true when no rows match filter', async () => { // ARRANGE - const { id: dataStoreId } = await dataStoreService.createDataStore(project1.id, { - name: 'dataStore', + const { id: dataTableId } = await dataTableService.createDataTable(project1.id, { + name: 'dataTable', columns: [ { name: 'name', type: 'string' }, { name: 'age', type: 'number' }, ], }); - await dataStoreService.insertRows(dataStoreId, project1.id, [{ name: 'Alice', age: 30 }]); + await dataTableService.insertRows(dataTableId, project1.id, [{ name: 'Alice', age: 30 }]); // ACT - const result = await dataStoreService.updateRows( - dataStoreId, + const result = await dataTableService.updateRows( + dataTableId, project1.id, { filter: { @@ -3171,29 +3171,29 @@ describe('dataStore', () => { expect(result).toEqual([]); // Should not update any rows - const { data } = await dataStoreService.getManyRowsAndCount(dataStoreId, project1.id, {}); + const { data } = await dataTableService.getManyRowsAndCount(dataTableId, project1.id, {}); expect(data[0].age).toBe(30); }); it('should handle multiple rows with dryRun=true returning multiple before/after pairs', async () => { // ARRANGE - const { id: dataStoreId } = await dataStoreService.createDataStore(project1.id, { - name: 'dataStore', + const { id: dataTableId } = await dataTableService.createDataTable(project1.id, { + name: 'dataTable', columns: [ { name: 'name', type: 'string' }, { name: 'active', type: 'boolean' }, ], }); - await dataStoreService.insertRows(dataStoreId, project1.id, [ + await dataTableService.insertRows(dataTableId, project1.id, [ { name: 'Alice', active: false }, { name: 'Bob', active: false }, { name: 'Charlie', active: true }, ]); // ACT - const result = await dataStoreService.updateRows( - dataStoreId, + const result = await dataTableService.updateRows( + dataTableId, project1.id, { filter: { @@ -3226,7 +3226,7 @@ describe('dataStore', () => { ); // Should not actually update the rows - const { data } = await dataStoreService.getManyRowsAndCount(dataStoreId, project1.id, {}); + const { data } = await dataTableService.getManyRowsAndCount(dataTableId, project1.id, {}); const activeFalseRows = data.filter((row) => row.active === false); expect(activeFalseRows).toHaveLength(2); }); @@ -3235,8 +3235,8 @@ describe('dataStore', () => { describe('getManyRowsAndCount', () => { it('retrieves rows correctly', async () => { // ARRANGE - const { id: dataStoreId } = await dataStoreService.createDataStore(project1.id, { - name: 'dataStore', + const { id: dataTableId } = await dataTableService.createDataTable(project1.id, { + name: 'dataTable', columns: [ { name: 'c1', type: 'number' }, { name: 'c2', type: 'boolean' }, @@ -3266,11 +3266,11 @@ describe('dataStore', () => { }, ]; - const ids = await dataStoreService.insertRows(dataStoreId, project1.id, rows, 'id'); + const ids = await dataTableService.insertRows(dataTableId, project1.id, rows, 'id'); expect(ids).toEqual([{ id: 1 }, { id: 2 }, { id: 3 }]); // ACT - const result = await dataStoreService.getManyRowsAndCount(dataStoreId, project1.id, {}); + const result = await dataTableService.getManyRowsAndCount(dataTableId, project1.id, {}); // ASSERT expect(result.count).toEqual(3); @@ -3308,15 +3308,15 @@ describe('dataStore', () => { it('should fail when filter contains invalid column names', async () => { // ARRANGE - const { id: dataStoreId } = await dataStoreService.createDataStore(project1.id, { - name: 'dataStore', + const { id: dataTableId } = await dataTableService.createDataTable(project1.id, { + name: 'dataTable', columns: [{ name: 'name', type: 'string' }], }); - await dataStoreService.insertRows(dataStoreId, project1.id, [{ name: 'Alice' }]); + await dataTableService.insertRows(dataTableId, project1.id, [{ name: 'Alice' }]); // ACT - const result = dataStoreService.updateRows(dataStoreId, project1.id, { + const result = dataTableService.updateRows(dataTableId, project1.id, { filter: { type: 'and', filters: [{ columnName: 'invalidColumn', condition: 'eq', value: 'Alice' }], @@ -3325,121 +3325,121 @@ describe('dataStore', () => { }); // ASSERT - await expect(result).rejects.toThrow(DataStoreValidationError); + await expect(result).rejects.toThrow(DataTableValidationError); }); }); - describe('transferDataStoresByProjectId', () => { - it('should transfer all data stores from one project to another', async () => { + describe('transferDataTablesByProjectId', () => { + it('should transfer all data tables from one project to another', async () => { // ARRANGE - const { id: dataStoreId1, name: dataStoreName1 } = await dataStoreService.createDataStore( + const { id: dataTableId1, name: dataTableName1 } = await dataTableService.createDataTable( project1.id, { - name: 'dataStore1', + name: 'dataTable1', columns: [{ name: 'col1', type: 'string' }], }, ); - const { id: dataStoreId2, name: dataStoreName2 } = await dataStoreService.createDataStore( + const { id: dataTableId2, name: dataTableName2 } = await dataTableService.createDataTable( project1.id, { - name: 'dataStore2', + name: 'dataTable2', columns: [{ name: 'col1', type: 'string' }], }, ); // ACT - await dataStoreService.transferDataStoresByProjectId(project1.id, project2.id); + await dataTableService.transferDataTablesByProjectId(project1.id, project2.id); // ASSERT - const dataStore1 = await dataStoreRepository.findOneByOrFail({ - id: dataStoreId1, + const dataTable1 = await dataTableRepository.findOneByOrFail({ + id: dataTableId1, }); - expect(dataStore1).toBeDefined(); - expect(dataStore1.projectId).toBe(project2.id); - expect(dataStore1.name).toBe(dataStoreName1); + expect(dataTable1).toBeDefined(); + expect(dataTable1.projectId).toBe(project2.id); + expect(dataTable1.name).toBe(dataTableName1); - const dataStore2 = await dataStoreRepository.findOneByOrFail({ - id: dataStoreId2, + const dataTable2 = await dataTableRepository.findOneByOrFail({ + id: dataTableId2, }); - expect(dataStore2).toBeDefined(); - expect(dataStore2.projectId).toBe(project2.id); - expect(dataStore2.name).toBe(dataStoreName2); + expect(dataTable2).toBeDefined(); + expect(dataTable2.projectId).toBe(project2.id); + expect(dataTable2.name).toBe(dataTableName2); }); - it('should not affect data stores in other projects', async () => { + it('should not affect data tables in other projects', async () => { // ARRANGE - const { id: dataStoreId1 } = await dataStoreService.createDataStore(project1.id, { - name: 'dataStore1', + const { id: dataTableId1 } = await dataTableService.createDataTable(project1.id, { + name: 'dataTable1', columns: [{ name: 'col1', type: 'string' }], }); - const { id: dataStoreId2 } = await dataStoreService.createDataStore(project2.id, { - name: 'dataStore2', + const { id: dataTableId2 } = await dataTableService.createDataTable(project2.id, { + name: 'dataTable2', columns: [{ name: 'col1', type: 'string' }], }); // ACT - await dataStoreService.transferDataStoresByProjectId(project1.id, project2.id); + await dataTableService.transferDataTablesByProjectId(project1.id, project2.id); // ASSERT - const dataStore1 = await dataStoreRepository.findOneByOrFail({ - id: dataStoreId1, + const dataTable1 = await dataTableRepository.findOneByOrFail({ + id: dataTableId1, }); - expect(dataStore1).toBeDefined(); - expect(dataStore1.projectId).toBe(project2.id); + expect(dataTable1).toBeDefined(); + expect(dataTable1.projectId).toBe(project2.id); - const dataStore2 = await dataStoreRepository.findOneByOrFail({ - id: dataStoreId2, + const dataTable2 = await dataTableRepository.findOneByOrFail({ + id: dataTableId2, project: { id: project2.id, }, }); - expect(dataStore2).toBeDefined(); - expect(dataStore2.projectId).toBe(project2.id); + expect(dataTable2).toBeDefined(); + expect(dataTable2.projectId).toBe(project2.id); }); - it('should rename data stores if name conflict occurs in target project', async () => { + it('should rename data tables if name conflict occurs in target project', async () => { // ARRANGE - const { id: dataStoreId1 } = await dataStoreService.createDataStore(project1.id, { - name: 'dataStore', + const { id: dataTableId1 } = await dataTableService.createDataTable(project1.id, { + name: 'dataTable', columns: [{ name: 'col1', type: 'string' }], }); - await dataStoreService.createDataStore(project2.id, { - name: 'dataStore', + await dataTableService.createDataTable(project2.id, { + name: 'dataTable', columns: [{ name: 'col1', type: 'string' }], }); // ACT - await dataStoreService.transferDataStoresByProjectId(project1.id, project2.id); + await dataTableService.transferDataTablesByProjectId(project1.id, project2.id); // ASSERT - const dataStore1 = await dataStoreRepository.findOneByOrFail({ - id: dataStoreId1, + const dataTable1 = await dataTableRepository.findOneByOrFail({ + id: dataTableId1, }); - expect(dataStore1).toBeDefined(); - expect(dataStore1.projectId).toBe(project2.id); - expect(dataStore1.name).toBe(`dataStore (${project1.name})`); + expect(dataTable1).toBeDefined(); + expect(dataTable1.projectId).toBe(project2.id); + expect(dataTable1.name).toBe(`dataTable (${project1.name})`); }); }); - describe('deleteDataStoreByProjectId', () => { - it('should delete all data stores for a given project ID', async () => { + describe('deleteDataTableByProjectId', () => { + it('should delete all data tables for a given project ID', async () => { // ARRANGE - const { id: dataStoreId1 } = await dataStoreService.createDataStore(project1.id, { - name: 'dataStore1', + const { id: dataTableId1 } = await dataTableService.createDataTable(project1.id, { + name: 'dataTable1', columns: [{ name: 'col1', type: 'string' }], }); - const { id: dataStoreId2 } = await dataStoreService.createDataStore(project1.id, { - name: 'dataStore2', + const { id: dataTableId2 } = await dataTableService.createDataTable(project1.id, { + name: 'dataTable2', columns: [{ name: 'col1', type: 'string' }], }); // ACT - await dataStoreService.deleteDataStoreByProjectId(project1.id); + await dataTableService.deleteDataTableByProjectId(project1.id); // ASSERT await expect( - dataStoreRepository.findOneByOrFail({ - id: dataStoreId1, + dataTableRepository.findOneByOrFail({ + id: dataTableId1, project: { id: project1.id, }, @@ -3447,8 +3447,8 @@ describe('dataStore', () => { ).rejects.toThrow(); await expect( - dataStoreRepository.findOneByOrFail({ - id: dataStoreId2, + dataTableRepository.findOneByOrFail({ + id: dataTableId2, project: { id: project1.id, }, @@ -3456,38 +3456,38 @@ describe('dataStore', () => { ).rejects.toThrow(); }); - it('should not delete data stores for other projects', async () => { + it('should not delete data tables for other projects', async () => { // ARRANGE - const { id: dataStoreId1 } = await dataStoreService.createDataStore(project1.id, { - name: 'dataStoreA', + const { id: dataTableId1 } = await dataTableService.createDataTable(project1.id, { + name: 'dataTableA', columns: [{ name: 'col1', type: 'string' }], }); - const { id: dataStoreId2 } = await dataStoreService.createDataStore(project2.id, { - name: 'dataStoreB', + const { id: dataTableId2 } = await dataTableService.createDataTable(project2.id, { + name: 'dataTableB', columns: [{ name: 'col1', type: 'string' }], }); // ACT - await dataStoreService.deleteDataStoreByProjectId(project1.id); + await dataTableService.deleteDataTableByProjectId(project1.id); // ASSERT await expect( - dataStoreRepository.findOneByOrFail({ - id: dataStoreId1, + dataTableRepository.findOneByOrFail({ + id: dataTableId1, project: { id: project1.id, }, }), ).rejects.toThrow(); - const dataStore2 = await dataStoreRepository.findOneByOrFail({ - id: dataStoreId2, + const dataTable2 = await dataTableRepository.findOneByOrFail({ + id: dataTableId2, project: { id: project2.id, }, }); - expect(dataStore2).toBeDefined(); - expect(dataStore2.id).toBe(dataStoreId2); + expect(dataTable2).toBeDefined(); + expect(dataTable2.id).toBe(dataTableId2); }); }); }); diff --git a/packages/cli/src/modules/data-table/__tests__/test-helpers.ts b/packages/cli/src/modules/data-table/__tests__/test-helpers.ts index 6c589f73ad2..e795d4ea1d3 100644 --- a/packages/cli/src/modules/data-table/__tests__/test-helpers.ts +++ b/packages/cli/src/modules/data-table/__tests__/test-helpers.ts @@ -1,9 +1,9 @@ import { Container } from '@n8n/di'; -import { DataStoreSizeValidator } from '../data-store-size-validator.service'; +import { DataTableSizeValidator } from '../data-table-size-validator.service'; -export function mockDataStoreSizeValidator() { - const sizeValidator = Container.get(DataStoreSizeValidator); +export function mockDataTableSizeValidator() { + const sizeValidator = Container.get(DataTableSizeValidator); jest.spyOn(sizeValidator, 'validateSize').mockResolvedValue(); jest.spyOn(sizeValidator, 'getCachedSizeData').mockResolvedValue({ totalBytes: 50 * 1024 * 1024, // 50MB - under the default limit diff --git a/packages/cli/src/modules/data-table/data-store-aggregate.controller.ts b/packages/cli/src/modules/data-table/data-table-aggregate.controller.ts similarity index 58% rename from packages/cli/src/modules/data-table/data-store-aggregate.controller.ts rename to packages/cli/src/modules/data-table/data-table-aggregate.controller.ts index bb0dcf9cccd..ceea2f6d662 100644 --- a/packages/cli/src/modules/data-table/data-store-aggregate.controller.ts +++ b/packages/cli/src/modules/data-table/data-table-aggregate.controller.ts @@ -1,23 +1,23 @@ -import { ListDataStoreQueryDto } from '@n8n/api-types'; +import { ListDataTableQueryDto } from '@n8n/api-types'; import { AuthenticatedRequest } from '@n8n/db'; import { Get, GlobalScope, Query, RestController } from '@n8n/decorators'; -import { DataStoreAggregateService } from './data-store-aggregate.service'; -import { DataStoreService } from './data-store.service'; +import { DataTableAggregateService } from './data-table-aggregate.service'; +import { DataTableService } from './data-table.service'; @RestController('/data-tables-global') -export class DataStoreAggregateController { +export class DataTableAggregateController { constructor( - private readonly dataStoreAggregateService: DataStoreAggregateService, - private readonly dataStoreService: DataStoreService, + private readonly dataStoreAggregateService: DataTableAggregateService, + private readonly dataStoreService: DataTableService, ) {} @Get('/') @GlobalScope('dataStore:list') - async listDataStores( + async listDataTables( req: AuthenticatedRequest, _res: Response, - @Query payload: ListDataStoreQueryDto, + @Query payload: ListDataTableQueryDto, ) { return await this.dataStoreAggregateService.getManyAndCount(req.user, payload); } diff --git a/packages/cli/src/modules/data-table/data-store-aggregate.service.ts b/packages/cli/src/modules/data-table/data-table-aggregate.service.ts similarity index 70% rename from packages/cli/src/modules/data-table/data-store-aggregate.service.ts rename to packages/cli/src/modules/data-table/data-table-aggregate.service.ts index 908d2ddff3e..4cf3367f3a8 100644 --- a/packages/cli/src/modules/data-table/data-store-aggregate.service.ts +++ b/packages/cli/src/modules/data-table/data-table-aggregate.service.ts @@ -1,16 +1,16 @@ -import type { ListDataStoreQueryDto } from '@n8n/api-types'; +import type { ListDataTableQueryDto } from '@n8n/api-types'; import { Logger } from '@n8n/backend-common'; import { User } from '@n8n/db'; import { Service } from '@n8n/di'; import { ProjectService } from '@/services/project.service.ee'; -import { DataStoreRepository } from './data-store.repository'; +import { DataTableRepository } from './data-table.repository'; @Service() -export class DataStoreAggregateService { +export class DataTableAggregateService { constructor( - private readonly dataStoreRepository: DataStoreRepository, + private readonly dataTableRepository: DataTableRepository, private readonly projectService: ProjectService, private readonly logger: Logger, ) { @@ -19,7 +19,7 @@ export class DataStoreAggregateService { async start() {} async shutdown() {} - async getManyAndCount(user: User, options: ListDataStoreQueryDto) { + async getManyAndCount(user: User, options: ListDataTableQueryDto) { const projects = await this.projectService.getProjectRelationsForUser(user); let projectIds = projects.map((x) => x.projectId); if (options.filter?.projectId) { @@ -31,7 +31,7 @@ export class DataStoreAggregateService { return { count: 0, data: [] }; } - return await this.dataStoreRepository.getManyAndCount({ + return await this.dataTableRepository.getManyAndCount({ ...options, filter: { ...options.filter, diff --git a/packages/cli/src/modules/data-table/data-store-column.repository.ts b/packages/cli/src/modules/data-table/data-table-column.repository.ts similarity index 74% rename from packages/cli/src/modules/data-table/data-store-column.repository.ts rename to packages/cli/src/modules/data-table/data-table-column.repository.ts index 906831fef9c..95f0507cbce 100644 --- a/packages/cli/src/modules/data-table/data-store-column.repository.ts +++ b/packages/cli/src/modules/data-table/data-table-column.repository.ts @@ -1,4 +1,4 @@ -import { DataStoreCreateColumnSchema } from '@n8n/api-types'; +import { DataTableCreateColumnSchema } from '@n8n/api-types'; import { withTransaction } from '@n8n/db'; import { Service } from '@n8n/di'; import { DataSource, EntityManager, Repository } from '@n8n/typeorm'; @@ -8,18 +8,18 @@ import { UnexpectedError, } from 'n8n-workflow'; -import { DataStoreRowsRepository } from './data-store-rows.repository'; import { DataTableColumn } from './data-table-column.entity'; +import { DataTableRowsRepository } from './data-table-rows.repository'; import { DataTable } from './data-table.entity'; -import { DataStoreColumnNameConflictError } from './errors/data-store-column-name-conflict.error'; -import { DataStoreSystemColumnNameConflictError } from './errors/data-store-system-column-name-conflict.error'; -import { DataStoreValidationError } from './errors/data-store-validation.error'; +import { DataTableColumnNameConflictError } from './errors/data-table-column-name-conflict.error'; +import { DataTableSystemColumnNameConflictError } from './errors/data-table-system-column-name-conflict.error'; +import { DataTableValidationError } from './errors/data-table-validation.error'; @Service() -export class DataStoreColumnRepository extends Repository { +export class DataTableColumnRepository extends Repository { constructor( dataSource: DataSource, - private dataStoreRowsRepository: DataStoreRowsRepository, + private dataTableRowsRepository: DataTableRowsRepository, ) { super(DataTableColumn, dataSource.manager); } @@ -44,13 +44,13 @@ export class DataStoreColumnRepository extends Repository { ); } - async addColumn(dataTableId: string, schema: DataStoreCreateColumnSchema, trx?: EntityManager) { + async addColumn(dataTableId: string, schema: DataTableCreateColumnSchema, trx?: EntityManager) { return await withTransaction(this.manager, trx, async (em) => { if (DATA_TABLE_SYSTEM_COLUMNS.includes(schema.name)) { - throw new DataStoreSystemColumnNameConflictError(schema.name); + throw new DataTableSystemColumnNameConflictError(schema.name); } if (schema.name === DATA_TABLE_SYSTEM_TESTING_COLUMN) { - throw new DataStoreSystemColumnNameConflictError(schema.name, 'testing'); + throw new DataTableSystemColumnNameConflictError(schema.name, 'testing'); } const existingColumnMatch = await em.existsBy(DataTableColumn, { @@ -61,9 +61,9 @@ export class DataStoreColumnRepository extends Repository { if (existingColumnMatch) { const dataTable = await em.findOneBy(DataTable, { id: dataTableId }); if (!dataTable) { - throw new UnexpectedError('Data store not found'); + throw new UnexpectedError('Data table not found'); } - throw new DataStoreColumnNameConflictError(schema.name, dataTable.name); + throw new DataTableColumnNameConflictError(schema.name, dataTable.name); } if (schema.index === undefined) { @@ -81,7 +81,7 @@ export class DataStoreColumnRepository extends Repository { // @ts-ignore Workaround for intermittent typecheck issue with _QueryDeepPartialEntity await em.insert(DataTableColumn, column); - await this.dataStoreRowsRepository.addColumn( + await this.dataTableRowsRepository.addColumn( dataTableId, column, em.connection.options.type, @@ -92,17 +92,17 @@ export class DataStoreColumnRepository extends Repository { }); } - async deleteColumn(dataStoreId: string, column: DataTableColumn, trx?: EntityManager) { + async deleteColumn(dataTableId: string, column: DataTableColumn, trx?: EntityManager) { await withTransaction(this.manager, trx, async (em) => { await em.remove(DataTableColumn, column); - await this.dataStoreRowsRepository.dropColumnFromTable( - dataStoreId, + await this.dataTableRowsRepository.dropColumnFromTable( + dataTableId, column.name, em.connection.options.type, em, ); - await this.shiftColumns(dataStoreId, column.index, -1, em); + await this.shiftColumns(dataTableId, column.index, -1, em); }); } @@ -116,11 +116,11 @@ export class DataStoreColumnRepository extends Repository { const columnCount = await em.countBy(DataTableColumn, { dataTableId }); if (targetIndex < 0) { - throw new DataStoreValidationError('tried to move column to negative index'); + throw new DataTableValidationError('tried to move column to negative index'); } if (targetIndex >= columnCount) { - throw new DataStoreValidationError( + throw new DataTableValidationError( 'tried to move column to an index larger than column count', ); } diff --git a/packages/cli/src/modules/data-table/data-store-proxy.service.ts b/packages/cli/src/modules/data-table/data-table-proxy.service.ts similarity index 62% rename from packages/cli/src/modules/data-table/data-store-proxy.service.ts rename to packages/cli/src/modules/data-table/data-table-proxy.service.ts index a6564bfe9fa..62930be06e7 100644 --- a/packages/cli/src/modules/data-table/data-store-proxy.service.ts +++ b/packages/cli/src/modules/data-table/data-table-proxy.service.ts @@ -1,30 +1,30 @@ -import type { DataStoreListOptions } from '@n8n/api-types'; +import type { DataTableListOptions } from '@n8n/api-types'; import { Logger } from '@n8n/backend-common'; import { Service } from '@n8n/di'; import { - AddDataStoreColumnOptions, - CreateDataStoreOptions, - DataStore, - DataStoreColumn, - DataStoreProxyProvider, - DataStoreRows, + AddDataTableColumnOptions, + CreateDataTableOptions, + DataTable, + DataTableColumn, + DataTableProxyProvider, + DataTableRows, DeleteDataTableRowsOptions, - IDataStoreProjectAggregateService, - IDataStoreProjectService, + IDataTableProjectAggregateService, + IDataTableProjectService, DataTableInsertRowsReturnType, INode, - ListDataStoreOptions, - ListDataStoreRowsOptions, - MoveDataStoreColumnOptions, - UpdateDataStoreOptions, - UpdateDataStoreRowOptions, - UpsertDataStoreRowOptions, + ListDataTableOptions, + ListDataTableRowsOptions, + MoveDataTableColumnOptions, + UpdateDataTableOptions, + UpdateDataTableRowOptions, + UpsertDataTableRowOptions, Workflow, } from 'n8n-workflow'; import { OwnershipService } from '@/services/ownership.service'; -import { DataStoreService } from './data-store.service'; +import { DataTableService } from './data-table.service'; const ALLOWED_NODES = [ 'n8n-nodes-base.dataTable', @@ -40,9 +40,9 @@ export function isAllowedNode(s: string): s is AllowedNode { } @Service() -export class DataStoreProxyService implements DataStoreProxyProvider { +export class DataTableProxyService implements DataTableProxyProvider { constructor( - private readonly dataStoreService: DataStoreService, + private readonly dataStoreService: DataTableService, private readonly ownershipService: OwnershipService, private readonly logger: Logger, ) { @@ -60,80 +60,80 @@ export class DataStoreProxyService implements DataStoreProxyProvider { return homeProject.id; } - async getDataStoreAggregateProxy( + async getDataTableAggregateProxy( workflow: Workflow, node: INode, projectId?: string, - ): Promise { + ): Promise { this.validateRequest(node); projectId = projectId ?? (await this.getProjectId(workflow)); return this.makeAggregateOperations(projectId); } - async getDataStoreProxy( + async getDataTableProxy( workflow: Workflow, node: INode, dataStoreId: string, projectId?: string, - ): Promise { + ): Promise { this.validateRequest(node); projectId = projectId ?? (await this.getProjectId(workflow)); - return this.makeDataStoreOperations(projectId, dataStoreId); + return this.makeDataTableOperations(projectId, dataStoreId); } - private makeAggregateOperations(projectId: string): IDataStoreProjectAggregateService { + private makeAggregateOperations(projectId: string): IDataTableProjectAggregateService { const dataStoreService = this.dataStoreService; return { getProjectId() { return projectId; }, - async getManyAndCount(options: ListDataStoreOptions = {}) { - const serviceOptions: DataStoreListOptions = { + async getManyAndCount(options: ListDataTableOptions = {}) { + const serviceOptions: DataTableListOptions = { ...options, filter: { projectId, ...(options.filter ?? {}) }, }; return await dataStoreService.getManyAndCount(serviceOptions); }, - async createDataStore(options: CreateDataStoreOptions): Promise { - return await dataStoreService.createDataStore(projectId, options); + async createDataTable(options: CreateDataTableOptions): Promise { + return await dataStoreService.createDataTable(projectId, options); }, - async deleteDataStoreAll(): Promise { - return await dataStoreService.deleteDataStoreByProjectId(projectId); + async deleteDataTableAll(): Promise { + return await dataStoreService.deleteDataTableByProjectId(projectId); }, }; } - private makeDataStoreOperations( + private makeDataTableOperations( projectId: string, dataStoreId: string, - ): Omit { + ): Omit { const dataStoreService = this.dataStoreService; return { - // DataStore management - async updateDataStore(options: UpdateDataStoreOptions): Promise { - return await dataStoreService.updateDataStore(dataStoreId, projectId, options); + // DataTable management + async updateDataTable(options: UpdateDataTableOptions): Promise { + return await dataStoreService.updateDataTable(dataStoreId, projectId, options); }, - async deleteDataStore(): Promise { - return await dataStoreService.deleteDataStore(dataStoreId, projectId); + async deleteDataTable(): Promise { + return await dataStoreService.deleteDataTable(dataStoreId, projectId); }, // Column operations - async getColumns(): Promise { + async getColumns(): Promise { return await dataStoreService.getColumns(dataStoreId, projectId); }, - async addColumn(options: AddDataStoreColumnOptions): Promise { + async addColumn(options: AddDataTableColumnOptions): Promise { return await dataStoreService.addColumn(dataStoreId, projectId, options); }, - async moveColumn(columnId: string, options: MoveDataStoreColumnOptions): Promise { + async moveColumn(columnId: string, options: MoveDataTableColumnOptions): Promise { return await dataStoreService.moveColumn(dataStoreId, projectId, columnId, options); }, @@ -142,18 +142,18 @@ export class DataStoreProxyService implements DataStoreProxyProvider { }, // Row operations - async getManyRowsAndCount(options: Partial) { + async getManyRowsAndCount(options: Partial) { return await dataStoreService.getManyRowsAndCount(dataStoreId, projectId, options); }, async insertRows( - rows: DataStoreRows, + rows: DataTableRows, returnType: T, ) { return await dataStoreService.insertRows(dataStoreId, projectId, rows, returnType); }, - async updateRows(options: UpdateDataStoreRowOptions) { + async updateRows(options: UpdateDataTableRowOptions) { return await dataStoreService.updateRows( dataStoreId, projectId, @@ -163,7 +163,7 @@ export class DataStoreProxyService implements DataStoreProxyProvider { ); }, - async upsertRow(options: UpsertDataStoreRowOptions) { + async upsertRow(options: UpsertDataTableRowOptions) { return await dataStoreService.upsertRow( dataStoreId, projectId, diff --git a/packages/cli/src/modules/data-table/data-store-rows.repository.ts b/packages/cli/src/modules/data-table/data-table-rows.repository.ts similarity index 86% rename from packages/cli/src/modules/data-table/data-store-rows.repository.ts rename to packages/cli/src/modules/data-table/data-table-rows.repository.ts index 44bee690883..9ffc11f6dc5 100644 --- a/packages/cli/src/modules/data-table/data-store-rows.repository.ts +++ b/packages/cli/src/modules/data-table/data-table-rows.repository.ts @@ -1,4 +1,4 @@ -import { ListDataStoreContentQueryDto, DataTableFilter } from '@n8n/api-types'; +import { DataTableFilter, ListDataTableContentQueryDto } from '@n8n/api-types'; import { CreateTable, DslColumn, withTransaction } from '@n8n/db'; import { Service } from '@n8n/di'; import { @@ -12,18 +12,17 @@ import { DeleteQueryBuilder, } from '@n8n/typeorm'; import { - DataStoreColumnJsType, - DataStoreRows, - DataStoreRowReturn, + DataTableColumnJsType, + DataTableRows, + DataTableRowReturn, UnexpectedError, - DataStoreRowsReturn, + DataTableRowsReturn, DATA_TABLE_SYSTEM_COLUMNS, DataTableInsertRowsReturnType, DataTableInsertRowsResult, - DataStoreRowReturnWithState, + DataTableRowReturnWithState, } from 'n8n-workflow'; -import { DataStoreUserTableName } from './data-store.types'; import { DataTableColumn } from './data-table-column.entity'; import { addColumnQuery, @@ -38,6 +37,7 @@ import { toSqliteGlobFromPercent, toTableName, } from './utils/sql-utils'; +import { DataTableUserTableName } from './data-table.types'; // eslint-disable-next-line @typescript-eslint/no-explicit-any type QueryBuilder = SelectQueryBuilder; @@ -153,12 +153,12 @@ function getConditionAndParams( } @Service() -export class DataStoreRowsRepository { +export class DataTableRowsRepository { constructor(private dataSource: DataSource) {} async insertRowsBulk( - table: DataStoreUserTableName, - rows: DataStoreRows, + table: DataTableUserTableName, + rows: DataTableRows, columns: DataTableColumn[], trx?: EntityManager, ) { @@ -192,9 +192,9 @@ export class DataStoreRowsRepository { if (endExclusive <= start) break; - const completeRows = new Array(endExclusive - start); + const completeRows = new Array(endExclusive - start); for (let j = start; j < endExclusive; ++j) { - const insertArray: DataStoreColumnJsType[] = []; + const insertArray: DataTableColumnJsType[] = []; for (let h = 0; h < columnNames.length; ++h) { const column = columns[h]; @@ -218,25 +218,25 @@ export class DataStoreRowsRepository { } async insertRows( - dataStoreId: string, - rows: DataStoreRows, + dataTableId: string, + rows: DataTableRows, columns: DataTableColumn[], returnType: T, trx?: EntityManager, ): Promise>; async insertRows( - dataStoreId: string, - rows: DataStoreRows, + dataTableId: string, + rows: DataTableRows, columns: DataTableColumn[], returnType: T, trx?: EntityManager, ): Promise { return await withTransaction(this.dataSource.manager, trx, async (em) => { - const inserted: Array> = []; + const inserted: Array> = []; const dbType = this.dataSource.options.type; const useReturning = dbType === 'postgres' || dbType === 'mariadb'; - const table = toTableName(dataStoreId); + const table = toTableName(dataTableId); const escapedColumns = columns.map((c) => this.dataSource.driver.escape(c.name)); const escapedSystemColumns = DATA_TABLE_SYSTEM_COLUMNS.map((x) => this.dataSource.driver.escape(x), @@ -289,7 +289,7 @@ export class DataStoreRowsRepository { continue; } - const insertedRows = await this.getManyByIds(dataStoreId, ids, columns, em); + const insertedRows = await this.getManyByIds(dataTableId, ids, columns, em); inserted.push(...insertedRows); } @@ -299,16 +299,16 @@ export class DataStoreRowsRepository { } async updateRows( - dataStoreId: string, - data: Record, + dataTableId: string, + data: Record, filter: DataTableFilter, columns: DataTableColumn[], returnData?: T, trx?: EntityManager, - ): Promise; + ): Promise; async updateRows( - dataStoreId: string, - data: Record, + dataTableId: string, + data: Record, filter: DataTableFilter, columns: DataTableColumn[], returnData: boolean = false, @@ -318,7 +318,7 @@ export class DataStoreRowsRepository { const dbType = this.dataSource.options.type; const useReturning = dbType === 'postgres'; - const table = toTableName(dataStoreId); + const table = toTableName(dataTableId); const escapedColumns = columns.map((c) => this.dataSource.driver.escape(c.name)); const escapedSystemColumns = DATA_TABLE_SYSTEM_COLUMNS.map((x) => this.dataSource.driver.escape(x), @@ -326,11 +326,11 @@ export class DataStoreRowsRepository { const selectColumns = [...escapedSystemColumns, ...escapedColumns]; const setData = this.prepareUpdateData(data, columns, dbType); - let affectedRows: Array> = []; + let affectedRows: Array> = []; if (!useReturning && returnData) { // Only Postgres supports RETURNING statement on updates (with our typeorm), // on other engines we must query the list of updates rows later by ID - affectedRows = await this.getAffectedRowsForUpdate(dataStoreId, filter, columns, true, trx); + affectedRows = await this.getAffectedRowsForUpdate(dataTableId, filter, columns, true, trx); } setData.updatedAt = normalizeValue(new Date(), 'date', dbType); @@ -355,21 +355,21 @@ export class DataStoreRowsRepository { } const ids = affectedRows.map((row) => row.id); - return await this.getManyByIds(dataStoreId, ids, columns, em); + return await this.getManyByIds(dataTableId, ids, columns, em); }); } async dryRunUpdateRows( - dataStoreId: string, - data: Record, + dataTableId: string, + data: Record, filter: DataTableFilter, columns: DataTableColumn[], trx?: EntityManager, - ): Promise { + ): Promise { const dbType = this.dataSource.options.type; const beforeRows = await this.getAffectedRowsForUpdate( - dataStoreId, + dataTableId, filter, columns, false, @@ -385,13 +385,13 @@ export class DataStoreRowsRepository { } async dryRunUpsertRow( - dataStoreId: string, - data: Record, + dataTableId: string, + data: Record, filter: DataTableFilter, columns: DataTableColumn[], trx?: EntityManager, - ): Promise { - const updateResult = await this.dryRunUpdateRows(dataStoreId, data, filter, columns, trx); + ): Promise { + const updateResult = await this.dryRunUpdateRows(dataTableId, data, filter, columns, trx); if (updateResult.length > 0) { return updateResult; @@ -401,7 +401,7 @@ export class DataStoreRowsRepository { const dbType = this.dataSource.options.type; const now = new Date(); const preparedData = this.prepareUpdateData(data, columns, dbType); - const insertedRow: DataStoreRowReturn = { + const insertedRow: DataTableRowReturn = { id: 0, // Placeholder ID for dry run createdAt: now, updatedAt: now, @@ -446,7 +446,7 @@ export class DataStoreRowsRepository { return true; } - let affectedRows: DataStoreRowReturn[] = []; + let affectedRows: DataTableRowReturn[] = []; if (!useReturning) { const selectQuery = em.createQueryBuilder().select('*').from(table, 'dataTable'); @@ -455,7 +455,7 @@ export class DataStoreRowsRepository { this.applyFilters(selectQuery, filter, 'dataTable', columns); } - const rawRows = await selectQuery.getRawMany(); + const rawRows = await selectQuery.getRawMany(); affectedRows = normalizeRows(rawRows, columns); } @@ -490,18 +490,18 @@ export class DataStoreRowsRepository { } private async getAffectedRowsForUpdate( - dataStoreId: string, + dataTableId: string, filter: DataTableFilter, columns: DataTableColumn[], idsOnly: T, trx?: EntityManager, - ): Promise> : DataStoreRowReturn[]> { + ): Promise> : DataTableRowReturn[]> { return await withTransaction(this.dataSource.manager, trx, async (em) => { - const table = toTableName(dataStoreId); + const table = toTableName(dataTableId); const selectColumns = idsOnly ? 'id' : '*'; const selectQuery = em.createQueryBuilder().select(selectColumns).from(table, 'dataTable'); this.applyFilters(selectQuery, filter, 'dataTable', columns); - const rawRows: DataStoreRowsReturn = await selectQuery.getRawMany(); + const rawRows: DataTableRowsReturn = await selectQuery.getRawMany(); if (idsOnly) { return rawRows; @@ -512,10 +512,10 @@ export class DataStoreRowsRepository { } private prepareUpdateData( - data: Record, + data: Record, columns: DataTableColumn[], dbType: DataSourceOptions['type'], - ): Record { + ): Record { const setData = { ...data }; for (const column of columns) { if (column.name in setData) { @@ -526,9 +526,9 @@ export class DataStoreRowsRepository { } private toDryRunRows( - beforeState: DataStoreRowReturn | null, - afterState: DataStoreRowReturn | null, - ): DataStoreRowReturnWithState[] { + beforeState: DataTableRowReturn | null, + afterState: DataTableRowReturn | null, + ): DataTableRowReturnWithState[] { if (beforeState === null && afterState === null) { throw new Error('Both before and after rows cannot be null'); } @@ -559,7 +559,7 @@ export class DataStoreRowsRepository { } async createTableWithColumns( - dataStoreId: string, + dataTableId: string, columns: DataTableColumn[], trx?: EntityManager, ) { @@ -569,7 +569,7 @@ export class DataStoreRowsRepository { } const dslColumns = [new DslColumn('id').int.autoGenerate2.primary, ...toDslColumns(columns)]; - const createTable = new CreateTable(toTableName(dataStoreId), '', em.queryRunner).withColumns( + const createTable = new CreateTable(toTableName(dataTableId), '', em.queryRunner).withColumns( ...dslColumns, ).withTimestamps; @@ -577,40 +577,40 @@ export class DataStoreRowsRepository { }); } - async dropTable(dataStoreId: string, trx?: EntityManager) { + async dropTable(dataTableId: string, trx?: EntityManager) { await withTransaction(this.dataSource.manager, trx, async (em) => { if (!em.queryRunner) { throw new UnexpectedError('QueryRunner is not available'); } - await em.queryRunner.dropTable(toTableName(dataStoreId), true); + await em.queryRunner.dropTable(toTableName(dataTableId), true); }); } async addColumn( - dataStoreId: string, + dataTableId: string, column: DataTableColumn, dbType: DataSourceOptions['type'], trx?: EntityManager, ) { await withTransaction(this.dataSource.manager, trx, async (em) => { - await em.query(addColumnQuery(toTableName(dataStoreId), column, dbType)); + await em.query(addColumnQuery(toTableName(dataTableId), column, dbType)); }); } async dropColumnFromTable( - dataStoreId: string, + dataTableId: string, columnName: string, dbType: DataSourceOptions['type'], trx?: EntityManager, ) { await withTransaction(this.dataSource.manager, trx, async (em) => { - await em.query(deleteColumnQuery(toTableName(dataStoreId), columnName, dbType)); + await em.query(deleteColumnQuery(toTableName(dataTableId), columnName, dbType)); }); } async getManyAndCount( - dataStoreId: string, - dto: ListDataStoreContentQueryDto, + dataTableId: string, + dto: ListDataTableContentQueryDto, columns?: DataTableColumn[], trx?: EntityManager, ) { @@ -618,8 +618,8 @@ export class DataStoreRowsRepository { this.dataSource.manager, trx, async (em) => { - const [countQuery, query] = this.getManyQuery(dataStoreId, dto, em, columns); - const data: DataStoreRowsReturn = await query.select('*').getRawMany(); + const [countQuery, query] = this.getManyQuery(dataTableId, dto, em, columns); + const data: DataTableRowsReturn = await query.select('*').getRawMany(); const countResult = await countQuery.select('COUNT(*) as count').getRawOne<{ count: number | string | null; }>(); @@ -634,7 +634,7 @@ export class DataStoreRowsRepository { } async getManyByIds( - dataStoreId: string, + dataTableId: string, ids: number[], columns: DataTableColumn[], trx?: EntityManager, @@ -643,7 +643,7 @@ export class DataStoreRowsRepository { this.dataSource.manager, trx, async (em) => { - const table = toTableName(dataStoreId); + const table = toTableName(dataTableId); const escapedColumns = columns.map((c) => this.dataSource.driver.escape(c.name)); const escapedSystemColumns = DATA_TABLE_SYSTEM_COLUMNS.map((x) => this.dataSource.driver.escape(x), @@ -659,7 +659,7 @@ export class DataStoreRowsRepository { .select(selectColumns) .from(table, 'dataTable') .where({ id: In(ids) }) - .getRawMany(); + .getRawMany(); return normalizeRows(rows, columns); }, @@ -668,15 +668,15 @@ export class DataStoreRowsRepository { } private getManyQuery( - dataStoreId: string, - dto: ListDataStoreContentQueryDto, + dataTableId: string, + dto: ListDataTableContentQueryDto, em: EntityManager, columns?: DataTableColumn[], ): [QueryBuilder, QueryBuilder] { const query = em.createQueryBuilder(); const tableReference = 'dataTable'; - query.from(toTableName(dataStoreId), tableReference); + query.from(toTableName(dataTableId), tableReference); if (dto.filter) { this.applyFilters(query, dto.filter, tableReference, columns); } @@ -716,7 +716,7 @@ export class DataStoreRowsRepository { } } - private applySorting(query: QueryBuilder, dto: ListDataStoreContentQueryDto): void { + private applySorting(query: QueryBuilder, dto: ListDataTableContentQueryDto): void { if (!dto.sortBy) { return; } @@ -731,7 +731,7 @@ export class DataStoreRowsRepository { query.orderBy(quotedField, direction); } - private applyPagination(query: QueryBuilder, dto: ListDataStoreContentQueryDto): void { + private applyPagination(query: QueryBuilder, dto: ListDataTableContentQueryDto): void { query.skip(dto.skip ?? 0); if (dto.take) query.take(dto.take); } diff --git a/packages/cli/src/modules/data-table/data-store-size-validator.service.ts b/packages/cli/src/modules/data-table/data-table-size-validator.service.ts similarity index 91% rename from packages/cli/src/modules/data-table/data-store-size-validator.service.ts rename to packages/cli/src/modules/data-table/data-table-size-validator.service.ts index 53258858663..adf5dbee658 100644 --- a/packages/cli/src/modules/data-table/data-store-size-validator.service.ts +++ b/packages/cli/src/modules/data-table/data-table-size-validator.service.ts @@ -2,10 +2,10 @@ import { GlobalConfig } from '@n8n/config'; import { Service } from '@n8n/di'; import { DataTableSizeStatus, DataTablesSizeData } from 'n8n-workflow'; -import { DataStoreValidationError } from './errors/data-store-validation.error'; +import { DataTableValidationError } from './errors/data-table-validation.error'; @Service() -export class DataStoreSizeValidator { +export class DataTableSizeValidator { private lastCheck: Date | undefined; private cachedSizeData: DataTablesSizeData | undefined; private pendingCheck: Promise | null = null; @@ -53,8 +53,8 @@ export class DataStoreSizeValidator { ): Promise { const size = await this.getCachedSizeData(fetchSizeFn, now); if (size.totalBytes >= this.globalConfig.dataTable.maxSize) { - throw new DataStoreValidationError( - `Data store size limit exceeded: ${this.toMb(size.totalBytes)}MB used, limit is ${this.toMb(this.globalConfig.dataTable.maxSize)}MB`, + throw new DataTableValidationError( + `Data table size limit exceeded: ${this.toMb(size.totalBytes)}MB used, limit is ${this.toMb(this.globalConfig.dataTable.maxSize)}MB`, ); } } diff --git a/packages/cli/src/modules/data-table/data-store.controller.ts b/packages/cli/src/modules/data-table/data-table.controller.ts similarity index 59% rename from packages/cli/src/modules/data-table/data-store.controller.ts rename to packages/cli/src/modules/data-table/data-table.controller.ts index c6c27cb60ab..161449dc13f 100644 --- a/packages/cli/src/modules/data-table/data-store.controller.ts +++ b/packages/cli/src/modules/data-table/data-table.controller.ts @@ -1,14 +1,14 @@ import { - AddDataStoreRowsDto, - AddDataStoreColumnDto, - CreateDataStoreDto, + AddDataTableRowsDto, + AddDataTableColumnDto, + CreateDataTableDto, DeleteDataTableRowsDto, - ListDataStoreContentQueryDto, - ListDataStoreQueryDto, - MoveDataStoreColumnDto, - UpdateDataStoreDto, + ListDataTableContentQueryDto, + ListDataTableQueryDto, + MoveDataTableColumnDto, + UpdateDataTableDto, UpdateDataTableRowDto, - UpsertDataStoreRowDto, + UpsertDataTableRowDto, } from '@n8n/api-types'; import { AuthenticatedRequest } from '@n8n/db'; import { @@ -22,38 +22,38 @@ import { Query, RestController, } from '@n8n/decorators'; -import { DataStoreRowReturn } from 'n8n-workflow'; +import { DataTableRowReturn } from 'n8n-workflow'; import { BadRequestError } from '@/errors/response-errors/bad-request.error'; import { ConflictError } from '@/errors/response-errors/conflict.error'; import { InternalServerError } from '@/errors/response-errors/internal-server.error'; import { NotFoundError } from '@/errors/response-errors/not-found.error'; -import { DataStoreService } from './data-store.service'; -import { DataStoreColumnNameConflictError } from './errors/data-store-column-name-conflict.error'; -import { DataStoreColumnNotFoundError } from './errors/data-store-column-not-found.error'; -import { DataStoreNameConflictError } from './errors/data-store-name-conflict.error'; -import { DataStoreNotFoundError } from './errors/data-store-not-found.error'; -import { DataStoreSystemColumnNameConflictError } from './errors/data-store-system-column-name-conflict.error'; -import { DataStoreValidationError } from './errors/data-store-validation.error'; +import { DataTableService } from './data-table.service'; +import { DataTableColumnNameConflictError } from './errors/data-table-column-name-conflict.error'; +import { DataTableColumnNotFoundError } from './errors/data-table-column-not-found.error'; +import { DataTableNameConflictError } from './errors/data-table-name-conflict.error'; +import { DataTableNotFoundError } from './errors/data-table-not-found.error'; +import { DataTableSystemColumnNameConflictError } from './errors/data-table-system-column-name-conflict.error'; +import { DataTableValidationError } from './errors/data-table-validation.error'; @RestController('/projects/:projectId/data-tables') -export class DataStoreController { - constructor(private readonly dataStoreService: DataStoreService) {} +export class DataTableController { + constructor(private readonly dataTableService: DataTableService) {} @Post('/') @ProjectScope('dataStore:create') - async createDataStore( + async createDataTable( req: AuthenticatedRequest<{ projectId: string }>, _res: Response, - @Body dto: CreateDataStoreDto, + @Body dto: CreateDataTableDto, ) { try { - return await this.dataStoreService.createDataStore(req.params.projectId, dto); + return await this.dataTableService.createDataTable(req.params.projectId, dto); } catch (e: unknown) { if (!(e instanceof Error)) { throw e; - } else if (e instanceof DataStoreNameConflictError) { + } else if (e instanceof DataTableNameConflictError) { throw new ConflictError(e.message); } else { throw new InternalServerError(e.message, e); @@ -63,32 +63,32 @@ export class DataStoreController { @Get('/') @ProjectScope('dataStore:listProject') - async listProjectDataStores( + async listProjectDataTables( req: AuthenticatedRequest<{ projectId: string }>, _res: Response, - @Query payload: ListDataStoreQueryDto, + @Query payload: ListDataTableQueryDto, ) { const providedFilter = payload?.filter ?? {}; - return await this.dataStoreService.getManyAndCount({ + return await this.dataTableService.getManyAndCount({ ...payload, filter: { ...providedFilter, projectId: req.params.projectId }, }); } - @Patch('/:dataStoreId') + @Patch('/:dataTableId') @ProjectScope('dataStore:update') - async updateDataStore( + async updateDataTable( req: AuthenticatedRequest<{ projectId: string }>, _res: Response, - @Param('dataStoreId') dataStoreId: string, - @Body dto: UpdateDataStoreDto, + @Param('dataTableId') dataTableId: string, + @Body dto: UpdateDataTableDto, ) { try { - return await this.dataStoreService.updateDataStore(dataStoreId, req.params.projectId, dto); + return await this.dataTableService.updateDataTable(dataTableId, req.params.projectId, dto); } catch (e: unknown) { - if (e instanceof DataStoreNotFoundError) { + if (e instanceof DataTableNotFoundError) { throw new NotFoundError(e.message); - } else if (e instanceof DataStoreNameConflictError) { + } else if (e instanceof DataTableNameConflictError) { throw new ConflictError(e.message); } else if (e instanceof Error) { throw new InternalServerError(e.message, e); @@ -98,17 +98,17 @@ export class DataStoreController { } } - @Delete('/:dataStoreId') + @Delete('/:dataTableId') @ProjectScope('dataStore:delete') - async deleteDataStore( + async deleteDataTable( req: AuthenticatedRequest<{ projectId: string }>, _res: Response, - @Param('dataStoreId') dataStoreId: string, + @Param('dataTableId') dataTableId: string, ) { try { - return await this.dataStoreService.deleteDataStore(dataStoreId, req.params.projectId); + return await this.dataTableService.deleteDataTable(dataTableId, req.params.projectId); } catch (e: unknown) { - if (e instanceof DataStoreNotFoundError) { + if (e instanceof DataTableNotFoundError) { throw new NotFoundError(e.message); } else if (e instanceof Error) { throw new InternalServerError(e.message, e); @@ -118,17 +118,17 @@ export class DataStoreController { } } - @Get('/:dataStoreId/columns') + @Get('/:dataTableId/columns') @ProjectScope('dataStore:read') async getColumns( req: AuthenticatedRequest<{ projectId: string }>, _res: Response, - @Param('dataStoreId') dataStoreId: string, + @Param('dataTableId') dataTableId: string, ) { try { - return await this.dataStoreService.getColumns(dataStoreId, req.params.projectId); + return await this.dataTableService.getColumns(dataTableId, req.params.projectId); } catch (e: unknown) { - if (e instanceof DataStoreNotFoundError) { + if (e instanceof DataTableNotFoundError) { throw new NotFoundError(e.message); } else if (e instanceof Error) { throw new InternalServerError(e.message, e); @@ -138,22 +138,22 @@ export class DataStoreController { } } - @Post('/:dataStoreId/columns') + @Post('/:dataTableId/columns') @ProjectScope('dataStore:update') async addColumn( req: AuthenticatedRequest<{ projectId: string }>, _res: Response, - @Param('dataStoreId') dataStoreId: string, - @Body dto: AddDataStoreColumnDto, + @Param('dataTableId') dataTableId: string, + @Body dto: AddDataTableColumnDto, ) { try { - return await this.dataStoreService.addColumn(dataStoreId, req.params.projectId, dto); + return await this.dataTableService.addColumn(dataTableId, req.params.projectId, dto); } catch (e: unknown) { - if (e instanceof DataStoreNotFoundError) { + if (e instanceof DataTableNotFoundError) { throw new NotFoundError(e.message); } else if ( - e instanceof DataStoreColumnNameConflictError || - e instanceof DataStoreSystemColumnNameConflictError + e instanceof DataTableColumnNameConflictError || + e instanceof DataTableSystemColumnNameConflictError ) { throw new ConflictError(e.message); } else if (e instanceof Error) { @@ -164,18 +164,18 @@ export class DataStoreController { } } - @Delete('/:dataStoreId/columns/:columnId') + @Delete('/:dataTableId/columns/:columnId') @ProjectScope('dataStore:update') async deleteColumn( req: AuthenticatedRequest<{ projectId: string }>, _res: Response, - @Param('dataStoreId') dataStoreId: string, + @Param('dataTableId') dataTableId: string, @Param('columnId') columnId: string, ) { try { - return await this.dataStoreService.deleteColumn(dataStoreId, req.params.projectId, columnId); + return await this.dataTableService.deleteColumn(dataTableId, req.params.projectId, columnId); } catch (e: unknown) { - if (e instanceof DataStoreNotFoundError || e instanceof DataStoreColumnNotFoundError) { + if (e instanceof DataTableNotFoundError || e instanceof DataTableColumnNotFoundError) { throw new NotFoundError(e.message); } else if (e instanceof Error) { throw new InternalServerError(e.message, e); @@ -185,26 +185,26 @@ export class DataStoreController { } } - @Patch('/:dataStoreId/columns/:columnId/move') + @Patch('/:dataTableId/columns/:columnId/move') @ProjectScope('dataStore:update') async moveColumn( req: AuthenticatedRequest<{ projectId: string }>, _res: Response, - @Param('dataStoreId') dataStoreId: string, + @Param('dataTableId') dataTableId: string, @Param('columnId') columnId: string, - @Body dto: MoveDataStoreColumnDto, + @Body dto: MoveDataTableColumnDto, ) { try { - return await this.dataStoreService.moveColumn( - dataStoreId, + return await this.dataTableService.moveColumn( + dataTableId, req.params.projectId, columnId, dto, ); } catch (e: unknown) { - if (e instanceof DataStoreNotFoundError || e instanceof DataStoreColumnNotFoundError) { + if (e instanceof DataTableNotFoundError || e instanceof DataTableColumnNotFoundError) { throw new NotFoundError(e.message); - } else if (e instanceof DataStoreValidationError) { + } else if (e instanceof DataTableValidationError) { throw new BadRequestError(e.message); } else if (e instanceof Error) { throw new InternalServerError(e.message, e); @@ -214,22 +214,22 @@ export class DataStoreController { } } - @Get('/:dataStoreId/rows') + @Get('/:dataTableId/rows') @ProjectScope('dataStore:readRow') - async getDataStoreRows( + async getDataTableRows( req: AuthenticatedRequest<{ projectId: string }>, _res: Response, - @Param('dataStoreId') dataStoreId: string, - @Query dto: ListDataStoreContentQueryDto, + @Param('dataTableId') dataTableId: string, + @Query dto: ListDataTableContentQueryDto, ) { try { - return await this.dataStoreService.getManyRowsAndCount( - dataStoreId, + return await this.dataTableService.getManyRowsAndCount( + dataTableId, req.params.projectId, dto, ); } catch (e: unknown) { - if (e instanceof DataStoreNotFoundError) { + if (e instanceof DataTableNotFoundError) { throw new NotFoundError(e.message); } else if (e instanceof Error) { throw new InternalServerError(e.message, e); @@ -242,31 +242,31 @@ export class DataStoreController { /** * @returns the IDs of the inserted rows */ - async appendDataStoreRows( + async appendDataTableRows( req: AuthenticatedRequest<{ projectId: string }>, _res: Response, - dataStoreId: string, - dto: AddDataStoreRowsDto & { returnType?: T }, - ): Promise>>; - @Post('/:dataStoreId/insert') + dataTableId: string, + dto: AddDataTableRowsDto & { returnType?: T }, + ): Promise>>; + @Post('/:dataTableId/insert') @ProjectScope('dataStore:writeRow') - async appendDataStoreRows( + async appendDataTableRows( req: AuthenticatedRequest<{ projectId: string }>, _res: Response, - @Param('dataStoreId') dataStoreId: string, - @Body dto: AddDataStoreRowsDto, + @Param('dataTableId') dataTableId: string, + @Body dto: AddDataTableRowsDto, ) { try { - return await this.dataStoreService.insertRows( - dataStoreId, + return await this.dataTableService.insertRows( + dataTableId, req.params.projectId, dto.data, dto.returnType, ); } catch (e: unknown) { - if (e instanceof DataStoreNotFoundError) { + if (e instanceof DataTableNotFoundError) { throw new NotFoundError(e.message); - } else if (e instanceof DataStoreValidationError) { + } else if (e instanceof DataTableValidationError) { throw new BadRequestError(e.message); } else if (e instanceof Error) { throw new InternalServerError(e.message, e); @@ -276,20 +276,20 @@ export class DataStoreController { } } - @Post('/:dataStoreId/upsert') + @Post('/:dataTableId/upsert') @ProjectScope('dataStore:writeRow') - async upsertDataStoreRow( + async upsertDataTableRow( req: AuthenticatedRequest<{ projectId: string }>, _res: Response, - @Param('dataStoreId') dataStoreId: string, - @Body dto: UpsertDataStoreRowDto, + @Param('dataTableId') dataTableId: string, + @Body dto: UpsertDataTableRowDto, ) { try { // because of strict overloads, we need separate paths const dryRun = dto.dryRun; if (dryRun) { - return await this.dataStoreService.upsertRow( - dataStoreId, + return await this.dataTableService.upsertRow( + dataTableId, req.params.projectId, dto, true, // we want to always return data for dry runs @@ -299,8 +299,8 @@ export class DataStoreController { const returnData = dto.returnData; if (returnData) { - return await this.dataStoreService.upsertRow( - dataStoreId, + return await this.dataTableService.upsertRow( + dataTableId, req.params.projectId, dto, returnData, @@ -308,17 +308,17 @@ export class DataStoreController { ); } - return await this.dataStoreService.upsertRow( - dataStoreId, + return await this.dataTableService.upsertRow( + dataTableId, req.params.projectId, dto, returnData, dryRun, ); } catch (e: unknown) { - if (e instanceof DataStoreNotFoundError) { + if (e instanceof DataTableNotFoundError) { throw new NotFoundError(e.message); - } else if (e instanceof DataStoreValidationError) { + } else if (e instanceof DataTableValidationError) { throw new BadRequestError(e.message); } else if (e instanceof Error) { throw new InternalServerError(e.message, e); @@ -328,20 +328,20 @@ export class DataStoreController { } } - @Patch('/:dataStoreId/rows') + @Patch('/:dataTableId/rows') @ProjectScope('dataStore:writeRow') - async updateDataStoreRows( + async updateDataTableRows( req: AuthenticatedRequest<{ projectId: string }>, _res: Response, - @Param('dataStoreId') dataStoreId: string, + @Param('dataTableId') dataTableId: string, @Body dto: UpdateDataTableRowDto, ) { try { // because of strict overloads, we need separate paths const dryRun = dto.dryRun; if (dryRun) { - return await this.dataStoreService.updateRows( - dataStoreId, + return await this.dataTableService.updateRows( + dataTableId, req.params.projectId, dto, true, // we want to always return data for dry runs @@ -351,8 +351,8 @@ export class DataStoreController { const returnData = dto.returnData; if (returnData) { - return await this.dataStoreService.updateRows( - dataStoreId, + return await this.dataTableService.updateRows( + dataTableId, req.params.projectId, dto, returnData, @@ -360,17 +360,17 @@ export class DataStoreController { ); } - return await this.dataStoreService.updateRows( - dataStoreId, + return await this.dataTableService.updateRows( + dataTableId, req.params.projectId, dto, returnData, dryRun, ); } catch (e: unknown) { - if (e instanceof DataStoreNotFoundError) { + if (e instanceof DataTableNotFoundError) { throw new NotFoundError(e.message); - } else if (e instanceof DataStoreValidationError) { + } else if (e instanceof DataTableValidationError) { throw new BadRequestError(e.message); } else if (e instanceof Error) { throw new InternalServerError(e.message, e); @@ -389,16 +389,16 @@ export class DataStoreController { @Query dto: DeleteDataTableRowsDto, ) { try { - return await this.dataStoreService.deleteRows( + return await this.dataTableService.deleteRows( dataTableId, req.params.projectId, dto, dto.returnData, ); } catch (e: unknown) { - if (e instanceof DataStoreNotFoundError) { + if (e instanceof DataTableNotFoundError) { throw new NotFoundError(e.message); - } else if (e instanceof DataStoreValidationError) { + } else if (e instanceof DataTableValidationError) { throw new BadRequestError(e.message); } else if (e instanceof Error) { throw new InternalServerError(e.message, e); diff --git a/packages/cli/src/modules/data-table/data-table.module.ts b/packages/cli/src/modules/data-table/data-table.module.ts index 82ece531b27..d6d0fcae29a 100644 --- a/packages/cli/src/modules/data-table/data-table.module.ts +++ b/packages/cli/src/modules/data-table/data-table.module.ts @@ -5,23 +5,23 @@ import { Container } from '@n8n/di'; @BackendModule({ name: 'data-table' }) export class DataStoreModule implements ModuleInterface { async init() { - await import('./data-store.controller'); - await import('./data-store-aggregate.controller'); + await import('./data-table.controller'); + await import('./data-table-aggregate.controller'); - const { DataStoreService } = await import('./data-store.service'); - await Container.get(DataStoreService).start(); + const { DataTableService } = await import('./data-table.service'); + await Container.get(DataTableService).start(); - const { DataStoreAggregateService } = await import('./data-store-aggregate.service'); - await Container.get(DataStoreAggregateService).start(); + const { DataTableAggregateService } = await import('./data-table-aggregate.service'); + await Container.get(DataTableAggregateService).start(); } @OnShutdown() async shutdown() { - const { DataStoreService } = await import('./data-store.service'); - await Container.get(DataStoreService).shutdown(); + const { DataTableService } = await import('./data-table.service'); + await Container.get(DataTableService).shutdown(); - const { DataStoreAggregateService } = await import('./data-store-aggregate.service'); - await Container.get(DataStoreAggregateService).start(); + const { DataTableAggregateService } = await import('./data-table-aggregate.service'); + await Container.get(DataTableAggregateService).shutdown(); } async entities() { @@ -32,8 +32,8 @@ export class DataStoreModule implements ModuleInterface { } async context() { - const { DataStoreProxyService } = await import('./data-store-proxy.service'); + const { DataTableProxyService } = await import('./data-table-proxy.service'); - return { dataStoreProxyProvider: Container.get(DataStoreProxyService) }; + return { dataTableProxyProvider: Container.get(DataTableProxyService) }; } } diff --git a/packages/cli/src/modules/data-table/data-store.repository.ts b/packages/cli/src/modules/data-table/data-table.repository.ts similarity index 78% rename from packages/cli/src/modules/data-table/data-store.repository.ts rename to packages/cli/src/modules/data-table/data-table.repository.ts index b308709a0ea..202815d2a2e 100644 --- a/packages/cli/src/modules/data-table/data-store.repository.ts +++ b/packages/cli/src/modules/data-table/data-table.repository.ts @@ -1,7 +1,7 @@ import { - DATA_STORE_COLUMN_ERROR_MESSAGE, - type DataStoreCreateColumnSchema, - type ListDataStoreQueryDto, + DATA_TABLE_COLUMN_ERROR_MESSAGE, + type DataTableCreateColumnSchema, + type ListDataTableQueryDto, } from '@n8n/api-types'; import { GlobalConfig } from '@n8n/config'; import { Project, withTransaction } from '@n8n/db'; @@ -10,40 +10,40 @@ import { DataSource, EntityManager, Repository, SelectQueryBuilder } from '@n8n/ import { UnexpectedError } from 'n8n-workflow'; import type { DataTableInfo, DataTablesSizeData } from 'n8n-workflow'; -import { DataStoreRowsRepository } from './data-store-rows.repository'; -import { DataStoreUserTableName } from './data-store.types'; import { DataTableColumn } from './data-table-column.entity'; +import { DataTableRowsRepository } from './data-table-rows.repository'; import { DataTable } from './data-table.entity'; -import { DataStoreNameConflictError } from './errors/data-store-name-conflict.error'; -import { DataStoreValidationError } from './errors/data-store-validation.error'; +import { DataTableUserTableName } from './data-table.types'; +import { DataTableNameConflictError } from './errors/data-table-name-conflict.error'; +import { DataTableValidationError } from './errors/data-table-validation.error'; import { isValidColumnName, toTableId, toTableName } from './utils/sql-utils'; @Service() -export class DataStoreRepository extends Repository { +export class DataTableRepository extends Repository { constructor( dataSource: DataSource, - private dataStoreRowsRepository: DataStoreRowsRepository, + private dataTableRowsRepository: DataTableRowsRepository, private readonly globalConfig: GlobalConfig, ) { super(DataTable, dataSource.manager); } - async createDataStore( + async createDataTable( projectId: string, name: string, - columns: DataStoreCreateColumnSchema[], + columns: DataTableCreateColumnSchema[], trx?: EntityManager, ) { return await withTransaction(this.manager, trx, async (em) => { if (columns.some((c) => !isValidColumnName(c.name))) { - throw new DataStoreValidationError(DATA_STORE_COLUMN_ERROR_MESSAGE); + throw new DataTableValidationError(DATA_TABLE_COLUMN_ERROR_MESSAGE); } - const dataStore = em.create(DataTable, { name, columns, projectId }); + const dataTable = em.create(DataTable, { name, columns, projectId }); // @ts-ignore Workaround for intermittent typecheck issue with _QueryDeepPartialEntity - await em.insert(DataTable, dataStore); - const dataTableId = dataStore.id; + await em.insert(DataTable, dataTable); + const dataTableId = dataTable.id; // insert columns const columnEntities = columns.map((col, index) => @@ -61,30 +61,30 @@ export class DataStoreRepository extends Repository { } // create user table (will create empty table with just id column if no columns) - await this.dataStoreRowsRepository.createTableWithColumns(dataTableId, columnEntities, em); + await this.dataTableRowsRepository.createTableWithColumns(dataTableId, columnEntities, em); if (!dataTableId) { - throw new UnexpectedError('Data store creation failed'); + throw new UnexpectedError('Data table creation failed'); } - const createdDataStore = await em.findOneOrFail(DataTable, { + const createdDataTable = await em.findOneOrFail(DataTable, { where: { id: dataTableId }, relations: ['project', 'columns'], }); - return createdDataStore; + return createdDataTable; }); } - async deleteDataStore(dataStoreId: string, trx?: EntityManager) { + async deleteDataTable(dataTableId: string, trx?: EntityManager) { return await withTransaction(this.manager, trx, async (em) => { - await em.delete(DataTable, { id: dataStoreId }); - await this.dataStoreRowsRepository.dropTable(dataStoreId, em); + await em.delete(DataTable, { id: dataTableId }); + await this.dataTableRowsRepository.dropTable(dataTableId, em); return true; }); } - async transferDataStoreByProjectId( + async transferDataTableByProjectId( fromProjectId: string, toProjectId: string, trx?: EntityManager, @@ -112,8 +112,8 @@ export class DataStoreRepository extends Repository { }); if (stillHasNameClash) { - throw new DataStoreNameConflictError( - `Failed to transfer data store "${existing.name}" to the target project "${toProjectId}". A data table with the same name already exists in the target project.`, + throw new 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.`, ); } } @@ -126,13 +126,13 @@ export class DataStoreRepository extends Repository { }); } - async deleteDataStoreByProjectId(projectId: string, trx?: EntityManager) { + async deleteDataTableByProjectId(projectId: string, trx?: EntityManager) { return await withTransaction(this.manager, trx, async (em) => { const existingTables = await em.findBy(DataTable, { projectId }); let changed = false; for (const match of existingTables) { - const result = await this.deleteDataStore(match.id, em); + const result = await this.deleteDataTable(match.id, em); changed = changed || result; } @@ -140,14 +140,14 @@ export class DataStoreRepository extends Repository { }); } - async deleteDataStoreAll(trx?: EntityManager) { + async deleteDataTableAll(trx?: EntityManager) { return await withTransaction(this.manager, trx, async (em) => { const existingTables = await em.findBy(DataTable, {}); let changed = false; for (const match of existingTables) { const result = await em.delete(DataTable, { id: match.id }); - await this.dataStoreRowsRepository.dropTable(match.id, em); + await this.dataTableRowsRepository.dropTable(match.id, em); changed = changed || (result.affected ?? 0) > 0; } @@ -155,19 +155,19 @@ export class DataStoreRepository extends Repository { }); } - async getManyAndCount(options: Partial) { + async getManyAndCount(options: Partial) { const query = this.getManyQuery(options); const [data, count] = await query.getManyAndCount(); return { count, data }; } - async getMany(options: Partial) { + async getMany(options: Partial) { const query = this.getManyQuery(options); return await query.getMany(); } - private getManyQuery(options: Partial): SelectQueryBuilder { - const query = this.createQueryBuilder('dataStore'); + private getManyQuery(options: Partial): SelectQueryBuilder { + const query = this.createQueryBuilder('dataTable'); this.applySelections(query); this.applyFilters(query, options.filter); @@ -183,13 +183,13 @@ export class DataStoreRepository extends Repository { private applyFilters( query: SelectQueryBuilder, - filter: Partial['filter'], + filter: Partial['filter'], ): void { for (const x of ['id', 'projectId'] as const) { const content = [filter?.[x]].flat().filter((x) => x !== undefined); if (content.length === 0) continue; - query.andWhere(`dataStore.${x} IN (:...${x}s)`, { + query.andWhere(`dataTable.${x} IN (:...${x}s)`, { /* * If list is empty, add a dummy value to prevent an error * when using the IN operator with an empty array. @@ -202,7 +202,7 @@ export class DataStoreRepository extends Repository { const nameFilters = Array.isArray(filter.name) ? filter.name : [filter.name]; nameFilters.forEach((name, i) => { - query.andWhere(`LOWER(dataStore.name) LIKE LOWER(:name${i})`, { + query.andWhere(`LOWER(dataTable.name) LIKE LOWER(:name${i})`, { ['name' + i]: `%${name}%`, }); }); @@ -211,7 +211,7 @@ export class DataStoreRepository extends Repository { private applySorting(query: SelectQueryBuilder, sortBy?: string): void { if (!sortBy) { - query.orderBy('dataStore.updatedAt', 'DESC'); + query.orderBy('dataTable.updatedAt', 'DESC'); return; } @@ -231,16 +231,16 @@ export class DataStoreRepository extends Repository { ): void { if (field === 'name') { query - .addSelect('LOWER(dataStore.name)', 'datastore_name_lower') - .orderBy('datastore_name_lower', direction); + .addSelect('LOWER(dataTable.name)', 'datatable_name_lower') + .orderBy('datatable_name_lower', direction); } else if (['createdAt', 'updatedAt'].includes(field)) { - query.orderBy(`dataStore.${field}`, direction); + query.orderBy(`dataTable.${field}`, direction); } } private applyPagination( query: SelectQueryBuilder, - options: Partial, + options: Partial, ): void { query.skip(options.skip ?? 0); if (options.take !== undefined) query.take(options.take); @@ -248,16 +248,16 @@ export class DataStoreRepository extends Repository { private applyDefaultSelect(query: SelectQueryBuilder): void { query - .leftJoinAndSelect('dataStore.project', 'project') - .leftJoinAndSelect('dataStore.columns', 'data_store_column') + .leftJoinAndSelect('dataTable.project', 'project') + .leftJoinAndSelect('dataTable.columns', 'data_table_column') .select([ - 'dataStore', - ...this.getDataStoreColumnFields('data_store_column'), + 'dataTable', + ...this.getDataTableColumnFields('data_table_column'), ...this.getProjectFields('project'), ]); } - private getDataStoreColumnFields(alias: string): string[] { + private getDataTableColumnFields(alias: string): string[] { return [ `${alias}.id`, `${alias}.name`, @@ -377,9 +377,9 @@ export class DataStoreRepository extends Repository { for (const row of result) { if (row.table_bytes !== null && row.table_name) { - const dataStoreId = toTableId(row.table_name as DataStoreUserTableName); + const dataTableId = toTableId(row.table_name as DataTableUserTableName); const sizeBytes = this.parseSize(row.table_bytes); - sizeMap.set(dataStoreId, (sizeMap.get(dataStoreId) ?? 0) + sizeBytes); + sizeMap.set(dataTableId, (sizeMap.get(dataTableId) ?? 0) + sizeBytes); } } diff --git a/packages/cli/src/modules/data-table/data-store.service.ts b/packages/cli/src/modules/data-table/data-table.service.ts similarity index 57% rename from packages/cli/src/modules/data-table/data-store.service.ts rename to packages/cli/src/modules/data-table/data-table.service.ts index 54f0d5ae184..b9e8771d3f7 100644 --- a/packages/cli/src/modules/data-table/data-store.service.ts +++ b/packages/cli/src/modules/data-table/data-table.service.ts @@ -1,12 +1,12 @@ import type { - AddDataStoreColumnDto, - CreateDataStoreDto, + AddDataTableColumnDto, + CreateDataTableDto, DeleteDataTableRowsDto, - ListDataStoreContentQueryDto, - MoveDataStoreColumnDto, - DataStoreListOptions, - UpsertDataStoreRowDto, - UpdateDataStoreDto, + ListDataTableContentQueryDto, + MoveDataTableColumnDto, + DataTableListOptions, + UpsertDataTableRowDto, + UpdateDataTableDto, UpdateDataTableRowDto, } from '@n8n/api-types'; import { Logger } from '@n8n/backend-common'; @@ -14,42 +14,42 @@ import { ProjectRelationRepository, type User } from '@n8n/db'; import { Service } from '@n8n/di'; import { DateTime } from 'luxon'; import type { - DataStoreColumnJsType, + DataTableColumnJsType, DataTableFilter, - DataStoreRow, - DataStoreRowReturn, - DataStoreRows, + DataTableRow, + DataTableRowReturn, + DataTableRows, DataTableInsertRowsReturnType, DataTableInsertRowsResult, DataTablesSizeResult, DataTableInfoById, - DataStoreColumnType, - DataStoreRowReturnWithState, + DataTableColumnType, + DataTableRowReturnWithState, } from 'n8n-workflow'; import { DATA_TABLE_SYSTEM_COLUMN_TYPE_MAP, validateFieldType } from 'n8n-workflow'; import { RoleService } from '@/services/role.service'; -import { DataStoreColumnRepository } from './data-store-column.repository'; -import { DataStoreRowsRepository } from './data-store-rows.repository'; -import { DataStoreSizeValidator } from './data-store-size-validator.service'; -import { DataStoreRepository } from './data-store.repository'; -import { columnTypeToFieldType } from './data-store.types'; import { DataTableColumn } from './data-table-column.entity'; -import { DataStoreColumnNotFoundError } from './errors/data-store-column-not-found.error'; -import { DataStoreNameConflictError } from './errors/data-store-name-conflict.error'; -import { DataStoreNotFoundError } from './errors/data-store-not-found.error'; -import { DataStoreValidationError } from './errors/data-store-validation.error'; +import { DataTableColumnRepository } from './data-table-column.repository'; +import { DataTableRowsRepository } from './data-table-rows.repository'; +import { DataTableSizeValidator } from './data-table-size-validator.service'; +import { DataTableRepository } from './data-table.repository'; +import { columnTypeToFieldType } from './data-table.types'; +import { DataTableColumnNotFoundError } from './errors/data-table-column-not-found.error'; +import { DataTableNameConflictError } from './errors/data-table-name-conflict.error'; +import { DataTableNotFoundError } from './errors/data-table-not-found.error'; +import { DataTableValidationError } from './errors/data-table-validation.error'; import { normalizeRows } from './utils/sql-utils'; @Service() -export class DataStoreService { +export class DataTableService { constructor( - private readonly dataStoreRepository: DataStoreRepository, - private readonly dataStoreColumnRepository: DataStoreColumnRepository, - private readonly dataStoreRowsRepository: DataStoreRowsRepository, + private readonly dataTableRepository: DataTableRepository, + private readonly dataTableColumnRepository: DataTableColumnRepository, + private readonly dataTableRowsRepository: DataTableRowsRepository, private readonly logger: Logger, - private readonly dataStoreSizeValidator: DataStoreSizeValidator, + private readonly dataTableSizeValidator: DataTableSizeValidator, private readonly projectRelationRepository: ProjectRelationRepository, private readonly roleService: RoleService, ) { @@ -59,107 +59,107 @@ export class DataStoreService { async start() {} async shutdown() {} - async createDataStore(projectId: string, dto: CreateDataStoreDto) { + async createDataTable(projectId: string, dto: CreateDataTableDto) { await this.validateUniqueName(dto.name, projectId); - const result = await this.dataStoreRepository.createDataStore(projectId, dto.name, dto.columns); + const result = await this.dataTableRepository.createDataTable(projectId, dto.name, dto.columns); - this.dataStoreSizeValidator.reset(); + this.dataTableSizeValidator.reset(); return result; } - // Updates data store properties (currently limited to renaming) - async updateDataStore(dataStoreId: string, projectId: string, dto: UpdateDataStoreDto) { - await this.validateDataStoreExists(dataStoreId, projectId); + // Updates data table properties (currently limited to renaming) + async updateDataTable(dataTableId: string, projectId: string, dto: UpdateDataTableDto) { + await this.validateDataTableExists(dataTableId, projectId); await this.validateUniqueName(dto.name, projectId); - await this.dataStoreRepository.update({ id: dataStoreId }, { name: dto.name }); + await this.dataTableRepository.update({ id: dataTableId }, { name: dto.name }); return true; } - async transferDataStoresByProjectId(fromProjectId: string, toProjectId: string) { - return await this.dataStoreRepository.transferDataStoreByProjectId(fromProjectId, toProjectId); + async transferDataTablesByProjectId(fromProjectId: string, toProjectId: string) { + return await this.dataTableRepository.transferDataTableByProjectId(fromProjectId, toProjectId); } - async deleteDataStoreByProjectId(projectId: string) { - const result = await this.dataStoreRepository.deleteDataStoreByProjectId(projectId); + async deleteDataTableByProjectId(projectId: string) { + const result = await this.dataTableRepository.deleteDataTableByProjectId(projectId); if (result) { - this.dataStoreSizeValidator.reset(); + this.dataTableSizeValidator.reset(); } return result; } - async deleteDataStoreAll() { - const result = await this.dataStoreRepository.deleteDataStoreAll(); + async deleteDataTableAll() { + const result = await this.dataTableRepository.deleteDataTableAll(); if (result) { - this.dataStoreSizeValidator.reset(); + this.dataTableSizeValidator.reset(); } return result; } - async deleteDataStore(dataStoreId: string, projectId: string) { - await this.validateDataStoreExists(dataStoreId, projectId); + async deleteDataTable(dataTableId: string, projectId: string) { + await this.validateDataTableExists(dataTableId, projectId); - await this.dataStoreRepository.deleteDataStore(dataStoreId); + await this.dataTableRepository.deleteDataTable(dataTableId); - this.dataStoreSizeValidator.reset(); + this.dataTableSizeValidator.reset(); return true; } - async addColumn(dataStoreId: string, projectId: string, dto: AddDataStoreColumnDto) { - await this.validateDataStoreExists(dataStoreId, projectId); + async addColumn(dataTableId: string, projectId: string, dto: AddDataTableColumnDto) { + await this.validateDataTableExists(dataTableId, projectId); - return await this.dataStoreColumnRepository.addColumn(dataStoreId, dto); + return await this.dataTableColumnRepository.addColumn(dataTableId, dto); } async moveColumn( - dataStoreId: string, + dataTableId: string, projectId: string, columnId: string, - dto: MoveDataStoreColumnDto, + dto: MoveDataTableColumnDto, ) { - await this.validateDataStoreExists(dataStoreId, projectId); - const existingColumn = await this.validateColumnExists(dataStoreId, columnId); + await this.validateDataTableExists(dataTableId, projectId); + const existingColumn = await this.validateColumnExists(dataTableId, columnId); - await this.dataStoreColumnRepository.moveColumn(dataStoreId, existingColumn, dto.targetIndex); + await this.dataTableColumnRepository.moveColumn(dataTableId, existingColumn, dto.targetIndex); return true; } - async deleteColumn(dataStoreId: string, projectId: string, columnId: string) { - await this.validateDataStoreExists(dataStoreId, projectId); - const existingColumn = await this.validateColumnExists(dataStoreId, columnId); + async deleteColumn(dataTableId: string, projectId: string, columnId: string) { + await this.validateDataTableExists(dataTableId, projectId); + const existingColumn = await this.validateColumnExists(dataTableId, columnId); - await this.dataStoreColumnRepository.deleteColumn(dataStoreId, existingColumn); + await this.dataTableColumnRepository.deleteColumn(dataTableId, existingColumn); return true; } - async getManyAndCount(options: DataStoreListOptions) { - return await this.dataStoreRepository.getManyAndCount(options); + async getManyAndCount(options: DataTableListOptions) { + return await this.dataTableRepository.getManyAndCount(options); } async getManyRowsAndCount( - dataStoreId: string, + dataTableId: string, projectId: string, - dto: ListDataStoreContentQueryDto, + dto: ListDataTableContentQueryDto, ) { - await this.validateDataStoreExists(dataStoreId, projectId); + await this.validateDataTableExists(dataTableId, projectId); - return await this.dataStoreColumnRepository.manager.transaction(async (em) => { - const columns = await this.dataStoreColumnRepository.getColumns(dataStoreId, em); + return await this.dataTableColumnRepository.manager.transaction(async (em) => { + const columns = await this.dataTableColumnRepository.getColumns(dataTableId, em); if (dto.filter) { this.validateAndTransformFilters(dto.filter, columns); } - const result = await this.dataStoreRowsRepository.getManyAndCount( - dataStoreId, + const result = await this.dataTableRowsRepository.getManyAndCount( + dataTableId, dto, columns, em, @@ -171,33 +171,33 @@ export class DataStoreService { }); } - async getColumns(dataStoreId: string, projectId: string) { - await this.validateDataStoreExists(dataStoreId, projectId); + async getColumns(dataTableId: string, projectId: string) { + await this.validateDataTableExists(dataTableId, projectId); - return await this.dataStoreColumnRepository.getColumns(dataStoreId); + return await this.dataTableColumnRepository.getColumns(dataTableId); } async insertRows( - dataStoreId: string, + dataTableId: string, projectId: string, - rows: DataStoreRows, + rows: DataTableRows, returnType?: T, ): Promise>; async insertRows( - dataStoreId: string, + dataTableId: string, projectId: string, - rows: DataStoreRows, + rows: DataTableRows, returnType: DataTableInsertRowsReturnType = 'count', ) { await this.validateDataTableSize(); - await this.validateDataStoreExists(dataStoreId, projectId); + await this.validateDataTableExists(dataTableId, projectId); - const result = await this.dataStoreColumnRepository.manager.transaction(async (trx) => { - const columns = await this.dataStoreColumnRepository.getColumns(dataStoreId, trx); + const result = await this.dataTableColumnRepository.manager.transaction(async (trx) => { + const columns = await this.dataTableColumnRepository.getColumns(dataTableId, trx); this.validateRowsWithColumns(rows, columns); - return await this.dataStoreRowsRepository.insertRows( - dataStoreId, + return await this.dataTableRowsRepository.insertRows( + dataTableId, rows, columns, returnType, @@ -205,7 +205,7 @@ export class DataStoreService { ); }); - this.dataStoreSizeValidator.reset(); + this.dataTableSizeValidator.reset(); return result; } @@ -213,40 +213,40 @@ export class DataStoreService { async upsertRow( dataTableId: string, projectId: string, - dto: Omit, + dto: Omit, returnData: true, dryRun?: boolean, - ): Promise; + ): Promise; async upsertRow( dataTableId: string, projectId: string, - dto: Omit, + dto: Omit, returnData?: boolean, dryRun?: true, - ): Promise; + ): Promise; async upsertRow( dataTableId: string, projectId: string, - dto: Omit, + dto: Omit, returnData?: false, dryRun?: false, ): Promise; async upsertRow( dataTableId: string, projectId: string, - dto: Omit, + dto: Omit, returnData: boolean = false, dryRun: boolean = false, ) { await this.validateDataTableSize(); - await this.validateDataStoreExists(dataTableId, projectId); + await this.validateDataTableExists(dataTableId, projectId); - const result = await this.dataStoreColumnRepository.manager.transaction(async (trx) => { - const columns = await this.dataStoreColumnRepository.getColumns(dataTableId, trx); + const result = await this.dataTableColumnRepository.manager.transaction(async (trx) => { + const columns = await this.dataTableColumnRepository.getColumns(dataTableId, trx); this.validateUpdateParams(dto, columns); if (dryRun) { - return await this.dataStoreRowsRepository.dryRunUpsertRow( + return await this.dataTableRowsRepository.dryRunUpsertRow( dataTableId, dto.data, dto.filter, @@ -255,7 +255,7 @@ export class DataStoreService { ); } - const updated = await this.dataStoreRowsRepository.updateRows( + const updated = await this.dataTableRowsRepository.updateRows( dataTableId, dto.data, dto.filter, @@ -269,7 +269,7 @@ export class DataStoreService { } // No rows were updated, so insert a new one - const inserted = await this.dataStoreRowsRepository.insertRows( + const inserted = await this.dataTableRowsRepository.insertRows( dataTableId, [dto.data], columns, @@ -280,7 +280,7 @@ export class DataStoreService { }); if (!dryRun) { - this.dataStoreSizeValidator.reset(); + this.dataTableSizeValidator.reset(); } return result; @@ -291,16 +291,16 @@ export class DataStoreService { columns: DataTableColumn[], ) { if (columns.length === 0) { - throw new DataStoreValidationError( + throw new DataTableValidationError( 'No columns found for this data table or data table not found', ); } if (!filter?.filters || filter.filters.length === 0) { - throw new DataStoreValidationError('Filter must not be empty'); + throw new DataTableValidationError('Filter must not be empty'); } if (!data || Object.keys(data).length === 0) { - throw new DataStoreValidationError('Data columns must not be empty'); + throw new DataTableValidationError('Data columns must not be empty'); } this.validateRowsWithColumns([data], columns, false); @@ -313,14 +313,14 @@ export class DataStoreService { dto: Omit, returnData: true, dryRun?: boolean, - ): Promise; + ): Promise; async updateRows( dataTableId: string, projectId: string, dto: Omit, returnData?: boolean, dryRun?: true, - ): Promise; + ): Promise; async updateRows( dataTableId: string, projectId: string, @@ -336,14 +336,14 @@ export class DataStoreService { dryRun: boolean = false, ) { await this.validateDataTableSize(); - await this.validateDataStoreExists(dataTableId, projectId); + await this.validateDataTableExists(dataTableId, projectId); - const result = await this.dataStoreColumnRepository.manager.transaction(async (trx) => { - const columns = await this.dataStoreColumnRepository.getColumns(dataTableId, trx); + const result = await this.dataTableColumnRepository.manager.transaction(async (trx) => { + const columns = await this.dataTableColumnRepository.getColumns(dataTableId, trx); this.validateUpdateParams(dto, columns); if (dryRun) { - return await this.dataStoreRowsRepository.dryRunUpdateRows( + return await this.dataTableRowsRepository.dryRunUpdateRows( dataTableId, dto.data, dto.filter, @@ -352,7 +352,7 @@ export class DataStoreService { ); } - return await this.dataStoreRowsRepository.updateRows( + return await this.dataTableRowsRepository.updateRows( dataTableId, dto.data, dto.filter, @@ -363,55 +363,55 @@ export class DataStoreService { }); if (!dryRun) { - this.dataStoreSizeValidator.reset(); + this.dataTableSizeValidator.reset(); } return result; } async deleteRows( - dataStoreId: string, + dataTableId: string, projectId: string, dto: Omit, returnData: true, dryRun?: boolean, - ): Promise; + ): Promise; async deleteRows( - dataStoreId: string, + dataTableId: string, projectId: string, dto: Omit, returnData?: boolean, dryRun?: true, - ): Promise; + ): Promise; async deleteRows( - dataStoreId: string, + dataTableId: string, projectId: string, dto: Omit, returnData?: false, dryRun?: false, ): Promise; async deleteRows( - dataStoreId: string, + dataTableId: string, projectId: string, dto: Omit, returnData: boolean = false, dryRun: boolean = false, ) { - await this.validateDataStoreExists(dataStoreId, projectId); + await this.validateDataTableExists(dataTableId, projectId); - const result = await this.dataStoreColumnRepository.manager.transaction(async (trx) => { - const columns = await this.dataStoreColumnRepository.getColumns(dataStoreId, trx); + const result = await this.dataTableColumnRepository.manager.transaction(async (trx) => { + const columns = await this.dataTableColumnRepository.getColumns(dataTableId, trx); if (!dto.filter?.filters || dto.filter.filters.length === 0) { - throw new DataStoreValidationError( + throw new DataTableValidationError( 'Filter is required for delete operations to prevent accidental deletion of all data', ); } this.validateAndTransformFilters(dto.filter, columns); - return await this.dataStoreRowsRepository.deleteRows( - dataStoreId, + return await this.dataTableRowsRepository.deleteRows( + dataTableId, columns, dto.filter, returnData, @@ -421,15 +421,15 @@ export class DataStoreService { }); if (!dryRun) { - this.dataStoreSizeValidator.reset(); + this.dataTableSizeValidator.reset(); } return result; } private validateRowsWithColumns( - rows: DataStoreRows, - columns: Array<{ name: string; type: DataStoreColumnType }>, + rows: DataTableRows, + columns: Array<{ name: string; type: DataTableColumnType }>, includeSystemColumns = false, ): void { // Include system columns like 'id' if requested @@ -448,14 +448,14 @@ export class DataStoreService { const keys = Object.keys(row); for (const key of keys) { if (!columnNames.has(key)) { - throw new DataStoreValidationError(`unknown column name '${key}'`); + throw new DataTableValidationError(`unknown column name '${key}'`); } this.validateCell(row, key, columnTypeMap); } } } - private validateCell(row: DataStoreRow, key: string, columnTypeMap: Map) { + private validateCell(row: DataTableRow, key: string, columnTypeMap: Map) { const cell = row[key]; if (cell === null) return; @@ -471,7 +471,7 @@ export class DataStoreService { }); if (!validationResult.valid) { - throw new DataStoreValidationError( + throw new DataTableValidationError( `value '${String(cell)}' does not match column type '${columnType}': ${validationResult.errorMessage}`, ); } @@ -483,51 +483,51 @@ export class DataStoreService { row[key] = dateInISO; return; } catch { - throw new DataStoreValidationError( + throw new DataTableValidationError( `value '${String(cell)}' does not match column type 'date'`, ); } } - row[key] = validationResult.newValue as DataStoreColumnJsType; + row[key] = validationResult.newValue as DataTableColumnJsType; } - private async validateDataStoreExists(dataStoreId: string, projectId: string) { - const existingTable = await this.dataStoreRepository.findOneBy({ - id: dataStoreId, + private async validateDataTableExists(dataTableId: string, projectId: string) { + const existingTable = await this.dataTableRepository.findOneBy({ + id: dataTableId, project: { id: projectId, }, }); if (!existingTable) { - throw new DataStoreNotFoundError(dataStoreId); + throw new DataTableNotFoundError(dataTableId); } return existingTable; } private async validateColumnExists(dataTableId: string, columnId: string) { - const existingColumn = await this.dataStoreColumnRepository.findOneBy({ + const existingColumn = await this.dataTableColumnRepository.findOneBy({ id: columnId, dataTableId, }); if (existingColumn === null) { - throw new DataStoreColumnNotFoundError(dataTableId, columnId); + throw new DataTableColumnNotFoundError(dataTableId, columnId); } return existingColumn; } private async validateUniqueName(name: string, projectId: string) { - const hasNameClash = await this.dataStoreRepository.existsBy({ + const hasNameClash = await this.dataTableRepository.existsBy({ name, projectId, }); if (hasNameClash) { - throw new DataStoreNameConflictError(name); + throw new DataTableNameConflictError(name); } } @@ -548,12 +548,12 @@ export class DataStoreService { for (const filter of filterObject.filters) { if (['like', 'ilike'].includes(filter.condition)) { if (filter.value === null || filter.value === undefined) { - throw new DataStoreValidationError( + throw new DataTableValidationError( `${filter.condition.toUpperCase()} filter value cannot be null or undefined`, ); } if (typeof filter.value !== 'string') { - throw new DataStoreValidationError( + throw new DataTableValidationError( `${filter.condition.toUpperCase()} filter value must be a string`, ); } @@ -565,7 +565,7 @@ export class DataStoreService { if (['gt', 'gte', 'lt', 'lte'].includes(filter.condition)) { if (filter.value === null || filter.value === undefined) { - throw new DataStoreValidationError( + throw new DataTableValidationError( `${filter.condition.toUpperCase()} filter value cannot be null or undefined`, ); } @@ -574,14 +574,14 @@ export class DataStoreService { } private async validateDataTableSize() { - await this.dataStoreSizeValidator.validateSize( - async () => await this.dataStoreRepository.findDataTablesSize(), + await this.dataTableSizeValidator.validateSize( + async () => await this.dataTableRepository.findDataTablesSize(), ); } async getDataTablesSize(user: User): Promise { - const allSizeData = await this.dataStoreSizeValidator.getCachedSizeData( - async () => await this.dataStoreRepository.findDataTablesSize(), + const allSizeData = await this.dataTableSizeValidator.getCachedSizeData( + async () => await this.dataTableRepository.findDataTablesSize(), ); const roles = await this.roleService.rolesWithScope('project', ['dataStore:listProject']); @@ -602,7 +602,7 @@ export class DataStoreService { return { totalBytes: allSizeData.totalBytes, - quotaStatus: this.dataStoreSizeValidator.sizeToState(allSizeData.totalBytes), + quotaStatus: this.dataTableSizeValidator.sizeToState(allSizeData.totalBytes), dataTables: accessibleDataTables, }; } diff --git a/packages/cli/src/modules/data-table/data-store.types.ts b/packages/cli/src/modules/data-table/data-table.types.ts similarity index 84% rename from packages/cli/src/modules/data-table/data-store.types.ts rename to packages/cli/src/modules/data-table/data-table.types.ts index 8bfc1b43f3a..840d95a48ea 100644 --- a/packages/cli/src/modules/data-table/data-store.types.ts +++ b/packages/cli/src/modules/data-table/data-table.types.ts @@ -1,6 +1,6 @@ import type { FieldTypeMap } from 'n8n-workflow'; -export type DataStoreUserTableName = `${string}data_table_user_${string}`; +export type DataTableUserTableName = `${string}data_table_user_${string}`; export const columnTypeToFieldType: Record = { // eslint-disable-next-line id-denylist diff --git a/packages/cli/src/modules/data-table/errors/data-store-column-name-conflict.error.ts b/packages/cli/src/modules/data-table/errors/data-store-column-name-conflict.error.ts deleted file mode 100644 index 641ba6a4cd9..00000000000 --- a/packages/cli/src/modules/data-table/errors/data-store-column-name-conflict.error.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { UserError } from 'n8n-workflow'; - -export class DataStoreColumnNameConflictError extends UserError { - constructor(columnName: string, dataStoreName: string) { - super( - `Data store column with name '${columnName}' already exists in data store '${dataStoreName}'`, - { - level: 'warning', - }, - ); - } -} diff --git a/packages/cli/src/modules/data-table/errors/data-store-column-not-found.error.ts b/packages/cli/src/modules/data-table/errors/data-store-column-not-found.error.ts deleted file mode 100644 index 1e8cc5a50a0..00000000000 --- a/packages/cli/src/modules/data-table/errors/data-store-column-not-found.error.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { UserError } from 'n8n-workflow'; - -export class DataStoreColumnNotFoundError extends UserError { - constructor(dataStoreId: string, columnId: string) { - super(`Could not find the column '${columnId}' in the data store: ${dataStoreId}`, { - level: 'warning', - }); - } -} diff --git a/packages/cli/src/modules/data-table/errors/data-store-name-conflict.error.ts b/packages/cli/src/modules/data-table/errors/data-store-name-conflict.error.ts deleted file mode 100644 index 214a98aef4d..00000000000 --- a/packages/cli/src/modules/data-table/errors/data-store-name-conflict.error.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { UserError } from 'n8n-workflow'; - -export class DataStoreNameConflictError extends UserError { - constructor(name: string) { - super(`Data store with name '${name}' already exists in this project`, { - level: 'warning', - }); - } -} diff --git a/packages/cli/src/modules/data-table/errors/data-store-not-found.error.ts b/packages/cli/src/modules/data-table/errors/data-store-not-found.error.ts deleted file mode 100644 index afe225e7acc..00000000000 --- a/packages/cli/src/modules/data-table/errors/data-store-not-found.error.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { UserError } from 'n8n-workflow'; - -export class DataStoreNotFoundError extends UserError { - constructor(dataStoreId: string) { - super(`Could not find the data table: '${dataStoreId}'`, { - level: 'warning', - }); - } -} diff --git a/packages/cli/src/modules/data-table/errors/data-store-validation.error.ts b/packages/cli/src/modules/data-table/errors/data-store-validation.error.ts deleted file mode 100644 index 640e574c269..00000000000 --- a/packages/cli/src/modules/data-table/errors/data-store-validation.error.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { UserError } from 'n8n-workflow'; - -export class DataStoreValidationError extends UserError { - constructor(msg: string) { - super(`Validation error with data store request: ${msg}`, { - level: 'warning', - }); - } -} diff --git a/packages/cli/src/modules/data-table/errors/data-table-column-name-conflict.error.ts b/packages/cli/src/modules/data-table/errors/data-table-column-name-conflict.error.ts new file mode 100644 index 00000000000..10847a8dd75 --- /dev/null +++ b/packages/cli/src/modules/data-table/errors/data-table-column-name-conflict.error.ts @@ -0,0 +1,12 @@ +import { UserError } from 'n8n-workflow'; + +export class DataTableColumnNameConflictError extends UserError { + constructor(columnName: string, dataTableName: string) { + super( + `Data table column with name '${columnName}' already exists in data table '${dataTableName}'`, + { + level: 'warning', + }, + ); + } +} diff --git a/packages/cli/src/modules/data-table/errors/data-table-column-not-found.error.ts b/packages/cli/src/modules/data-table/errors/data-table-column-not-found.error.ts new file mode 100644 index 00000000000..e874731534c --- /dev/null +++ b/packages/cli/src/modules/data-table/errors/data-table-column-not-found.error.ts @@ -0,0 +1,9 @@ +import { UserError } from 'n8n-workflow'; + +export class DataTableColumnNotFoundError extends UserError { + constructor(dataTableId: string, columnId: string) { + super(`Could not find the column '${columnId}' in the data table: ${dataTableId}`, { + level: 'warning', + }); + } +} diff --git a/packages/cli/src/modules/data-table/errors/data-table-name-conflict.error.ts b/packages/cli/src/modules/data-table/errors/data-table-name-conflict.error.ts new file mode 100644 index 00000000000..8dff8adb3cb --- /dev/null +++ b/packages/cli/src/modules/data-table/errors/data-table-name-conflict.error.ts @@ -0,0 +1,9 @@ +import { UserError } from 'n8n-workflow'; + +export class DataTableNameConflictError extends UserError { + constructor(name: string) { + super(`Data table with name '${name}' already exists in this project`, { + level: 'warning', + }); + } +} diff --git a/packages/cli/src/modules/data-table/errors/data-table-not-found.error.ts b/packages/cli/src/modules/data-table/errors/data-table-not-found.error.ts new file mode 100644 index 00000000000..3640bc20aeb --- /dev/null +++ b/packages/cli/src/modules/data-table/errors/data-table-not-found.error.ts @@ -0,0 +1,9 @@ +import { UserError } from 'n8n-workflow'; + +export class DataTableNotFoundError extends UserError { + constructor(dataTableId: string) { + super(`Could not find the data table: '${dataTableId}'`, { + level: 'warning', + }); + } +} diff --git a/packages/cli/src/modules/data-table/errors/data-store-system-column-name-conflict.error.ts b/packages/cli/src/modules/data-table/errors/data-table-system-column-name-conflict.error.ts similarity index 77% rename from packages/cli/src/modules/data-table/errors/data-store-system-column-name-conflict.error.ts rename to packages/cli/src/modules/data-table/errors/data-table-system-column-name-conflict.error.ts index 500e924fba7..4ff30b3685f 100644 --- a/packages/cli/src/modules/data-table/errors/data-store-system-column-name-conflict.error.ts +++ b/packages/cli/src/modules/data-table/errors/data-table-system-column-name-conflict.error.ts @@ -1,6 +1,6 @@ import { UserError } from 'n8n-workflow'; -export class DataStoreSystemColumnNameConflictError extends UserError { +export class DataTableSystemColumnNameConflictError extends UserError { constructor(columnName: string, type: string = 'system') { super(`Column name "${columnName}" is reserved as a ${type} column name.`, { level: 'warning', diff --git a/packages/cli/src/modules/data-table/errors/data-table-validation.error.ts b/packages/cli/src/modules/data-table/errors/data-table-validation.error.ts new file mode 100644 index 00000000000..f4b84d05f70 --- /dev/null +++ b/packages/cli/src/modules/data-table/errors/data-table-validation.error.ts @@ -0,0 +1,9 @@ +import { UserError } from 'n8n-workflow'; + +export class DataTableValidationError extends UserError { + constructor(msg: string) { + super(`Validation error with data table request: ${msg}`, { + level: 'warning', + }); + } +} diff --git a/packages/cli/src/modules/data-table/utils/sql-utils.ts b/packages/cli/src/modules/data-table/utils/sql-utils.ts index 636c6936d6b..59badbb1ee4 100644 --- a/packages/cli/src/modules/data-table/utils/sql-utils.ts +++ b/packages/cli/src/modules/data-table/utils/sql-utils.ts @@ -1,21 +1,21 @@ import { - dataStoreColumnNameSchema, - DATA_STORE_COLUMN_ERROR_MESSAGE, - type DataStoreCreateColumnSchema, + dataTableColumnNameSchema, + DATA_TABLE_COLUMN_ERROR_MESSAGE, + type DataTableCreateColumnSchema, } from '@n8n/api-types'; import { GlobalConfig } from '@n8n/config'; import { DslColumn } from '@n8n/db'; import { Container } from '@n8n/di'; import type { DataSourceOptions } from '@n8n/typeorm'; -import type { DataStoreColumnJsType, DataStoreRowReturn, DataStoreRowsReturn } from 'n8n-workflow'; +import type { DataTableColumnJsType, DataTableRowReturn, DataTableRowsReturn } from 'n8n-workflow'; import { UnexpectedError } from 'n8n-workflow'; import { NotFoundError } from '@/errors/response-errors/not-found.error'; -import type { DataStoreUserTableName } from '../data-store.types'; import type { DataTableColumn } from '../data-table-column.entity'; +import type { DataTableUserTableName } from '../data-table.types'; -export function toDslColumns(columns: DataStoreCreateColumnSchema[]): DslColumn[] { +export function toDslColumns(columns: DataTableCreateColumnSchema[]): DslColumn[] { return columns.map((col) => { const name = new DslColumn(col.name.trim()); @@ -34,8 +34,8 @@ export function toDslColumns(columns: DataStoreCreateColumnSchema[]): DslColumn[ }); } -function dataStoreColumnTypeToSql( - type: DataStoreCreateColumnSchema['type'], +function dataTableColumnTypeToSql( + type: DataTableCreateColumnSchema['type'], dbType: DataSourceOptions['type'], ) { switch (type) { @@ -66,24 +66,24 @@ function dataStoreColumnTypeToSql( } function columnToWildcardAndType( - column: DataStoreCreateColumnSchema, + column: DataTableCreateColumnSchema, dbType: DataSourceOptions['type'], ) { - return `${quoteIdentifier(column.name, dbType)} ${dataStoreColumnTypeToSql(column.type, dbType)}`; + return `${quoteIdentifier(column.name, dbType)} ${dataTableColumnTypeToSql(column.type, dbType)}`; } export function isValidColumnName(name: string) { - return dataStoreColumnNameSchema.safeParse(name).success; + return dataTableColumnNameSchema.safeParse(name).success; } export function addColumnQuery( - tableName: DataStoreUserTableName, - column: DataStoreCreateColumnSchema, + tableName: DataTableUserTableName, + column: DataTableCreateColumnSchema, dbType: DataSourceOptions['type'], ) { // API requests should already conform to this, but better safe than sorry if (!isValidColumnName(column.name)) { - throw new UnexpectedError(DATA_STORE_COLUMN_ERROR_MESSAGE); + throw new UnexpectedError(DATA_TABLE_COLUMN_ERROR_MESSAGE); } const quotedTableName = quoteIdentifier(tableName, dbType); @@ -92,7 +92,7 @@ export function addColumnQuery( } export function deleteColumnQuery( - tableName: DataStoreUserTableName, + tableName: DataTableUserTableName, column: string, dbType: DataSourceOptions['type'], ): string { @@ -129,7 +129,7 @@ function hasInsertId(data: unknown): data is WithInsertId { return typeof data === 'object' && data !== null && 'insertId' in data && isNumber(data.insertId); } -function hasRowReturnData(data: unknown): data is DataStoreRowReturn { +function hasRowReturnData(data: unknown): data is DataTableRowReturn { return ( typeof data === 'object' && data !== null && @@ -142,11 +142,11 @@ function hasRowReturnData(data: unknown): data is DataStoreRowReturn { ); } -function hasRowId(data: unknown): data is Pick { +function hasRowId(data: unknown): data is Pick { return typeof data === 'object' && data !== null && 'id' in data && isNumber(data.id); } -export function extractReturningData(raw: unknown): DataStoreRowReturn[] { +export function extractReturningData(raw: unknown): DataTableRowReturn[] { if (!isArrayOf(raw, hasRowReturnData)) { throw new UnexpectedError( `Expected INSERT INTO raw to be { id: number; createdAt: string; updatedAt: string }[] on Postgres or MariaDB. Is '${JSON.stringify(raw)}'`, @@ -183,7 +183,7 @@ export function extractInsertedIds(raw: unknown, dbType: DataSourceOptions['type } } -export function normalizeRows(rows: DataStoreRowsReturn, columns: DataTableColumn[]) { +export function normalizeRows(rows: DataTableRowsReturn, columns: DataTableColumn[]) { // we need to normalize system dates as well const systemColumns = [ { name: 'createdAt', type: 'date' }, @@ -242,10 +242,10 @@ function formatDateForDatabase(date: Date, dbType?: DataSourceOptions['type']): } export function normalizeValue( - value: DataStoreColumnJsType, + value: DataTableColumnJsType, columnType: string | undefined, dbType?: DataSourceOptions['type'], -): DataStoreColumnJsType { +): DataTableColumnJsType { if (columnType !== 'date' || value === null || value === undefined) { return value; } @@ -292,11 +292,11 @@ export function escapeLikeSpecials(input: string): string { .replace(/_/g, '\\_'); // make '_' literal ('%' stays a wildcard) } -export function toTableName(dataStoreId: string): DataStoreUserTableName { +export function toTableName(dataTableId: string): DataTableUserTableName { const { tablePrefix } = Container.get(GlobalConfig).database; - return `${tablePrefix}data_table_user_${dataStoreId}`; + return `${tablePrefix}data_table_user_${dataTableId}`; } -export function toTableId(tableName: DataStoreUserTableName) { +export function toTableId(tableName: DataTableUserTableName) { return tableName.replace(/.*data_table_user_/, ''); } diff --git a/packages/cli/src/scaling/__tests__/job-processor.service.test.ts b/packages/cli/src/scaling/__tests__/job-processor.service.test.ts index 0a7663adae2..b5294c38576 100644 --- a/packages/cli/src/scaling/__tests__/job-processor.service.test.ts +++ b/packages/cli/src/scaling/__tests__/job-processor.service.test.ts @@ -21,7 +21,7 @@ import { CredentialsHelper } from '@/credentials-helper'; import { VariablesService } from '@/environments.ee/variables/variables.service.ee'; import { ExternalHooks } from '@/external-hooks'; import type { ManualExecutionService } from '@/manual-execution.service'; -import { DataStoreProxyService } from '@/modules/data-table/data-store-proxy.service'; +import { DataTableProxyService } from '@/modules/data-table/data-table-proxy.service'; import { WorkflowStatisticsService } from '@/services/workflow-statistics.service'; import * as WorkflowExecuteAdditionalData from '@/workflow-execute-additional-data'; import { WorkflowStaticDataService } from '@/workflows/workflow-static-data.service'; @@ -34,7 +34,7 @@ mockInstance(ExternalSecretsProxy); mockInstance(WorkflowStaticDataService); mockInstance(WorkflowStatisticsService); mockInstance(ExternalHooks); -mockInstance(DataStoreProxyService); +mockInstance(DataTableProxyService); const processRunExecutionDataMock = jest.fn(); jest.mock('n8n-core', () => { diff --git a/packages/cli/src/services/project.service.ee.ts b/packages/cli/src/services/project.service.ee.ts index baa395d61b2..f8d011dce78 100644 --- a/packages/cli/src/services/project.service.ee.ts +++ b/packages/cli/src/services/project.service.ee.ts @@ -95,8 +95,8 @@ export class ProjectService { } private get dataTableService() { - return import('@/modules/data-table/data-store.service').then(({ DataStoreService }) => - Container.get(DataStoreService), + return import('@/modules/data-table/data-table.service').then(({ DataTableService }) => + Container.get(DataTableService), ); } @@ -189,9 +189,9 @@ export class ProjectService { const dataTableService = await this.dataTableService; if (targetProject) { - await dataTableService.transferDataStoresByProjectId(project.id, targetProject.id); + await dataTableService.transferDataTablesByProjectId(project.id, targetProject.id); } else { - await dataTableService.deleteDataStoreByProjectId(project.id); + await dataTableService.deleteDataTableByProjectId(project.id); } } diff --git a/packages/cli/test/integration/shared/db/data-stores.ts b/packages/cli/test/integration/shared/db/data-stores.ts deleted file mode 100644 index c917bcd9d29..00000000000 --- a/packages/cli/test/integration/shared/db/data-stores.ts +++ /dev/null @@ -1,43 +0,0 @@ -import type { CreateDataStoreColumnDto } from '@n8n/api-types'; -import { randomName } from '@n8n/backend-test-utils'; -import type { Project } from '@n8n/db'; -import { Container } from '@n8n/di'; -import type { DataStoreRows } from 'n8n-workflow'; - -import { DataStoreColumnRepository } from '@/modules/data-table/data-store-column.repository'; -import { DataStoreRowsRepository } from '@/modules/data-table/data-store-rows.repository'; -import { DataStoreRepository } from '@/modules/data-table/data-store.repository'; - -export const createDataStore = async ( - project: Project, - options: { - name?: string; - columns?: CreateDataStoreColumnDto[]; - data?: DataStoreRows; - updatedAt?: Date; - } = {}, -) => { - const dataStoreRepository = Container.get(DataStoreRepository); - const dataStore = await dataStoreRepository.createDataStore( - project.id, - options.name ?? randomName(), - options.columns ?? [], - ); - - if (options.updatedAt) { - await dataStoreRepository.update(dataStore.id, { - updatedAt: options.updatedAt, - }); - dataStore.updatedAt = options.updatedAt; - } - - if (options.data) { - const dataStoreColumnRepository = Container.get(DataStoreColumnRepository); - const columns = await dataStoreColumnRepository.getColumns(dataStore.id); - - const dataStoreRowsRepository = Container.get(DataStoreRowsRepository); - await dataStoreRowsRepository.insertRows(dataStore.id, options.data, columns, 'count'); - } - - return dataStore; -}; diff --git a/packages/cli/test/integration/shared/db/data-tables.ts b/packages/cli/test/integration/shared/db/data-tables.ts new file mode 100644 index 00000000000..07deebb8128 --- /dev/null +++ b/packages/cli/test/integration/shared/db/data-tables.ts @@ -0,0 +1,43 @@ +import type { CreateDataTableColumnDto } from '@n8n/api-types'; +import { randomName } from '@n8n/backend-test-utils'; +import type { Project } from '@n8n/db'; +import { Container } from '@n8n/di'; +import type { DataTableRows } from 'n8n-workflow'; + +import { DataTableColumnRepository } from '@/modules/data-table/data-table-column.repository'; +import { DataTableRowsRepository } from '@/modules/data-table/data-table-rows.repository'; +import { DataTableRepository } from '@/modules/data-table/data-table.repository'; + +export const createDataTable = async ( + project: Project, + options: { + name?: string; + columns?: CreateDataTableColumnDto[]; + data?: DataTableRows; + updatedAt?: Date; + } = {}, +) => { + const dataTableRepository = Container.get(DataTableRepository); + const dataTable = await dataTableRepository.createDataTable( + project.id, + options.name ?? randomName(), + options.columns ?? [], + ); + + if (options.updatedAt) { + await dataTableRepository.update(dataTable.id, { + updatedAt: options.updatedAt, + }); + dataTable.updatedAt = options.updatedAt; + } + + if (options.data) { + const dataTableColumnRepository = Container.get(DataTableColumnRepository); + const columns = await dataTableColumnRepository.getColumns(dataTable.id); + + const dataTableRowsRepository = Container.get(DataTableRowsRepository); + await dataTableRowsRepository.insertRows(dataTable.id, options.data, columns, 'count'); + } + + return dataTable; +}; diff --git a/packages/core/src/execution-engine/index.ts b/packages/core/src/execution-engine/index.ts index 4ca4f762c0d..37f21ea7bf4 100644 --- a/packages/core/src/execution-engine/index.ts +++ b/packages/core/src/execution-engine/index.ts @@ -1,4 +1,4 @@ -import type { DataStoreProxyProvider } from 'n8n-workflow'; +import type { DataTableProxyProvider } from 'n8n-workflow'; import type { ExecutionLifecycleHooks } from './execution-lifecycle-hooks'; import type { ExternalSecretsProxy } from './external-secrets-proxy'; @@ -7,7 +7,7 @@ declare module 'n8n-workflow' { interface IWorkflowExecuteAdditionalData { hooks?: ExecutionLifecycleHooks; externalSecretsProxy: ExternalSecretsProxy; - 'data-table'?: { dataStoreProxyProvider: DataStoreProxyProvider }; + 'data-table'?: { dataTableProxyProvider: DataTableProxyProvider }; // Project ID is currently only added on the additionalData if the user // has data table listing permission for that project. We should consider // that only data tables belonging to their respective projects are shown. diff --git a/packages/core/src/execution-engine/node-execution-context/execute-context.ts b/packages/core/src/execution-engine/node-execution-context/execute-context.ts index 8424d5d9f41..7ad823f636a 100644 --- a/packages/core/src/execution-engine/node-execution-context/execute-context.ts +++ b/packages/core/src/execution-engine/node-execution-context/execute-context.ts @@ -36,7 +36,7 @@ import { } from './utils/binary-helper-functions'; import { constructExecutionMetaData } from './utils/construct-execution-metadata'; import { copyInputItems } from './utils/copy-input-items'; -import { getDataStoreHelperFunctions } from './utils/data-store-helper-functions'; +import { getDataTableHelperFunctions } from './utils/data-table-helper-functions'; import { getDeduplicationHelperFunctions } from './utils/deduplication-helper-functions'; import { getFileSystemHelperFunctions } from './utils/file-system-helper-functions'; import { getInputConnectionData } from './utils/get-input-connection-data'; @@ -95,7 +95,7 @@ export class ExecuteContext extends BaseExecuteContext implements IExecuteFuncti connectionInputData, ), ...getBinaryHelperFunctions(additionalData, workflow.id), - ...getDataStoreHelperFunctions(additionalData, workflow, node), + ...getDataTableHelperFunctions(additionalData, workflow, node), ...getSSHTunnelFunctions(), ...getFileSystemHelperFunctions(node), ...getDeduplicationHelperFunctions(workflow, node), diff --git a/packages/core/src/execution-engine/node-execution-context/load-options-context.ts b/packages/core/src/execution-engine/node-execution-context/load-options-context.ts index e7350a96e5a..16edcb6294e 100644 --- a/packages/core/src/execution-engine/node-execution-context/load-options-context.ts +++ b/packages/core/src/execution-engine/node-execution-context/load-options-context.ts @@ -10,7 +10,7 @@ import type { } from 'n8n-workflow'; import { NodeExecutionContext } from './node-execution-context'; -import { getDataStoreHelperFunctions } from './utils/data-store-helper-functions'; +import { getDataTableHelperFunctions } from './utils/data-table-helper-functions'; import { extractValue } from './utils/extract-value'; import { getRequestHelperFunctions } from './utils/request-helper-functions'; import { getSSHTunnelFunctions } from './utils/ssh-tunnel-helper-functions'; @@ -29,7 +29,7 @@ export class LoadOptionsContext extends NodeExecutionContext implements ILoadOpt this.helpers = { ...getSSHTunnelFunctions(), ...getRequestHelperFunctions(workflow, node, additionalData), - ...getDataStoreHelperFunctions(additionalData, workflow, node), + ...getDataTableHelperFunctions(additionalData, workflow, node), }; } diff --git a/packages/core/src/execution-engine/node-execution-context/supply-data-context.ts b/packages/core/src/execution-engine/node-execution-context/supply-data-context.ts index 45d468298c7..5d956703f72 100644 --- a/packages/core/src/execution-engine/node-execution-context/supply-data-context.ts +++ b/packages/core/src/execution-engine/node-execution-context/supply-data-context.ts @@ -29,7 +29,7 @@ import { } from './utils/binary-helper-functions'; import { constructExecutionMetaData } from './utils/construct-execution-metadata'; import { copyInputItems } from './utils/copy-input-items'; -import { getDataStoreHelperFunctions } from './utils/data-store-helper-functions'; +import { getDataTableHelperFunctions } from './utils/data-table-helper-functions'; import { getDeduplicationHelperFunctions } from './utils/deduplication-helper-functions'; import { getFileSystemHelperFunctions } from './utils/file-system-helper-functions'; // eslint-disable-next-line import-x/no-cycle @@ -89,7 +89,7 @@ export class SupplyDataContext extends BaseExecuteContext implements ISupplyData ...getSSHTunnelFunctions(), ...getFileSystemHelperFunctions(node), ...getBinaryHelperFunctions(additionalData, workflow.id), - ...getDataStoreHelperFunctions(additionalData, workflow, node), + ...getDataTableHelperFunctions(additionalData, workflow, node), ...getDeduplicationHelperFunctions(workflow, node), assertBinaryData: (itemIndex, propertyName) => assertBinaryData(inputData, node, itemIndex, propertyName, 0), diff --git a/packages/core/src/execution-engine/node-execution-context/utils/data-store-helper-functions.ts b/packages/core/src/execution-engine/node-execution-context/utils/data-store-helper-functions.ts deleted file mode 100644 index e782e2168da..00000000000 --- a/packages/core/src/execution-engine/node-execution-context/utils/data-store-helper-functions.ts +++ /dev/null @@ -1,30 +0,0 @@ -import type { - DataStoreProxyFunctions, - INode, - Workflow, - IWorkflowExecuteAdditionalData, -} from 'n8n-workflow'; - -export function getDataStoreHelperFunctions( - additionalData: IWorkflowExecuteAdditionalData, - workflow: Workflow, - node: INode, -): Partial { - const dataStoreProxyProvider = additionalData['data-table']?.dataStoreProxyProvider; - if (!dataStoreProxyProvider) return {}; - return { - getDataStoreAggregateProxy: async () => - await dataStoreProxyProvider.getDataStoreAggregateProxy( - workflow, - node, - additionalData.dataTableProjectId, - ), - getDataStoreProxy: async (dataStoreId: string) => - await dataStoreProxyProvider.getDataStoreProxy( - workflow, - node, - dataStoreId, - additionalData.dataTableProjectId, - ), - }; -} diff --git a/packages/core/src/execution-engine/node-execution-context/utils/data-table-helper-functions.ts b/packages/core/src/execution-engine/node-execution-context/utils/data-table-helper-functions.ts new file mode 100644 index 00000000000..e8f63a8bf53 --- /dev/null +++ b/packages/core/src/execution-engine/node-execution-context/utils/data-table-helper-functions.ts @@ -0,0 +1,30 @@ +import type { + DataTableProxyFunctions, + INode, + Workflow, + IWorkflowExecuteAdditionalData, +} from 'n8n-workflow'; + +export function getDataTableHelperFunctions( + additionalData: IWorkflowExecuteAdditionalData, + workflow: Workflow, + node: INode, +): Partial { + const dataTableProxyProvider = additionalData['data-table']?.dataTableProxyProvider; + if (!dataTableProxyProvider) return {}; + return { + getDataTableAggregateProxy: async () => + await dataTableProxyProvider.getDataTableAggregateProxy( + workflow, + node, + additionalData.dataTableProjectId, + ), + getDataTableProxy: async (dataTableId: string) => + await dataTableProxyProvider.getDataTableProxy( + workflow, + node, + dataTableId, + additionalData.dataTableProjectId, + ), + }; +} diff --git a/packages/frontend/editor-ui/src/features/dataStore/constants.ts b/packages/frontend/editor-ui/src/features/dataStore/constants.ts index 556b7628782..7c49d0b3d34 100644 --- a/packages/frontend/editor-ui/src/features/dataStore/constants.ts +++ b/packages/frontend/editor-ui/src/features/dataStore/constants.ts @@ -1,4 +1,4 @@ -import { DATA_STORE_COLUMN_REGEX } from '@n8n/api-types'; +import { DATA_TABLE_COLUMN_REGEX } from '@n8n/api-types'; // Route and view identifiers export const DATA_STORE_VIEW = 'data-stores'; @@ -29,7 +29,7 @@ export const DEFAULT_ID_COLUMN_NAME = 'id'; export const MAX_COLUMN_NAME_LENGTH = 128; -export const COLUMN_NAME_REGEX = DATA_STORE_COLUMN_REGEX; +export const COLUMN_NAME_REGEX = DATA_TABLE_COLUMN_REGEX; export const MIN_LOADING_TIME = 500; // ms diff --git a/packages/nodes-base/nodes/DataTable/DataTable.node.json b/packages/nodes-base/nodes/DataTable/DataTable.node.json index 9fd7d69b30a..8e0559fa805 100644 --- a/packages/nodes-base/nodes/DataTable/DataTable.node.json +++ b/packages/nodes-base/nodes/DataTable/DataTable.node.json @@ -11,7 +11,7 @@ } ] }, - "alias": ["data", "table", "knowledge", "data store", "store", "sheet"], + "alias": ["data", "table", "knowledge", "data table", "table", "sheet"], "subcategories": { "Core Nodes": ["Helpers"] } diff --git a/packages/nodes-base/nodes/DataTable/actions/row/insert.operation.ts b/packages/nodes-base/nodes/DataTable/actions/row/insert.operation.ts index 685fc3d48b9..1b1086e15ac 100644 --- a/packages/nodes-base/nodes/DataTable/actions/row/insert.operation.ts +++ b/packages/nodes-base/nodes/DataTable/actions/row/insert.operation.ts @@ -1,5 +1,5 @@ import type { - IDataStoreProjectService, + IDataTableProjectService, IDisplayOptions, IExecuteFunctions, INodeExecutionData, @@ -65,7 +65,7 @@ export async function execute( export async function executeBulk( this: IExecuteFunctions, - proxy: IDataStoreProjectService, + proxy: IDataTableProjectService, ): Promise { const optimizeBulkEnabled = this.getNodeParameter('options.optimizeBulk', 0, false); const rows = this.getInputData().flatMap((_, i) => [getAddRow(this, i)]); diff --git a/packages/nodes-base/nodes/DataTable/common/constants.ts b/packages/nodes-base/nodes/DataTable/common/constants.ts index 44dc361694c..c0421567066 100644 --- a/packages/nodes-base/nodes/DataTable/common/constants.ts +++ b/packages/nodes-base/nodes/DataTable/common/constants.ts @@ -1,4 +1,4 @@ -import type { DataStoreColumnJsType } from 'n8n-workflow'; +import type { DataTableColumnJsType } from 'n8n-workflow'; export const ANY_CONDITION = 'anyCondition'; export const ALL_CONDITIONS = 'allConditions'; @@ -15,5 +15,5 @@ export type FieldEntry = | { keyName: string; condition?: 'eq' | 'neq' | 'like' | 'ilike' | 'gt' | 'gte' | 'lt' | 'lte'; - keyValue: DataStoreColumnJsType; + keyValue: DataTableColumnJsType; }; diff --git a/packages/nodes-base/nodes/DataTable/common/selectMany.ts b/packages/nodes-base/nodes/DataTable/common/selectMany.ts index 5631b326b0e..c37392147b3 100644 --- a/packages/nodes-base/nodes/DataTable/common/selectMany.ts +++ b/packages/nodes-base/nodes/DataTable/common/selectMany.ts @@ -1,8 +1,8 @@ import { DATA_TABLE_SYSTEM_COLUMNS, NodeOperationError } from 'n8n-workflow'; import type { DataTableFilter, - DataStoreRowReturn, - IDataStoreProjectService, + DataTableRowReturn, + IDataTableProjectService, IDisplayOptions, IExecuteFunctions, INodeProperties, @@ -144,10 +144,10 @@ export async function getSelectFilter( export async function executeSelectMany( ctx: IExecuteFunctions, index: number, - dataStoreProxy: IDataStoreProjectService, + dataStoreProxy: IDataTableProjectService, rejectEmpty = false, limit?: number, -): Promise> { +): Promise> { const filter = await getSelectFilter(ctx, index); if (rejectEmpty && filter.filters.length === 0) { @@ -155,7 +155,7 @@ export async function executeSelectMany( } const PAGE_SIZE = 1000; - const result: Array<{ json: DataStoreRowReturn }> = []; + const result: Array<{ json: DataTableRowReturn }> = []; const returnAll = ctx.getNodeParameter('returnAll', index, false); limit = limit ?? (!returnAll ? ctx.getNodeParameter('limit', index, ROWS_LIMIT_DEFAULT) : 0); diff --git a/packages/nodes-base/nodes/DataTable/common/utils.ts b/packages/nodes-base/nodes/DataTable/common/utils.ts index 3f8a4c4215b..c19e0c5df6a 100644 --- a/packages/nodes-base/nodes/DataTable/common/utils.ts +++ b/packages/nodes-base/nodes/DataTable/common/utils.ts @@ -3,11 +3,11 @@ import type { IDataObject, INode, DataTableFilter, - IDataStoreProjectAggregateService, - IDataStoreProjectService, + IDataTableProjectAggregateService, + IDataTableProjectService, IExecuteFunctions, ILoadOptionsFunctions, - DataStoreColumnJsType, + DataTableColumnJsType, } from 'n8n-workflow'; import { NodeOperationError } from 'n8n-workflow'; @@ -28,50 +28,50 @@ function isDateLike(v: unknown): v is DateLike { export async function getDataTableProxyExecute( ctx: IExecuteFunctions, index: number = 0, -): Promise { - if (ctx.helpers.getDataStoreProxy === undefined) +): Promise { + if (ctx.helpers.getDataTableProxy === undefined) throw new NodeOperationError( ctx.getNode(), 'Attempted to use Data table node but the module is disabled', ); - const dataStoreId = ctx.getNodeParameter(DATA_TABLE_ID_FIELD, index, undefined, { + const dataTableId = ctx.getNodeParameter(DATA_TABLE_ID_FIELD, index, undefined, { extractValue: true, }) as string; - return await ctx.helpers.getDataStoreProxy(dataStoreId); + return await ctx.helpers.getDataTableProxy(dataTableId); } export async function getDataTableProxyLoadOptions( ctx: ILoadOptionsFunctions, -): Promise { - if (ctx.helpers.getDataStoreProxy === undefined) +): Promise { + if (ctx.helpers.getDataTableProxy === undefined) throw new NodeOperationError( ctx.getNode(), 'Attempted to use Data table node but the module is disabled', ); - const dataStoreId = ctx.getNodeParameter(DATA_TABLE_ID_FIELD, undefined, { + const dataTableId = ctx.getNodeParameter(DATA_TABLE_ID_FIELD, undefined, { extractValue: true, }) as string; - if (!dataStoreId) { + if (!dataTableId) { return; } - return await ctx.helpers.getDataStoreProxy(dataStoreId); + return await ctx.helpers.getDataTableProxy(dataTableId); } export async function getDataTableAggregateProxy( ctx: IExecuteFunctions | ILoadOptionsFunctions, -): Promise { - if (ctx.helpers.getDataStoreAggregateProxy === undefined) +): Promise { + if (ctx.helpers.getDataTableAggregateProxy === undefined) throw new NodeOperationError( ctx.getNode(), 'Attempted to use Data table node but the module is disabled', ); - return await ctx.helpers.getDataStoreAggregateProxy(); + return await ctx.helpers.getDataTableAggregateProxy(); } export function isFieldEntry(obj: unknown): obj is FieldEntry { @@ -134,9 +134,9 @@ export function dataObjectToApiInput( data: IDataObject, node: INode, row: number, -): Record { +): Record { return Object.fromEntries( - Object.entries(data).map(([k, v]): [string, DataStoreColumnJsType] => { + Object.entries(data).map(([k, v]): [string, DataTableColumnJsType] => { if (v === undefined || v === null) return [k, null]; if (Array.isArray(v)) { diff --git a/packages/nodes-base/nodes/DataTable/test/common/selectMany.test.ts b/packages/nodes-base/nodes/DataTable/test/common/selectMany.test.ts index 47b78090efb..a4c5ca4a4d1 100644 --- a/packages/nodes-base/nodes/DataTable/test/common/selectMany.test.ts +++ b/packages/nodes-base/nodes/DataTable/test/common/selectMany.test.ts @@ -1,7 +1,7 @@ import { type INode, NodeOperationError, - type IDataStoreProjectService, + type IDataTableProjectService, type IExecuteFunctions, } from 'n8n-workflow'; @@ -13,9 +13,9 @@ import { executeSelectMany, getSelectFilter } from '../../common/selectMany'; describe('selectMany utils', () => { let mockExecuteFunctions: IExecuteFunctions; const getManyRowsAndCount = jest.fn(); - const dataStoreProxy = jest.mocked({ + const dataStoreProxy = jest.mocked({ getManyRowsAndCount, - } as unknown as IDataStoreProjectService); + } as unknown as IDataTableProjectService); const dataTableId = 2345; let filters: FieldEntry[]; const node = { id: 1 } as unknown as INode; @@ -29,7 +29,7 @@ describe('selectMany utils', () => { }, ]; - const mockDataStoreProxy = { + const mockDataTableProxy = { getColumns: jest.fn().mockResolvedValue([ { name: 'name', type: 'string' }, { name: 'age', type: 'number' }, @@ -50,7 +50,7 @@ describe('selectMany utils', () => { } }), helpers: { - getDataStoreProxy: jest.fn().mockResolvedValue(mockDataStoreProxy), + getDataTableProxy: jest.fn().mockResolvedValue(mockDataTableProxy), }, } as unknown as IExecuteFunctions; diff --git a/packages/nodes-base/nodes/Evaluation/EvaluationTrigger/EvaluationTrigger.node.ee.ts b/packages/nodes-base/nodes/Evaluation/EvaluationTrigger/EvaluationTrigger.node.ee.ts index 2ae257b49d4..71a9624a187 100644 --- a/packages/nodes-base/nodes/Evaluation/EvaluationTrigger/EvaluationTrigger.node.ee.ts +++ b/packages/nodes-base/nodes/Evaluation/EvaluationTrigger/EvaluationTrigger.node.ee.ts @@ -190,7 +190,7 @@ export class EvaluationTrigger implements INodeType { ? (this.getNodeParameter('maxRows', 0, MAX_ROWS) as number) : MAX_ROWS; - if (this.helpers.getDataStoreProxy === undefined) { + if (this.helpers.getDataTableProxy === undefined) { throw new NodeOperationError( this.getNode(), 'Attempted to use Data table node but the module is disabled', @@ -205,7 +205,7 @@ export class EvaluationTrigger implements INodeType { const dataTableId = this.getNodeParameter('dataTableId', 0, undefined, { extractValue: true, }) as string; - const dataTableProxy = await this.helpers.getDataStoreProxy(dataTableId); + const dataTableProxy = await this.helpers.getDataTableProxy(dataTableId); const filter = await getDataTableFilter(this, 0); @@ -318,7 +318,7 @@ export class EvaluationTrigger implements INodeType { ? (this.getNodeParameter('maxRows', 0, MAX_ROWS) as number) : MAX_ROWS; - if (this.helpers.getDataStoreProxy === undefined) { + if (this.helpers.getDataTableProxy === undefined) { throw new NodeOperationError( this.getNode(), 'Attempted to use Data table node but the module is disabled', @@ -328,7 +328,7 @@ export class EvaluationTrigger implements INodeType { const dataTableId = this.getNodeParameter('dataTableId', 0, undefined, { extractValue: true, }) as string; - const dataTableProxy = await this.helpers.getDataStoreProxy(dataTableId); + const dataTableProxy = await this.helpers.getDataTableProxy(dataTableId); const filter = await getDataTableFilter(this, 0); const { data } = await dataTableProxy.getManyRowsAndCount({ diff --git a/packages/nodes-base/nodes/Evaluation/test/Evaluation.node.test.ts b/packages/nodes-base/nodes/Evaluation/test/Evaluation.node.test.ts index 74251ddb94c..3fcd78792cc 100644 --- a/packages/nodes-base/nodes/Evaluation/test/Evaluation.node.test.ts +++ b/packages/nodes-base/nodes/Evaluation/test/Evaluation.node.test.ts @@ -1,6 +1,6 @@ import { mock } from 'jest-mock-extended'; import { - type IDataStoreProjectService, + type IDataTableProjectService, NodeOperationError, type AssignmentCollectionValue, type IExecuteFunctions, @@ -15,14 +15,14 @@ describe('Test Evaluation', () => { const sheetName = 'Sheet5'; const spreadsheetId = '1oqFpPgEPTGDw7BPkp1SfPXq3Cb3Hyr1SROtf-Ec4zvA'; - const mockDataTable = mock({ + const mockDataTable = mock({ getColumns: jest.fn(), addColumn: jest.fn(), updateRows: jest.fn(), }); const mockExecuteFunctions = mock({ - helpers: { getDataStoreProxy: jest.fn().mockResolvedValue(mockDataTable) }, + helpers: { getDataTableProxy: jest.fn().mockResolvedValue(mockDataTable) }, }); beforeEach(() => { diff --git a/packages/nodes-base/nodes/Evaluation/test/EvaluationTrigger.node.test.ts b/packages/nodes-base/nodes/Evaluation/test/EvaluationTrigger.node.test.ts index 1833ccd8b3e..d4fea298c84 100644 --- a/packages/nodes-base/nodes/Evaluation/test/EvaluationTrigger.node.test.ts +++ b/packages/nodes-base/nodes/Evaluation/test/EvaluationTrigger.node.test.ts @@ -338,7 +338,7 @@ describe('Evaluation Trigger Node', () => { mockExecuteFunctions = mockDeep({ getNode: jest.fn().mockReturnValue({ typeVersion: 4.7 }), helpers: { - getDataStoreProxy: jest.fn().mockResolvedValue(mockDataTable), + getDataTableProxy: jest.fn().mockResolvedValue(mockDataTable), }, }); }); diff --git a/packages/nodes-base/nodes/Evaluation/test/evaluationUtils.test.ts b/packages/nodes-base/nodes/Evaluation/test/evaluationUtils.test.ts index a5792137a64..8154325e0fa 100644 --- a/packages/nodes-base/nodes/Evaluation/test/evaluationUtils.test.ts +++ b/packages/nodes-base/nodes/Evaluation/test/evaluationUtils.test.ts @@ -175,7 +175,7 @@ describe('setOutputs', () => { addExecutionHints: jest.fn(), getMode: jest.fn().mockReturnValue('evaluation'), helpers: { - getDataStoreProxy: jest.fn().mockResolvedValue(mockDataTable), + getDataTableProxy: jest.fn().mockResolvedValue(mockDataTable), }, ...options, }); diff --git a/packages/nodes-base/nodes/Evaluation/utils/evaluationUtils.ts b/packages/nodes-base/nodes/Evaluation/utils/evaluationUtils.ts index 3a47ba004a4..39273213efe 100644 --- a/packages/nodes-base/nodes/Evaluation/utils/evaluationUtils.ts +++ b/packages/nodes-base/nodes/Evaluation/utils/evaluationUtils.ts @@ -11,7 +11,7 @@ import type { INodeExecutionData, JsonObject, JsonValue, - DataStoreColumnJsType, + DataTableColumnJsType, } from 'n8n-workflow'; import { getGoogleSheet, getSheet } from './evaluationTriggerUtils'; @@ -52,7 +52,7 @@ function isOutputsArray( ); } -export function toDataTableValue(value: JsonValue): DataStoreColumnJsType { +export function toDataTableValue(value: JsonValue): DataTableColumnJsType { if ( typeof value === 'string' || typeof value === 'number' || @@ -139,7 +139,7 @@ export async function setOutputs(this: IExecuteFunctions): Promise & - Partial>; - -export type CreateDataStoreOptions = Pick & { - columns: CreateDataStoreColumnOptions[]; -}; - -export type UpdateDataStoreOptions = { name: string }; - -export type ListDataStoreOptions = { - filter?: Record; - sortBy?: - | 'name:asc' - | 'name:desc' - | 'createdAt:asc' - | 'createdAt:desc' - | 'updatedAt:asc' - | 'updatedAt:desc' - | 'sizeBytes:asc' - | 'sizeBytes:desc'; - take?: number; - skip?: number; -}; - -export type DataTableFilter = { - type: 'and' | 'or'; - filters: Array<{ - columnName: string; - condition: 'eq' | 'neq' | 'like' | 'ilike' | 'gt' | 'gte' | 'lt' | 'lte'; - value: DataStoreColumnJsType; - }>; -}; - -export type ListDataStoreRowsOptions = { - filter?: DataTableFilter; - sortBy?: [string, 'ASC' | 'DESC']; - take?: number; - skip?: number; -}; - -export type UpdateDataStoreRowOptions = { - filter: DataTableFilter; - data: DataStoreRow; - dryRun?: boolean; -}; - -export type UpsertDataStoreRowOptions = { - filter: DataTableFilter; - data: DataStoreRow; - dryRun?: boolean; -}; - -export type DeleteDataTableRowsOptions = { - filter: DataTableFilter; - dryRun?: boolean; -}; - -export type MoveDataStoreColumnOptions = { - targetIndex: number; -}; - -export type AddDataStoreColumnOptions = Pick & - Partial>; - -export type DataStoreColumnJsType = string | number | boolean | Date | null; - -export const DATA_TABLE_SYSTEM_COLUMN_TYPE_MAP: Record = { - id: 'number', - createdAt: 'date', - updatedAt: 'date', -}; - -export const DATA_TABLE_SYSTEM_COLUMNS = Object.keys(DATA_TABLE_SYSTEM_COLUMN_TYPE_MAP); -export const DATA_TABLE_SYSTEM_TESTING_COLUMN = 'dryRunState'; - -export type DataStoreRowReturnBase = { - id: number; - createdAt: Date; - updatedAt: Date; -}; -export type DataStoreRow = Record; -export type DataStoreRows = DataStoreRow[]; -export type DataStoreRowReturn = DataStoreRow & DataStoreRowReturnBase; -export type DataStoreRowsReturn = DataStoreRowReturn[]; - -export type DataStoreRowReturnWithState = DataStoreRow & { - id: number | null; - createdAt: Date | null; - updatedAt: Date | null; - dryRunState: 'before' | 'after'; -}; - -export type DataStoreRowUpdatePair = { - before: DataStoreRowReturn; - after: DataStoreRowReturn; -}; - -export type DataTableInsertRowsReturnType = 'all' | 'id' | 'count'; -export type DataTableInsertRowsBulkResult = { success: true; insertedRows: number }; -export type DataTableInsertRowsResult< - T extends DataTableInsertRowsReturnType = DataTableInsertRowsReturnType, -> = T extends 'all' - ? DataStoreRowReturn[] - : T extends 'id' - ? Array> - : DataTableInsertRowsBulkResult; - -export type DataTableSizeStatus = 'ok' | 'warn' | 'error'; - -export type DataTableInfo = { - id: string; - name: string; - projectId: string; - projectName: string; - sizeBytes: number; -}; - -export type DataTableInfoById = Record; - -export type DataTablesSizeData = { - totalBytes: number; - dataTables: DataTableInfoById; -}; - -export type DataTablesSizeResult = DataTablesSizeData & { - quotaStatus: DataTableSizeStatus; -}; - -// APIs for a data store service operating on a specific projectId -export interface IDataStoreProjectAggregateService { - getProjectId(): string; - - createDataStore(options: CreateDataStoreOptions): Promise; - - getManyAndCount(options: ListDataStoreOptions): Promise<{ count: number; data: DataStore[] }>; - - deleteDataStoreAll(): Promise; -} -// APIs for a data store service operating on a specific projectId and dataStoreId -export interface IDataStoreProjectService { - updateDataStore(options: UpdateDataStoreOptions): Promise; - - deleteDataStore(): Promise; - - getColumns(): Promise; - - addColumn(options: AddDataStoreColumnOptions): Promise; - - moveColumn(columnId: string, options: MoveDataStoreColumnOptions): Promise; - - deleteColumn(columnId: string): Promise; - - getManyRowsAndCount( - dto: Partial, - ): Promise<{ count: number; data: DataStoreRowsReturn }>; - - insertRows( - rows: DataStoreRows, - returnType: T, - ): Promise>; - - updateRows( - options: UpdateDataStoreRowOptions, - ): Promise; - - upsertRow( - options: UpsertDataStoreRowOptions, - ): Promise; - - deleteRows(options: DeleteDataTableRowsOptions): Promise; -} diff --git a/packages/workflow/src/data-table.types.ts b/packages/workflow/src/data-table.types.ts new file mode 100644 index 00000000000..94833afdb7b --- /dev/null +++ b/packages/workflow/src/data-table.types.ts @@ -0,0 +1,190 @@ +export type DataTableColumnType = 'string' | 'number' | 'boolean' | 'date'; + +export type DataTableColumn = { + id: string; + name: string; + type: DataTableColumnType; + index: number; + dataTableId: string; +}; + +export type DataTable = { + id: string; + name: string; + columns: DataTableColumn[]; + createdAt: Date; + updatedAt: Date; + projectId: string; +}; + +export type CreateDataTableColumnOptions = Pick & + Partial>; + +export type CreateDataTableOptions = Pick & { + columns: CreateDataTableColumnOptions[]; +}; + +export type UpdateDataTableOptions = { name: string }; + +export type ListDataTableOptions = { + filter?: Record; + sortBy?: + | 'name:asc' + | 'name:desc' + | 'createdAt:asc' + | 'createdAt:desc' + | 'updatedAt:asc' + | 'updatedAt:desc' + | 'sizeBytes:asc' + | 'sizeBytes:desc'; + take?: number; + skip?: number; +}; + +export type DataTableFilter = { + type: 'and' | 'or'; + filters: Array<{ + columnName: string; + condition: 'eq' | 'neq' | 'like' | 'ilike' | 'gt' | 'gte' | 'lt' | 'lte'; + value: DataTableColumnJsType; + }>; +}; + +export type ListDataTableRowsOptions = { + filter?: DataTableFilter; + sortBy?: [string, 'ASC' | 'DESC']; + take?: number; + skip?: number; +}; + +export type UpdateDataTableRowOptions = { + filter: DataTableFilter; + data: DataTableRow; + dryRun?: boolean; +}; + +export type UpsertDataTableRowOptions = { + filter: DataTableFilter; + data: DataTableRow; + dryRun?: boolean; +}; + +export type DeleteDataTableRowsOptions = { + filter: DataTableFilter; + dryRun?: boolean; +}; + +export type MoveDataTableColumnOptions = { + targetIndex: number; +}; + +export type AddDataTableColumnOptions = Pick & + Partial>; + +export type DataTableColumnJsType = string | number | boolean | Date | null; + +export const DATA_TABLE_SYSTEM_COLUMN_TYPE_MAP: Record = { + id: 'number', + createdAt: 'date', + updatedAt: 'date', +}; + +export const DATA_TABLE_SYSTEM_COLUMNS = Object.keys(DATA_TABLE_SYSTEM_COLUMN_TYPE_MAP); +export const DATA_TABLE_SYSTEM_TESTING_COLUMN = 'dryRunState'; + +export type DataTableRowReturnBase = { + id: number; + createdAt: Date; + updatedAt: Date; +}; +export type DataTableRow = Record; +export type DataTableRows = DataTableRow[]; +export type DataTableRowReturn = DataTableRow & DataTableRowReturnBase; +export type DataTableRowsReturn = DataTableRowReturn[]; + +export type DataTableRowReturnWithState = DataTableRow & { + id: number | null; + createdAt: Date | null; + updatedAt: Date | null; + dryRunState: 'before' | 'after'; +}; + +export type DataTableRowUpdatePair = { + before: DataTableRowReturn; + after: DataTableRowReturn; +}; + +export type DataTableInsertRowsReturnType = 'all' | 'id' | 'count'; +export type DataTableInsertRowsBulkResult = { success: true; insertedRows: number }; +export type DataTableInsertRowsResult< + T extends DataTableInsertRowsReturnType = DataTableInsertRowsReturnType, +> = T extends 'all' + ? DataTableRowReturn[] + : T extends 'id' + ? Array> + : DataTableInsertRowsBulkResult; + +export type DataTableSizeStatus = 'ok' | 'warn' | 'error'; + +export type DataTableInfo = { + id: string; + name: string; + projectId: string; + projectName: string; + sizeBytes: number; +}; + +export type DataTableInfoById = Record; + +export type DataTablesSizeData = { + totalBytes: number; + dataTables: DataTableInfoById; +}; + +export type DataTablesSizeResult = DataTablesSizeData & { + quotaStatus: DataTableSizeStatus; +}; + +// APIs for a data table service operating on a specific projectId +export interface IDataTableProjectAggregateService { + getProjectId(): string; + + createDataTable(options: CreateDataTableOptions): Promise; + + getManyAndCount(options: ListDataTableOptions): Promise<{ count: number; data: DataTable[] }>; + + deleteDataTableAll(): Promise; +} +// APIs for a data table service operating on a specific projectId and dataTableId +export interface IDataTableProjectService { + updateDataTable(options: UpdateDataTableOptions): Promise; + + deleteDataTable(): Promise; + + getColumns(): Promise; + + addColumn(options: AddDataTableColumnOptions): Promise; + + moveColumn(columnId: string, options: MoveDataTableColumnOptions): Promise; + + deleteColumn(columnId: string): Promise; + + getManyRowsAndCount( + dto: Partial, + ): Promise<{ count: number; data: DataTableRowsReturn }>; + + insertRows( + rows: DataTableRows, + returnType: T, + ): Promise>; + + updateRows( + options: UpdateDataTableRowOptions, + ): Promise; + + upsertRow( + options: UpsertDataTableRowOptions, + ): Promise; + + deleteRows(options: DeleteDataTableRowsOptions): Promise; +} diff --git a/packages/workflow/src/index.ts b/packages/workflow/src/index.ts index bed083bc781..c2221e0882b 100644 --- a/packages/workflow/src/index.ts +++ b/packages/workflow/src/index.ts @@ -7,7 +7,7 @@ export * from './errors'; export * from './constants'; export * from './common'; export * from './cron'; -export * from './data-store.types'; +export * from './data-table.types'; export * from './deferred-promise'; export * from './global-state'; export * from './interfaces'; diff --git a/packages/workflow/src/interfaces.ts b/packages/workflow/src/interfaces.ts index 7e5c427bd13..0b417a8c897 100644 --- a/packages/workflow/src/interfaces.ts +++ b/packages/workflow/src/interfaces.ts @@ -14,9 +14,9 @@ import type { URLSearchParams } from 'url'; import type { CODE_EXECUTION_MODES, CODE_LANGUAGES, LOG_LEVELS } from './constants'; import type { - IDataStoreProjectAggregateService, - IDataStoreProjectService, -} from './data-store.types'; + IDataTableProjectAggregateService, + IDataTableProjectService, +} from './data-table.types'; import type { IDeferredPromise } from './deferred-promise'; import type { ExecutionCancelledError } from './errors'; import type { ExpressionError } from './errors/expression.error'; @@ -925,24 +925,24 @@ type FunctionsBaseWithRequiredKeys = Functions export type ContextType = 'flow' | 'node'; -export type DataStoreProxyProvider = { - getDataStoreAggregateProxy( +export type DataTableProxyProvider = { + getDataTableAggregateProxy( workflow: Workflow, node: INode, projectId?: string, - ): Promise; - getDataStoreProxy( + ): Promise; + getDataTableProxy( workflow: Workflow, node: INode, - dataStoreId: string, + dataTableId: string, projectId?: string, - ): Promise; + ): Promise; }; -export type DataStoreProxyFunctions = { - // These are optional to account for situations where the data-store module is disabled - getDataStoreAggregateProxy?(): Promise; - getDataStoreProxy?(dataStoreId: string): Promise; +export type DataTableProxyFunctions = { + // These are optional to account for situations where the data-table module is disabled + getDataTableAggregateProxy?(): Promise; + getDataTableProxy?(dataTableId: string): Promise; }; type BaseExecutionFunctions = FunctionsBaseWithRequiredKeys<'getMode'> & { @@ -1008,7 +1008,7 @@ export type IExecuteFunctions = ExecuteFunctions.GetNodeParameterFn & DeduplicationHelperFunctions & FileSystemHelperFunctions & SSHTunnelFunctions & - DataStoreProxyFunctions & { + DataTableProxyFunctions & { normalizeItems(items: INodeExecutionData | INodeExecutionData[]): INodeExecutionData[]; constructExecutionMetaData( inputData: INodeExecutionData[], @@ -1093,7 +1093,7 @@ export interface ILoadOptionsFunctions extends FunctionsBase { ): NodeParameterValueType | object | undefined; getCurrentNodeParameters(): INodeParameters | undefined; - helpers: RequestHelperFunctions & SSHTunnelFunctions & DataStoreProxyFunctions; + helpers: RequestHelperFunctions & SSHTunnelFunctions & DataTableProxyFunctions; } export type FieldValueOption = { name: string; type: FieldType | 'any' };