mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
sync upstream
This commit is contained in:
commit
c1edcc9e56
8
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
8
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Feature request
|
||||
url: https://community.n8n.io
|
||||
about: Suggest an idea for this project
|
||||
- name: Question / Problem
|
||||
url: https://community.n8n.io
|
||||
about: Questions and problems with n8n
|
15
.github/ISSUE_TEMPLATE/feature_request.md
vendored
15
.github/ISSUE_TEMPLATE/feature_request.md
vendored
|
@ -1,15 +0,0 @@
|
|||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Please do not create a GitHub issue for feature requests**
|
||||
|
||||
Post all of them to the forum:
|
||||
[https://community.n8n.io](https://community.n8n.io)
|
||||
|
||||
They get all collected there and people can upvote existing requests. That makes it then easier to know how to prioritize them. Thanks!
|
15
.github/ISSUE_TEMPLATE/question---problem.md
vendored
15
.github/ISSUE_TEMPLATE/question---problem.md
vendored
|
@ -1,15 +0,0 @@
|
|||
---
|
||||
name: Question / Problem
|
||||
about: Questions and problems with n8n
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Please do not create a GitHub issue for questions or problems**
|
||||
|
||||
Post all of them to the forum:
|
||||
[https://community.n8n.io](https://community.n8n.io)
|
||||
|
||||
Check there first if it got answered already before and if not create a new topic.
|
|
@ -38,7 +38,7 @@ The most important directories:
|
|||
- [/packages/editor-ui](/packages/editor-ui) - Vue frontend workflow editor
|
||||
- [/packages/node-dev](/packages/node-dev) - Simple CLI to create new n8n-nodes
|
||||
- [/packages/nodes-base](/packages/nodes-base) - Base n8n nodes
|
||||
- [/packages/worflow](/packages/worflow) - Workflow code with interfaces which
|
||||
- [/packages/workflow](/packages/workflow) - Workflow code with interfaces which
|
||||
get used by front- & backend
|
||||
|
||||
|
||||
|
|
|
@ -123,7 +123,7 @@ Some third-party services have their own libraries on npm which make it a little
|
|||
const response = await this.helpers.request(options);
|
||||
```
|
||||
|
||||
That is simply using the npm package `request-promise-native` which is the basic npm `request` module but with promises.
|
||||
That is simply using the npm package [`request-promise-native`](https://github.com/request/request-promise-native) which is the basic npm `request` module but with promises. For a full set of `options` consider looking at [the underlying `request` options documentation](https://github.com/request/request#requestoptions-callback).
|
||||
|
||||
|
||||
### Reuse parameter names
|
||||
|
|
|
@ -5,7 +5,8 @@ import {
|
|||
} from "n8n-core";
|
||||
import { Command, flags } from '@oclif/command';
|
||||
const open = require('open');
|
||||
import { promisify } from "util";
|
||||
import { promisify } from 'util';
|
||||
import { dirname } from 'path';
|
||||
|
||||
import * as config from '../config';
|
||||
import {
|
||||
|
@ -21,6 +22,10 @@ import {
|
|||
|
||||
const tunnel = promisify(localtunnel);
|
||||
|
||||
// // Add support for internationalization
|
||||
// const fullIcuPath = require.resolve('full-icu');
|
||||
// process.env.NODE_ICU_DATA = dirname(fullIcuPath);
|
||||
|
||||
let activeWorkflowRunner: ActiveWorkflowRunner.ActiveWorkflowRunner | undefined;
|
||||
let processExistCode = 0;
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "n8n",
|
||||
"version": "0.35.0",
|
||||
"version": "0.36.1",
|
||||
"description": "n8n Workflow Automation Tool",
|
||||
"license": "SEE LICENSE IN LICENSE.md",
|
||||
"homepage": "https://n8n.io",
|
||||
|
@ -59,6 +59,7 @@
|
|||
"@types/express": "^4.16.1",
|
||||
"@types/jest": "^24.0.18",
|
||||
"@types/localtunnel": "^1.9.0",
|
||||
"@types/lodash.get": "^4.4.2",
|
||||
"@types/node": "^10.10.1",
|
||||
"@types/open": "^6.1.0",
|
||||
"@types/parseurl": "^1.3.1",
|
||||
|
@ -71,9 +72,9 @@
|
|||
"typescript": "~3.5.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/jsonwebtoken": "^8.3.4",
|
||||
"@oclif/command": "^1.5.18",
|
||||
"@oclif/errors": "^1.2.2",
|
||||
"@types/jsonwebtoken": "^8.3.4",
|
||||
"basic-auth": "^2.0.1",
|
||||
"body-parser": "^1.18.3",
|
||||
"compression": "^1.7.4",
|
||||
|
@ -88,11 +89,12 @@
|
|||
"jsonwebtoken": "^8.5.1",
|
||||
"jwks-rsa": "^1.6.0",
|
||||
"localtunnel": "^1.9.1",
|
||||
"lodash.get": "^4.4.2",
|
||||
"mongodb": "^3.2.3",
|
||||
"n8n-core": "~0.15.0",
|
||||
"n8n-editor-ui": "~0.25.0",
|
||||
"n8n-nodes-base": "~0.30.0",
|
||||
"n8n-workflow": "~0.15.0",
|
||||
"n8n-core": "~0.16.0",
|
||||
"n8n-editor-ui": "~0.26.0",
|
||||
"n8n-nodes-base": "~0.31.0",
|
||||
"n8n-workflow": "~0.16.0",
|
||||
"open": "^6.1.0",
|
||||
"pg": "^7.11.0",
|
||||
"request-promise-native": "^1.0.7",
|
||||
|
|
|
@ -28,6 +28,7 @@ import {
|
|||
IRunExecutionData,
|
||||
ITaskData,
|
||||
IWebhookData,
|
||||
IWebhookResponseData,
|
||||
IWorkflowExecuteAdditionalData,
|
||||
NodeHelpers,
|
||||
Workflow,
|
||||
|
@ -76,6 +77,8 @@ export function getWorkflowWebhooks(workflow: Workflow, additionalData: IWorkflo
|
|||
let parentNodes: string[] | undefined;
|
||||
if (destinationNode !== undefined) {
|
||||
parentNodes = workflow.getParentNodes(destinationNode);
|
||||
// Also add the destination node in case it itself is a webhook node
|
||||
parentNodes.push(destinationNode);
|
||||
}
|
||||
|
||||
for (const node of Object.values(workflow.nodes)) {
|
||||
|
@ -122,7 +125,7 @@ export function getWorkflowWebhooks(workflow: Workflow, additionalData: IWorkflo
|
|||
// If the mode is not known we error. Is probably best like that instead of using
|
||||
// the default that people know as early as possible (probably already testing phase)
|
||||
// that something does not resolve properly.
|
||||
const errorMessage = `The response mode ${responseMode} is not valid!.`;
|
||||
const errorMessage = `The response mode ${responseMode} is not valid!`;
|
||||
responseCallback(new Error(errorMessage), {});
|
||||
throw new ResponseHelper.ResponseError(errorMessage, 500, 500);
|
||||
}
|
||||
|
@ -136,12 +139,41 @@ export function getWorkflowWebhooks(workflow: Workflow, additionalData: IWorkflo
|
|||
additionalData.httpResponse = res;
|
||||
|
||||
let didSendResponse = false;
|
||||
let runExecutionDataMerge = {};
|
||||
try {
|
||||
// Run the webhook function to see what should be returned and if
|
||||
// the workflow should be executed or not
|
||||
const webhookResultData = await webhookData.workflow.runWebhook(webhookData, workflowStartNode, additionalData, NodeExecuteFunctions, executionMode);
|
||||
let webhookResultData: IWebhookResponseData;
|
||||
|
||||
if (webhookResultData.noWebhookResponse === true) {
|
||||
try {
|
||||
webhookResultData = await webhookData.workflow.runWebhook(webhookData, workflowStartNode, additionalData, NodeExecuteFunctions, executionMode);
|
||||
} catch (e) {
|
||||
// Send error response to webhook caller
|
||||
const errorMessage = 'Workflow Webhook Error: Workflow could not be started!';
|
||||
responseCallback(new Error(errorMessage), {});
|
||||
didSendResponse = true;
|
||||
|
||||
// Add error to execution data that it can be logged and send to Editor-UI
|
||||
runExecutionDataMerge = {
|
||||
resultData: {
|
||||
runData: {},
|
||||
lastNodeExecuted: workflowStartNode.name,
|
||||
error: {
|
||||
message: e.message,
|
||||
stack: e.stack,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
webhookResultData = {
|
||||
noWebhookResponse: true,
|
||||
// Add empty data that it at least tries to "execute" the webhook
|
||||
// which then so gets the chance to throw the error.
|
||||
workflowData: [[{json: {}}]],
|
||||
};
|
||||
}
|
||||
|
||||
if (webhookResultData.noWebhookResponse === true && didSendResponse === false) {
|
||||
// The response got already send
|
||||
responseCallback(null, {
|
||||
noWebhookResponse: true,
|
||||
|
@ -153,18 +185,24 @@ export function getWorkflowWebhooks(workflow: Workflow, additionalData: IWorkflo
|
|||
// Workflow should not run
|
||||
if (webhookResultData.webhookResponse !== undefined) {
|
||||
// Data to respond with is given
|
||||
responseCallback(null, {
|
||||
data: webhookResultData.webhookResponse,
|
||||
responseCode,
|
||||
});
|
||||
if (didSendResponse === false) {
|
||||
responseCallback(null, {
|
||||
data: webhookResultData.webhookResponse,
|
||||
responseCode,
|
||||
});
|
||||
didSendResponse = true;
|
||||
}
|
||||
} else {
|
||||
// Send default response
|
||||
responseCallback(null, {
|
||||
data: {
|
||||
message: 'Webhook call got received.',
|
||||
},
|
||||
responseCode,
|
||||
});
|
||||
if (didSendResponse === false) {
|
||||
responseCallback(null, {
|
||||
data: {
|
||||
message: 'Webhook call got received.',
|
||||
},
|
||||
responseCode,
|
||||
});
|
||||
didSendResponse = true;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -215,6 +253,11 @@ export function getWorkflowWebhooks(workflow: Workflow, additionalData: IWorkflo
|
|||
},
|
||||
};
|
||||
|
||||
if (Object.keys(runExecutionDataMerge).length !== 0) {
|
||||
// If data to merge got defined add it to the execution data
|
||||
Object.assign(runExecutionData, runExecutionDataMerge);
|
||||
}
|
||||
|
||||
const runData: IWorkflowExecutionDataProcess = {
|
||||
credentials,
|
||||
executionMode,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "n8n-core",
|
||||
"version": "0.15.0",
|
||||
"version": "0.16.0",
|
||||
"description": "Core functionality of n8n",
|
||||
"license": "SEE LICENSE IN LICENSE.md",
|
||||
"homepage": "https://n8n.io",
|
||||
|
@ -42,7 +42,7 @@
|
|||
"crypto-js": "^3.1.9-1",
|
||||
"lodash.get": "^4.4.2",
|
||||
"mmmagic": "^0.5.2",
|
||||
"n8n-workflow": "~0.15.0",
|
||||
"n8n-workflow": "~0.16.0",
|
||||
"request-promise-native": "^1.0.7"
|
||||
},
|
||||
"jest": {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "n8n-editor-ui",
|
||||
"version": "0.25.0",
|
||||
"version": "0.26.0",
|
||||
"description": "Workflow Editor UI for n8n",
|
||||
"license": "SEE LICENSE IN LICENSE.md",
|
||||
"homepage": "https://n8n.io",
|
||||
|
@ -59,7 +59,7 @@
|
|||
"lodash.debounce": "^4.0.8",
|
||||
"lodash.get": "^4.4.2",
|
||||
"lodash.set": "^4.3.2",
|
||||
"n8n-workflow": "~0.15.0",
|
||||
"n8n-workflow": "~0.16.0",
|
||||
"node-sass": "^4.12.0",
|
||||
"prismjs": "^1.17.1",
|
||||
"quill": "^2.0.0-dev.3",
|
||||
|
@ -71,7 +71,7 @@
|
|||
"typescript": "~3.5.2",
|
||||
"vue": "^2.6.9",
|
||||
"vue-cli-plugin-webpack-bundle-analyzer": "^1.3.0",
|
||||
"vue-json-pretty": "^1.4.1",
|
||||
"vue-json-tree": "^0.4.1",
|
||||
"vue-prism-editor": "^0.3.0",
|
||||
"vue-router": "^3.0.6",
|
||||
"vue-template-compiler": "^2.5.17",
|
||||
|
|
|
@ -22,8 +22,12 @@
|
|||
</el-tooltip>
|
||||
</div>
|
||||
<el-row v-for="parameter in credentialTypeData.properties" :key="parameter.name" class="parameter-wrapper">
|
||||
<el-col :span="6">
|
||||
<el-col :span="6" class="parameter-name">
|
||||
{{parameter.displayName}}:
|
||||
<el-tooltip placement="top" class="parameter-info" v-if="parameter.description" effect="light">
|
||||
<div slot="content" v-html="parameter.description"></div>
|
||||
<font-awesome-icon icon="question-circle"/>
|
||||
</el-tooltip>
|
||||
</el-col>
|
||||
<el-col :span="18">
|
||||
<parameter-input :parameter="parameter" :value="propertyValue[parameter.name]" :path="parameter.name" :isCredential="true" @valueChanged="valueChanged" />
|
||||
|
@ -299,6 +303,20 @@ export default mixins(
|
|||
|
||||
.parameter-wrapper {
|
||||
line-height: 3em;
|
||||
|
||||
.parameter-name {
|
||||
position: relative;
|
||||
|
||||
&:hover {
|
||||
.parameter-info {
|
||||
display: inline;
|
||||
}
|
||||
}
|
||||
|
||||
.parameter-info {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.credentials-info {
|
||||
|
|
|
@ -326,11 +326,20 @@ export default mixins(
|
|||
// and the error is not displayed on the node in the workflow
|
||||
const validOptions = this.parameterOptions!.map((options: INodePropertyOptions) => options.value);
|
||||
|
||||
if (this.displayValue === null || !validOptions.includes(this.displayValue as string)) {
|
||||
if (issues.parameters === undefined) {
|
||||
issues.parameters = {};
|
||||
const checkValues: string[] = [];
|
||||
if (Array.isArray(this.displayValue)) {
|
||||
checkValues.push.apply(checkValues, this.displayValue);
|
||||
} else {
|
||||
checkValues.push(this.displayValue as string);
|
||||
}
|
||||
|
||||
for (const checkValue of checkValues) {
|
||||
if (checkValue === null || !validOptions.includes(checkValue)) {
|
||||
if (issues.parameters === undefined) {
|
||||
issues.parameters = {};
|
||||
}
|
||||
issues.parameters[this.parameter.name] = [`The value "${checkValue}" is not supported!`];
|
||||
}
|
||||
issues.parameters[this.parameter.name] = [`The value "${this.displayValue}" is not supported!`];
|
||||
}
|
||||
} else if (this.remoteParameterOptionsLoadingIssues !== null) {
|
||||
if (issues.parameters === undefined) {
|
||||
|
|
|
@ -95,11 +95,12 @@
|
|||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<vue-json-pretty
|
||||
<json-tree
|
||||
v-else-if="displayMode === 'JSON'"
|
||||
:data="jsonData"
|
||||
:level="10"
|
||||
class="json-data"
|
||||
:data="jsonData">
|
||||
</vue-json-pretty>
|
||||
/>
|
||||
</div>
|
||||
<div v-else-if="displayMode === 'Binary'">
|
||||
<div v-if="binaryData.length === 0" class="no-data">
|
||||
|
@ -162,7 +163,7 @@
|
|||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
// @ts-ignore
|
||||
import VueJsonPretty from 'vue-json-pretty';
|
||||
import JsonTree from 'vue-json-tree';
|
||||
import {
|
||||
GenericValue,
|
||||
IBinaryData,
|
||||
|
@ -200,7 +201,7 @@ export default mixins(
|
|||
name: 'RunData',
|
||||
components: {
|
||||
BinaryDataDisplay,
|
||||
VueJsonPretty,
|
||||
JsonTree,
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
|
@ -572,9 +573,16 @@ export default mixins(
|
|||
}
|
||||
|
||||
.json-data {
|
||||
overflow-x: hidden;
|
||||
white-space: initial;
|
||||
word-wrap: break-word;
|
||||
.json-tree {
|
||||
color: $--custom-input-font;
|
||||
|
||||
.json-tree-value-number {
|
||||
color: #b03030;
|
||||
}
|
||||
.json-tree-value-string {
|
||||
color: #8aab1a;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.error-display,
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
{
|
||||
"linterOptions": {
|
||||
"exclude": [
|
||||
"node_modules/**/*"
|
||||
"node_modules/**/*",
|
||||
"../../node_modules/**/*"
|
||||
]
|
||||
},
|
||||
"defaultSeverity": "error",
|
||||
|
|
|
@ -135,6 +135,15 @@ Method get called when the workflow gets executed
|
|||
- `execute`: Executed once no matter how many items
|
||||
- `executeSingle`: Executed once for every item
|
||||
|
||||
By default always `execute` should be used especially when creating a
|
||||
third-party integration. The reason for that is that it is way more flexible
|
||||
and allows to, for example, return a different amount of items than it received
|
||||
as input. This is very important when a node should query data like return
|
||||
all users. In that case, does the node normally just receive one input-item
|
||||
but returns as many as users exist. So in doubt always `execute` should be
|
||||
used!
|
||||
|
||||
|
||||
**Trigger node**
|
||||
|
||||
Method gets called once when the workflow gets activated. It can then trigger
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
"build": "tsc",
|
||||
"postpack": "rm -f oclif.manifest.json",
|
||||
"prepack": "echo \"Building project...\" && rm -rf dist && tsc -b && oclif-dev manifest",
|
||||
"test": "jest",
|
||||
"tslint": "tslint -p tsconfig.json -c tslint.json",
|
||||
"watch": "tsc --watch"
|
||||
},
|
||||
|
@ -65,23 +64,5 @@
|
|||
"request": "^2.88.0",
|
||||
"tmp-promise": "^2.0.2",
|
||||
"typescript": "~3.5.2"
|
||||
},
|
||||
"jest": {
|
||||
"transform": {
|
||||
"^.+\\.tsx?$": "ts-jest"
|
||||
},
|
||||
"testURL": "http://localhost/",
|
||||
"testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$",
|
||||
"testPathIgnorePatterns": [
|
||||
"/dist/",
|
||||
"/node_modules/"
|
||||
],
|
||||
"moduleFileExtensions": [
|
||||
"ts",
|
||||
"tsx",
|
||||
"js",
|
||||
"json",
|
||||
"node"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
describe('Placeholder', () => {
|
||||
|
||||
test('example', () => {
|
||||
expect(1 + 1).toEqual(2);
|
||||
});
|
||||
|
||||
});
|
|
@ -4,8 +4,7 @@
|
|||
"es2017"
|
||||
],
|
||||
"types": [
|
||||
"node",
|
||||
"jest"
|
||||
"node"
|
||||
],
|
||||
"module": "commonjs",
|
||||
"esModuleInterop": true,
|
||||
|
|
39
packages/nodes-base/credentials/FileMaker.credentials.ts
Normal file
39
packages/nodes-base/credentials/FileMaker.credentials.ts
Normal file
|
@ -0,0 +1,39 @@
|
|||
import {
|
||||
ICredentialType,
|
||||
NodePropertyTypes,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
|
||||
export class FileMaker implements ICredentialType {
|
||||
name = 'fileMaker';
|
||||
displayName = 'FileMaker API';
|
||||
properties = [
|
||||
{
|
||||
displayName: 'Host',
|
||||
name: 'host',
|
||||
type: 'string' as NodePropertyTypes,
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Database',
|
||||
name: 'db',
|
||||
type: 'string' as NodePropertyTypes,
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Login',
|
||||
name: 'login',
|
||||
type: 'string' as NodePropertyTypes,
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Password',
|
||||
name: 'password',
|
||||
type: 'string' as NodePropertyTypes,
|
||||
typeOptions: {
|
||||
password: true,
|
||||
},
|
||||
default: '',
|
||||
},
|
||||
];
|
||||
}
|
|
@ -13,6 +13,7 @@ export class GoogleApi implements ICredentialType {
|
|||
name: 'email',
|
||||
type: 'string' as NodePropertyTypes,
|
||||
default: '',
|
||||
description: 'The Google Service account similar to user-808@project.iam.gserviceaccount.com.<br />See the <a href="https://github.com/jovotech/learn-jovo/blob/master/tutorials/google-spreadsheet-private-cms/README.md#google-api-console">tutorial</a> on how to create one.',
|
||||
|
||||
},
|
||||
{
|
||||
|
@ -21,6 +22,7 @@ export class GoogleApi implements ICredentialType {
|
|||
lines: 5,
|
||||
type: 'string' as NodePropertyTypes,
|
||||
default: '',
|
||||
description: 'Use the multiline editor. Make sure there are exactly 3 lines.<br />-----BEGIN PRIVATE KEY-----<br />KEY IN A SINGLE LINE<br />-----END PRIVATE KEY-----',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
|
18
packages/nodes-base/credentials/IntercomApi.credentials.ts
Normal file
18
packages/nodes-base/credentials/IntercomApi.credentials.ts
Normal file
|
@ -0,0 +1,18 @@
|
|||
import {
|
||||
ICredentialType,
|
||||
NodePropertyTypes,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
|
||||
export class IntercomApi implements ICredentialType {
|
||||
name = 'intercomApi';
|
||||
displayName = 'Intercom API';
|
||||
properties = [
|
||||
{
|
||||
displayName: 'API Key',
|
||||
name: 'apiKey',
|
||||
type: 'string' as NodePropertyTypes,
|
||||
default: '',
|
||||
},
|
||||
];
|
||||
}
|
17
packages/nodes-base/credentials/MailchimpApi.credentials.ts
Normal file
17
packages/nodes-base/credentials/MailchimpApi.credentials.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
import {
|
||||
ICredentialType,
|
||||
NodePropertyTypes,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
export class MailchimpApi implements ICredentialType {
|
||||
name = 'mailchimpApi';
|
||||
displayName = 'Mailchimp API';
|
||||
properties = [
|
||||
{
|
||||
displayName: 'API Key',
|
||||
name: 'apiKey',
|
||||
type: 'string' as NodePropertyTypes,
|
||||
default: '',
|
||||
},
|
||||
];
|
||||
}
|
|
@ -12,15 +12,14 @@ export class NextCloudApi implements ICredentialType {
|
|||
displayName: 'Web DAV URL',
|
||||
name: 'webDavUrl',
|
||||
type: 'string' as NodePropertyTypes,
|
||||
placeholder: 'https://nextcloud.example.com/remote.php/webdav/',
|
||||
default: '',
|
||||
|
||||
},
|
||||
{
|
||||
displayName: 'User',
|
||||
name: 'user',
|
||||
type: 'string' as NodePropertyTypes,
|
||||
default: '',
|
||||
|
||||
},
|
||||
{
|
||||
displayName: 'Password',
|
||||
|
|
|
@ -5,7 +5,7 @@ import {
|
|||
|
||||
|
||||
export class PayPalApi implements ICredentialType {
|
||||
name = 'paypalApi';
|
||||
name = 'payPalApi';
|
||||
displayName = 'PayPal API';
|
||||
properties = [
|
||||
{
|
|
@ -13,6 +13,7 @@ export class TelegramApi implements ICredentialType {
|
|||
name: 'accessToken',
|
||||
type: 'string' as NodePropertyTypes,
|
||||
default: '',
|
||||
description: 'Chat with the <a href="https://telegram.me/botfather">bot father</a> to obtain the access token.',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
|
|
@ -53,6 +53,14 @@ export class AsanaTrigger implements INodeType {
|
|||
required: true,
|
||||
description: 'The resource ID to subscribe to. The resource can be a task or project.',
|
||||
},
|
||||
{
|
||||
displayName: 'Workspace',
|
||||
name: 'workspace',
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: false,
|
||||
description: 'The workspace ID the resource is registered under. This is only required if you want to allow overriding existing webhooks.',
|
||||
},
|
||||
],
|
||||
|
||||
};
|
||||
|
@ -93,6 +101,8 @@ export class AsanaTrigger implements INodeType {
|
|||
|
||||
const resource = this.getNodeParameter('resource') as string;
|
||||
|
||||
const workspace = this.getNodeParameter('workspace') as string;
|
||||
|
||||
const endpoint = `webhooks`;
|
||||
|
||||
const body = {
|
||||
|
@ -100,7 +110,22 @@ export class AsanaTrigger implements INodeType {
|
|||
target: webhookUrl,
|
||||
};
|
||||
|
||||
const responseData = await asanaApiRequest.call(this, 'POST', endpoint, body);
|
||||
let responseData;
|
||||
try {
|
||||
responseData = await asanaApiRequest.call(this, 'POST', endpoint, body);
|
||||
} catch(error) {
|
||||
// delete webhook if it already exists
|
||||
if (error.statusCode === 403) {
|
||||
const webhookData = await asanaApiRequest.call(this, 'GET', endpoint, {}, { workspace });
|
||||
const webhook = webhookData.data.find((webhook: any) => { // tslint:disable-line:no-any
|
||||
return webhook.target === webhookUrl && webhook.resource.gid === resource;
|
||||
});
|
||||
await asanaApiRequest.call(this, 'DELETE', `${endpoint}/${webhook.gid}`, {});
|
||||
responseData = await asanaApiRequest.call(this, 'POST', endpoint, body);
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
if (responseData.data === undefined || responseData.data.id === undefined) {
|
||||
// Required data is missing so was not successful
|
||||
|
|
909
packages/nodes-base/nodes/FileMaker/FileMaker.node.ts
Normal file
909
packages/nodes-base/nodes/FileMaker/FileMaker.node.ts
Normal file
|
@ -0,0 +1,909 @@
|
|||
import {IExecuteFunctions} from 'n8n-core';
|
||||
import {
|
||||
ILoadOptionsFunctions,
|
||||
INodeExecutionData, INodePropertyOptions,
|
||||
INodeType,
|
||||
INodeTypeDescription,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
|
||||
import {OptionsWithUri} from 'request';
|
||||
import {
|
||||
layoutsApiRequest,
|
||||
getFields,
|
||||
getPortals,
|
||||
getScripts,
|
||||
getToken,
|
||||
parseSort,
|
||||
parsePortals,
|
||||
parseQuery,
|
||||
parseScripts,
|
||||
parseFields,
|
||||
logout
|
||||
} from "./GenericFunctions";
|
||||
|
||||
export class FileMaker implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
displayName: 'FileMaker',
|
||||
name: 'filemaker',
|
||||
icon: 'file:filemaker.png',
|
||||
group: ['input'],
|
||||
version: 1,
|
||||
description: 'Retrieve data from FileMaker data API.',
|
||||
defaults: {
|
||||
name: 'FileMaker',
|
||||
color: '#665533',
|
||||
},
|
||||
inputs: ['main'],
|
||||
outputs: ['main'],
|
||||
credentials: [
|
||||
{
|
||||
name: 'fileMaker',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
properties: [
|
||||
{
|
||||
displayName: 'Action',
|
||||
name: 'action',
|
||||
type: 'options',
|
||||
default: 'record',
|
||||
options: [
|
||||
/*{
|
||||
name: 'Login',
|
||||
value: 'login',
|
||||
},
|
||||
{
|
||||
name: 'Logout',
|
||||
value: 'logout',
|
||||
},*/
|
||||
{
|
||||
name: 'Find Records',
|
||||
value: 'find',
|
||||
},
|
||||
{
|
||||
name: 'Get Records',
|
||||
value: 'records',
|
||||
},
|
||||
{
|
||||
name: 'Get Records By Id',
|
||||
value: 'record',
|
||||
},
|
||||
{
|
||||
name: 'Perform Script',
|
||||
value: 'performscript',
|
||||
},
|
||||
{
|
||||
name: 'Create Record',
|
||||
value: 'create',
|
||||
},
|
||||
{
|
||||
name: 'Edit Record',
|
||||
value: 'edit',
|
||||
},
|
||||
{
|
||||
name: 'Duplicate Record',
|
||||
value: 'duplicate',
|
||||
},
|
||||
{
|
||||
name: 'Delete Record',
|
||||
value: 'delete',
|
||||
},
|
||||
],
|
||||
description: 'Action to perform.',
|
||||
},
|
||||
|
||||
// ----------------------------------
|
||||
// shared
|
||||
// ----------------------------------
|
||||
{
|
||||
displayName: 'Layout',
|
||||
name: 'layout',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getLayouts',
|
||||
},
|
||||
options: [],
|
||||
default: '',
|
||||
required: true,
|
||||
displayOptions: {},
|
||||
placeholder: 'Layout Name',
|
||||
description: 'FileMaker Layout Name.',
|
||||
},
|
||||
{
|
||||
displayName: 'Record Id',
|
||||
name: 'recid',
|
||||
type: 'number',
|
||||
default: '',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
action: [
|
||||
'record',
|
||||
'edit',
|
||||
'delete',
|
||||
'duplicate',
|
||||
],
|
||||
},
|
||||
},
|
||||
placeholder: 'Record ID',
|
||||
description: 'Internal Record ID returned by get (recordid)',
|
||||
},
|
||||
{
|
||||
displayName: 'Offset',
|
||||
name: 'offset',
|
||||
placeholder: '0',
|
||||
description: 'The record number of the first record in the range of records.',
|
||||
type: 'number',
|
||||
default: '1',
|
||||
displayOptions: {
|
||||
show: {
|
||||
action: [
|
||||
'find',
|
||||
'records',
|
||||
],
|
||||
},
|
||||
}
|
||||
},
|
||||
{
|
||||
displayName: 'Limit',
|
||||
name: 'limit',
|
||||
placeholder: '100',
|
||||
description: 'The maximum number of records that should be returned. If not specified, the default value is 100.',
|
||||
type: 'number',
|
||||
default: '100',
|
||||
displayOptions: {
|
||||
show: {
|
||||
action: [
|
||||
'find',
|
||||
'records',
|
||||
],
|
||||
},
|
||||
}
|
||||
},
|
||||
{
|
||||
displayName: 'Get portals',
|
||||
name: 'getPortals',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: 'Should we get portal data as well ?',
|
||||
displayOptions: {
|
||||
show: {
|
||||
action: [
|
||||
'record',
|
||||
'records',
|
||||
'find',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Portals',
|
||||
name: 'portals',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
multipleValues: true,
|
||||
multipleValueButtonText: 'Add portal',
|
||||
loadOptionsMethod: 'getPortals',
|
||||
},
|
||||
options: [],
|
||||
default: [],
|
||||
displayOptions: {
|
||||
show: {
|
||||
action: [
|
||||
'record',
|
||||
'records',
|
||||
'find',
|
||||
],
|
||||
getPortals: [
|
||||
true,
|
||||
],
|
||||
},
|
||||
},
|
||||
placeholder: 'Portals',
|
||||
description: 'The portal result set to return. Use the portal object name or portal<br />table name. If this parameter is omitted, the API will return all<br />portal objects and records in the layout. For best performance<br />, pass the portal object name or portal table name.',
|
||||
},
|
||||
// ----------------------------------
|
||||
// find/records
|
||||
// ----------------------------------
|
||||
{
|
||||
displayName: 'Response Layout',
|
||||
name: 'responseLayout',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getResponseLayouts',
|
||||
},
|
||||
options: [],
|
||||
default: '',
|
||||
required: false,
|
||||
displayOptions: {
|
||||
show: {
|
||||
action: [
|
||||
'find'
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Queries',
|
||||
name: 'queries',
|
||||
placeholder: 'Add query',
|
||||
type: 'fixedCollection',
|
||||
typeOptions: {
|
||||
multipleValues: true,
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
action: [
|
||||
'find',
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'Queries ',
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
name: 'query',
|
||||
displayName: 'Query',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Fields',
|
||||
name: 'fields',
|
||||
placeholder: 'Add field',
|
||||
type: 'fixedCollection',
|
||||
default: {},
|
||||
typeOptions: {
|
||||
multipleValues: true,
|
||||
},
|
||||
options: [{
|
||||
name: 'field',
|
||||
displayName: 'Field',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Field',
|
||||
name: 'name',
|
||||
type: 'options',
|
||||
default: '',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getFields',
|
||||
},
|
||||
options: [],
|
||||
description: 'Search Field',
|
||||
},
|
||||
{
|
||||
displayName: 'Value',
|
||||
name: 'value',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Value to search',
|
||||
},
|
||||
]
|
||||
}
|
||||
],
|
||||
description: 'Field Name',
|
||||
},
|
||||
{
|
||||
displayName: 'Omit',
|
||||
name: 'omit',
|
||||
type: 'boolean',
|
||||
default: false
|
||||
},
|
||||
]
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Sort data?',
|
||||
name: 'setSort',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: 'Should we sort data ?',
|
||||
displayOptions: {
|
||||
show: {
|
||||
action: [
|
||||
'find',
|
||||
'record',
|
||||
'records',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Sort',
|
||||
name: 'sortParametersUi',
|
||||
placeholder: 'Add Sort Rules',
|
||||
type: 'fixedCollection',
|
||||
typeOptions: {
|
||||
multipleValues: true,
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
setSort: [
|
||||
true,
|
||||
],
|
||||
action: [
|
||||
'find',
|
||||
'records',
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'Sort rules',
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
name: 'rules',
|
||||
displayName: 'Rules',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Field',
|
||||
name: 'name',
|
||||
type: 'options',
|
||||
default: '',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getFields',
|
||||
},
|
||||
options: [],
|
||||
description: 'Field Name.',
|
||||
},
|
||||
{
|
||||
displayName: 'Order',
|
||||
name: 'value',
|
||||
type: 'options',
|
||||
default: 'ascend',
|
||||
options: [
|
||||
{
|
||||
name: 'Ascend',
|
||||
value: 'ascend'
|
||||
},
|
||||
{
|
||||
name: 'Descend',
|
||||
value: 'descend'
|
||||
},
|
||||
],
|
||||
description: 'Sort order.',
|
||||
},
|
||||
]
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Before find script',
|
||||
name: 'setScriptBefore',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: 'Define a script to be run before the action specified by the API call and after the subsequent sort.',
|
||||
displayOptions: {
|
||||
show: {
|
||||
action: [
|
||||
'find',
|
||||
'record',
|
||||
'records',
|
||||
],
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Script Name',
|
||||
name: 'scriptBefore',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getScripts',
|
||||
},
|
||||
options: [],
|
||||
default: '',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
action: [
|
||||
'find',
|
||||
'record',
|
||||
'records',
|
||||
],
|
||||
setScriptBefore: [
|
||||
true
|
||||
],
|
||||
},
|
||||
},
|
||||
placeholder: 'Script Name',
|
||||
description: 'The name of the FileMaker script to be run after the action specified by the API call and after the subsequent sort.',
|
||||
},
|
||||
{
|
||||
displayName: 'Script Parameter',
|
||||
name: 'scriptBeforeParam',
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: false,
|
||||
displayOptions: {
|
||||
show: {
|
||||
action: [
|
||||
'find',
|
||||
'record',
|
||||
'records',
|
||||
],
|
||||
setScriptBefore: [
|
||||
true
|
||||
],
|
||||
},
|
||||
},
|
||||
placeholder: 'Script Parameters',
|
||||
description: 'A parameter for the FileMaker script.',
|
||||
},
|
||||
{
|
||||
displayName: 'Before sort script',
|
||||
name: 'setScriptSort',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: 'Define a script to be run after the action specified by the API call but before the subsequent sort.',
|
||||
displayOptions: {
|
||||
show: {
|
||||
action: [
|
||||
'find',
|
||||
'record',
|
||||
'records',
|
||||
],
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Script Name',
|
||||
name: 'scriptSort',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getScripts',
|
||||
},
|
||||
options: [],
|
||||
default: '',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
action: [
|
||||
'find',
|
||||
'record',
|
||||
'records',
|
||||
],
|
||||
setScriptSort: [
|
||||
true
|
||||
],
|
||||
},
|
||||
},
|
||||
placeholder: 'Script Name',
|
||||
description: 'The name of the FileMaker script to be run after the action specified by the API call but before the subsequent sort.',
|
||||
},
|
||||
{
|
||||
displayName: 'Script Parameter',
|
||||
name: 'scriptSortParam',
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: false,
|
||||
displayOptions: {
|
||||
show: {
|
||||
action: [
|
||||
'find',
|
||||
'record',
|
||||
'records',
|
||||
],
|
||||
setScriptSort: [
|
||||
true
|
||||
],
|
||||
},
|
||||
},
|
||||
placeholder: 'Script Parameters',
|
||||
description: 'A parameter for the FileMaker script.',
|
||||
},
|
||||
{
|
||||
displayName: 'After sort script',
|
||||
name: 'setScriptAfter',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: 'Define a script to be run after the action specified by the API call but before the subsequent sort.',
|
||||
displayOptions: {
|
||||
show: {
|
||||
action: [
|
||||
'find',
|
||||
'record',
|
||||
'records',
|
||||
],
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Script Name',
|
||||
name: 'scriptAfter',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getScripts',
|
||||
},
|
||||
options: [],
|
||||
default: '',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
action: [
|
||||
'find',
|
||||
'record',
|
||||
'records',
|
||||
],
|
||||
setScriptAfter: [
|
||||
true
|
||||
],
|
||||
},
|
||||
},
|
||||
placeholder: 'Script Name',
|
||||
description: 'The name of the FileMaker script to be run after the action specified by the API call and after the subsequent sort.',
|
||||
},
|
||||
{
|
||||
displayName: 'Script Parameter',
|
||||
name: 'scriptAfterParam',
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: false,
|
||||
displayOptions: {
|
||||
show: {
|
||||
action: [
|
||||
'find',
|
||||
'record',
|
||||
'records',
|
||||
],
|
||||
setScriptAfter: [
|
||||
true
|
||||
],
|
||||
},
|
||||
},
|
||||
placeholder: 'Script Parameters',
|
||||
description: 'A parameter for the FileMaker script.',
|
||||
},
|
||||
// ----------------------------------
|
||||
// create/edit
|
||||
// ----------------------------------
|
||||
/*{
|
||||
displayName: 'fieldData',
|
||||
name: 'fieldData',
|
||||
placeholder: '{"field1": "value", "field2": "value", ...}',
|
||||
description: 'Additional fields to add.',
|
||||
type: 'string',
|
||||
default: '{}',
|
||||
displayOptions: {
|
||||
show: {
|
||||
action: [
|
||||
'create',
|
||||
'edit',
|
||||
],
|
||||
},
|
||||
}
|
||||
},*/
|
||||
{
|
||||
displayName: 'Mod Id',
|
||||
name: 'modId',
|
||||
description: 'The last modification ID. When you use modId, a record is edited only when the modId matches.',
|
||||
type: 'number',
|
||||
default: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
action: [
|
||||
'edit',
|
||||
],
|
||||
},
|
||||
}
|
||||
},
|
||||
{
|
||||
displayName: 'Fields',
|
||||
name: 'fieldsParametersUi',
|
||||
placeholder: 'Add field',
|
||||
type: 'fixedCollection',
|
||||
typeOptions: {
|
||||
multipleValues: true,
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
action: [
|
||||
'create',
|
||||
'edit',
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'Fields to define',
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
name: 'fields',
|
||||
displayName: 'Fields',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Field',
|
||||
name: 'name',
|
||||
type: 'options',
|
||||
default: '',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getFields',
|
||||
},
|
||||
options: [],
|
||||
description: 'Field Name.',
|
||||
},
|
||||
{
|
||||
displayName: 'Value',
|
||||
name: 'value',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
]
|
||||
},
|
||||
],
|
||||
},
|
||||
// ----------------------------------
|
||||
// performscript
|
||||
// ----------------------------------
|
||||
{
|
||||
displayName: 'Script Name',
|
||||
name: 'script',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getScripts',
|
||||
},
|
||||
options: [],
|
||||
default: '',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
action: [
|
||||
'performscript'
|
||||
],
|
||||
},
|
||||
},
|
||||
placeholder: 'Script Name',
|
||||
description: 'The name of the FileMaker script to be run.',
|
||||
},
|
||||
{
|
||||
displayName: 'Script Parameter',
|
||||
name: 'scriptParam',
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: false,
|
||||
displayOptions: {
|
||||
show: {
|
||||
action: [
|
||||
'performscript'
|
||||
],
|
||||
},
|
||||
},
|
||||
placeholder: 'Script Parameters',
|
||||
description: 'A parameter for the FileMaker script.',
|
||||
},
|
||||
]
|
||||
};
|
||||
|
||||
methods = {
|
||||
loadOptions: {
|
||||
// Get all the available topics to display them to user so that he can
|
||||
// select them easily
|
||||
async getLayouts(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||
let returnData: INodePropertyOptions[];
|
||||
|
||||
try {
|
||||
returnData = await layoutsApiRequest.call(this);
|
||||
} catch (err) {
|
||||
throw new Error(`FileMaker Error: ${err}`);
|
||||
}
|
||||
|
||||
return returnData;
|
||||
},
|
||||
async getResponseLayouts(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||
const returnData: INodePropertyOptions[] = [];
|
||||
returnData.push({
|
||||
name: 'Use main layout',
|
||||
value: '',
|
||||
});
|
||||
|
||||
let layouts;
|
||||
try {
|
||||
layouts = await layoutsApiRequest.call(this);
|
||||
} catch (err) {
|
||||
throw new Error(`FileMaker Error: ${err}`);
|
||||
}
|
||||
for (const layout of layouts) {
|
||||
returnData.push({
|
||||
name: layout.name,
|
||||
value: layout.name,
|
||||
});
|
||||
}
|
||||
return returnData;
|
||||
},
|
||||
|
||||
async getFields(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||
const returnData: INodePropertyOptions[] = [];
|
||||
|
||||
let fields;
|
||||
try {
|
||||
fields = await getFields.call(this);
|
||||
} catch (err) {
|
||||
throw new Error(`FileMaker Error: ${err}`);
|
||||
}
|
||||
for (const field of fields) {
|
||||
returnData.push({
|
||||
name: field.name,
|
||||
value: field.name,
|
||||
});
|
||||
}
|
||||
return returnData;
|
||||
},
|
||||
|
||||
async getScripts(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||
const returnData: INodePropertyOptions[] = [];
|
||||
|
||||
let scripts;
|
||||
try {
|
||||
scripts = await getScripts.call(this);
|
||||
} catch (err) {
|
||||
throw new Error(`FileMaker Error: ${err}`);
|
||||
}
|
||||
for (const script of scripts) {
|
||||
if (!script.isFolder) {
|
||||
returnData.push({
|
||||
name: script.name,
|
||||
value: script.name,
|
||||
});
|
||||
}
|
||||
}
|
||||
return returnData;
|
||||
},
|
||||
|
||||
async getPortals(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||
const returnData: INodePropertyOptions[] = [];
|
||||
|
||||
let portals;
|
||||
try {
|
||||
portals = await getPortals.call(this);
|
||||
} catch (err) {
|
||||
throw new Error(`FileMaker Error: ${err}`);
|
||||
}
|
||||
Object.keys(portals).forEach((portal) => {
|
||||
returnData.push({
|
||||
name: portal,
|
||||
value: portal,
|
||||
});
|
||||
});
|
||||
|
||||
return returnData;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
||||
const items = this.getInputData();
|
||||
const returnData: INodeExecutionData[] = [];
|
||||
|
||||
const credentials = this.getCredentials('fileMaker');
|
||||
|
||||
if (credentials === undefined) {
|
||||
throw new Error('No credentials got returned!');
|
||||
}
|
||||
|
||||
let token;
|
||||
try {
|
||||
token = await getToken.call(this);
|
||||
} catch (e) {
|
||||
throw new Error(`Login fail: ${e}`);
|
||||
}
|
||||
|
||||
let requestOptions: OptionsWithUri;
|
||||
|
||||
const host = credentials.host as string;
|
||||
const database = credentials.db as string;
|
||||
|
||||
const url = `https://${host}/fmi/data/v1`;
|
||||
|
||||
const action = this.getNodeParameter('action', 0) as string;
|
||||
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
// Reset all values
|
||||
requestOptions = {
|
||||
uri: '',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`,
|
||||
},
|
||||
method: 'GET',
|
||||
json: true
|
||||
};
|
||||
|
||||
const layout = this.getNodeParameter('layout', 0) as string;
|
||||
|
||||
if (action === 'record') {
|
||||
const recid = this.getNodeParameter('recid', 0) as string;
|
||||
requestOptions.uri = url + `/databases/${database}/layouts/${layout}/records/${recid}`;
|
||||
requestOptions.qs = {
|
||||
'portal': JSON.stringify(parsePortals.call(this)),
|
||||
...parseScripts.call(this)
|
||||
};
|
||||
} else if (action === 'records') {
|
||||
requestOptions.uri = url + `/databases/${database}/layouts/${layout}/records`;
|
||||
requestOptions.qs = {
|
||||
'_offset': this.getNodeParameter('offset', 0),
|
||||
'_limit': this.getNodeParameter('limit', 0),
|
||||
'portal': JSON.stringify(parsePortals.call(this)),
|
||||
...parseScripts.call(this)
|
||||
};
|
||||
const sort = parseSort.call(this);
|
||||
if (sort) {
|
||||
requestOptions.body.sort = sort;
|
||||
}
|
||||
} else if (action === 'find') {
|
||||
requestOptions.uri = url + `/databases/${database}/layouts/${layout}/_find`;
|
||||
requestOptions.method = 'POST';
|
||||
requestOptions.body = {
|
||||
'query': parseQuery.call(this),
|
||||
'offset': this.getNodeParameter('offset', 0),
|
||||
'limit': this.getNodeParameter('limit', 0),
|
||||
'layout.response': this.getNodeParameter('responseLayout', 0),
|
||||
...parseScripts.call(this)
|
||||
};
|
||||
const sort = parseSort.call(this);
|
||||
if (sort) {
|
||||
requestOptions.body.sort = sort;
|
||||
}
|
||||
} else if (action === 'create') {
|
||||
requestOptions.uri = url + `/databases/${database}/layouts/${layout}/records`;
|
||||
requestOptions.method = 'POST';
|
||||
requestOptions.headers!['Content-Type'] = 'application/json';
|
||||
|
||||
//TODO: handle portalData
|
||||
requestOptions.body = {
|
||||
fieldData: {...parseFields.call(this)},
|
||||
portalData: {},
|
||||
...parseScripts.call(this)
|
||||
};
|
||||
} else if (action === 'edit') {
|
||||
const recid = this.getNodeParameter('recid', 0) as string;
|
||||
requestOptions.uri = url + `/databases/${database}/layouts/${layout}/records/${recid}`;
|
||||
requestOptions.method = 'PATCH';
|
||||
requestOptions.headers!['Content-Type'] = 'application/json';
|
||||
|
||||
//TODO: handle portalData
|
||||
requestOptions.body = {
|
||||
fieldData: {...parseFields.call(this)},
|
||||
portalData: {},
|
||||
...parseScripts.call(this)
|
||||
};
|
||||
} else if (action === 'performscript') {
|
||||
const scriptName = this.getNodeParameter('script', 0) as string;
|
||||
requestOptions.uri = url + `/databases/${database}/layouts/${layout}/script/${scriptName}`;
|
||||
requestOptions.qs = {
|
||||
'script.param': this.getNodeParameter('scriptParam', 0),
|
||||
};
|
||||
} else if (action === 'duplicate') {
|
||||
const recid = this.getNodeParameter('recid', 0) as string;
|
||||
requestOptions.uri = url + `/databases/${database}/layouts/${layout}/records/${recid}`;
|
||||
requestOptions.method = 'POST';
|
||||
requestOptions.headers!['Content-Type'] = 'application/json';
|
||||
requestOptions.qs = {
|
||||
...parseScripts.call(this)
|
||||
};
|
||||
} else if (action === 'delete') {
|
||||
const recid = this.getNodeParameter('recid', 0) as string;
|
||||
requestOptions.uri = url + `/databases/${database}/layouts/${layout}/records/${recid}`;
|
||||
requestOptions.method = 'DELETE';
|
||||
requestOptions.qs = {
|
||||
...parseScripts.call(this)
|
||||
};
|
||||
} else {
|
||||
throw new Error(`The action "${action}" is not implemented yet!`);
|
||||
}
|
||||
|
||||
// Now that the options are all set make the actual http request
|
||||
let response;
|
||||
try {
|
||||
response = await this.helpers.request(requestOptions);
|
||||
} catch (error) {
|
||||
response = error.response.body;
|
||||
}
|
||||
|
||||
if (typeof response === 'string') {
|
||||
throw new Error('Response body is not valid JSON. Change "Response Format" to "String"');
|
||||
}
|
||||
await logout.call(this, token);
|
||||
|
||||
returnData.push({json: response});
|
||||
}
|
||||
|
||||
return this.prepareOutputData(returnData);
|
||||
}
|
||||
}
|
390
packages/nodes-base/nodes/FileMaker/GenericFunctions.ts
Normal file
390
packages/nodes-base/nodes/FileMaker/GenericFunctions.ts
Normal file
|
@ -0,0 +1,390 @@
|
|||
import {
|
||||
IExecuteFunctions,
|
||||
ILoadOptionsFunctions,
|
||||
IExecuteSingleFunctions
|
||||
} from 'n8n-core';
|
||||
|
||||
import {
|
||||
IDataObject, INodePropertyOptions,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import {OptionsWithUri} from 'request';
|
||||
import {Url} from "url";
|
||||
|
||||
interface ScriptsOptions {
|
||||
script?: any; //tslint:disable-line:no-any
|
||||
'script.param'?: any; //tslint:disable-line:no-any
|
||||
'script.prerequest'?: any; //tslint:disable-line:no-any
|
||||
'script.prerequest.param'?: any; //tslint:disable-line:no-any
|
||||
'script.presort'?: any; //tslint:disable-line:no-any
|
||||
'script.presort.param'?: any; //tslint:disable-line:no-any
|
||||
}
|
||||
interface LayoutObject {
|
||||
name: string;
|
||||
isFolder?: boolean;
|
||||
folderLayoutNames?:LayoutObject[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Make an API request to ActiveCampaign
|
||||
*
|
||||
* @param {IHookFunctions} this
|
||||
* @param {string} method
|
||||
* @returns {Promise<any>}
|
||||
*/
|
||||
export async function layoutsApiRequest(this: ILoadOptionsFunctions | IExecuteFunctions | IExecuteSingleFunctions): Promise<INodePropertyOptions[]> { // tslint:disable-line:no-any
|
||||
const token = await getToken.call(this);
|
||||
const credentials = this.getCredentials('fileMaker');
|
||||
|
||||
if (credentials === undefined) {
|
||||
throw new Error('No credentials got returned!');
|
||||
}
|
||||
const host = credentials.host as string;
|
||||
const db = credentials.db as string;
|
||||
|
||||
const url = `https://${host}/fmi/data/v1/databases/${db}/layouts`;
|
||||
const options: OptionsWithUri = {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`,
|
||||
},
|
||||
method: 'GET',
|
||||
uri: url,
|
||||
json: true
|
||||
};
|
||||
|
||||
try {
|
||||
const responseData = await this.helpers.request!(options);
|
||||
const items = parseLayouts(responseData.response.layouts);
|
||||
items.sort((a, b) => a.name > b.name ? 0 : 1);
|
||||
return items;
|
||||
} catch (error) {
|
||||
// If that data does not exist for some reason return the actual error
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
function parseLayouts(layouts: LayoutObject[]): INodePropertyOptions[] {
|
||||
const returnData: INodePropertyOptions[] = [];
|
||||
for (const layout of layouts) {
|
||||
if (layout.isFolder!) {
|
||||
returnData.push(...parseLayouts(layout.folderLayoutNames!));
|
||||
} else {
|
||||
returnData.push({
|
||||
name: layout.name,
|
||||
value: layout.name,
|
||||
});
|
||||
}
|
||||
}
|
||||
return returnData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make an API request to ActiveCampaign
|
||||
*
|
||||
* @returns {Promise<any>}
|
||||
*/
|
||||
export async function getFields(this: ILoadOptionsFunctions): Promise<any> { // tslint:disable-line:no-any
|
||||
const token = await getToken.call(this);
|
||||
const credentials = this.getCredentials('fileMaker');
|
||||
const layout = this.getCurrentNodeParameter('layout') as string;
|
||||
|
||||
if (credentials === undefined) {
|
||||
throw new Error('No credentials got returned!');
|
||||
}
|
||||
const host = credentials.host as string;
|
||||
const db = credentials.db as string;
|
||||
|
||||
const url = `https://${host}/fmi/data/v1/databases/${db}/layouts/${layout}`;
|
||||
const options: OptionsWithUri = {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`,
|
||||
},
|
||||
method: 'GET',
|
||||
uri: url,
|
||||
json: true
|
||||
};
|
||||
|
||||
try {
|
||||
const responseData = await this.helpers.request!(options);
|
||||
return responseData.response.fieldMetaData;
|
||||
|
||||
} catch (error) {
|
||||
// If that data does not exist for some reason return the actual error
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Make an API request to ActiveCampaign
|
||||
*
|
||||
* @returns {Promise<any>}
|
||||
*/
|
||||
export async function getPortals(this: ILoadOptionsFunctions): Promise<any> { // tslint:disable-line:no-any
|
||||
const token = await getToken.call(this);
|
||||
const credentials = this.getCredentials('fileMaker');
|
||||
const layout = this.getCurrentNodeParameter('layout') as string;
|
||||
|
||||
if (credentials === undefined) {
|
||||
throw new Error('No credentials got returned!');
|
||||
}
|
||||
const host = credentials.host as string;
|
||||
const db = credentials.db as string;
|
||||
|
||||
const url = `https://${host}/fmi/data/v1/databases/${db}/layouts/${layout}`;
|
||||
const options: OptionsWithUri = {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`,
|
||||
},
|
||||
method: 'GET',
|
||||
uri: url,
|
||||
json: true
|
||||
};
|
||||
|
||||
try {
|
||||
const responseData = await this.helpers.request!(options);
|
||||
return responseData.response.portalMetaData;
|
||||
|
||||
} catch (error) {
|
||||
// If that data does not exist for some reason return the actual error
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make an API request to ActiveCampaign
|
||||
*
|
||||
* @returns {Promise<any>}
|
||||
*/
|
||||
export async function getScripts(this: ILoadOptionsFunctions): Promise<any> { // tslint:disable-line:no-any
|
||||
const token = await getToken.call(this);
|
||||
const credentials = this.getCredentials('fileMaker');
|
||||
|
||||
if (credentials === undefined) {
|
||||
throw new Error('No credentials got returned!');
|
||||
}
|
||||
const host = credentials.host as string;
|
||||
const db = credentials.db as string;
|
||||
|
||||
const url = `https://${host}/fmi/data/v1/databases/${db}/scripts`;
|
||||
const options: OptionsWithUri = {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`,
|
||||
},
|
||||
method: 'GET',
|
||||
uri: url,
|
||||
json: true
|
||||
};
|
||||
|
||||
try {
|
||||
const responseData = await this.helpers.request!(options);
|
||||
return responseData.response.scripts;
|
||||
|
||||
} catch (error) {
|
||||
// If that data does not exist for some reason return the actual error
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export async function getToken(this: ILoadOptionsFunctions | IExecuteFunctions | IExecuteSingleFunctions): Promise<any> { // tslint:disable-line:no-any
|
||||
const credentials = this.getCredentials('fileMaker');
|
||||
if (credentials === undefined) {
|
||||
throw new Error('No credentials got returned!');
|
||||
}
|
||||
|
||||
const host = credentials.host as string;
|
||||
const db = credentials.db as string;
|
||||
const login = credentials.login as string;
|
||||
const password = credentials.password as string;
|
||||
|
||||
const url = `https://${host}/fmi/data/v1/databases/${db}/sessions`;
|
||||
|
||||
let requestOptions: OptionsWithUri;
|
||||
// Reset all values
|
||||
requestOptions = {
|
||||
uri: url,
|
||||
headers: {},
|
||||
method: 'POST',
|
||||
json: true
|
||||
//rejectUnauthorized: !this.getNodeParameter('allowUnauthorizedCerts', itemIndex, false) as boolean,
|
||||
};
|
||||
requestOptions.auth = {
|
||||
user: login as string,
|
||||
pass: password as string,
|
||||
};
|
||||
requestOptions.body = {
|
||||
"fmDataSource": [
|
||||
{
|
||||
"database": host,
|
||||
"username": login as string,
|
||||
"password": password as string
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await this.helpers.request!(requestOptions);
|
||||
|
||||
if (typeof response === 'string') {
|
||||
throw new Error('Response body is not valid JSON. Change "Response Format" to "String"');
|
||||
}
|
||||
|
||||
return response.response.token;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
|
||||
let errorMessage;
|
||||
if (error.response) {
|
||||
errorMessage = error.response.body.messages[0].message + '(' + error.response.body.messages[0].message + ')';
|
||||
} else {
|
||||
errorMessage = `${error.message} (${error.name})`;
|
||||
}
|
||||
if (errorMessage !== undefined) {
|
||||
throw errorMessage;
|
||||
}
|
||||
throw error.message;
|
||||
}
|
||||
}
|
||||
|
||||
export async function logout(this: ILoadOptionsFunctions | IExecuteFunctions | IExecuteSingleFunctions, token: string): Promise<any> { // tslint:disable-line:no-any
|
||||
const credentials = this.getCredentials('fileMaker');
|
||||
if (credentials === undefined) {
|
||||
throw new Error('No credentials got returned!');
|
||||
}
|
||||
|
||||
const host = credentials.host as string;
|
||||
const db = credentials.db as string;
|
||||
|
||||
const url = `https://${host}/fmi/data/v1/databases/${db}/sessions/${token}`;
|
||||
|
||||
let requestOptions: OptionsWithUri;
|
||||
// Reset all values
|
||||
requestOptions = {
|
||||
uri: url,
|
||||
headers: {},
|
||||
method: 'DELETE',
|
||||
json: true
|
||||
//rejectUnauthorized: !this.getNodeParameter('allowUnauthorizedCerts', itemIndex, false) as boolean,
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await this.helpers.request!(requestOptions);
|
||||
|
||||
if (typeof response === 'string') {
|
||||
throw new Error('Response body is not valid JSON. Change "Response Format" to "String"');
|
||||
}
|
||||
|
||||
return response;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
|
||||
const errorMessage = error.response.body.messages[0].message + '(' + error.response.body.messages[0].message + ')';
|
||||
|
||||
if (errorMessage !== undefined) {
|
||||
throw errorMessage;
|
||||
}
|
||||
throw error.response.body;
|
||||
}
|
||||
}
|
||||
|
||||
export function parseSort(this: IExecuteFunctions): object | null {
|
||||
let sort;
|
||||
const setSort = this.getNodeParameter('setSort', 0, false);
|
||||
if (!setSort) {
|
||||
sort = null;
|
||||
} else {
|
||||
sort = [];
|
||||
const sortParametersUi = this.getNodeParameter('sortParametersUi', 0, {}) as IDataObject;
|
||||
if (sortParametersUi.rules !== undefined) {
|
||||
// @ts-ignore
|
||||
for (const parameterData of sortParametersUi!.rules as IDataObject[]) {
|
||||
// @ts-ignore
|
||||
sort.push({
|
||||
'fieldName': parameterData!.name as string,
|
||||
'sortOrder': parameterData!.value
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
return sort;
|
||||
}
|
||||
|
||||
|
||||
export function parseScripts(this: IExecuteFunctions): object | null {
|
||||
const setScriptAfter = this.getNodeParameter('setScriptAfter', 0, false);
|
||||
const setScriptBefore = this.getNodeParameter('setScriptBefore', 0, false);
|
||||
const setScriptSort = this.getNodeParameter('setScriptSort', 0, false);
|
||||
|
||||
if (!setScriptAfter && setScriptBefore && setScriptSort) {
|
||||
return {};
|
||||
} else {
|
||||
const scripts = {} as ScriptsOptions;
|
||||
if (setScriptAfter) {
|
||||
scripts.script = this.getNodeParameter('scriptAfter', 0);
|
||||
scripts!['script.param'] = this.getNodeParameter('scriptAfter', 0);
|
||||
}
|
||||
if (setScriptBefore) {
|
||||
scripts['script.prerequest'] = this.getNodeParameter('scriptBefore', 0);
|
||||
scripts['script.prerequest.param'] = this.getNodeParameter('scriptBeforeParam', 0);
|
||||
}
|
||||
if (setScriptSort) {
|
||||
scripts['script.presort'] = this.getNodeParameter('scriptSort', 0);
|
||||
scripts['script.presort.param'] = this.getNodeParameter('scriptSortParam', 0);
|
||||
}
|
||||
return scripts;
|
||||
}
|
||||
}
|
||||
|
||||
export function parsePortals(this: IExecuteFunctions): object | null {
|
||||
let portals;
|
||||
const getPortals = this.getNodeParameter('getPortals', 0);
|
||||
if (!getPortals) {
|
||||
portals = [];
|
||||
} else {
|
||||
portals = this.getNodeParameter('portals', 0);
|
||||
}
|
||||
// @ts-ignore
|
||||
return portals;
|
||||
}
|
||||
|
||||
|
||||
export function parseQuery(this: IExecuteFunctions): object | null {
|
||||
let queries;
|
||||
const queriesParamUi = this.getNodeParameter('queries', 0, {}) as IDataObject;
|
||||
if (queriesParamUi.query !== undefined) {
|
||||
// @ts-ignore
|
||||
queries = [];
|
||||
for (const queryParam of queriesParamUi!.query as IDataObject[]) {
|
||||
const query = {
|
||||
'omit': queryParam.omit ? 'true' : 'false',
|
||||
};
|
||||
// @ts-ignore
|
||||
for (const field of queryParam!.fields!.field as IDataObject[]) {
|
||||
// @ts-ignore
|
||||
query[field.name] = field!.value;
|
||||
}
|
||||
queries.push(query);
|
||||
}
|
||||
} else {
|
||||
queries = null;
|
||||
}
|
||||
// @ts-ignore
|
||||
return queries;
|
||||
}
|
||||
|
||||
export function parseFields(this: IExecuteFunctions): object | null {
|
||||
let fieldData;
|
||||
const fieldsParametersUi = this.getNodeParameter('fieldsParametersUi', 0, {}) as IDataObject;
|
||||
if (fieldsParametersUi.fields !== undefined) {
|
||||
// @ts-ignore
|
||||
fieldData = {};
|
||||
for (const field of fieldsParametersUi!.fields as IDataObject[]) {
|
||||
// @ts-ignore
|
||||
fieldData[field.name] = field!.value;
|
||||
}
|
||||
} else {
|
||||
fieldData = null;
|
||||
}
|
||||
// @ts-ignore
|
||||
return fieldData;
|
||||
}
|
BIN
packages/nodes-base/nodes/FileMaker/filemaker.png
Normal file
BIN
packages/nodes-base/nodes/FileMaker/filemaker.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 16 KiB |
|
@ -1,5 +1,5 @@
|
|||
import { IDataObject } from 'n8n-workflow';
|
||||
import { google } from 'googleapis';
|
||||
import { google, sheets_v4 } from 'googleapis';
|
||||
import { JWT } from 'google-auth-library';
|
||||
import { getAuthenticationClient } from './GoogleApi';
|
||||
|
||||
|
@ -24,6 +24,18 @@ export interface ILookupValues {
|
|||
lookupValue: string;
|
||||
}
|
||||
|
||||
export interface IToDeleteRange {
|
||||
amount: number;
|
||||
startIndex: number;
|
||||
sheetId: number;
|
||||
}
|
||||
|
||||
export interface IToDelete {
|
||||
[key: string]: IToDeleteRange[] | undefined;
|
||||
columns?: IToDeleteRange[];
|
||||
rows?: IToDeleteRange[];
|
||||
}
|
||||
|
||||
export type ValueInputOption = 'RAW' | 'USER_ENTERED';
|
||||
|
||||
export type ValueRenderOption = 'FORMATTED_VALUE' | 'FORMULA' | 'UNFORMATTED_VALUE';
|
||||
|
@ -85,6 +97,44 @@ export class GoogleSheet {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the sheets in a Spreadsheet
|
||||
*/
|
||||
async spreadsheetGetSheets() {
|
||||
const client = await this.getAuthenticationClient();
|
||||
|
||||
const response = await Sheets.spreadsheets.get(
|
||||
{
|
||||
auth: client,
|
||||
spreadsheetId: this.id,
|
||||
fields: 'sheets.properties'
|
||||
}
|
||||
);
|
||||
|
||||
return response.data;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets values in one or more ranges of a spreadsheet.
|
||||
*/
|
||||
async spreadsheetBatchUpdate(requests: sheets_v4.Schema$Request[]) { // tslint:disable-line:no-any
|
||||
const client = await this.getAuthenticationClient();
|
||||
|
||||
const response = await Sheets.spreadsheets.batchUpdate(
|
||||
{
|
||||
auth: client,
|
||||
spreadsheetId: this.id,
|
||||
requestBody: {
|
||||
requests,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
return response.data;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the cell values
|
||||
*/
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
import { sheets_v4 } from 'googleapis';
|
||||
|
||||
import { IExecuteFunctions } from 'n8n-core';
|
||||
import {
|
||||
IDataObject,
|
||||
ILoadOptionsFunctions,
|
||||
INodeExecutionData,
|
||||
INodePropertyOptions,
|
||||
INodeType,
|
||||
INodeTypeDescription,
|
||||
} from 'n8n-workflow';
|
||||
|
@ -11,6 +15,7 @@ import {
|
|||
IGoogleAuthCredentials,
|
||||
ILookupValues,
|
||||
ISheetUpdateData,
|
||||
IToDelete,
|
||||
ValueInputOption,
|
||||
ValueRenderOption,
|
||||
} from './GoogleSheet';
|
||||
|
@ -52,6 +57,11 @@ export class GoogleSheets implements INodeType {
|
|||
value: 'clear',
|
||||
description: 'Clears data from a Sheet',
|
||||
},
|
||||
{
|
||||
name: 'Delete',
|
||||
value: 'delete',
|
||||
description: 'Delete columns and rows from a Sheet',
|
||||
},
|
||||
{
|
||||
name: 'Lookup',
|
||||
value: 'lookup',
|
||||
|
@ -81,17 +91,126 @@ export class GoogleSheets implements INodeType {
|
|||
type: 'string',
|
||||
default: '',
|
||||
required: true,
|
||||
description: 'The ID of the Google Sheet.',
|
||||
description: 'The ID of the Google Sheet.<br />Found as part of the sheet URL https://docs.google.com/spreadsheets/d/{ID}/',
|
||||
},
|
||||
{
|
||||
displayName: 'Range',
|
||||
name: 'range',
|
||||
type: 'string',
|
||||
displayOptions: {
|
||||
hide: {
|
||||
operation: [
|
||||
'delete'
|
||||
],
|
||||
},
|
||||
},
|
||||
default: 'A:F',
|
||||
required: true,
|
||||
description: 'The columns to read and append data to.<br />If it contains multiple sheets it can also be<br />added like this: "MySheet!A:F"',
|
||||
description: 'The table range to read from or to append data to. See the Google <a href="https://developers.google.com/sheets/api/guides/values#writing">documentation</a> for the details.<br />If it contains multiple sheets it can also be<br />added like this: "MySheet!A:F"',
|
||||
},
|
||||
|
||||
|
||||
// ----------------------------------
|
||||
// Delete
|
||||
// ----------------------------------
|
||||
{
|
||||
displayName: 'To Delete',
|
||||
name: 'toDelete',
|
||||
placeholder: 'Add Columns/Rows to delete',
|
||||
description: 'Deletes colums and rows from a sheet.',
|
||||
type: 'fixedCollection',
|
||||
typeOptions: {
|
||||
multipleValues: true,
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'delete'
|
||||
],
|
||||
},
|
||||
},
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Columns',
|
||||
name: 'columns',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Sheet',
|
||||
name: 'sheetId',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getSheets',
|
||||
},
|
||||
options: [],
|
||||
default: '',
|
||||
required: true,
|
||||
description: 'The sheet to delete columns from',
|
||||
},
|
||||
{
|
||||
displayName: 'Start Index',
|
||||
name: 'startIndex',
|
||||
type: 'number',
|
||||
typeOptions: {
|
||||
minValue: 0,
|
||||
},
|
||||
default: 0,
|
||||
description: 'The start index (0 based and inclusive) of column to delete.',
|
||||
},
|
||||
{
|
||||
displayName: 'Amount',
|
||||
name: 'amount',
|
||||
type: 'number',
|
||||
typeOptions: {
|
||||
minValue: 1,
|
||||
},
|
||||
default: 1,
|
||||
description: 'Number of columns to delete.',
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
displayName: 'Rows',
|
||||
name: 'rows',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Sheet',
|
||||
name: 'sheetId',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getSheets',
|
||||
},
|
||||
options: [],
|
||||
default: '',
|
||||
required: true,
|
||||
description: 'The sheet to delete columns from',
|
||||
},
|
||||
{
|
||||
displayName: 'Start Index',
|
||||
name: 'startIndex',
|
||||
type: 'number',
|
||||
typeOptions: {
|
||||
minValue: 0,
|
||||
},
|
||||
default: 0,
|
||||
description: 'The start index (0 based and inclusive) of row to delete.',
|
||||
},
|
||||
{
|
||||
displayName: 'Amount',
|
||||
name: 'amount',
|
||||
type: 'number',
|
||||
typeOptions: {
|
||||
minValue: 1,
|
||||
},
|
||||
default: 1,
|
||||
description: 'Number of rows to delete.',
|
||||
},
|
||||
]
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
|
||||
// ----------------------------------
|
||||
// Read
|
||||
// ----------------------------------
|
||||
|
@ -178,6 +297,7 @@ export class GoogleSheets implements INodeType {
|
|||
operation: [
|
||||
'append',
|
||||
'clear',
|
||||
'delete',
|
||||
],
|
||||
rawData: [
|
||||
true
|
||||
|
@ -201,6 +321,7 @@ export class GoogleSheets implements INodeType {
|
|||
hide: {
|
||||
operation: [
|
||||
'clear',
|
||||
'delete',
|
||||
],
|
||||
rawData: [
|
||||
true
|
||||
|
@ -208,7 +329,7 @@ export class GoogleSheets implements INodeType {
|
|||
},
|
||||
},
|
||||
default: 0,
|
||||
description: 'Index of the row which contains the key. Starts with 0.',
|
||||
description: 'Index of the row which contains the keys. Starts at 0.<br />The incoming node data is matched to the keys for assignment. The matching is case sensitve.',
|
||||
},
|
||||
|
||||
|
||||
|
@ -401,6 +522,48 @@ export class GoogleSheets implements INodeType {
|
|||
};
|
||||
|
||||
|
||||
methods = {
|
||||
loadOptions: {
|
||||
// Get all the sheets in a Spreadsheet
|
||||
async getSheets(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||
const spreadsheetId = this.getCurrentNodeParameter('sheetId') as string;
|
||||
|
||||
const credentials = this.getCredentials('googleApi');
|
||||
|
||||
if (credentials === undefined) {
|
||||
throw new Error('No credentials got returned!');
|
||||
}
|
||||
|
||||
const googleCredentials = {
|
||||
email: credentials.email,
|
||||
privateKey: credentials.privateKey,
|
||||
} as IGoogleAuthCredentials;
|
||||
|
||||
const sheet = new GoogleSheet(spreadsheetId, googleCredentials);
|
||||
const responseData = await sheet.spreadsheetGetSheets();
|
||||
|
||||
if (responseData === undefined) {
|
||||
throw new Error('No data got returned');
|
||||
}
|
||||
|
||||
const returnData: INodePropertyOptions[] = [];
|
||||
for (const sheet of responseData.sheets!) {
|
||||
if (sheet.properties!.sheetType !== 'GRID') {
|
||||
continue;
|
||||
}
|
||||
|
||||
returnData.push({
|
||||
name: sheet.properties!.title as string,
|
||||
value: sheet.properties!.sheetId as unknown as string,
|
||||
});
|
||||
}
|
||||
|
||||
return returnData;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
||||
const spreadsheetId = this.getNodeParameter('sheetId', 0) as string;
|
||||
const credentials = this.getCredentials('googleApi');
|
||||
|
@ -416,10 +579,13 @@ export class GoogleSheets implements INodeType {
|
|||
|
||||
const sheet = new GoogleSheet(spreadsheetId, googleCredentials);
|
||||
|
||||
const range = this.getNodeParameter('range', 0) as string;
|
||||
|
||||
const operation = this.getNodeParameter('operation', 0) as string;
|
||||
|
||||
let range = '';
|
||||
if (operation !== 'delete') {
|
||||
range = this.getNodeParameter('range', 0) as string;
|
||||
}
|
||||
|
||||
const options = this.getNodeParameter('options', 0, {}) as IDataObject;
|
||||
|
||||
const valueInputMode = (options.valueInputMode || 'RAW') as ValueInputOption;
|
||||
|
@ -452,6 +618,41 @@ export class GoogleSheets implements INodeType {
|
|||
|
||||
await sheet.clearData(range);
|
||||
|
||||
const items = this.getInputData();
|
||||
return this.prepareOutputData(items);
|
||||
} else if (operation === 'delete') {
|
||||
// ----------------------------------
|
||||
// delete
|
||||
// ----------------------------------
|
||||
|
||||
const requests: sheets_v4.Schema$Request[] = [];
|
||||
|
||||
const toDelete = this.getNodeParameter('toDelete', 0) as IToDelete;
|
||||
|
||||
const deletePropertyToDimensions: IDataObject = {
|
||||
'columns': 'COLUMNS',
|
||||
'rows': 'ROWS',
|
||||
};
|
||||
|
||||
for (const propertyName of Object.keys(deletePropertyToDimensions)) {
|
||||
if (toDelete[propertyName] !== undefined) {
|
||||
toDelete[propertyName]!.forEach(range => {
|
||||
requests.push({
|
||||
deleteDimension: {
|
||||
range: {
|
||||
sheetId: range.sheetId,
|
||||
dimension: deletePropertyToDimensions[propertyName] as string,
|
||||
startIndex: range.startIndex,
|
||||
endIndex: range.startIndex + range.amount,
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const data = await sheet.spreadsheetBatchUpdate(requests);
|
||||
|
||||
const items = this.getInputData();
|
||||
return this.prepareOutputData(items);
|
||||
} else if (operation === 'lookup') {
|
||||
|
|
|
@ -360,7 +360,6 @@ export class HttpRequest implements INodeType {
|
|||
},
|
||||
],
|
||||
},
|
||||
|
||||
// Body Parameter
|
||||
{
|
||||
displayName: 'Body Parameters',
|
||||
|
|
411
packages/nodes-base/nodes/Intercom/CompanyDescription.ts
Normal file
411
packages/nodes-base/nodes/Intercom/CompanyDescription.ts
Normal file
|
@ -0,0 +1,411 @@
|
|||
import { INodeProperties } from "n8n-workflow";
|
||||
|
||||
export const companyOperations = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'company',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Create',
|
||||
value: 'create',
|
||||
description: 'Create a new company',
|
||||
},
|
||||
{
|
||||
name: 'Get',
|
||||
value: 'get',
|
||||
description: 'Get data of a company',
|
||||
},
|
||||
{
|
||||
name: 'Get All',
|
||||
value: 'getAll',
|
||||
description: 'Get data of all companies',
|
||||
},
|
||||
{
|
||||
name: 'Update',
|
||||
value: 'update',
|
||||
description: 'Update a company',
|
||||
},
|
||||
{
|
||||
name: 'Users',
|
||||
value: 'users',
|
||||
description: `List company's users`,
|
||||
},
|
||||
],
|
||||
default: 'create',
|
||||
description: 'The operation to perform.',
|
||||
},
|
||||
] as INodeProperties[];
|
||||
|
||||
export const companyFields = [
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* company:users */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
{
|
||||
displayName: 'List By',
|
||||
name: 'listBy',
|
||||
type: 'options',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'company',
|
||||
],
|
||||
operation: [
|
||||
'users',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'ID',
|
||||
value: 'id',
|
||||
description: 'The Intercom defined id representing the company',
|
||||
},
|
||||
{
|
||||
name: 'Company ID',
|
||||
value: 'companyId',
|
||||
description: 'The company_id you have given to the company',
|
||||
},
|
||||
],
|
||||
default: '',
|
||||
description: 'List by',
|
||||
},
|
||||
{
|
||||
displayName: 'Value',
|
||||
name: 'value',
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'company',
|
||||
],
|
||||
operation: [
|
||||
'users',
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'View by value',
|
||||
},
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* company:getAll */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
{
|
||||
displayName: 'Return All',
|
||||
name: 'returnAll',
|
||||
type: 'boolean',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'company',
|
||||
],
|
||||
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: [
|
||||
'company',
|
||||
],
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
returnAll: [
|
||||
false,
|
||||
],
|
||||
},
|
||||
},
|
||||
typeOptions: {
|
||||
minValue: 1,
|
||||
maxValue: 60,
|
||||
},
|
||||
default: 50,
|
||||
description: 'How many results to return.',
|
||||
},
|
||||
{
|
||||
displayName: 'Filters',
|
||||
name: 'filters',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Filter',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'company',
|
||||
],
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Segment ID',
|
||||
name: 'segment_id',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Segment representing the Lead',
|
||||
},
|
||||
{
|
||||
displayName: 'Tag ID',
|
||||
name: 'tag_id',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Tag representing the Lead',
|
||||
},
|
||||
]
|
||||
},
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* company:get */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
{
|
||||
displayName: 'Select By',
|
||||
name: 'selectBy',
|
||||
type: 'options',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'company',
|
||||
],
|
||||
operation: [
|
||||
'get',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Company ID',
|
||||
value: 'companyId',
|
||||
description: 'The company_id you have given to the company',
|
||||
},
|
||||
{
|
||||
name: 'ID',
|
||||
value: 'id',
|
||||
description: 'The Intercom defined id representing the company',
|
||||
},
|
||||
{
|
||||
name: 'Name',
|
||||
value: 'name',
|
||||
description: 'The name of the company',
|
||||
},
|
||||
],
|
||||
default: '',
|
||||
description: 'What property to use to query the company.',
|
||||
},
|
||||
{
|
||||
displayName: 'Value',
|
||||
name: 'value',
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'company',
|
||||
],
|
||||
operation: [
|
||||
'get',
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'View by value',
|
||||
},
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* company:create/update */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
{
|
||||
displayName: 'Company Id',
|
||||
name: 'companyId',
|
||||
type: 'string',
|
||||
default: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'company',
|
||||
],
|
||||
operation: [
|
||||
'create',
|
||||
'update',
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'The company id you have defined for the company',
|
||||
},
|
||||
{
|
||||
displayName: 'JSON Parameters',
|
||||
name: 'jsonParameters',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'create',
|
||||
'update',
|
||||
],
|
||||
resource: [
|
||||
'company',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Additional Fields',
|
||||
name: 'additionalFields',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'create',
|
||||
'update',
|
||||
],
|
||||
resource: [
|
||||
'company',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Industry',
|
||||
name: 'industry',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'The industry that this company operates in',
|
||||
},
|
||||
{
|
||||
displayName: 'Monthly Spend',
|
||||
name: 'monthlySpend',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'The phone number of the user',
|
||||
},
|
||||
{
|
||||
displayName: 'Name',
|
||||
name: 'name',
|
||||
type: 'string',
|
||||
default: '',
|
||||
placeholder: '',
|
||||
description: 'Name of the user',
|
||||
},
|
||||
{
|
||||
displayName: 'Plan',
|
||||
name: 'plan',
|
||||
type: 'string',
|
||||
default: '',
|
||||
placeholder: '',
|
||||
description: 'The name of the plan you have associated with the company',
|
||||
},
|
||||
{
|
||||
displayName: 'Size',
|
||||
name: 'size',
|
||||
type: 'number',
|
||||
default: '',
|
||||
description: 'The number of employees in this company',
|
||||
},
|
||||
{
|
||||
displayName: 'Website',
|
||||
name: 'website',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: `The URL for this company's website. Please note that the value<br />specified here is not validated. Accepts any string.`,
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
displayName: 'Custom Attributes',
|
||||
name: 'customAttributesJson',
|
||||
type: 'json',
|
||||
required: false,
|
||||
typeOptions: {
|
||||
alwaysOpenEditWindow: true,
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'company',
|
||||
],
|
||||
operation: [
|
||||
'create',
|
||||
'update',
|
||||
],
|
||||
jsonParameters: [
|
||||
true,
|
||||
],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
description: 'A hash of key/value pairs to represent custom data you want to attribute to a user.',
|
||||
},
|
||||
{
|
||||
displayName: 'Custom Attributes',
|
||||
name: 'customAttributesUi',
|
||||
type: 'fixedCollection',
|
||||
default: '',
|
||||
placeholder: 'Add Attribute',
|
||||
typeOptions: {
|
||||
multipleValues: true,
|
||||
},
|
||||
required: false,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'company',
|
||||
],
|
||||
operation: [
|
||||
'create',
|
||||
'update',
|
||||
],
|
||||
jsonParameters: [
|
||||
false,
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'customAttributesValues',
|
||||
displayName: 'Attributes',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Name',
|
||||
name: 'name',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Value',
|
||||
name: 'value',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
],
|
||||
}
|
||||
],
|
||||
description: 'A hash of key/value pairs to represent custom data you want to attribute to a user.',
|
||||
},
|
||||
] as INodeProperties[];
|
13
packages/nodes-base/nodes/Intercom/CompanyInteface.ts
Normal file
13
packages/nodes-base/nodes/Intercom/CompanyInteface.ts
Normal file
|
@ -0,0 +1,13 @@
|
|||
import { IDataObject } from "n8n-workflow";
|
||||
|
||||
export interface ICompany {
|
||||
remote_created_at?: string;
|
||||
company_id?: string;
|
||||
name?: string;
|
||||
monthly_spend?: number;
|
||||
plan?: string;
|
||||
size?: number;
|
||||
website?: string;
|
||||
industry?: string;
|
||||
custom_attributes?: IDataObject;
|
||||
}
|
82
packages/nodes-base/nodes/Intercom/GenericFunctions.ts
Normal file
82
packages/nodes-base/nodes/Intercom/GenericFunctions.ts
Normal file
|
@ -0,0 +1,82 @@
|
|||
import { OptionsWithUri } from 'request';
|
||||
|
||||
import {
|
||||
IExecuteFunctions,
|
||||
IHookFunctions,
|
||||
ILoadOptionsFunctions,
|
||||
IExecuteSingleFunctions
|
||||
} from 'n8n-core';
|
||||
|
||||
import {
|
||||
IDataObject,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
export async function intercomApiRequest(this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, endpoint: string, method: string, body: any = {}, query?: IDataObject, uri?: string): Promise<any> { // tslint:disable-line:no-any
|
||||
const credentials = this.getCredentials('intercomApi');
|
||||
if (credentials === undefined) {
|
||||
throw new Error('No credentials got returned!');
|
||||
}
|
||||
|
||||
const headerWithAuthentication = Object.assign({},
|
||||
{ Authorization: `Bearer ${credentials.apiKey}`, Accept: 'application/json' });
|
||||
|
||||
const options: OptionsWithUri = {
|
||||
headers: headerWithAuthentication,
|
||||
method,
|
||||
qs: query,
|
||||
uri: uri || `https://api.intercom.io${endpoint}`,
|
||||
body,
|
||||
json: true
|
||||
};
|
||||
|
||||
try {
|
||||
return await this.helpers.request!(options);
|
||||
} catch (error) {
|
||||
const errorMessage = error.response.body.message || error.response.body.Message;
|
||||
|
||||
if (errorMessage !== undefined) {
|
||||
throw errorMessage;
|
||||
}
|
||||
throw error.response.body;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Make an API request to paginated intercom endpoint
|
||||
* and return all results
|
||||
*/
|
||||
export async function intercomApiRequestAllItems(this: IHookFunctions | IExecuteFunctions, propertyName: string, endpoint: string, method: string, body: any = {}, query: IDataObject = {}): Promise<any> { // tslint:disable-line:no-any
|
||||
|
||||
const returnData: IDataObject[] = [];
|
||||
|
||||
let responseData;
|
||||
|
||||
query.per_page = 60;
|
||||
|
||||
let uri: string | undefined;
|
||||
|
||||
do {
|
||||
responseData = await intercomApiRequest.call(this, endpoint, method, body, query, uri);
|
||||
uri = responseData.pages.next;
|
||||
returnData.push.apply(returnData, responseData[propertyName]);
|
||||
} while (
|
||||
responseData.pages !== undefined &&
|
||||
responseData.pages.next !== undefined &&
|
||||
responseData.pages.next !== null
|
||||
);
|
||||
|
||||
return returnData;
|
||||
}
|
||||
|
||||
|
||||
export function validateJSON(json: string | undefined): any { // tslint:disable-line:no-any
|
||||
let result;
|
||||
try {
|
||||
result = JSON.parse(json!);
|
||||
} catch (exception) {
|
||||
result = '';
|
||||
}
|
||||
return result;
|
||||
}
|
529
packages/nodes-base/nodes/Intercom/Intercom.node.ts
Normal file
529
packages/nodes-base/nodes/Intercom/Intercom.node.ts
Normal file
|
@ -0,0 +1,529 @@
|
|||
import {
|
||||
IExecuteFunctions,
|
||||
} from 'n8n-core';
|
||||
import {
|
||||
IDataObject,
|
||||
INodeTypeDescription,
|
||||
INodeExecutionData,
|
||||
INodeType,
|
||||
ILoadOptionsFunctions,
|
||||
INodePropertyOptions,
|
||||
} from 'n8n-workflow';
|
||||
import {
|
||||
leadOpeations,
|
||||
leadFields,
|
||||
} from './LeadDescription';
|
||||
import {
|
||||
intercomApiRequest,
|
||||
intercomApiRequestAllItems,
|
||||
validateJSON,
|
||||
} from './GenericFunctions';
|
||||
import {
|
||||
ILead,
|
||||
ILeadCompany,
|
||||
IAvatar,
|
||||
} from './LeadInterface';
|
||||
import { userOpeations, userFields } from './UserDescription';
|
||||
import { IUser, IUserCompany } from './UserInterface';
|
||||
import { companyOperations, companyFields } from './CompanyDescription';
|
||||
import { ICompany } from './CompanyInteface';
|
||||
|
||||
export class Intercom implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
displayName: 'Intercom',
|
||||
name: 'intercom',
|
||||
icon: 'file:intercom.png',
|
||||
group: ['output'],
|
||||
version: 1,
|
||||
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
||||
description: 'Consume intercom API',
|
||||
defaults: {
|
||||
name: 'Intercom',
|
||||
color: '#c02428',
|
||||
},
|
||||
inputs: ['main'],
|
||||
outputs: ['main'],
|
||||
credentials: [
|
||||
{
|
||||
name: 'intercomApi',
|
||||
required: true,
|
||||
}
|
||||
],
|
||||
properties: [
|
||||
{
|
||||
displayName: 'Resource',
|
||||
name: 'resource',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Company',
|
||||
value: 'company',
|
||||
description: 'Companies allow you to represent commercial organizations using your product.',
|
||||
},
|
||||
{
|
||||
name: 'Lead',
|
||||
value: 'lead',
|
||||
description: 'Leads are useful for representing logged-out users of your application.',
|
||||
},
|
||||
{
|
||||
name: 'User',
|
||||
value: 'user',
|
||||
description: 'The Users resource is the primary way of interacting with Intercom',
|
||||
},
|
||||
],
|
||||
default: 'user',
|
||||
description: 'Resource to consume.',
|
||||
},
|
||||
...leadOpeations,
|
||||
...userOpeations,
|
||||
...companyOperations,
|
||||
...userFields,
|
||||
...leadFields,
|
||||
...companyFields,
|
||||
],
|
||||
};
|
||||
|
||||
methods = {
|
||||
loadOptions: {
|
||||
// Get all the available companies to display them to user so that he can
|
||||
// select them easily
|
||||
async getCompanies(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||
const returnData: INodePropertyOptions[] = [];
|
||||
let companies, response;
|
||||
try {
|
||||
response = await intercomApiRequest.call(this, '/companies', 'GET');
|
||||
} catch (err) {
|
||||
throw new Error(`Intercom Error: ${err}`);
|
||||
}
|
||||
companies = response.companies;
|
||||
for (const company of companies) {
|
||||
const companyName = company.name;
|
||||
const companyId = company.company_id;
|
||||
returnData.push({
|
||||
name: companyName,
|
||||
value: companyId,
|
||||
});
|
||||
}
|
||||
return returnData;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
||||
const items = this.getInputData();
|
||||
const returnData: IDataObject[] = [];
|
||||
const length = items.length as unknown as number;
|
||||
let qs: IDataObject;
|
||||
let responseData;
|
||||
for (let i = 0; i < length; i++) {
|
||||
qs = {};
|
||||
const resource = this.getNodeParameter('resource', 0) as string;
|
||||
const operation = this.getNodeParameter('operation', 0) as string;
|
||||
//https://developers.intercom.com/intercom-api-reference/reference#leads
|
||||
if (resource === 'lead') {
|
||||
if (operation === 'create' || operation === 'update') {
|
||||
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
|
||||
const jsonActive = this.getNodeParameter('jsonParameters', i) as boolean;
|
||||
const body: ILead = {};
|
||||
if (operation === 'create') {
|
||||
body.email = this.getNodeParameter('email', i) as string;
|
||||
}
|
||||
if (additionalFields.email) {
|
||||
body.email = additionalFields.email as string;
|
||||
}
|
||||
if (additionalFields.phone) {
|
||||
body.phone = additionalFields.phone as string;
|
||||
}
|
||||
if (additionalFields.name) {
|
||||
body.name = additionalFields.name as string;
|
||||
}
|
||||
if (additionalFields.unsubscribedFromEmails) {
|
||||
body.unsubscribed_from_emails = additionalFields.unsubscribedFromEmails as boolean;
|
||||
}
|
||||
if (additionalFields.updateLastRequestAt) {
|
||||
body.update_last_request_at = additionalFields.updateLastRequestAt as boolean;
|
||||
}
|
||||
if (additionalFields.utmSource) {
|
||||
body.utm_source = additionalFields.utmSource as string;
|
||||
}
|
||||
if (additionalFields.utmMedium) {
|
||||
body.utm_medium = additionalFields.utmMedium as string;
|
||||
}
|
||||
if (additionalFields.utmCampaign) {
|
||||
body.utm_campaign = additionalFields.utmCampaign as string;
|
||||
}
|
||||
if (additionalFields.utmTerm) {
|
||||
body.utm_term = additionalFields.utmTerm as string;
|
||||
}
|
||||
if (additionalFields.utmContent) {
|
||||
body.utm_content = additionalFields.utmContent as string;
|
||||
}
|
||||
if (additionalFields.avatar) {
|
||||
const avatar: IAvatar = {
|
||||
type: 'avatar',
|
||||
image_url: additionalFields.avatar as string,
|
||||
};
|
||||
body.avatar = avatar;
|
||||
}
|
||||
if (additionalFields.companies) {
|
||||
const companies: ILeadCompany[] = [];
|
||||
// @ts-ignore
|
||||
additionalFields.companies.forEach( o => {
|
||||
const company: ILeadCompany = {};
|
||||
company.company_id = o;
|
||||
companies.push(company);
|
||||
});
|
||||
body.companies = companies;
|
||||
}
|
||||
if (!jsonActive) {
|
||||
const customAttributesValues = (this.getNodeParameter('customAttributesUi', i) as IDataObject).customAttributesValues as IDataObject[];
|
||||
if (customAttributesValues) {
|
||||
const customAttributes = {};
|
||||
for (let i = 0; i < customAttributesValues.length; i++) {
|
||||
// @ts-ignore
|
||||
customAttributes[customAttributesValues[i].name] = customAttributesValues[i].value;
|
||||
}
|
||||
body.custom_attributes = customAttributes;
|
||||
}
|
||||
} else {
|
||||
const customAttributesJson = validateJSON(this.getNodeParameter('customAttributesJson', i) as string);
|
||||
if (customAttributesJson) {
|
||||
body.custom_attributes = customAttributesJson;
|
||||
}
|
||||
}
|
||||
|
||||
if (operation === 'update') {
|
||||
const updateBy = this.getNodeParameter('updateBy', 0) as string;
|
||||
const value = this.getNodeParameter('value', i) as string;
|
||||
if (updateBy === 'userId') {
|
||||
body.user_id = value;
|
||||
}
|
||||
if (updateBy === 'id') {
|
||||
body.id = value;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
responseData = await intercomApiRequest.call(this, '/contacts', 'POST', body);
|
||||
} catch (err) {
|
||||
throw new Error(`Intercom Error: ${JSON.stringify(err)}`);
|
||||
}
|
||||
}
|
||||
if (operation === 'get') {
|
||||
const selectBy = this.getNodeParameter('selectBy', 0) as string;
|
||||
const value = this.getNodeParameter('value', i) as string;
|
||||
if (selectBy === 'email') {
|
||||
qs.email = value;
|
||||
}
|
||||
if (selectBy === 'userId') {
|
||||
qs.user_id = value;
|
||||
}
|
||||
if (selectBy === 'phone') {
|
||||
qs.phone = value;
|
||||
}
|
||||
try {
|
||||
if (selectBy === 'id') {
|
||||
responseData = await intercomApiRequest.call(this, `/contacts/${value}`, 'GET');
|
||||
} else {
|
||||
responseData = await intercomApiRequest.call(this, '/contacts', 'GET', {}, qs);
|
||||
responseData = responseData.contacts;
|
||||
}
|
||||
} catch (err) {
|
||||
throw new Error(`Intercom Error: ${JSON.stringify(err)}`);
|
||||
}
|
||||
}
|
||||
if (operation === 'getAll') {
|
||||
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
|
||||
const filters = this.getNodeParameter('filters', i) as IDataObject;
|
||||
Object.assign(qs, filters);
|
||||
|
||||
try {
|
||||
if (returnAll === true) {
|
||||
responseData = await intercomApiRequestAllItems.call(this, 'contacts', '/contacts', 'GET', {}, qs);
|
||||
} else {
|
||||
qs.per_page = this.getNodeParameter('limit', i) as number;
|
||||
responseData = await intercomApiRequest.call(this, '/contacts', 'GET', {}, qs);
|
||||
responseData = responseData.contacts;
|
||||
}
|
||||
} catch (err) {
|
||||
throw new Error(`Intercom Error: ${JSON.stringify(err)}`);
|
||||
}
|
||||
}
|
||||
if (operation === 'delete') {
|
||||
const deleteBy = this.getNodeParameter('deleteBy', 0) as string;
|
||||
const value = this.getNodeParameter('value', i) as string;
|
||||
try {
|
||||
if (deleteBy === 'id') {
|
||||
responseData = await intercomApiRequest.call(this, `/contacts/${value}`, 'DELETE');
|
||||
} else {
|
||||
qs.user_id = value;
|
||||
responseData = await intercomApiRequest.call(this, '/contacts', 'DELETE', {}, qs);
|
||||
}
|
||||
} catch (err) {
|
||||
throw new Error(`Intercom Error: ${JSON.stringify(err)}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
//https://developers.intercom.com/intercom-api-reference/reference#users
|
||||
if (resource === 'user') {
|
||||
if (operation === 'create' || operation === 'update') {
|
||||
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
|
||||
const jsonActive = this.getNodeParameter('jsonParameters', i) as boolean;
|
||||
const body: IUser = {};
|
||||
|
||||
if (operation === 'create') {
|
||||
const identifierType = this.getNodeParameter('identifierType', i) as string;
|
||||
if (identifierType === 'email') {
|
||||
body.email = this.getNodeParameter('idValue', i) as string;
|
||||
} else if (identifierType === 'userId') {
|
||||
body.user_id = this.getNodeParameter('idValue', i) as string;
|
||||
}
|
||||
}
|
||||
|
||||
if (additionalFields.email) {
|
||||
body.email = additionalFields.email as string;
|
||||
}
|
||||
if (additionalFields.userId) {
|
||||
body.user_id = additionalFields.userId as string;
|
||||
}
|
||||
if (additionalFields.phone) {
|
||||
body.phone = additionalFields.phone as string;
|
||||
}
|
||||
if (additionalFields.name) {
|
||||
body.name = additionalFields.name as string;
|
||||
}
|
||||
if (additionalFields.unsubscribedFromEmails) {
|
||||
body.unsubscribed_from_emails = additionalFields.unsubscribedFromEmails as boolean;
|
||||
}
|
||||
if (additionalFields.updateLastRequestAt) {
|
||||
body.update_last_request_at = additionalFields.updateLastRequestAt as boolean;
|
||||
}
|
||||
if (additionalFields.sessionCount) {
|
||||
body.session_count = additionalFields.sessionCount as number;
|
||||
}
|
||||
if (additionalFields.avatar) {
|
||||
const avatar: IAvatar = {
|
||||
type: 'avatar',
|
||||
image_url: additionalFields.avatar as string,
|
||||
};
|
||||
body.avatar = avatar;
|
||||
}
|
||||
if (additionalFields.utmSource) {
|
||||
body.utm_source = additionalFields.utmSource as string;
|
||||
}
|
||||
if (additionalFields.utmMedium) {
|
||||
body.utm_medium = additionalFields.utmMedium as string;
|
||||
}
|
||||
if (additionalFields.utmCampaign) {
|
||||
body.utm_campaign = additionalFields.utmCampaign as string;
|
||||
}
|
||||
if (additionalFields.utmTerm) {
|
||||
body.utm_term = additionalFields.utmTerm as string;
|
||||
}
|
||||
if (additionalFields.utmContent) {
|
||||
body.utm_content = additionalFields.utmContent as string;
|
||||
}
|
||||
if (additionalFields.companies) {
|
||||
const companies: IUserCompany[] = [];
|
||||
// @ts-ignore
|
||||
additionalFields.companies.forEach( o => {
|
||||
const company: IUserCompany = {};
|
||||
company.company_id = o;
|
||||
companies.push(company);
|
||||
});
|
||||
body.companies = companies;
|
||||
}
|
||||
if (additionalFields.sessionCount) {
|
||||
body.session_count = additionalFields.sessionCount as number;
|
||||
}
|
||||
if (!jsonActive) {
|
||||
const customAttributesValues = (this.getNodeParameter('customAttributesUi', i) as IDataObject).customAttributesValues as IDataObject[];
|
||||
if (customAttributesValues) {
|
||||
const customAttributes = {};
|
||||
for (let i = 0; i < customAttributesValues.length; i++) {
|
||||
// @ts-ignore
|
||||
customAttributes[customAttributesValues[i].name] = customAttributesValues[i].value;
|
||||
}
|
||||
body.custom_attributes = customAttributes;
|
||||
}
|
||||
} else {
|
||||
const customAttributesJson = validateJSON(this.getNodeParameter('customAttributesJson', i) as string);
|
||||
if (customAttributesJson) {
|
||||
body.custom_attributes = customAttributesJson;
|
||||
}
|
||||
}
|
||||
|
||||
if (operation === 'update') {
|
||||
const updateBy = this.getNodeParameter('updateBy', 0) as string;
|
||||
const value = this.getNodeParameter('value', i) as string;
|
||||
if (updateBy === 'userId') {
|
||||
body.user_id = value;
|
||||
}
|
||||
if (updateBy === 'id') {
|
||||
body.id = value;
|
||||
}
|
||||
if (updateBy === 'email') {
|
||||
body.email = value;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
responseData = await intercomApiRequest.call(this, '/users', 'POST', body, qs);
|
||||
} catch (err) {
|
||||
throw new Error(`Intercom Error: ${JSON.stringify(err)}`);
|
||||
}
|
||||
}
|
||||
if (operation === 'get') {
|
||||
const selectBy = this.getNodeParameter('selectBy', 0) as string;
|
||||
const value = this.getNodeParameter('value', i) as string;
|
||||
if (selectBy === 'userId') {
|
||||
qs.user_id = value;
|
||||
}
|
||||
try {
|
||||
if (selectBy === 'id') {
|
||||
responseData = await intercomApiRequest.call(this, `/users/${value}`, 'GET', {}, qs);
|
||||
} else {
|
||||
responseData = await intercomApiRequest.call(this, '/users', 'GET', {}, qs);
|
||||
}
|
||||
} catch (err) {
|
||||
throw new Error(`Intercom Error: ${JSON.stringify(err)}`);
|
||||
}
|
||||
}
|
||||
if (operation === 'getAll') {
|
||||
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
|
||||
const filters = this.getNodeParameter('filters', i) as IDataObject;
|
||||
Object.assign(qs, filters);
|
||||
|
||||
try {
|
||||
if (returnAll === true) {
|
||||
responseData = await intercomApiRequestAllItems.call(this, 'users', '/users', 'GET', {}, qs);
|
||||
} else {
|
||||
qs.per_page = this.getNodeParameter('limit', i) as number;
|
||||
responseData = await intercomApiRequest.call(this, '/users', 'GET', {}, qs);
|
||||
responseData = responseData.users;
|
||||
}
|
||||
} catch (err) {
|
||||
throw new Error(`Intercom Error: ${JSON.stringify(err)}`);
|
||||
}
|
||||
}
|
||||
if (operation === 'delete') {
|
||||
const id = this.getNodeParameter('id', i) as string;
|
||||
try {
|
||||
responseData = await intercomApiRequest.call(this, `/users/${id}`, 'DELETE');
|
||||
} catch (err) {
|
||||
throw new Error(`Intercom Error: ${JSON.stringify(err)}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
//https://developers.intercom.com/intercom-api-reference/reference#companies
|
||||
if (resource === 'company') {
|
||||
if (operation === 'create' || operation === 'update') {
|
||||
const id = this.getNodeParameter('companyId', i) as string;
|
||||
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
|
||||
const jsonActive = this.getNodeParameter('jsonParameters', i) as boolean;
|
||||
const body: ICompany = {
|
||||
company_id: id,
|
||||
};
|
||||
if (additionalFields.monthlySpend) {
|
||||
body.monthly_spend = additionalFields.monthlySpend as number;
|
||||
}
|
||||
if (additionalFields.name) {
|
||||
body.name = additionalFields.name as string;
|
||||
}
|
||||
if (additionalFields.plan) {
|
||||
body.plan = additionalFields.plan as string;
|
||||
}
|
||||
if (additionalFields.size) {
|
||||
body.size = additionalFields.size as number;
|
||||
}
|
||||
if (additionalFields.website) {
|
||||
body.website = additionalFields.website as string;
|
||||
}
|
||||
if (additionalFields.industry) {
|
||||
body.industry = additionalFields.industry as string;
|
||||
}
|
||||
if (!jsonActive) {
|
||||
const customAttributesValues = (this.getNodeParameter('customAttributesUi', i) as IDataObject).customAttributesValues as IDataObject[];
|
||||
if (customAttributesValues) {
|
||||
const customAttributes = {};
|
||||
for (let i = 0; i < customAttributesValues.length; i++) {
|
||||
// @ts-ignore
|
||||
customAttributes[customAttributesValues[i].name] = customAttributesValues[i].value;
|
||||
}
|
||||
body.custom_attributes = customAttributes;
|
||||
}
|
||||
} else {
|
||||
const customAttributesJson = validateJSON(this.getNodeParameter('customAttributesJson', i) as string);
|
||||
if (customAttributesJson) {
|
||||
body.custom_attributes = customAttributesJson;
|
||||
}
|
||||
}
|
||||
try {
|
||||
responseData = await intercomApiRequest.call(this, '/companies', 'POST', body, qs);
|
||||
} catch (err) {
|
||||
throw new Error(`Intercom Error: ${JSON.stringify(err)}`);
|
||||
}
|
||||
}
|
||||
if (operation === 'get') {
|
||||
const selectBy = this.getNodeParameter('selectBy', 0) as string;
|
||||
const value = this.getNodeParameter('value', i) as string;
|
||||
if (selectBy === 'companyId') {
|
||||
qs.company_id = value;
|
||||
}
|
||||
if (selectBy === 'name') {
|
||||
qs.name = value;
|
||||
}
|
||||
try {
|
||||
if (selectBy === 'id') {
|
||||
responseData = await intercomApiRequest.call(this, `/companies/${value}`, 'GET', {}, qs);
|
||||
} else {
|
||||
responseData = await intercomApiRequest.call(this, '/companies', 'GET', {}, qs);
|
||||
}
|
||||
} catch (err) {
|
||||
throw new Error(`Intercom Error: ${JSON.stringify(err)}`);
|
||||
}
|
||||
}
|
||||
if (operation === 'getAll') {
|
||||
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
|
||||
const filters = this.getNodeParameter('filters', i) as IDataObject;
|
||||
Object.assign(qs, filters);
|
||||
|
||||
try {
|
||||
if (returnAll === true) {
|
||||
responseData = await intercomApiRequestAllItems.call(this, 'companies', '/companies', 'GET', {}, qs);
|
||||
} else {
|
||||
qs.per_page = this.getNodeParameter('limit', i) as number;
|
||||
responseData = await intercomApiRequest.call(this, '/companies', 'GET', {}, qs);
|
||||
responseData = responseData.companies;
|
||||
}
|
||||
} catch (err) {
|
||||
throw new Error(`Intercom Error: ${JSON.stringify(err)}`);
|
||||
}
|
||||
}
|
||||
if (operation === 'users') {
|
||||
const filterBy = this.getNodeParameter('filterBy', 0) as string;
|
||||
const value = this.getNodeParameter('value', i) as string;
|
||||
if (filterBy === 'companyId') {
|
||||
qs.company_id = value;
|
||||
}
|
||||
try {
|
||||
if (filterBy === 'id') {
|
||||
responseData = await intercomApiRequest.call(this, `/companies/${value}/users`, 'GET', {}, qs);
|
||||
} else {
|
||||
qs.type = 'users';
|
||||
responseData = await intercomApiRequest.call(this, '/companies', 'GET', {}, qs);
|
||||
}
|
||||
} catch (err) {
|
||||
throw new Error(`Intercom Error: ${JSON.stringify(err)}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Array.isArray(responseData)) {
|
||||
returnData.push.apply(returnData, responseData as IDataObject[]);
|
||||
} else {
|
||||
returnData.push(responseData as IDataObject);
|
||||
}
|
||||
}
|
||||
return [this.helpers.returnJsonArray(returnData)];
|
||||
}
|
||||
}
|
521
packages/nodes-base/nodes/Intercom/LeadDescription.ts
Normal file
521
packages/nodes-base/nodes/Intercom/LeadDescription.ts
Normal file
|
@ -0,0 +1,521 @@
|
|||
import { INodeProperties } from "n8n-workflow";
|
||||
|
||||
export const leadOpeations = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'lead',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Create',
|
||||
value: 'create',
|
||||
description: 'Create a new lead',
|
||||
},
|
||||
{
|
||||
name: 'Get',
|
||||
value: 'get',
|
||||
description: 'Get data of a lead',
|
||||
},
|
||||
{
|
||||
name: 'Get All',
|
||||
value: 'getAll',
|
||||
description: 'Get data of all leads',
|
||||
},
|
||||
{
|
||||
name: 'Update',
|
||||
value: 'update',
|
||||
description: 'Update new lead',
|
||||
},
|
||||
{
|
||||
name: 'Delete',
|
||||
value: 'delete',
|
||||
description: 'Delete a lead',
|
||||
}
|
||||
],
|
||||
default: 'create',
|
||||
description: 'The operation to perform.',
|
||||
},
|
||||
] as INodeProperties[];
|
||||
|
||||
export const leadFields = [
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* lead:delete */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
{
|
||||
displayName: 'Delete By',
|
||||
name: 'deleteBy',
|
||||
type: 'options',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'lead',
|
||||
],
|
||||
operation: [
|
||||
'delete',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'ID',
|
||||
value: 'id',
|
||||
description: 'The Intercom defined id representing the Lead',
|
||||
},
|
||||
{
|
||||
name: 'User ID',
|
||||
value: 'userId',
|
||||
description: 'Automatically generated identifier for the Lead',
|
||||
},
|
||||
],
|
||||
default: '',
|
||||
description: 'Delete by',
|
||||
},
|
||||
{
|
||||
displayName: 'Value',
|
||||
name: 'value',
|
||||
type: 'string',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'lead',
|
||||
],
|
||||
operation: [
|
||||
'delete',
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'Delete by value',
|
||||
},
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* lead:get */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
{
|
||||
displayName: 'Select By',
|
||||
name: 'selectBy',
|
||||
type: 'options',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'lead',
|
||||
],
|
||||
operation: [
|
||||
'get',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Email',
|
||||
value: 'email',
|
||||
description: 'Email representing the Lead',
|
||||
},
|
||||
{
|
||||
name: 'ID',
|
||||
value: 'id',
|
||||
description: 'The Intercom defined id representing the Lead',
|
||||
},
|
||||
{
|
||||
name: 'User ID',
|
||||
value: 'userId',
|
||||
description: 'Automatically generated identifier for the Lead',
|
||||
},
|
||||
{
|
||||
name: 'Phone',
|
||||
value: 'phone',
|
||||
description: 'Phone representing the Lead',
|
||||
},
|
||||
],
|
||||
default: '',
|
||||
description: 'The property to select the lead by.',
|
||||
},
|
||||
{
|
||||
displayName: 'Value',
|
||||
name: 'value',
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'lead',
|
||||
],
|
||||
operation: [
|
||||
'get',
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'View by value',
|
||||
},
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* lead:getAll */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
{
|
||||
displayName: 'Return All',
|
||||
name: 'returnAll',
|
||||
type: 'boolean',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'lead',
|
||||
],
|
||||
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: [
|
||||
'lead',
|
||||
],
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
returnAll: [
|
||||
false,
|
||||
],
|
||||
},
|
||||
},
|
||||
typeOptions: {
|
||||
minValue: 1,
|
||||
maxValue: 60,
|
||||
},
|
||||
default: 50,
|
||||
description: 'How many results to return.',
|
||||
},
|
||||
{
|
||||
displayName: 'Filters',
|
||||
name: 'filters',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Filter',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'lead',
|
||||
],
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Email',
|
||||
name: 'email',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'The email address of the lead',
|
||||
},
|
||||
{
|
||||
displayName: 'Phone',
|
||||
name: 'phone',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'The phone number of the lead',
|
||||
},
|
||||
]
|
||||
},
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* lead:update */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
{
|
||||
displayName: 'Update By',
|
||||
name: 'updateBy',
|
||||
type: 'options',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'lead',
|
||||
],
|
||||
operation: [
|
||||
'update',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'User ID',
|
||||
value: 'userId',
|
||||
description: 'Automatically generated identifier for the Lead',
|
||||
},
|
||||
{
|
||||
name: 'ID',
|
||||
value: 'id',
|
||||
description: 'The Intercom defined id representing the Lead',
|
||||
},
|
||||
],
|
||||
default: 'id',
|
||||
description: 'The property via which to query the lead.',
|
||||
},
|
||||
{
|
||||
displayName: 'Value',
|
||||
name: 'value',
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'lead',
|
||||
],
|
||||
operation: [
|
||||
'update',
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'Value of the property to identify the lead to update',
|
||||
},
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* lead:create */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
{
|
||||
displayName: 'Email',
|
||||
name: 'email',
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'lead',
|
||||
],
|
||||
operation: [
|
||||
'create',
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'The email of the user.',
|
||||
},
|
||||
{
|
||||
displayName: 'JSON Parameters',
|
||||
name: 'jsonParameters',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'create',
|
||||
'update',
|
||||
],
|
||||
resource: [
|
||||
'lead',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Additional Fields',
|
||||
name: 'additionalFields',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'create',
|
||||
'update',
|
||||
],
|
||||
resource: [
|
||||
'lead',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Avatar',
|
||||
name: 'avatar',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'An avatar image URL. note: the image url needs to be https.',
|
||||
},
|
||||
{
|
||||
displayName: 'Name',
|
||||
name: 'name',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Name of the user',
|
||||
},
|
||||
{
|
||||
displayName: 'Phone',
|
||||
name: 'phone',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'The phone number of the user',
|
||||
},
|
||||
{
|
||||
displayName: 'Unsubscribed From Emails',
|
||||
name: 'unsubscribedFromEmails',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: 'Whether the Lead is unsubscribed from emails',
|
||||
},
|
||||
{
|
||||
displayName: 'Update Last Request At',
|
||||
name: 'updateLastRequestAt',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: 'A boolean value, which if true, instructs Intercom to update the<br />users last_request_at value to the current API service time in<br />UTC. default value if not sent is false.',
|
||||
},
|
||||
{
|
||||
displayName: 'Companies',
|
||||
name: 'companies',
|
||||
type: 'multiOptions',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getCompanies',
|
||||
},
|
||||
default: [],
|
||||
description: 'Identifies the companies this user belongs to.',
|
||||
},
|
||||
{
|
||||
displayName: 'Email',
|
||||
name: 'email',
|
||||
type: 'string',
|
||||
default: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
'/resource': [
|
||||
'lead',
|
||||
],
|
||||
'/operation': [
|
||||
'update',
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'The email of the user.',
|
||||
},
|
||||
{
|
||||
displayName: 'UTM Source',
|
||||
name: 'utmSource',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'An avatar image URL. note: the image url needs to be https.',
|
||||
},
|
||||
{
|
||||
displayName: 'UTM Medium',
|
||||
name: 'utmMedium',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Identifies what type of link was used',
|
||||
},
|
||||
{
|
||||
displayName: 'UTM Campaign',
|
||||
name: 'utmCampaign',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Identifies a specific product promotion or strategic campaign',
|
||||
},
|
||||
{
|
||||
displayName: 'UTM Term',
|
||||
name: 'utmTerm',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Identifies search terms',
|
||||
},
|
||||
{
|
||||
displayName: 'UTM Content',
|
||||
name: 'utmContent',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Identifies what specifically was clicked to bring the user to the site',
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
displayName: 'Custom Attributes',
|
||||
name: 'customAttributesJson',
|
||||
type: 'json',
|
||||
required: false,
|
||||
typeOptions: {
|
||||
alwaysOpenEditWindow: true,
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'lead',
|
||||
],
|
||||
operation: [
|
||||
'create',
|
||||
'update',
|
||||
],
|
||||
jsonParameters: [
|
||||
true,
|
||||
],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
description: 'A hash of key/value pairs to represent custom data you want to attribute to a user.',
|
||||
},
|
||||
{
|
||||
displayName: 'Custom Attributes',
|
||||
name: 'customAttributesUi',
|
||||
type: 'fixedCollection',
|
||||
default: '',
|
||||
placeholder: 'Add Attribute',
|
||||
typeOptions: {
|
||||
multipleValues: true,
|
||||
},
|
||||
required: false,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'lead',
|
||||
],
|
||||
operation: [
|
||||
'create',
|
||||
'update',
|
||||
],
|
||||
jsonParameters: [
|
||||
false,
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'customAttributesValues',
|
||||
displayName: 'Attributes',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Name',
|
||||
name: 'name',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Value',
|
||||
name: 'value',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
],
|
||||
}
|
||||
],
|
||||
description: 'A hash of key/value pairs to represent custom data you want to attribute to a user.',
|
||||
},
|
||||
] as INodeProperties[];
|
29
packages/nodes-base/nodes/Intercom/LeadInterface.ts
Normal file
29
packages/nodes-base/nodes/Intercom/LeadInterface.ts
Normal file
|
@ -0,0 +1,29 @@
|
|||
import { IDataObject } from "n8n-workflow";
|
||||
|
||||
export interface ILeadCompany {
|
||||
company_id?: string;
|
||||
}
|
||||
|
||||
export interface IAvatar {
|
||||
type?: string;
|
||||
image_url?: string;
|
||||
}
|
||||
|
||||
export interface ILead {
|
||||
user_id?: string;
|
||||
id?: string;
|
||||
email?: string;
|
||||
phone?: string;
|
||||
name?: string;
|
||||
custom_attributes?: IDataObject;
|
||||
companies?: ILeadCompany[];
|
||||
last_request_at?: number;
|
||||
unsubscribed_from_emails?: boolean;
|
||||
update_last_request_at?: boolean;
|
||||
avatar?: IAvatar;
|
||||
utm_source?: string;
|
||||
utm_medium?: string;
|
||||
utm_campaign?: string;
|
||||
utm_term?: string;
|
||||
utm_content?: string;
|
||||
}
|
574
packages/nodes-base/nodes/Intercom/UserDescription.ts
Normal file
574
packages/nodes-base/nodes/Intercom/UserDescription.ts
Normal file
|
@ -0,0 +1,574 @@
|
|||
import { INodeProperties } from "n8n-workflow";
|
||||
|
||||
export const userOpeations = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'user',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Create',
|
||||
value: 'create',
|
||||
description: 'Create a new user',
|
||||
},
|
||||
{
|
||||
name: 'Get',
|
||||
value: 'get',
|
||||
description: 'Get data of a user',
|
||||
},
|
||||
{
|
||||
name: 'Get All',
|
||||
value: 'getAll',
|
||||
description: 'Get data of all users',
|
||||
},
|
||||
{
|
||||
name: 'Update',
|
||||
value: 'update',
|
||||
description: 'Update a user',
|
||||
},
|
||||
{
|
||||
name: 'Delete',
|
||||
value: 'delete',
|
||||
description: 'Delete a user',
|
||||
}
|
||||
],
|
||||
default: 'create',
|
||||
description: 'The operation to perform.',
|
||||
},
|
||||
] as INodeProperties[];
|
||||
|
||||
export const userFields = [
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* user:delete */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
{
|
||||
displayName: 'ID',
|
||||
name: 'id',
|
||||
type: 'string',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'user',
|
||||
],
|
||||
operation: [
|
||||
'delete',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
description: 'The Intercom defined id representing the Lead',
|
||||
},
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* user:getAll */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
{
|
||||
displayName: 'Return All',
|
||||
name: 'returnAll',
|
||||
type: 'boolean',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'user',
|
||||
],
|
||||
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: [
|
||||
'user',
|
||||
],
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
returnAll: [
|
||||
false,
|
||||
],
|
||||
},
|
||||
},
|
||||
typeOptions: {
|
||||
minValue: 1,
|
||||
maxValue: 60,
|
||||
},
|
||||
default: 50,
|
||||
description: 'How many results to return.',
|
||||
},
|
||||
{
|
||||
displayName: 'Filters',
|
||||
name: 'filters',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Filter',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'user',
|
||||
],
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Company ID',
|
||||
name: 'company_id',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Company ID representing the user',
|
||||
},
|
||||
{
|
||||
displayName: 'Email',
|
||||
name: 'email',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'The email address of the user',
|
||||
},
|
||||
{
|
||||
displayName: 'Tag ID',
|
||||
name: 'tag_id',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Tag representing the user',
|
||||
},
|
||||
{
|
||||
displayName: 'Segment ID',
|
||||
name: 'segment_id',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Segment representing the user',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* user:get */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
{
|
||||
displayName: 'Select By',
|
||||
name: 'selectBy',
|
||||
type: 'options',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'user',
|
||||
],
|
||||
operation: [
|
||||
'get',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'ID',
|
||||
value: 'id',
|
||||
default: '',
|
||||
description: 'The Intercom defined id representing the Lead',
|
||||
},
|
||||
{
|
||||
name: 'User ID',
|
||||
value: 'userId',
|
||||
default: '',
|
||||
description: 'Automatically generated identifier for the Lead',
|
||||
},
|
||||
],
|
||||
default: '',
|
||||
description: 'The property to select the user by.',
|
||||
},
|
||||
{
|
||||
displayName: 'Value',
|
||||
name: 'value',
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'user',
|
||||
],
|
||||
operation: [
|
||||
'get',
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'View by value',
|
||||
},
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* user:update */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
{
|
||||
displayName: 'Update By',
|
||||
name: 'updateBy',
|
||||
type: 'options',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'user',
|
||||
],
|
||||
operation: [
|
||||
'update',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'ID',
|
||||
value: 'id',
|
||||
description: 'The Intercom defined id representing the user',
|
||||
},
|
||||
{
|
||||
name: 'Email',
|
||||
value: 'email',
|
||||
description: 'The email address of user',
|
||||
},
|
||||
{
|
||||
name: 'User ID',
|
||||
value: 'userId',
|
||||
description: 'Automatically generated identifier for the user',
|
||||
},
|
||||
|
||||
],
|
||||
default: 'id',
|
||||
description: 'The property via which to query the user.',
|
||||
},
|
||||
{
|
||||
displayName: 'Value',
|
||||
name: 'value',
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'user',
|
||||
],
|
||||
operation: [
|
||||
'update',
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'Value of the property to identify the user to update',
|
||||
},
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* user:create */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
{
|
||||
displayName: 'Identifier Type',
|
||||
name: 'identifierType',
|
||||
type: 'options',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'user',
|
||||
],
|
||||
operation: [
|
||||
'create',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'User ID',
|
||||
value: 'userId',
|
||||
description: 'A unique string identifier for the user. It is required on creation if an email is not supplied.',
|
||||
},
|
||||
{
|
||||
name: 'Email',
|
||||
value: 'email',
|
||||
description: `The user's email address. It is required on creation if a user_id is not supplied.`,
|
||||
},
|
||||
],
|
||||
default: '',
|
||||
description: 'Unique string identifier',
|
||||
},
|
||||
{
|
||||
displayName: 'Value',
|
||||
name: 'idValue',
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'user',
|
||||
],
|
||||
operation: [
|
||||
'create',
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'Unique string identifier value',
|
||||
},
|
||||
{
|
||||
displayName: 'JSON Parameters',
|
||||
name: 'jsonParameters',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'create',
|
||||
'update',
|
||||
],
|
||||
resource: [
|
||||
'user'
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Additional Fields',
|
||||
name: 'additionalFields',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'create',
|
||||
'update',
|
||||
],
|
||||
resource: [
|
||||
'user'
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Email',
|
||||
name: 'email',
|
||||
displayOptions: {
|
||||
show: {
|
||||
'/operation': [
|
||||
'update',
|
||||
],
|
||||
'/resource': [
|
||||
'user'
|
||||
],
|
||||
},
|
||||
hide: {
|
||||
'/updateBy': [
|
||||
'email',
|
||||
]
|
||||
},
|
||||
},
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Email of the user',
|
||||
},
|
||||
{
|
||||
displayName: 'User ID',
|
||||
name: 'userId',
|
||||
displayOptions: {
|
||||
show: {
|
||||
'/operation': [
|
||||
'update',
|
||||
],
|
||||
'/resource': [
|
||||
'user'
|
||||
],
|
||||
},
|
||||
hide: {
|
||||
'/updateBy': [
|
||||
'email',
|
||||
'userId',
|
||||
]
|
||||
},
|
||||
},
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Email of the user',
|
||||
},
|
||||
{
|
||||
displayName: 'Phone',
|
||||
name: 'phone',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'The phone number of the user',
|
||||
},
|
||||
{
|
||||
displayName: 'Name',
|
||||
name: 'name',
|
||||
type: 'string',
|
||||
default: '',
|
||||
placeholder: '',
|
||||
description: 'Name of the user',
|
||||
},
|
||||
{
|
||||
displayName: 'Unsubscribed From Emails',
|
||||
name: 'unsubscribedFromEmails',
|
||||
type: 'boolean',
|
||||
default: '',
|
||||
placeholder: '',
|
||||
description: 'Whether the user is unsubscribed from emails',
|
||||
},
|
||||
{
|
||||
displayName: 'Update Last Request At',
|
||||
name: 'updateLastRequestAt',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
options: [],
|
||||
description: 'A boolean value, which if true, instructs Intercom to update the users<br />last_request_at value to the current API service time in UTC.',
|
||||
},
|
||||
{
|
||||
displayName: 'Session Count',
|
||||
name: 'sessionCount',
|
||||
type: 'number',
|
||||
default: false,
|
||||
options: [],
|
||||
description: `How many sessions the user has recorded`,
|
||||
},
|
||||
{
|
||||
displayName: 'Companies',
|
||||
name: 'companies',
|
||||
type: 'multiOptions',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getCompanies',
|
||||
},
|
||||
default: [],
|
||||
description: 'Identifies the companies this user belongs to.',
|
||||
},
|
||||
{
|
||||
displayName: 'Avatar',
|
||||
name: 'avatar',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'An avatar image URL. note: the image url needs to be https.',
|
||||
},
|
||||
{
|
||||
displayName: 'UTM Source',
|
||||
name: 'utmSource',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'An avatar image URL. note: the image url needs to be https.',
|
||||
},
|
||||
{
|
||||
displayName: 'UTM Medium',
|
||||
name: 'utmMedium',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Identifies what type of link was used',
|
||||
},
|
||||
{
|
||||
displayName: 'UTM Campaign',
|
||||
name: 'utmCampaign',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Identifies a specific product promotion or strategic campaign',
|
||||
},
|
||||
{
|
||||
displayName: 'UTM Term',
|
||||
name: 'utmTerm',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Identifies search terms',
|
||||
},
|
||||
{
|
||||
displayName: 'UTM Content',
|
||||
name: 'utmContent',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Identifies what specifically was clicked to bring the user to the site',
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
displayName: 'Custom Attributes',
|
||||
name: 'customAttributesJson',
|
||||
type: 'json',
|
||||
required: false,
|
||||
typeOptions: {
|
||||
alwaysOpenEditWindow: true,
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'user',
|
||||
],
|
||||
operation: [
|
||||
'create',
|
||||
'update',
|
||||
],
|
||||
jsonParameters: [
|
||||
true,
|
||||
],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
description: 'A hash of key/value pairs to represent custom data you want to attribute to a user.',
|
||||
},
|
||||
{
|
||||
displayName: 'Custom Attributes',
|
||||
name: 'customAttributesUi',
|
||||
type: 'fixedCollection',
|
||||
default: '',
|
||||
placeholder: 'Add Attribute',
|
||||
typeOptions: {
|
||||
multipleValues: true,
|
||||
},
|
||||
required: false,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'user',
|
||||
],
|
||||
operation: [
|
||||
'create',
|
||||
'update',
|
||||
],
|
||||
jsonParameters: [
|
||||
false,
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'customAttributesValues',
|
||||
displayName: 'Attributes',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Name',
|
||||
name: 'name',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Value',
|
||||
name: 'value',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
],
|
||||
}
|
||||
],
|
||||
description: 'A hash of key/value pairs to represent custom data you want to attribute to a user.',
|
||||
},
|
||||
] as INodeProperties[];
|
32
packages/nodes-base/nodes/Intercom/UserInterface.ts
Normal file
32
packages/nodes-base/nodes/Intercom/UserInterface.ts
Normal file
|
@ -0,0 +1,32 @@
|
|||
import { IDataObject } from "n8n-workflow";
|
||||
|
||||
export interface IUserCompany {
|
||||
company_id?: string;
|
||||
}
|
||||
|
||||
export interface IAvatar {
|
||||
type?: string;
|
||||
image_url?: string;
|
||||
}
|
||||
|
||||
export interface IUser {
|
||||
user_id?: string;
|
||||
id?: string;
|
||||
email?: string;
|
||||
phone?: string;
|
||||
name?: string;
|
||||
custom_attributes?: IDataObject;
|
||||
companies?: IUserCompany[];
|
||||
last_request_at?: number;
|
||||
signed_up_at?: string;
|
||||
unsubscribed_from_emails?: boolean;
|
||||
update_last_request_at?: boolean;
|
||||
last_seen_user_agent?: boolean;
|
||||
session_count?: number;
|
||||
avatar?: IAvatar;
|
||||
utm_source?: string;
|
||||
utm_medium?: string;
|
||||
utm_campaign?: string;
|
||||
utm_term?: string;
|
||||
utm_content?: string;
|
||||
}
|
BIN
packages/nodes-base/nodes/Intercom/intercom.png
Normal file
BIN
packages/nodes-base/nodes/Intercom/intercom.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.8 KiB |
58
packages/nodes-base/nodes/Mailchimp/GenericFunctions.ts
Normal file
58
packages/nodes-base/nodes/Mailchimp/GenericFunctions.ts
Normal file
|
@ -0,0 +1,58 @@
|
|||
import { OptionsWithUri } from 'request';
|
||||
|
||||
import {
|
||||
IExecuteFunctions,
|
||||
IHookFunctions,
|
||||
ILoadOptionsFunctions,
|
||||
IExecuteSingleFunctions
|
||||
} from 'n8n-core';
|
||||
|
||||
export async function mailchimpApiRequest(this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, endpoint: string, method: string, body: any = {}, headers?: object): Promise<any> { // tslint:disable-line:no-any
|
||||
const credentials = this.getCredentials('mailchimpApi');
|
||||
|
||||
if (credentials === undefined) {
|
||||
throw new Error('No credentials got returned!');
|
||||
}
|
||||
|
||||
const headerWithAuthentication = Object.assign({}, headers, { Authorization: `apikey ${credentials.apiKey}` });
|
||||
|
||||
if (!(credentials.apiKey as string).includes('-')) {
|
||||
throw new Error('The API key is not valid!');
|
||||
}
|
||||
|
||||
const datacenter = (credentials.apiKey as string).split('-').pop();
|
||||
|
||||
const host = 'api.mailchimp.com/3.0';
|
||||
|
||||
const options: OptionsWithUri = {
|
||||
headers: headerWithAuthentication,
|
||||
method,
|
||||
uri: `https://${datacenter}.${host}${endpoint}`,
|
||||
json: true,
|
||||
};
|
||||
|
||||
if (Object.keys(body).length !== 0) {
|
||||
options.body = body;
|
||||
}
|
||||
|
||||
try {
|
||||
return await this.helpers.request!(options);
|
||||
} catch (error) {
|
||||
const errorMessage = error.response.body.message || error.response.body.Message;
|
||||
|
||||
if (errorMessage !== undefined) {
|
||||
throw errorMessage;
|
||||
}
|
||||
throw error.response.body;
|
||||
}
|
||||
}
|
||||
|
||||
export function validateJSON(json: string | undefined): any { // tslint:disable-line:no-any
|
||||
let result;
|
||||
try {
|
||||
result = JSON.parse(json!);
|
||||
} catch (exception) {
|
||||
result = '';
|
||||
}
|
||||
return result;
|
||||
}
|
558
packages/nodes-base/nodes/Mailchimp/Mailchimp.node.ts
Normal file
558
packages/nodes-base/nodes/Mailchimp/Mailchimp.node.ts
Normal file
|
@ -0,0 +1,558 @@
|
|||
import * as moment from 'moment';
|
||||
|
||||
import {
|
||||
IExecuteSingleFunctions,
|
||||
} from 'n8n-core';
|
||||
import {
|
||||
IDataObject,
|
||||
ILoadOptionsFunctions,
|
||||
INodeTypeDescription,
|
||||
INodeExecutionData,
|
||||
INodeType,
|
||||
INodePropertyOptions,
|
||||
} from 'n8n-workflow';
|
||||
import {
|
||||
mailchimpApiRequest,
|
||||
validateJSON,
|
||||
} from './GenericFunctions';
|
||||
|
||||
enum Status {
|
||||
subscribe = 'subscribe',
|
||||
unsubscribed = 'unsubscribe',
|
||||
cleaned = 'cleaned',
|
||||
pending = 'pending',
|
||||
transactional = 'transactional',
|
||||
}
|
||||
|
||||
interface ILocation {
|
||||
latitude?: number;
|
||||
longitude?: number;
|
||||
}
|
||||
|
||||
interface ICreateMemberBody {
|
||||
listId: string;
|
||||
email_address: string;
|
||||
email_type?: string;
|
||||
status?: Status;
|
||||
language?: string;
|
||||
vip?: boolean;
|
||||
location?: ILocation;
|
||||
ips_signup?: string;
|
||||
timestamp_signup?: string;
|
||||
ip_opt?: string;
|
||||
timestamp_opt?: string;
|
||||
tags?: string[];
|
||||
merge_fields?: IDataObject;
|
||||
}
|
||||
|
||||
export class Mailchimp implements INodeType {
|
||||
|
||||
description: INodeTypeDescription = {
|
||||
displayName: 'Mailchimp',
|
||||
name: 'mailchimp',
|
||||
icon: 'file:mailchimp.png',
|
||||
group: ['output'],
|
||||
version: 1,
|
||||
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
||||
description: 'Consume Mailchimp API',
|
||||
defaults: {
|
||||
name: 'Mailchimp',
|
||||
color: '#c02428',
|
||||
},
|
||||
inputs: ['main'],
|
||||
outputs: ['main'],
|
||||
credentials: [
|
||||
{
|
||||
name: 'mailchimpApi',
|
||||
required: true,
|
||||
}
|
||||
],
|
||||
properties: [
|
||||
{
|
||||
displayName: 'Resource',
|
||||
name: 'resource',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Member',
|
||||
value: 'member',
|
||||
description: 'Add member to list',
|
||||
},
|
||||
],
|
||||
default: 'member',
|
||||
required: true,
|
||||
description: 'Resource to consume.',
|
||||
},
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'member',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Create',
|
||||
value: 'create',
|
||||
description: 'Create a new member on list',
|
||||
},
|
||||
],
|
||||
default: 'create',
|
||||
description: 'The operation to perform.',
|
||||
},
|
||||
{
|
||||
displayName: 'List',
|
||||
name: 'list',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getLists',
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'member',
|
||||
],
|
||||
operation: [
|
||||
'create',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
options: [],
|
||||
required: true,
|
||||
description: 'List of lists',
|
||||
},
|
||||
{
|
||||
displayName: 'Email',
|
||||
name: 'email',
|
||||
type: 'string',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'member',
|
||||
],
|
||||
operation: [
|
||||
'create',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
description: 'Email address for a subscriber.',
|
||||
},
|
||||
{
|
||||
displayName: 'Status',
|
||||
name: 'status',
|
||||
type: 'options',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'member',
|
||||
],
|
||||
operation: [
|
||||
'create',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Subscribed',
|
||||
value: 'subscribed',
|
||||
description: '',
|
||||
},
|
||||
{
|
||||
name: 'Unsubscribed',
|
||||
value: 'unsubscribed',
|
||||
description: '',
|
||||
},
|
||||
{
|
||||
name: 'Cleaned',
|
||||
value: 'cleaned',
|
||||
description: '',
|
||||
},
|
||||
{
|
||||
name: 'Pending',
|
||||
value: 'pending',
|
||||
description: '',
|
||||
},
|
||||
{
|
||||
name: 'Transactional',
|
||||
value: 'transactional',
|
||||
description: '',
|
||||
},
|
||||
],
|
||||
default: '',
|
||||
description: `Subscriber's current status.`,
|
||||
},
|
||||
{
|
||||
displayName: 'JSON Parameters',
|
||||
name: 'jsonParameters',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource:[
|
||||
'member'
|
||||
],
|
||||
operation: [
|
||||
'create',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Options',
|
||||
name: 'options',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Option',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource:[
|
||||
'member',
|
||||
],
|
||||
operation: [
|
||||
'create',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Email Type',
|
||||
name: 'emailType',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Email',
|
||||
value: 'email',
|
||||
description: '',
|
||||
},
|
||||
{
|
||||
name: 'Text',
|
||||
value: 'text',
|
||||
description: '',
|
||||
},
|
||||
],
|
||||
default: '',
|
||||
description: 'Type of email this member asked to get',
|
||||
},
|
||||
{
|
||||
displayName: 'Signup IP',
|
||||
name: 'ipSignup',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'IP address the subscriber signed up from.',
|
||||
},
|
||||
{
|
||||
displayName: 'Opt-in IP',
|
||||
name: 'ipOptIn',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'The IP address the subscriber used to confirm their opt-in status.',
|
||||
},
|
||||
{
|
||||
displayName: 'Signup Timestamp',
|
||||
name: 'timestampSignup',
|
||||
type: 'dateTime',
|
||||
default: '',
|
||||
description: 'The date and time the subscriber signed up for the list in ISO 8601 format.',
|
||||
},
|
||||
{
|
||||
displayName: 'Language',
|
||||
name: 'language',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: `If set/detected, the subscriber's language.`,
|
||||
},
|
||||
{
|
||||
displayName: 'Vip',
|
||||
name: 'vip',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: `Vip status for subscribers`,
|
||||
},
|
||||
{
|
||||
displayName: 'Opt-in Timestamp',
|
||||
name: 'timestampOpt',
|
||||
type: 'dateTime',
|
||||
default: '',
|
||||
description: `The date and time the subscribe confirmed their opt-in status in ISO 8601 format.`,
|
||||
},
|
||||
{
|
||||
displayName: 'Tags',
|
||||
name: 'tags',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: `The tags that are associated with a member separeted by ,.`,
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
displayName: 'Location',
|
||||
name: 'locationFieldsUi',
|
||||
type: 'fixedCollection',
|
||||
placeholder: 'Add Location',
|
||||
default: {},
|
||||
description: `Subscriber location information.n`,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource:[
|
||||
'member',
|
||||
],
|
||||
operation: [
|
||||
'create',
|
||||
],
|
||||
jsonParameters: [
|
||||
false,
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'locationFieldsValues',
|
||||
displayName: 'Location',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Latitude',
|
||||
name: 'latitude',
|
||||
type: 'string',
|
||||
required: true,
|
||||
description: 'The location latitude.',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Longitude',
|
||||
name: 'longitude',
|
||||
type: 'string',
|
||||
required: true,
|
||||
description: 'The location longitude.',
|
||||
default: '',
|
||||
},
|
||||
],
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Merge Fields',
|
||||
name: 'mergeFieldsUi',
|
||||
placeholder: 'Add Merge Fields',
|
||||
type: 'fixedCollection',
|
||||
default: {},
|
||||
typeOptions: {
|
||||
multipleValues: true,
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource:[
|
||||
'member'
|
||||
],
|
||||
operation: [
|
||||
'create',
|
||||
],
|
||||
jsonParameters: [
|
||||
false,
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'An individual merge var and value for a member.',
|
||||
options: [
|
||||
{
|
||||
name: 'mergeFieldsValues',
|
||||
displayName: 'Field',
|
||||
typeOptions: {
|
||||
multipleValueButtonText: 'Add Field',
|
||||
},
|
||||
values: [
|
||||
{
|
||||
displayName: 'Field Name',
|
||||
name: 'name',
|
||||
type: 'string',
|
||||
required: true,
|
||||
description: 'Merge Field name',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Field Value',
|
||||
name: 'value',
|
||||
required: true,
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Merge field value.',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Merge Fields',
|
||||
name: 'mergeFieldsJson',
|
||||
type: 'json',
|
||||
typeOptions: {
|
||||
alwaysOpenEditWindow: true,
|
||||
},
|
||||
default: '',
|
||||
description: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource:[
|
||||
'member',
|
||||
],
|
||||
operation: [
|
||||
'create',
|
||||
],
|
||||
jsonParameters: [
|
||||
true,
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Location',
|
||||
name: 'locationJson',
|
||||
type: 'json',
|
||||
typeOptions: {
|
||||
alwaysOpenEditWindow: true,
|
||||
},
|
||||
default: '',
|
||||
description: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource:[
|
||||
'member',
|
||||
],
|
||||
operation: [
|
||||
'create',
|
||||
],
|
||||
jsonParameters: [
|
||||
true,
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
};
|
||||
|
||||
|
||||
methods = {
|
||||
loadOptions: {
|
||||
|
||||
// Get all the available lists to display them to user so that he can
|
||||
// select them easily
|
||||
async getLists(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||
const returnData: INodePropertyOptions[] = [];
|
||||
let lists, response;
|
||||
try {
|
||||
response = await mailchimpApiRequest.call(this, '/lists', 'GET');
|
||||
lists = response.lists;
|
||||
} catch (err) {
|
||||
throw new Error(`Mailchimp Error: ${err}`);
|
||||
}
|
||||
for (const list of lists) {
|
||||
const listName = list.name;
|
||||
const listId = list.id;
|
||||
|
||||
returnData.push({
|
||||
name: listName,
|
||||
value: listId,
|
||||
});
|
||||
}
|
||||
return returnData;
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
async executeSingle(this: IExecuteSingleFunctions): Promise<INodeExecutionData> {
|
||||
let response = {};
|
||||
const resource = this.getNodeParameter('resource') as string;
|
||||
const operation = this.getNodeParameter('operation') as string;
|
||||
|
||||
if (resource === 'member') {
|
||||
if (operation === 'create') {
|
||||
|
||||
const listId = this.getNodeParameter('list') as string;
|
||||
const email = this.getNodeParameter('email') as string;
|
||||
const status = this.getNodeParameter('status') as Status;
|
||||
const options = this.getNodeParameter('options') as IDataObject;
|
||||
const jsonActive = this.getNodeParameter('jsonParameters') as IDataObject;
|
||||
|
||||
const body: ICreateMemberBody = {
|
||||
listId,
|
||||
email_address: email,
|
||||
status
|
||||
};
|
||||
if (options.emailType) {
|
||||
body.email_type = options.emailType as string;
|
||||
}
|
||||
if (options.languaje) {
|
||||
body.language = options.language as string;
|
||||
}
|
||||
if (options.vip) {
|
||||
body.vip = options.vip as boolean;
|
||||
}
|
||||
if (options.ipSignup) {
|
||||
body.ips_signup = options.ipSignup as string;
|
||||
}
|
||||
if (options.ipOptIn) {
|
||||
body.ip_opt = options.ipOptIn as string;
|
||||
}
|
||||
if (options.timestampOpt) {
|
||||
body.timestamp_opt = moment(options.timestampOpt as string).format('YYYY-MM-DD HH:MM:SS') as string;
|
||||
}
|
||||
if (options.timestampSignup) {
|
||||
body.timestamp_signup = moment(options.timestampSignup as string).format('YYYY-MM-DD HH:MM:SS') as string;
|
||||
}
|
||||
if (options.tags) {
|
||||
// @ts-ignore
|
||||
body.tags = options.tags.split(',') as string[];
|
||||
}
|
||||
if (!jsonActive) {
|
||||
const locationValues = (this.getNodeParameter('locationFieldsUi') as IDataObject).locationFieldsValues as IDataObject;
|
||||
if (locationValues) {
|
||||
const location: ILocation = {};
|
||||
for (const key of Object.keys(locationValues)) {
|
||||
if (key === 'latitude') {
|
||||
location.latitude = parseInt(locationValues[key] as string, 10) as number;
|
||||
} else if (key === 'longitude') {
|
||||
location.longitude = parseInt(locationValues[key] as string, 10) as number;
|
||||
}
|
||||
}
|
||||
body.location = location;
|
||||
}
|
||||
const mergeFieldsValues = (this.getNodeParameter('mergeFieldsUi') as IDataObject).mergeFieldsValues as IDataObject[];
|
||||
if (mergeFieldsValues) {
|
||||
const mergeFields = {};
|
||||
for (let i = 0; i < mergeFieldsValues.length; i++) {
|
||||
// @ts-ignore
|
||||
mergeFields[mergeFieldsValues[i].name] = mergeFieldsValues[i].value;
|
||||
}
|
||||
body.merge_fields = mergeFields;
|
||||
}
|
||||
} else {
|
||||
const locationJson = validateJSON(this.getNodeParameter('locationJson') as string);
|
||||
const mergeFieldsJson = validateJSON(this.getNodeParameter('mergeFieldsJson') as string);
|
||||
if (locationJson) {
|
||||
body.location = locationJson;
|
||||
}
|
||||
if (mergeFieldsJson) {
|
||||
body.merge_fields = mergeFieldsJson;
|
||||
}
|
||||
}
|
||||
try {
|
||||
response = await mailchimpApiRequest.call(this, `/lists/${listId}/members`, 'POST', body);
|
||||
} catch (err) {
|
||||
throw new Error(`Mailchimp Error: ${JSON.stringify(err)}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
json: response,
|
||||
};
|
||||
}
|
||||
}
|
BIN
packages/nodes-base/nodes/Mailchimp/mailchimp.png
Normal file
BIN
packages/nodes-base/nodes/Mailchimp/mailchimp.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.9 KiB |
|
@ -54,7 +54,7 @@ export class Merge implements INodeType {
|
|||
{
|
||||
name: 'Pass-through',
|
||||
value: 'passThrough',
|
||||
description: 'Passes through data of one input. The output will conain only items of the defined input.',
|
||||
description: 'Passes through data of one input. The output will contain only items of the defined input.',
|
||||
},
|
||||
{
|
||||
name: 'Remove Key Matches',
|
||||
|
|
|
@ -71,11 +71,12 @@ async function getAccessToken(this: IHookFunctions | IExecuteFunctions | IExecut
|
|||
throw error.response.body;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make an API request to paginated paypal endpoint
|
||||
* and return all results
|
||||
*/
|
||||
export async function paypalApiRequestAllItems(this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, propertyName: string, endpoint: string, method: string, body: any = {}, query?: IDataObject, uri?: string): Promise<any> { // tslint:disable-line:no-any
|
||||
export async function payPalApiRequestAllItems(this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, propertyName: string, endpoint: string, method: string, body: any = {}, query?: IDataObject, uri?: string): Promise<any> { // tslint:disable-line:no-any
|
||||
|
||||
const returnData: IDataObject[] = [];
|
||||
|
||||
|
@ -84,7 +85,7 @@ export async function paypalApiRequestAllItems(this: IHookFunctions | IExecuteFu
|
|||
query!.page_size = 1000;
|
||||
|
||||
do {
|
||||
responseData = await paypalApiRequest.call(this, endpoint, method, body, query, uri);
|
||||
responseData = await payPalApiRequest.call(this, endpoint, method, body, query, uri);
|
||||
uri = getNext(responseData.links);
|
||||
returnData.push.apply(returnData, responseData[propertyName]);
|
||||
} while (
|
|
@ -1,7 +1,6 @@
|
|||
import {
|
||||
IExecuteFunctions,
|
||||
} from 'n8n-core';
|
||||
|
||||
import {
|
||||
IDataObject,
|
||||
INodeTypeDescription,
|
||||
|
@ -9,8 +8,10 @@ import {
|
|||
INodeType,
|
||||
} from 'n8n-workflow';
|
||||
import {
|
||||
payoutOpeations,
|
||||
payoutOperations,
|
||||
payoutItemOperations,
|
||||
payoutFields,
|
||||
payoutItemFields,
|
||||
} from './PaymentDescription';
|
||||
import {
|
||||
IPaymentBatch,
|
||||
|
@ -21,14 +22,14 @@ import {
|
|||
} from './PaymentInteface';
|
||||
import {
|
||||
validateJSON,
|
||||
paypalApiRequest,
|
||||
paypalApiRequestAllItems
|
||||
payPalApiRequest,
|
||||
payPalApiRequestAllItems
|
||||
} from './GenericFunctions';
|
||||
|
||||
export class PayPal implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
displayName: 'PayPal',
|
||||
name: 'paypal',
|
||||
name: 'payPal',
|
||||
icon: 'file:paypal.png',
|
||||
group: ['output'],
|
||||
version: 1,
|
||||
|
@ -42,7 +43,7 @@ export class PayPal implements INodeType {
|
|||
outputs: ['main'],
|
||||
credentials: [
|
||||
{
|
||||
name: 'paypalApi',
|
||||
name: 'payPalApi',
|
||||
required: true,
|
||||
}
|
||||
],
|
||||
|
@ -55,14 +56,21 @@ export class PayPal implements INodeType {
|
|||
{
|
||||
name: 'Payout',
|
||||
value: 'payout',
|
||||
description: 'Use the Payouts API to make payments to multiple PayPal or Venmo recipients. The Payouts API is a fast, convenient way to send commissions, rebates, rewards, and general disbursements. You can send up to 15,000 payments per call. If you integrated the Payouts API before September 1, 2017, you receive transaction reports through Mass Payments Reporting.',
|
||||
},
|
||||
{
|
||||
name: 'Payout Item',
|
||||
value: 'payoutItem',
|
||||
},
|
||||
],
|
||||
default: 'payout',
|
||||
description: 'Resource to consume.',
|
||||
},
|
||||
...payoutOpeations,
|
||||
|
||||
// Payout
|
||||
...payoutOperations,
|
||||
...payoutItemOperations,
|
||||
...payoutFields,
|
||||
...payoutItemFields,
|
||||
],
|
||||
};
|
||||
|
||||
|
@ -71,10 +79,12 @@ export class PayPal implements INodeType {
|
|||
const returnData: IDataObject[] = [];
|
||||
const length = items.length as unknown as number;
|
||||
let responseData;
|
||||
let qs: IDataObject = {};
|
||||
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++) {
|
||||
const resource = this.getNodeParameter('resource', 0) as string;
|
||||
const operation = this.getNodeParameter('operation', 0) as string;
|
||||
if (resource === 'payout') {
|
||||
if (operation === 'create') {
|
||||
const body: IPaymentBatch = {};
|
||||
|
@ -97,18 +107,18 @@ export class PayPal implements INodeType {
|
|||
const payoutItems: IItem[] = [];
|
||||
const itemsValues = (this.getNodeParameter('itemsUi', i) as IDataObject).itemsValues as IDataObject[];
|
||||
if (itemsValues && itemsValues.length > 0) {
|
||||
itemsValues.forEach( o => {
|
||||
const payoutItem: IItem = {};
|
||||
const amount: IAmount = {};
|
||||
amount.currency = o.currency as string;
|
||||
amount.value = parseFloat(o.amount as string);
|
||||
payoutItem.amount = amount;
|
||||
payoutItem.note = o.note as string || '';
|
||||
payoutItem.receiver = o.receiverValue as string;
|
||||
payoutItem.recipient_type = o.recipientType as RecipientType;
|
||||
payoutItem.recipient_wallet = o.recipientWallet as RecipientWallet;
|
||||
payoutItem.sender_item_id = o.senderItemId as string || '';
|
||||
payoutItems.push(payoutItem);
|
||||
itemsValues.forEach(o => {
|
||||
const payoutItem: IItem = {};
|
||||
const amount: IAmount = {};
|
||||
amount.currency = o.currency as string;
|
||||
amount.value = parseFloat(o.amount as string);
|
||||
payoutItem.amount = amount;
|
||||
payoutItem.note = o.note as string || '';
|
||||
payoutItem.receiver = o.receiverValue as string;
|
||||
payoutItem.recipient_type = o.recipientType as RecipientType;
|
||||
payoutItem.recipient_wallet = o.recipientWallet as RecipientWallet;
|
||||
payoutItem.sender_item_id = o.senderItemId as string || '';
|
||||
payoutItems.push(payoutItem);
|
||||
});
|
||||
body.items = payoutItems;
|
||||
} else {
|
||||
|
@ -119,40 +129,41 @@ export class PayPal implements INodeType {
|
|||
body.items = itemsJson;
|
||||
}
|
||||
try {
|
||||
responseData = await paypalApiRequest.call(this, '/payments/payouts', 'POST', body);
|
||||
responseData = await payPalApiRequest.call(this, '/payments/payouts', 'POST', body);
|
||||
} catch (err) {
|
||||
throw new Error(`Paypal Error: ${JSON.stringify(err)}`);
|
||||
throw new Error(`PayPal Error: ${JSON.stringify(err)}`);
|
||||
}
|
||||
}
|
||||
if (operation === 'get') {
|
||||
const payoutItemId = this.getNodeParameter('payoutItemId', i) as string;
|
||||
try {
|
||||
responseData = await paypalApiRequest.call(this,`/payments/payouts-item/${payoutItemId}`, 'GET', {}, qs);
|
||||
} catch (err) {
|
||||
throw new Error(`Paypal Error: ${JSON.stringify(err)}`);
|
||||
}
|
||||
}
|
||||
if (operation === 'getAll') {
|
||||
const payoutBatchId = this.getNodeParameter('payoutBatchId', i) as string;
|
||||
const returnAll = this.getNodeParameter('returnAll', 0) as boolean;
|
||||
try {
|
||||
if (returnAll === true) {
|
||||
responseData = await paypalApiRequestAllItems.call(this, 'items', `/payments/payouts/${payoutBatchId}`, 'GET', {}, qs);
|
||||
responseData = await payPalApiRequestAllItems.call(this, 'items', `/payments/payouts/${payoutBatchId}`, 'GET', {}, qs);
|
||||
} else {
|
||||
qs.page_size = this.getNodeParameter('limit', i) as number;
|
||||
responseData = await paypalApiRequest.call(this,`/payments/payouts/${payoutBatchId}`, 'GET', {}, qs);
|
||||
responseData = await payPalApiRequest.call(this, `/payments/payouts/${payoutBatchId}`, 'GET', {}, qs);
|
||||
responseData = responseData.items;
|
||||
}
|
||||
} catch (err) {
|
||||
throw new Error(`Paypal Error: ${JSON.stringify(err)}`);
|
||||
throw new Error(`PayPal Error: ${JSON.stringify(err)}`);
|
||||
}
|
||||
}
|
||||
if (operation === 'delete') {
|
||||
} else if (resource === 'payoutItem') {
|
||||
if (operation === 'get') {
|
||||
const payoutItemId = this.getNodeParameter('payoutItemId', i) as string;
|
||||
try {
|
||||
responseData = await paypalApiRequest.call(this,`/payments/payouts-item/${payoutItemId}/cancel`, 'POST', {}, qs);
|
||||
responseData = await payPalApiRequest.call(this,`/payments/payouts-item/${payoutItemId}`, 'GET', {}, qs);
|
||||
} catch (err) {
|
||||
throw new Error(`Paypal Error: ${JSON.stringify(err)}`);
|
||||
throw new Error(`PayPal Error: ${JSON.stringify(err)}`);
|
||||
}
|
||||
}
|
||||
if (operation === 'cancel') {
|
||||
const payoutItemId = this.getNodeParameter('payoutItemId', i) as string;
|
||||
try {
|
||||
responseData = await payPalApiRequest.call(this,`/payments/payouts-item/${payoutItemId}/cancel`, 'POST', {}, qs);
|
||||
} catch (err) {
|
||||
throw new Error(`PayPal Error: ${JSON.stringify(err)}`);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
import { INodeProperties } from "n8n-workflow";
|
||||
|
||||
export const payoutOpeations = [
|
||||
export const payoutOperations = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
|
@ -21,17 +21,7 @@ export const payoutOpeations = [
|
|||
{
|
||||
name: 'Get',
|
||||
value: 'get',
|
||||
description: 'Show payout item details',
|
||||
},
|
||||
{
|
||||
name: 'Get All',
|
||||
value: 'getAll',
|
||||
description: 'Show payout batch details',
|
||||
},
|
||||
{
|
||||
name: 'Delete',
|
||||
value: 'delete',
|
||||
description: 'Cancels an unclaimed payout item, by ID.',
|
||||
description: 'Show batch payout details',
|
||||
},
|
||||
],
|
||||
default: 'create',
|
||||
|
@ -120,7 +110,7 @@ export const payoutFields = [
|
|||
{
|
||||
name: 'Email',
|
||||
value: 'email',
|
||||
description: 'The unencrypted email. Value is a string of up to 127 single-byte characters.',
|
||||
description: 'The unencrypted email.',
|
||||
},
|
||||
{
|
||||
name: 'PayPal ID',
|
||||
|
@ -137,7 +127,7 @@ export const payoutFields = [
|
|||
type: 'string',
|
||||
required: true,
|
||||
default: '',
|
||||
description: 'The receiver of the payment. Corresponds to the recipient_type value in the request. Max value of up to 127 single-byte characters.',
|
||||
description: 'The receiver of the payment. Corresponds to the recipient_type value<br />in the request. Max length: 127 characters.',
|
||||
},
|
||||
{
|
||||
displayName: 'Currency',
|
||||
|
@ -190,7 +180,7 @@ export const payoutFields = [
|
|||
type: 'string',
|
||||
required: false,
|
||||
default: '',
|
||||
description: 'The sender-specified note for notifications. Supports up to 4000 ASCII characters and 1000 non-ASCII characters.',
|
||||
description: 'The sender-specified note for notifications. Supports up to<br />4000 ASCII characters and 1000 non-ASCII characters.',
|
||||
},
|
||||
{
|
||||
displayName: 'Sender Item ID',
|
||||
|
@ -266,128 +256,164 @@ export const payoutFields = [
|
|||
name: 'emailSubject',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'The subject line for the email that PayPal sends when payment for a payout item completes. The subject line is the same for all recipients. Value is an alphanumeric string of up to 255 single-byte characters.',
|
||||
description: 'The subject line for the email that PayPal sends when payment<br />for a payout item completes. The subject line is the same for all<br />recipients. Max length: 255 characters.',
|
||||
},
|
||||
{
|
||||
displayName: 'Email Message',
|
||||
name: 'emailMessage',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'The email message that PayPal sends when the payout item completes. The message is the same for all recipients.',
|
||||
description: 'The email message that PayPal sends when the payout item completes.<br />The message is the same for all recipients.',
|
||||
},
|
||||
{
|
||||
displayName: 'Note',
|
||||
name: 'note',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'The payouts and item-level notes are concatenated in the email. The maximum combined length of the notes is 1000 characters.',
|
||||
description: 'The payouts and item-level notes are concatenated in the email.<br />Max length: 1000 characters.',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* payout:getAll */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* payout:get */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
{
|
||||
displayName: 'Payout Batch Id',
|
||||
name: 'payoutBatchId',
|
||||
type: 'string',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'payout',
|
||||
],
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
{
|
||||
displayName: 'Payout Batch Id',
|
||||
name: 'payoutBatchId',
|
||||
type: 'string',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'payout',
|
||||
],
|
||||
operation: [
|
||||
'get',
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'The ID of the payout for which to show details.',
|
||||
},
|
||||
description: 'The ID of the payout for which to show details.',
|
||||
},
|
||||
{
|
||||
displayName: 'Return All',
|
||||
name: 'returnAll',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'payout',
|
||||
],
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
{
|
||||
displayName: 'Return All',
|
||||
name: 'returnAll',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'payout',
|
||||
],
|
||||
operation: [
|
||||
'get',
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'If all results should be returned or only up to a given limit.',
|
||||
},
|
||||
description: 'If all results should be returned or only up to a given limit.',
|
||||
},
|
||||
{
|
||||
displayName: 'Limit',
|
||||
name: 'limit',
|
||||
type: 'number',
|
||||
typeOptions: {
|
||||
maxValue: 1000,
|
||||
minValue: 1
|
||||
},
|
||||
default: 100,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'payout',
|
||||
],
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
returnAll: [
|
||||
false,
|
||||
],
|
||||
{
|
||||
displayName: 'Limit',
|
||||
name: 'limit',
|
||||
type: 'number',
|
||||
typeOptions: {
|
||||
maxValue: 1000,
|
||||
minValue: 1
|
||||
},
|
||||
default: 100,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'payout',
|
||||
],
|
||||
operation: [
|
||||
'get',
|
||||
],
|
||||
returnAll: [
|
||||
false,
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'If all results should be returned or only up to a given limit.',
|
||||
},
|
||||
description: 'If all results should be returned or only up to a given limit.',
|
||||
},
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* payout:get */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Payout Item Id',
|
||||
name: 'payoutItemId',
|
||||
type: 'string',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'payout',
|
||||
],
|
||||
operation: [
|
||||
'get',
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'The ID of the payout item for which to show details.',
|
||||
},
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* payout:delete */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
{
|
||||
displayName: 'Payout Item Id',
|
||||
name: 'payoutItemId',
|
||||
type: 'string',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'payout',
|
||||
],
|
||||
operation: [
|
||||
'delete',
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'The ID of the payout item to cancel.',
|
||||
},
|
||||
] as INodeProperties[];
|
||||
|
||||
|
||||
export const payoutItemOperations = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'payoutItem',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Cancel',
|
||||
value: 'cancel',
|
||||
description: 'Cancels an unclaimed payout item',
|
||||
},
|
||||
{
|
||||
name: 'Get',
|
||||
value: 'get',
|
||||
description: 'Show payout item details',
|
||||
},
|
||||
],
|
||||
default: 'get',
|
||||
description: 'The operation to perform.',
|
||||
},
|
||||
] as INodeProperties[];
|
||||
|
||||
export const payoutItemFields = [
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* payoutItem:get */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Payout Item Id',
|
||||
name: 'payoutItemId',
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'payoutItem',
|
||||
],
|
||||
operation: [
|
||||
'get',
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'The ID of the payout item for which to show details.',
|
||||
},
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* payoutItem:cancel */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
{
|
||||
displayName: 'Payout Item Id',
|
||||
name: 'payoutItemId',
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'payoutItem',
|
||||
],
|
||||
operation: [
|
||||
'cancel',
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'The ID of the payout item to cancel.',
|
||||
},
|
||||
] as INodeProperties[];
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 4.9 KiB |
|
@ -199,11 +199,11 @@ export class Telegram implements INodeType {
|
|||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'getChat',
|
||||
'leaveChat',
|
||||
'getChatMember',
|
||||
'setChatDescription',
|
||||
'setChatTitle',
|
||||
'get',
|
||||
'leave',
|
||||
'member',
|
||||
'setDescription',
|
||||
'setTitle',
|
||||
'sendAudio',
|
||||
'sendChatAction',
|
||||
'sendDocument',
|
||||
|
@ -433,7 +433,7 @@ export class Telegram implements INodeType {
|
|||
},
|
||||
},
|
||||
required: true,
|
||||
description: 'Unique identifier for the target chat or username of the target<br />channel (in the format @channelusername).',
|
||||
description: 'Unique identifier for the target chat or username of the target<br />channel (in the format @channelusername). To find your chat id ask @get_id_bot.',
|
||||
},
|
||||
{
|
||||
displayName: 'Message ID',
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "n8n-nodes-base",
|
||||
"version": "0.30.0",
|
||||
"version": "0.31.0",
|
||||
"description": "Base nodes of n8n",
|
||||
"license": "SEE LICENSE IN LICENSE.md",
|
||||
"homepage": "https://n8n.io",
|
||||
|
@ -34,14 +34,17 @@
|
|||
"dist/credentials/ChargebeeApi.credentials.js",
|
||||
"dist/credentials/DropboxApi.credentials.js",
|
||||
"dist/credentials/FreshdeskApi.credentials.js",
|
||||
"dist/credentials/FileMaker.credentials.js",
|
||||
"dist/credentials/GithubApi.credentials.js",
|
||||
"dist/credentials/GitlabApi.credentials.js",
|
||||
"dist/credentials/GoogleApi.credentials.js",
|
||||
"dist/credentials/HttpBasicAuth.credentials.js",
|
||||
"dist/credentials/HttpDigestAuth.credentials.js",
|
||||
"dist/credentials/HttpHeaderAuth.credentials.js",
|
||||
"dist/credentials/IntercomApi.credentials.js",
|
||||
"dist/credentials/Imap.credentials.js",
|
||||
"dist/credentials/LinkFishApi.credentials.js",
|
||||
"dist/credentials/MailchimpApi.credentials.js",
|
||||
"dist/credentials/MailgunApi.credentials.js",
|
||||
"dist/credentials/MandrillApi.credentials.js",
|
||||
"dist/credentials/MattermostApi.credentials.js",
|
||||
|
@ -49,8 +52,8 @@
|
|||
"dist/credentials/NextCloudApi.credentials.js",
|
||||
"dist/credentials/OpenWeatherMapApi.credentials.js",
|
||||
"dist/credentials/PipedriveApi.credentials.js",
|
||||
"dist/credentials/Postgres.credentials.js",
|
||||
"dist/credentials/PayPalApi.credentials.js",
|
||||
"dist/credentials/Postgres.credentials.js",
|
||||
"dist/credentials/PayPalApi.credentials.js",
|
||||
"dist/credentials/Redis.credentials.js",
|
||||
"dist/credentials/RocketchatApi.credentials.js",
|
||||
"dist/credentials/SlackApi.credentials.js",
|
||||
|
@ -84,6 +87,7 @@
|
|||
"dist/nodes/EmailSend.node.js",
|
||||
"dist/nodes/ErrorTrigger.node.js",
|
||||
"dist/nodes/ExecuteCommand.node.js",
|
||||
"dist/nodes/FileMaker/FileMaker.node.js",
|
||||
"dist/nodes/Freshdesk/Freshdesk.node.js",
|
||||
"dist/nodes/Function.node.js",
|
||||
"dist/nodes/FunctionItem.node.js",
|
||||
|
@ -97,7 +101,9 @@
|
|||
"dist/nodes/HttpRequest.node.js",
|
||||
"dist/nodes/If.node.js",
|
||||
"dist/nodes/Interval.node.js",
|
||||
"dist/nodes/Intercom/Intercom.node.js",
|
||||
"dist/nodes/LinkFish/LinkFish.node.js",
|
||||
"dist/nodes/Mailchimp/Mailchimp.node.js",
|
||||
"dist/nodes/Mailgun/Mailgun.node.js",
|
||||
"dist/nodes/Mandrill/Mandrill.node.js",
|
||||
"dist/nodes/Mattermost/Mattermost.node.js",
|
||||
|
@ -109,9 +115,14 @@
|
|||
"dist/nodes/OpenWeatherMap.node.js",
|
||||
"dist/nodes/Pipedrive/Pipedrive.node.js",
|
||||
"dist/nodes/Pipedrive/PipedriveTrigger.node.js",
|
||||
<<<<<<< HEAD
|
||||
"dist/nodes/Postgres/Postgres.node.js",
|
||||
"dist/nodes/Paypal/PayPal.node.js",
|
||||
"dist/nodes/Paypal/PayPalTrigger.node.js",
|
||||
=======
|
||||
"dist/nodes/Postgres/Postgres.node.js",
|
||||
"dist/nodes/PayPal/PayPal.node.js",
|
||||
>>>>>>> master
|
||||
"dist/nodes/Rocketchat/Rocketchat.node.js",
|
||||
"dist/nodes/ReadBinaryFile.node.js",
|
||||
"dist/nodes/ReadBinaryFiles.node.js",
|
||||
|
@ -153,11 +164,11 @@
|
|||
"@types/node": "^10.10.1",
|
||||
"@types/nodemailer": "^4.6.5",
|
||||
"@types/redis": "^2.8.11",
|
||||
"@types/request-promise-native": "^1.0.15",
|
||||
"@types/request-promise-native": "~1.0.15",
|
||||
"@types/xml2js": "^0.4.3",
|
||||
"gulp": "^4.0.0",
|
||||
"jest": "^24.9.0",
|
||||
"n8n-workflow": "~0.15.0",
|
||||
"n8n-workflow": "~0.16.0",
|
||||
"ts-jest": "^24.0.2",
|
||||
"tslint": "^5.17.0",
|
||||
"typescript": "~3.5.2"
|
||||
|
@ -174,7 +185,7 @@
|
|||
"lodash.set": "^4.3.2",
|
||||
"lodash.unset": "^4.5.2",
|
||||
"mongodb": "^3.3.2",
|
||||
"n8n-core": "~0.15.0",
|
||||
"n8n-core": "~0.16.0",
|
||||
"nodemailer": "^5.1.1",
|
||||
"pdf-parse": "^1.1.1",
|
||||
"pg-promise": "^9.0.3",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "n8n-workflow",
|
||||
"version": "0.15.0",
|
||||
"version": "0.16.0",
|
||||
"description": "Workflow base code of n8n",
|
||||
"license": "SEE LICENSE IN LICENSE.md",
|
||||
"homepage": "https://n8n.io",
|
||||
|
|
|
@ -343,7 +343,7 @@ export interface INodeProperties {
|
|||
|
||||
export interface INodePropertyOptions {
|
||||
name: string;
|
||||
value: string;
|
||||
value: string | number;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
|
|
|
@ -321,8 +321,8 @@ export function getNodeParameters(nodePropertiesArray: INodeProperties[], nodeVa
|
|||
|
||||
if (returnDefaults === true) {
|
||||
// Set also when it has the default value
|
||||
if (['boolean', 'number'].includes(nodeProperties.type)) {
|
||||
// Boolean and numbers are special as false and 0 are valid values
|
||||
if (['boolean', 'number', 'options'].includes(nodeProperties.type)) {
|
||||
// Boolean, numbers and options are special as false and 0 are valid values
|
||||
// and should not be replaced with default value
|
||||
nodeParameters[nodeProperties.name] = nodeValues[nodeProperties.name] !== undefined ? nodeValues[nodeProperties.name] : nodeProperties.default;
|
||||
} else {
|
||||
|
|
|
@ -1061,6 +1061,15 @@ export class Workflow {
|
|||
return null;
|
||||
}
|
||||
|
||||
if (runExecutionData.resultData.lastNodeExecuted === node.name && runExecutionData.resultData.error !== undefined) {
|
||||
// The node did already fail. So throw an error here that it displays and logs it correctly.
|
||||
// Does get used by webhook and trigger nodes in case they throw an error that it is possible
|
||||
// to log the error and display in Editor-UI.
|
||||
const error = new Error(runExecutionData.resultData.error.message);
|
||||
error.stack = runExecutionData.resultData.error.stack;
|
||||
throw error;
|
||||
}
|
||||
|
||||
if (nodeType.executeSingle) {
|
||||
const returnPromises: Array<Promise<INodeExecutionData>> = [];
|
||||
|
||||
|
|
Loading…
Reference in a new issue