diff --git a/packages/nodes-base/nodes/Coda/Coda.node.ts b/packages/nodes-base/nodes/Coda/Coda.node.ts index d0274d1fb8..04d611aa53 100644 --- a/packages/nodes-base/nodes/Coda/Coda.node.ts +++ b/packages/nodes-base/nodes/Coda/Coda.node.ts @@ -17,6 +17,18 @@ import { tableFields, tableOperations, } from './TableDescription'; +import { + formulaFields, + formulaOperations, +} from './FormulaDescription'; +import { + controlFields, + controlOperations, +} from './ControlDescription'; +import { + viewFields, + viewOperations, +} from './ViewDescription'; export class Coda implements INodeType { description: INodeTypeDescription = { @@ -45,17 +57,38 @@ export class Coda implements INodeType { name: 'resource', type: 'options', options: [ + { + name: 'Control', + value: 'control', + description: 'Controls provide a user-friendly way to input a value that can affect other parts of the doc.', + }, + { + name: 'Formula', + value: 'formula', + description: 'Formulas can be great for performing one-off computations', + }, { name: 'Table', value: 'table', description: `Access data of tables in documents.`, }, + { + name: 'View', + value: 'view', + description: `Access data of views in documents.`, + }, ], default: 'table', description: 'Resource to consume.', }, ...tableOperations, ...tableFields, + ...formulaOperations, + ...formulaFields, + ...controlOperations, + ...controlFields, + ...viewOperations, + ...viewFields, ], }; @@ -66,12 +99,7 @@ export class Coda implements INodeType { async getDocs(this: ILoadOptionsFunctions): Promise { const returnData: INodePropertyOptions[] = []; const qs = {}; - let docs; - try { - docs = await codaApiRequestAllItems.call(this,'items', 'GET', `/docs`, {}, qs); - } catch (err) { - throw new Error(`Coda Error: ${err}`); - } + const docs = await codaApiRequestAllItems.call(this,'items', 'GET', `/docs`, {}, qs); for (const doc of docs) { const docName = doc.name; const docId = doc.id; @@ -86,15 +114,10 @@ export class Coda implements INodeType { // select them easily async getTables(this: ILoadOptionsFunctions): Promise { const returnData: INodePropertyOptions[] = []; - let tables; const docId = this.getCurrentNodeParameter('docId'); - try { - tables = await codaApiRequestAllItems.call(this, 'items', 'GET', `/docs/${docId}/tables`, {}); - } catch (err) { - throw new Error(`Coda Error: ${err}`); - } + const tables = await codaApiRequestAllItems.call(this, 'items', 'GET', `/docs/${docId}/tables`, {}); for (const table of tables) { const tableName = table.name; const tableId = table.id; @@ -105,6 +128,93 @@ export class Coda implements INodeType { } return returnData; }, + // Get all the available columns to display them to user so that he can + // select them easily + async getColumns(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + + const docId = this.getCurrentNodeParameter('docId'); + const tableId = this.getCurrentNodeParameter('tableId'); + + const columns = await codaApiRequestAllItems.call(this, 'items', 'GET', `/docs/${docId}/tables/${tableId}/columns`, {}); + for (const column of columns) { + const columnName = column.name; + const columnId = column.id; + returnData.push({ + name: columnName, + value: columnId, + }); + } + return returnData; + }, + // Get all the available views to display them to user so that he can + // select them easily + async getViews(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const docId = this.getCurrentNodeParameter('docId'); + const views = await codaApiRequestAllItems.call(this, 'items', 'GET', `/docs/${docId}/views`, {}); + for (const view of views) { + const viewName = view.name; + const viewId = view.id; + returnData.push({ + name: viewName, + value: viewId, + }); + } + return returnData; + }, + // Get all the available formulas to display them to user so that he can + // select them easily + async getFormulas(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const docId = this.getCurrentNodeParameter('docId'); + const formulas = await codaApiRequestAllItems.call(this, 'items', 'GET', `/docs/${docId}/formulas`, {}); + for (const formula of formulas) { + const formulaName = formula.name; + const formulaId = formula.id; + returnData.push({ + name: formulaName, + value: formulaId, + }); + } + return returnData; + }, + // Get all the available view rows to display them to user so that he can + // select them easily + async getViewRows(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const docId = this.getCurrentNodeParameter('docId'); + const viewId = this.getCurrentNodeParameter('viewId'); + const viewRows = await codaApiRequestAllItems.call(this, 'items', 'GET', `/docs/${docId}/views/${viewId}/rows`, {}); + for (const viewRow of viewRows) { + const viewRowName = viewRow.name; + const viewRowId = viewRow.id; + returnData.push({ + name: viewRowName, + value: viewRowId, + }); + } + return returnData; + }, + // Get all the available view columns to display them to user so that he can + // select them easily + async getViewColumns(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + + const docId = this.getCurrentNodeParameter('docId'); + const viewId = this.getCurrentNodeParameter('viewId'); + + const viewColumns = await codaApiRequestAllItems.call(this, 'items', 'GET', `/docs/${docId}/views/${viewId}/columns`, {}); + for (const viewColumn of viewColumns) { + const viewColumnName = viewColumn.name; + const viewColumnId = viewColumn.id; + returnData.push({ + name: viewColumnName, + value: viewColumnId, + }); + } + return returnData; + }, }, }; @@ -114,7 +224,6 @@ export class Coda implements INodeType { let responseData; const resource = this.getNodeParameter('resource', 0) as string; const operation = this.getNodeParameter('operation', 0) as string; - let qs: IDataObject = {}; if (resource === 'table') { @@ -217,6 +326,9 @@ export class Coda implements INodeType { if (options.visibleOnly) { qs.visibleOnly = options.visibleOnly as boolean; } + if (options.query) { + qs.query = options.query as string; + } try { if (returnAll === true) { responseData = await codaApiRequestAllItems.call(this, 'items', 'GET', endpoint, {}, qs); @@ -226,7 +338,7 @@ export class Coda implements INodeType { responseData = responseData.items; } } catch (err) { - throw new Error(`Flow Error: ${err.message}`); + throw new Error(`Coda Error: ${err.message}`); } if (options.rawData === true) { @@ -266,8 +378,257 @@ export class Coda implements INodeType { // Return the incoming data return [items]; } + // https://coda.io/developers/apis/v1beta1#operation/pushButton + if (operation === 'pushButton') { + for (let i = 0; i < items.length; i++) { + const docId = this.getNodeParameter('docId', i) as string; + const tableId = this.getNodeParameter('tableId', i) as string; + const rowId = this.getNodeParameter('rowId', i) as string; + const columnId = this.getNodeParameter('columnId', i) as string; + const endpoint = `/docs/${docId}/tables/${tableId}/rows/${rowId}/buttons/${columnId}`; + responseData = await codaApiRequest.call(this, 'POST', endpoint, {}); + returnData.push(responseData); + } + return [this.helpers.returnJsonArray(returnData)]; + } + //https://coda.io/developers/apis/v1beta1#operation/getColumn + if (operation === 'getColumn') { + for (let i = 0; i < items.length; i++) { + const docId = this.getNodeParameter('docId', i) as string; + const tableId = this.getNodeParameter('tableId', i) as string; + const columnId = this.getNodeParameter('columnId', i) as string; + const endpoint = `/docs/${docId}/tables/${tableId}/columns/${columnId}`; + responseData = await codaApiRequest.call(this, 'GET', endpoint, {}); + returnData.push(responseData); + } + return [this.helpers.returnJsonArray(returnData)]; + } + //https://coda.io/developers/apis/v1beta1#operation/listColumns + if (operation === 'getAllColumns') { + for (let i = 0; i < items.length; i++) { + const returnAll = this.getNodeParameter('returnAll', 0) as boolean; + const docId = this.getNodeParameter('docId', i) as string; + const tableId = this.getNodeParameter('tableId', i) as string; + const endpoint = `/docs/${docId}/tables/${tableId}/columns`; + if (returnAll) { + responseData = await codaApiRequestAllItems.call(this, 'items', 'GET', endpoint, {}); + } else { + qs.limit = this.getNodeParameter('limit', 0) as number; + responseData = await codaApiRequest.call(this, 'GET', endpoint, {}, qs); + responseData = responseData.items; + } + returnData.push.apply(returnData,responseData); + } + return [this.helpers.returnJsonArray(returnData)]; + } } + if (resource === 'formula') { + //https://coda.io/developers/apis/v1beta1#operation/getFormula + if (operation === 'get') { + for (let i = 0; i < items.length; i++) { + const docId = this.getNodeParameter('docId', i) as string; + const formulaId = this.getNodeParameter('formulaId', i) as string; + const endpoint = `/docs/${docId}/formulas/${formulaId}`; + responseData = await codaApiRequest.call(this, 'GET', endpoint, {}); + returnData.push(responseData); + } + return [this.helpers.returnJsonArray(returnData)]; + } + //https://coda.io/developers/apis/v1beta1#operation/listFormulas + if (operation === 'getAll') { + for (let i = 0; i < items.length; i++) { + const returnAll = this.getNodeParameter('returnAll', 0) as boolean; + const docId = this.getNodeParameter('docId', i) as string; + const endpoint = `/docs/${docId}/formulas`; + if (returnAll) { + responseData = await codaApiRequestAllItems.call(this, 'items', 'GET', endpoint, {}); + } else { + qs.limit = this.getNodeParameter('limit', 0) as number; + responseData = await codaApiRequest.call(this, 'GET', endpoint, {}, qs); + responseData = responseData.items; + } + returnData.push.apply(returnData,responseData); + } + return [this.helpers.returnJsonArray(returnData)]; + } + } + if (resource === 'control') { + //https://coda.io/developers/apis/v1beta1#operation/getControl + if (operation === 'get') { + for (let i = 0; i < items.length; i++) { + const docId = this.getNodeParameter('docId', i) as string; + const controlId = this.getNodeParameter('controlId', i) as string; + const endpoint = `/docs/${docId}/controls/${controlId}`; + responseData = await codaApiRequest.call(this, 'GET', endpoint, {}); + returnData.push(responseData); + } + return [this.helpers.returnJsonArray(returnData)]; + } + //https://coda.io/developers/apis/v1beta1#operation/listControls + if (operation === 'getAll') { + for (let i = 0; i < items.length; i++) { + const returnAll = this.getNodeParameter('returnAll', 0) as boolean; + const docId = this.getNodeParameter('docId', i) as string; + const endpoint = `/docs/${docId}/controls`; + if (returnAll) { + responseData = await codaApiRequestAllItems.call(this, 'items', 'GET', endpoint, {}); + } else { + qs.limit = this.getNodeParameter('limit', 0) as number; + responseData = await codaApiRequest.call(this, 'GET', endpoint, {}, qs); + responseData = responseData.items; + } + returnData.push.apply(returnData,responseData); + } + return [this.helpers.returnJsonArray(returnData)]; + } + } + if (resource === 'view') { + //https://coda.io/developers/apis/v1beta1#operation/getView + if (operation === 'get') { + for (let i = 0; i < items.length; i++) { + const docId = this.getNodeParameter('docId', i) as string; + const viewId = this.getNodeParameter('viewId', i) as string; + const endpoint = `/docs/${docId}/views/${viewId}`; + responseData = await codaApiRequest.call(this, 'GET', endpoint, {}); + returnData.push(responseData); + } + return [this.helpers.returnJsonArray(returnData)]; + } + //https://coda.io/developers/apis/v1beta1#operation/listViews + if (operation === 'getAll') { + for (let i = 0; i < items.length; i++) { + const returnAll = this.getNodeParameter('returnAll', 0) as boolean; + const docId = this.getNodeParameter('docId', i) as string; + const endpoint = `/docs/${docId}/views`; + if (returnAll) { + responseData = await codaApiRequestAllItems.call(this, 'items', 'GET', endpoint, {}); + } else { + qs.limit = this.getNodeParameter('limit', 0) as number; + responseData = await codaApiRequest.call(this, 'GET', endpoint, {}, qs); + responseData = responseData.items; + } + returnData.push.apply(returnData,responseData); + } + return [this.helpers.returnJsonArray(returnData)]; + } + if (operation === 'getAllViewRows') { + const docId = this.getNodeParameter('docId', 0) as string; + const returnAll = this.getNodeParameter('returnAll', 0) as boolean; + const viewId = this.getNodeParameter('viewId', 0) as string; + const options = this.getNodeParameter('options', 0) as IDataObject; + const endpoint = `/docs/${docId}/views/${viewId}/rows`; + if (options.useColumnNames === false) { + qs.useColumnNames = options.useColumnNames as boolean; + } else { + qs.useColumnNames = true; + } + if (options.valueFormat) { + qs.valueFormat = options.valueFormat as string; + } + if (options.sortBy) { + qs.sortBy = options.sortBy as string; + } + if (options.query) { + qs.query = options.query as string; + } + try { + if (returnAll === true) { + responseData = await codaApiRequestAllItems.call(this, 'items', 'GET', endpoint, {}, qs); + } else { + qs.limit = this.getNodeParameter('limit', 0) as number; + responseData = await codaApiRequest.call(this, 'GET', endpoint, {}, qs); + responseData = responseData.items; + } + } catch (err) { + throw new Error(`Coda Error: ${err.message}`); + } + if (options.rawData === true) { + return [this.helpers.returnJsonArray(responseData)]; + } else { + for (const item of responseData) { + returnData.push({ + id: item.id, + ...item.values, + }); + } + return [this.helpers.returnJsonArray(returnData)]; + } + } + //https://coda.io/developers/apis/v1beta1#operation/deleteViewRow + if (operation === 'deleteViewRow') { + for (let i = 0; i < items.length; i++) { + const docId = this.getNodeParameter('docId', i) as string; + const viewId = this.getNodeParameter('viewId', i) as string; + const rowId = this.getNodeParameter('rowId', i) as string; + const endpoint = `/docs/${docId}/views/${viewId}/rows/${rowId}`; + responseData = await codaApiRequest.call(this, 'DELETE', endpoint); + returnData.push.apply(returnData,responseData); + } + return [this.helpers.returnJsonArray(returnData)]; + } + //https://coda.io/developers/apis/v1beta1#operation/pushViewButton + if (operation === 'pushViewButton') { + for (let i = 0; i < items.length; i++) { + const docId = this.getNodeParameter('docId', i) as string; + const viewId = this.getNodeParameter('viewId', i) as string; + const rowId = this.getNodeParameter('rowId', i) as string; + const columnId = this.getNodeParameter('columnId', i) as string; + const endpoint = `/docs/${docId}/views/${viewId}/rows/${rowId}/buttons/${columnId}`; + responseData = await codaApiRequest.call(this, 'POST', endpoint); + returnData.push.apply(returnData,responseData); + } + return [this.helpers.returnJsonArray(returnData)]; + } + if (operation === 'getAllViewColumns') { + for (let i = 0; i < items.length; i++) { + const returnAll = this.getNodeParameter('returnAll', 0) as boolean; + const docId = this.getNodeParameter('docId', i) as string; + const viewId = this.getNodeParameter('viewId', i) as string; + const endpoint = `/docs/${docId}/views/${viewId}/columns`; + if (returnAll) { + responseData = await codaApiRequestAllItems.call(this, 'items', 'GET', endpoint, {}); + } else { + qs.limit = this.getNodeParameter('limit', 0) as number; + responseData = await codaApiRequest.call(this, 'GET', endpoint, {}, qs); + responseData = responseData.items; + } + returnData.push.apply(returnData,responseData); + } + return [this.helpers.returnJsonArray(returnData)]; + } + //https://coda.io/developers/apis/v1beta1#operation/updateViewRow + if (operation === 'updateViewRow') { + for (let i = 0; i < items.length; i++) { + qs = {}; + const docId = this.getNodeParameter('docId', i) as string; + const viewId = this.getNodeParameter('viewId', i) as string; + const rowId = this.getNodeParameter('rowId', i) as string; + const keyName = this.getNodeParameter('keyName', i) as string; + const options = this.getNodeParameter('options', i) as IDataObject; + const body: IDataObject = {}; + const endpoint = `/docs/${docId}/views/${viewId}/rows/${rowId}`; + if (options.disableParsing) { + qs.disableParsing = options.disableParsing as boolean; + } + const cells = []; + cells.length = 0; + //@ts-ignore + for (const key of Object.keys(items[i].json[keyName])) { + cells.push({ + column: key, + //@ts-ignore + value: items[i].json[keyName][key], + }); + } + body.row = { + cells + }, + await codaApiRequest.call(this, 'PUT', endpoint, body, qs); + } + return [items]; + } + } return []; } } diff --git a/packages/nodes-base/nodes/Coda/ControlDescription.ts b/packages/nodes-base/nodes/Coda/ControlDescription.ts new file mode 100644 index 0000000000..49311d549b --- /dev/null +++ b/packages/nodes-base/nodes/Coda/ControlDescription.ts @@ -0,0 +1,141 @@ +import { INodeProperties } from 'n8n-workflow'; + +export const controlOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'control', + ], + }, + }, + options: [ + { + name: 'Get', + value: 'get', + description: 'Get a control', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Get all controls', + }, + ], + default: 'get', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const controlFields = [ + +/* -------------------------------------------------------------------------- */ +/* control:get */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Doc', + name: 'docId', + type: 'options', + required: true, + typeOptions: { + loadOptionsMethod: 'getDocs', + }, + default: '', + displayOptions: { + show: { + resource: [ + 'control', + ], + operation: [ + 'get', + ] + }, + }, + description: 'ID of the doc.', + }, + { + displayName: 'Control ID', + name: 'controlId', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'control', + ], + operation: [ + 'get', + ] + }, + }, + description: 'The control to get the row from.', + }, +/* -------------------------------------------------------------------------- */ +/* control:getAll */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Doc', + name: 'docId', + type: 'options', + required: true, + typeOptions: { + loadOptionsMethod: 'getDocs', + }, + default: '', + displayOptions: { + show: { + resource: [ + 'control', + ], + operation: [ + 'getAll', + ] + }, + }, + description: 'ID of the doc.', + }, + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + resource: [ + 'control', + ], + operation: [ + 'getAll', + ] + }, + }, + default: false, + description: 'If all results should be returned or only up to a given limit.', + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + displayOptions: { + show: { + resource: [ + 'control', + ], + operation: [ + 'getAll', + ], + returnAll: [ + false, + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 100, + }, + default: 50, + description: 'How many results to return.', + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Coda/FormulaDescription.ts b/packages/nodes-base/nodes/Coda/FormulaDescription.ts new file mode 100644 index 0000000000..9e6ddbc043 --- /dev/null +++ b/packages/nodes-base/nodes/Coda/FormulaDescription.ts @@ -0,0 +1,141 @@ +import { INodeProperties } from 'n8n-workflow'; + +export const formulaOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'formula', + ], + }, + }, + options: [ + { + name: 'Get', + value: 'get', + description: 'Get a formula', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Get all formulas', + }, + ], + default: 'get', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const formulaFields = [ + +/* -------------------------------------------------------------------------- */ +/* formula:get */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Doc', + name: 'docId', + type: 'options', + required: true, + typeOptions: { + loadOptionsMethod: 'getDocs', + }, + default: '', + displayOptions: { + show: { + resource: [ + 'formula', + ], + operation: [ + 'get', + ] + }, + }, + description: 'ID of the doc.', + }, + { + displayName: 'Formula ID', + name: 'formulaId', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'formula', + ], + operation: [ + 'get', + ] + }, + }, + description: 'The formula to get the row from.', + }, +/* -------------------------------------------------------------------------- */ +/* formula:getAll */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Doc', + name: 'docId', + type: 'options', + required: true, + typeOptions: { + loadOptionsMethod: 'getDocs', + }, + default: '', + displayOptions: { + show: { + resource: [ + 'formula', + ], + operation: [ + 'getAll', + ] + }, + }, + description: 'ID of the doc.', + }, + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + resource: [ + 'formula', + ], + operation: [ + 'getAll', + ] + }, + }, + default: false, + description: 'If all results should be returned or only up to a given limit.', + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + displayOptions: { + show: { + resource: [ + 'formula', + ], + operation: [ + 'getAll', + ], + returnAll: [ + false, + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 100, + }, + default: 50, + description: 'How many results to return.', + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Coda/GenericFunctions.ts b/packages/nodes-base/nodes/Coda/GenericFunctions.ts index 2d26d593a7..5752a81314 100644 --- a/packages/nodes-base/nodes/Coda/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Coda/GenericFunctions.ts @@ -32,7 +32,7 @@ export async function codaApiRequest(this: IExecuteFunctions | IExecuteSingleFun errorMessage = error.response.body.message || error.response.body.Message || error.message; } - throw new Error(errorMessage); + throw new Error('Coda Error: ' + errorMessage); } } diff --git a/packages/nodes-base/nodes/Coda/TableDescription.ts b/packages/nodes-base/nodes/Coda/TableDescription.ts index 647d92e1cb..4eb40bbe6a 100644 --- a/packages/nodes-base/nodes/Coda/TableDescription.ts +++ b/packages/nodes-base/nodes/Coda/TableDescription.ts @@ -19,9 +19,14 @@ export const tableOperations = [ description: 'Create/Upsert a row', }, { - name: 'Get Row', - value: 'getRow', - description: 'Get row', + name: 'Delete Row', + value: 'deleteRow', + description: 'Delete one or multiple rows', + }, + { + name: 'Get All Columns', + value: 'getAllColumns', + description: 'Get all columns', }, { name: 'Get All Rows', @@ -29,9 +34,19 @@ export const tableOperations = [ description: 'Get all the rows', }, { - name: 'Delete Row', - value: 'deleteRow', - description: 'Delete one or multiple rows', + name: 'Get Column', + value: 'getColumn', + description: 'Get a column', + }, + { + name: 'Get Row', + value: 'getRow', + description: 'Get row', + }, + { + name: 'Push Button', + value: 'pushButton', + description: 'Pushes a button', }, ], default: 'createRow', @@ -42,7 +57,7 @@ export const tableOperations = [ export const tableFields = [ /* -------------------------------------------------------------------------- */ -/* table:createRow */ +/* table:createRow */ /* -------------------------------------------------------------------------- */ { displayName: 'Doc', @@ -106,14 +121,6 @@ export const tableFields = [ }, }, options: [ - { - displayName: 'Key Columns', - name: 'keyColumns', - type: 'string', - default: '', - description: `Optional column IDs, URLs, or names (fragile and discouraged), - specifying columns to be used as upsert keys. If more than one separate by ,`, - }, { displayName: 'Disable Parsing', name: 'disableParsing', @@ -121,11 +128,18 @@ export const tableFields = [ default: false, description: `If true, the API will not attempt to parse the data in any way.`, }, + { + displayName: 'Key Columns', + name: 'keyColumns', + type: 'string', + default: '', + description: `Optional column IDs, URLs, or names (fragile and discouraged)
, + specifying columns to be used as upsert keys. If more than one separate by ,`, + }, ] }, - /* -------------------------------------------------------------------------- */ -/* table:get */ +/* table:get */ /* -------------------------------------------------------------------------- */ { displayName: 'Doc', @@ -188,9 +202,11 @@ export const tableFields = [ ] }, }, - description: `ID or name of the row. Names are discouraged because they're easily prone to being changed by users. - If you're using a name, be sure to URI-encode it. - If there are multiple rows with the same value in the identifying column, an arbitrary one will be selected`, + description: `ID or name of the row. Names are discouraged because
+ they're easily prone to being changed by users. If you're
+ using a name, be sure to URI-encode it. If there are
+ multiple rows with the same value in the identifying column,
+ an arbitrary one will be selected`, }, { displayName: 'Options', @@ -209,6 +225,13 @@ export const tableFields = [ }, }, options: [ + { + displayName: 'RAW Data', + name: 'rawData', + type: 'boolean', + default: false, + description: `Returns the data exactly in the way it got received from the API.`, + }, { displayName: 'Use Column Names', name: 'useColumnNames', @@ -218,13 +241,6 @@ export const tableFields = [ This is generally discouraged as it is fragile. If columns are renamed,
code using original names may throw errors.`, }, - { - displayName: 'RAW Data', - name: 'rawData', - type: 'boolean', - default: false, - description: `Returns the data exactly in the way it got received from the API.`, - }, { displayName: 'ValueFormat', name: 'valueFormat', @@ -249,7 +265,7 @@ export const tableFields = [ ] }, /* -------------------------------------------------------------------------- */ -/* table:getAll */ +/* table:getAll */ /* -------------------------------------------------------------------------- */ { displayName: 'Doc', @@ -355,34 +371,16 @@ export const tableFields = [ }, options: [ { - displayName: 'Use Column Names', - name: 'useColumnNames', - type: 'boolean', - default: false, - description: `Use column names instead of column IDs in the returned output.
- This is generally discouraged as it is fragile. If columns are renamed,
- code using original names may throw errors.`, - }, - { - displayName: 'ValueFormat', - name: 'valueFormat', - type: 'options', + displayName: 'Query', + name: 'query', + type: 'string', + typeOptions: { + alwaysOpenEditWindow: true, + }, default: '', - options: [ - { - name: 'Simple', - value: 'simple', - }, - { - name: 'Simple With Arrays', - value: 'simpleWithArrays', - }, - { - name: 'Rich', - value: 'rich', - }, - ], - description: `The format that cell values are returned as.`, + description: `Query used to filter returned rows, specified as :.
+ If you'd like to use a column name instead of an ID, you must quote it (e.g., "My Column":123).
+ Also note that value is a JSON value; if you'd like to use a string, you must surround it in quotes (e.g., "groceries").`, }, { displayName: 'RAW Data', @@ -406,9 +404,39 @@ export const tableFields = [ value: 'natural', }, ], - description: `Specifies the sort order of the rows returned. + description: `Specifies the sort order of the rows returned.
If left unspecified, rows are returned by creation time ascending.`, }, + { + displayName: 'Use Column Names', + name: 'useColumnNames', + type: 'boolean', + default: false, + description: `Use column names instead of column IDs in the returned output.
+ This is generally discouraged as it is fragile. If columns
+ are renamed, code using original names may throw errors.`, + }, + { + displayName: 'ValueFormat', + name: 'valueFormat', + type: 'options', + default: '', + options: [ + { + name: 'Simple', + value: 'simple', + }, + { + name: 'Simple With Arrays', + value: 'simpleWithArrays', + }, + { + name: 'Rich', + value: 'rich', + }, + ], + description: `The format that cell values are returned as.`, + }, { displayName: 'Visible Only', name: 'visibleOnly', @@ -484,5 +512,253 @@ export const tableFields = [ }, description: 'Row IDs to delete.', }, - +/* -------------------------------------------------------------------------- */ +/* table:pushButton */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Doc', + name: 'docId', + type: 'options', + required: true, + typeOptions: { + loadOptionsMethod: 'getDocs', + }, + default: '', + displayOptions: { + show: { + resource: [ + 'table', + ], + operation: [ + 'pushButton', + ] + }, + }, + description: 'ID of the doc.', + }, + { + displayName: 'Table', + name: 'tableId', + type: 'options', + typeOptions: { + loadOptionsDependsOn: [ + 'docId', + ], + loadOptionsMethod: 'getTables', + }, + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'table', + ], + operation: [ + 'pushButton', + ] + }, + }, + description: 'The table to get the row from.', + }, + { + displayName: 'Row ID', + name: 'rowId', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'table', + ], + operation: [ + 'pushButton', + ] + }, + }, + description: `ID or name of the row. Names are discouraged because
+ they're easily prone to being changed by users. If you're
+ using a name, be sure to URI-encode it. If there are multiple
+ rows with the same value in the identifying column, an arbitrary
+ one will be selected`, + }, + { + displayName: 'Column', + name: 'columnId', + type: 'options', + required: true, + typeOptions: { + loadOptionsMethod: 'getColumns', + loadOptionsDependsOn: [ + 'docId', + 'tableId', + ], + }, + default: '', + displayOptions: { + show: { + resource: [ + 'table', + ], + operation: [ + 'pushButton', + ] + }, + }, + }, +/* -------------------------------------------------------------------------- */ +/* table:getColumn */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Doc', + name: 'docId', + type: 'options', + required: true, + typeOptions: { + loadOptionsMethod: 'getDocs', + }, + default: '', + displayOptions: { + show: { + resource: [ + 'table', + ], + operation: [ + 'getColumn', + ] + }, + }, + description: 'ID of the doc.', + }, + { + displayName: 'Table', + name: 'tableId', + type: 'options', + typeOptions: { + loadOptionsDependsOn: [ + 'docId', + ], + loadOptionsMethod: 'getTables', + }, + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'table', + ], + operation: [ + 'getColumn', + ] + }, + }, + description: 'The table to get the row from.', + }, + { + displayName: 'Column ID', + name: 'columnId', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'table', + ], + operation: [ + 'getColumn', + ] + }, + }, + description: 'The table to get the row from.', + }, +/* -------------------------------------------------------------------------- */ +/* table:getAllColumns */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Doc', + name: 'docId', + type: 'options', + required: true, + typeOptions: { + loadOptionsMethod: 'getDocs', + }, + default: '', + displayOptions: { + show: { + resource: [ + 'table', + ], + operation: [ + 'getAllColumns', + ] + }, + }, + description: 'ID of the doc.', + }, + { + displayName: 'Table', + name: 'tableId', + type: 'options', + typeOptions: { + loadOptionsDependsOn: [ + 'docId', + ], + loadOptionsMethod: 'getTables', + }, + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'table', + ], + operation: [ + 'getAllColumns', + ] + }, + }, + description: 'The table to get the row from.', + }, + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + resource: [ + 'table', + ], + operation: [ + 'getAllColumns', + ] + }, + }, + default: false, + description: 'If all results should be returned or only up to a given limit.', + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + displayOptions: { + show: { + resource: [ + 'table', + ], + operation: [ + 'getAllColumns', + ], + returnAll: [ + false, + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 100, + }, + default: 50, + description: 'How many results to return.', + }, ] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Coda/ViewDescription.ts b/packages/nodes-base/nodes/Coda/ViewDescription.ts new file mode 100644 index 0000000000..d9c8472609 --- /dev/null +++ b/packages/nodes-base/nodes/Coda/ViewDescription.ts @@ -0,0 +1,714 @@ +import { INodeProperties } from 'n8n-workflow'; + +export const viewOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'view', + ], + }, + }, + options: [ + { + name: 'Delete Row', + value: 'deleteViewRow', + description: 'Delete view row', + }, + { + name: 'Get', + value: 'get', + description: 'Get a view', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Get all views', + }, + { + name: 'Get Columns', + value: 'getAllViewColumns', + description: 'Get all views columns', + }, + { + name: 'Get Rows', + value: 'getAllViewRows', + description: 'Get all views rows', + }, + { + name: 'Update Row', + value: 'updateViewRow', + description: 'Update row', + }, + { + name: 'Push Button', + value: 'pushViewButton', + description: 'Push view button', + }, + ], + default: 'get', + description: 'The operation to perform.', + }, +] as INodeProperties[]; + +export const viewFields = [ + +/* -------------------------------------------------------------------------- */ +/* view:get */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Doc', + name: 'docId', + type: 'options', + required: true, + typeOptions: { + loadOptionsMethod: 'getDocs', + }, + default: '', + displayOptions: { + show: { + resource: [ + 'view', + ], + operation: [ + 'get', + ] + }, + }, + description: 'ID of the doc.', + }, + { + displayName: 'View ID', + name: 'viewId', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'view', + ], + operation: [ + 'get', + ] + }, + }, + description: 'The view to get the row from.', + }, +/* -------------------------------------------------------------------------- */ +/* view:getAll */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Doc', + name: 'docId', + type: 'options', + required: true, + typeOptions: { + loadOptionsMethod: 'getDocs', + }, + default: '', + displayOptions: { + show: { + resource: [ + 'view', + ], + operation: [ + 'getAll', + ] + }, + }, + description: 'ID of the doc.', + }, + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + resource: [ + 'view', + ], + operation: [ + 'getAll', + ] + }, + }, + default: false, + description: 'If all results should be returned or only up to a given limit.', + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + displayOptions: { + show: { + resource: [ + 'view', + ], + operation: [ + 'getAll', + ], + returnAll: [ + false, + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 100, + }, + default: 50, + description: 'How many results to return.', + }, +/* -------------------------------------------------------------------------- */ +/* view:getAllViewRows */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Doc', + name: 'docId', + type: 'options', + required: true, + typeOptions: { + loadOptionsMethod: 'getDocs', + }, + default: '', + displayOptions: { + show: { + resource: [ + 'view', + ], + operation: [ + 'getAllViewRows', + ] + }, + }, + description: 'ID of the doc.', + }, + { + displayName: 'View', + name: 'viewId', + type: 'options', + typeOptions: { + loadOptionsDependsOn: [ + 'docId', + ], + loadOptionsMethod: 'getViews', + }, + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'view', + ], + operation: [ + 'getAllViewRows', + ] + }, + }, + description: 'The table to get the rows from.', + }, + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + resource: [ + 'view', + ], + operation: [ + 'getAllViewRows', + ] + }, + }, + default: false, + description: 'If all results should be returned or only up to a given limit.', + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + displayOptions: { + show: { + resource: [ + 'view', + ], + operation: [ + 'getAllViewRows', + ], + returnAll: [ + false, + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 100, + }, + default: 50, + description: 'How many results to return.', + }, + { + displayName: 'Options', + name: 'options', + type: 'collection', + placeholder: 'Add Option', + default: {}, + displayOptions: { + show: { + resource: [ + 'view', + ], + operation: [ + 'getAllViewRows', + ], + }, + }, + options: [ + { + displayName: 'Query', + name: 'query', + type: 'string', + typeOptions: { + alwaysOpenEditWindow: true, + }, + default: '', + description: `Query used to filter returned rows, specified as :.
+ If you'd like to use a column name instead of an ID, you must quote it (e.g., "My Column":123).
+ Also note that value is a JSON value; if you'd like to use a string, you must surround it in quotes (e.g., "groceries").`, + }, + { + displayName: 'Use Column Names', + name: 'useColumnNames', + type: 'boolean', + default: false, + description: `Use column names instead of column IDs in the returned output.
+ This is generally discouraged as it is fragile. If columns are renamed,
+ code using original names may throw errors.`, + }, + { + displayName: 'ValueFormat', + name: 'valueFormat', + type: 'options', + default: '', + options: [ + { + name: 'Simple', + value: 'simple', + }, + { + name: 'Simple With Arrays', + value: 'simpleWithArrays', + }, + { + name: 'Rich', + value: 'rich', + }, + ], + description: `The format that cell values are returned as.`, + }, + { + displayName: 'RAW Data', + name: 'rawData', + type: 'boolean', + default: false, + description: `Returns the data exactly in the way it got received from the API.`, + }, + { + displayName: 'Sort By', + name: 'sortBy', + type: 'options', + default: '', + options: [ + { + name: 'Created At', + value: 'createdAt', + }, + { + name: 'Natural', + value: 'natural', + }, + ], + description: `Specifies the sort order of the rows returned.
+ If left unspecified, rows are returned by creation time ascending.`, + }, + ] + }, +/* -------------------------------------------------------------------------- */ +/* view:getAllViewColumns */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Doc', + name: 'docId', + type: 'options', + required: true, + typeOptions: { + loadOptionsMethod: 'getDocs', + }, + default: '', + displayOptions: { + show: { + resource: [ + 'view', + ], + operation: [ + 'getAllViewColumns', + ] + }, + }, + description: 'ID of the doc.', + }, + { + displayName: 'View', + name: 'viewId', + type: 'options', + typeOptions: { + loadOptionsDependsOn: [ + 'docId', + ], + loadOptionsMethod: 'getViews', + }, + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'view', + ], + operation: [ + 'getAllViewColumns', + ] + }, + }, + description: 'The table to get the rows from.', + }, + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + resource: [ + 'view', + ], + operation: [ + 'getAllViewColumns', + ] + }, + }, + default: false, + description: 'If all results should be returned or only up to a given limit.', + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + displayOptions: { + show: { + resource: [ + 'view', + ], + operation: [ + 'getAllViewColumns', + ], + returnAll: [ + false, + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 100, + }, + default: 50, + description: 'How many results to return.', + }, +/* -------------------------------------------------------------------------- */ +/* view:deleteViewRow */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Doc', + name: 'docId', + type: 'options', + required: true, + typeOptions: { + loadOptionsMethod: 'getDocs', + }, + default: '', + displayOptions: { + show: { + resource: [ + 'view', + ], + operation: [ + 'deleteViewRow', + ] + }, + }, + description: 'ID of the doc.', + }, + { + displayName: 'View', + name: 'viewId', + type: 'options', + required: true, + default: '', + typeOptions: { + loadOptionsMethod: 'getViews', + loadOptionsDependsOn: [ + 'docId', + ], + }, + displayOptions: { + show: { + resource: [ + 'view', + ], + operation: [ + 'deleteViewRow', + ] + }, + }, + description: 'The view to get the row from.', + }, + { + displayName: 'Row', + name: 'rowId', + type: 'options', + required: true, + default: '', + typeOptions: { + loadOptionsMethod: 'getViewRows', + loadOptionsDependsOn: [ + 'viewId', + ], + }, + displayOptions: { + show: { + resource: [ + 'view', + ], + operation: [ + 'deleteViewRow', + ] + }, + }, + description: 'The view to get the row from.', + }, +/* -------------------------------------------------------------------------- */ +/* view:pushViewButton */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Doc', + name: 'docId', + type: 'options', + required: true, + typeOptions: { + loadOptionsMethod: 'getDocs', + }, + default: '', + displayOptions: { + show: { + resource: [ + 'view', + ], + operation: [ + 'pushViewButton', + ] + }, + }, + description: 'ID of the doc.', + }, + { + displayName: 'View', + name: 'viewId', + type: 'options', + required: true, + default: '', + typeOptions: { + loadOptionsMethod: 'getViews', + loadOptionsDependsOn: [ + 'docId', + ], + }, + displayOptions: { + show: { + resource: [ + 'view', + ], + operation: [ + 'pushViewButton', + ] + }, + }, + description: 'The view to get the row from.', + }, + { + displayName: 'Row', + name: 'rowId', + type: 'options', + required: true, + default: '', + typeOptions: { + loadOptionsMethod: 'getViewRows', + loadOptionsDependsOn: [ + 'viewId', + ], + }, + displayOptions: { + show: { + resource: [ + 'view', + ], + operation: [ + 'pushViewButton', + ] + }, + }, + description: 'The view to get the row from.', + }, + { + displayName: 'Column', + name: 'columnId', + type: 'options', + required: true, + typeOptions: { + loadOptionsMethod: 'getViewColumns', + loadOptionsDependsOn: [ + 'docId', + 'viewId', + ], + }, + default: '', + displayOptions: { + show: { + resource: [ + 'view', + ], + operation: [ + 'pushViewButton', + ] + }, + }, + }, +/* -------------------------------------------------------------------------- */ +/* view:updateViewRow */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Doc', + name: 'docId', + type: 'options', + required: true, + typeOptions: { + loadOptionsMethod: 'getDocs', + }, + default: '', + displayOptions: { + show: { + resource: [ + 'view', + ], + operation: [ + 'updateViewRow', + ] + }, + }, + description: 'ID of the doc.', + }, + { + displayName: 'View', + name: 'viewId', + type: 'options', + required: true, + default: '', + typeOptions: { + loadOptionsMethod: 'getViews', + loadOptionsDependsOn: [ + 'docId', + ], + }, + displayOptions: { + show: { + resource: [ + 'view', + ], + operation: [ + 'updateViewRow', + ] + }, + }, + description: 'The view to get the row from.', + }, + { + displayName: 'Row', + name: 'rowId', + type: 'options', + required: true, + default: '', + typeOptions: { + loadOptionsMethod: 'getViewRows', + loadOptionsDependsOn: [ + 'viewId', + ], + }, + displayOptions: { + show: { + resource: [ + 'view', + ], + operation: [ + 'updateViewRow', + ] + }, + }, + description: 'The view to get the row from.', + }, + { + displayName: 'Key Name', + name: 'keyName', + type: 'string', + required: true, + default: 'columns', + displayOptions: { + show: { + resource: [ + 'view', + ], + operation: [ + 'updateViewRow', + ] + }, + }, + description: 'The view to get the row from.', + }, + { + displayName: 'Options', + name: 'options', + type: 'collection', + placeholder: 'Add Option', + default: {}, + displayOptions: { + show: { + resource: [ + 'view', + ], + operation: [ + 'updateViewRow', + ], + }, + }, + options: [ + { + displayName: 'Disable Parsing', + name: 'disableParsing', + type: 'boolean', + default: false, + description: `If true, the API will not attempt to parse the data in any way.`, + }, + ] + }, +] as INodeProperties[];