Skip to content

Commit

Permalink
Trello and Airtable search backend (#3974)
Browse files Browse the repository at this point in the history
* ✨ add search to Trello boards

* ✨ add RLC to Trello cards

* ♻️ use new versioning system for Trello v2

* 🐛 move list search out of /nodes/ and add check for required param

* ✨ re-add trello search methods

* 🥅 throw error if RLC search isn't sent a method name

This will likely be removed when the declarative style of search has
been added.

* ✨ add requires filter field to RLC search

* ✨ add searchable flag to Trello searches

* ✨ add RLC for cardId and boardId on all operations

* ✨ add ID and URL RLC to Airtable
  • Loading branch information
valya authored Sep 5, 2022
1 parent 77debab commit deb78b5
Show file tree
Hide file tree
Showing 25 changed files with 1,745 additions and 5,255 deletions.
2 changes: 1 addition & 1 deletion packages/cli/src/Server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -992,7 +992,7 @@ class App {
);
}

return { results: [] };
throw new ResponseError('Parameter methodName is required.', undefined, 400);
},
),
);
Expand Down
174 changes: 162 additions & 12 deletions packages/nodes-base/nodes/Airtable/Airtable.node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,35 @@ import { IExecuteFunctions } from 'n8n-core';

import {
IDataObject,
ILoadOptionsFunctions,
INodeExecutionData,
INodeListSearchResult,
INodeType,
INodeTypeDescription,
NodeOperationError,
} from 'n8n-workflow';

import { apiRequest, apiRequestAllItems, downloadRecordAttachments } from './GenericFunctions';

interface AirtableBase {
id: string;
name: string;
}

interface AirtableTable {
id: string;
name: string;
description: string;
}

export class Airtable implements INodeType {
description: INodeTypeDescription = {
displayName: 'Airtable',
name: 'airtable',
icon: 'file:airtable.svg',
group: ['input'],
version: 1,
version: [1, 2],
defaultVersion: 2,
description: 'Read, update, write and delete data from Airtable',
defaults: {
name: 'Airtable',
Expand Down Expand Up @@ -80,6 +94,11 @@ export class Airtable implements INodeType {
default: '',
required: true,
description: 'The ID of the base to access',
displayOptions: {
show: {
'@version': [1],
},
},
},
{
displayName: 'Table ID',
Expand All @@ -89,6 +108,121 @@ export class Airtable implements INodeType {
placeholder: 'Stories',
required: true,
description: 'The ID of the table to access',
displayOptions: {
show: {
'@version': [1],
},
},
},

{
displayName: 'Base ID',
name: 'applicationRLC',
type: 'resourceLocator',
default: { mode: 'list', value: '' },
required: true,
displayOptions: {
show: {
'@version': [2],
},
},
description: 'The ID of the base to access',
modes: [
// eslint-disable-next-line n8n-nodes-base/node-param-default-missing
{
displayName: 'ID',
name: 'id',
type: 'string',
hint: 'Enter base Id',
validation: [
{
type: 'regex',
properties: {
regex: '[a-zA-Z0-9]+',
errorMessage: 'ID value cannot be empty',
},
},
],
placeholder: 'appD3dfaeidke',
url: '=https://airtable.com/{{$value}}',
},
// eslint-disable-next-line n8n-nodes-base/node-param-default-missing
{
displayName: 'By URL',
name: 'url',
type: 'string',
hint: 'Enter base URL',
placeholder: 'https://airtable.com/app12DiScdfes/tblAAAAAAAAAAAAA/viwHdfasdfeieg5p',
validation: [
{
type: 'regex',
properties: {
regex: 'https://airtable.com/([a-zA-Z0-9]+)/[a-zA-Z0-9/]+',
errorMessage:
'URL has to be in the format: https://airtable.com/<base ID>/<table ID>/<view ID>',
},
},
],
extractValue: {
type: 'regex',
regex: 'https://airtable.com/([a-zA-Z0-9]+)',
},
},
],
},
{
displayName: 'Table ID',
name: 'tableRLC',
type: 'resourceLocator',
default: { mode: 'list', value: '' },
required: true,
displayOptions: {
show: {
'@version': [2],
},
},
description: 'The ID of the table',
modes: [
// eslint-disable-next-line n8n-nodes-base/node-param-default-missing
{
displayName: 'ID',
name: 'id',
type: 'string',
hint: 'Enter table Id',
validation: [
{
type: 'regex',
properties: {
regex: '[a-zA-Z0-9]+',
errorMessage: 'ID value cannot be empty',
},
},
],
placeholder: 'tbl3dirwqeidke',
},
// eslint-disable-next-line n8n-nodes-base/node-param-default-missing
{
displayName: 'By URL',
name: 'url',
type: 'string',
hint: 'Enter table URL',
placeholder: 'https://airtable.com/app12DiScdfes/tblAAAAAAAAAAAAA/viwHdfasdfeieg5p',
validation: [
{
type: 'regex',
properties: {
regex: 'https://airtable.com/[a-zA-Z0-9]+/([a-zA-Z0-9]+)',
errorMessage:
'URL has to be in the format: https://airtable.com/<base ID>/<table ID>/<view ID>',
},
},
],
extractValue: {
type: 'regex',
regex: 'https://airtable.com/[a-zA-Z0-9]+/([a-zA-Z0-9]+)',
},
},
],
},

// ----------------------------------
Expand Down Expand Up @@ -421,10 +555,27 @@ export class Airtable implements INodeType {
const returnData: INodeExecutionData[] = [];
let responseData;

const version = this.getNode().typeVersion;

const operation = this.getNodeParameter('operation', 0) as string;

const application = this.getNodeParameter('application', 0) as string;
const table = encodeURI(this.getNodeParameter('table', 0) as string);
let application: string;
if (version === 2) {
application = this.getNodeParameter('applicationRLC', 0, undefined, {
extractValue: true,
}) as string;
} else {
application = this.getNodeParameter('application', 0) as string;
}

let table: string;
if (version === 2) {
table = this.getNodeParameter('tableRLC', 0, undefined, {
extractValue: true,
}) as string;
} else {
table = this.getNodeParameter('table', 0) as string;
}

let returnAll = false;
let endpoint = '';
Expand Down Expand Up @@ -493,7 +644,7 @@ export class Airtable implements INodeType {
}
} catch (error) {
if (this.continueOnFail()) {
returnData.push({json: { error: error.message }});
returnData.push({ json: { error: error.message } });
continue;
}
throw error;
Expand Down Expand Up @@ -538,7 +689,7 @@ export class Airtable implements INodeType {
}
} catch (error) {
if (this.continueOnFail()) {
returnData.push({json:{ error: error.message }});
returnData.push({ json: { error: error.message } });
continue;
}
throw error;
Expand Down Expand Up @@ -589,14 +740,13 @@ export class Airtable implements INodeType {

// We can return from here
return [
this.helpers.constructExecutionMetaData(
this.helpers.returnJsonArray(returnData),
{ itemData: { item: 0 } },
),
this.helpers.constructExecutionMetaData(this.helpers.returnJsonArray(returnData), {
itemData: { item: 0 },
}),
];
} catch (error) {
if (this.continueOnFail()) {
returnData.push({json:{ error: error.message }});
returnData.push({ json: { error: error.message } });
} else {
throw error;
}
Expand Down Expand Up @@ -631,7 +781,7 @@ export class Airtable implements INodeType {
returnData.push(...executionData);
} catch (error) {
if (this.continueOnFail()) {
returnData.push({json:{ error: error.message }});
returnData.push({ json: { error: error.message } });
continue;
}
throw error;
Expand Down Expand Up @@ -718,7 +868,7 @@ export class Airtable implements INodeType {
}
} catch (error) {
if (this.continueOnFail()) {
returnData.push({json:{ error: error.message }});
returnData.push({ json: { error: error.message } });
continue;
}
throw error;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,76 @@ export const attachmentOperations: INodeProperties[] = [
];

export const attachmentFields: INodeProperties[] = [
{
displayName: 'Card ID',
name: 'cardIdAttachmentRLC',
type: 'resourceLocator',
default: { mode: 'list', value: '' },
required: true,
modes: [
// eslint-disable-next-line n8n-nodes-base/node-param-default-missing
{
displayName: 'From List',
name: 'list',
type: 'list',
hint: 'Select a card from the list',
placeholder: 'Choose...',
typeOptions: {
searchListMethod: 'searchCards',
searchFilterRequired: true,
searchable: true,
},
},
// eslint-disable-next-line n8n-nodes-base/node-param-default-missing
{
displayName: 'ID',
name: 'id',
type: 'string',
hint: 'Enter Card Id',
validation: [
{
type: 'regex',
properties: {
regex: '[a-zA-Z0-9]+',
errorMessage: 'ID value cannot be empty',
},
},
],
placeholder: 'wiIaGwqE',
url: '=https://trello.com/c/{{$value}}',
},
// eslint-disable-next-line n8n-nodes-base/node-param-default-missing
{
displayName: 'By URL',
name: 'url',
type: 'string',
hint: 'Enter Card URL',
placeholder: 'https://trello.com/c/e123456/card-name',
validation: [
{
type: 'regex',
properties: {
regex: 'http(s)?://trello.com/c/([a-zA-Z0-9]+)/[a-zA-Z0-9]+',
errorMessage:
'URL has to be in the format: http(s)://trello.com/c/<card ID>/<card name>',
},
},
],
extractValue: {
type: 'regex',
regex: 'https://trello.com/c/([a-zA-Z0-9]+)',
},
},
],
displayOptions: {
show: {
operation: ['delete', 'create', 'get', 'getAll'],
resource: ['attachment'],
'@version': [2],
},
},
description: 'The ID of the card',
},
// ----------------------------------
// attachment:create
// ----------------------------------
Expand All @@ -58,6 +128,7 @@ export const attachmentFields: INodeProperties[] = [
show: {
operation: ['create'],
resource: ['attachment'],
'@version': [1],
},
},
description: 'The ID of the card to add attachment to',
Expand Down Expand Up @@ -120,6 +191,7 @@ export const attachmentFields: INodeProperties[] = [
show: {
operation: ['delete'],
resource: ['attachment'],
'@version': [1],
},
},
description: 'The ID of the card that attachment belongs to',
Expand Down Expand Up @@ -152,6 +224,7 @@ export const attachmentFields: INodeProperties[] = [
show: {
operation: ['getAll'],
resource: ['attachment'],
'@version': [1],
},
},
description: 'The ID of the card to get attachments',
Expand Down Expand Up @@ -192,6 +265,7 @@ export const attachmentFields: INodeProperties[] = [
show: {
operation: ['get'],
resource: ['attachment'],
'@version': [1],
},
},
description: 'The ID of the card to get attachment',
Expand Down
Loading

0 comments on commit deb78b5

Please sign in to comment.