🔀 Merge branch 'RicardoE105-feature/pipedrive-node'

This commit is contained in:
Jan Oberhauser 2020-07-10 10:26:37 +02:00
commit 06cc2d4993
4 changed files with 218 additions and 86 deletions

View file

@ -6,25 +6,23 @@ n8n is a free and open [fair-code](http://faircode.io) licensed node based Workf
<a href="https://raw.githubusercontent.com/n8n-io/n8n/master/assets/n8n-screenshot.png"><img src="https://raw.githubusercontent.com/n8n-io/n8n/master/assets/n8n-screenshot.png" width="550" alt="n8n.io - Screenshot"></a> <a href="https://raw.githubusercontent.com/n8n-io/n8n/master/assets/n8n-screenshot.png"><img src="https://raw.githubusercontent.com/n8n-io/n8n/master/assets/n8n-screenshot.png" width="550" alt="n8n.io - Screenshot"></a>
## Contents ## Contents
- [Demo](#demo) - [Demo](#demo)
- [Available integrations](#available-integrations) - [Available integrations](#available-integrations)
- [Documentation](#documentation) - [Documentation](#documentation)
- [Start n8n in Docker](#start-n8n-in-docker) - [Start n8n in Docker](#start-n8n-in-docker)
- [Start with tunnel](#start-with-tunnel) - [Start with tunnel](#start-with-tunnel)
- [Securing n8n](#securing-n8n) - [Securing n8n](#securing-n8n)
- [Persist data](#persist-data) - [Persist data](#persist-data)
- [Passing Sensitive Data via File](#passing-sensitive-data-via-file) - [Passing Sensitive Data via File](#passing-sensitive-data-via-file)
- [Updating a Running docker-compose Instance](#updating-a-running-docker-compose-instance) - [Updating a Running docker-compose Instance](#updating-a-running-docker-compose-instance)
- [Example Setup with Lets Encrypt](#example-setup-with-lets-encrypt) - [Example Setup with Lets Encrypt](#example-setup-with-lets-encrypt)
- [What does n8n mean and how do you pronounce it](#what-does-n8n-mean-and-how-do-you-pronounce-it) - [What does n8n mean and how do you pronounce it](#what-does-n8n-mean-and-how-do-you-pronounce-it)
- [Support](#support) - [Support](#support)
- [Jobs](#jobs) - [Jobs](#jobs)
- [Upgrading](#upgrading) - [Upgrading](#upgrading)
- [License](#license) - [License](#license)
## Demo ## Demo
@ -49,9 +47,9 @@ Additional information and example workflows on the n8n.io website: [https://n8n
``` ```
docker run -it --rm \ docker run -it --rm \
--name n8n \ --name n8n \
-p 5678:5678 \ -p 5678:5678 \
n8nio/n8n n8nio/n8n
``` ```
You can then access n8n by opening: You can then access n8n by opening:
@ -71,14 +69,13 @@ To use it simply start n8n with `--tunnel`
``` ```
docker run -it --rm \ docker run -it --rm \
--name n8n \ --name n8n \
-p 5678:5678 \ -p 5678:5678 \
-v ~/.n8n:/root/.n8n \ -v ~/.n8n:/root/.n8n \
n8nio/n8n \ n8nio/n8n \
n8n start --tunnel n8n start --tunnel
``` ```
## Securing n8n ## Securing n8n
By default n8n can be accessed by everybody. This is OK if you have it only running By default n8n can be accessed by everybody. This is OK if you have it only running
@ -93,7 +90,6 @@ N8N_BASIC_AUTH_USER=<USER>
N8N_BASIC_AUTH_PASSWORD=<PASSWORD> N8N_BASIC_AUTH_PASSWORD=<PASSWORD>
``` ```
## Persist data ## Persist data
The workflow data gets by default saved in an SQLite database in the user The workflow data gets by default saved in an SQLite database in the user
@ -102,10 +98,10 @@ settings like webhook URL and encryption key.
``` ```
docker run -it --rm \ docker run -it --rm \
--name n8n \ --name n8n \
-p 5678:5678 \ -p 5678:5678 \
-v ~/.n8n:/root/.n8n \ -v ~/.n8n:/root/.n8n \
n8nio/n8n n8nio/n8n
``` ```
### Start with other Database ### Start with other Database
@ -121,7 +117,6 @@ for the credentials. If none gets found n8n creates automatically one on
startup. In case credentials are already saved with a different encryption key startup. In case credentials are already saved with a different encryption key
it can not be used anymore as encrypting it is not possible anymore. it can not be used anymore as encrypting it is not possible anymore.
#### Use with MongoDB #### Use with MongoDB
> **WARNING**: Use Postgres if possible! Mongo has problems with saving large > **WARNING**: Use Postgres if possible! Mongo has problems with saving large
@ -129,40 +124,39 @@ it can not be used anymore as encrypting it is not possible anymore.
> may be dropped in the future. > may be dropped in the future.
Replace the following placeholders with the actual data: Replace the following placeholders with the actual data:
- <MONGO_DATABASE> - MONGO_DATABASE
- <MONGO_HOST> - MONGO_HOST
- <MONGO_PORT> - MONGO_PORT
- <MONGO_USER> - MONGO_USER
- <MONGO_PASSWORD> - MONGO_PASSWORD
``` ```
docker run -it --rm \ docker run -it --rm \
--name n8n \ --name n8n \
-p 5678:5678 \ -p 5678:5678 \
-e DB_TYPE=mongodb \ -e DB_TYPE=mongodb \
-e DB_MONGODB_CONNECTION_URL="mongodb://<MONGO_USER>:<MONGO_PASSWORD>@<MONGO_SERVER>:<MONGO_PORT>/<MONGO_DATABASE>" \ -e DB_MONGODB_CONNECTION_URL="mongodb://<MONGO_USER>:<MONGO_PASSWORD>@<MONGO_SERVER>:<MONGO_PORT>/<MONGO_DATABASE>" \
-v ~/.n8n:/root/.n8n \ -v ~/.n8n:/root/.n8n \
n8nio/n8n \ n8nio/n8n \
n8n start n8n start
``` ```
A full working setup with docker-compose can be found [here](https://github.com/n8n-io/n8n/blob/master/docker/compose/withMongo/README.md) A full working setup with docker-compose can be found [here](https://github.com/n8n-io/n8n/blob/master/docker/compose/withMongo/README.md)
#### Use with PostgresDB #### Use with PostgresDB
Replace the following placeholders with the actual data: Replace the following placeholders with the actual data:
- <POSTGRES_DATABASE> - POSTGRES_DATABASE
- <POSTGRES_HOST> - POSTGRES_HOST
- <POSTGRES_PASSWORD> - POSTGRES_PASSWORD
- <POSTGRES_PORT> - POSTGRES_PORT
- <POSTGRES_USER> - POSTGRES_USER
- <POSTGRES_SCHEMA> - POSTGRES_SCHEMA
``` ```
docker run -it --rm \ docker run -it --rm \
--name n8n \ --name n8n \
-p 5678:5678 \ -p 5678:5678 \
-e DB_TYPE=postgresdb \ -e DB_TYPE=postgresdb \
-e DB_POSTGRESDB_DATABASE=<POSTGRES_DATABASE> \ -e DB_POSTGRESDB_DATABASE=<POSTGRES_DATABASE> \
-e DB_POSTGRESDB_HOST=<POSTGRES_HOST> \ -e DB_POSTGRESDB_HOST=<POSTGRES_HOST> \
@ -170,39 +164,37 @@ docker run -it --rm \
-e DB_POSTGRESDB_USER=<POSTGRES_USER> \ -e DB_POSTGRESDB_USER=<POSTGRES_USER> \
-e DB_POSTGRESDB_SCHEMA=<POSTGRES_SCHEMA> \ -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 \
n8n start n8n start
``` ```
A full working setup with docker-compose can be found [here](https://github.com/n8n-io/n8n/blob/master/docker/compose/withPostgres/README.md) A full working setup with docker-compose can be found [here](https://github.com/n8n-io/n8n/blob/master/docker/compose/withPostgres/README.md)
#### Use with MySQL #### Use with MySQL
Replace the following placeholders with the actual data: Replace the following placeholders with the actual data:
- <MYSQLDB_DATABASE> - MYSQLDB_DATABASE
- <MYSQLDB_HOST> - MYSQLDB_HOST
- <MYSQLDB_PASSWORD> - MYSQLDB_PASSWORD
- <MYSQLDB_PORT> - MYSQLDB_PORT
- <MYSQLDB_USER> - MYSQLDB_USER
``` ```
docker run -it --rm \ docker run -it --rm \
--name n8n \ --name n8n \
-p 5678:5678 \ -p 5678:5678 \
-e DB_TYPE=mysqldb \ -e DB_TYPE=mysqldb \
-e DB_MYSQLDB_DATABASE=<MYSQLDB_DATABASE> \ -e DB_MYSQLDB_DATABASE=<MYSQLDB_DATABASE> \
-e DB_MYSQLDB_HOST=<MYSQLDB_HOST> \ -e DB_MYSQLDB_HOST=<MYSQLDB_HOST> \
-e DB_MYSQLDB_PORT=<MYSQLDB_PORT> \ -e DB_MYSQLDB_PORT=<MYSQLDB_PORT> \
-e DB_MYSQLDB_USER=<MYSQLDB_USER> \ -e DB_MYSQLDB_USER=<MYSQLDB_USER> \
-e DB_MYSQLDB_PASSWORD=<MYSQLDB_PASSWORD> \ -e DB_MYSQLDB_PASSWORD=<MYSQLDB_PASSWORD> \
-v ~/.n8n:/root/.n8n \ -v ~/.n8n:/root/.n8n \
n8nio/n8n \ n8nio/n8n \
n8n start n8n start
``` ```
## Passing Sensitive Data via File ## Passing Sensitive Data via File
To avoid passing sensitive information via environment variables "_FILE" may be To avoid passing sensitive information via environment variables "_FILE" may be
@ -211,16 +203,15 @@ with the given name. That makes it possible to load data easily from
Docker- and Kubernetes-Secrets. Docker- and Kubernetes-Secrets.
The following environment variables support file input: The following environment variables support file input:
- DB_MONGODB_CONNECTION_URL_FILE - DB_MONGODB_CONNECTION_URL_FILE
- DB_POSTGRESDB_DATABASE_FILE - DB_POSTGRESDB_DATABASE_FILE
- DB_POSTGRESDB_HOST_FILE - DB_POSTGRESDB_HOST_FILE
- 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 - DB_POSTGRESDB_SCHEMA_FILE
- N8N_BASIC_AUTH_PASSWORD_FILE - N8N_BASIC_AUTH_PASSWORD_FILE
- N8N_BASIC_AUTH_USER_FILE - N8N_BASIC_AUTH_USER_FILE
## Example Setup with Lets Encrypt ## Example Setup with Lets Encrypt
@ -251,11 +242,11 @@ the environment variable `TZ`.
Example to use the same timezone for both: Example to use the same timezone for both:
``` ```
docker run -it --rm \ docker run -it --rm \
--name n8n \ --name n8n \
-p 5678:5678 \ -p 5678:5678 \
-e GENERIC_TIMEZONE="Europe/Berlin" \ -e GENERIC_TIMEZONE="Europe/Berlin" \
-e TZ="Europe/Berlin" \ -e TZ="Europe/Berlin" \
n8nio/n8n n8nio/n8n
``` ```

View file

@ -19,7 +19,7 @@
<div class="header"> <div class="header">
<div class="title-text"> <div class="title-text">
<strong v-if="dataCount < this.MAX_DISPLAY_ITEMS_AUTO_ALL && dataSize < MAX_DISPLAY_DATA_SIZE"> <strong v-if="dataCount < maxDisplayItems && dataSize < MAX_DISPLAY_DATA_SIZE">
Results: {{ dataCount }} Results: {{ dataCount }}
</strong> </strong>
<strong v-else>Results: <strong v-else>Results:
@ -248,7 +248,11 @@ export default mixins(
return executionData.resultData.runData; return executionData.resultData.runData;
}, },
maxDisplayItemsOptions (): number[] { maxDisplayItemsOptions (): number[] {
return [25, 50, 100, 250, 500, 1000, this.dataCount].filter(option => option <= this.dataCount); const options = [25, 50, 100, 250, 500, 1000].filter(option => option <= this.dataCount);
if (!options.includes(this.dataCount)) {
options.push(this.dataCount);
}
return options;
}, },
node (): INodeUi | null { node (): INodeUi | null {
return this.$store.getters.activeNode; return this.$store.getters.activeNode;

View file

@ -48,6 +48,9 @@ export async function pipedriveApiRequest(this: IHookFunctions | IExecuteFunctio
query.api_token = credentials.apiToken; query.api_token = credentials.apiToken;
const options: OptionsWithUri = { const options: OptionsWithUri = {
headers: {
Accept: 'application/json',
},
method, method,
qs: query, qs: query,
uri: `https://api.pipedrive.com/v1${endpoint}`, uri: `https://api.pipedrive.com/v1${endpoint}`,
@ -93,7 +96,7 @@ export async function pipedriveApiRequest(this: IHookFunctions | IExecuteFunctio
if (error.response && error.response.body && error.response.body.error) { if (error.response && error.response.body && error.response.body.error) {
// Try to return the error prettier // Try to return the error prettier
let errorMessage = `Pipedrive error response [${error.statusCode}]: ${error.response.body.error}`; let errorMessage = `Pipedrive error response [${error.statusCode}]: ${error.response.body.error.message}`;
if (error.response.body.error_info) { if (error.response.body.error_info) {
errorMessage += ` - ${error.response.body.error_info}`; errorMessage += ` - ${error.response.body.error_info}`;
} }
@ -124,7 +127,7 @@ export async function pipedriveApiRequestAllItems(this: IHookFunctions | IExecut
if (query === undefined) { if (query === undefined) {
query = {}; query = {};
} }
query.limit = 500; query.limit = 100;
query.start = 0; query.start = 0;
const returnData: IDataObject[] = []; const returnData: IDataObject[] = [];
@ -133,7 +136,12 @@ export async function pipedriveApiRequestAllItems(this: IHookFunctions | IExecut
do { do {
responseData = await pipedriveApiRequest.call(this, method, endpoint, body, query); responseData = await pipedriveApiRequest.call(this, method, endpoint, body, query);
returnData.push.apply(returnData, responseData.data); // the search path returns data diferently
if (responseData.data.items) {
returnData.push.apply(returnData, responseData.data.items);
} else {
returnData.push.apply(returnData, responseData.data);
}
query.start = responseData.additionalData.pagination.next_start; query.start = responseData.additionalData.pagination.next_start;
} while ( } while (

View file

@ -25,7 +25,6 @@ interface CustomProperty {
value: string; value: string;
} }
/** /**
* Add the additional fields to the body * Add the additional fields to the body
* *
@ -362,6 +361,11 @@ export class Pipedrive implements INodeType {
value: 'getAll', value: 'getAll',
description: 'Get data of all persons', description: 'Get data of all persons',
}, },
{
name: 'Search',
value: 'search',
description: 'Search all persons',
},
{ {
name: 'Update', name: 'Update',
value: 'update', value: 'update',
@ -2021,6 +2025,7 @@ export class Pipedrive implements INodeType {
show: { show: {
operation: [ operation: [
'getAll', 'getAll',
'search',
], ],
}, },
}, },
@ -2035,6 +2040,7 @@ export class Pipedrive implements INodeType {
show: { show: {
operation: [ operation: [
'getAll', 'getAll',
'search',
], ],
returnAll: [ returnAll: [
false, false,
@ -2088,6 +2094,81 @@ export class Pipedrive implements INodeType {
}, },
], ],
}, },
// ----------------------------------
// person:search
// ----------------------------------
{
displayName: 'Term',
name: 'term',
type: 'string',
displayOptions: {
show: {
operation: [
'search',
],
resource: [
'person',
],
},
},
default: '',
description: 'The search term to look for. Minimum 2 characters (or 1 if using exact_match).',
},
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
displayOptions: {
show: {
operation: [
'search',
],
resource: [
'person',
],
},
},
default: {},
options: [
{
displayName: 'Exact Match',
name: 'exactMatch',
type: 'boolean',
default: false,
description: 'When enabled, only full exact matches against the given term are returned. It is not case sensitive.',
},
{
displayName: 'Fields',
name: 'fields',
type: 'string',
default: '',
description: 'A comma-separated string array. The fields to perform the search from. Defaults to all of them.',
},
{
displayName: 'Include Fields',
name: 'includeFields',
type: 'string',
default: '',
description: 'Supports including optional fields in the results which are not provided by default.',
},
{
displayName: 'Organization ID',
name: 'organizationId',
type: 'string',
default: '',
description: 'Will filter Deals by the provided Organization ID.',
},
{
displayName: 'RAW Data',
name: 'rawData',
type: 'boolean',
default: false,
description: `Returns the data exactly in the way it got received from the API.`,
},
],
},
], ],
}; };
@ -2526,6 +2607,39 @@ export class Pipedrive implements INodeType {
endpoint = `/persons`; endpoint = `/persons`;
} else if (operation === 'search') {
// ----------------------------------
// persons:search
// ----------------------------------
requestMethod = 'GET';
qs.term = this.getNodeParameter('term', i) as string;
returnAll = this.getNodeParameter('returnAll', i) as boolean;
if (returnAll === false) {
qs.limit = this.getNodeParameter('limit', i) as number;
}
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
if (additionalFields.fields) {
qs.fields = additionalFields.fields as string;
}
if (additionalFields.exactMatch) {
qs.exact_match = additionalFields.exactMatch as boolean;
}
if (additionalFields.organizationId) {
qs.organization_id = parseInt(additionalFields.organizationId as string, 10);
}
if (additionalFields.includeFields) {
qs.include_fields = additionalFields.includeFields as string;
}
endpoint = `/persons/search`;
} else if (operation === 'update') { } else if (operation === 'update') {
// ---------------------------------- // ----------------------------------
// person:update // person:update
@ -2562,7 +2676,9 @@ export class Pipedrive implements INodeType {
let responseData; let responseData;
if (returnAll === true) { if (returnAll === true) {
responseData = await pipedriveApiRequestAllItems.call(this, requestMethod, endpoint, body, qs); responseData = await pipedriveApiRequestAllItems.call(this, requestMethod, endpoint, body, qs);
} else { } else {
if (customProperties !== undefined) { if (customProperties !== undefined) {
@ -2597,6 +2713,19 @@ export class Pipedrive implements INodeType {
responseData.data = []; responseData.data = [];
} }
if (operation === 'search' && responseData.data && responseData.data.items) {
responseData.data = responseData.data.items;
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
if (additionalFields.rawData !== true) {
responseData.data = responseData.data.map((item: { result_score: number, item: object }) => {
return {
result_score: item.result_score,
...item.item,
};
});
}
}
if (Array.isArray(responseData.data)) { if (Array.isArray(responseData.data)) {
returnData.push.apply(returnData, responseData.data as IDataObject[]); returnData.push.apply(returnData, responseData.data as IDataObject[]);
} else { } else {