{
/>
-
+
+
+
@@ -199,7 +322,6 @@ onMounted(() => {
--ag-font-family: var(--font-family);
--ag-font-size: var(--font-size-xs);
--ag-row-height: calc(var(--ag-grid-size) * 0.8 + 32px);
-
--ag-header-background-color: var(--color-background-base);
--ag-header-font-size: var(--font-size-xs);
--ag-header-font-weight: var(--font-weight-bold);
diff --git a/packages/frontend/editor-ui/src/features/dataStore/composables/useDataStoreTypes.ts b/packages/frontend/editor-ui/src/features/dataStore/composables/useDataStoreTypes.ts
index dfe57b3d19..3686ca861b 100644
--- a/packages/frontend/editor-ui/src/features/dataStore/composables/useDataStoreTypes.ts
+++ b/packages/frontend/editor-ui/src/features/dataStore/composables/useDataStoreTypes.ts
@@ -1,5 +1,9 @@
import type { IconName } from '@n8n/design-system/components/N8nIcon/icons';
-import type { AGGridCellType, DataStoreColumnType } from '@/features/dataStore/datastore.types';
+import type {
+ AGGridCellType,
+ DataStoreColumnType,
+ DataStoreValue,
+} from '@/features/dataStore/datastore.types';
/* eslint-disable id-denylist */
const COLUMN_TYPE_ICONS: Record = {
@@ -27,8 +31,24 @@ export const useDataStoreTypes = () => {
return colType;
};
+ const getDefaultValueForType = (colType: DataStoreColumnType): DataStoreValue => {
+ switch (colType) {
+ case 'string':
+ return '';
+ case 'number':
+ return 0;
+ case 'boolean':
+ return false;
+ case 'date':
+ return null;
+ default:
+ return null;
+ }
+ };
+
return {
getIconForType,
mapToAGCellType,
+ getDefaultValueForType,
};
};
diff --git a/packages/frontend/editor-ui/src/features/dataStore/constants.ts b/packages/frontend/editor-ui/src/features/dataStore/constants.ts
index afe8c8f8c2..04119ba48d 100644
--- a/packages/frontend/editor-ui/src/features/dataStore/constants.ts
+++ b/packages/frontend/editor-ui/src/features/dataStore/constants.ts
@@ -19,3 +19,7 @@ export const DEFAULT_ID_COLUMN_NAME = 'id';
export const MAX_COLUMN_NAME_LENGTH = 128;
export const COLUMN_NAME_REGEX = /^[a-zA-Z0-9][a-zA-Z0-9-]*$/;
+
+export const NO_TABLE_YET_MESSAGE = 'SQLITE_ERROR: no such table:';
+
+export const MIN_LOADING_TIME = 500; // ms
diff --git a/packages/frontend/editor-ui/src/features/dataStore/dataStore.api.ts b/packages/frontend/editor-ui/src/features/dataStore/dataStore.api.ts
index 1c7e4a8b04..6d506c0fb0 100644
--- a/packages/frontend/editor-ui/src/features/dataStore/dataStore.api.ts
+++ b/packages/frontend/editor-ui/src/features/dataStore/dataStore.api.ts
@@ -5,6 +5,7 @@ import type {
DataStoreColumnCreatePayload,
DataStore,
DataStoreColumn,
+ DataStoreRow,
} from '@/features/dataStore/datastore.types';
export const fetchDataStoresApi = async (
@@ -17,7 +18,7 @@ export const fetchDataStoresApi = async (
filter?: {
id?: string | string[];
name?: string | string[];
- projectId?: string | string[];
+ projectId: string | string[];
},
) => {
const apiEndpoint = projectId ? `/projects/${projectId}/data-stores` : '/data-stores-global';
@@ -26,8 +27,8 @@ export const fetchDataStoresApi = async (
'GET',
apiEndpoint,
{
- ...options,
- ...(filter ?? {}),
+ options: options ?? undefined,
+ filter: filter ?? undefined,
},
);
};
@@ -35,7 +36,7 @@ export const fetchDataStoresApi = async (
export const createDataStoreApi = async (
context: IRestApiContext,
name: string,
- projectId?: string,
+ projectId: string,
columns?: DataStoreColumnCreatePayload[],
) => {
return await makeRestApiRequest(
@@ -52,7 +53,7 @@ export const createDataStoreApi = async (
export const deleteDataStoreApi = async (
context: IRestApiContext,
dataStoreId: string,
- projectId?: string,
+ projectId: string,
) => {
return await makeRestApiRequest(
context,
@@ -69,7 +70,7 @@ export const updateDataStoreApi = async (
context: IRestApiContext,
dataStoreId: string,
name: string,
- projectId?: string,
+ projectId: string,
) => {
return await makeRestApiRequest(
context,
@@ -96,3 +97,54 @@ export const addDataStoreColumnApi = async (
},
);
};
+
+export const getDataStoreRowsApi = async (
+ context: IRestApiContext,
+ dataStoreId: string,
+ projectId: string,
+ options?: {
+ skip?: number;
+ take?: number;
+ },
+) => {
+ return await makeRestApiRequest<{
+ count: number;
+ data: DataStoreRow[];
+ }>(context, 'GET', `/projects/${projectId}/data-stores/${dataStoreId}/rows`, {
+ ...(options ?? {}),
+ });
+};
+
+export const insertDataStoreRowApi = async (
+ context: IRestApiContext,
+ dataStoreId: string,
+ row: DataStoreRow,
+ projectId: string,
+) => {
+ return await makeRestApiRequest(
+ context,
+ 'POST',
+ `/projects/${projectId}/data-stores/${dataStoreId}/insert`,
+ {
+ data: [row],
+ },
+ );
+};
+
+export const upsertDataStoreRowsApi = async (
+ context: IRestApiContext,
+ dataStoreId: string,
+ rows: DataStoreRow[],
+ projectId: string,
+ matchFields: string[] = ['id'],
+) => {
+ return await makeRestApiRequest(
+ context,
+ 'POST',
+ `/projects/${projectId}/data-stores/${dataStoreId}/upsert`,
+ {
+ rows,
+ matchFields,
+ },
+ );
+};
diff --git a/packages/frontend/editor-ui/src/features/dataStore/dataStore.store.ts b/packages/frontend/editor-ui/src/features/dataStore/dataStore.store.ts
index c3b94f36ae..01ca0f8cc0 100644
--- a/packages/frontend/editor-ui/src/features/dataStore/dataStore.store.ts
+++ b/packages/frontend/editor-ui/src/features/dataStore/dataStore.store.ts
@@ -8,14 +8,24 @@ import {
deleteDataStoreApi,
updateDataStoreApi,
addDataStoreColumnApi,
+ getDataStoreRowsApi,
+ insertDataStoreRowApi,
+ upsertDataStoreRowsApi,
} from '@/features/dataStore/dataStore.api';
-import type { DataStore, DataStoreColumnCreatePayload } from '@/features/dataStore/datastore.types';
+import type {
+ DataStore,
+ DataStoreColumnCreatePayload,
+ DataStoreRow,
+} from '@/features/dataStore/datastore.types';
import { useProjectsStore } from '@/stores/projects.store';
+import { useDataStoreTypes } from '@/features/dataStore/composables/useDataStoreTypes';
export const useDataStoreStore = defineStore(DATA_STORE_STORE, () => {
const rootStore = useRootStore();
const projectStore = useProjectsStore();
+ const dataStoreTypes = useDataStoreTypes();
+
const dataStores = ref([]);
const totalCount = ref(0);
@@ -28,7 +38,7 @@ export const useDataStoreStore = defineStore(DATA_STORE_STORE, () => {
totalCount.value = response.count;
};
- const createDataStore = async (name: string, projectId?: string) => {
+ const createDataStore = async (name: string, projectId: string) => {
const newStore = await createDataStoreApi(rootStore.restApiContext, name, projectId);
if (!newStore.project && projectId) {
const project = await projectStore.fetchProject(projectId);
@@ -41,7 +51,7 @@ export const useDataStoreStore = defineStore(DATA_STORE_STORE, () => {
return newStore;
};
- const deleteDataStore = async (datastoreId: string, projectId?: string) => {
+ const deleteDataStore = async (datastoreId: string, projectId: string) => {
const deleted = await deleteDataStoreApi(rootStore.restApiContext, datastoreId, projectId);
if (deleted) {
dataStores.value = dataStores.value.filter((store) => store.id !== datastoreId);
@@ -50,7 +60,7 @@ export const useDataStoreStore = defineStore(DATA_STORE_STORE, () => {
return deleted;
};
- const updateDataStore = async (datastoreId: string, name: string, projectId?: string) => {
+ const updateDataStore = async (datastoreId: string, name: string, projectId: string) => {
const updated = await updateDataStoreApi(
rootStore.restApiContext,
datastoreId,
@@ -68,6 +78,7 @@ export const useDataStoreStore = defineStore(DATA_STORE_STORE, () => {
const fetchDataStoreDetails = async (datastoreId: string, projectId: string) => {
const response = await fetchDataStoresApi(rootStore.restApiContext, projectId, undefined, {
+ projectId,
id: datastoreId,
});
if (response.data.length > 0) {
@@ -104,6 +115,36 @@ export const useDataStoreStore = defineStore(DATA_STORE_STORE, () => {
return newColumn;
};
+ const fetchDataStoreContent = async (
+ datastoreId: string,
+ projectId: string,
+ page: number,
+ pageSize: number,
+ ) => {
+ return await getDataStoreRowsApi(rootStore.restApiContext, datastoreId, projectId, {
+ skip: (page - 1) * pageSize,
+ take: pageSize,
+ });
+ };
+
+ const insertEmptyRow = async (dataStore: DataStore) => {
+ const emptyRow: DataStoreRow = {};
+ dataStore.columns.forEach((column) => {
+ // Set default values based on column type
+ emptyRow[column.name] = dataStoreTypes.getDefaultValueForType(column.type);
+ });
+ return await insertDataStoreRowApi(
+ rootStore.restApiContext,
+ dataStore.id,
+ emptyRow,
+ dataStore.projectId,
+ );
+ };
+
+ const upsertRow = async (dataStoreId: string, projectId: string, row: DataStoreRow) => {
+ return await upsertDataStoreRowsApi(rootStore.restApiContext, dataStoreId, [row], projectId);
+ };
+
return {
dataStores,
totalCount,
@@ -114,5 +155,8 @@ export const useDataStoreStore = defineStore(DATA_STORE_STORE, () => {
fetchDataStoreDetails,
fetchOrFindDataStore,
addDataStoreColumn,
+ fetchDataStoreContent,
+ insertEmptyRow,
+ upsertRow,
};
});
diff --git a/packages/frontend/editor-ui/src/features/dataStore/typeGuards.ts b/packages/frontend/editor-ui/src/features/dataStore/typeGuards.ts
new file mode 100644
index 0000000000..c5f9e3ab11
--- /dev/null
+++ b/packages/frontend/editor-ui/src/features/dataStore/typeGuards.ts
@@ -0,0 +1,11 @@
+import type { DataStoreValue } from '@/features/dataStore/datastore.types';
+
+export const isDataStoreValue = (value: unknown): value is DataStoreValue => {
+ return (
+ value === null ||
+ typeof value === 'string' ||
+ typeof value === 'number' ||
+ typeof value === 'boolean' ||
+ value instanceof Date
+ );
+};