n8n/packages/nodes-base/nodes/SeaTable/SeaTable.node.ts

320 lines
10 KiB
TypeScript
Raw Normal View History

import {
IExecuteFunctions,
} from 'n8n-core';
import {
IDataObject,
ILoadOptionsFunctions,
INodeExecutionData,
INodePropertyOptions,
INodeType,
INodeTypeDescription,
NodeOperationError,
} from 'n8n-workflow';
import {
getTableColumns,
getTableViews,
rowExport,
rowFormatColumns,
rowMapKeyToName,
seaTableApiRequest,
setableApiRequestAllItems,
split,
updateAble,
} from './GenericFunctions';
import {
rowFields,
rowOperations,
} from './RowDescription';
import {
TColumnsUiValues,
TColumnValue,
} from './types';
import {
ICtx,
IRow,
IRowObject,
} from './Interfaces';
export class SeaTable implements INodeType {
description: INodeTypeDescription = {
displayName: 'SeaTable',
name: 'seaTable',
icon: 'file:seaTable.svg',
group: ['input'],
version: 1,
subtitle: '={{$parameter["resource"] + ": " + $parameter["operation"]}}',
description: 'Consume the SeaTable API',
defaults: {
name: 'SeaTable',
color: '#FF8000',
},
inputs: ['main'],
outputs: ['main'],
credentials: [
{
name: 'seaTableApi',
required: true,
},
],
properties: [
{
displayName: 'Resource',
name: 'resource',
type: 'options',
options: [
{
name: 'Row',
value: 'row',
},
],
default: 'row',
description: 'The resource to operate on',
},
...rowOperations,
...rowFields,
],
};
methods = {
loadOptions: {
async getTableNames(this: ILoadOptionsFunctions) {
const returnData: INodePropertyOptions[] = [];
const { metadata: { tables } } = await seaTableApiRequest.call(this, {}, 'GET', `/dtable-server/api/v1/dtables/{{dtable_uuid}}/metadata`);
for (const table of tables) {
returnData.push({
name: table.name,
value: table.name,
});
}
return returnData;
},
async getTableIds(this: ILoadOptionsFunctions) {
const returnData: INodePropertyOptions[] = [];
const { metadata: { tables } } = await seaTableApiRequest.call(this, {}, 'GET', `/dtable-server/api/v1/dtables/{{dtable_uuid}}/metadata`);
for (const table of tables) {
returnData.push({
name: table.name,
value: table._id,
});
}
return returnData;
},
async getTableUpdateAbleColumns(this: ILoadOptionsFunctions) {
const tableName = this.getNodeParameter('tableName') as string;
const columns = await getTableColumns.call(this, tableName,);
return columns.filter(column => column.editable).map(column => ({ name: column.name, value: column.name }));
},
async getAllSortableColumns(this: ILoadOptionsFunctions) {
const tableName = this.getNodeParameter('tableName') as string;
const columns = await getTableColumns.call(this, tableName);
return columns.filter(column => !['file', 'image', 'url', 'collaborator', 'long-text'].includes(column.type)).map(column => ({ name: column.name, value: column.name }));
},
async getViews(this: ILoadOptionsFunctions) {
const tableName = this.getNodeParameter('tableName') as string;
const views = await getTableViews.call(this, tableName);
return views.map(view => ({ name: view.name, value: view.name }));
},
},
};
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
const items = this.getInputData();
const returnData: IDataObject[] = [];
let responseData;
const resource = this.getNodeParameter('resource', 0) as string;
const operation = this.getNodeParameter('operation', 0) as string;
const body: IDataObject = {};
const qs: IDataObject = {};
const ctx: ICtx = {};
if (resource === 'row') {
if (operation === 'create') {
// ----------------------------------
// row:create
// ----------------------------------
const tableName = this.getNodeParameter('tableName', 0) as string;
const tableColumns = await getTableColumns.call(this, tableName);
body.table_name = tableName;
const fieldsToSend = this.getNodeParameter('fieldsToSend', 0) as 'defineBelow' | 'autoMapInputData';
let rowInput: IRowObject = {};
for (let i = 0; i < items.length; i++) {
rowInput = {} as IRowObject;
try {
if (fieldsToSend === 'autoMapInputData') {
const incomingKeys = Object.keys(items[i].json);
const inputDataToIgnore = split(this.getNodeParameter('inputsToIgnore', i, '') as string);
for (const key of incomingKeys) {
if (inputDataToIgnore.includes(key)) continue;
rowInput[key] = items[i].json[key] as TColumnValue;
}
} else {
const columns = this.getNodeParameter('columnsUi.columnValues', i, []) as TColumnsUiValues;
for (const column of columns) {
rowInput[column.columnName] = column.columnValue;
}
}
body.row = rowExport(rowInput, updateAble(tableColumns));
responseData = await seaTableApiRequest.call(this, ctx, 'POST', `/dtable-server/api/v1/dtables/{{dtable_uuid}}/rows/`, body);
const { _id: insertId } = responseData;
if (insertId === undefined) {
throw new NodeOperationError(this.getNode(), 'SeaTable: No identity after appending row.');
}
const newRowInsertData = rowMapKeyToName(responseData, tableColumns);
qs.table_name = tableName;
qs.convert = true;
const newRow = await seaTableApiRequest.call(this, ctx, 'GET', `/dtable-server/api/v1/dtables/{{dtable_uuid}}/rows/${encodeURIComponent(insertId)}/`, body, qs);
if (newRow._id === undefined) {
throw new NodeOperationError(this.getNode(), 'SeaTable: No identity for appended row.');
}
const row = rowFormatColumns({ ...newRowInsertData, ...newRow }, tableColumns.map(({ name }) => name).concat(['_id', '_ctime', '_mtime']));
returnData.push(row);
} catch (error) {
if (this.continueOnFail()) {
returnData.push({ error: error.message });
continue;
}
throw error;
}
}
} else if (operation === 'get') {
for (let i = 0; i < items.length; i++) {
try {
const tableId = this.getNodeParameter('tableId', 0) as string;
const rowId = this.getNodeParameter('rowId', i) as string;
const response = await seaTableApiRequest.call(this, ctx, 'GET', `/dtable-server/api/v1/dtables/{{dtable_uuid}}/rows/${rowId}`, {}, { table_id: tableId, convert: true }) as IDataObject;
returnData.push(response);
} catch (error) {
if (this.continueOnFail()) {
returnData.push({ error: error.message });
continue;
}
throw error;
}
}
} else if (operation === 'getAll') {
// ----------------------------------
// row:getAll
// ----------------------------------
const tableName = this.getNodeParameter('tableName', 0) as string;
const tableColumns = await getTableColumns.call(this, tableName);
try {
for (let i = 0; i < items.length; i++) {
const endpoint = `/dtable-server/api/v1/dtables/{{dtable_uuid}}/rows/`;
qs.table_name = tableName;
const filters = this.getNodeParameter('filters', i) as IDataObject;
const options = this.getNodeParameter('options', i) as IDataObject;
const returnAll = this.getNodeParameter('returnAll', 0) as boolean;
Object.assign(qs, filters, options);
if (returnAll) {
responseData = await setableApiRequestAllItems.call(this, ctx, 'rows', 'GET', endpoint, body, qs);
} else {
qs.limit = this.getNodeParameter('limit', 0) as number;
responseData = await seaTableApiRequest.call(this, ctx, 'GET', endpoint, body, qs);
responseData = responseData.rows;
}
const rows = responseData.map((row: IRow) => rowFormatColumns({ ...row }, tableColumns.map(({ name }) => name).concat(['_id', '_ctime', '_mtime'])));
returnData.push(...rows);
}
} catch (error) {
if (this.continueOnFail()) {
returnData.push({ error: error.message });
}
throw error;
}
} else if (operation === 'delete') {
for (let i = 0; i < items.length; i++) {
try {
const tableName = this.getNodeParameter('tableName', 0) as string;
const rowId = this.getNodeParameter('rowId', i) as string;
const body: IDataObject = {
table_name: tableName,
row_id: rowId,
};
const response = await seaTableApiRequest.call(this, ctx, 'DELETE', `/dtable-server/api/v1/dtables/{{dtable_uuid}}/rows/`, body, qs) as IDataObject;
returnData.push(response);
} catch (error) {
if (this.continueOnFail()) {
returnData.push({ error: error.message });
continue;
}
throw error;
}
}
} else if (operation === 'update') {
// ----------------------------------
// row:update
// ----------------------------------
const tableName = this.getNodeParameter('tableName', 0) as string;
const tableColumns = await getTableColumns.call(this, tableName);
body.table_name = tableName;
const fieldsToSend = this.getNodeParameter('fieldsToSend', 0) as 'defineBelow' | 'autoMapInputData';
let rowInput: IRowObject = {};
for (let i = 0; i < items.length; i++) {
const rowId = this.getNodeParameter('rowId', i) as string;
rowInput = {} as IRowObject;
try {
if (fieldsToSend === 'autoMapInputData') {
const incomingKeys = Object.keys(items[i].json);
const inputDataToIgnore = split(this.getNodeParameter('inputsToIgnore', i, '') as string);
for (const key of incomingKeys) {
if (inputDataToIgnore.includes(key)) continue;
rowInput[key] = items[i].json[key] as TColumnValue;
}
} else {
const columns = this.getNodeParameter('columnsUi.columnValues', i, []) as TColumnsUiValues;
for (const column of columns) {
rowInput[column.columnName] = column.columnValue;
}
}
body.row = rowExport(rowInput, updateAble(tableColumns));
body.table_name = tableName;
body.row_id = rowId;
responseData = await seaTableApiRequest.call(this, ctx, 'PUT', `/dtable-server/api/v1/dtables/{{dtable_uuid}}/rows/`, body);
2021-09-29 17:24:34 -07:00
returnData.push({ _id: rowId, ... responseData });
} catch (error) {
if (this.continueOnFail()) {
returnData.push({ error: error.message });
continue;
}
throw error;
}
}
} else {
throw new NodeOperationError(this.getNode(), `The operation "${operation}" is not known!`);
}
}
return [this.helpers.returnJsonArray(returnData)];
}
}