mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
commit
618ae1a82b
|
@ -11,7 +11,9 @@ RUN \
|
||||||
# Set a custom user to not have n8n run as root
|
# Set a custom user to not have n8n run as root
|
||||||
USER root
|
USER root
|
||||||
|
|
||||||
RUN npm_config_user=root npm install -g n8n@${N8N_VERSION}
|
RUN npm_config_user=root npm install -g full-icu n8n@${N8N_VERSION}
|
||||||
|
|
||||||
|
ENV NODE_ICU_DATA /usr/local/lib/node_modules/full-icu
|
||||||
|
|
||||||
WORKDIR /data
|
WORKDIR /data
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
FROM node:12.13.0-alpine
|
FROM node:12.16-alpine
|
||||||
|
|
||||||
ARG N8N_VERSION
|
ARG N8N_VERSION
|
||||||
|
|
||||||
|
@ -13,9 +13,11 @@ USER root
|
||||||
# Install n8n and the also temporary all the packages
|
# Install n8n and the also temporary all the packages
|
||||||
# it needs to build it correctly.
|
# it needs to build it correctly.
|
||||||
RUN apk --update add --virtual build-dependencies python build-base ca-certificates && \
|
RUN apk --update add --virtual build-dependencies python build-base ca-certificates && \
|
||||||
npm_config_user=root npm install -g n8n@${N8N_VERSION} && \
|
npm_config_user=root npm install -g full-icu n8n@${N8N_VERSION} && \
|
||||||
apk del build-dependencies
|
apk del build-dependencies
|
||||||
|
|
||||||
|
ENV NODE_ICU_DATA /usr/local/lib/node_modules/full-icu
|
||||||
|
|
||||||
WORKDIR /data
|
WORKDIR /data
|
||||||
|
|
||||||
COPY docker-entrypoint.sh /docker-entrypoint.sh
|
COPY docker-entrypoint.sh /docker-entrypoint.sh
|
||||||
|
|
|
@ -156,6 +156,7 @@ Replace the following placeholders with the actual data:
|
||||||
- <POSTGRES_PASSWORD>
|
- <POSTGRES_PASSWORD>
|
||||||
- <POSTGRES_PORT>
|
- <POSTGRES_PORT>
|
||||||
- <POSTGRES_USER>
|
- <POSTGRES_USER>
|
||||||
|
- <POSTGRES_SCHEMA>
|
||||||
|
|
||||||
```
|
```
|
||||||
docker run -it --rm \
|
docker run -it --rm \
|
||||||
|
@ -166,6 +167,7 @@ docker run -it --rm \
|
||||||
-e DB_POSTGRESDB_HOST=<POSTGRES_HOST> \
|
-e DB_POSTGRESDB_HOST=<POSTGRES_HOST> \
|
||||||
-e DB_POSTGRESDB_PORT=<POSTGRES_PORT> \
|
-e DB_POSTGRESDB_PORT=<POSTGRES_PORT> \
|
||||||
-e DB_POSTGRESDB_USER=<POSTGRES_USER> \
|
-e DB_POSTGRESDB_USER=<POSTGRES_USER> \
|
||||||
|
-e DB_POSTGRESDB_SCHEMA=<POSTGRES_SCHEMA> \
|
||||||
-e DB_POSTGRESDB_PASSWORD=<POSTGRES_PASSWORD> \
|
-e DB_POSTGRESDB_PASSWORD=<POSTGRES_PASSWORD> \
|
||||||
-v ~/.n8n:/root/.n8n \
|
-v ~/.n8n:/root/.n8n \
|
||||||
n8nio/n8n \
|
n8nio/n8n \
|
||||||
|
@ -214,6 +216,7 @@ The following environment variables support file input:
|
||||||
- DB_POSTGRESDB_PASSWORD_FILE
|
- DB_POSTGRESDB_PASSWORD_FILE
|
||||||
- DB_POSTGRESDB_PORT_FILE
|
- DB_POSTGRESDB_PORT_FILE
|
||||||
- DB_POSTGRESDB_USER_FILE
|
- DB_POSTGRESDB_USER_FILE
|
||||||
|
- DB_POSTGRESDB_SCHEMA_FILE
|
||||||
- N8N_BASIC_AUTH_PASSWORD_FILE
|
- N8N_BASIC_AUTH_PASSWORD_FILE
|
||||||
- N8N_BASIC_AUTH_USER_FILE
|
- N8N_BASIC_AUTH_USER_FILE
|
||||||
|
|
||||||
|
|
|
@ -137,6 +137,19 @@ export NODE_FUNCTION_ALLOW_EXTERNAL=moment,lodash
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## SSL
|
||||||
|
|
||||||
|
It is possible to start n8n with SSL enabled by supplying a certificate to use:
|
||||||
|
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export N8N_PROTOCOL=https
|
||||||
|
export N8N_SSL_KEY=/data/certs/server.key
|
||||||
|
export N8N_SSL_CERT=/data/certs/server.pem
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Timezone
|
## Timezone
|
||||||
|
|
||||||
The timezone is set by default to "America/New_York". It gets for example used by the
|
The timezone is set by default to "America/New_York". It gets for example used by the
|
||||||
|
@ -177,3 +190,52 @@ webhook URLs get registred with external services.
|
||||||
```bash
|
```bash
|
||||||
export WEBHOOK_TUNNEL_URL="https://n8n.example.com/"
|
export WEBHOOK_TUNNEL_URL="https://n8n.example.com/"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Configuration via file
|
||||||
|
|
||||||
|
It is also possible to configure n8n via a configuration file.
|
||||||
|
|
||||||
|
It is not necessary to define all values. Only the ones which should be
|
||||||
|
different from the defaults.
|
||||||
|
|
||||||
|
If needed also multiple files can be supplied to for example have generic
|
||||||
|
base settings and some specific ones depending on the environment.
|
||||||
|
|
||||||
|
The path to the JSON configuration file to use can be set via the environment
|
||||||
|
variable `N8N_CONFIG_FILES`.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Single file
|
||||||
|
export N8N_CONFIG_FILES=/folder/my-config.json
|
||||||
|
|
||||||
|
# Multiple files can be comma-separated
|
||||||
|
export N8N_CONFIG_FILES=/folder/my-config.json,/folder/production.json
|
||||||
|
```
|
||||||
|
|
||||||
|
A possible configuration file could look like this:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"executions": {
|
||||||
|
"process": "main",
|
||||||
|
"saveDataOnSuccess": "none"
|
||||||
|
},
|
||||||
|
"generic": {
|
||||||
|
"timezone": "Europe/Berlin"
|
||||||
|
},
|
||||||
|
"security": {
|
||||||
|
"basicAuth": {
|
||||||
|
"active": true,
|
||||||
|
"user": "frank",
|
||||||
|
"password": "some-secure-password"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nodes": {
|
||||||
|
"exclude": "[\"n8n-nodes-base.executeCommand\",\"n8n-nodes-base.writeBinaryFile\"]"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
All possible values which can be set and their defaults can be found here:
|
||||||
|
|
||||||
|
[https://github.com/n8n-io/n8n/blob/master/packages/cli/config/index.ts](https://github.com/n8n-io/n8n/blob/master/packages/cli/config/index.ts)
|
||||||
|
|
|
@ -4,6 +4,13 @@ By default, n8n uses SQLite to save credentials, past executions, and workflows.
|
||||||
n8n however also supports MongoDB and PostgresDB.
|
n8n however also supports MongoDB and PostgresDB.
|
||||||
|
|
||||||
|
|
||||||
|
## Shared Settings
|
||||||
|
|
||||||
|
The following environment variables get used by all databases:
|
||||||
|
|
||||||
|
- `DB_TABLE_PREFIX` (default: '') - Prefix for table names
|
||||||
|
|
||||||
|
|
||||||
## MongoDB
|
## MongoDB
|
||||||
|
|
||||||
!> **WARNING**: Use Postgres if possible! Mongo has problems with saving large
|
!> **WARNING**: Use Postgres if possible! Mongo has problems with saving large
|
||||||
|
@ -38,6 +45,7 @@ To use PostgresDB as database you can provide the following environment variable
|
||||||
- `DB_POSTGRESDB_PORT` (default: 5432)
|
- `DB_POSTGRESDB_PORT` (default: 5432)
|
||||||
- `DB_POSTGRESDB_USER` (default: 'root')
|
- `DB_POSTGRESDB_USER` (default: 'root')
|
||||||
- `DB_POSTGRESDB_PASSWORD` (default: empty)
|
- `DB_POSTGRESDB_PASSWORD` (default: empty)
|
||||||
|
- `DB_POSTGRESDB_SCHEMA` (default: 'public')
|
||||||
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
@ -47,6 +55,7 @@ export DB_POSTGRESDB_HOST=postgresdb
|
||||||
export DB_POSTGRESDB_PORT=5432
|
export DB_POSTGRESDB_PORT=5432
|
||||||
export DB_POSTGRESDB_USER=n8n
|
export DB_POSTGRESDB_USER=n8n
|
||||||
export DB_POSTGRESDB_PASSWORD=n8n
|
export DB_POSTGRESDB_PASSWORD=n8n
|
||||||
|
export DB_POSTGRESDB_SCHEMA=n8n
|
||||||
|
|
||||||
n8n start
|
n8n start
|
||||||
```
|
```
|
||||||
|
|
|
@ -49,6 +49,7 @@ The following special variables are available:
|
||||||
|
|
||||||
- **$binary**: Incoming binary data of a node
|
- **$binary**: Incoming binary data of a node
|
||||||
- **$data**: Incoming JSON data of a node
|
- **$data**: Incoming JSON data of a node
|
||||||
|
- **$evaluateExpression**: Evaluates a string as expression
|
||||||
- **$env**: Environment variables
|
- **$env**: Environment variables
|
||||||
- **$node**: Data of other nodes (output-data, parameters)
|
- **$node**: Data of other nodes (output-data, parameters)
|
||||||
- **$parameters**: Parameters of the current node
|
- **$parameters**: Parameters of the current node
|
||||||
|
|
|
@ -99,6 +99,22 @@ const channel = $node["Slack"].parameter["channel"];
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
#### Method: evaluateExpression(expression: string, itemIndex: number)
|
||||||
|
|
||||||
|
Evaluates a given string as expression.
|
||||||
|
If no `itemIndex` is provided it uses by default in the Function-Node the data of item 0 and
|
||||||
|
in the Function Item-Node the data of the current item.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
items[0].json.variable1 = evaluateExpression('{{1+2}}');
|
||||||
|
items[0].json.variable2 = evaluateExpression($node["Set"].json["myExpression"], 1);
|
||||||
|
|
||||||
|
return items;
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
#### Method: getWorkflowStaticData(type)
|
#### Method: getWorkflowStaticData(type)
|
||||||
|
|
||||||
Gives access to the static workflow data.
|
Gives access to the static workflow data.
|
||||||
|
@ -114,7 +130,7 @@ same in the whole workflow. And every node in the workflow can access it. The no
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
```typescript
|
```javascript
|
||||||
// Get the global workflow static data
|
// Get the global workflow static data
|
||||||
const staticData = getWorkflowStaticData('global');
|
const staticData = getWorkflowStaticData('global');
|
||||||
// Get the static data of the node
|
// Get the static data of the node
|
||||||
|
|
|
@ -13,5 +13,6 @@ The following environment variables support file input:
|
||||||
- DB_POSTGRESDB_PASSWORD_FILE
|
- DB_POSTGRESDB_PASSWORD_FILE
|
||||||
- DB_POSTGRESDB_PORT_FILE
|
- DB_POSTGRESDB_PORT_FILE
|
||||||
- DB_POSTGRESDB_USER_FILE
|
- DB_POSTGRESDB_USER_FILE
|
||||||
|
- DB_POSTGRESDB_SCHEMA_FILE
|
||||||
- N8N_BASIC_AUTH_PASSWORD_FILE
|
- N8N_BASIC_AUTH_PASSWORD_FILE
|
||||||
- N8N_BASIC_AUTH_USER_FILE
|
- N8N_BASIC_AUTH_USER_FILE
|
||||||
|
|
|
@ -20,10 +20,6 @@ import {
|
||||||
} from "../src";
|
} from "../src";
|
||||||
|
|
||||||
|
|
||||||
// // Add support for internationalization
|
|
||||||
// const fullIcuPath = require.resolve('full-icu');
|
|
||||||
// process.env.NODE_ICU_DATA = dirname(fullIcuPath);
|
|
||||||
|
|
||||||
let activeWorkflowRunner: ActiveWorkflowRunner.ActiveWorkflowRunner | undefined;
|
let activeWorkflowRunner: ActiveWorkflowRunner.ActiveWorkflowRunner | undefined;
|
||||||
let processExistCode = 0;
|
let processExistCode = 0;
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,12 @@ const config = convict({
|
||||||
env: 'DB_MONGODB_CONNECTION_URL'
|
env: 'DB_MONGODB_CONNECTION_URL'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
tablePrefix: {
|
||||||
|
doc: 'Prefix for table names',
|
||||||
|
format: '*',
|
||||||
|
default: '',
|
||||||
|
env: 'DB_TABLE_PREFIX'
|
||||||
|
},
|
||||||
postgresdb: {
|
postgresdb: {
|
||||||
database: {
|
database: {
|
||||||
doc: 'PostgresDB Database',
|
doc: 'PostgresDB Database',
|
||||||
|
@ -51,6 +57,12 @@ const config = convict({
|
||||||
default: 'root',
|
default: 'root',
|
||||||
env: 'DB_POSTGRESDB_USER'
|
env: 'DB_POSTGRESDB_USER'
|
||||||
},
|
},
|
||||||
|
schema: {
|
||||||
|
doc: 'PostgresDB Schema',
|
||||||
|
format: String,
|
||||||
|
default: 'public',
|
||||||
|
env: 'DB_POSTGRESDB_SCHEMA'
|
||||||
|
},
|
||||||
},
|
},
|
||||||
mysqldb: {
|
mysqldb: {
|
||||||
database: {
|
database: {
|
||||||
|
@ -163,6 +175,18 @@ const config = convict({
|
||||||
env: 'N8N_PROTOCOL',
|
env: 'N8N_PROTOCOL',
|
||||||
doc: 'HTTP Protocol via which n8n can be reached'
|
doc: 'HTTP Protocol via which n8n can be reached'
|
||||||
},
|
},
|
||||||
|
ssl_key: {
|
||||||
|
format: String,
|
||||||
|
default: 'server.key',
|
||||||
|
env: 'N8N_SSL_KEY',
|
||||||
|
doc: 'SSL Key for HTTPS Protocol'
|
||||||
|
},
|
||||||
|
ssl_cert: {
|
||||||
|
format: String,
|
||||||
|
default: 'server.pem',
|
||||||
|
env: 'N8N_SSL_CERT',
|
||||||
|
doc: 'SSL Cert for HTTPS Protocol'
|
||||||
|
},
|
||||||
|
|
||||||
security: {
|
security: {
|
||||||
basicAuth: {
|
basicAuth: {
|
||||||
|
@ -261,6 +285,15 @@ const config = convict({
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Overwrite default configuration with settings which got defined in
|
||||||
|
// optional configuration files
|
||||||
|
if (process.env.N8N_CONFIG_FILES !== undefined) {
|
||||||
|
const configFiles = process.env.N8N_CONFIG_FILES.split(',');
|
||||||
|
console.log(`\nLoading configuration overwrites from:\n - ${configFiles.join('\n - ')}\n`);
|
||||||
|
|
||||||
|
config.loadFile(configFiles);
|
||||||
|
}
|
||||||
|
|
||||||
config.validate({
|
config.validate({
|
||||||
allowed: 'strict',
|
allowed: 'strict',
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "n8n",
|
"name": "n8n",
|
||||||
"version": "0.55.1",
|
"version": "0.59.0",
|
||||||
"description": "n8n Workflow Automation Tool",
|
"description": "n8n Workflow Automation Tool",
|
||||||
"license": "SEE LICENSE IN LICENSE.md",
|
"license": "SEE LICENSE IN LICENSE.md",
|
||||||
"homepage": "https://n8n.io",
|
"homepage": "https://n8n.io",
|
||||||
|
@ -94,10 +94,10 @@
|
||||||
"lodash.get": "^4.4.2",
|
"lodash.get": "^4.4.2",
|
||||||
"mongodb": "^3.2.3",
|
"mongodb": "^3.2.3",
|
||||||
"mysql2": "^2.0.1",
|
"mysql2": "^2.0.1",
|
||||||
"n8n-core": "~0.26.0",
|
"n8n-core": "~0.28.0",
|
||||||
"n8n-editor-ui": "~0.37.0",
|
"n8n-editor-ui": "~0.39.0",
|
||||||
"n8n-nodes-base": "~0.50.1",
|
"n8n-nodes-base": "~0.54.0",
|
||||||
"n8n-workflow": "~0.23.0",
|
"n8n-workflow": "~0.25.0",
|
||||||
"open": "^7.0.0",
|
"open": "^7.0.0",
|
||||||
"pg": "^7.11.0",
|
"pg": "^7.11.0",
|
||||||
"request-promise-native": "^1.0.7",
|
"request-promise-native": "^1.0.7",
|
||||||
|
|
|
@ -42,6 +42,7 @@ export async function init(synchronize?: boolean): Promise<IDatabaseCollections>
|
||||||
entities = MongoDb;
|
entities = MongoDb;
|
||||||
connectionOptions = {
|
connectionOptions = {
|
||||||
type: 'mongodb',
|
type: 'mongodb',
|
||||||
|
entityPrefix: await GenericHelpers.getConfigValue('database.tablePrefix') as string,
|
||||||
url: await GenericHelpers.getConfigValue('database.mongodb.connectionUrl') as string,
|
url: await GenericHelpers.getConfigValue('database.mongodb.connectionUrl') as string,
|
||||||
useNewUrlParser: true,
|
useNewUrlParser: true,
|
||||||
};
|
};
|
||||||
|
@ -52,11 +53,13 @@ export async function init(synchronize?: boolean): Promise<IDatabaseCollections>
|
||||||
entities = PostgresDb;
|
entities = PostgresDb;
|
||||||
connectionOptions = {
|
connectionOptions = {
|
||||||
type: 'postgres',
|
type: 'postgres',
|
||||||
|
entityPrefix: await GenericHelpers.getConfigValue('database.tablePrefix') as string,
|
||||||
database: await GenericHelpers.getConfigValue('database.postgresdb.database') as string,
|
database: await GenericHelpers.getConfigValue('database.postgresdb.database') as string,
|
||||||
host: await GenericHelpers.getConfigValue('database.postgresdb.host') as string,
|
host: await GenericHelpers.getConfigValue('database.postgresdb.host') as string,
|
||||||
password: await GenericHelpers.getConfigValue('database.postgresdb.password') as string,
|
password: await GenericHelpers.getConfigValue('database.postgresdb.password') as string,
|
||||||
port: await GenericHelpers.getConfigValue('database.postgresdb.port') as number,
|
port: await GenericHelpers.getConfigValue('database.postgresdb.port') as number,
|
||||||
username: await GenericHelpers.getConfigValue('database.postgresdb.user') as string,
|
username: await GenericHelpers.getConfigValue('database.postgresdb.user') as string,
|
||||||
|
schema: await GenericHelpers.getConfigValue('database.postgresdb.schema') as string,
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -66,10 +69,11 @@ export async function init(synchronize?: boolean): Promise<IDatabaseCollections>
|
||||||
connectionOptions = {
|
connectionOptions = {
|
||||||
type: 'mysql',
|
type: 'mysql',
|
||||||
database: await GenericHelpers.getConfigValue('database.mysqldb.database') as string,
|
database: await GenericHelpers.getConfigValue('database.mysqldb.database') as string,
|
||||||
|
entityPrefix: await GenericHelpers.getConfigValue('database.tablePrefix') as string,
|
||||||
host: await GenericHelpers.getConfigValue('database.mysqldb.host') as string,
|
host: await GenericHelpers.getConfigValue('database.mysqldb.host') as string,
|
||||||
password: await GenericHelpers.getConfigValue('database.mysqldb.password') as string,
|
password: await GenericHelpers.getConfigValue('database.mysqldb.password') as string,
|
||||||
port: await GenericHelpers.getConfigValue('database.mysqldb.port') as number,
|
port: await GenericHelpers.getConfigValue('database.mysqldb.port') as number,
|
||||||
username: await GenericHelpers.getConfigValue('database.mysqldb.user') as string
|
username: await GenericHelpers.getConfigValue('database.mysqldb.user') as string,
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -79,6 +83,7 @@ export async function init(synchronize?: boolean): Promise<IDatabaseCollections>
|
||||||
connectionOptions = {
|
connectionOptions = {
|
||||||
type: 'sqlite',
|
type: 'sqlite',
|
||||||
database: path.join(n8nFolder, 'database.sqlite'),
|
database: path.join(n8nFolder, 'database.sqlite'),
|
||||||
|
entityPrefix: await GenericHelpers.getConfigValue('database.tablePrefix') as string,
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
import * as express from 'express';
|
import * as express from 'express';
|
||||||
|
import {
|
||||||
|
readFileSync,
|
||||||
|
} from 'fs';
|
||||||
import {
|
import {
|
||||||
dirname as pathDirname,
|
dirname as pathDirname,
|
||||||
join as pathJoin,
|
join as pathJoin,
|
||||||
|
@ -97,6 +100,10 @@ class App {
|
||||||
push: Push.Push;
|
push: Push.Push;
|
||||||
versions: IPackageVersions | undefined;
|
versions: IPackageVersions | undefined;
|
||||||
|
|
||||||
|
protocol: string;
|
||||||
|
sslKey: string;
|
||||||
|
sslCert: string;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.app = express();
|
this.app = express();
|
||||||
|
|
||||||
|
@ -112,6 +119,10 @@ class App {
|
||||||
this.push = Push.getInstance();
|
this.push = Push.getInstance();
|
||||||
|
|
||||||
this.activeExecutionsInstance = ActiveExecutions.getInstance();
|
this.activeExecutionsInstance = ActiveExecutions.getInstance();
|
||||||
|
|
||||||
|
this.protocol = config.get('protocol');
|
||||||
|
this.sslKey = config.get('ssl_key');
|
||||||
|
this.sslCert = config.get('ssl_cert');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1099,16 +1110,7 @@ class App {
|
||||||
// Removes a test webhook
|
// Removes a test webhook
|
||||||
this.app.delete('/rest/test-webhook/:id', ResponseHelper.send(async (req: express.Request, res: express.Response): Promise<boolean> => {
|
this.app.delete('/rest/test-webhook/:id', ResponseHelper.send(async (req: express.Request, res: express.Response): Promise<boolean> => {
|
||||||
const workflowId = req.params.id;
|
const workflowId = req.params.id;
|
||||||
|
return this.testWebhooks.cancelTestWebhook(workflowId);
|
||||||
const workflowData = await Db.collections.Workflow!.findOne(workflowId);
|
|
||||||
if (workflowData === undefined) {
|
|
||||||
throw new ResponseHelper.ResponseError(`Could not find workflow with id "${workflowId}" so webhook could not be deleted!`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const nodeTypes = NodeTypes();
|
|
||||||
const workflow = new Workflow({ id: workflowId.toString(), name: workflowData.name, nodes: workflowData.nodes, connections: workflowData.connections, active: workflowData.active, nodeTypes, staticData: workflowData.staticData, settings: workflowData.settings });
|
|
||||||
|
|
||||||
return this.testWebhooks.cancelTestWebhook(workflowId, workflow);
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
@ -1264,7 +1266,20 @@ export async function start(): Promise<void> {
|
||||||
|
|
||||||
await app.config();
|
await app.config();
|
||||||
|
|
||||||
app.app.listen(PORT, async () => {
|
let server;
|
||||||
|
|
||||||
|
if(app.protocol === 'https'){
|
||||||
|
const https = require('https');
|
||||||
|
const privateKey = readFileSync(app.sslKey,'utf8');
|
||||||
|
const cert = readFileSync(app.sslCert,'utf8');
|
||||||
|
const credentials = { key: privateKey,cert };
|
||||||
|
server = https.createServer(credentials,app.app);
|
||||||
|
}else{
|
||||||
|
const http = require('http');
|
||||||
|
server = http.createServer(app.app);
|
||||||
|
}
|
||||||
|
|
||||||
|
server.listen(PORT, async () => {
|
||||||
const versions = await GenericHelpers.getVersions();
|
const versions = await GenericHelpers.getVersions();
|
||||||
console.log(`n8n ready on port ${PORT}`);
|
console.log(`n8n ready on port ${PORT}`);
|
||||||
console.log(`Version: ${versions.cli}`);
|
console.log(`Version: ${versions.cli}`);
|
||||||
|
|
|
@ -131,7 +131,7 @@ export class TestWebhooks {
|
||||||
|
|
||||||
// Remove test-webhooks automatically if they do not get called (after 120 seconds)
|
// Remove test-webhooks automatically if they do not get called (after 120 seconds)
|
||||||
const timeout = setTimeout(() => {
|
const timeout = setTimeout(() => {
|
||||||
this.cancelTestWebhook(workflowData.id.toString(), workflow);
|
this.cancelTestWebhook(workflowData.id.toString());
|
||||||
}, 120000);
|
}, 120000);
|
||||||
|
|
||||||
let key: string;
|
let key: string;
|
||||||
|
@ -143,10 +143,10 @@ export class TestWebhooks {
|
||||||
workflowData,
|
workflowData,
|
||||||
};
|
};
|
||||||
await this.activeWebhooks!.add(workflow, webhookData, mode);
|
await this.activeWebhooks!.add(workflow, webhookData, mode);
|
||||||
}
|
|
||||||
|
|
||||||
// Save static data!
|
// Save static data!
|
||||||
await WorkflowHelpers.saveStaticData(workflow);
|
this.testWebhookData[key].workflowData.staticData = workflow.staticData;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -159,7 +159,9 @@ export class TestWebhooks {
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
* @memberof TestWebhooks
|
* @memberof TestWebhooks
|
||||||
*/
|
*/
|
||||||
cancelTestWebhook(workflowId: string, workflow: Workflow): boolean {
|
cancelTestWebhook(workflowId: string): boolean {
|
||||||
|
const nodeTypes = NodeTypes();
|
||||||
|
|
||||||
let foundWebhook = false;
|
let foundWebhook = false;
|
||||||
for (const webhookKey of Object.keys(this.testWebhookData)) {
|
for (const webhookKey of Object.keys(this.testWebhookData)) {
|
||||||
const webhookData = this.testWebhookData[webhookKey];
|
const webhookData = this.testWebhookData[webhookKey];
|
||||||
|
@ -182,6 +184,9 @@ export class TestWebhooks {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const workflowData = webhookData.workflowData;
|
||||||
|
const workflow = new Workflow({ id: workflowData.id.toString(), name: workflowData.name, nodes: workflowData.nodes, connections: workflowData.connections, active: workflowData.active, nodeTypes, staticData: workflowData.staticData, settings: workflowData.settings });
|
||||||
|
|
||||||
// Remove the webhook
|
// Remove the webhook
|
||||||
delete this.testWebhookData[webhookKey];
|
delete this.testWebhookData[webhookKey];
|
||||||
this.activeWebhooks!.removeWorkflow(workflow);
|
this.activeWebhooks!.removeWorkflow(workflow);
|
||||||
|
@ -207,7 +212,7 @@ export class TestWebhooks {
|
||||||
for (const webhookKey of Object.keys(this.testWebhookData)) {
|
for (const webhookKey of Object.keys(this.testWebhookData)) {
|
||||||
workflowData = this.testWebhookData[webhookKey].workflowData;
|
workflowData = this.testWebhookData[webhookKey].workflowData;
|
||||||
workflow = new Workflow({ id: workflowData.id.toString(), name: workflowData.name, nodes: workflowData.nodes, connections: workflowData.connections, active: workflowData.active, nodeTypes, staticData: workflowData.staticData, settings: workflowData.settings });
|
workflow = new Workflow({ id: workflowData.id.toString(), name: workflowData.name, nodes: workflowData.nodes, connections: workflowData.connections, active: workflowData.active, nodeTypes, staticData: workflowData.staticData, settings: workflowData.settings });
|
||||||
workflows.push();
|
workflows.push(workflow);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.activeWebhooks.removeAll(workflows);
|
return this.activeWebhooks.removeAll(workflows);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "n8n-core",
|
"name": "n8n-core",
|
||||||
"version": "0.26.0",
|
"version": "0.28.0",
|
||||||
"description": "Core functionality of n8n",
|
"description": "Core functionality of n8n",
|
||||||
"license": "SEE LICENSE IN LICENSE.md",
|
"license": "SEE LICENSE IN LICENSE.md",
|
||||||
"homepage": "https://n8n.io",
|
"homepage": "https://n8n.io",
|
||||||
|
@ -44,7 +44,7 @@
|
||||||
"crypto-js": "3.1.9-1",
|
"crypto-js": "3.1.9-1",
|
||||||
"lodash.get": "^4.4.2",
|
"lodash.get": "^4.4.2",
|
||||||
"mmmagic": "^0.5.2",
|
"mmmagic": "^0.5.2",
|
||||||
"n8n-workflow": "~0.23.0",
|
"n8n-workflow": "~0.25.0",
|
||||||
"p-cancelable": "^2.0.0",
|
"p-cancelable": "^2.0.0",
|
||||||
"request-promise-native": "^1.0.7"
|
"request-promise-native": "^1.0.7"
|
||||||
},
|
},
|
||||||
|
|
|
@ -490,6 +490,9 @@ export function getExecuteFunctions(workflow: Workflow, runExecutionData: IRunEx
|
||||||
continueOnFail: () => {
|
continueOnFail: () => {
|
||||||
return continueOnFail(node);
|
return continueOnFail(node);
|
||||||
},
|
},
|
||||||
|
evaluateExpression: (expression: string, itemIndex: number) => {
|
||||||
|
return workflow.resolveSimpleParameterValue('=' + expression, runExecutionData, runIndex, itemIndex, node.name, connectionInputData);
|
||||||
|
},
|
||||||
async executeWorkflow(workflowInfo: IExecuteWorkflowInfo, inputData?: INodeExecutionData[]): Promise<any> { // tslint:disable-line:no-any
|
async executeWorkflow(workflowInfo: IExecuteWorkflowInfo, inputData?: INodeExecutionData[]): Promise<any> { // tslint:disable-line:no-any
|
||||||
return additionalData.executeWorkflow(workflowInfo, additionalData, inputData);
|
return additionalData.executeWorkflow(workflowInfo, additionalData, inputData);
|
||||||
},
|
},
|
||||||
|
@ -578,6 +581,10 @@ export function getExecuteSingleFunctions(workflow: Workflow, runExecutionData:
|
||||||
continueOnFail: () => {
|
continueOnFail: () => {
|
||||||
return continueOnFail(node);
|
return continueOnFail(node);
|
||||||
},
|
},
|
||||||
|
evaluateExpression: (expression: string, evaluateItemIndex: number | undefined) => {
|
||||||
|
evaluateItemIndex = evaluateItemIndex === undefined ? itemIndex : evaluateItemIndex;
|
||||||
|
return workflow.resolveSimpleParameterValue('=' + expression, runExecutionData, runIndex, evaluateItemIndex, node.name, connectionInputData);
|
||||||
|
},
|
||||||
getContext(type: string): IContextObject {
|
getContext(type: string): IContextObject {
|
||||||
return NodeHelpers.getContext(runExecutionData, type, node);
|
return NodeHelpers.getContext(runExecutionData, type, node);
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "n8n-editor-ui",
|
"name": "n8n-editor-ui",
|
||||||
"version": "0.37.0",
|
"version": "0.39.0",
|
||||||
"description": "Workflow Editor UI for n8n",
|
"description": "Workflow Editor UI for n8n",
|
||||||
"license": "SEE LICENSE IN LICENSE.md",
|
"license": "SEE LICENSE IN LICENSE.md",
|
||||||
"homepage": "https://n8n.io",
|
"homepage": "https://n8n.io",
|
||||||
|
@ -63,7 +63,7 @@
|
||||||
"lodash.debounce": "^4.0.8",
|
"lodash.debounce": "^4.0.8",
|
||||||
"lodash.get": "^4.4.2",
|
"lodash.get": "^4.4.2",
|
||||||
"lodash.set": "^4.3.2",
|
"lodash.set": "^4.3.2",
|
||||||
"n8n-workflow": "~0.23.0",
|
"n8n-workflow": "~0.25.0",
|
||||||
"node-sass": "^4.12.0",
|
"node-sass": "^4.12.0",
|
||||||
"prismjs": "^1.17.1",
|
"prismjs": "^1.17.1",
|
||||||
"quill": "^2.0.0-dev.3",
|
"quill": "^2.0.0-dev.3",
|
||||||
|
|
|
@ -13,5 +13,22 @@ export class JotFormApi implements ICredentialType {
|
||||||
type: 'string' as NodePropertyTypes,
|
type: 'string' as NodePropertyTypes,
|
||||||
default: '',
|
default: '',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
displayName: 'API Domain',
|
||||||
|
name: 'apiDomain',
|
||||||
|
type: 'options' as NodePropertyTypes,
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'api.jotform.com',
|
||||||
|
value: 'api.jotform.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'eu-api.jotform.com',
|
||||||
|
value: 'eu-api.jotform.com',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
default: 'api.jotform.com',
|
||||||
|
description: 'The API domain to use. Use "eu-api.jotform.com" if your account is in based in Europe.',
|
||||||
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
17
packages/nodes-base/credentials/MondayComApi.credentials.ts
Normal file
17
packages/nodes-base/credentials/MondayComApi.credentials.ts
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
import {
|
||||||
|
ICredentialType,
|
||||||
|
NodePropertyTypes,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
export class MondayComApi implements ICredentialType {
|
||||||
|
name = 'mondayComApi';
|
||||||
|
displayName = 'Monday.com API';
|
||||||
|
properties = [
|
||||||
|
{
|
||||||
|
displayName: 'Token V2',
|
||||||
|
name: 'apiToken',
|
||||||
|
type: 'string' as NodePropertyTypes,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
|
@ -25,7 +25,7 @@ export class AffinityTrigger implements INodeType {
|
||||||
version: 1,
|
version: 1,
|
||||||
description: 'Handle Affinity events via webhooks',
|
description: 'Handle Affinity events via webhooks',
|
||||||
defaults: {
|
defaults: {
|
||||||
name: 'Affinity Trigger',
|
name: 'Affinity-Trigger',
|
||||||
color: '#3343df',
|
color: '#3343df',
|
||||||
},
|
},
|
||||||
inputs: [],
|
inputs: [],
|
||||||
|
@ -184,7 +184,11 @@ export class AffinityTrigger implements INodeType {
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
async create(this: IHookFunctions): Promise<boolean> {
|
async create(this: IHookFunctions): Promise<boolean> {
|
||||||
const webhookUrl = this.getNodeWebhookUrl('default');
|
const webhookUrl = this.getNodeWebhookUrl('default') as string;
|
||||||
|
|
||||||
|
if (webhookUrl.includes('%20')) {
|
||||||
|
throw new Error('The name of the Affinity Trigger Node is not allowed to contain any spaces!');
|
||||||
|
}
|
||||||
|
|
||||||
const events = this.getNodeParameter('events') as string[];
|
const events = this.getNodeParameter('events') as string[];
|
||||||
|
|
||||||
|
|
|
@ -47,6 +47,9 @@ export class Function implements INodeType {
|
||||||
|
|
||||||
// Define the global objects for the custom function
|
// Define the global objects for the custom function
|
||||||
const sandbox = {
|
const sandbox = {
|
||||||
|
evaluateExpression: (expression: string, itemIndex = 0) => {
|
||||||
|
return this.evaluateExpression(expression, itemIndex);
|
||||||
|
},
|
||||||
getNodeParameter: this.getNodeParameter,
|
getNodeParameter: this.getNodeParameter,
|
||||||
getWorkflowStaticData: this.getWorkflowStaticData,
|
getWorkflowStaticData: this.getWorkflowStaticData,
|
||||||
helpers: this.helpers,
|
helpers: this.helpers,
|
||||||
|
|
|
@ -48,6 +48,9 @@ export class FunctionItem implements INodeType {
|
||||||
|
|
||||||
// Define the global objects for the custom function
|
// Define the global objects for the custom function
|
||||||
const sandbox = {
|
const sandbox = {
|
||||||
|
evaluateExpression: (expression: string, itemIndex: number | undefined) => {
|
||||||
|
return this.evaluateExpression(expression, itemIndex);
|
||||||
|
},
|
||||||
getBinaryData: (): IBinaryKeyData | undefined => {
|
getBinaryData: (): IBinaryKeyData | undefined => {
|
||||||
return item.binary;
|
return item.binary;
|
||||||
},
|
},
|
||||||
|
|
|
@ -3,6 +3,10 @@ import { google, sheets_v4 } from 'googleapis';
|
||||||
import { JWT } from 'google-auth-library';
|
import { JWT } from 'google-auth-library';
|
||||||
import { getAuthenticationClient } from './GoogleApi';
|
import { getAuthenticationClient } from './GoogleApi';
|
||||||
|
|
||||||
|
import {
|
||||||
|
utils as xlsxUtils,
|
||||||
|
} from 'xlsx';
|
||||||
|
|
||||||
const Sheets = google.sheets('v4'); // tslint:disable-line:variable-name
|
const Sheets = google.sheets('v4'); // tslint:disable-line:variable-name
|
||||||
|
|
||||||
export interface ISheetOptions {
|
export interface ISheetOptions {
|
||||||
|
@ -271,6 +275,12 @@ export class GoogleSheet {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
getColumnWithOffset (startColumn: string, offset: number): string {
|
||||||
|
const columnIndex = xlsxUtils.decode_col(startColumn) + offset;
|
||||||
|
return xlsxUtils.encode_col(columnIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates data in a sheet
|
* Updates data in a sheet
|
||||||
*
|
*
|
||||||
|
@ -291,7 +301,14 @@ export class GoogleSheet {
|
||||||
}
|
}
|
||||||
[rangeStart, rangeEnd] = range.split(':');
|
[rangeStart, rangeEnd] = range.split(':');
|
||||||
|
|
||||||
const keyRowRange = `${sheet ? sheet + '!' : ''}${rangeStart}${dataStartRowIndex}:${rangeEnd}${dataStartRowIndex}`;
|
const rangeStartSplit = rangeStart.match(/([a-zA-Z]{1,10})([0-9]{0,10})/);
|
||||||
|
const rangeEndSplit = rangeEnd.match(/([a-zA-Z]{1,10})([0-9]{0,10})/);
|
||||||
|
|
||||||
|
if (rangeStartSplit === null || rangeStartSplit.length !== 3 || rangeEndSplit === null || rangeEndSplit.length !== 3) {
|
||||||
|
throw new Error(`The range "${range}" is not valid.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const keyRowRange = `${sheet ? sheet + '!' : ''}${rangeStartSplit[1]}${dataStartRowIndex}:${rangeEndSplit[1]}${dataStartRowIndex}`;
|
||||||
|
|
||||||
const sheetDatakeyRow = await this.getData(keyRowRange, valueRenderMode);
|
const sheetDatakeyRow = await this.getData(keyRowRange, valueRenderMode);
|
||||||
|
|
||||||
|
@ -307,9 +324,11 @@ export class GoogleSheet {
|
||||||
throw new Error(`Could not find column for key "${indexKey}"!`);
|
throw new Error(`Could not find column for key "${indexKey}"!`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const characterCode = rangeStart.toUpperCase().charCodeAt(0) + keyIndex;
|
const startRowIndex = rangeStartSplit[2] || '';
|
||||||
let keyColumnRange = String.fromCharCode(characterCode);
|
const endRowIndex = rangeEndSplit[2] || '';
|
||||||
keyColumnRange = `${sheet ? sheet + '!' : ''}${keyColumnRange}:${keyColumnRange}`;
|
|
||||||
|
const keyColumn = this.getColumnWithOffset(rangeStartSplit[1], keyIndex);
|
||||||
|
const keyColumnRange = `${sheet ? sheet + '!' : ''}${keyColumn}${startRowIndex}:${keyColumn}${endRowIndex}`;
|
||||||
|
|
||||||
const sheetDataKeyColumn = await this.getData(keyColumnRange, valueRenderMode);
|
const sheetDataKeyColumn = await this.getData(keyColumnRange, valueRenderMode);
|
||||||
|
|
||||||
|
@ -348,7 +367,6 @@ export class GoogleSheet {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the row index in which the data should be updated
|
// Get the row index in which the data should be updated
|
||||||
// TODO: Should probably change the indexes to be 1 based because Google Sheet is
|
|
||||||
updateRowIndex = keyColumnIndexLookup.indexOf(itemKey) + dataStartRowIndex + 1;
|
updateRowIndex = keyColumnIndexLookup.indexOf(itemKey) + dataStartRowIndex + 1;
|
||||||
|
|
||||||
// Check all the properties in the sheet and check which ones exist on the
|
// Check all the properties in the sheet and check which ones exist on the
|
||||||
|
@ -367,7 +385,7 @@ export class GoogleSheet {
|
||||||
// Property exists so add it to the data to update
|
// Property exists so add it to the data to update
|
||||||
|
|
||||||
// Get the column name in which the property data can be found
|
// Get the column name in which the property data can be found
|
||||||
updateColumnName = String.fromCharCode(rangeStart.toUpperCase().charCodeAt(0) + keyColumnOrder.indexOf(propertyName));
|
updateColumnName = this.getColumnWithOffset(rangeStartSplit[1], keyColumnOrder.indexOf(propertyName));
|
||||||
|
|
||||||
updateData.push({
|
updateData.push({
|
||||||
range: `${sheet ? sheet + '!' : ''}${updateColumnName}${updateRowIndex}`,
|
range: `${sheet ? sheet + '!' : ''}${updateColumnName}${updateRowIndex}`,
|
||||||
|
@ -426,7 +444,7 @@ export class GoogleSheet {
|
||||||
|
|
||||||
// Loop over all the items and find the one with the matching value
|
// Loop over all the items and find the one with the matching value
|
||||||
for (rowIndex = dataStartRowIndex; rowIndex < inputData.length; rowIndex++) {
|
for (rowIndex = dataStartRowIndex; rowIndex < inputData.length; rowIndex++) {
|
||||||
if (inputData[rowIndex][returnColumnIndex].toString() === lookupValue.lookupValue.toString()) {
|
if (inputData[rowIndex][returnColumnIndex]?.toString() === lookupValue.lookupValue.toString()) {
|
||||||
returnData.push(inputData[rowIndex]);
|
returnData.push(inputData[rowIndex]);
|
||||||
|
|
||||||
if (returnAllMatches !== true) {
|
if (returnAllMatches !== true) {
|
||||||
|
|
|
@ -20,7 +20,6 @@ import {
|
||||||
ValueRenderOption,
|
ValueRenderOption,
|
||||||
} from './GoogleSheet';
|
} from './GoogleSheet';
|
||||||
|
|
||||||
|
|
||||||
export class GoogleSheets implements INodeType {
|
export class GoogleSheets implements INodeType {
|
||||||
description: INodeTypeDescription = {
|
description: INodeTypeDescription = {
|
||||||
displayName: 'Google Sheets ',
|
displayName: 'Google Sheets ',
|
||||||
|
@ -358,7 +357,6 @@ export class GoogleSheets implements INodeType {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
default: '',
|
default: '',
|
||||||
placeholder: 'frank@example.com',
|
placeholder: 'frank@example.com',
|
||||||
required: true,
|
|
||||||
displayOptions: {
|
displayOptions: {
|
||||||
show: {
|
show: {
|
||||||
operation: [
|
operation: [
|
||||||
|
@ -407,6 +405,21 @@ export class GoogleSheets implements INodeType {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
options: [
|
options: [
|
||||||
|
{
|
||||||
|
displayName: 'Continue If Empty',
|
||||||
|
name: 'continue',
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
'/operation': [
|
||||||
|
'lookup',
|
||||||
|
'read',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
description: 'By default, the workflow stops executing if the lookup/read does not return values.',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Return All Matches',
|
displayName: 'Return All Matches',
|
||||||
name: 'returnAllMatches',
|
name: 'returnAllMatches',
|
||||||
|
@ -679,7 +692,13 @@ export class GoogleSheets implements INodeType {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const returnData = await sheet.lookupValues(sheetData, keyRow, dataStartRow, lookupValues, options.returnAllMatches as boolean | undefined);
|
let returnData = await sheet.lookupValues(sheetData, keyRow, dataStartRow, lookupValues, options.returnAllMatches as boolean | undefined);
|
||||||
|
|
||||||
|
if (returnData.length === 0 && options.continue && options.returnAllMatches) {
|
||||||
|
returnData = [{}];
|
||||||
|
} else if (returnData.length === 1 && Object.keys(returnData[0]).length === 0 && !options.continue && !options.returnAllMatches) {
|
||||||
|
returnData = [];
|
||||||
|
}
|
||||||
|
|
||||||
return [this.helpers.returnJsonArray(returnData)];
|
return [this.helpers.returnJsonArray(returnData)];
|
||||||
} else if (operation === 'read') {
|
} else if (operation === 'read') {
|
||||||
|
@ -708,6 +727,10 @@ export class GoogleSheets implements INodeType {
|
||||||
returnData = sheet.structureArrayDataByColumn(sheetData, keyRow, dataStartRow);
|
returnData = sheet.structureArrayDataByColumn(sheetData, keyRow, dataStartRow);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (returnData.length === 0 && options.continue) {
|
||||||
|
returnData = [{}];
|
||||||
|
}
|
||||||
|
|
||||||
return [this.helpers.returnJsonArray(returnData)];
|
return [this.helpers.returnJsonArray(returnData)];
|
||||||
} else if (operation === 'update') {
|
} else if (operation === 'update') {
|
||||||
// ----------------------------------
|
// ----------------------------------
|
||||||
|
|
326
packages/nodes-base/nodes/Hubspot/FormDescription.ts
Normal file
326
packages/nodes-base/nodes/Hubspot/FormDescription.ts
Normal file
|
@ -0,0 +1,326 @@
|
||||||
|
import { INodeProperties } from "n8n-workflow";
|
||||||
|
|
||||||
|
export const formOperations = [
|
||||||
|
{
|
||||||
|
displayName: 'Operation',
|
||||||
|
name: 'operation',
|
||||||
|
type: 'options',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'form',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Get Fields',
|
||||||
|
value: 'getFields',
|
||||||
|
description: 'Get all fields from a form',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Submit',
|
||||||
|
value: 'submit',
|
||||||
|
description: 'Submit data to a form',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
default: 'getFields',
|
||||||
|
description: 'The operation to perform.',
|
||||||
|
},
|
||||||
|
] as INodeProperties[];
|
||||||
|
|
||||||
|
export const formFields = [
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* form:submit */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
{
|
||||||
|
displayName: 'Form',
|
||||||
|
name: 'formId',
|
||||||
|
type: 'options',
|
||||||
|
typeOptions: {
|
||||||
|
loadOptionsMethod: 'getForms',
|
||||||
|
},
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'form',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'submit',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: `The ID of the form you're sending data to.`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Additional Fields',
|
||||||
|
name: 'additionalFields',
|
||||||
|
type: 'collection',
|
||||||
|
placeholder: 'Add Field',
|
||||||
|
default: {},
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'form',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'submit',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
displayName: 'Skip Validation',
|
||||||
|
name: 'skipValidation',
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
description: `Whether or not to skip validation based on the form settings.`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Submitted At',
|
||||||
|
name: 'submittedAt',
|
||||||
|
type: 'dateTime',
|
||||||
|
default: '',
|
||||||
|
description: 'Time of the form submission.',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Context',
|
||||||
|
name: 'contextUi',
|
||||||
|
placeholder: 'Add Context',
|
||||||
|
type: 'fixedCollection',
|
||||||
|
typeOptions: {
|
||||||
|
multipleValues: false,
|
||||||
|
},
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'form',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'submit',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: {},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
displayName: 'Context',
|
||||||
|
name: 'contextValue',
|
||||||
|
values: [
|
||||||
|
{
|
||||||
|
displayName: 'HubSpot usertoken',
|
||||||
|
name: 'hutk',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
description: 'Include this parameter and set it to the hubspotutk cookie value to enable cookie tracking on your submission',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'IP Address',
|
||||||
|
name: 'ipAddress',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
description: 'The IP address of the visitor filling out the form.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Page URI',
|
||||||
|
name: 'pageUri',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
description: 'The URI of the page the submission happened on.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Page Name',
|
||||||
|
name: 'pageName',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
description: 'The name or title of the page the submission happened on.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Page ID',
|
||||||
|
name: 'pageId',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
description: 'The ID of a page created on the HubSpot CMS.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'SFDC campaign ID',
|
||||||
|
name: 'sfdcCampaignId',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
description: `If the form is for an account using the HubSpot Salesforce Integration,</br>
|
||||||
|
you can include the ID of a Salesforce campaign to add the contact to the specified campaign.`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Go to Webinar Webinar ID',
|
||||||
|
name: 'goToWebinarWebinarKey',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
description: `If the form is for an account using the HubSpot GoToWebinar Integration,</br>
|
||||||
|
you can include the ID of a webinar to enroll the contact in that webinar when they submit the form.`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Legal Consent',
|
||||||
|
name: 'lengalConsentUi',
|
||||||
|
placeholder: 'Add Legal Consent',
|
||||||
|
type: 'fixedCollection',
|
||||||
|
typeOptions: {
|
||||||
|
multipleValues: false,
|
||||||
|
},
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'form',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'submit',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: {},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
displayName: 'Consent',
|
||||||
|
name: 'lengalConsentValues',
|
||||||
|
values: [
|
||||||
|
{
|
||||||
|
displayName: 'Consent To Process',
|
||||||
|
name: 'consentToProcess',
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
description: 'Whether or not the visitor checked the Consent to process checkbox',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Text',
|
||||||
|
name: 'text',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
description: 'The text displayed to the visitor for the Consent to process checkbox',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Communications',
|
||||||
|
name: 'communicationsUi',
|
||||||
|
placeholder: 'Add Communication',
|
||||||
|
type: 'fixedCollection',
|
||||||
|
typeOptions: {
|
||||||
|
multipleValues: true,
|
||||||
|
},
|
||||||
|
default: {},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
displayName: 'Communication',
|
||||||
|
name: 'communicationValues',
|
||||||
|
values: [
|
||||||
|
{
|
||||||
|
displayName: 'Subcription Type',
|
||||||
|
name: 'subscriptionTypeId',
|
||||||
|
type: 'options',
|
||||||
|
typeOptions: {
|
||||||
|
loadOptionsMethod: 'getSubscriptionTypes',
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: 'The ID of the specific subscription type',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Value',
|
||||||
|
name: 'value',
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
description: ' Whether or not the visitor checked the checkbox for this subscription type.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Text',
|
||||||
|
name: 'text',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
description: 'The text displayed to the visitor for this specific subscription checkbox',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Legitimate Interest',
|
||||||
|
name: 'legitimateInterestValues',
|
||||||
|
values: [
|
||||||
|
{
|
||||||
|
displayName: 'Subcription Type',
|
||||||
|
name: 'subscriptionTypeId',
|
||||||
|
type: 'options',
|
||||||
|
typeOptions: {
|
||||||
|
loadOptionsMethod: 'getSubscriptionTypes',
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: 'The ID of the specific subscription type that this forms indicates interest to.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Value',
|
||||||
|
name: 'value',
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
description: `This must be true when using the 'legitimateInterest' option, as it reflects</br>
|
||||||
|
the consent indicated by the visitor when submitting the form`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Legal Basis',
|
||||||
|
name: 'legalBasis',
|
||||||
|
type: 'options',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Customer',
|
||||||
|
value: 'CUSTOMER',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Lead',
|
||||||
|
value: 'LEAD',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
default: '',
|
||||||
|
description: 'The privacy text displayed to the visitor.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Text',
|
||||||
|
name: 'text',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
description: 'The privacy text displayed to the visitor.',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* form:getFields */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
{
|
||||||
|
displayName: 'Form',
|
||||||
|
name: 'formId',
|
||||||
|
type: 'options',
|
||||||
|
typeOptions: {
|
||||||
|
loadOptionsMethod: 'getForms',
|
||||||
|
},
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'form',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getFields',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: 'The ID of the form',
|
||||||
|
},
|
||||||
|
] as INodeProperties[];
|
21
packages/nodes-base/nodes/Hubspot/FormInterface.ts
Normal file
21
packages/nodes-base/nodes/Hubspot/FormInterface.ts
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import { IDataObject } from "n8n-workflow";
|
||||||
|
|
||||||
|
export interface IContext {
|
||||||
|
goToWebinarWebinarKey?: string;
|
||||||
|
hutk?: string;
|
||||||
|
ipAddress?: string;
|
||||||
|
pageId?: string;
|
||||||
|
pageName?: string;
|
||||||
|
pageUri?: string;
|
||||||
|
sfdcCampaignId?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IForm {
|
||||||
|
portalId?: number;
|
||||||
|
formId?: string;
|
||||||
|
fields?: IDataObject[];
|
||||||
|
legalConsentOptions?: IDataObject;
|
||||||
|
context?: IContext[];
|
||||||
|
submittedAt?: number;
|
||||||
|
skipValidation?: boolean;
|
||||||
|
}
|
|
@ -26,17 +26,20 @@ export async function hubspotApiRequest(this: IHookFunctions | IExecuteFunctions
|
||||||
json: true,
|
json: true,
|
||||||
useQuerystring: true,
|
useQuerystring: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return await this.helpers.request!(options);
|
return await this.helpers.request!(options);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const errorMessage = error.response.body.message || error.response.body.Message || error.message;
|
|
||||||
throw new Error(`Hubspot error response [${error.statusCode}]: ${errorMessage}`);
|
if (error.response && error.response.body && error.response.body.errors) {
|
||||||
|
// Try to return the error prettier
|
||||||
|
const errorMessages = error.response.body.errors.map((e: IDataObject) => e.message);
|
||||||
|
throw new Error(`Hubspot error response [${error.statusCode}]: ${errorMessages.join(' | ')}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Make an API request to paginated hubspot endpoint
|
* Make an API request to paginated hubspot endpoint
|
||||||
* and return all results
|
* and return all results
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import {
|
import {
|
||||||
IExecuteFunctions,
|
IExecuteFunctions,
|
||||||
} from 'n8n-core';
|
} from 'n8n-core';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
IDataObject,
|
IDataObject,
|
||||||
INodeTypeDescription,
|
INodeTypeDescription,
|
||||||
|
@ -9,15 +10,30 @@ import {
|
||||||
ILoadOptionsFunctions,
|
ILoadOptionsFunctions,
|
||||||
INodePropertyOptions,
|
INodePropertyOptions,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
hubspotApiRequest,
|
hubspotApiRequest,
|
||||||
hubspotApiRequestAllItems,
|
hubspotApiRequestAllItems,
|
||||||
} from './GenericFunctions';
|
} from './GenericFunctions';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
dealOperations,
|
dealOperations,
|
||||||
dealFields,
|
dealFields,
|
||||||
} from '../Hubspot/DealDescription';
|
} from './DealDescription';
|
||||||
import { IDeal, IAssociation } from './DealInterface';
|
|
||||||
|
import {
|
||||||
|
IDeal,
|
||||||
|
IAssociation
|
||||||
|
} from './DealInterface';
|
||||||
|
|
||||||
|
import {
|
||||||
|
formOperations,
|
||||||
|
formFields,
|
||||||
|
} from './FormDescription';
|
||||||
|
|
||||||
|
import {
|
||||||
|
IForm
|
||||||
|
} from './FormInterface';
|
||||||
|
|
||||||
export class Hubspot implements INodeType {
|
export class Hubspot implements INodeType {
|
||||||
description: INodeTypeDescription = {
|
description: INodeTypeDescription = {
|
||||||
|
@ -50,6 +66,10 @@ export class Hubspot implements INodeType {
|
||||||
name: 'Deal',
|
name: 'Deal',
|
||||||
value: 'deal',
|
value: 'deal',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'Form',
|
||||||
|
value: 'form',
|
||||||
|
},
|
||||||
],
|
],
|
||||||
default: 'deal',
|
default: 'deal',
|
||||||
description: 'Resource to consume.',
|
description: 'Resource to consume.',
|
||||||
|
@ -58,23 +78,22 @@ export class Hubspot implements INodeType {
|
||||||
// Deal
|
// Deal
|
||||||
...dealOperations,
|
...dealOperations,
|
||||||
...dealFields,
|
...dealFields,
|
||||||
|
// Form
|
||||||
|
...formOperations,
|
||||||
|
...formFields,
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
methods = {
|
methods = {
|
||||||
loadOptions: {
|
loadOptions: {
|
||||||
|
|
||||||
// Get all the groups to display them to user so that he can
|
// Get all the groups to display them to user so that he can
|
||||||
// select them easily
|
// select them easily
|
||||||
async getDealStages(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
async getDealStages(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||||
const returnData: INodePropertyOptions[] = [];
|
const returnData: INodePropertyOptions[] = [];
|
||||||
let stages;
|
|
||||||
try {
|
|
||||||
const endpoint = '/crm-pipelines/v1/pipelines/deals';
|
const endpoint = '/crm-pipelines/v1/pipelines/deals';
|
||||||
stages = await hubspotApiRequest.call(this, 'GET', endpoint, {});
|
let stages = await hubspotApiRequest.call(this, 'GET', endpoint, {});
|
||||||
stages = stages.results[0].stages;
|
stages = stages.results[0].stages;
|
||||||
} catch (err) {
|
|
||||||
throw new Error(`Hubspot Error: ${err}`);
|
|
||||||
}
|
|
||||||
for (const stage of stages) {
|
for (const stage of stages) {
|
||||||
const stageName = stage.label;
|
const stageName = stage.label;
|
||||||
const stageId = stage.stageId;
|
const stageId = stage.stageId;
|
||||||
|
@ -90,13 +109,8 @@ export class Hubspot implements INodeType {
|
||||||
// select them easily
|
// select them easily
|
||||||
async getCompanies(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
async getCompanies(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||||
const returnData: INodePropertyOptions[] = [];
|
const returnData: INodePropertyOptions[] = [];
|
||||||
let companies;
|
|
||||||
try {
|
|
||||||
const endpoint = '/companies/v2/companies/paged';
|
const endpoint = '/companies/v2/companies/paged';
|
||||||
companies = await hubspotApiRequestAllItems.call(this, 'results', 'GET', endpoint);
|
const companies = await hubspotApiRequestAllItems.call(this, 'results', 'GET', endpoint);
|
||||||
} catch (err) {
|
|
||||||
throw new Error(`Hubspot Error: ${err}`);
|
|
||||||
}
|
|
||||||
for (const company of companies) {
|
for (const company of companies) {
|
||||||
const companyName = company.properties.name.value;
|
const companyName = company.properties.name.value;
|
||||||
const companyId = company.companyId;
|
const companyId = company.companyId;
|
||||||
|
@ -112,13 +126,8 @@ export class Hubspot implements INodeType {
|
||||||
// select them easily
|
// select them easily
|
||||||
async getContacts(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
async getContacts(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||||
const returnData: INodePropertyOptions[] = [];
|
const returnData: INodePropertyOptions[] = [];
|
||||||
let contacts;
|
|
||||||
try {
|
|
||||||
const endpoint = '/contacts/v1/lists/all/contacts/all';
|
const endpoint = '/contacts/v1/lists/all/contacts/all';
|
||||||
contacts = await hubspotApiRequestAllItems.call(this, 'contacts', 'GET', endpoint);
|
const contacts = await hubspotApiRequestAllItems.call(this, 'contacts', 'GET', endpoint);
|
||||||
} catch (err) {
|
|
||||||
throw new Error(`Hubspot Error: ${err}`);
|
|
||||||
}
|
|
||||||
for (const contact of contacts) {
|
for (const contact of contacts) {
|
||||||
const contactName = `${contact.properties.firstname.value} ${contact.properties.lastname.value}` ;
|
const contactName = `${contact.properties.firstname.value} ${contact.properties.lastname.value}` ;
|
||||||
const contactId = contact.vid;
|
const contactId = contact.vid;
|
||||||
|
@ -134,13 +143,8 @@ export class Hubspot implements INodeType {
|
||||||
// select them easily
|
// select them easily
|
||||||
async getDealTypes(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
async getDealTypes(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||||
const returnData: INodePropertyOptions[] = [];
|
const returnData: INodePropertyOptions[] = [];
|
||||||
let dealTypes;
|
|
||||||
try {
|
|
||||||
const endpoint = '/properties/v1/deals/properties/named/dealtype';
|
const endpoint = '/properties/v1/deals/properties/named/dealtype';
|
||||||
dealTypes = await hubspotApiRequest.call(this, 'GET', endpoint);
|
const dealTypes = await hubspotApiRequest.call(this, 'GET', endpoint);
|
||||||
} catch (err) {
|
|
||||||
throw new Error(`Hubspot Error: ${err}`);
|
|
||||||
}
|
|
||||||
for (const dealType of dealTypes.options) {
|
for (const dealType of dealTypes.options) {
|
||||||
const dealTypeName = dealType.label ;
|
const dealTypeName = dealType.label ;
|
||||||
const dealTypeId = dealType.value;
|
const dealTypeId = dealType.value;
|
||||||
|
@ -151,6 +155,40 @@ export class Hubspot implements INodeType {
|
||||||
}
|
}
|
||||||
return returnData;
|
return returnData;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Get all the forms to display them to user so that he can
|
||||||
|
// select them easily
|
||||||
|
async getForms(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||||
|
const returnData: INodePropertyOptions[] = [];
|
||||||
|
const endpoint = '/forms/v2/forms';
|
||||||
|
const forms = await hubspotApiRequest.call(this, 'GET', endpoint, {}, { formTypes: 'ALL' });
|
||||||
|
for (const form of forms) {
|
||||||
|
const formName = form.name;
|
||||||
|
const formId = form.guid;
|
||||||
|
returnData.push({
|
||||||
|
name: formName,
|
||||||
|
value: formId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return returnData;
|
||||||
|
},
|
||||||
|
|
||||||
|
// Get all the subscription types to display them to user so that he can
|
||||||
|
// select them easily
|
||||||
|
async getSubscriptionTypes(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||||
|
const returnData: INodePropertyOptions[] = [];
|
||||||
|
const endpoint = '/email/public/v1/subscriptions';
|
||||||
|
const subscriptions = await hubspotApiRequestAllItems.call(this, 'subscriptionDefinitions', 'GET', endpoint, {});
|
||||||
|
for (const subscription of subscriptions) {
|
||||||
|
const subscriptionName = subscription.name;
|
||||||
|
const subscriptionId = subscription.id;
|
||||||
|
returnData.push({
|
||||||
|
name: subscriptionName,
|
||||||
|
value: subscriptionId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return returnData;
|
||||||
|
},
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -220,12 +258,8 @@ export class Hubspot implements INodeType {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
body.associations = association;
|
body.associations = association;
|
||||||
try {
|
|
||||||
const endpoint = '/deals/v1/deal';
|
const endpoint = '/deals/v1/deal';
|
||||||
responseData = await hubspotApiRequest.call(this, 'POST', endpoint, body);
|
responseData = await hubspotApiRequest.call(this, 'POST', endpoint, body);
|
||||||
} catch (err) {
|
|
||||||
throw new Error(`Hubspot Error: ${JSON.stringify(err)}`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (operation === 'update') {
|
if (operation === 'update') {
|
||||||
const body: IDeal = {};
|
const body: IDeal = {};
|
||||||
|
@ -273,12 +307,8 @@ export class Hubspot implements INodeType {
|
||||||
value: updateFields.pipeline as string
|
value: updateFields.pipeline as string
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
try {
|
|
||||||
const endpoint = `/deals/v1/deal/${dealId}`;
|
const endpoint = `/deals/v1/deal/${dealId}`;
|
||||||
responseData = await hubspotApiRequest.call(this, 'PUT', endpoint, body);
|
responseData = await hubspotApiRequest.call(this, 'PUT', endpoint, body);
|
||||||
} catch (err) {
|
|
||||||
throw new Error(`Hubspot Error: ${JSON.stringify(err)}`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (operation === 'get') {
|
if (operation === 'get') {
|
||||||
const dealId = this.getNodeParameter('dealId', i) as string;
|
const dealId = this.getNodeParameter('dealId', i) as string;
|
||||||
|
@ -286,12 +316,8 @@ export class Hubspot implements INodeType {
|
||||||
if (additionalFields.includePropertyVersions) {
|
if (additionalFields.includePropertyVersions) {
|
||||||
qs.includePropertyVersions = additionalFields.includePropertyVersions as boolean;
|
qs.includePropertyVersions = additionalFields.includePropertyVersions as boolean;
|
||||||
}
|
}
|
||||||
try {
|
|
||||||
const endpoint = `/deals/v1/deal/${dealId}`;
|
const endpoint = `/deals/v1/deal/${dealId}`;
|
||||||
responseData = await hubspotApiRequest.call(this, 'GET', endpoint);
|
responseData = await hubspotApiRequest.call(this, 'GET', endpoint);
|
||||||
} catch (err) {
|
|
||||||
throw new Error(`Hubspot Error: ${JSON.stringify(err)}`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (operation === 'getAll') {
|
if (operation === 'getAll') {
|
||||||
const filters = this.getNodeParameter('filters', i) as IDataObject;
|
const filters = this.getNodeParameter('filters', i) as IDataObject;
|
||||||
|
@ -307,7 +333,6 @@ export class Hubspot implements INodeType {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
qs.propertiesWithHistory = filters.propertiesWithHistory.split(',');
|
qs.propertiesWithHistory = filters.propertiesWithHistory.split(',');
|
||||||
}
|
}
|
||||||
try {
|
|
||||||
const endpoint = `/deals/v1/deal/paged`;
|
const endpoint = `/deals/v1/deal/paged`;
|
||||||
if (returnAll) {
|
if (returnAll) {
|
||||||
responseData = await hubspotApiRequestAllItems.call(this, 'deals', 'GET', endpoint, {}, qs);
|
responseData = await hubspotApiRequestAllItems.call(this, 'deals', 'GET', endpoint, {}, qs);
|
||||||
|
@ -316,9 +341,6 @@ export class Hubspot implements INodeType {
|
||||||
responseData = await hubspotApiRequest.call(this, 'GET', endpoint, {}, qs);
|
responseData = await hubspotApiRequest.call(this, 'GET', endpoint, {}, qs);
|
||||||
responseData = responseData.deals;
|
responseData = responseData.deals;
|
||||||
}
|
}
|
||||||
} catch (err) {
|
|
||||||
throw new Error(`Hubspot Error: ${JSON.stringify(err)}`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (operation === 'getRecentlyCreated' || operation === 'getRecentlyModified') {
|
if (operation === 'getRecentlyCreated' || operation === 'getRecentlyModified') {
|
||||||
let endpoint;
|
let endpoint;
|
||||||
|
@ -330,7 +352,6 @@ export class Hubspot implements INodeType {
|
||||||
if (filters.includePropertyVersions) {
|
if (filters.includePropertyVersions) {
|
||||||
qs.includePropertyVersions = filters.includePropertyVersions as boolean;
|
qs.includePropertyVersions = filters.includePropertyVersions as boolean;
|
||||||
}
|
}
|
||||||
try {
|
|
||||||
if (operation === 'getRecentlyCreated') {
|
if (operation === 'getRecentlyCreated') {
|
||||||
endpoint = `/deals/v1/deal/recent/created`;
|
endpoint = `/deals/v1/deal/recent/created`;
|
||||||
} else {
|
} else {
|
||||||
|
@ -343,19 +364,66 @@ export class Hubspot implements INodeType {
|
||||||
responseData = await hubspotApiRequest.call(this, 'GET', endpoint, {}, qs);
|
responseData = await hubspotApiRequest.call(this, 'GET', endpoint, {}, qs);
|
||||||
responseData = responseData.results;
|
responseData = responseData.results;
|
||||||
}
|
}
|
||||||
} catch (err) {
|
|
||||||
throw new Error(`Hubspot Error: ${JSON.stringify(err)}`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (operation === 'delete') {
|
if (operation === 'delete') {
|
||||||
const dealId = this.getNodeParameter('dealId', i) as string;
|
const dealId = this.getNodeParameter('dealId', i) as string;
|
||||||
try {
|
|
||||||
const endpoint = `/deals/v1/deal/${dealId}`;
|
const endpoint = `/deals/v1/deal/${dealId}`;
|
||||||
responseData = await hubspotApiRequest.call(this, 'DELETE', endpoint);
|
responseData = await hubspotApiRequest.call(this, 'DELETE', endpoint);
|
||||||
} catch (err) {
|
|
||||||
throw new Error(`Hubspot Error: ${JSON.stringify(err)}`);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//https://developers.hubspot.com/docs/methods/forms/forms_overview
|
||||||
|
if (resource === 'form') {
|
||||||
|
//https://developers.hubspot.com/docs/methods/forms/v2/get_fields
|
||||||
|
if (operation === 'getFields') {
|
||||||
|
const formId = this.getNodeParameter('formId', i) as string;
|
||||||
|
responseData = await hubspotApiRequest.call(this, 'GET', `/forms/v2/fields/${formId}`);
|
||||||
|
}
|
||||||
|
//https://developers.hubspot.com/docs/methods/forms/submit_form_v3
|
||||||
|
if (operation === 'submit') {
|
||||||
|
const formId = this.getNodeParameter('formId', i) as string;
|
||||||
|
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
|
||||||
|
const context = (this.getNodeParameter('contextUi', i) as IDataObject).contextValue as IDataObject;
|
||||||
|
const legalConsent = (this.getNodeParameter('lengalConsentUi', i) as IDataObject).lengalConsentValues as IDataObject;
|
||||||
|
const legitimateInteres = (this.getNodeParameter('lengalConsentUi', i) as IDataObject).legitimateInterestValues as IDataObject;
|
||||||
|
const { portalId } = await hubspotApiRequest.call(this, 'GET', `/forms/v2/forms/${formId}`);
|
||||||
|
const body: IForm = {
|
||||||
|
formId,
|
||||||
|
portalId,
|
||||||
|
legalConsentOptions: {},
|
||||||
|
fields: [],
|
||||||
|
};
|
||||||
|
if (additionalFields.submittedAt) {
|
||||||
|
body.submittedAt = new Date(additionalFields.submittedAt as string).getTime();
|
||||||
|
}
|
||||||
|
if (additionalFields.skipValidation) {
|
||||||
|
body.skipValidation = additionalFields.skipValidation as boolean;
|
||||||
|
}
|
||||||
|
const consent: IDataObject = {};
|
||||||
|
if (legalConsent) {
|
||||||
|
if (legalConsent.consentToProcess) {
|
||||||
|
consent!.consentToProcess = legalConsent.consentToProcess as boolean;
|
||||||
|
}
|
||||||
|
if (legalConsent.text) {
|
||||||
|
consent!.text = legalConsent.text as string;
|
||||||
|
}
|
||||||
|
if (legalConsent.communicationsUi) {
|
||||||
|
consent.communications = (legalConsent.communicationsUi as IDataObject).communicationValues as IDataObject;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
body.legalConsentOptions!.consent = consent;
|
||||||
|
const fields: IDataObject = items[i].json;
|
||||||
|
for (const key of Object.keys(fields)) {
|
||||||
|
body.fields?.push({ name: key, value: fields[key] });
|
||||||
|
}
|
||||||
|
if (body.legalConsentOptions!.legitimateInterest) {
|
||||||
|
Object.assign(body, { legalConsentOptions: { legitimateInterest: legitimateInteres } });
|
||||||
|
}
|
||||||
|
if (context) {
|
||||||
|
Object.assign(body, { context });
|
||||||
|
}
|
||||||
|
const uri = `https://api.hsforms.com/submissions/v3/integration/submit/${portalId}/${formId}`;
|
||||||
|
responseData = await hubspotApiRequest.call(this, 'POST', '', body, {}, uri);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (Array.isArray(responseData)) {
|
if (Array.isArray(responseData)) {
|
||||||
returnData.push.apply(returnData, responseData as IDataObject[]);
|
returnData.push.apply(returnData, responseData as IDataObject[]);
|
||||||
|
|
|
@ -21,7 +21,7 @@ export async function jotformApiRequest(this: IHookFunctions | IExecuteFunctions
|
||||||
method,
|
method,
|
||||||
qs,
|
qs,
|
||||||
form: body,
|
form: body,
|
||||||
uri: uri ||`http://api.jotform.com${resource}`,
|
uri: uri || `https://${credentials.apiDomain || 'api.jotform.com'}${resource}`,
|
||||||
json: true
|
json: true
|
||||||
};
|
};
|
||||||
if (!Object.keys(body).length) {
|
if (!Object.keys(body).length) {
|
||||||
|
|
|
@ -299,7 +299,7 @@ export const emailFields = [
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Template',
|
displayName: 'Template ID',
|
||||||
name: 'templateId',
|
name: 'templateId',
|
||||||
type: 'string',
|
type: 'string',
|
||||||
required: true,
|
required: true,
|
||||||
|
|
|
@ -185,7 +185,7 @@ export class Mailjet implements INodeType {
|
||||||
//https://dev.mailjet.com/email/guides/send-api-v31/#use-a-template
|
//https://dev.mailjet.com/email/guides/send-api-v31/#use-a-template
|
||||||
if (operation === 'sendTemplate') {
|
if (operation === 'sendTemplate') {
|
||||||
const fromEmail = this.getNodeParameter('fromEmail', i) as string;
|
const fromEmail = this.getNodeParameter('fromEmail', i) as string;
|
||||||
const templateId = this.getNodeParameter('templateId', i) as string;
|
const templateId = parseInt(this.getNodeParameter('templateId', i) as string, 10);
|
||||||
const subject = this.getNodeParameter('subject', i) as string;
|
const subject = this.getNodeParameter('subject', i) as string;
|
||||||
const variables = (this.getNodeParameter('variablesUi', i) as IDataObject).variablesValues as IDataObject[];
|
const variables = (this.getNodeParameter('variablesUi', i) as IDataObject).variablesValues as IDataObject[];
|
||||||
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
|
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
|
||||||
|
|
230
packages/nodes-base/nodes/MondayCom/BoardColumnDescription.ts
Normal file
230
packages/nodes-base/nodes/MondayCom/BoardColumnDescription.ts
Normal file
|
@ -0,0 +1,230 @@
|
||||||
|
import {
|
||||||
|
INodeProperties
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
export const boardColumnOperations = [
|
||||||
|
{
|
||||||
|
displayName: 'Operation',
|
||||||
|
name: 'operation',
|
||||||
|
type: 'options',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'boardColumn',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Create',
|
||||||
|
value: 'create',
|
||||||
|
description: 'Create a new column',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Get All',
|
||||||
|
value: 'getAll',
|
||||||
|
description: 'Get all columns',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
default: 'create',
|
||||||
|
description: 'The operation to perform.',
|
||||||
|
},
|
||||||
|
] as INodeProperties[];
|
||||||
|
|
||||||
|
export const boardColumnFields = [
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* boardColumn:create */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
{
|
||||||
|
displayName: 'Board ID',
|
||||||
|
name: 'boardId',
|
||||||
|
type: 'options',
|
||||||
|
default: '',
|
||||||
|
typeOptions: {
|
||||||
|
loadOptionsMethod: 'getBoards',
|
||||||
|
},
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'boardColumn',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'create',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Title',
|
||||||
|
name: 'title',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'boardColumn',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'create',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Column Type',
|
||||||
|
name: 'columnType',
|
||||||
|
type: 'options',
|
||||||
|
default: '',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Country',
|
||||||
|
value: 'country',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Checkbox',
|
||||||
|
value: 'checkbox',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Date',
|
||||||
|
value: 'date',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Dropdown',
|
||||||
|
value: 'dropdown',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Email',
|
||||||
|
value: 'email',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Hour',
|
||||||
|
value: 'hour',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Link',
|
||||||
|
value: 'Link',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Long Text',
|
||||||
|
value: 'longText',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Numbers',
|
||||||
|
value: 'numbers',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'People',
|
||||||
|
value: 'people',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Person',
|
||||||
|
value: 'person',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Phone',
|
||||||
|
value: 'phone',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Rating',
|
||||||
|
value: 'rating',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Status',
|
||||||
|
value: 'status',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Tags',
|
||||||
|
value: 'tags',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Team',
|
||||||
|
value: 'team',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Text',
|
||||||
|
value: 'text',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Timeline',
|
||||||
|
value: 'timeline',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Timezone',
|
||||||
|
value: 'timezone',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Week',
|
||||||
|
value: 'week',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'World Clock',
|
||||||
|
value: 'worldClock',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'boardColumn',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'create',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Additional Fields',
|
||||||
|
name: 'additionalFields',
|
||||||
|
type: 'collection',
|
||||||
|
placeholder: 'Add Field',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'boardColumn',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'create',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: {},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
displayName: 'Defauls',
|
||||||
|
name: 'defaults',
|
||||||
|
type: 'json',
|
||||||
|
typeOptions: {
|
||||||
|
alwaysOpenEditWindow: true,
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: `The new column's defaults.`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* boardColumn:getAll */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
{
|
||||||
|
displayName: 'Board ID',
|
||||||
|
name: 'boardId',
|
||||||
|
type: 'options',
|
||||||
|
default: '',
|
||||||
|
typeOptions: {
|
||||||
|
loadOptionsMethod: 'getBoards',
|
||||||
|
},
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'boardColumn',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
] as INodeProperties[];
|
220
packages/nodes-base/nodes/MondayCom/BoardDescription.ts
Normal file
220
packages/nodes-base/nodes/MondayCom/BoardDescription.ts
Normal file
|
@ -0,0 +1,220 @@
|
||||||
|
import {
|
||||||
|
INodeProperties,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
export const boardOperations = [
|
||||||
|
{
|
||||||
|
displayName: 'Operation',
|
||||||
|
name: 'operation',
|
||||||
|
type: 'options',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'board',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Archive',
|
||||||
|
value: 'archive',
|
||||||
|
description: 'Archive a board',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Create',
|
||||||
|
value: 'create',
|
||||||
|
description: 'Create a new board',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Get',
|
||||||
|
value: 'get',
|
||||||
|
description: 'Get a board',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Get All',
|
||||||
|
value: 'getAll',
|
||||||
|
description: 'Get all boards',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
default: 'create',
|
||||||
|
description: 'The operation to perform.',
|
||||||
|
},
|
||||||
|
] as INodeProperties[];
|
||||||
|
|
||||||
|
export const boardFields = [
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* board:archive */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
{
|
||||||
|
displayName: 'Board ID',
|
||||||
|
name: 'boardId',
|
||||||
|
type: 'options',
|
||||||
|
default: '',
|
||||||
|
typeOptions: {
|
||||||
|
loadOptionsMethod: 'getBoards',
|
||||||
|
},
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'board',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'archive',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
description: 'Board unique identifiers.',
|
||||||
|
},
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* board:create */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
{
|
||||||
|
displayName: 'Name',
|
||||||
|
name: 'name',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
operation: [
|
||||||
|
'create',
|
||||||
|
],
|
||||||
|
resource: [
|
||||||
|
'board',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: `The board's name`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Kind',
|
||||||
|
name: 'kind',
|
||||||
|
type: 'options',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Share',
|
||||||
|
value: 'share',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Public',
|
||||||
|
value: 'public',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Private',
|
||||||
|
value: 'private',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
operation: [
|
||||||
|
'create',
|
||||||
|
],
|
||||||
|
resource: [
|
||||||
|
'board',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: `The board's kind (public / private / share)`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Additional Fields',
|
||||||
|
name: 'additionalFields',
|
||||||
|
type: 'collection',
|
||||||
|
placeholder: 'Add Field',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
operation: [
|
||||||
|
'create',
|
||||||
|
],
|
||||||
|
resource: [
|
||||||
|
'board',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: {},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
displayName: 'Template ID',
|
||||||
|
name: 'templateId',
|
||||||
|
type: 'number',
|
||||||
|
typeOptions: {
|
||||||
|
minValue: 0,
|
||||||
|
},
|
||||||
|
default: 0,
|
||||||
|
description: 'Optional board template id',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* board:get */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
{
|
||||||
|
displayName: 'Board ID',
|
||||||
|
name: 'boardId',
|
||||||
|
type: 'options',
|
||||||
|
default: '',
|
||||||
|
typeOptions: {
|
||||||
|
loadOptionsMethod: 'getBoards',
|
||||||
|
},
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'board',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'get',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
description: 'Board unique identifiers.',
|
||||||
|
},
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* board:getAll */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
{
|
||||||
|
displayName: 'Return All',
|
||||||
|
name: 'returnAll',
|
||||||
|
type: 'boolean',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'board',
|
||||||
|
],
|
||||||
|
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: [
|
||||||
|
'board',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
returnAll: [
|
||||||
|
false,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
typeOptions: {
|
||||||
|
minValue: 1,
|
||||||
|
maxValue: 100,
|
||||||
|
},
|
||||||
|
default: 50,
|
||||||
|
description: 'How many results to return.',
|
||||||
|
},
|
||||||
|
] as INodeProperties[];
|
151
packages/nodes-base/nodes/MondayCom/BoardGroupDescription.ts
Normal file
151
packages/nodes-base/nodes/MondayCom/BoardGroupDescription.ts
Normal file
|
@ -0,0 +1,151 @@
|
||||||
|
import {
|
||||||
|
INodeProperties,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
export const boardGroupOperations = [
|
||||||
|
{
|
||||||
|
displayName: 'Operation',
|
||||||
|
name: 'operation',
|
||||||
|
type: 'options',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'boardGroup',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Delete',
|
||||||
|
value: 'delete',
|
||||||
|
description: 'Delete a group in a board',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Create',
|
||||||
|
value: 'create',
|
||||||
|
description: 'Create a group in a board',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Get All',
|
||||||
|
value: 'getAll',
|
||||||
|
description: `Get board's groups`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
default: 'create',
|
||||||
|
description: 'The operation to perform.',
|
||||||
|
},
|
||||||
|
] as INodeProperties[];
|
||||||
|
|
||||||
|
export const boardGroupFields = [
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* boardGroup:create */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
{
|
||||||
|
displayName: 'Board ID',
|
||||||
|
name: 'boardId',
|
||||||
|
type: 'options',
|
||||||
|
default: '',
|
||||||
|
typeOptions: {
|
||||||
|
loadOptionsMethod: 'getBoards',
|
||||||
|
},
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'boardGroup',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'create',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Name',
|
||||||
|
name: 'name',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
operation: [
|
||||||
|
'create',
|
||||||
|
],
|
||||||
|
resource: [
|
||||||
|
'boardGroup',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: `The group name`,
|
||||||
|
},
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* boardGroup:delete */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
{
|
||||||
|
displayName: 'Board ID',
|
||||||
|
name: 'boardId',
|
||||||
|
type: 'options',
|
||||||
|
typeOptions: {
|
||||||
|
loadOptionsMethod: 'getBoards',
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'boardGroup',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'delete',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Group ID',
|
||||||
|
name: 'groupId',
|
||||||
|
type: 'options',
|
||||||
|
default: '',
|
||||||
|
typeOptions: {
|
||||||
|
loadOptionsMethod: 'getGroups',
|
||||||
|
loadOptionsDependsOn: [
|
||||||
|
'boardId',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'boardGroup',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'delete',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* boardGroup:getAll */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
{
|
||||||
|
displayName: 'Board ID',
|
||||||
|
name: 'boardId',
|
||||||
|
type: 'options',
|
||||||
|
default: '',
|
||||||
|
typeOptions: {
|
||||||
|
loadOptionsMethod: 'getBoards',
|
||||||
|
},
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'boardGroup',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
] as INodeProperties[];
|
380
packages/nodes-base/nodes/MondayCom/BoardItemDescription.ts
Normal file
380
packages/nodes-base/nodes/MondayCom/BoardItemDescription.ts
Normal file
|
@ -0,0 +1,380 @@
|
||||||
|
import {
|
||||||
|
INodeProperties,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
export const boardItemOperations = [
|
||||||
|
{
|
||||||
|
displayName: 'Operation',
|
||||||
|
name: 'operation',
|
||||||
|
type: 'options',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'boardItem',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Create',
|
||||||
|
value: 'create',
|
||||||
|
description: `Create an item in a board's group`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Delete',
|
||||||
|
value: 'delete',
|
||||||
|
description: `Delete an item`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Get',
|
||||||
|
value: 'get',
|
||||||
|
description: 'Get an item',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Get All',
|
||||||
|
value: 'getAll',
|
||||||
|
description: 'Get all item',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Get By Column Value',
|
||||||
|
value: 'getByColumnValue',
|
||||||
|
description: 'Get items by column value',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
default: 'create',
|
||||||
|
description: 'The operation to perform.',
|
||||||
|
},
|
||||||
|
] as INodeProperties[];
|
||||||
|
|
||||||
|
export const boardItemFields = [
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* boardItem:create */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
{
|
||||||
|
displayName: 'Board ID',
|
||||||
|
name: 'boardId',
|
||||||
|
type: 'options',
|
||||||
|
typeOptions: {
|
||||||
|
loadOptionsMethod: 'getBoards',
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'boardItem',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'create',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Group ID',
|
||||||
|
name: 'groupId',
|
||||||
|
type: 'options',
|
||||||
|
default: '',
|
||||||
|
typeOptions: {
|
||||||
|
loadOptionsMethod: 'getGroups',
|
||||||
|
loadOptionsDependsOn: [
|
||||||
|
'boardId'
|
||||||
|
],
|
||||||
|
},
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'boardItem',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'create',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Name',
|
||||||
|
name: 'name',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
operation: [
|
||||||
|
'create',
|
||||||
|
],
|
||||||
|
resource: [
|
||||||
|
'boardItem',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: `The new item's name.`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Additional Fields',
|
||||||
|
name: 'additionalFields',
|
||||||
|
type: 'collection',
|
||||||
|
placeholder: 'Add Field',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
operation: [
|
||||||
|
'create',
|
||||||
|
],
|
||||||
|
resource: [
|
||||||
|
'boardItem',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: {},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
displayName: 'Column Values',
|
||||||
|
name: 'columnValues',
|
||||||
|
type: 'json',
|
||||||
|
typeOptions: {
|
||||||
|
alwaysOpenEditWindow: true,
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: 'The column values of the new item',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* boardItem:delete */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
{
|
||||||
|
displayName: 'Item ID',
|
||||||
|
name: 'itemId',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'boardItem',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'delete',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
description: `Item's ID`
|
||||||
|
},
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* boardItem:get */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
{
|
||||||
|
displayName: 'Item ID',
|
||||||
|
name: 'itemId',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'boardItem',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'get',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
description: `Item's ID (Multiple can be added separated by comma)`
|
||||||
|
},
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* boardItem:getAll */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
{
|
||||||
|
displayName: 'Board ID',
|
||||||
|
name: 'boardId',
|
||||||
|
type: 'options',
|
||||||
|
default: '',
|
||||||
|
typeOptions: {
|
||||||
|
loadOptionsMethod: 'getBoards',
|
||||||
|
},
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'boardItem',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Group ID',
|
||||||
|
name: 'groupId',
|
||||||
|
default: '',
|
||||||
|
type: 'options',
|
||||||
|
typeOptions: {
|
||||||
|
loadOptionsMethod: 'getGroups',
|
||||||
|
loadOptionsDependsOn: [
|
||||||
|
'boardId',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'boardItem',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Return All',
|
||||||
|
name: 'returnAll',
|
||||||
|
type: 'boolean',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'boardItem',
|
||||||
|
],
|
||||||
|
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: [
|
||||||
|
'boardItem',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
returnAll: [
|
||||||
|
false,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
typeOptions: {
|
||||||
|
minValue: 1,
|
||||||
|
maxValue: 100,
|
||||||
|
},
|
||||||
|
default: 50,
|
||||||
|
description: 'How many results to return.',
|
||||||
|
},
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* boardItem:getByColumnValue */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
{
|
||||||
|
displayName: 'Board ID',
|
||||||
|
name: 'boardId',
|
||||||
|
type: 'options',
|
||||||
|
typeOptions: {
|
||||||
|
loadOptionsMethod: 'getBoards',
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'boardItem',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getByColumnValue',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
description: 'The unique identifier of the board.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Column ID',
|
||||||
|
name: 'columnId',
|
||||||
|
type: 'options',
|
||||||
|
typeOptions: {
|
||||||
|
loadOptionsMethod: 'getColumns',
|
||||||
|
loadOptionsDependsOn: [
|
||||||
|
'boardId'
|
||||||
|
],
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'boardItem',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getByColumnValue',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
description: `The column's unique identifier.`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Column Value',
|
||||||
|
name: 'columnValue',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
default: '',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'boardItem',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getByColumnValue',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
description: 'The column value to search items by.'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Return All',
|
||||||
|
name: 'returnAll',
|
||||||
|
type: 'boolean',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'boardItem',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getByColumnValue',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
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: [
|
||||||
|
'boardItem',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getByColumnValue',
|
||||||
|
],
|
||||||
|
returnAll: [
|
||||||
|
false,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
typeOptions: {
|
||||||
|
minValue: 1,
|
||||||
|
maxValue: 100,
|
||||||
|
},
|
||||||
|
default: 50,
|
||||||
|
description: 'How many results to return.',
|
||||||
|
},
|
||||||
|
] as INodeProperties[];
|
68
packages/nodes-base/nodes/MondayCom/GenericFunctions.ts
Normal file
68
packages/nodes-base/nodes/MondayCom/GenericFunctions.ts
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
import {
|
||||||
|
OptionsWithUri,
|
||||||
|
} from 'request';
|
||||||
|
|
||||||
|
import {
|
||||||
|
IExecuteFunctions,
|
||||||
|
ILoadOptionsFunctions,
|
||||||
|
} from 'n8n-core';
|
||||||
|
|
||||||
|
import {
|
||||||
|
IDataObject,
|
||||||
|
IHookFunctions,
|
||||||
|
IWebhookFunctions
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
import {
|
||||||
|
get,
|
||||||
|
} from 'lodash';
|
||||||
|
|
||||||
|
export async function mondayComApiRequest(this: IExecuteFunctions | IWebhookFunctions | IHookFunctions | ILoadOptionsFunctions, body: any = {}, option: IDataObject = {}): Promise<any> { // tslint:disable-line:no-any
|
||||||
|
|
||||||
|
const credentials = this.getCredentials('mondayComApi');
|
||||||
|
|
||||||
|
if (credentials === undefined) {
|
||||||
|
throw new Error('No credentials got returned!');
|
||||||
|
}
|
||||||
|
|
||||||
|
const endpoint = 'https://api.monday.com/v2/';
|
||||||
|
|
||||||
|
let options: OptionsWithUri = {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Authorization': credentials.apiToken,
|
||||||
|
},
|
||||||
|
method: 'POST',
|
||||||
|
body,
|
||||||
|
uri: endpoint,
|
||||||
|
json: true
|
||||||
|
};
|
||||||
|
options = Object.assign({}, options, option);
|
||||||
|
try {
|
||||||
|
return await this.helpers.request!(options);
|
||||||
|
} catch (error) {
|
||||||
|
if (error.response) {
|
||||||
|
const errorMessage = error.response.body.error_message;
|
||||||
|
throw new Error(`Monday error response [${error.statusCode}]: ${errorMessage}`);
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function mondayComApiRequestAllItems(this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions, propertyName: string, body: any = {}): Promise<any> { // tslint:disable-line:no-any
|
||||||
|
|
||||||
|
const returnData: IDataObject[] = [];
|
||||||
|
|
||||||
|
let responseData;
|
||||||
|
body.variables.limit = 50;
|
||||||
|
body.variables.page = 1;
|
||||||
|
|
||||||
|
do {
|
||||||
|
responseData = await mondayComApiRequest.call(this, body);
|
||||||
|
returnData.push.apply(returnData, get(responseData, propertyName));
|
||||||
|
body.variables.page++;
|
||||||
|
} while (
|
||||||
|
get(responseData, propertyName).length > 0
|
||||||
|
);
|
||||||
|
return returnData;
|
||||||
|
}
|
629
packages/nodes-base/nodes/MondayCom/MondayCom.node.ts
Normal file
629
packages/nodes-base/nodes/MondayCom/MondayCom.node.ts
Normal file
|
@ -0,0 +1,629 @@
|
||||||
|
import {
|
||||||
|
IExecuteFunctions,
|
||||||
|
} from 'n8n-core';
|
||||||
|
|
||||||
|
import {
|
||||||
|
IDataObject,
|
||||||
|
ILoadOptionsFunctions,
|
||||||
|
INodeExecutionData,
|
||||||
|
INodePropertyOptions,
|
||||||
|
INodeType,
|
||||||
|
INodeTypeDescription,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
import {
|
||||||
|
mondayComApiRequest,
|
||||||
|
mondayComApiRequestAllItems,
|
||||||
|
} from './GenericFunctions';
|
||||||
|
|
||||||
|
import {
|
||||||
|
boardFields,
|
||||||
|
boardOperations,
|
||||||
|
} from './BoardDescription';
|
||||||
|
|
||||||
|
import {
|
||||||
|
boardColumnFields,
|
||||||
|
boardColumnOperations,
|
||||||
|
} from './BoardColumnDescription';
|
||||||
|
|
||||||
|
import {
|
||||||
|
boardGroupFields,
|
||||||
|
boardGroupOperations,
|
||||||
|
} from './BoardGroupDescription';
|
||||||
|
|
||||||
|
import {
|
||||||
|
boardItemFields,
|
||||||
|
boardItemOperations,
|
||||||
|
} from './BoardItemDescription';
|
||||||
|
|
||||||
|
import {
|
||||||
|
snakeCase,
|
||||||
|
} from 'change-case';
|
||||||
|
|
||||||
|
interface IGraphqlBody {
|
||||||
|
query: string;
|
||||||
|
variables: IDataObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MondayCom implements INodeType {
|
||||||
|
description: INodeTypeDescription = {
|
||||||
|
displayName: 'Monday.com',
|
||||||
|
name: 'mondayCom',
|
||||||
|
icon: 'file:mondayCom.png',
|
||||||
|
group: ['output'],
|
||||||
|
version: 1,
|
||||||
|
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
||||||
|
description: 'Consume Monday.com API',
|
||||||
|
defaults: {
|
||||||
|
name: 'Monday.com',
|
||||||
|
color: '#4353ff',
|
||||||
|
},
|
||||||
|
inputs: ['main'],
|
||||||
|
outputs: ['main'],
|
||||||
|
credentials: [
|
||||||
|
{
|
||||||
|
name: 'mondayComApi',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
properties: [
|
||||||
|
{
|
||||||
|
displayName: 'Resource',
|
||||||
|
name: 'resource',
|
||||||
|
type: 'options',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Board',
|
||||||
|
value: 'board',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Board Column',
|
||||||
|
value: 'boardColumn',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Board Group',
|
||||||
|
value: 'boardGroup',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Board Item',
|
||||||
|
value: 'boardItem',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
default: 'board',
|
||||||
|
description: 'Resource to consume.',
|
||||||
|
},
|
||||||
|
//BOARD
|
||||||
|
...boardOperations,
|
||||||
|
...boardFields,
|
||||||
|
// BOARD COLUMN
|
||||||
|
...boardColumnOperations,
|
||||||
|
...boardColumnFields,
|
||||||
|
// BOARD GROUP
|
||||||
|
...boardGroupOperations,
|
||||||
|
...boardGroupFields,
|
||||||
|
// BOARD ITEM
|
||||||
|
...boardItemOperations,
|
||||||
|
...boardItemFields,
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
methods = {
|
||||||
|
loadOptions: {
|
||||||
|
// Get all the available boards to display them to user so that he can
|
||||||
|
// select them easily
|
||||||
|
async getBoards(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||||
|
const returnData: INodePropertyOptions[] = [];
|
||||||
|
const body = {
|
||||||
|
query:
|
||||||
|
`query ($page: Int, $limit: Int) {
|
||||||
|
boards (page: $page, limit: $limit){
|
||||||
|
id
|
||||||
|
description
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
variables: {
|
||||||
|
page: 1,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const boards = await mondayComApiRequestAllItems.call(this, 'data.boards', body);
|
||||||
|
if (boards === undefined) {
|
||||||
|
return returnData;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const board of boards) {
|
||||||
|
const boardName = board.name;
|
||||||
|
const boardId = board.id;
|
||||||
|
const boardDescription = board.description;
|
||||||
|
returnData.push({
|
||||||
|
name: boardName,
|
||||||
|
value: boardId,
|
||||||
|
description: boardDescription,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return returnData;
|
||||||
|
},
|
||||||
|
// Get all the available columns to display them to user so that he can
|
||||||
|
// select them easily
|
||||||
|
async getColumns(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||||
|
const returnData: INodePropertyOptions[] = [];
|
||||||
|
const boardId = parseInt(this.getCurrentNodeParameter('boardId') as string, 10);
|
||||||
|
const body: IGraphqlBody = {
|
||||||
|
query:
|
||||||
|
`query ($boardId: [Int]) {
|
||||||
|
boards (ids: $boardId){
|
||||||
|
columns() {
|
||||||
|
id
|
||||||
|
title
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
variables: {
|
||||||
|
page: 1,
|
||||||
|
boardId,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const { data } = await mondayComApiRequest.call(this, body);
|
||||||
|
if (data === undefined) {
|
||||||
|
return returnData;
|
||||||
|
}
|
||||||
|
|
||||||
|
const columns = data.boards[0].columns;
|
||||||
|
for (const column of columns) {
|
||||||
|
const columnName = column.title;
|
||||||
|
const columnId = column.id;
|
||||||
|
returnData.push({
|
||||||
|
name: columnName,
|
||||||
|
value: columnId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return returnData;
|
||||||
|
},
|
||||||
|
// Get all the available groups to display them to user so that he can
|
||||||
|
// select them easily
|
||||||
|
async getGroups(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||||
|
const returnData: INodePropertyOptions[] = [];
|
||||||
|
const boardId = parseInt(this.getCurrentNodeParameter('boardId') as string, 10);
|
||||||
|
const body = {
|
||||||
|
query:
|
||||||
|
`query ($boardId: Int!) {
|
||||||
|
boards ( ids: [$boardId]){
|
||||||
|
groups () {
|
||||||
|
id
|
||||||
|
title
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
variables: {
|
||||||
|
boardId,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const { data } = await mondayComApiRequest.call(this, body);
|
||||||
|
if (data === undefined) {
|
||||||
|
return returnData;
|
||||||
|
}
|
||||||
|
|
||||||
|
const groups = data.boards[0].groups;
|
||||||
|
for (const group of groups) {
|
||||||
|
const groupName = group.title;
|
||||||
|
const groupId = group.id;
|
||||||
|
returnData.push({
|
||||||
|
name: groupName,
|
||||||
|
value: groupId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return returnData;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
||||||
|
const items = this.getInputData();
|
||||||
|
const returnData: IDataObject[] = [];
|
||||||
|
const length = items.length as unknown as number;
|
||||||
|
let responseData;
|
||||||
|
const qs: IDataObject = {};
|
||||||
|
const resource = this.getNodeParameter('resource', 0) as string;
|
||||||
|
const operation = this.getNodeParameter('operation', 0) as string;
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
if (resource === 'board') {
|
||||||
|
if (operation === 'archive') {
|
||||||
|
const boardId = parseInt(this.getNodeParameter('boardId', i) as string, 10);
|
||||||
|
|
||||||
|
const body: IGraphqlBody = {
|
||||||
|
query:
|
||||||
|
`mutation ($id: Int!) {
|
||||||
|
archive_board (board_id: $id) {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
variables: {
|
||||||
|
id: boardId,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
responseData = await mondayComApiRequest.call(this, body);
|
||||||
|
responseData = responseData.data.archive_board;
|
||||||
|
}
|
||||||
|
if (operation === 'create') {
|
||||||
|
const name = this.getNodeParameter('name', i) as string;
|
||||||
|
const kind = this.getNodeParameter('kind', i) as string;
|
||||||
|
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
|
||||||
|
|
||||||
|
const body: IGraphqlBody = {
|
||||||
|
query:
|
||||||
|
`mutation ($name: String!, $kind: BoardKind!, $templateId: Int) {
|
||||||
|
create_board (board_name: $name, board_kind: $kind, template_id: $templateId) {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
variables: {
|
||||||
|
name,
|
||||||
|
kind,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (additionalFields.templateId) {
|
||||||
|
body.variables.templateId = additionalFields.templateId as number;
|
||||||
|
}
|
||||||
|
|
||||||
|
responseData = await mondayComApiRequest.call(this, body);
|
||||||
|
responseData = responseData.data.create_board;
|
||||||
|
}
|
||||||
|
if (operation === 'get') {
|
||||||
|
const boardId = parseInt(this.getNodeParameter('boardId', i) as string, 10);
|
||||||
|
|
||||||
|
const body: IGraphqlBody = {
|
||||||
|
query:
|
||||||
|
`query ($id: [Int]) {
|
||||||
|
boards (ids: $id){
|
||||||
|
id
|
||||||
|
name
|
||||||
|
description
|
||||||
|
state
|
||||||
|
board_folder_id
|
||||||
|
board_kind
|
||||||
|
owner() {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
variables: {
|
||||||
|
id: boardId,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
responseData = await mondayComApiRequest.call(this, body);
|
||||||
|
responseData = responseData.data.boards;
|
||||||
|
}
|
||||||
|
if (operation === 'getAll') {
|
||||||
|
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
|
||||||
|
|
||||||
|
const body: IGraphqlBody = {
|
||||||
|
query:
|
||||||
|
`query ($page: Int, $limit: Int) {
|
||||||
|
boards (page: $page, limit: $limit){
|
||||||
|
id
|
||||||
|
name
|
||||||
|
description
|
||||||
|
state
|
||||||
|
board_folder_id
|
||||||
|
board_kind
|
||||||
|
owner() {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
variables: {
|
||||||
|
page: 1,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (returnAll === true) {
|
||||||
|
responseData = await mondayComApiRequestAllItems.call(this, 'data.boards', body);
|
||||||
|
} else {
|
||||||
|
body.variables.limit = this.getNodeParameter('limit', i) as number,
|
||||||
|
responseData = await mondayComApiRequest.call(this, body);
|
||||||
|
responseData = responseData.data.boards;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (resource === 'boardColumn') {
|
||||||
|
if (operation === 'create') {
|
||||||
|
const boardId = parseInt(this.getNodeParameter('boardId', i) as string, 10);
|
||||||
|
const title = this.getNodeParameter('title', i) as string;
|
||||||
|
const columnType = this.getNodeParameter('columnType', i) as string;
|
||||||
|
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
|
||||||
|
|
||||||
|
const body: IGraphqlBody = {
|
||||||
|
query:
|
||||||
|
`mutation ($boardId: Int!, $title: String!, $columnType: ColumnType, $defaults: JSON ) {
|
||||||
|
create_column (board_id: $boardId, title: $title, column_type: $columnType, defaults: $defaults) {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
variables: {
|
||||||
|
boardId,
|
||||||
|
title,
|
||||||
|
columnType: snakeCase(columnType),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (additionalFields.defaults) {
|
||||||
|
try {
|
||||||
|
JSON.parse(additionalFields.defaults as string);
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error('Defauls must be a valid JSON');
|
||||||
|
}
|
||||||
|
body.variables.defaults = JSON.stringify(JSON.parse(additionalFields.defaults as string));
|
||||||
|
}
|
||||||
|
|
||||||
|
responseData = await mondayComApiRequest.call(this, body);
|
||||||
|
responseData = responseData.data.create_column;
|
||||||
|
}
|
||||||
|
if (operation === 'getAll') {
|
||||||
|
const boardId = parseInt(this.getNodeParameter('boardId', i) as string, 10);
|
||||||
|
|
||||||
|
const body: IGraphqlBody = {
|
||||||
|
query:
|
||||||
|
`query ($boardId: [Int]) {
|
||||||
|
boards (ids: $boardId){
|
||||||
|
columns() {
|
||||||
|
id
|
||||||
|
title
|
||||||
|
type
|
||||||
|
settings_str
|
||||||
|
archived
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
variables: {
|
||||||
|
page: 1,
|
||||||
|
boardId,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
responseData = await mondayComApiRequest.call(this, body);
|
||||||
|
responseData = responseData.data.boards[0].columns;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (resource === 'boardGroup') {
|
||||||
|
if (operation === 'create') {
|
||||||
|
const boardId = parseInt(this.getNodeParameter('boardId', i) as string, 10);
|
||||||
|
const name = this.getNodeParameter('name', i) as string;
|
||||||
|
|
||||||
|
const body: IGraphqlBody = {
|
||||||
|
query:
|
||||||
|
`mutation ($boardId: Int!, $groupName: String!) {
|
||||||
|
create_group (board_id: $boardId, group_name: $groupName) {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
variables: {
|
||||||
|
boardId,
|
||||||
|
groupName: name,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
responseData = await mondayComApiRequest.call(this, body);
|
||||||
|
responseData = responseData.data.create_group;
|
||||||
|
}
|
||||||
|
if (operation === 'delete') {
|
||||||
|
const boardId = parseInt(this.getNodeParameter('boardId', i) as string, 10);
|
||||||
|
const groupId = this.getNodeParameter('groupId', i) as string;
|
||||||
|
|
||||||
|
const body: IGraphqlBody = {
|
||||||
|
query:
|
||||||
|
`mutation ($boardId: Int!, $groupId: String!) {
|
||||||
|
delete_group (board_id: $boardId, group_id: $groupId) {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
variables: {
|
||||||
|
boardId,
|
||||||
|
groupId,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
responseData = await mondayComApiRequest.call(this, body);
|
||||||
|
responseData = responseData.data.delete_group;
|
||||||
|
}
|
||||||
|
if (operation === 'getAll') {
|
||||||
|
const boardId = parseInt(this.getNodeParameter('boardId', i) as string, 10);
|
||||||
|
|
||||||
|
const body: IGraphqlBody = {
|
||||||
|
query:
|
||||||
|
`query ($boardId: [Int]) {
|
||||||
|
boards (ids: $boardId, ){
|
||||||
|
id
|
||||||
|
groups() {
|
||||||
|
id
|
||||||
|
title
|
||||||
|
color
|
||||||
|
position
|
||||||
|
archived
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
variables: {
|
||||||
|
boardId,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
responseData = await mondayComApiRequest.call(this, body);
|
||||||
|
responseData = responseData.data.boards[0].groups;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (resource === 'boardItem') {
|
||||||
|
if (operation === 'create') {
|
||||||
|
const boardId = parseInt(this.getNodeParameter('boardId', i) as string, 10);
|
||||||
|
const groupId = this.getNodeParameter('groupId', i) as string;
|
||||||
|
const itemName = this.getNodeParameter('name', i) as string;
|
||||||
|
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
|
||||||
|
|
||||||
|
const body: IGraphqlBody = {
|
||||||
|
query:
|
||||||
|
`mutation ($boardId: Int!, $groupId: String!, $itemName: String!, $columnValues: JSON) {
|
||||||
|
create_item (board_id: $boardId, group_id: $groupId, item_name: $itemName, column_values: $columnValues) {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
variables: {
|
||||||
|
boardId,
|
||||||
|
groupId,
|
||||||
|
itemName,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (additionalFields.columnValues) {
|
||||||
|
try {
|
||||||
|
JSON.parse(additionalFields.columnValues as string);
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error('Custom Values must be a valid JSON');
|
||||||
|
}
|
||||||
|
body.variables.columnValues = JSON.stringify(JSON.parse(additionalFields.columnValues as string));
|
||||||
|
}
|
||||||
|
|
||||||
|
responseData = await mondayComApiRequest.call(this, body);
|
||||||
|
responseData = responseData.data.create_item;
|
||||||
|
}
|
||||||
|
if (operation === 'delete') {
|
||||||
|
const itemId = parseInt((this.getNodeParameter('itemId', i) as string), 10);
|
||||||
|
|
||||||
|
const body: IGraphqlBody = {
|
||||||
|
query:
|
||||||
|
`mutation ($itemId: Int!) {
|
||||||
|
delete_item (item_id: $itemId) {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
variables: {
|
||||||
|
itemId,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
responseData = await mondayComApiRequest.call(this, body);
|
||||||
|
responseData = responseData.data.delete_item;
|
||||||
|
}
|
||||||
|
if (operation === 'get') {
|
||||||
|
const itemIds = ((this.getNodeParameter('itemId', i) as string).split(',') as string[]).map((n) => parseInt(n, 10));
|
||||||
|
|
||||||
|
const body: IGraphqlBody = {
|
||||||
|
query:
|
||||||
|
`query ($itemId: [Int!]){
|
||||||
|
items (ids: $itemId) {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
created_at
|
||||||
|
state
|
||||||
|
column_values() {
|
||||||
|
id
|
||||||
|
text
|
||||||
|
title
|
||||||
|
type
|
||||||
|
value
|
||||||
|
additional_info
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
variables: {
|
||||||
|
itemId: itemIds,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
responseData = await mondayComApiRequest.call(this, body);
|
||||||
|
responseData = responseData.data.items;
|
||||||
|
}
|
||||||
|
if (operation === 'getAll') {
|
||||||
|
const boardId = parseInt(this.getNodeParameter('boardId', i) as string, 10);
|
||||||
|
const groupId = this.getNodeParameter('groupId', i) as string;
|
||||||
|
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
|
||||||
|
|
||||||
|
const body: IGraphqlBody = {
|
||||||
|
query:
|
||||||
|
`query ($boardId: [Int], $groupId: [String], $page: Int, $limit: Int) {
|
||||||
|
boards (ids: $boardId) {
|
||||||
|
groups (ids: $groupId) {
|
||||||
|
id
|
||||||
|
items(limit: $limit, page: $page) {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
created_at
|
||||||
|
state
|
||||||
|
column_values() {
|
||||||
|
id
|
||||||
|
text
|
||||||
|
title
|
||||||
|
type
|
||||||
|
value
|
||||||
|
additional_info
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
variables: {
|
||||||
|
boardId,
|
||||||
|
groupId,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (returnAll) {
|
||||||
|
responseData = await mondayComApiRequestAllItems.call(this, 'data.boards[0].groups[0].items', body);
|
||||||
|
} else {
|
||||||
|
body.variables.limit = this.getNodeParameter('limit', i) as number;
|
||||||
|
responseData = await mondayComApiRequest.call(this, body);
|
||||||
|
responseData = responseData.data.boards[0].groups[0].items;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if (operation === 'getByColumnValue') {
|
||||||
|
const boardId = parseInt(this.getNodeParameter('boardId', i) as string, 10);
|
||||||
|
const columnId = this.getNodeParameter('columnId', i) as string;
|
||||||
|
const columnValue = this.getNodeParameter('columnValue', i) as string;
|
||||||
|
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
|
||||||
|
|
||||||
|
const body: IGraphqlBody = {
|
||||||
|
query:
|
||||||
|
`query ($boardId: Int!, $columnId: String!, $columnValue: String!, $page: Int, $limit: Int ){
|
||||||
|
items_by_column_values (board_id: $boardId, column_id: $columnId, column_value: $columnValue, page: $page, limit: $limit) {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
created_at
|
||||||
|
state
|
||||||
|
board {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
column_values() {
|
||||||
|
id
|
||||||
|
text
|
||||||
|
title
|
||||||
|
type
|
||||||
|
value
|
||||||
|
additional_info
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
variables: {
|
||||||
|
boardId,
|
||||||
|
columnId,
|
||||||
|
columnValue,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (returnAll) {
|
||||||
|
responseData = await mondayComApiRequestAllItems.call(this, 'data.items_by_column_values', body);
|
||||||
|
} else {
|
||||||
|
body.variables.limit = this.getNodeParameter('limit', i) as number;
|
||||||
|
responseData = await mondayComApiRequest.call(this, body);
|
||||||
|
responseData = responseData.data.items_by_column_values;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Array.isArray(responseData)) {
|
||||||
|
returnData.push.apply(returnData, responseData as IDataObject[]);
|
||||||
|
} else {
|
||||||
|
returnData.push(responseData as IDataObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return [this.helpers.returnJsonArray(returnData)];
|
||||||
|
}
|
||||||
|
}
|
BIN
packages/nodes-base/nodes/MondayCom/mondayCom.png
Normal file
BIN
packages/nodes-base/nodes/MondayCom/mondayCom.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
|
@ -107,6 +107,21 @@ export class Postgres implements INodeType {
|
||||||
// ----------------------------------
|
// ----------------------------------
|
||||||
// insert
|
// insert
|
||||||
// ----------------------------------
|
// ----------------------------------
|
||||||
|
{
|
||||||
|
displayName: 'Schema',
|
||||||
|
name: 'schema',
|
||||||
|
type: 'string',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
operation: [
|
||||||
|
'insert'
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: 'public',
|
||||||
|
required: true,
|
||||||
|
description: 'Name of the schema the table belongs to',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Table',
|
displayName: 'Table',
|
||||||
name: 'table',
|
name: 'table',
|
||||||
|
@ -137,6 +152,20 @@ export class Postgres implements INodeType {
|
||||||
placeholder: 'id,name,description',
|
placeholder: 'id,name,description',
|
||||||
description: 'Comma separated list of the properties which should used as columns for the new rows.',
|
description: 'Comma separated list of the properties which should used as columns for the new rows.',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Return Fields',
|
||||||
|
name: 'returnFields',
|
||||||
|
type: 'string',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
operation: [
|
||||||
|
'insert'
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '*',
|
||||||
|
description: 'Comma separated list of the fields that the operation will return',
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
// ----------------------------------
|
// ----------------------------------
|
||||||
|
@ -239,20 +268,24 @@ export class Postgres implements INodeType {
|
||||||
// ----------------------------------
|
// ----------------------------------
|
||||||
|
|
||||||
const table = this.getNodeParameter('table', 0) as string;
|
const table = this.getNodeParameter('table', 0) as string;
|
||||||
|
const schema = this.getNodeParameter('schema', 0) as string;
|
||||||
|
let returnFields = (this.getNodeParameter('returnFields', 0) as string).split(',') as string[];
|
||||||
const columnString = this.getNodeParameter('columns', 0) as string;
|
const columnString = this.getNodeParameter('columns', 0) as string;
|
||||||
|
|
||||||
const columns = columnString.split(',').map(column => column.trim());
|
const columns = columnString.split(',').map(column => column.trim());
|
||||||
|
|
||||||
const cs = new pgp.helpers.ColumnSet(columns, { table });
|
const cs = new pgp.helpers.ColumnSet(columns);
|
||||||
|
|
||||||
|
const te = new pgp.helpers.TableName({ table, schema });
|
||||||
|
|
||||||
// Prepare the data to insert and copy it to be returned
|
// Prepare the data to insert and copy it to be returned
|
||||||
const insertItems = getItemCopy(items, columns);
|
const insertItems = getItemCopy(items, columns);
|
||||||
|
|
||||||
// Generate the multi-row insert query and return the id of new row
|
// Generate the multi-row insert query and return the id of new row
|
||||||
const query = pgp.helpers.insert(insertItems, cs) + ' RETURNING id';
|
returnFields = returnFields.map(value => value.trim()).filter(value => !!value);
|
||||||
|
const query = pgp.helpers.insert(insertItems, cs, te) + (returnFields.length ? ` RETURNING ${returnFields.join(',')}` : '');
|
||||||
|
|
||||||
// Executing the query to insert the data
|
// Executing the query to insert the data
|
||||||
const insertData = await db.many(query);
|
const insertData = await db.manyOrNone(query);
|
||||||
|
|
||||||
// Add the id to the data
|
// Add the id to the data
|
||||||
for (let i = 0; i < insertData.length; i++) {
|
for (let i = 0; i < insertData.length; i++) {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { IExecuteFunctions } from 'n8n-core';
|
import { IExecuteFunctions } from 'n8n-core';
|
||||||
import {
|
import {
|
||||||
|
IDataObject,
|
||||||
INodeExecutionData,
|
INodeExecutionData,
|
||||||
INodeParameters,
|
INodeParameters,
|
||||||
INodeType,
|
INodeType,
|
||||||
|
@ -103,6 +104,26 @@ export class Set implements INodeType {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
displayName: 'Options',
|
||||||
|
name: 'options',
|
||||||
|
type: 'collection',
|
||||||
|
placeholder: 'Add Option',
|
||||||
|
default: {},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
displayName: 'Dot Notation',
|
||||||
|
name: 'dotNotation',
|
||||||
|
type: 'boolean',
|
||||||
|
default: true,
|
||||||
|
description: `By default does dot-notation get used in property names..<br />
|
||||||
|
This means that "a.b" will set the property "b" underneath "a" so { "a": { "b": value} }.<br />
|
||||||
|
If that is not intended this can be deactivated, it will then set { "a.b": value } instead.
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -122,6 +143,7 @@ export class Set implements INodeType {
|
||||||
for (let itemIndex = 0; itemIndex < items.length; itemIndex++) {
|
for (let itemIndex = 0; itemIndex < items.length; itemIndex++) {
|
||||||
keepOnlySet = this.getNodeParameter('keepOnlySet', itemIndex, []) as boolean;
|
keepOnlySet = this.getNodeParameter('keepOnlySet', itemIndex, []) as boolean;
|
||||||
item = items[itemIndex];
|
item = items[itemIndex];
|
||||||
|
const options = this.getNodeParameter('options', itemIndex, {}) as IDataObject;
|
||||||
|
|
||||||
const newItem: INodeExecutionData = {
|
const newItem: INodeExecutionData = {
|
||||||
json: {},
|
json: {},
|
||||||
|
@ -141,17 +163,29 @@ export class Set implements INodeType {
|
||||||
|
|
||||||
// Add boolean values
|
// Add boolean values
|
||||||
(this.getNodeParameter('values.boolean', itemIndex, []) as INodeParameters[]).forEach((setItem) => {
|
(this.getNodeParameter('values.boolean', itemIndex, []) as INodeParameters[]).forEach((setItem) => {
|
||||||
|
if (options.dotNotation === false) {
|
||||||
|
newItem.json[setItem.name as string] = !!setItem.value;
|
||||||
|
} else {
|
||||||
set(newItem.json, setItem.name as string, !!setItem.value);
|
set(newItem.json, setItem.name as string, !!setItem.value);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add number values
|
// Add number values
|
||||||
(this.getNodeParameter('values.number', itemIndex, []) as INodeParameters[]).forEach((setItem) => {
|
(this.getNodeParameter('values.number', itemIndex, []) as INodeParameters[]).forEach((setItem) => {
|
||||||
|
if (options.dotNotation === false) {
|
||||||
|
newItem.json[setItem.name as string] = setItem.value;
|
||||||
|
} else {
|
||||||
set(newItem.json, setItem.name as string, setItem.value);
|
set(newItem.json, setItem.name as string, setItem.value);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add string values
|
// Add string values
|
||||||
(this.getNodeParameter('values.string', itemIndex, []) as INodeParameters[]).forEach((setItem) => {
|
(this.getNodeParameter('values.string', itemIndex, []) as INodeParameters[]).forEach((setItem) => {
|
||||||
|
if (options.dotNotation === false) {
|
||||||
|
newItem.json[setItem.name as string] = setItem.value;
|
||||||
|
} else {
|
||||||
set(newItem.json, setItem.name as string, setItem.value);
|
set(newItem.json, setItem.name as string, setItem.value);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
returnData.push(newItem);
|
returnData.push(newItem);
|
||||||
|
|
|
@ -196,6 +196,20 @@ export class SpreadsheetFile implements INodeType {
|
||||||
default: false,
|
default: false,
|
||||||
description: 'If the data should be returned RAW instead of parsed.',
|
description: 'If the data should be returned RAW instead of parsed.',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Read As String',
|
||||||
|
name: 'readAsString',
|
||||||
|
type: 'boolean',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
'/operation': [
|
||||||
|
'fromFile'
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: false,
|
||||||
|
description: 'In some cases and file formats, it is necessary to read<br />specifically as string else some special character get interpreted wrong.',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Sheet Name',
|
displayName: 'Sheet Name',
|
||||||
name: 'sheetName',
|
name: 'sheetName',
|
||||||
|
@ -259,7 +273,12 @@ export class SpreadsheetFile implements INodeType {
|
||||||
|
|
||||||
// Read the binary spreadsheet data
|
// Read the binary spreadsheet data
|
||||||
const binaryData = Buffer.from(item.binary[binaryPropertyName].data, BINARY_ENCODING);
|
const binaryData = Buffer.from(item.binary[binaryPropertyName].data, BINARY_ENCODING);
|
||||||
const workbook = xlsxRead(binaryData, { raw: options.rawData as boolean });
|
let workbook;
|
||||||
|
if (options.readAsString === true) {
|
||||||
|
workbook = xlsxRead(binaryData.toString(), { type: 'string', raw: options.rawData as boolean });
|
||||||
|
} else {
|
||||||
|
workbook = xlsxRead(binaryData, { raw: options.rawData as boolean });
|
||||||
|
}
|
||||||
|
|
||||||
if (workbook.SheetNames.length === 0) {
|
if (workbook.SheetNames.length === 0) {
|
||||||
throw new Error('Spreadsheet does not have any sheets!');
|
throw new Error('Spreadsheet does not have any sheets!');
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import {
|
import {
|
||||||
|
BINARY_ENCODING,
|
||||||
IWebhookFunctions,
|
IWebhookFunctions,
|
||||||
} from 'n8n-core';
|
} from 'n8n-core';
|
||||||
|
|
||||||
|
@ -218,6 +219,35 @@ export class Webhook implements INodeType {
|
||||||
placeholder: 'Add Option',
|
placeholder: 'Add Option',
|
||||||
default: {},
|
default: {},
|
||||||
options: [
|
options: [
|
||||||
|
{
|
||||||
|
displayName: 'Binary Data',
|
||||||
|
name: 'binaryData',
|
||||||
|
type: 'boolean',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
'/httpMethod': [
|
||||||
|
'POST',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: false,
|
||||||
|
description: 'Set to true if webhook will receive binary data.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Binary Property',
|
||||||
|
name: 'binaryPropertyName',
|
||||||
|
type: 'string',
|
||||||
|
default: 'data',
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
binaryData: [
|
||||||
|
true,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
description: 'Name of the binary property to which to<br />write the data of the received file.',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Response Content-Type',
|
displayName: 'Response Content-Type',
|
||||||
name: 'responseContentType',
|
name: 'responseContentType',
|
||||||
|
@ -257,6 +287,13 @@ export class Webhook implements INodeType {
|
||||||
displayName: 'Raw Body',
|
displayName: 'Raw Body',
|
||||||
name: 'rawBody',
|
name: 'rawBody',
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
|
displayOptions: {
|
||||||
|
hide: {
|
||||||
|
binaryData: [
|
||||||
|
true,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
default: false,
|
default: false,
|
||||||
description: 'Raw body (binary)',
|
description: 'Raw body (binary)',
|
||||||
},
|
},
|
||||||
|
@ -265,10 +302,9 @@ export class Webhook implements INodeType {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
async webhook(this: IWebhookFunctions): Promise<IWebhookResponseData> {
|
async webhook(this: IWebhookFunctions): Promise<IWebhookResponseData> {
|
||||||
const authentication = this.getNodeParameter('authentication', 0) as string;
|
const authentication = this.getNodeParameter('authentication') as string;
|
||||||
const options = this.getNodeParameter('options', 0) as IDataObject;
|
const options = this.getNodeParameter('options', {}) as IDataObject;
|
||||||
const req = this.getRequestObject();
|
const req = this.getRequestObject();
|
||||||
const resp = this.getResponseObject();
|
const resp = this.getResponseObject();
|
||||||
const headers = this.getHeaderData();
|
const headers = this.getHeaderData();
|
||||||
|
@ -328,8 +364,9 @@ export class Webhook implements INodeType {
|
||||||
const fileJson = files[file].toJSON() as IDataObject;
|
const fileJson = files[file].toJSON() as IDataObject;
|
||||||
const [fileName, fileExtension] = (fileJson.name as string).split('.');
|
const [fileName, fileExtension] = (fileJson.name as string).split('.');
|
||||||
const fileContent = await fs.promises.readFile(files[file].path);
|
const fileContent = await fs.promises.readFile(files[file].path);
|
||||||
|
const buffer = Buffer.from(fileContent);
|
||||||
set(returnData[0], `binary[${fileName}]`, {
|
set(returnData[0], `binary[${fileName}]`, {
|
||||||
data: fileContent,
|
data: buffer.toString(BINARY_ENCODING),
|
||||||
mimeType: fileJson.type,
|
mimeType: fileJson.type,
|
||||||
fileName: fileJson.name,
|
fileName: fileJson.name,
|
||||||
fileExtension,
|
fileExtension,
|
||||||
|
@ -345,6 +382,47 @@ export class Webhook implements INodeType {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (options.binaryData === true) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const binaryPropertyName = options.binaryPropertyName || 'data';
|
||||||
|
const data: Buffer[] = [];
|
||||||
|
|
||||||
|
req.on('data', (chunk) => {
|
||||||
|
data.push(chunk);
|
||||||
|
});
|
||||||
|
|
||||||
|
req.on('end', async () => {
|
||||||
|
const returnItem: INodeExecutionData = {
|
||||||
|
binary: {},
|
||||||
|
json: {
|
||||||
|
body: this.getBodyData(),
|
||||||
|
headers,
|
||||||
|
query: this.getQueryData(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const returnData: IDataObject[] = [{ json: {} }];
|
||||||
|
set(returnData[0], `binary[${binaryPropertyName}]`, {
|
||||||
|
data: Buffer.concat(data).toString(BINARY_ENCODING),
|
||||||
|
mimeType: req.headers['content-type'],
|
||||||
|
});
|
||||||
|
returnItem.binary![binaryPropertyName as string] = await this.helpers.prepareBinaryData(Buffer.concat(data));
|
||||||
|
|
||||||
|
return resolve({
|
||||||
|
workflowData: [
|
||||||
|
[
|
||||||
|
returnItem
|
||||||
|
]
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
req.on('error', (err) => {
|
||||||
|
throw new Error(err.message);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const response: INodeExecutionData = {
|
const response: INodeExecutionData = {
|
||||||
json: {
|
json: {
|
||||||
body: this.getBodyData(),
|
body: this.getBodyData(),
|
||||||
|
@ -352,11 +430,12 @@ export class Webhook implements INodeType {
|
||||||
query: this.getQueryData(),
|
query: this.getQueryData(),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
if (options.rawBody) {
|
if (options.rawBody) {
|
||||||
response.binary = {
|
response.binary = {
|
||||||
data: {
|
data: {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
data: req.rawBody.toString('base64'),
|
data: req.rawBody.toString(BINARY_ENCODING),
|
||||||
mimeType,
|
mimeType,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "n8n-nodes-base",
|
"name": "n8n-nodes-base",
|
||||||
"version": "0.50.1",
|
"version": "0.54.0",
|
||||||
"description": "Base nodes of n8n",
|
"description": "Base nodes of n8n",
|
||||||
"license": "SEE LICENSE IN LICENSE.md",
|
"license": "SEE LICENSE IN LICENSE.md",
|
||||||
"homepage": "https://n8n.io",
|
"homepage": "https://n8n.io",
|
||||||
|
@ -75,6 +75,7 @@
|
||||||
"dist/credentials/MattermostApi.credentials.js",
|
"dist/credentials/MattermostApi.credentials.js",
|
||||||
"dist/credentials/MauticApi.credentials.js",
|
"dist/credentials/MauticApi.credentials.js",
|
||||||
"dist/credentials/MoceanApi.credentials.js",
|
"dist/credentials/MoceanApi.credentials.js",
|
||||||
|
"dist/credentials/MondayComApi.credentials.js",
|
||||||
"dist/credentials/MongoDb.credentials.js",
|
"dist/credentials/MongoDb.credentials.js",
|
||||||
"dist/credentials/Msg91Api.credentials.js",
|
"dist/credentials/Msg91Api.credentials.js",
|
||||||
"dist/credentials/MySql.credentials.js",
|
"dist/credentials/MySql.credentials.js",
|
||||||
|
@ -185,6 +186,7 @@
|
||||||
"dist/nodes/Mautic/MauticTrigger.node.js",
|
"dist/nodes/Mautic/MauticTrigger.node.js",
|
||||||
"dist/nodes/Merge.node.js",
|
"dist/nodes/Merge.node.js",
|
||||||
"dist/nodes/Mocean/Mocean.node.js",
|
"dist/nodes/Mocean/Mocean.node.js",
|
||||||
|
"dist/nodes/MondayCom/MondayCom.node.js",
|
||||||
"dist/nodes/MongoDb/MongoDb.node.js",
|
"dist/nodes/MongoDb/MongoDb.node.js",
|
||||||
"dist/nodes/MoveBinaryData.node.js",
|
"dist/nodes/MoveBinaryData.node.js",
|
||||||
"dist/nodes/Msg91/Msg91.node.js",
|
"dist/nodes/Msg91/Msg91.node.js",
|
||||||
|
@ -260,7 +262,7 @@
|
||||||
"@types/xml2js": "^0.4.3",
|
"@types/xml2js": "^0.4.3",
|
||||||
"gulp": "^4.0.0",
|
"gulp": "^4.0.0",
|
||||||
"jest": "^24.9.0",
|
"jest": "^24.9.0",
|
||||||
"n8n-workflow": "~0.23.0",
|
"n8n-workflow": "~0.25.0",
|
||||||
"ts-jest": "^24.0.2",
|
"ts-jest": "^24.0.2",
|
||||||
"tslint": "^5.17.0",
|
"tslint": "^5.17.0",
|
||||||
"typescript": "~3.7.4"
|
"typescript": "~3.7.4"
|
||||||
|
@ -282,7 +284,7 @@
|
||||||
"lodash.unset": "^4.5.2",
|
"lodash.unset": "^4.5.2",
|
||||||
"mongodb": "^3.3.2",
|
"mongodb": "^3.3.2",
|
||||||
"mysql2": "^2.0.1",
|
"mysql2": "^2.0.1",
|
||||||
"n8n-core": "~0.26.0",
|
"n8n-core": "~0.28.0",
|
||||||
"nodemailer": "^5.1.1",
|
"nodemailer": "^5.1.1",
|
||||||
"pdf-parse": "^1.1.1",
|
"pdf-parse": "^1.1.1",
|
||||||
"pg-promise": "^9.0.3",
|
"pg-promise": "^9.0.3",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "n8n-workflow",
|
"name": "n8n-workflow",
|
||||||
"version": "0.23.0",
|
"version": "0.25.0",
|
||||||
"description": "Workflow base code of n8n",
|
"description": "Workflow base code of n8n",
|
||||||
"license": "SEE LICENSE IN LICENSE.md",
|
"license": "SEE LICENSE IN LICENSE.md",
|
||||||
"homepage": "https://n8n.io",
|
"homepage": "https://n8n.io",
|
||||||
|
|
|
@ -155,6 +155,7 @@ export interface IExecuteContextData {
|
||||||
|
|
||||||
export interface IExecuteFunctions {
|
export interface IExecuteFunctions {
|
||||||
continueOnFail(): boolean;
|
continueOnFail(): boolean;
|
||||||
|
evaluateExpression(expression: string, itemIndex: number): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[];
|
||||||
executeWorkflow(workflowInfo: IExecuteWorkflowInfo, inputData?: INodeExecutionData[]): Promise<any>; // tslint:disable-line:no-any
|
executeWorkflow(workflowInfo: IExecuteWorkflowInfo, inputData?: INodeExecutionData[]): Promise<any>; // tslint:disable-line:no-any
|
||||||
getContext(type: string): IContextObject;
|
getContext(type: string): IContextObject;
|
||||||
getCredentials(type: string): ICredentialDataDecryptedObject | undefined;
|
getCredentials(type: string): ICredentialDataDecryptedObject | undefined;
|
||||||
|
@ -176,6 +177,7 @@ export interface IExecuteFunctions {
|
||||||
|
|
||||||
export interface IExecuteSingleFunctions {
|
export interface IExecuteSingleFunctions {
|
||||||
continueOnFail(): boolean;
|
continueOnFail(): boolean;
|
||||||
|
evaluateExpression(expression: string, itemIndex: number | undefined): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[];
|
||||||
getContext(type: string): IContextObject;
|
getContext(type: string): IContextObject;
|
||||||
getCredentials(type: string): ICredentialDataDecryptedObject | undefined;
|
getCredentials(type: string): ICredentialDataDecryptedObject | undefined;
|
||||||
getInputData(inputIndex?: number, inputName?: string): INodeExecutionData;
|
getInputData(inputIndex?: number, inputName?: string): INodeExecutionData;
|
||||||
|
@ -530,8 +532,12 @@ export interface IWorkflowDataProxyData {
|
||||||
$binary: any; // tslint:disable-line:no-any
|
$binary: any; // tslint:disable-line:no-any
|
||||||
$data: any; // tslint:disable-line:no-any
|
$data: any; // tslint:disable-line:no-any
|
||||||
$env: any; // tslint:disable-line:no-any
|
$env: any; // tslint:disable-line:no-any
|
||||||
|
$evaluateExpression: any; // tslint:disable-line:no-any
|
||||||
|
$item: any; // tslint:disable-line:no-any
|
||||||
|
$json: any; // tslint:disable-line:no-any
|
||||||
$node: any; // tslint:disable-line:no-any
|
$node: any; // tslint:disable-line:no-any
|
||||||
$parameter: any; // tslint:disable-line:no-any
|
$parameter: any; // tslint:disable-line:no-any
|
||||||
|
$workflow: any; // tslint:disable-line:no-any
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IWorkflowMetadata {
|
export interface IWorkflowMetadata {
|
||||||
|
|
|
@ -897,10 +897,14 @@ export class Workflow {
|
||||||
|
|
||||||
// Generate a data proxy which allows to query workflow data
|
// Generate a data proxy which allows to query workflow data
|
||||||
const dataProxy = new WorkflowDataProxy(this, runExecutionData, runIndex, itemIndex, activeNodeName, connectionInputData);
|
const dataProxy = new WorkflowDataProxy(this, runExecutionData, runIndex, itemIndex, activeNodeName, connectionInputData);
|
||||||
|
const data = dataProxy.getDataProxy();
|
||||||
|
data.$evaluateExpression = (expression: string) => {
|
||||||
|
return this.resolveSimpleParameterValue('=' + expression, runExecutionData, runIndex, itemIndex, activeNodeName, connectionInputData, returnObjectAsString);
|
||||||
|
};
|
||||||
|
|
||||||
// Execute the expression
|
// Execute the expression
|
||||||
try {
|
try {
|
||||||
const returnValue = tmpl.tmpl(parameterValue, dataProxy.getDataProxy());
|
const returnValue = tmpl.tmpl(parameterValue, data);
|
||||||
if (returnValue !== null && typeof returnValue === 'object') {
|
if (returnValue !== null && typeof returnValue === 'object') {
|
||||||
if (Object.keys(returnValue).length === 0) {
|
if (Object.keys(returnValue).length === 0) {
|
||||||
// When expression is incomplete it returns a Proxy which causes problems.
|
// When expression is incomplete it returns a Proxy which causes problems.
|
||||||
|
|
|
@ -300,6 +300,7 @@ export class WorkflowDataProxy {
|
||||||
$binary: {}, // Placeholder
|
$binary: {}, // Placeholder
|
||||||
$data: {}, // Placeholder
|
$data: {}, // Placeholder
|
||||||
$env: this.envGetter(),
|
$env: this.envGetter(),
|
||||||
|
$evaluateExpression: (expression: string) => { }, // Placeholder
|
||||||
$item: (itemIndex: number) => {
|
$item: (itemIndex: number) => {
|
||||||
const dataProxy = new WorkflowDataProxy(this.workflow, this.runExecutionData, this.runIndex, itemIndex, this.activeNodeName, this.connectionInputData);
|
const dataProxy = new WorkflowDataProxy(this.workflow, this.runExecutionData, this.runIndex, itemIndex, this.activeNodeName, this.connectionInputData);
|
||||||
return dataProxy.getDataProxy();
|
return dataProxy.getDataProxy();
|
||||||
|
|
Loading…
Reference in a new issue