mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
Merge branch 'master' into elasticsearch-node
This commit is contained in:
commit
561f6e453e
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "n8n",
|
"name": "n8n",
|
||||||
"version": "0.119.0",
|
"version": "0.121.0",
|
||||||
"description": "n8n Workflow Automation Tool",
|
"description": "n8n Workflow Automation Tool",
|
||||||
"license": "SEE LICENSE IN LICENSE.md",
|
"license": "SEE LICENSE IN LICENSE.md",
|
||||||
"homepage": "https://n8n.io",
|
"homepage": "https://n8n.io",
|
||||||
|
@ -55,6 +55,7 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@oclif/dev-cli": "^1.22.2",
|
"@oclif/dev-cli": "^1.22.2",
|
||||||
"@types/basic-auth": "^1.1.2",
|
"@types/basic-auth": "^1.1.2",
|
||||||
|
"@types/bcryptjs": "^2.4.2",
|
||||||
"@types/bull": "^3.3.10",
|
"@types/bull": "^3.3.10",
|
||||||
"@types/compression": "1.0.1",
|
"@types/compression": "1.0.1",
|
||||||
"@types/connect-history-api-fallback": "^1.3.1",
|
"@types/connect-history-api-fallback": "^1.3.1",
|
||||||
|
@ -79,11 +80,11 @@
|
||||||
"typescript": "~3.9.7"
|
"typescript": "~3.9.7"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@node-rs/bcrypt": "^1.2.0",
|
|
||||||
"@oclif/command": "^1.5.18",
|
"@oclif/command": "^1.5.18",
|
||||||
"@oclif/errors": "^1.2.2",
|
"@oclif/errors": "^1.2.2",
|
||||||
"@types/jsonwebtoken": "^8.3.4",
|
"@types/jsonwebtoken": "^8.3.4",
|
||||||
"basic-auth": "^2.0.1",
|
"basic-auth": "^2.0.1",
|
||||||
|
"bcryptjs": "^2.4.3",
|
||||||
"body-parser": "^1.18.3",
|
"body-parser": "^1.18.3",
|
||||||
"body-parser-xml": "^1.1.0",
|
"body-parser-xml": "^1.1.0",
|
||||||
"bull": "^3.19.0",
|
"bull": "^3.19.0",
|
||||||
|
@ -104,10 +105,10 @@
|
||||||
"localtunnel": "^2.0.0",
|
"localtunnel": "^2.0.0",
|
||||||
"lodash.get": "^4.4.2",
|
"lodash.get": "^4.4.2",
|
||||||
"mysql2": "~2.2.0",
|
"mysql2": "~2.2.0",
|
||||||
"n8n-core": "~0.70.0",
|
"n8n-core": "~0.72.0",
|
||||||
"n8n-editor-ui": "~0.89.0",
|
"n8n-editor-ui": "~0.91.0",
|
||||||
"n8n-nodes-base": "~0.116.0",
|
"n8n-nodes-base": "~0.118.0",
|
||||||
"n8n-workflow": "~0.57.0",
|
"n8n-workflow": "~0.59.0",
|
||||||
"oauth-1.0a": "^2.2.6",
|
"oauth-1.0a": "^2.2.6",
|
||||||
"open": "^7.0.0",
|
"open": "^7.0.0",
|
||||||
"pg": "^8.3.0",
|
"pg": "^8.3.0",
|
||||||
|
|
|
@ -11,22 +11,6 @@ import { IPackageVersions } from './';
|
||||||
let versionCache: IPackageVersions | undefined;
|
let versionCache: IPackageVersions | undefined;
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Displays a message to the user
|
|
||||||
*
|
|
||||||
* @export
|
|
||||||
* @param {string} message The message to display
|
|
||||||
* @param {string} [level='log']
|
|
||||||
*/
|
|
||||||
export function logOutput(message: string, level = 'log'): void {
|
|
||||||
if (level === 'log') {
|
|
||||||
console.log(message);
|
|
||||||
} else if (level === 'error') {
|
|
||||||
console.error(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the base URL n8n is reachable from
|
* Returns the base URL n8n is reachable from
|
||||||
*
|
*
|
||||||
|
|
|
@ -4,11 +4,18 @@ import {
|
||||||
} from 'n8n-core';
|
} from 'n8n-core';
|
||||||
import {
|
import {
|
||||||
ICredentialType,
|
ICredentialType,
|
||||||
|
ILogger,
|
||||||
INodeType,
|
INodeType,
|
||||||
INodeTypeData,
|
INodeTypeData,
|
||||||
|
LoggerProxy,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
import * as config from '../config';
|
import * as config from '../config';
|
||||||
|
|
||||||
|
import {
|
||||||
|
getLogger,
|
||||||
|
} from '../src/Logger';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
access as fsAccess,
|
access as fsAccess,
|
||||||
readdir as fsReaddir,
|
readdir as fsReaddir,
|
||||||
|
@ -31,7 +38,12 @@ class LoadNodesAndCredentialsClass {
|
||||||
|
|
||||||
nodeModulesPath = '';
|
nodeModulesPath = '';
|
||||||
|
|
||||||
|
logger: ILogger;
|
||||||
|
|
||||||
async init() {
|
async init() {
|
||||||
|
this.logger = getLogger();
|
||||||
|
LoggerProxy.init(this.logger);
|
||||||
|
|
||||||
// Get the path to the node-modules folder to be later able
|
// Get the path to the node-modules folder to be later able
|
||||||
// to load the credentials and nodes
|
// to load the credentials and nodes
|
||||||
const checkPaths = [
|
const checkPaths = [
|
||||||
|
@ -171,6 +183,10 @@ class LoadNodesAndCredentialsClass {
|
||||||
tempNode.description.icon = 'file:' + path.join(path.dirname(filePath), tempNode.description.icon.substr(5));
|
tempNode.description.icon = 'file:' + path.join(path.dirname(filePath), tempNode.description.icon.substr(5));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (tempNode.executeSingle) {
|
||||||
|
this.logger.warn(`"executeSingle" will get deprecated soon. Please update the code of node "${packageName}.${nodeName}" to use "execute" instead!`, { filePath });
|
||||||
|
}
|
||||||
|
|
||||||
if (this.includeNodes !== undefined && !this.includeNodes.includes(fullNodeName)) {
|
if (this.includeNodes !== undefined && !this.includeNodes.includes(fullNodeName)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,6 @@ import {
|
||||||
resolve as pathResolve,
|
resolve as pathResolve,
|
||||||
} from 'path';
|
} from 'path';
|
||||||
import {
|
import {
|
||||||
getConnection,
|
|
||||||
getConnectionManager,
|
getConnectionManager,
|
||||||
In,
|
In,
|
||||||
} from 'typeorm';
|
} from 'typeorm';
|
||||||
|
@ -22,7 +21,9 @@ import { RequestOptions } from 'oauth-1.0a';
|
||||||
import * as csrf from 'csrf';
|
import * as csrf from 'csrf';
|
||||||
import * as requestPromise from 'request-promise-native';
|
import * as requestPromise from 'request-promise-native';
|
||||||
import { createHmac } from 'crypto';
|
import { createHmac } from 'crypto';
|
||||||
import { compare } from '@node-rs/bcrypt';
|
// IMPORTANT! Do not switch to anther bcrypt library unless really necessary and
|
||||||
|
// tested with all possible systems like Windows, Alpine on ARM, FreeBSD, ...
|
||||||
|
import { compare } from 'bcryptjs';
|
||||||
import * as promClient from 'prom-client';
|
import * as promClient from 'prom-client';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
@ -572,6 +573,7 @@ class App {
|
||||||
|
|
||||||
const newWorkflowData = req.body as IWorkflowBase;
|
const newWorkflowData = req.body as IWorkflowBase;
|
||||||
const id = req.params.id;
|
const id = req.params.id;
|
||||||
|
newWorkflowData.id = id;
|
||||||
|
|
||||||
await this.externalHooks.run('workflow.update', [newWorkflowData]);
|
await this.externalHooks.run('workflow.update', [newWorkflowData]);
|
||||||
|
|
||||||
|
@ -716,6 +718,7 @@ class App {
|
||||||
// get generated dynamically
|
// get generated dynamically
|
||||||
this.app.get(`/${this.restEndpoint}/node-parameter-options`, ResponseHelper.send(async (req: express.Request, res: express.Response): Promise<INodePropertyOptions[]> => {
|
this.app.get(`/${this.restEndpoint}/node-parameter-options`, ResponseHelper.send(async (req: express.Request, res: express.Response): Promise<INodePropertyOptions[]> => {
|
||||||
const nodeType = req.query.nodeType as string;
|
const nodeType = req.query.nodeType as string;
|
||||||
|
const path = req.query.path as string;
|
||||||
let credentials: INodeCredentials | undefined = undefined;
|
let credentials: INodeCredentials | undefined = undefined;
|
||||||
const currentNodeParameters = JSON.parse('' + req.query.currentNodeParameters) as INodeParameters;
|
const currentNodeParameters = JSON.parse('' + req.query.currentNodeParameters) as INodeParameters;
|
||||||
if (req.query.credentials !== undefined) {
|
if (req.query.credentials !== undefined) {
|
||||||
|
@ -725,7 +728,7 @@ class App {
|
||||||
|
|
||||||
const nodeTypes = NodeTypes();
|
const nodeTypes = NodeTypes();
|
||||||
|
|
||||||
const loadDataInstance = new LoadNodeParameterOptions(nodeType, nodeTypes, JSON.parse('' + req.query.currentNodeParameters), credentials!);
|
const loadDataInstance = new LoadNodeParameterOptions(nodeType, nodeTypes, path, JSON.parse('' + req.query.currentNodeParameters), credentials!);
|
||||||
|
|
||||||
const workflowData = loadDataInstance.getWorkflowData() as IWorkflowBase;
|
const workflowData = loadDataInstance.getWorkflowData() as IWorkflowBase;
|
||||||
const workflowCredentials = await WorkflowCredentials(workflowData.nodes);
|
const workflowCredentials = await WorkflowCredentials(workflowData.nodes);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "n8n-core",
|
"name": "n8n-core",
|
||||||
"version": "0.70.0",
|
"version": "0.72.0",
|
||||||
"description": "Core functionality of n8n",
|
"description": "Core functionality of n8n",
|
||||||
"license": "SEE LICENSE IN LICENSE.md",
|
"license": "SEE LICENSE IN LICENSE.md",
|
||||||
"homepage": "https://n8n.io",
|
"homepage": "https://n8n.io",
|
||||||
|
@ -47,7 +47,7 @@
|
||||||
"file-type": "^14.6.2",
|
"file-type": "^14.6.2",
|
||||||
"lodash.get": "^4.4.2",
|
"lodash.get": "^4.4.2",
|
||||||
"mime-types": "^2.1.27",
|
"mime-types": "^2.1.27",
|
||||||
"n8n-workflow": "~0.57.0",
|
"n8n-workflow": "~0.59.0",
|
||||||
"oauth-1.0a": "^2.2.6",
|
"oauth-1.0a": "^2.2.6",
|
||||||
"p-cancelable": "^2.0.0",
|
"p-cancelable": "^2.0.0",
|
||||||
"request": "^2.88.2",
|
"request": "^2.88.2",
|
||||||
|
|
|
@ -18,10 +18,12 @@ const TEMP_WORKFLOW_NAME = 'Temp-Workflow';
|
||||||
|
|
||||||
|
|
||||||
export class LoadNodeParameterOptions {
|
export class LoadNodeParameterOptions {
|
||||||
|
path: string;
|
||||||
workflow: Workflow;
|
workflow: Workflow;
|
||||||
|
|
||||||
|
|
||||||
constructor(nodeTypeName: string, nodeTypes: INodeTypes, currentNodeParameters: INodeParameters, credentials?: INodeCredentials) {
|
constructor(nodeTypeName: string, nodeTypes: INodeTypes, path: string, currentNodeParameters: INodeParameters, credentials?: INodeCredentials) {
|
||||||
|
this.path = path;
|
||||||
const nodeType = nodeTypes.getByName(nodeTypeName);
|
const nodeType = nodeTypes.getByName(nodeTypeName);
|
||||||
|
|
||||||
if (nodeType === undefined) {
|
if (nodeType === undefined) {
|
||||||
|
@ -89,7 +91,7 @@ export class LoadNodeParameterOptions {
|
||||||
throw new Error(`The node-type "${node!.type}" does not have the method "${methodName}" defined!`);
|
throw new Error(`The node-type "${node!.type}" does not have the method "${methodName}" defined!`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const thisArgs = NodeExecuteFunctions.getLoadOptionsFunctions(this.workflow, node!, additionalData);
|
const thisArgs = NodeExecuteFunctions.getLoadOptionsFunctions(this.workflow, node!, this.path, additionalData);
|
||||||
|
|
||||||
return nodeType!.methods.loadOptions[methodName].call(thisArgs);
|
return nodeType!.methods.loadOptions[methodName].call(thisArgs);
|
||||||
}
|
}
|
||||||
|
|
|
@ -691,7 +691,7 @@ export function getExecuteFunctions(workflow: Workflow, runExecutionData: IRunEx
|
||||||
return continueOnFail(node);
|
return continueOnFail(node);
|
||||||
},
|
},
|
||||||
evaluateExpression: (expression: string, itemIndex: number) => {
|
evaluateExpression: (expression: string, itemIndex: number) => {
|
||||||
return workflow.expression.resolveSimpleParameterValue('=' + expression, runExecutionData, runIndex, itemIndex, node.name, connectionInputData, mode);
|
return workflow.expression.resolveSimpleParameterValue('=' + expression, {}, runExecutionData, runIndex, itemIndex, node.name, connectionInputData, mode);
|
||||||
},
|
},
|
||||||
async executeWorkflow(workflowInfo: IExecuteWorkflowInfo, inputData?: INodeExecutionData[]): Promise<any> { // tslint:disable-line:no-any
|
async executeWorkflow(workflowInfo: IExecuteWorkflowInfo, inputData?: INodeExecutionData[]): Promise<any> { // tslint:disable-line:no-any
|
||||||
return additionalData.executeWorkflow(workflowInfo, additionalData, inputData);
|
return additionalData.executeWorkflow(workflowInfo, additionalData, inputData);
|
||||||
|
@ -742,7 +742,7 @@ export function getExecuteFunctions(workflow: Workflow, runExecutionData: IRunEx
|
||||||
return getWorkflowMetadata(workflow);
|
return getWorkflowMetadata(workflow);
|
||||||
},
|
},
|
||||||
getWorkflowDataProxy: (itemIndex: number): IWorkflowDataProxyData => {
|
getWorkflowDataProxy: (itemIndex: number): IWorkflowDataProxyData => {
|
||||||
const dataProxy = new WorkflowDataProxy(workflow, runExecutionData, runIndex, itemIndex, node.name, connectionInputData, mode);
|
const dataProxy = new WorkflowDataProxy(workflow, runExecutionData, runIndex, itemIndex, node.name, connectionInputData, {}, mode);
|
||||||
return dataProxy.getDataProxy();
|
return dataProxy.getDataProxy();
|
||||||
},
|
},
|
||||||
getWorkflowStaticData(type: string): IDataObject {
|
getWorkflowStaticData(type: string): IDataObject {
|
||||||
|
@ -789,7 +789,7 @@ export function getExecuteSingleFunctions(workflow: Workflow, runExecutionData:
|
||||||
},
|
},
|
||||||
evaluateExpression: (expression: string, evaluateItemIndex: number | undefined) => {
|
evaluateExpression: (expression: string, evaluateItemIndex: number | undefined) => {
|
||||||
evaluateItemIndex = evaluateItemIndex === undefined ? itemIndex : evaluateItemIndex;
|
evaluateItemIndex = evaluateItemIndex === undefined ? itemIndex : evaluateItemIndex;
|
||||||
return workflow.expression.resolveSimpleParameterValue('=' + expression, runExecutionData, runIndex, evaluateItemIndex, node.name, connectionInputData, mode);
|
return workflow.expression.resolveSimpleParameterValue('=' + expression, {}, runExecutionData, runIndex, evaluateItemIndex, node.name, connectionInputData, mode);
|
||||||
},
|
},
|
||||||
getContext(type: string): IContextObject {
|
getContext(type: string): IContextObject {
|
||||||
return NodeHelpers.getContext(runExecutionData, type, node);
|
return NodeHelpers.getContext(runExecutionData, type, node);
|
||||||
|
@ -841,7 +841,7 @@ export function getExecuteSingleFunctions(workflow: Workflow, runExecutionData:
|
||||||
return getWorkflowMetadata(workflow);
|
return getWorkflowMetadata(workflow);
|
||||||
},
|
},
|
||||||
getWorkflowDataProxy: (): IWorkflowDataProxyData => {
|
getWorkflowDataProxy: (): IWorkflowDataProxyData => {
|
||||||
const dataProxy = new WorkflowDataProxy(workflow, runExecutionData, runIndex, itemIndex, node.name, connectionInputData, mode);
|
const dataProxy = new WorkflowDataProxy(workflow, runExecutionData, runIndex, itemIndex, node.name, connectionInputData, {}, mode);
|
||||||
return dataProxy.getDataProxy();
|
return dataProxy.getDataProxy();
|
||||||
},
|
},
|
||||||
getWorkflowStaticData(type: string): IDataObject {
|
getWorkflowStaticData(type: string): IDataObject {
|
||||||
|
@ -871,18 +871,20 @@ export function getExecuteSingleFunctions(workflow: Workflow, runExecutionData:
|
||||||
* @param {IWorkflowExecuteAdditionalData} additionalData
|
* @param {IWorkflowExecuteAdditionalData} additionalData
|
||||||
* @returns {ILoadOptionsFunctions}
|
* @returns {ILoadOptionsFunctions}
|
||||||
*/
|
*/
|
||||||
export function getLoadOptionsFunctions(workflow: Workflow, node: INode, additionalData: IWorkflowExecuteAdditionalData): ILoadOptionsFunctions {
|
export function getLoadOptionsFunctions(workflow: Workflow, node: INode, path: string, additionalData: IWorkflowExecuteAdditionalData): ILoadOptionsFunctions {
|
||||||
return ((workflow: Workflow, node: INode) => {
|
return ((workflow: Workflow, node: INode, path: string) => {
|
||||||
const that = {
|
const that = {
|
||||||
getCredentials(type: string): ICredentialDataDecryptedObject | undefined {
|
getCredentials(type: string): ICredentialDataDecryptedObject | undefined {
|
||||||
return getCredentials(workflow, node, type, additionalData, 'internal');
|
return getCredentials(workflow, node, type, additionalData, 'internal');
|
||||||
},
|
},
|
||||||
getCurrentNodeParameter: (parameterName: string): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] | object | undefined => {
|
getCurrentNodeParameter: (parameterPath: string): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] | object | undefined => {
|
||||||
const nodeParameters = additionalData.currentNodeParameters;
|
const nodeParameters = additionalData.currentNodeParameters;
|
||||||
if (nodeParameters && nodeParameters[parameterName]) {
|
|
||||||
return nodeParameters[parameterName];
|
if (parameterPath.charAt(0) === '&') {
|
||||||
|
parameterPath = `${path.split('.').slice(1, -1).join('.')}.${parameterPath.slice(1)}`;
|
||||||
}
|
}
|
||||||
return undefined;
|
|
||||||
|
return get(nodeParameters, parameterPath);
|
||||||
},
|
},
|
||||||
getCurrentNodeParameters: (): INodeParameters | undefined => {
|
getCurrentNodeParameters: (): INodeParameters | undefined => {
|
||||||
return additionalData.currentNodeParameters;
|
return additionalData.currentNodeParameters;
|
||||||
|
@ -915,7 +917,7 @@ export function getLoadOptionsFunctions(workflow: Workflow, node: INode, additio
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
return that;
|
return that;
|
||||||
})(workflow, node);
|
})(workflow, node, path);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "n8n-editor-ui",
|
"name": "n8n-editor-ui",
|
||||||
"version": "0.89.0",
|
"version": "0.91.0",
|
||||||
"description": "Workflow Editor UI for n8n",
|
"description": "Workflow Editor UI for n8n",
|
||||||
"license": "SEE LICENSE IN LICENSE.md",
|
"license": "SEE LICENSE IN LICENSE.md",
|
||||||
"homepage": "https://n8n.io",
|
"homepage": "https://n8n.io",
|
||||||
|
@ -65,7 +65,7 @@
|
||||||
"lodash.debounce": "^4.0.8",
|
"lodash.debounce": "^4.0.8",
|
||||||
"lodash.get": "^4.4.2",
|
"lodash.get": "^4.4.2",
|
||||||
"lodash.set": "^4.3.2",
|
"lodash.set": "^4.3.2",
|
||||||
"n8n-workflow": "~0.57.0",
|
"n8n-workflow": "~0.59.0",
|
||||||
"node-sass": "^4.12.0",
|
"node-sass": "^4.12.0",
|
||||||
"normalize-wheel": "^1.0.1",
|
"normalize-wheel": "^1.0.1",
|
||||||
"prismjs": "^1.17.1",
|
"prismjs": "^1.17.1",
|
||||||
|
|
|
@ -131,7 +131,7 @@ export interface IRestApi {
|
||||||
getSettings(): Promise<IN8nUISettings>;
|
getSettings(): Promise<IN8nUISettings>;
|
||||||
getNodeTypes(): Promise<INodeTypeDescription[]>;
|
getNodeTypes(): Promise<INodeTypeDescription[]>;
|
||||||
getNodesInformation(nodeList: string[]): Promise<INodeTypeDescription[]>;
|
getNodesInformation(nodeList: string[]): Promise<INodeTypeDescription[]>;
|
||||||
getNodeParameterOptions(nodeType: string, methodName: string, currentNodeParameters: INodeParameters, credentials?: INodeCredentials): Promise<INodePropertyOptions[]>;
|
getNodeParameterOptions(nodeType: string, path: string, methodName: string, currentNodeParameters: INodeParameters, credentials?: INodeCredentials): Promise<INodePropertyOptions[]>;
|
||||||
removeTestWebhook(workflowId: string): Promise<boolean>;
|
removeTestWebhook(workflowId: string): Promise<boolean>;
|
||||||
runWorkflow(runData: IStartRunData): Promise<IExecutionPushResponse>;
|
runWorkflow(runData: IStartRunData): Promise<IExecutionPushResponse>;
|
||||||
createNewWorkflow(sendData: IWorkflowData): Promise<IWorkflowDb>;
|
createNewWorkflow(sendData: IWorkflowData): Promise<IWorkflowDb>;
|
||||||
|
@ -444,4 +444,4 @@ export interface ILinkMenuItemProperties {
|
||||||
icon: string;
|
icon: string;
|
||||||
href: string;
|
href: string;
|
||||||
newWindow?: boolean;
|
newWindow?: boolean;
|
||||||
}
|
}
|
||||||
|
|
|
@ -177,7 +177,11 @@ export default mixins(genericHelpers)
|
||||||
} else if (optionParameter.typeOptions !== undefined && optionParameter.typeOptions.multipleValues === true) {
|
} else if (optionParameter.typeOptions !== undefined && optionParameter.typeOptions.multipleValues === true) {
|
||||||
// Multiple values are allowed so append option to array
|
// Multiple values are allowed so append option to array
|
||||||
newParameterValue[optionParameter.name] = get(this.nodeValues, `${this.path}.${optionParameter.name}`, []);
|
newParameterValue[optionParameter.name] = get(this.nodeValues, `${this.path}.${optionParameter.name}`, []);
|
||||||
(newParameterValue[optionParameter.name] as INodeParameters[]).push(JSON.parse(JSON.stringify(optionParameter.default)));
|
if (Array.isArray(optionParameter.default)) {
|
||||||
|
(newParameterValue[optionParameter.name] as INodeParameters[]).push(...JSON.parse(JSON.stringify(optionParameter.default)));
|
||||||
|
} else if (optionParameter.default !== '' && typeof optionParameter.default !== 'object') {
|
||||||
|
(newParameterValue[optionParameter.name] as INodeParameters[]).push(JSON.parse(JSON.stringify(optionParameter.default)));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Add a new option
|
// Add a new option
|
||||||
newParameterValue[optionParameter.name] = JSON.parse(JSON.stringify(optionParameter.default));
|
newParameterValue[optionParameter.name] = JSON.parse(JSON.stringify(optionParameter.default));
|
||||||
|
|
|
@ -37,9 +37,6 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import {
|
import {
|
||||||
INodeIssues,
|
|
||||||
INodeIssueData,
|
|
||||||
INodeIssueObjectProperty,
|
|
||||||
INodeTypeDescription,
|
INodeTypeDescription,
|
||||||
INodeParameters,
|
INodeParameters,
|
||||||
INodeProperties,
|
INodeProperties,
|
||||||
|
@ -409,9 +406,9 @@ export default mixins(
|
||||||
name: node.name,
|
name: node.name,
|
||||||
value: nodeParameters,
|
value: nodeParameters,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.$store.commit('setNodeParameters', updateInformation);
|
this.$store.commit('setNodeParameters', updateInformation);
|
||||||
|
|
||||||
this.$externalHooks().run('nodeSettings.valueChanged', { parameterPath, newValue, parameters: this.parameters, oldNodeParameters });
|
this.$externalHooks().run('nodeSettings.valueChanged', { parameterPath, newValue, parameters: this.parameters, oldNodeParameters });
|
||||||
|
|
||||||
this.updateNodeParameterIssues(node, nodeType);
|
this.updateNodeParameterIssues(node, nodeType);
|
||||||
|
|
|
@ -230,7 +230,7 @@ export default mixins(
|
||||||
|
|
||||||
// Get the resolved parameter values of the current node
|
// Get the resolved parameter values of the current node
|
||||||
const currentNodeParameters = this.$store.getters.activeNode.parameters;
|
const currentNodeParameters = this.$store.getters.activeNode.parameters;
|
||||||
const resolvedNodeParameters = this.getResolveNodeParameters(currentNodeParameters);
|
const resolvedNodeParameters = this.resolveParameter(currentNodeParameters);
|
||||||
|
|
||||||
const returnValues: string[] = [];
|
const returnValues: string[] = [];
|
||||||
for (const parameterPath of loadOptionsDependsOn) {
|
for (const parameterPath of loadOptionsDependsOn) {
|
||||||
|
@ -456,21 +456,6 @@ export default mixins(
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
getResolveNodeParameters (nodeParameters: INodeParameters): INodeParameters {
|
|
||||||
const returnData: INodeParameters = {};
|
|
||||||
for (const key of Object.keys(nodeParameters)) {
|
|
||||||
if (Array.isArray(nodeParameters[key])) {
|
|
||||||
returnData[key] = (nodeParameters[key] as string[]).map(value => {
|
|
||||||
return this.resolveExpression(value as string) as string;
|
|
||||||
});
|
|
||||||
} else if (typeof nodeParameters[key] === 'object') {
|
|
||||||
returnData[key] = this.getResolveNodeParameters(nodeParameters[key] as INodeParameters);
|
|
||||||
} else {
|
|
||||||
returnData[key] = this.resolveExpression(nodeParameters[key] as string);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return returnData;
|
|
||||||
},
|
|
||||||
async loadRemoteParameterOptions () {
|
async loadRemoteParameterOptions () {
|
||||||
if (this.node === null || this.remoteMethod === undefined || this.remoteParameterOptionsLoading) {
|
if (this.node === null || this.remoteMethod === undefined || this.remoteParameterOptionsLoading) {
|
||||||
return;
|
return;
|
||||||
|
@ -481,10 +466,10 @@ export default mixins(
|
||||||
|
|
||||||
// Get the resolved parameter values of the current node
|
// Get the resolved parameter values of the current node
|
||||||
const currentNodeParameters = this.$store.getters.activeNode.parameters;
|
const currentNodeParameters = this.$store.getters.activeNode.parameters;
|
||||||
const resolvedNodeParameters = this.getResolveNodeParameters(currentNodeParameters);
|
const resolvedNodeParameters = this.resolveParameter(currentNodeParameters) as INodeParameters;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const options = await this.restApi().getNodeParameterOptions(this.node.type, this.remoteMethod, resolvedNodeParameters, this.node.credentials);
|
const options = await this.restApi().getNodeParameterOptions(this.node.type, this.path, this.remoteMethod, resolvedNodeParameters, this.node.credentials);
|
||||||
this.remoteParameterOptions.push.apply(this.remoteParameterOptions, options);
|
this.remoteParameterOptions.push.apply(this.remoteParameterOptions, options);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.remoteParameterOptionsLoadingIssues = error.message;
|
this.remoteParameterOptionsLoadingIssues = error.message;
|
||||||
|
|
|
@ -76,26 +76,27 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Vue from 'vue';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
INodeParameters,
|
||||||
INodeProperties,
|
INodeProperties,
|
||||||
|
NodeParameterValue,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
import { IUpdateInformation } from '@/Interface';
|
import { IUpdateInformation } from '@/Interface';
|
||||||
|
|
||||||
import MultipleParameter from '@/components/MultipleParameter.vue';
|
import MultipleParameter from '@/components/MultipleParameter.vue';
|
||||||
import { genericHelpers } from '@/components/mixins/genericHelpers';
|
import { genericHelpers } from '@/components/mixins/genericHelpers';
|
||||||
import { nodeHelpers } from '@/components/mixins/nodeHelpers';
|
import { workflowHelpers } from '@/components/mixins/workflowHelpers';
|
||||||
import ParameterInputFull from '@/components/ParameterInputFull.vue';
|
import ParameterInputFull from '@/components/ParameterInputFull.vue';
|
||||||
|
|
||||||
import { get } from 'lodash';
|
import { get, set } from 'lodash';
|
||||||
|
|
||||||
import mixins from 'vue-typed-mixins';
|
import mixins from 'vue-typed-mixins';
|
||||||
|
|
||||||
export default mixins(
|
export default mixins(
|
||||||
genericHelpers,
|
genericHelpers,
|
||||||
nodeHelpers,
|
workflowHelpers,
|
||||||
)
|
)
|
||||||
.extend({
|
.extend({
|
||||||
name: 'ParameterInputList',
|
name: 'ParameterInputList',
|
||||||
|
@ -110,9 +111,12 @@ export default mixins(
|
||||||
'hideDelete', // boolean
|
'hideDelete', // boolean
|
||||||
],
|
],
|
||||||
computed: {
|
computed: {
|
||||||
filteredParameters (): INodeProperties {
|
filteredParameters (): INodeProperties[] {
|
||||||
return this.parameters.filter((parameter: INodeProperties) => this.displayNodeParameter(parameter));
|
return this.parameters.filter((parameter: INodeProperties) => this.displayNodeParameter(parameter));
|
||||||
},
|
},
|
||||||
|
filteredParameterNames (): string[] {
|
||||||
|
return this.filteredParameters.map(parameter => parameter.name);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
multipleValues (parameter: INodeProperties): boolean {
|
multipleValues (parameter: INodeProperties): boolean {
|
||||||
|
@ -157,12 +161,75 @@ export default mixins(
|
||||||
// If it is not defined no need to do a proper check
|
// If it is not defined no need to do a proper check
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const nodeValues: INodeParameters = {};
|
||||||
|
let rawValues = this.nodeValues;
|
||||||
|
if (this.path) {
|
||||||
|
rawValues = get(this.nodeValues, this.path);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve expressions
|
||||||
|
const resolveKeys = Object.keys(rawValues);
|
||||||
|
let key: string;
|
||||||
|
let i = 0;
|
||||||
|
let parameterGotResolved = false;
|
||||||
|
do {
|
||||||
|
key = resolveKeys.shift() as string;
|
||||||
|
if (typeof rawValues[key] === 'string' && rawValues[key].charAt(0) === '=') {
|
||||||
|
// Contains an expression that
|
||||||
|
if (rawValues[key].includes('$parameter') && resolveKeys.some(parameterName => rawValues[key].includes(parameterName))) {
|
||||||
|
// Contains probably an expression of a missing parameter so skip
|
||||||
|
resolveKeys.push(key);
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
// Contains probably no expression with a missing parameter so resolve
|
||||||
|
nodeValues[key] = this.resolveExpression(rawValues[key], nodeValues) as NodeParameterValue;
|
||||||
|
parameterGotResolved = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Does not contain an expression, add directly
|
||||||
|
nodeValues[key] = rawValues[key];
|
||||||
|
}
|
||||||
|
// TODO: Think about how to calculate this best
|
||||||
|
if (i++ > 50) {
|
||||||
|
// Make sure we do not get caught
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while(resolveKeys.length !== 0);
|
||||||
|
|
||||||
|
if (parameterGotResolved === true) {
|
||||||
|
if (this.path) {
|
||||||
|
rawValues = JSON.parse(JSON.stringify(this.nodeValues));
|
||||||
|
set(rawValues, this.path, nodeValues);
|
||||||
|
return this.displayParameter(rawValues, parameter, this.path);
|
||||||
|
} else {
|
||||||
|
return this.displayParameter(nodeValues, parameter, '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return this.displayParameter(this.nodeValues, parameter, this.path);
|
return this.displayParameter(this.nodeValues, parameter, this.path);
|
||||||
},
|
},
|
||||||
valueChanged (parameterData: IUpdateInformation): void {
|
valueChanged (parameterData: IUpdateInformation): void {
|
||||||
this.$emit('valueChanged', parameterData);
|
this.$emit('valueChanged', parameterData);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
watch: {
|
||||||
|
filteredParameterNames(newValue, oldValue) {
|
||||||
|
// After a parameter does not get displayed anymore make sure that its value gets removed
|
||||||
|
// Is only needed for the edge-case when a parameter gets displayed depending on another field
|
||||||
|
// which contains an expression.
|
||||||
|
for (const parameter of oldValue) {
|
||||||
|
if (!newValue.includes(parameter)) {
|
||||||
|
const parameterData = {
|
||||||
|
name: `${this.path}.${parameter}`,
|
||||||
|
node: this.$store.getters.activeNode.name,
|
||||||
|
value: undefined,
|
||||||
|
};
|
||||||
|
this.$emit('valueChanged', parameterData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
beforeCreate: function () { // tslint:disable-line
|
beforeCreate: function () { // tslint:disable-line
|
||||||
// Because we have a circular dependency on CollectionParameter import it here
|
// Because we have a circular dependency on CollectionParameter import it here
|
||||||
// to not break Vue.
|
// to not break Vue.
|
||||||
|
|
|
@ -379,7 +379,7 @@ export default mixins(
|
||||||
return returnData;
|
return returnData;
|
||||||
}
|
}
|
||||||
|
|
||||||
const dataProxy = new WorkflowDataProxy(workflow, runExecutionData, runIndex, itemIndex, nodeName, connectionInputData, 'manual');
|
const dataProxy = new WorkflowDataProxy(workflow, runExecutionData, runIndex, itemIndex, nodeName, connectionInputData, {}, 'manual');
|
||||||
const proxy = dataProxy.getDataProxy();
|
const proxy = dataProxy.getDataProxy();
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
|
|
|
@ -157,9 +157,10 @@ export const restApi = Vue.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
// Returns all the parameter options from the server
|
// Returns all the parameter options from the server
|
||||||
getNodeParameterOptions: (nodeType: string, methodName: string, currentNodeParameters: INodeParameters, credentials?: INodeCredentials): Promise<INodePropertyOptions[]> => {
|
getNodeParameterOptions: (nodeType: string, path: string, methodName: string, currentNodeParameters: INodeParameters, credentials?: INodeCredentials): Promise<INodePropertyOptions[]> => {
|
||||||
const sendData = {
|
const sendData = {
|
||||||
nodeType,
|
nodeType,
|
||||||
|
path,
|
||||||
methodName,
|
methodName,
|
||||||
credentials,
|
credentials,
|
||||||
currentNodeParameters,
|
currentNodeParameters,
|
||||||
|
|
|
@ -2,9 +2,12 @@ import { PLACEHOLDER_EMPTY_WORKFLOW_ID } from '@/constants';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
IConnections,
|
IConnections,
|
||||||
|
IDataObject,
|
||||||
INode,
|
INode,
|
||||||
INodeExecutionData,
|
INodeExecutionData,
|
||||||
INodeIssues,
|
INodeIssues,
|
||||||
|
INodeParameters,
|
||||||
|
NodeParameterValue,
|
||||||
INodeType,
|
INodeType,
|
||||||
INodeTypes,
|
INodeTypes,
|
||||||
INodeTypeData,
|
INodeTypeData,
|
||||||
|
@ -335,8 +338,8 @@ export const workflowHelpers = mixins(
|
||||||
return nodeData;
|
return nodeData;
|
||||||
},
|
},
|
||||||
|
|
||||||
// Executes the given expression and returns its value
|
|
||||||
resolveExpression (expression: string) {
|
resolveParameter(parameter: NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[]) {
|
||||||
const inputIndex = 0;
|
const inputIndex = 0;
|
||||||
const itemIndex = 0;
|
const itemIndex = 0;
|
||||||
const runIndex = 0;
|
const runIndex = 0;
|
||||||
|
@ -362,7 +365,22 @@ export const workflowHelpers = mixins(
|
||||||
connectionInputData = [];
|
connectionInputData = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
return workflow.expression.getParameterValue(expression, runExecutionData, runIndex, itemIndex, activeNode.name, connectionInputData, 'manual', true);
|
return workflow.expression.getParameterValue(parameter, runExecutionData, runIndex, itemIndex, activeNode.name, connectionInputData, 'manual', false) as IDataObject;
|
||||||
|
},
|
||||||
|
|
||||||
|
resolveExpression(expression: string, siblingParameters: INodeParameters = {}) {
|
||||||
|
|
||||||
|
const parameters = {
|
||||||
|
'__xxxxxxx__': expression,
|
||||||
|
...siblingParameters,
|
||||||
|
};
|
||||||
|
const returnData = this.resolveParameter(parameters) as IDataObject;
|
||||||
|
|
||||||
|
if (typeof returnData['__xxxxxxx__'] === 'object') {
|
||||||
|
const workflow = this.getWorkflow();
|
||||||
|
return workflow.expression.convertObjectValueToString(returnData['__xxxxxxx__'] as object);
|
||||||
|
}
|
||||||
|
return returnData['__xxxxxxx__'];
|
||||||
},
|
},
|
||||||
|
|
||||||
// Saves the currently loaded workflow to the database.
|
// Saves the currently loaded workflow to the database.
|
||||||
|
|
|
@ -93,6 +93,7 @@ import {
|
||||||
faTrash,
|
faTrash,
|
||||||
faUndo,
|
faUndo,
|
||||||
faUsers,
|
faUsers,
|
||||||
|
faClock,
|
||||||
} from '@fortawesome/free-solid-svg-icons';
|
} from '@fortawesome/free-solid-svg-icons';
|
||||||
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
|
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
|
||||||
|
|
||||||
|
@ -174,6 +175,7 @@ library.add(faTimes);
|
||||||
library.add(faTrash);
|
library.add(faTrash);
|
||||||
library.add(faUndo);
|
library.add(faUndo);
|
||||||
library.add(faUsers);
|
library.add(faUsers);
|
||||||
|
library.add(faClock);
|
||||||
|
|
||||||
Vue.component('font-awesome-icon', FontAwesomeIcon);
|
Vue.component('font-awesome-icon', FontAwesomeIcon);
|
||||||
|
|
||||||
|
|
|
@ -1851,21 +1851,19 @@ export default mixins(
|
||||||
for (type of Object.keys(currentConnections[sourceNode])) {
|
for (type of Object.keys(currentConnections[sourceNode])) {
|
||||||
connection[type] = [];
|
connection[type] = [];
|
||||||
for (sourceIndex = 0; sourceIndex < currentConnections[sourceNode][type].length; sourceIndex++) {
|
for (sourceIndex = 0; sourceIndex < currentConnections[sourceNode][type].length; sourceIndex++) {
|
||||||
if (!currentConnections[sourceNode][type][sourceIndex]) {
|
|
||||||
// There is so something wrong with the data so ignore
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const nodeSourceConnections = [];
|
const nodeSourceConnections = [];
|
||||||
for (connectionIndex = 0; connectionIndex < currentConnections[sourceNode][type][sourceIndex].length; connectionIndex++) {
|
if (currentConnections[sourceNode][type][sourceIndex]) {
|
||||||
const nodeConnection: NodeInputConnections = [];
|
for (connectionIndex = 0; connectionIndex < currentConnections[sourceNode][type][sourceIndex].length; connectionIndex++) {
|
||||||
connectionData = currentConnections[sourceNode][type][sourceIndex][connectionIndex];
|
const nodeConnection: NodeInputConnections = [];
|
||||||
if (!createNodeNames.includes(connectionData.node)) {
|
connectionData = currentConnections[sourceNode][type][sourceIndex][connectionIndex];
|
||||||
// Node does not get created so skip input connection
|
if (!createNodeNames.includes(connectionData.node)) {
|
||||||
continue;
|
// Node does not get created so skip input connection
|
||||||
}
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
nodeSourceConnections.push(connectionData);
|
nodeSourceConnections.push(connectionData);
|
||||||
// Add connection
|
// Add connection
|
||||||
|
}
|
||||||
}
|
}
|
||||||
connection[type].push(nodeSourceConnections);
|
connection[type].push(nodeSourceConnections);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "n8n-node-dev",
|
"name": "n8n-node-dev",
|
||||||
"version": "0.12.0",
|
"version": "0.13.0",
|
||||||
"description": "CLI to simplify n8n credentials/node development",
|
"description": "CLI to simplify n8n credentials/node development",
|
||||||
"license": "SEE LICENSE IN LICENSE.md",
|
"license": "SEE LICENSE IN LICENSE.md",
|
||||||
"homepage": "https://n8n.io",
|
"homepage": "https://n8n.io",
|
||||||
|
@ -59,8 +59,8 @@
|
||||||
"change-case": "^4.1.1",
|
"change-case": "^4.1.1",
|
||||||
"copyfiles": "^2.1.1",
|
"copyfiles": "^2.1.1",
|
||||||
"inquirer": "^7.0.1",
|
"inquirer": "^7.0.1",
|
||||||
"n8n-core": "~0.70.0",
|
"n8n-core": "~0.71.0",
|
||||||
"n8n-workflow": "^0.57.0",
|
"n8n-workflow": "~0.58.0",
|
||||||
"oauth-1.0a": "^2.2.6",
|
"oauth-1.0a": "^2.2.6",
|
||||||
"replace-in-file": "^6.0.0",
|
"replace-in-file": "^6.0.0",
|
||||||
"request": "^2.88.2",
|
"request": "^2.88.2",
|
||||||
|
|
18
packages/nodes-base/credentials/NotionApi.credentials.ts
Normal file
18
packages/nodes-base/credentials/NotionApi.credentials.ts
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import {
|
||||||
|
ICredentialType,
|
||||||
|
NodePropertyTypes,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
export class NotionApi implements ICredentialType {
|
||||||
|
name = 'notionApi';
|
||||||
|
displayName = 'Notion API';
|
||||||
|
documentationUrl = 'notion';
|
||||||
|
properties = [
|
||||||
|
{
|
||||||
|
displayName: 'API Key',
|
||||||
|
name: 'apiKey',
|
||||||
|
type: 'string' as NodePropertyTypes,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
import {
|
||||||
|
ICredentialType,
|
||||||
|
NodePropertyTypes,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
export class NotionOAuth2Api implements ICredentialType {
|
||||||
|
name = 'notionOAuth2Api';
|
||||||
|
extends = [
|
||||||
|
'oAuth2Api',
|
||||||
|
];
|
||||||
|
displayName = 'Notion OAuth2 API';
|
||||||
|
documentationUrl = 'notion';
|
||||||
|
properties = [
|
||||||
|
{
|
||||||
|
displayName: 'Authorization URL',
|
||||||
|
name: 'authUrl',
|
||||||
|
type: 'hidden' as NodePropertyTypes,
|
||||||
|
default: 'https://api.notion.com/v1/oauth/authorize',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Access Token URL',
|
||||||
|
name: 'accessTokenUrl',
|
||||||
|
type: 'hidden' as NodePropertyTypes,
|
||||||
|
default: 'https://api.notion.com/v1/oauth/token',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Scope',
|
||||||
|
name: 'scope',
|
||||||
|
type: 'hidden' as NodePropertyTypes,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Auth URI Query Parameters',
|
||||||
|
name: 'authQueryParameters',
|
||||||
|
type: 'hidden' as NodePropertyTypes,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Authentication',
|
||||||
|
name: 'authentication',
|
||||||
|
type: 'hidden' as NodePropertyTypes,
|
||||||
|
default: 'header',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
|
@ -9,6 +9,22 @@ export class TwilioApi implements ICredentialType {
|
||||||
displayName = 'Twilio API';
|
displayName = 'Twilio API';
|
||||||
documentationUrl = 'twilio';
|
documentationUrl = 'twilio';
|
||||||
properties = [
|
properties = [
|
||||||
|
{
|
||||||
|
displayName: 'Auth Type',
|
||||||
|
name: 'authType',
|
||||||
|
type: 'options' as NodePropertyTypes,
|
||||||
|
default: 'authToken',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Auth Token',
|
||||||
|
value: 'authToken',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'API Key',
|
||||||
|
value: 'apiKey',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Account SID',
|
displayName: 'Account SID',
|
||||||
name: 'accountSid',
|
name: 'accountSid',
|
||||||
|
@ -20,6 +36,42 @@ export class TwilioApi implements ICredentialType {
|
||||||
name: 'authToken',
|
name: 'authToken',
|
||||||
type: 'string' as NodePropertyTypes,
|
type: 'string' as NodePropertyTypes,
|
||||||
default: '',
|
default: '',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
authType: [
|
||||||
|
'authToken',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'API Key SID',
|
||||||
|
name: 'apiKeySid',
|
||||||
|
type: 'string' as NodePropertyTypes,
|
||||||
|
default: '',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
authType: [
|
||||||
|
'apiKey',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'API Key Secret',
|
||||||
|
name: 'apiKeySecret',
|
||||||
|
type: 'string' as NodePropertyTypes,
|
||||||
|
typeOptions: {
|
||||||
|
password: true,
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
authType: [
|
||||||
|
'apiKey',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ export class DateTime implements INodeType {
|
||||||
description: INodeTypeDescription = {
|
description: INodeTypeDescription = {
|
||||||
displayName: 'Date & Time',
|
displayName: 'Date & Time',
|
||||||
name: 'dateTime',
|
name: 'dateTime',
|
||||||
icon: 'fa:calendar',
|
icon: 'fa:clock',
|
||||||
group: ['transform'],
|
group: ['transform'],
|
||||||
version: 1,
|
version: 1,
|
||||||
description: 'Allows you to manipulate date and time values',
|
description: 'Allows you to manipulate date and time values',
|
||||||
|
|
|
@ -24,6 +24,10 @@ import {
|
||||||
|
|
||||||
import * as lodash from 'lodash';
|
import * as lodash from 'lodash';
|
||||||
|
|
||||||
|
import {
|
||||||
|
LoggerProxy as Logger
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
export class EmailReadImap implements INodeType {
|
export class EmailReadImap implements INodeType {
|
||||||
description: INodeTypeDescription = {
|
description: INodeTypeDescription = {
|
||||||
displayName: 'EmailReadImap',
|
displayName: 'EmailReadImap',
|
||||||
|
@ -158,6 +162,13 @@ export class EmailReadImap implements INodeType {
|
||||||
default: false,
|
default: false,
|
||||||
description: 'Do connect even if SSL certificate validation is not possible.',
|
description: 'Do connect even if SSL certificate validation is not possible.',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Force reconnect',
|
||||||
|
name: 'forceReconnect',
|
||||||
|
type: 'number',
|
||||||
|
default: 60,
|
||||||
|
description: 'Sets an interval (in minutes) to force a reconnection.',
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -176,16 +187,8 @@ export class EmailReadImap implements INodeType {
|
||||||
const postProcessAction = this.getNodeParameter('postProcessAction') as string;
|
const postProcessAction = this.getNodeParameter('postProcessAction') as string;
|
||||||
const options = this.getNodeParameter('options', {}) as IDataObject;
|
const options = this.getNodeParameter('options', {}) as IDataObject;
|
||||||
|
|
||||||
let searchCriteria = [
|
const staticData = this.getWorkflowStaticData('node');
|
||||||
'UNSEEN',
|
Logger.debug('Loaded static data for node "EmailReadImap"', {staticData});
|
||||||
];
|
|
||||||
if (options.customEmailConfig !== undefined) {
|
|
||||||
try {
|
|
||||||
searchCriteria = JSON.parse(options.customEmailConfig as string);
|
|
||||||
} catch (error) {
|
|
||||||
throw new NodeOperationError(this.getNode(), `Custom email config is not valid JSON.`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the email text
|
// Returns the email text
|
||||||
const getText = async (parts: any[], message: Message, subtype: string) => { // tslint:disable-line:no-any
|
const getText = async (parts: any[], message: Message, subtype: string) => { // tslint:disable-line:no-any
|
||||||
|
@ -237,7 +240,7 @@ export class EmailReadImap implements INodeType {
|
||||||
|
|
||||||
|
|
||||||
// Returns all the new unseen messages
|
// Returns all the new unseen messages
|
||||||
const getNewEmails = async (connection: ImapSimple, searchCriteria: string[]): Promise<INodeExecutionData[]> => {
|
const getNewEmails = async (connection: ImapSimple, searchCriteria: Array<string | string[]>): Promise<INodeExecutionData[]> => {
|
||||||
const format = this.getNodeParameter('format', 0) as string;
|
const format = this.getNodeParameter('format', 0) as string;
|
||||||
|
|
||||||
let fetchOptions = {};
|
let fetchOptions = {};
|
||||||
|
@ -277,6 +280,12 @@ export class EmailReadImap implements INodeType {
|
||||||
const dataPropertyAttachmentsPrefixName = this.getNodeParameter('dataPropertyAttachmentsPrefixName') as string;
|
const dataPropertyAttachmentsPrefixName = this.getNodeParameter('dataPropertyAttachmentsPrefixName') as string;
|
||||||
|
|
||||||
for (const message of results) {
|
for (const message of results) {
|
||||||
|
if (staticData.lastMessageUid !== undefined && message.attributes.uid <= (staticData.lastMessageUid as number)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (staticData.lastMessageUid === undefined || staticData.lastMessageUid as number < message.attributes.uid) {
|
||||||
|
staticData.lastMessageUid = message.attributes.uid;
|
||||||
|
}
|
||||||
const part = lodash.find(message.parts, { which: '' });
|
const part = lodash.find(message.parts, { which: '' });
|
||||||
|
|
||||||
if (part === undefined) {
|
if (part === undefined) {
|
||||||
|
@ -295,6 +304,12 @@ export class EmailReadImap implements INodeType {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const message of results) {
|
for (const message of results) {
|
||||||
|
if (staticData.lastMessageUid !== undefined && message.attributes.uid <= (staticData.lastMessageUid as number)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (staticData.lastMessageUid === undefined || staticData.lastMessageUid as number < message.attributes.uid) {
|
||||||
|
staticData.lastMessageUid = message.attributes.uid;
|
||||||
|
}
|
||||||
const parts = getParts(message.attributes.struct!);
|
const parts = getParts(message.attributes.struct!);
|
||||||
|
|
||||||
newEmail = {
|
newEmail = {
|
||||||
|
@ -335,6 +350,12 @@ export class EmailReadImap implements INodeType {
|
||||||
}
|
}
|
||||||
} else if (format === 'raw') {
|
} else if (format === 'raw') {
|
||||||
for (const message of results) {
|
for (const message of results) {
|
||||||
|
if (staticData.lastMessageUid !== undefined && message.attributes.uid <= (staticData.lastMessageUid as number)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (staticData.lastMessageUid === undefined || staticData.lastMessageUid as number < message.attributes.uid) {
|
||||||
|
staticData.lastMessageUid = message.attributes.uid;
|
||||||
|
}
|
||||||
const part = lodash.find(message.parts, { which: 'TEXT' });
|
const part = lodash.find(message.parts, { which: 'TEXT' });
|
||||||
|
|
||||||
if (part === undefined) {
|
if (part === undefined) {
|
||||||
|
@ -366,6 +387,33 @@ export class EmailReadImap implements INodeType {
|
||||||
},
|
},
|
||||||
onmail: async () => {
|
onmail: async () => {
|
||||||
if (connection) {
|
if (connection) {
|
||||||
|
let searchCriteria = [
|
||||||
|
'UNSEEN',
|
||||||
|
] as Array<string | string[]>;
|
||||||
|
if (options.customEmailConfig !== undefined) {
|
||||||
|
try {
|
||||||
|
searchCriteria = JSON.parse(options.customEmailConfig as string);
|
||||||
|
} catch (error) {
|
||||||
|
throw new NodeOperationError(this.getNode(), `Custom email config is not valid JSON.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (staticData.lastMessageUid !== undefined) {
|
||||||
|
searchCriteria.push(['UID', `${staticData.lastMessageUid as number}:*`]);
|
||||||
|
/**
|
||||||
|
* A short explanation about UIDs and how they work
|
||||||
|
* can be found here: https://dev.to/kehers/imap-new-messages-since-last-check-44gm
|
||||||
|
* TL;DR:
|
||||||
|
* - You cannot filter using ['UID', 'CURRENT ID + 1:*'] because IMAP
|
||||||
|
* won't return correct results if current id + 1 does not yet exist.
|
||||||
|
* - UIDs can change but this is not being treated here.
|
||||||
|
* If the mailbox is recreated (lets say you remove all emails, remove
|
||||||
|
* the mail box and create another with same name, UIDs will change)
|
||||||
|
* - You can check if UIDs changed in the above example
|
||||||
|
* by checking UIDValidity.
|
||||||
|
*/
|
||||||
|
Logger.debug('Querying for new messages on node "EmailReadImap"', {searchCriteria});
|
||||||
|
}
|
||||||
|
|
||||||
const returnData = await getNewEmails(connection, searchCriteria);
|
const returnData = await getNewEmails(connection, searchCriteria);
|
||||||
|
|
||||||
if (returnData.length) {
|
if (returnData.length) {
|
||||||
|
@ -386,7 +434,9 @@ export class EmailReadImap implements INodeType {
|
||||||
return imapConnect(config).then(async conn => {
|
return imapConnect(config).then(async conn => {
|
||||||
conn.on('error', async err => {
|
conn.on('error', async err => {
|
||||||
if (err.code.toUpperCase() === 'ECONNRESET') {
|
if (err.code.toUpperCase() === 'ECONNRESET') {
|
||||||
|
Logger.verbose('IMAP connection was reset - reconnecting.');
|
||||||
connection = await establishConnection();
|
connection = await establishConnection();
|
||||||
|
await connection.openBox(mailbox);
|
||||||
}
|
}
|
||||||
throw err;
|
throw err;
|
||||||
});
|
});
|
||||||
|
@ -398,8 +448,22 @@ export class EmailReadImap implements INodeType {
|
||||||
|
|
||||||
await connection.openBox(mailbox);
|
await connection.openBox(mailbox);
|
||||||
|
|
||||||
|
let reconnectionInterval: NodeJS.Timeout | undefined;
|
||||||
|
|
||||||
|
if (options.forceReconnect !== undefined) {
|
||||||
|
reconnectionInterval = setInterval(async () => {
|
||||||
|
Logger.verbose('Forcing reconnection of IMAP node.');
|
||||||
|
await connection.end();
|
||||||
|
connection = await establishConnection();
|
||||||
|
await connection.openBox(mailbox);
|
||||||
|
}, options.forceReconnect as number * 1000 * 60);
|
||||||
|
}
|
||||||
|
|
||||||
// When workflow and so node gets set to inactive close the connectoin
|
// When workflow and so node gets set to inactive close the connectoin
|
||||||
async function closeFunction() {
|
async function closeFunction() {
|
||||||
|
if (reconnectionInterval) {
|
||||||
|
clearInterval(reconnectionInterval);
|
||||||
|
}
|
||||||
await connection.end();
|
await connection.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -210,7 +210,7 @@ export class EmailSend implements INodeType {
|
||||||
// Send the email
|
// Send the email
|
||||||
const info = await transporter.sendMail(mailOptions);
|
const info = await transporter.sendMail(mailOptions);
|
||||||
|
|
||||||
returnData.push({ json: info });
|
returnData.push({ json: info as unknown as IDataObject });
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.prepareOutputData(returnData);
|
return this.prepareOutputData(returnData);
|
||||||
|
|
|
@ -1970,7 +1970,6 @@ export class GoogleDrive implements INodeType {
|
||||||
// ----------------------------------
|
// ----------------------------------
|
||||||
// list
|
// list
|
||||||
// ----------------------------------
|
// ----------------------------------
|
||||||
|
|
||||||
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
|
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
|
||||||
|
|
||||||
const qs: IDataObject = {};
|
const qs: IDataObject = {};
|
||||||
|
@ -1986,6 +1985,7 @@ export class GoogleDrive implements INodeType {
|
||||||
const data = await googleApiRequest.call(this, 'GET', `/drive/v3/drives`, {}, qs);
|
const data = await googleApiRequest.call(this, 'GET', `/drive/v3/drives`, {}, qs);
|
||||||
response = data.drives as IDataObject[];
|
response = data.drives as IDataObject[];
|
||||||
}
|
}
|
||||||
|
|
||||||
returnData.push.apply(returnData, response);
|
returnData.push.apply(returnData, response);
|
||||||
}
|
}
|
||||||
if (operation === 'update') {
|
if (operation === 'update') {
|
||||||
|
@ -2004,7 +2004,8 @@ export class GoogleDrive implements INodeType {
|
||||||
returnData.push(response as IDataObject);
|
returnData.push(response as IDataObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (resource === 'file') {
|
}
|
||||||
|
if (resource === 'file') {
|
||||||
if (operation === 'copy') {
|
if (operation === 'copy') {
|
||||||
// ----------------------------------
|
// ----------------------------------
|
||||||
// copy
|
// copy
|
||||||
|
@ -2026,7 +2027,7 @@ export class GoogleDrive implements INodeType {
|
||||||
const qs = {
|
const qs = {
|
||||||
supportsAllDrives: true,
|
supportsAllDrives: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
const response = await googleApiRequest.call(this, 'POST', `/drive/v3/files/${fileId}/copy`, body, qs);
|
const response = await googleApiRequest.call(this, 'POST', `/drive/v3/files/${fileId}/copy`, body, qs);
|
||||||
|
|
||||||
returnData.push(response as IDataObject);
|
returnData.push(response as IDataObject);
|
||||||
|
@ -2264,7 +2265,8 @@ export class GoogleDrive implements INodeType {
|
||||||
returnData.push(responseData as IDataObject);
|
returnData.push(responseData as IDataObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (resource === 'folder') {
|
}
|
||||||
|
if (resource === 'folder') {
|
||||||
if (operation === 'create') {
|
if (operation === 'create') {
|
||||||
// ----------------------------------
|
// ----------------------------------
|
||||||
// folder:create
|
// folder:create
|
||||||
|
@ -2326,11 +2328,8 @@ export class GoogleDrive implements INodeType {
|
||||||
|
|
||||||
returnData.push(response as IDataObject);
|
returnData.push(response as IDataObject);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
throw new NodeOperationError(this.getNode(), `The resource "${resource}" is not known!`);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (resource === 'file' && operation === 'download') {
|
if (resource === 'file' && operation === 'download') {
|
||||||
// For file downloads the files get attached to the existing items
|
// For file downloads the files get attached to the existing items
|
||||||
return this.prepareOutputData(items);
|
return this.prepareOutputData(items);
|
||||||
|
|
|
@ -25,7 +25,37 @@ export class GraphQL implements INodeType {
|
||||||
},
|
},
|
||||||
inputs: ['main'],
|
inputs: ['main'],
|
||||||
outputs: ['main'],
|
outputs: ['main'],
|
||||||
|
credentials: [
|
||||||
|
{
|
||||||
|
name: 'httpHeaderAuth',
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
authentication: [
|
||||||
|
'headerAuth',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
properties: [
|
properties: [
|
||||||
|
{
|
||||||
|
displayName: 'Authentication',
|
||||||
|
name: 'authentication',
|
||||||
|
type: 'options',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Header Auth',
|
||||||
|
value: 'headerAuth',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'None',
|
||||||
|
value: 'none',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
default: 'none',
|
||||||
|
description: 'The way to authenticate.',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
displayName: 'HTTP Request Method',
|
displayName: 'HTTP Request Method',
|
||||||
name: 'requestMethod',
|
name: 'requestMethod',
|
||||||
|
@ -200,6 +230,7 @@ export class GraphQL implements INodeType {
|
||||||
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
||||||
|
|
||||||
const items = this.getInputData();
|
const items = this.getInputData();
|
||||||
|
const httpHeaderAuth = this.getCredentials('httpHeaderAuth');
|
||||||
|
|
||||||
let requestOptions: OptionsWithUri & RequestPromiseOptions;
|
let requestOptions: OptionsWithUri & RequestPromiseOptions;
|
||||||
|
|
||||||
|
@ -228,6 +259,11 @@ export class GraphQL implements INodeType {
|
||||||
rejectUnauthorized: !this.getNodeParameter('allowUnauthorizedCerts', itemIndex, false) as boolean,
|
rejectUnauthorized: !this.getNodeParameter('allowUnauthorizedCerts', itemIndex, false) as boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Add credentials if any are set
|
||||||
|
if (httpHeaderAuth !== undefined) {
|
||||||
|
requestOptions.headers![httpHeaderAuth.name as string] = httpHeaderAuth.value;
|
||||||
|
}
|
||||||
|
|
||||||
const gqlQuery = this.getNodeParameter('query', itemIndex, '') as string;
|
const gqlQuery = this.getNodeParameter('query', itemIndex, '') as string;
|
||||||
if (requestMethod === 'GET') {
|
if (requestMethod === 'GET') {
|
||||||
requestOptions.qs = {
|
requestOptions.qs = {
|
||||||
|
|
|
@ -2106,7 +2106,7 @@ export class Hubspot implements INodeType {
|
||||||
responseData = await hubspotApiRequestAllItems.call(this, 'results', 'POST', endpoint, body, qs);
|
responseData = await hubspotApiRequestAllItems.call(this, 'results', 'POST', endpoint, body, qs);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
qs.count = this.getNodeParameter('limit', 0) as number;
|
body.limit = this.getNodeParameter('limit', 0) as number;
|
||||||
responseData = await hubspotApiRequest.call(this, 'POST', endpoint, body, qs);
|
responseData = await hubspotApiRequest.call(this, 'POST', endpoint, body, qs);
|
||||||
responseData = responseData.results;
|
responseData = responseData.results;
|
||||||
}
|
}
|
||||||
|
|
358
packages/nodes-base/nodes/ICalendar.node.ts
Normal file
358
packages/nodes-base/nodes/ICalendar.node.ts
Normal file
|
@ -0,0 +1,358 @@
|
||||||
|
import {
|
||||||
|
IExecuteFunctions,
|
||||||
|
} from 'n8n-core';
|
||||||
|
|
||||||
|
import {
|
||||||
|
IDataObject,
|
||||||
|
INodeExecutionData,
|
||||||
|
INodeType,
|
||||||
|
INodeTypeDescription,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
import {
|
||||||
|
promisify,
|
||||||
|
} from 'util';
|
||||||
|
|
||||||
|
import * as moment from 'moment-timezone';
|
||||||
|
|
||||||
|
import * as ics from 'ics';
|
||||||
|
|
||||||
|
const createEvent = promisify(ics.createEvent);
|
||||||
|
|
||||||
|
export class ICalendar implements INodeType {
|
||||||
|
description: INodeTypeDescription = {
|
||||||
|
displayName: 'iCalendar',
|
||||||
|
name: 'iCal',
|
||||||
|
icon: 'fa:calendar',
|
||||||
|
group: ['input'],
|
||||||
|
version: 1,
|
||||||
|
subtitle: '={{$parameter["operation"]}}',
|
||||||
|
description: 'Create iCalendar file',
|
||||||
|
defaults: {
|
||||||
|
name: 'iCalendar',
|
||||||
|
color: '#408000',
|
||||||
|
},
|
||||||
|
inputs: ['main'],
|
||||||
|
outputs: ['main'],
|
||||||
|
credentials: [],
|
||||||
|
properties: [
|
||||||
|
{
|
||||||
|
displayName: 'Operation',
|
||||||
|
name: 'operation',
|
||||||
|
type: 'options',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Create Event File',
|
||||||
|
value: 'createEventFile',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
default: 'createEventFile',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Event Title',
|
||||||
|
name: 'title',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Start',
|
||||||
|
name: 'start',
|
||||||
|
type: 'dateTime',
|
||||||
|
default: '',
|
||||||
|
required: true,
|
||||||
|
description: 'Date and time at which the event begins. (For all-day events, the time will be ignored.)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'End',
|
||||||
|
name: 'end',
|
||||||
|
type: 'dateTime',
|
||||||
|
default: '',
|
||||||
|
required: true,
|
||||||
|
description: 'Date and time at which the event ends. (For all-day events, the time will be ignored.)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'All Day',
|
||||||
|
name: 'allDay',
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
description: 'Whether the event lasts all day or not.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Binary Property',
|
||||||
|
name: 'binaryPropertyName',
|
||||||
|
type: 'string',
|
||||||
|
default: 'data',
|
||||||
|
required: true,
|
||||||
|
description: 'The field that your iCalendar file will be<br />available under in the output.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Additional Fields',
|
||||||
|
name: 'additionalFields',
|
||||||
|
type: 'collection',
|
||||||
|
placeholder: 'Add Field',
|
||||||
|
default: {},
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
operation: [
|
||||||
|
'createEventFile',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
displayName: 'Attendees',
|
||||||
|
name: 'attendeesUi',
|
||||||
|
type: 'fixedCollection',
|
||||||
|
typeOptions: {
|
||||||
|
multipleValues: true,
|
||||||
|
},
|
||||||
|
placeholder: 'Add Attendee',
|
||||||
|
default: {},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
displayName: 'Attendees',
|
||||||
|
name: 'attendeeValues',
|
||||||
|
values: [
|
||||||
|
{
|
||||||
|
displayName: 'Name',
|
||||||
|
name: 'name',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Email',
|
||||||
|
name: 'email',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'RSVP',
|
||||||
|
name: 'rsvp',
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
description: `Whether the attendee has to confirm attendance or not.`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Busy Status',
|
||||||
|
name: 'busyStatus',
|
||||||
|
type: 'options',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Busy',
|
||||||
|
value: 'BUSY',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Tentative',
|
||||||
|
value: 'TENTATIVE',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
default: '',
|
||||||
|
description: 'Used to specify busy status for Microsoft applications, like Outlook.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Calendar Name',
|
||||||
|
name: 'calName',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
description: 'Specifies the calendar (not event) name. Used by Apple iCal and Microsoft Outlook (<a href="https://docs.microsoft.com/en-us/openspecs/exchange_server_protocols/ms-oxcical/1da58449-b97e-46bd-b018-a1ce576f3e6d" target="_blank">spec</a>).',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Description',
|
||||||
|
name: 'description',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'File Name',
|
||||||
|
name: 'fileName',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
description: 'The name of the file to be generated. Default value is event.ics.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Geolocation',
|
||||||
|
name: 'geolocationUi',
|
||||||
|
type: 'fixedCollection',
|
||||||
|
typeOptions: {
|
||||||
|
multipleValues: false,
|
||||||
|
},
|
||||||
|
placeholder: 'Add Geolocation',
|
||||||
|
default: {},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
displayName: 'Geolocation',
|
||||||
|
name: 'geolocationValues',
|
||||||
|
values: [
|
||||||
|
{
|
||||||
|
displayName: 'Latitude',
|
||||||
|
name: 'lat',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Longitude',
|
||||||
|
name: 'lon',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Location',
|
||||||
|
name: 'location',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
description: 'The intended venue.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Recurrence Rule',
|
||||||
|
name: 'recurrenceRule',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
description: `A rule to define the repeat pattern of the event (RRULE). (<a href="https://icalendar.org/rrule-tool.html" target="_blank">Rule generator</a>)`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Organizer',
|
||||||
|
name: 'organizerUi',
|
||||||
|
type: 'fixedCollection',
|
||||||
|
typeOptions: {
|
||||||
|
multipleValues: false,
|
||||||
|
},
|
||||||
|
placeholder: 'Add Organizer',
|
||||||
|
default: {},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
displayName: 'Organizer',
|
||||||
|
name: 'organizerValues',
|
||||||
|
values: [
|
||||||
|
{
|
||||||
|
displayName: 'Name',
|
||||||
|
name: 'name',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Email',
|
||||||
|
name: 'email',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Sequence',
|
||||||
|
name: 'sequence',
|
||||||
|
type: 'number',
|
||||||
|
default: 0,
|
||||||
|
description: 'When sending an update for an event (with the same uid), defines the revision sequence number.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Status',
|
||||||
|
name: 'status',
|
||||||
|
type: 'options',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Confirmed',
|
||||||
|
value: 'CONFIRMED',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Cancelled',
|
||||||
|
value: 'CANCELLED',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Tentative',
|
||||||
|
value: 'TENTATIVE',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
default: 'CONFIRMED',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'UID',
|
||||||
|
name: 'uid',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
description: `Universally unique id for the event (will be auto-generated if not specified here). Should be globally unique.`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'URL',
|
||||||
|
name: 'url',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
description: 'URL associated with event.',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
||||||
|
const items = this.getInputData();
|
||||||
|
const length = (items.length as unknown) as number;
|
||||||
|
const returnData: INodeExecutionData[] = [];
|
||||||
|
const operation = this.getNodeParameter('operation', 0) as string;
|
||||||
|
if (operation === 'createEventFile') {
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
const title = this.getNodeParameter('title', i) as string;
|
||||||
|
const allDay = this.getNodeParameter('allDay', i) as boolean;
|
||||||
|
const start = this.getNodeParameter('start', i) as string;
|
||||||
|
let end = this.getNodeParameter('end', i) as string;
|
||||||
|
end = (allDay) ? moment(end).utc().add(1, 'day').format() as string : end;
|
||||||
|
const binaryPropertyName = this.getNodeParameter('binaryPropertyName', i) as string;
|
||||||
|
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
|
||||||
|
let fileName = 'event.ics';
|
||||||
|
|
||||||
|
if (additionalFields.fileName) {
|
||||||
|
fileName = additionalFields.fileName as string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const data: ics.EventAttributes = {
|
||||||
|
title,
|
||||||
|
start: (moment(start).toArray().splice(0, (allDay) ? 3 : 6) as ics.DateArray),
|
||||||
|
end: (moment(end).toArray().splice(0, (allDay) ? 3 : 6) as ics.DateArray),
|
||||||
|
startInputType: 'utc',
|
||||||
|
endInputType: 'utc',
|
||||||
|
};
|
||||||
|
|
||||||
|
if (additionalFields.geolocationUi) {
|
||||||
|
data.geo = (additionalFields.geolocationUi as IDataObject).geolocationValues as ics.GeoCoordinates;
|
||||||
|
delete additionalFields.geolocationUi;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (additionalFields.organizerUi) {
|
||||||
|
data.organizer = (additionalFields.organizerUi as IDataObject).organizerValues as ics.Person;
|
||||||
|
delete additionalFields.organizerUi;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (additionalFields.attendeesUi) {
|
||||||
|
data.attendees = (additionalFields.attendeesUi as IDataObject).attendeeValues as ics.Attendee[];
|
||||||
|
delete additionalFields.attendeesUi;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.assign(data, additionalFields);
|
||||||
|
const buffer = Buffer.from(await createEvent(data) as string);
|
||||||
|
const binaryData = await this.helpers.prepareBinaryData(buffer, fileName, 'text/calendar');
|
||||||
|
returnData.push(
|
||||||
|
{
|
||||||
|
json: {},
|
||||||
|
binary: {
|
||||||
|
[binaryPropertyName]: binaryData,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return [returnData];
|
||||||
|
}
|
||||||
|
}
|
|
@ -83,7 +83,7 @@ export const ecommerceOrderFields = [
|
||||||
{
|
{
|
||||||
displayName: 'Order Title',
|
displayName: 'Order Title',
|
||||||
name: 'orderTitle',
|
name: 'orderTitle',
|
||||||
type: 'dateTime',
|
type: 'string',
|
||||||
required: true,
|
required: true,
|
||||||
displayOptions: {
|
displayOptions: {
|
||||||
show: {
|
show: {
|
||||||
|
|
123
packages/nodes-base/nodes/Notion/BlockDescription.ts
Normal file
123
packages/nodes-base/nodes/Notion/BlockDescription.ts
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
import {
|
||||||
|
INodeProperties,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
import {
|
||||||
|
blocks,
|
||||||
|
} from './Blocks';
|
||||||
|
|
||||||
|
export const blockOperations = [
|
||||||
|
{
|
||||||
|
displayName: 'Operation',
|
||||||
|
name: 'operation',
|
||||||
|
type: 'options',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'block',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Append',
|
||||||
|
value: 'append',
|
||||||
|
description: 'Append a block',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Get All',
|
||||||
|
value: 'getAll',
|
||||||
|
description: 'Get all children blocks',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
default: 'append',
|
||||||
|
description: 'The operation to perform.',
|
||||||
|
},
|
||||||
|
] as INodeProperties[];
|
||||||
|
|
||||||
|
export const blockFields = [
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* block:append */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
{
|
||||||
|
displayName: 'Block ID',
|
||||||
|
name: 'blockId',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'block',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'append',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
description: `The ID of block. A page it is also considered a block. Hence, a Page ID can be used as well.`,
|
||||||
|
},
|
||||||
|
...blocks('block', 'append'),
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* block:getAll */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
{
|
||||||
|
displayName: 'Block ID',
|
||||||
|
name: 'blockId',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'block',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Return All',
|
||||||
|
name: 'returnAll',
|
||||||
|
type: 'boolean',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'block',
|
||||||
|
],
|
||||||
|
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: [
|
||||||
|
'block',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
returnAll: [
|
||||||
|
false,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
typeOptions: {
|
||||||
|
minValue: 1,
|
||||||
|
maxValue: 100,
|
||||||
|
},
|
||||||
|
default: 50,
|
||||||
|
description: 'How many results to return.',
|
||||||
|
},
|
||||||
|
] as INodeProperties[];
|
571
packages/nodes-base/nodes/Notion/Blocks.ts
Normal file
571
packages/nodes-base/nodes/Notion/Blocks.ts
Normal file
|
@ -0,0 +1,571 @@
|
||||||
|
import {
|
||||||
|
IDisplayOptions,
|
||||||
|
INodeProperties,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
const colors = [
|
||||||
|
{
|
||||||
|
name: 'Default',
|
||||||
|
value: 'default',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Gray',
|
||||||
|
value: 'gray',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Brown',
|
||||||
|
value: 'brown',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Orange',
|
||||||
|
value: 'orange',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Yellow',
|
||||||
|
value: 'yellow',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Green',
|
||||||
|
value: 'green',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Blue',
|
||||||
|
value: 'blue',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Purple',
|
||||||
|
value: 'purple',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Pink',
|
||||||
|
value: 'pink',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Red',
|
||||||
|
value: 'red',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Gray Background',
|
||||||
|
value: 'gray_background',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Brown Background',
|
||||||
|
value: 'brown_background',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Orange Background',
|
||||||
|
value: 'orange_background',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Yellow Background',
|
||||||
|
value: 'yellow_background',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Green Background',
|
||||||
|
value: 'green_background',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Blue Background',
|
||||||
|
value: 'blue_background',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Purple Background',
|
||||||
|
value: 'purple_background',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Pink Background',
|
||||||
|
value: 'pink_background',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Red Background',
|
||||||
|
value: 'red_background',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const annotation = [
|
||||||
|
{
|
||||||
|
displayName: 'Annotations',
|
||||||
|
name: 'annotationUi',
|
||||||
|
type: 'collection',
|
||||||
|
placeholder: 'Add Annotation',
|
||||||
|
default: {},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
displayName: 'Bold',
|
||||||
|
name: 'bold',
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
description: 'Whether the text is bolded.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Italic',
|
||||||
|
name: 'italic',
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
description: 'Whether the text is italicized.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Strikethrough',
|
||||||
|
name: 'strikethrough',
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
description: 'Whether the text is struck through.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Underline',
|
||||||
|
name: 'underline',
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
description: 'Whether the text is underlined.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Code',
|
||||||
|
name: 'code',
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
description: 'Whether the text is code style.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Color',
|
||||||
|
name: 'color',
|
||||||
|
type: 'options',
|
||||||
|
options: colors,
|
||||||
|
default: '',
|
||||||
|
description: 'Color of the text.',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
description: 'All annotations that apply to this rich text.',
|
||||||
|
},
|
||||||
|
] as INodeProperties[];
|
||||||
|
|
||||||
|
const typeMention = [
|
||||||
|
{
|
||||||
|
displayName: 'Type',
|
||||||
|
name: 'mentionType',
|
||||||
|
type: 'options',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
textType: [
|
||||||
|
'mention',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Database',
|
||||||
|
value: 'database',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Date',
|
||||||
|
value: 'date',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Page',
|
||||||
|
value: 'page',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'User',
|
||||||
|
value: 'user',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
default: '',
|
||||||
|
description: `An inline mention of a user, page, database, or date. In the app these are</br>
|
||||||
|
created by typing @ followed by the name of a user, page, database, or a date.`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'User ID',
|
||||||
|
name: 'user',
|
||||||
|
type: 'options',
|
||||||
|
typeOptions: {
|
||||||
|
loadOptionsMethod: 'getUsers',
|
||||||
|
},
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
mentionType: [
|
||||||
|
'user',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: 'The id of the user being mentioned.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Page ID',
|
||||||
|
name: 'page',
|
||||||
|
type: 'string',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
mentionType: [
|
||||||
|
'page',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: 'The id of the page being mentioned.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Database ID',
|
||||||
|
name: 'database',
|
||||||
|
type: 'options',
|
||||||
|
typeOptions: {
|
||||||
|
loadOptionsMethod: 'getDatabases',
|
||||||
|
},
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
mentionType: [
|
||||||
|
'database',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: 'The id of the database being mentioned.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Range',
|
||||||
|
name: 'range',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
mentionType: [
|
||||||
|
'date',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
description: 'Weather or not you want to define a date range.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Date',
|
||||||
|
name: 'date',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
mentionType: [
|
||||||
|
'date',
|
||||||
|
],
|
||||||
|
range: [
|
||||||
|
false,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
type: 'dateTime',
|
||||||
|
default: '',
|
||||||
|
description: 'An ISO 8601 format date, with optional time.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Date Start',
|
||||||
|
name: 'dateStart',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
mentionType: [
|
||||||
|
'date',
|
||||||
|
],
|
||||||
|
range: [
|
||||||
|
true,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
type: 'dateTime',
|
||||||
|
default: '',
|
||||||
|
description: 'An ISO 8601 format date, with optional time.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Date End',
|
||||||
|
name: 'dateEnd',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
range: [
|
||||||
|
true,
|
||||||
|
],
|
||||||
|
mentionType: [
|
||||||
|
'date',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
type: 'dateTime',
|
||||||
|
default: '',
|
||||||
|
description: `An ISO 8601 formatted date, with optional time. Represents the end of a date range.`,
|
||||||
|
},
|
||||||
|
] as INodeProperties[];
|
||||||
|
|
||||||
|
const typeEquation = [
|
||||||
|
{
|
||||||
|
displayName: 'Expression',
|
||||||
|
name: 'expression',
|
||||||
|
type: 'string',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
textType: [
|
||||||
|
'equation',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: '',
|
||||||
|
},
|
||||||
|
] as INodeProperties[];
|
||||||
|
|
||||||
|
const typeText = [
|
||||||
|
{
|
||||||
|
displayName: 'Text',
|
||||||
|
name: 'text',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
textType: [
|
||||||
|
'text',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
description: `Text content. This field contains the actual content</br>
|
||||||
|
of your text and is probably the field you'll use most often.`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Is Link',
|
||||||
|
name: 'isLink',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
textType: [
|
||||||
|
'text',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Text Link',
|
||||||
|
name: 'textLink',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
textType: [
|
||||||
|
'text',
|
||||||
|
],
|
||||||
|
isLink: [
|
||||||
|
true,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
description: 'The URL that this link points to.',
|
||||||
|
},
|
||||||
|
] as INodeProperties[];
|
||||||
|
|
||||||
|
export const text = (displayOptions: IDisplayOptions) => [
|
||||||
|
{
|
||||||
|
displayName: 'Text',
|
||||||
|
name: 'text',
|
||||||
|
placeholder: 'Add Text',
|
||||||
|
type: 'fixedCollection',
|
||||||
|
default: '',
|
||||||
|
typeOptions: {
|
||||||
|
multipleValues: true,
|
||||||
|
},
|
||||||
|
displayOptions,
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'text',
|
||||||
|
displayName: 'Text',
|
||||||
|
values: [
|
||||||
|
{
|
||||||
|
displayName: 'Type',
|
||||||
|
name: 'textType',
|
||||||
|
type: 'options',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Equation',
|
||||||
|
value: 'equation',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Mention',
|
||||||
|
value: 'mention',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Text',
|
||||||
|
value: 'text',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
default: 'text',
|
||||||
|
description: '',
|
||||||
|
},
|
||||||
|
...typeText,
|
||||||
|
...typeMention,
|
||||||
|
...typeEquation,
|
||||||
|
|
||||||
|
...annotation,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
description: 'Rich text in the block.',
|
||||||
|
}] as INodeProperties[];
|
||||||
|
|
||||||
|
|
||||||
|
const todo = (type: string) => [{
|
||||||
|
displayName: 'Checked',
|
||||||
|
name: 'checked',
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
type: [
|
||||||
|
type,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
description: 'Whether the to_do is checked or not.',
|
||||||
|
}] as INodeProperties[];
|
||||||
|
|
||||||
|
const title = (type: string) => [{
|
||||||
|
displayName: 'Title',
|
||||||
|
name: 'title',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
type: [
|
||||||
|
type,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
description: 'Plain text of page title.',
|
||||||
|
}] as INodeProperties[];
|
||||||
|
|
||||||
|
const richText = (displayOptions: IDisplayOptions) => [
|
||||||
|
{
|
||||||
|
displayName: 'Rich Text',
|
||||||
|
name: 'richText',
|
||||||
|
type: 'boolean',
|
||||||
|
displayOptions,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
] as INodeProperties[];
|
||||||
|
|
||||||
|
const textContent = (displayOptions: IDisplayOptions) => [
|
||||||
|
{
|
||||||
|
displayName: 'Text',
|
||||||
|
name: 'textContent',
|
||||||
|
type: 'string',
|
||||||
|
displayOptions,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
] as INodeProperties[];
|
||||||
|
|
||||||
|
const block = (blockType: string) => {
|
||||||
|
const data: INodeProperties[] = [];
|
||||||
|
switch (blockType) {
|
||||||
|
case 'to_do':
|
||||||
|
data.push(...todo(blockType));
|
||||||
|
data.push(...richText({
|
||||||
|
show: {
|
||||||
|
type: [
|
||||||
|
blockType,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
data.push(...textContent({
|
||||||
|
show: {
|
||||||
|
type: [
|
||||||
|
blockType,
|
||||||
|
],
|
||||||
|
richText: [
|
||||||
|
false,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
data.push(...text({
|
||||||
|
show: {
|
||||||
|
type: [
|
||||||
|
blockType,
|
||||||
|
],
|
||||||
|
richText: [
|
||||||
|
true,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
break;
|
||||||
|
case 'child_page':
|
||||||
|
data.push(...title(blockType));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
data.push(...richText({
|
||||||
|
show: {
|
||||||
|
type: [
|
||||||
|
blockType,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
data.push(...textContent({
|
||||||
|
show: {
|
||||||
|
type: [
|
||||||
|
blockType,
|
||||||
|
],
|
||||||
|
richText: [
|
||||||
|
false,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
data.push(...text({
|
||||||
|
show: {
|
||||||
|
type: [
|
||||||
|
blockType,
|
||||||
|
],
|
||||||
|
richText: [
|
||||||
|
true,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const blocks = (resource: string, operation: string) => [{
|
||||||
|
displayName: 'Blocks',
|
||||||
|
name: 'blockUi',
|
||||||
|
type: 'fixedCollection',
|
||||||
|
typeOptions: {
|
||||||
|
multipleValues: true,
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
resource,
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
operation,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
placeholder: 'Add Block',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'blockValues',
|
||||||
|
displayName: 'Block',
|
||||||
|
values: [
|
||||||
|
{
|
||||||
|
displayName: 'Type',
|
||||||
|
name: 'type',
|
||||||
|
type: 'options',
|
||||||
|
typeOptions: {
|
||||||
|
loadOptionsMethod: 'getBlockTypes',
|
||||||
|
},
|
||||||
|
description: 'Type of block',
|
||||||
|
default: 'paragraph',
|
||||||
|
},
|
||||||
|
...block('paragraph'),
|
||||||
|
...block('heading_1'),
|
||||||
|
...block('heading_2'),
|
||||||
|
...block('heading_3'),
|
||||||
|
...block('toggle'),
|
||||||
|
...block('to_do'),
|
||||||
|
...block('child_page'),
|
||||||
|
...block('bulleted_list_item'),
|
||||||
|
...block('numbered_list_item'),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
] as INodeProperties[];
|
||||||
|
|
100
packages/nodes-base/nodes/Notion/DatabaseDescription.ts
Normal file
100
packages/nodes-base/nodes/Notion/DatabaseDescription.ts
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
import {
|
||||||
|
INodeProperties,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
export const databaseOperations = [
|
||||||
|
{
|
||||||
|
displayName: 'Operation',
|
||||||
|
name: 'operation',
|
||||||
|
type: 'options',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'database',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Get',
|
||||||
|
value: 'get',
|
||||||
|
description: 'Get a database',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Get All',
|
||||||
|
value: 'getAll',
|
||||||
|
description: 'Get all databases',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
default: 'get',
|
||||||
|
description: 'The operation to perform.',
|
||||||
|
},
|
||||||
|
] as INodeProperties[];
|
||||||
|
|
||||||
|
export const databaseFields = [
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* database:get */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
{
|
||||||
|
displayName: 'Database ID',
|
||||||
|
name: 'databaseId',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'database',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'get',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* database:getAll */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
{
|
||||||
|
displayName: 'Return All',
|
||||||
|
name: 'returnAll',
|
||||||
|
type: 'boolean',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'database',
|
||||||
|
],
|
||||||
|
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: [
|
||||||
|
'database',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
returnAll: [
|
||||||
|
false,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
typeOptions: {
|
||||||
|
minValue: 1,
|
||||||
|
maxValue: 100,
|
||||||
|
},
|
||||||
|
default: 50,
|
||||||
|
description: 'How many results to return.',
|
||||||
|
},
|
||||||
|
] as INodeProperties[];
|
994
packages/nodes-base/nodes/Notion/DatabasePageDescription.ts
Normal file
994
packages/nodes-base/nodes/Notion/DatabasePageDescription.ts
Normal file
|
@ -0,0 +1,994 @@
|
||||||
|
import {
|
||||||
|
INodeProperties,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
import {
|
||||||
|
blocks,
|
||||||
|
text,
|
||||||
|
} from './Blocks';
|
||||||
|
|
||||||
|
import {
|
||||||
|
filters,
|
||||||
|
} from './Filters';
|
||||||
|
|
||||||
|
export const databasePageOperations = [
|
||||||
|
{
|
||||||
|
displayName: 'Operation',
|
||||||
|
name: 'operation',
|
||||||
|
type: 'options',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'databasePage',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Create',
|
||||||
|
value: 'create',
|
||||||
|
description: 'Create a pages in a database',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Get All',
|
||||||
|
value: 'getAll',
|
||||||
|
description: 'Get all pages in a database',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Update',
|
||||||
|
value: 'update',
|
||||||
|
description: 'Update pages in a database',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
default: 'create',
|
||||||
|
description: 'The operation to perform.',
|
||||||
|
},
|
||||||
|
] as INodeProperties[];
|
||||||
|
|
||||||
|
export const databasePageFields = [
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* databasePage:create */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
{
|
||||||
|
displayName: 'Database ID',
|
||||||
|
name: 'databaseId',
|
||||||
|
type: 'options',
|
||||||
|
default: '',
|
||||||
|
typeOptions: {
|
||||||
|
loadOptionsMethod: 'getDatabases',
|
||||||
|
},
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'databasePage',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'create',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
description: 'The ID of the database that this databasePage belongs to.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Simple',
|
||||||
|
name: 'simple',
|
||||||
|
type: 'boolean',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'databasePage',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'create',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: true,
|
||||||
|
description: 'When set to true a simplify version of the response will be used else the raw data.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Properties',
|
||||||
|
name: 'propertiesUi',
|
||||||
|
type: 'fixedCollection',
|
||||||
|
typeOptions: {
|
||||||
|
multipleValues: true,
|
||||||
|
},
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'databasePage',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'create',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
placeholder: 'Add Property',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'propertyValues',
|
||||||
|
displayName: 'Property',
|
||||||
|
values: [
|
||||||
|
{
|
||||||
|
displayName: 'Key',
|
||||||
|
name: 'key',
|
||||||
|
type: 'options',
|
||||||
|
typeOptions: {
|
||||||
|
loadOptionsMethod: 'getDatabaseProperties',
|
||||||
|
loadOptionsDependsOn: [
|
||||||
|
'databaseId',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Type',
|
||||||
|
name: 'type',
|
||||||
|
type: 'hidden',
|
||||||
|
default: '={{$parameter["&key"].split("|")[1]}}',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Title',
|
||||||
|
name: 'title',
|
||||||
|
type: 'string',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
type: [
|
||||||
|
'title',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Rich Text',
|
||||||
|
name: 'richText',
|
||||||
|
type: 'boolean',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
type: [
|
||||||
|
'rich_text',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Text',
|
||||||
|
name: 'textContent',
|
||||||
|
type: 'string',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
type: [
|
||||||
|
'rich_text',
|
||||||
|
],
|
||||||
|
richText: [
|
||||||
|
false,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
...text({
|
||||||
|
show: {
|
||||||
|
type: [
|
||||||
|
'rich_text',
|
||||||
|
],
|
||||||
|
richText: [
|
||||||
|
true,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
displayName: 'Phone Number',
|
||||||
|
name: 'phoneValue',
|
||||||
|
type: 'string',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
type: [
|
||||||
|
'phone_number',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: `Phone number. No structure is enforced.`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Options',
|
||||||
|
name: 'multiSelectValue',
|
||||||
|
type: 'multiOptions',
|
||||||
|
typeOptions: {
|
||||||
|
loadOptionsMethod: 'getPropertySelectValues',
|
||||||
|
},
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
type: [
|
||||||
|
'multi_select',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: [],
|
||||||
|
description: `Name of the options you want to set.
|
||||||
|
Multiples can be defined separated by comma.`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Option',
|
||||||
|
name: 'selectValue',
|
||||||
|
type: 'options',
|
||||||
|
typeOptions: {
|
||||||
|
loadOptionsMethod: 'getPropertySelectValues',
|
||||||
|
},
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
type: [
|
||||||
|
'select',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: `Name of the option you want to set.`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Email',
|
||||||
|
name: 'emailValue',
|
||||||
|
type: 'string',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
type: [
|
||||||
|
'email',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: 'Email address.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'URL',
|
||||||
|
name: 'urlValue',
|
||||||
|
type: 'string',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
type: [
|
||||||
|
'url',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: 'Web address.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'User IDs',
|
||||||
|
name: 'peopleValue',
|
||||||
|
type: 'multiOptions',
|
||||||
|
typeOptions: {
|
||||||
|
loadOptionsMethod: 'getUsers',
|
||||||
|
},
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
type: [
|
||||||
|
'people',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: [],
|
||||||
|
description: 'List of users. Multiples can be defined separated by comma.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Relation IDs',
|
||||||
|
name: 'relationValue',
|
||||||
|
type: 'string',
|
||||||
|
typeOptions: {
|
||||||
|
multipleValues: true,
|
||||||
|
},
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
type: [
|
||||||
|
'relation',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: [],
|
||||||
|
description: 'List of databases that belong to another database. Multiples can be defined separated by comma.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Checked',
|
||||||
|
name: 'checkboxValue',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
type: [
|
||||||
|
'checkbox',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
description: `
|
||||||
|
Whether or not the checkbox is checked.</br>
|
||||||
|
true represents checked.</br>
|
||||||
|
false represents unchecked.
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Number',
|
||||||
|
name: 'numberValue',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
type: [
|
||||||
|
'number',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
type: 'number',
|
||||||
|
default: 0,
|
||||||
|
description: 'Number value.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Range',
|
||||||
|
name: 'range',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
type: [
|
||||||
|
'date',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
description: 'Weather or not you want to define a date range.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Date',
|
||||||
|
name: 'date',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
range: [
|
||||||
|
false,
|
||||||
|
],
|
||||||
|
type: [
|
||||||
|
'date',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
type: 'dateTime',
|
||||||
|
default: '',
|
||||||
|
description: 'An ISO 8601 format date, with optional time.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Date Start',
|
||||||
|
name: 'dateStart',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
range: [
|
||||||
|
true,
|
||||||
|
],
|
||||||
|
type: [
|
||||||
|
'date',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
type: 'dateTime',
|
||||||
|
default: '',
|
||||||
|
description: 'An ISO 8601 format date, with optional time.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Date End',
|
||||||
|
name: 'dateEnd',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
range: [
|
||||||
|
true,
|
||||||
|
],
|
||||||
|
type: [
|
||||||
|
'date',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
type: 'dateTime',
|
||||||
|
default: '',
|
||||||
|
description: `
|
||||||
|
An ISO 8601 formatted date, with optional time. Represents the end of a date range.`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
...blocks('databasePage', 'create'),
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* databasePage:update */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
{
|
||||||
|
displayName: 'Page ID',
|
||||||
|
name: 'pageId',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'databasePage',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'update',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
description: 'The ID of the databasePage to update.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Simple',
|
||||||
|
name: 'simple',
|
||||||
|
type: 'boolean',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'databasePage',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'update',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: true,
|
||||||
|
description: 'When set to true a simplify version of the response will be used else the raw data.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Properties',
|
||||||
|
name: 'propertiesUi',
|
||||||
|
type: 'fixedCollection',
|
||||||
|
typeOptions: {
|
||||||
|
multipleValues: true,
|
||||||
|
},
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'databasePage',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'update',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
placeholder: 'Add Property',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'propertyValues',
|
||||||
|
displayName: 'Property',
|
||||||
|
values: [
|
||||||
|
{
|
||||||
|
displayName: 'Key',
|
||||||
|
name: 'key',
|
||||||
|
type: 'options',
|
||||||
|
typeOptions: {
|
||||||
|
loadOptionsMethod: 'getDatabaseIdFromPage',
|
||||||
|
loadOptionsDependsOn: [
|
||||||
|
'pageId',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Type',
|
||||||
|
name: 'type',
|
||||||
|
type: 'hidden',
|
||||||
|
default: '={{$parameter["&key"].split("|")[1]}}',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Title',
|
||||||
|
name: 'title',
|
||||||
|
type: 'string',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
type: [
|
||||||
|
'title',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Rich Text',
|
||||||
|
name: 'richText',
|
||||||
|
type: 'boolean',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
type: [
|
||||||
|
'rich_text',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Text',
|
||||||
|
name: 'textContent',
|
||||||
|
type: 'string',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
type: [
|
||||||
|
'rich_text',
|
||||||
|
],
|
||||||
|
richText: [
|
||||||
|
false,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
...text({
|
||||||
|
show: {
|
||||||
|
type: [
|
||||||
|
'rich_text',
|
||||||
|
],
|
||||||
|
richText: [
|
||||||
|
true,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
displayName: 'Phone Number',
|
||||||
|
name: 'phoneValue',
|
||||||
|
type: 'string',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
type: [
|
||||||
|
'phone_number',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: `Phone number. No structure is enforced.`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Options',
|
||||||
|
name: 'multiSelectValue',
|
||||||
|
type: 'multiOptions',
|
||||||
|
typeOptions: {
|
||||||
|
loadOptionsMethod: 'getDatabaseOptionsFromPage',
|
||||||
|
},
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
type: [
|
||||||
|
'multi_select',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: [],
|
||||||
|
description: `Name of the options you want to set.
|
||||||
|
Multiples can be defined separated by comma.`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Option',
|
||||||
|
name: 'selectValue',
|
||||||
|
type: 'options',
|
||||||
|
typeOptions: {
|
||||||
|
loadOptionsMethod: 'getDatabaseOptionsFromPage',
|
||||||
|
},
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
type: [
|
||||||
|
'select',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: `Name of the option you want to set.`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Email',
|
||||||
|
name: 'emailValue',
|
||||||
|
type: 'string',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
type: [
|
||||||
|
'email',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: 'Email address.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'URL',
|
||||||
|
name: 'urlValue',
|
||||||
|
type: 'string',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
type: [
|
||||||
|
'url',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: 'Web address.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'User IDs',
|
||||||
|
name: 'peopleValue',
|
||||||
|
type: 'multiOptions',
|
||||||
|
typeOptions: {
|
||||||
|
loadOptionsMethod: 'getUsers',
|
||||||
|
},
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
type: [
|
||||||
|
'people',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: [],
|
||||||
|
description: 'List of users. Multiples can be defined separated by comma.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Relation IDs',
|
||||||
|
name: 'relationValue',
|
||||||
|
type: 'string',
|
||||||
|
typeOptions: {
|
||||||
|
multipleValues: true,
|
||||||
|
},
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
type: [
|
||||||
|
'relation',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: [],
|
||||||
|
description: 'List of databases that belong to another database. Multiples can be defined separated by comma.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Checked',
|
||||||
|
name: 'checkboxValue',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
type: [
|
||||||
|
'checkbox',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
description: `
|
||||||
|
Whether or not the checkbox is checked.</br>
|
||||||
|
true represents checked.</br>
|
||||||
|
false represents unchecked.
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Number',
|
||||||
|
name: 'numberValue',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
type: [
|
||||||
|
'number',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
type: 'number',
|
||||||
|
default: 0,
|
||||||
|
description: 'Number value.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Range',
|
||||||
|
name: 'range',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
type: [
|
||||||
|
'date',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
description: 'Weather or not you want to define a date range.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Date',
|
||||||
|
name: 'date',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
range: [
|
||||||
|
false,
|
||||||
|
],
|
||||||
|
type: [
|
||||||
|
'date',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
type: 'dateTime',
|
||||||
|
default: '',
|
||||||
|
description: 'An ISO 8601 format date, with optional time.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Date Start',
|
||||||
|
name: 'dateStart',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
range: [
|
||||||
|
true,
|
||||||
|
],
|
||||||
|
type: [
|
||||||
|
'date',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
type: 'dateTime',
|
||||||
|
default: '',
|
||||||
|
description: 'An ISO 8601 format date, with optional time.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Date End',
|
||||||
|
name: 'dateEnd',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
range: [
|
||||||
|
true,
|
||||||
|
],
|
||||||
|
type: [
|
||||||
|
'date',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
type: 'dateTime',
|
||||||
|
default: '',
|
||||||
|
description: `
|
||||||
|
An ISO 8601 formatted date, with optional time. Represents the end of a date range.`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* databasePage:getAll */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
{
|
||||||
|
displayName: 'Database ID',
|
||||||
|
name: 'databaseId',
|
||||||
|
type: 'options',
|
||||||
|
typeOptions: {
|
||||||
|
loadOptionsMethod: 'getDatabases',
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'databasePage',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Return All',
|
||||||
|
name: 'returnAll',
|
||||||
|
type: 'boolean',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'databasePage',
|
||||||
|
],
|
||||||
|
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: [
|
||||||
|
'databasePage',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
returnAll: [
|
||||||
|
false,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
typeOptions: {
|
||||||
|
minValue: 1,
|
||||||
|
maxValue: 100,
|
||||||
|
},
|
||||||
|
default: 50,
|
||||||
|
description: 'How many results to return.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Simple',
|
||||||
|
name: 'simple',
|
||||||
|
type: 'boolean',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'databasePage',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: true,
|
||||||
|
description: 'When set to true a simplify version of the response will be used else the raw data.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Options',
|
||||||
|
name: 'options',
|
||||||
|
type: 'collection',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
resource: [
|
||||||
|
'databasePage',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: {},
|
||||||
|
placeholder: 'Add Field',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
displayName: 'Filters',
|
||||||
|
name: 'filter',
|
||||||
|
placeholder: 'Add Filter',
|
||||||
|
type: 'fixedCollection',
|
||||||
|
typeOptions: {
|
||||||
|
multipleValues: false,
|
||||||
|
},
|
||||||
|
default: {},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
displayName: 'Single Condition',
|
||||||
|
name: 'singleCondition',
|
||||||
|
values: [
|
||||||
|
...filters,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Multiple Condition',
|
||||||
|
name: 'multipleCondition',
|
||||||
|
values: [
|
||||||
|
{
|
||||||
|
displayName: 'Condition',
|
||||||
|
name: 'condition',
|
||||||
|
placeholder: 'Add Condition',
|
||||||
|
type: 'fixedCollection',
|
||||||
|
typeOptions: {
|
||||||
|
multipleValues: true,
|
||||||
|
},
|
||||||
|
default: {},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
displayName: 'OR',
|
||||||
|
name: 'or',
|
||||||
|
values: [
|
||||||
|
...filters,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'AND',
|
||||||
|
name: 'and',
|
||||||
|
values: [
|
||||||
|
...filters,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Sort',
|
||||||
|
name: 'sort',
|
||||||
|
placeholder: 'Add Sort',
|
||||||
|
type: 'fixedCollection',
|
||||||
|
typeOptions: {
|
||||||
|
multipleValues: true,
|
||||||
|
},
|
||||||
|
default: {},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
displayName: 'Sort',
|
||||||
|
name: 'sortValue',
|
||||||
|
values: [
|
||||||
|
{
|
||||||
|
displayName: 'Timestamp',
|
||||||
|
name: 'timestamp',
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
description: `Whether or not to use the record's timestamp to sort the response.`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Property Name',
|
||||||
|
name: 'key',
|
||||||
|
type: 'options',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
timestamp: [
|
||||||
|
false,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
typeOptions: {
|
||||||
|
loadOptionsMethod: 'getFilterProperties',
|
||||||
|
loadOptionsDependsOn: [
|
||||||
|
'datatabaseId',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: 'The name of the property to filter by.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Property Name',
|
||||||
|
name: 'key',
|
||||||
|
type: 'options',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Created Time',
|
||||||
|
value: 'created_time',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Last Edited Time',
|
||||||
|
value: 'last_edited_time',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
timestamp: [
|
||||||
|
true,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: 'The name of the property to filter by.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Type',
|
||||||
|
name: 'type',
|
||||||
|
type: 'hidden',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
timestamp: [
|
||||||
|
true,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '={{$parameter["&key"].split("|")[1]}}',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Direction',
|
||||||
|
name: 'direction',
|
||||||
|
type: 'options',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Ascending',
|
||||||
|
value: 'ascending',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Descending',
|
||||||
|
value: 'descending',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
default: '',
|
||||||
|
description: 'The direction to sort.',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
] as INodeProperties[];
|
371
packages/nodes-base/nodes/Notion/Filters.ts
Normal file
371
packages/nodes-base/nodes/Notion/Filters.ts
Normal file
|
@ -0,0 +1,371 @@
|
||||||
|
import {
|
||||||
|
getConditions
|
||||||
|
} from './GenericFunctions';
|
||||||
|
|
||||||
|
export const filters = [{
|
||||||
|
displayName: 'Property Name',
|
||||||
|
name: 'key',
|
||||||
|
type: 'options',
|
||||||
|
typeOptions: {
|
||||||
|
loadOptionsMethod: 'getFilterProperties',
|
||||||
|
loadOptionsDependsOn: [
|
||||||
|
'datatabaseId',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: 'The name of the property to filter by.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Type',
|
||||||
|
name: 'type',
|
||||||
|
type: 'hidden',
|
||||||
|
default: '={{$parameter["&key"].split("|")[1]}}',
|
||||||
|
},
|
||||||
|
...getConditions(),
|
||||||
|
{
|
||||||
|
displayName: 'Title',
|
||||||
|
name: 'titleValue',
|
||||||
|
type: 'string',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
type: [
|
||||||
|
'title',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
hide: {
|
||||||
|
condition: [
|
||||||
|
'is_empty',
|
||||||
|
'is_not_empty',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Text',
|
||||||
|
name: 'richTextValue',
|
||||||
|
type: 'string',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
type: [
|
||||||
|
'rich_text',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
hide: {
|
||||||
|
condition: [
|
||||||
|
'is_empty',
|
||||||
|
'is_not_empty',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Phone Number',
|
||||||
|
name: 'phoneNumberValue',
|
||||||
|
type: 'string',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
type: [
|
||||||
|
'phone_number',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
hide: {
|
||||||
|
condition: [
|
||||||
|
'is_empty',
|
||||||
|
'is_not_empty',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: `Phone number. No structure is enforced.`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Option',
|
||||||
|
name: 'multiSelectValue',
|
||||||
|
type: 'options',
|
||||||
|
typeOptions: {
|
||||||
|
loadOptionsMethod: 'getPropertySelectValues',
|
||||||
|
},
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
type: [
|
||||||
|
'multi_select',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
hide: {
|
||||||
|
condition: [
|
||||||
|
'is_empty',
|
||||||
|
'is_not_empty',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: [],
|
||||||
|
description: `Name of the options you want to set.
|
||||||
|
Multiples can be defined separated by comma.`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Option',
|
||||||
|
name: 'selectValue',
|
||||||
|
type: 'options',
|
||||||
|
typeOptions: {
|
||||||
|
loadOptionsMethod: 'getPropertySelectValues',
|
||||||
|
},
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
type: [
|
||||||
|
'select',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
hide: {
|
||||||
|
condition: [
|
||||||
|
'is_empty',
|
||||||
|
'is_not_empty',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: `Name of the option you want to set.`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Email',
|
||||||
|
name: 'emailValue',
|
||||||
|
type: 'string',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
type: [
|
||||||
|
'email',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
hide: {
|
||||||
|
condition: [
|
||||||
|
'is_empty',
|
||||||
|
'is_not_empty',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: 'Email address.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'URL',
|
||||||
|
name: 'urlValue',
|
||||||
|
type: 'string',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
type: [
|
||||||
|
'url',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
hide: {
|
||||||
|
condition: [
|
||||||
|
'is_empty',
|
||||||
|
'is_not_empty',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: 'Web address.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'User ID',
|
||||||
|
name: 'peopleValue',
|
||||||
|
type: 'options',
|
||||||
|
typeOptions: {
|
||||||
|
loadOptionsMethod: 'getUsers',
|
||||||
|
},
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
type: [
|
||||||
|
'people',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
hide: {
|
||||||
|
condition: [
|
||||||
|
'is_empty',
|
||||||
|
'is_not_empty',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: 'List of users. Multiples can be defined separated by comma.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'User ID',
|
||||||
|
name: 'createdByValue',
|
||||||
|
type: 'options',
|
||||||
|
typeOptions: {
|
||||||
|
loadOptionsMethod: 'getUsers',
|
||||||
|
},
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
type: [
|
||||||
|
'created_by',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
hide: {
|
||||||
|
condition: [
|
||||||
|
'is_empty',
|
||||||
|
'is_not_empty',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: 'List of users. Multiples can be defined separated by comma.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'User ID',
|
||||||
|
name: 'lastEditedByValue',
|
||||||
|
type: 'options',
|
||||||
|
typeOptions: {
|
||||||
|
loadOptionsMethod: 'getUsers',
|
||||||
|
},
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
type: [
|
||||||
|
'last_edited_by',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
hide: {
|
||||||
|
condition: [
|
||||||
|
'is_empty',
|
||||||
|
'is_not_empty',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: 'List of users. Multiples can be defined separated by comma.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Relation ID',
|
||||||
|
name: 'relationValue',
|
||||||
|
type: 'string',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
type: [
|
||||||
|
'relation',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
hide: {
|
||||||
|
condition: [
|
||||||
|
'is_empty',
|
||||||
|
'is_not_empty',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Checked',
|
||||||
|
name: 'checkboxValue',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
type: [
|
||||||
|
'checkbox',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
description: `Whether or not the checkbox is checked.</br>
|
||||||
|
true represents checked.</br>
|
||||||
|
false represents unchecked.`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Number',
|
||||||
|
name: 'numberValue',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
type: [
|
||||||
|
'number',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
hide: {
|
||||||
|
condition: [
|
||||||
|
'is_empty',
|
||||||
|
'is_not_empty',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
type: 'number',
|
||||||
|
default: 0,
|
||||||
|
description: 'Number value.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Date',
|
||||||
|
name: 'date',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
type: [
|
||||||
|
'date',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
hide: {
|
||||||
|
condition: [
|
||||||
|
'is_empty',
|
||||||
|
'is_not_empty',
|
||||||
|
'past_week',
|
||||||
|
'past_month',
|
||||||
|
'past_year',
|
||||||
|
'next_week',
|
||||||
|
'next_month',
|
||||||
|
'next_year',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
type: 'dateTime',
|
||||||
|
default: '',
|
||||||
|
description: 'An ISO 8601 format date, with optional time.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Created Time',
|
||||||
|
name: 'createdTimeValue',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
type: [
|
||||||
|
'created_time',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
hide: {
|
||||||
|
condition: [
|
||||||
|
'is_empty',
|
||||||
|
'is_not_empty',
|
||||||
|
'past_week',
|
||||||
|
'past_month',
|
||||||
|
'past_year',
|
||||||
|
'next_week',
|
||||||
|
'next_month',
|
||||||
|
'next_year',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
type: 'dateTime',
|
||||||
|
default: '',
|
||||||
|
description: 'An ISO 8601 format date, with optional time.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Last Edited Time',
|
||||||
|
name: 'lastEditedTime',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
type: [
|
||||||
|
'last_edited_time',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
hide: {
|
||||||
|
condition: [
|
||||||
|
'is_empty',
|
||||||
|
'is_not_empty',
|
||||||
|
'past_week',
|
||||||
|
'past_month',
|
||||||
|
'past_year',
|
||||||
|
'next_week',
|
||||||
|
'next_month',
|
||||||
|
'next_year',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
type: 'dateTime',
|
||||||
|
default: '',
|
||||||
|
description: 'An ISO 8601 format date, with optional time.',
|
||||||
|
}];
|
544
packages/nodes-base/nodes/Notion/GenericFunctions.ts
Normal file
544
packages/nodes-base/nodes/Notion/GenericFunctions.ts
Normal file
|
@ -0,0 +1,544 @@
|
||||||
|
import {
|
||||||
|
OptionsWithUri,
|
||||||
|
} from 'request';
|
||||||
|
|
||||||
|
import {
|
||||||
|
IExecuteFunctions,
|
||||||
|
IExecuteSingleFunctions,
|
||||||
|
IHookFunctions,
|
||||||
|
ILoadOptionsFunctions,
|
||||||
|
} from 'n8n-core';
|
||||||
|
|
||||||
|
import {
|
||||||
|
IDataObject,
|
||||||
|
IDisplayOptions,
|
||||||
|
INodeProperties,
|
||||||
|
IPollFunctions,
|
||||||
|
NodeApiError,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
import {
|
||||||
|
camelCase,
|
||||||
|
capitalCase,
|
||||||
|
} from 'change-case';
|
||||||
|
|
||||||
|
import * as moment from 'moment-timezone';
|
||||||
|
|
||||||
|
export async function notionApiRequest(this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions | IPollFunctions, method: string, resource: string, body: any = {}, qs: IDataObject = {}, uri?: string, option: IDataObject = {}): Promise<any> { // tslint:disable-line:no-any
|
||||||
|
|
||||||
|
try {
|
||||||
|
let options: OptionsWithUri = {
|
||||||
|
headers: {
|
||||||
|
'Notion-Version': '2021-05-13',
|
||||||
|
},
|
||||||
|
method,
|
||||||
|
qs,
|
||||||
|
body,
|
||||||
|
uri: uri || `https://api.notion.com/v1${resource}`,
|
||||||
|
json: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
options = Object.assign({}, options, option);
|
||||||
|
const credentials = this.getCredentials('notionApi') as IDataObject;
|
||||||
|
options!.headers!['Authorization'] = `Bearer ${credentials.apiKey}`;
|
||||||
|
return this.helpers.request!(options);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
throw new NodeApiError(this.getNode(), error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function notionApiRequestAllItems(this: IExecuteFunctions | ILoadOptionsFunctions | IPollFunctions, propertyName: string, method: string, endpoint: string, body: any = {}, query: IDataObject = {}): Promise<any> { // tslint:disable-line:no-any
|
||||||
|
|
||||||
|
const returnData: IDataObject[] = [];
|
||||||
|
|
||||||
|
let responseData;
|
||||||
|
|
||||||
|
do {
|
||||||
|
responseData = await notionApiRequest.call(this, method, endpoint, body, query);
|
||||||
|
const { next_cursor } = responseData;
|
||||||
|
query['start_cursor'] = next_cursor;
|
||||||
|
body['start_cursor'] = next_cursor;
|
||||||
|
returnData.push.apply(returnData, responseData[propertyName]);
|
||||||
|
if (query.limit && query.limit <= returnData.length) {
|
||||||
|
return returnData;
|
||||||
|
}
|
||||||
|
} while (
|
||||||
|
responseData.has_more !== false
|
||||||
|
);
|
||||||
|
|
||||||
|
return returnData;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getBlockTypes() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
name: 'Paragraph',
|
||||||
|
value: 'paragraph',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Heading 1',
|
||||||
|
value: 'heading_1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Heading 2',
|
||||||
|
value: 'heading_2',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Heading 3',
|
||||||
|
value: 'heading_3',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Toggle',
|
||||||
|
value: 'toggle',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'To-Do',
|
||||||
|
value: 'to_do',
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// name: 'Child Page',
|
||||||
|
// value: 'child_page',
|
||||||
|
// },
|
||||||
|
{
|
||||||
|
name: 'Bulleted List Item',
|
||||||
|
value: 'bulleted_list_item',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Numbered List Item',
|
||||||
|
value: 'numbered_list_item',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
function textContent(content: string) {
|
||||||
|
return {
|
||||||
|
text: {
|
||||||
|
content,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function formatTitle(content: string) {
|
||||||
|
return {
|
||||||
|
title: [
|
||||||
|
textContent(content),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function formatText(content: string) {
|
||||||
|
return {
|
||||||
|
text: [
|
||||||
|
textContent(content),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLink(text: { textLink: string, isLink: boolean }) {
|
||||||
|
if (text.isLink === true && text.textLink !== '') {
|
||||||
|
return {
|
||||||
|
link: {
|
||||||
|
url: text.textLink,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTexts(texts: [{ textType: string, text: string, isLink: boolean, range: boolean, textLink: string, mentionType: string, dateStart: string, dateEnd: string, date: string, annotationUi: IDataObject, expression: string }]) {
|
||||||
|
const results = [];
|
||||||
|
for (const text of texts) {
|
||||||
|
if (text.textType === 'text') {
|
||||||
|
results.push({
|
||||||
|
type: 'text',
|
||||||
|
text: {
|
||||||
|
content: text.text,
|
||||||
|
...getLink(text),
|
||||||
|
},
|
||||||
|
annotations: text.annotationUi,
|
||||||
|
});
|
||||||
|
} else if (text.textType === 'mention') {
|
||||||
|
if (text.mentionType === 'date') {
|
||||||
|
results.push({
|
||||||
|
type: 'mention',
|
||||||
|
mention: {
|
||||||
|
type: text.mentionType,
|
||||||
|
[text.mentionType]: (text.range === true)
|
||||||
|
? { start: text.dateStart, end: text.dateEnd }
|
||||||
|
: { start: text.date, end: null },
|
||||||
|
},
|
||||||
|
annotations: text.annotationUi,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
//@ts-ignore
|
||||||
|
results.push({
|
||||||
|
type: 'mention',
|
||||||
|
mention: {
|
||||||
|
type: text.mentionType,
|
||||||
|
//@ts-ignore
|
||||||
|
[text.mentionType]: { id: text[text.mentionType] as string },
|
||||||
|
},
|
||||||
|
annotations: text.annotationUi,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else if (text.textType === 'equation') {
|
||||||
|
results.push({
|
||||||
|
type: 'equation',
|
||||||
|
equation: {
|
||||||
|
expression: text.expression,
|
||||||
|
},
|
||||||
|
annotations: text.annotationUi,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function formatBlocks(blocks: IDataObject[]) {
|
||||||
|
const results = [];
|
||||||
|
for (const block of blocks) {
|
||||||
|
results.push({
|
||||||
|
object: 'block',
|
||||||
|
type: block.type,
|
||||||
|
[block.type as string]: {
|
||||||
|
...(block.type === 'to_do') ? { checked: block.checked } : { checked: false },
|
||||||
|
//@ts-expect-error
|
||||||
|
// tslint:disable-next-line: no-any
|
||||||
|
text: (block.richText === false) ? formatText(block.textContent).text : getTexts(block.text.text as any || []),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
// tslint:disable-next-line: no-any
|
||||||
|
function getPropertyKeyValue(value: any, type: string, timezone: string) {
|
||||||
|
let result = {};
|
||||||
|
switch (type) {
|
||||||
|
case 'rich_text':
|
||||||
|
if (value.richText === false) {
|
||||||
|
result = { rich_text: [{ text: { content: value.textContent } }] };
|
||||||
|
} else {
|
||||||
|
result = { rich_text: getTexts(value.text.text) };
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'title':
|
||||||
|
result = { title: [{ text: { content: value.title } }] };
|
||||||
|
break;
|
||||||
|
case 'number':
|
||||||
|
result = { type: 'number', number: value.numberValue };
|
||||||
|
break;
|
||||||
|
case 'url':
|
||||||
|
result = { type: 'url', url: value.urlValue };
|
||||||
|
break;
|
||||||
|
case 'checkbox':
|
||||||
|
result = { type: 'checkbox', checkbox: value.checkboxValue };
|
||||||
|
break;
|
||||||
|
case 'relation':
|
||||||
|
result = {
|
||||||
|
// tslint:disable-next-line: no-any
|
||||||
|
type: 'relation', relation: (value.relationValue).reduce((acc: [], cur: any) => {
|
||||||
|
return acc.concat(cur.split(',').map((relation: string) => ({ id: relation })));
|
||||||
|
}, []),
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case 'multi_select':
|
||||||
|
result = {
|
||||||
|
// tslint:disable-next-line: no-any
|
||||||
|
type: 'multi_select', multi_select: value.multiSelectValue.filter((id: any) => id !== null).map((option: string) => ({ id: option })),
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case 'email':
|
||||||
|
result = {
|
||||||
|
type: 'email', email: value.emailValue,
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case 'people':
|
||||||
|
result = {
|
||||||
|
type: 'people', people: value.peopleValue.map((option: string) => ({ id: option })),
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case 'phone_number':
|
||||||
|
result = {
|
||||||
|
type: 'phone_number', phone_number: value.phoneValue,
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case 'select':
|
||||||
|
result = {
|
||||||
|
type: 'select', select: { id: value.selectValue },
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case 'date':
|
||||||
|
//&& value.dateStart !== 'Invalid date' && value.dateEnd !== 'Invalid date'
|
||||||
|
if (value.range === true) {
|
||||||
|
result = {
|
||||||
|
type: 'date', date: { start: moment.tz(value.dateStart, timezone).utc().format(), end: moment.tz(value.dateEnd, timezone).utc().format() },
|
||||||
|
};
|
||||||
|
//if (value.date !== 'Invalid date')
|
||||||
|
} else {
|
||||||
|
result = {
|
||||||
|
type: 'date', date: { start: moment.tz(value.date, timezone).utc().format(), end: null },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getNameAndType(key: string) {
|
||||||
|
const [name, type] = key.split('|');
|
||||||
|
return {
|
||||||
|
name,
|
||||||
|
type,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function mapProperties(properties: IDataObject[], timezone: string) {
|
||||||
|
return properties.reduce((obj, value) => Object.assign(obj, {
|
||||||
|
[`${(value.key as string).split('|')[0]}`]: getPropertyKeyValue(value, (value.key as string).split('|')[1], timezone),
|
||||||
|
}), {});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function mapSorting(data: [{ key: string, type: string, direction: string, timestamp: boolean }]) {
|
||||||
|
return data.map((sort) => {
|
||||||
|
return {
|
||||||
|
direction: sort.direction,
|
||||||
|
[(sort.timestamp) ? 'timestamp' : 'property']: sort.key.split('|')[0],
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function mapFilters(filters: IDataObject[], timezone: string) {
|
||||||
|
// tslint:disable-next-line: no-any
|
||||||
|
return filters.reduce((obj, value: { [key: string]: any }) => {
|
||||||
|
let key = getNameAndType(value.key).type;
|
||||||
|
let valuePropertyName = value[`${camelCase(key)}Value`];
|
||||||
|
if (['is_empty', 'is_not_empty'].includes(value.condition as string)) {
|
||||||
|
valuePropertyName = true;
|
||||||
|
} else if (['past_week', 'past_month', 'past_year', 'next_week', 'next_month', 'next_year'].includes(value.condition as string)) {
|
||||||
|
valuePropertyName = {};
|
||||||
|
}
|
||||||
|
if (key === 'rich_text') {
|
||||||
|
key = 'text';
|
||||||
|
} else if (key === 'phone_number') {
|
||||||
|
key = 'phone';
|
||||||
|
} else if (key === 'date') {
|
||||||
|
valuePropertyName = (valuePropertyName !== undefined && !Object.keys(valuePropertyName).length) ? {} : moment.tz(value.date, timezone).utc().format();
|
||||||
|
}
|
||||||
|
return Object.assign(obj, {
|
||||||
|
['property']: getNameAndType(value.key).name,
|
||||||
|
[key]: { [`${value.condition}`]: valuePropertyName },
|
||||||
|
});
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
// tslint:disable-next-line: no-any
|
||||||
|
export function simplifyProperties(properties: any) {
|
||||||
|
// tslint:disable-next-line: no-any
|
||||||
|
const results: any = {};
|
||||||
|
for (const key of Object.keys(properties)) {
|
||||||
|
const type = (properties[key] as IDataObject).type as string;
|
||||||
|
if (['text'].includes(properties[key].type)) {
|
||||||
|
const texts = properties[key].text.map((e: { plain_text: string }) => e.plain_text || {}).join('');
|
||||||
|
results[`${key}`] = texts;
|
||||||
|
} else if (['url', 'created_time', 'checkbox', 'number', 'last_edited_time', 'email', 'phone_number', 'date'].includes(properties[key].type)) {
|
||||||
|
// tslint:disable-next-line: no-any
|
||||||
|
results[`${key}`] = properties[key][type] as any;
|
||||||
|
} else if (['title'].includes(properties[key].type)) {
|
||||||
|
if (Array.isArray(properties[key][type]) && properties[key][type].length !== 0) {
|
||||||
|
results[`${key}`] = properties[key][type][0].plain_text;
|
||||||
|
} else {
|
||||||
|
results[`${key}`] = '';
|
||||||
|
}
|
||||||
|
} else if (['created_by', 'last_edited_by', 'select'].includes(properties[key].type)) {
|
||||||
|
results[`${key}`] = properties[key][type].name;
|
||||||
|
} else if (['people'].includes(properties[key].type)) {
|
||||||
|
if (Array.isArray(properties[key][type])) {
|
||||||
|
// tslint:disable-next-line: no-any
|
||||||
|
results[`${key}`] = properties[key][type].map((person: any) => person.person.email || {});
|
||||||
|
} else {
|
||||||
|
results[`${key}`] = properties[key][type];
|
||||||
|
}
|
||||||
|
} else if (['multi_select'].includes(properties[key].type)) {
|
||||||
|
if (Array.isArray(properties[key][type])) {
|
||||||
|
results[`${key}`] = properties[key][type].map((e: IDataObject) => e.name || {});
|
||||||
|
} else {
|
||||||
|
results[`${key}`] = properties[key][type].options.map((e: IDataObject) => e.name || {});
|
||||||
|
}
|
||||||
|
} else if (['relation'].includes(properties[key].type)) {
|
||||||
|
if (Array.isArray(properties[key][type])) {
|
||||||
|
results[`${key}`] = properties[key][type].map((e: IDataObject) => e.id || {});
|
||||||
|
} else {
|
||||||
|
results[`${key}`] = properties[key][type].database_id;
|
||||||
|
}
|
||||||
|
} else if (['formula'].includes(properties[key].type)) {
|
||||||
|
results[`${key}`] = properties[key][type][properties[key][type].type];
|
||||||
|
|
||||||
|
} else if (['rollup'].includes(properties[key].type)) {
|
||||||
|
//TODO figure how to resolve rollup field type
|
||||||
|
// results[`${key}`] = properties[key][type][properties[key][type].type];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
// tslint:disable-next-line: no-any
|
||||||
|
export function simplifyObjects(objects: any) {
|
||||||
|
if (!Array.isArray(objects)) {
|
||||||
|
objects = [objects];
|
||||||
|
}
|
||||||
|
const results: IDataObject[] = [];
|
||||||
|
for (const { object, id, properties, parent, title } of objects) {
|
||||||
|
if (object === 'page' && (parent.type === 'page_id' || parent.type === 'workspace')) {
|
||||||
|
results.push({
|
||||||
|
id,
|
||||||
|
title: properties.title.title[0].plain_text,
|
||||||
|
});
|
||||||
|
} else if (object === 'page' && parent.type === 'database_id') {
|
||||||
|
results.push({
|
||||||
|
id,
|
||||||
|
...simplifyProperties(properties),
|
||||||
|
});
|
||||||
|
} else if (object === 'database') {
|
||||||
|
results.push({
|
||||||
|
id,
|
||||||
|
title: title[0].plain_text,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getFormattedChildren(children: IDataObject[]) {
|
||||||
|
const results: IDataObject[] = [];
|
||||||
|
for (const child of children) {
|
||||||
|
const type = child.type;
|
||||||
|
results.push({ [`${type}`]: child, object: 'block', type });
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getConditions() {
|
||||||
|
|
||||||
|
const elements: INodeProperties[] = [];
|
||||||
|
|
||||||
|
const types: { [key: string]: string } = {
|
||||||
|
title: 'rich_text',
|
||||||
|
rich_text: 'rich_text',
|
||||||
|
number: 'number',
|
||||||
|
checkbox: 'checkbox',
|
||||||
|
select: 'select',
|
||||||
|
multi_select: 'multi_select',
|
||||||
|
date: 'date',
|
||||||
|
people: 'people',
|
||||||
|
files: 'files',
|
||||||
|
url: 'rich_text',
|
||||||
|
email: 'rich_text',
|
||||||
|
phone_number: 'rich_text',
|
||||||
|
relation: 'relation',
|
||||||
|
//formula: 'formula',
|
||||||
|
created_by: 'people',
|
||||||
|
created_time: 'date',
|
||||||
|
last_edited_by: 'people',
|
||||||
|
last_edited_time: 'date',
|
||||||
|
};
|
||||||
|
|
||||||
|
const typeConditions: { [key: string]: string[] } = {
|
||||||
|
rich_text: [
|
||||||
|
'equals',
|
||||||
|
'does_not_equal',
|
||||||
|
'contains',
|
||||||
|
'does_not_contain',
|
||||||
|
'starts_with',
|
||||||
|
'ends_with',
|
||||||
|
'is_empty',
|
||||||
|
'is_not_empty',
|
||||||
|
],
|
||||||
|
number: [
|
||||||
|
'equals',
|
||||||
|
'does_not_equal',
|
||||||
|
'grater_than',
|
||||||
|
'less_than',
|
||||||
|
'greater_than_or_equal_to',
|
||||||
|
'less_than_or_equal_to',
|
||||||
|
'is_empty',
|
||||||
|
'is_not_empty',
|
||||||
|
],
|
||||||
|
checkbox: [
|
||||||
|
'equals',
|
||||||
|
'does_not_equal',
|
||||||
|
],
|
||||||
|
select: [
|
||||||
|
'equals',
|
||||||
|
'does_not_equal',
|
||||||
|
'is_empty',
|
||||||
|
'is_not_empty',
|
||||||
|
],
|
||||||
|
multi_select: [
|
||||||
|
'contains',
|
||||||
|
'does_not_equal',
|
||||||
|
'is_empty',
|
||||||
|
'is_not_empty',
|
||||||
|
],
|
||||||
|
date: [
|
||||||
|
'equals',
|
||||||
|
'before',
|
||||||
|
'after',
|
||||||
|
'on_or_before',
|
||||||
|
'is_empty',
|
||||||
|
'is_not_empty',
|
||||||
|
'on_or_after',
|
||||||
|
'past_week',
|
||||||
|
'past_month',
|
||||||
|
'past_year',
|
||||||
|
'next_week',
|
||||||
|
'next_month',
|
||||||
|
'next_year',
|
||||||
|
],
|
||||||
|
people: [
|
||||||
|
'contains',
|
||||||
|
'does_not_contain',
|
||||||
|
'is_empty',
|
||||||
|
'is_not_empty',
|
||||||
|
],
|
||||||
|
files: [
|
||||||
|
'is_empty',
|
||||||
|
'is_not_empty',
|
||||||
|
],
|
||||||
|
relation: [
|
||||||
|
'contains',
|
||||||
|
'does_not_contain',
|
||||||
|
'is_empty',
|
||||||
|
'is_not_empty',
|
||||||
|
],
|
||||||
|
formula: [
|
||||||
|
'contains',
|
||||||
|
'does_not_contain',
|
||||||
|
'is_empty',
|
||||||
|
'is_not_empty',
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const type of Object.keys(types)) {
|
||||||
|
elements.push(
|
||||||
|
{
|
||||||
|
displayName: 'Condition',
|
||||||
|
name: 'condition',
|
||||||
|
type: 'options',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
type: [
|
||||||
|
type,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
} as IDisplayOptions,
|
||||||
|
options: (typeConditions[types[type]] as string[]).map((type: string) => ({ name: capitalCase(type), value: type })),
|
||||||
|
default: '',
|
||||||
|
description: 'The value of the property to filter by.',
|
||||||
|
} as INodeProperties,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return elements;
|
||||||
|
}
|
517
packages/nodes-base/nodes/Notion/Notion.node.ts
Normal file
517
packages/nodes-base/nodes/Notion/Notion.node.ts
Normal file
|
@ -0,0 +1,517 @@
|
||||||
|
import {
|
||||||
|
IExecuteFunctions,
|
||||||
|
} from 'n8n-core';
|
||||||
|
|
||||||
|
import {
|
||||||
|
IDataObject,
|
||||||
|
ILoadOptionsFunctions,
|
||||||
|
INodeExecutionData,
|
||||||
|
INodePropertyOptions,
|
||||||
|
INodeType,
|
||||||
|
INodeTypeDescription,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
import {
|
||||||
|
formatBlocks,
|
||||||
|
formatTitle,
|
||||||
|
getBlockTypes,
|
||||||
|
mapFilters,
|
||||||
|
mapProperties,
|
||||||
|
mapSorting,
|
||||||
|
notionApiRequest,
|
||||||
|
notionApiRequestAllItems,
|
||||||
|
simplifyObjects,
|
||||||
|
} from './GenericFunctions';
|
||||||
|
|
||||||
|
import {
|
||||||
|
databaseFields,
|
||||||
|
databaseOperations,
|
||||||
|
} from './DatabaseDescription';
|
||||||
|
|
||||||
|
import {
|
||||||
|
userFields,
|
||||||
|
userOperations,
|
||||||
|
} from './UserDescription';
|
||||||
|
|
||||||
|
import {
|
||||||
|
pageFields,
|
||||||
|
pageOperations,
|
||||||
|
} from './PageDescription';
|
||||||
|
|
||||||
|
import {
|
||||||
|
blockFields,
|
||||||
|
blockOperations,
|
||||||
|
} from './BlockDescription';
|
||||||
|
|
||||||
|
import {
|
||||||
|
databasePageFields,
|
||||||
|
databasePageOperations,
|
||||||
|
} from './DatabasePageDescription';
|
||||||
|
|
||||||
|
export class Notion implements INodeType {
|
||||||
|
description: INodeTypeDescription = {
|
||||||
|
displayName: 'Notion (Beta)',
|
||||||
|
name: 'notion',
|
||||||
|
icon: 'file:notion.svg',
|
||||||
|
group: ['output'],
|
||||||
|
version: 1,
|
||||||
|
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
||||||
|
description: 'Consume Notion API (Beta)',
|
||||||
|
defaults: {
|
||||||
|
name: 'Notion',
|
||||||
|
color: '#000000',
|
||||||
|
},
|
||||||
|
inputs: ['main'],
|
||||||
|
outputs: ['main'],
|
||||||
|
credentials: [
|
||||||
|
{
|
||||||
|
name: 'notionApi',
|
||||||
|
required: true,
|
||||||
|
// displayOptions: {
|
||||||
|
// show: {
|
||||||
|
// authentication: [
|
||||||
|
// 'apiKey',
|
||||||
|
// ],
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// name: 'notionOAuth2Api',
|
||||||
|
// required: true,
|
||||||
|
// displayOptions: {
|
||||||
|
// show: {
|
||||||
|
// authentication: [
|
||||||
|
// 'oAuth2',
|
||||||
|
// ],
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
],
|
||||||
|
properties: [
|
||||||
|
// {
|
||||||
|
// displayName: 'Authentication',
|
||||||
|
// name: 'authentication',
|
||||||
|
// type: 'options',
|
||||||
|
// options: [
|
||||||
|
// {
|
||||||
|
// name: 'API Key',
|
||||||
|
// value: 'apiKey',
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// name: 'OAuth2',
|
||||||
|
// value: 'oAuth2',
|
||||||
|
// },
|
||||||
|
// ],
|
||||||
|
// default: 'apiKey',
|
||||||
|
// description: 'The resource to operate on.',
|
||||||
|
// },
|
||||||
|
{
|
||||||
|
displayName: 'Resource',
|
||||||
|
name: 'resource',
|
||||||
|
type: 'options',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Block',
|
||||||
|
value: 'block',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Database',
|
||||||
|
value: 'database',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Database Page',
|
||||||
|
value: 'databasePage',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Page',
|
||||||
|
value: 'page',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'User',
|
||||||
|
value: 'user',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
default: 'page',
|
||||||
|
description: 'Resource to consume.',
|
||||||
|
},
|
||||||
|
...blockOperations,
|
||||||
|
...blockFields,
|
||||||
|
...databaseOperations,
|
||||||
|
...databaseFields,
|
||||||
|
...databasePageOperations,
|
||||||
|
...databasePageFields,
|
||||||
|
...pageOperations,
|
||||||
|
...pageFields,
|
||||||
|
...userOperations,
|
||||||
|
...userFields,
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
methods = {
|
||||||
|
loadOptions: {
|
||||||
|
async getDatabases(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||||
|
const returnData: INodePropertyOptions[] = [];
|
||||||
|
const body: IDataObject = {
|
||||||
|
page_size: 100,
|
||||||
|
filter: { property: 'object', value: 'database' },
|
||||||
|
};
|
||||||
|
const databases = await notionApiRequestAllItems.call(this, 'results', 'POST', `/search`, body);
|
||||||
|
for (const database of databases) {
|
||||||
|
returnData.push({
|
||||||
|
name: database.title[0].plain_text,
|
||||||
|
value: database.id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
returnData.sort((a, b) => {
|
||||||
|
if (a.name.toLocaleLowerCase() < b.name.toLocaleLowerCase()) { return -1; }
|
||||||
|
if (a.name.toLocaleLowerCase() > b.name.toLocaleLowerCase()) { return 1; }
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
return returnData;
|
||||||
|
},
|
||||||
|
async getDatabaseProperties(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||||
|
const returnData: INodePropertyOptions[] = [];
|
||||||
|
const databaseId = this.getCurrentNodeParameter('databaseId') as string;
|
||||||
|
const { properties } = await notionApiRequest.call(this, 'GET', `/databases/${databaseId}`);
|
||||||
|
for (const key of Object.keys(properties)) {
|
||||||
|
//remove parameters that cannot be set from the API.
|
||||||
|
if (!['created_time', 'last_edited_time', 'created_by', 'last_edited_by', 'formula', 'files'].includes(properties[key].type)) {
|
||||||
|
returnData.push({
|
||||||
|
name: `${key} - (${properties[key].type})`,
|
||||||
|
value: `${key}|${properties[key].type}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
returnData.sort((a, b) => {
|
||||||
|
if (a.name.toLocaleLowerCase() < b.name.toLocaleLowerCase()) { return -1; }
|
||||||
|
if (a.name.toLocaleLowerCase() > b.name.toLocaleLowerCase()) { return 1; }
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
return returnData;
|
||||||
|
},
|
||||||
|
async getFilterProperties(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||||
|
const returnData: INodePropertyOptions[] = [];
|
||||||
|
const databaseId = this.getCurrentNodeParameter('databaseId') as string;
|
||||||
|
const { properties } = await notionApiRequest.call(this, 'GET', `/databases/${databaseId}`);
|
||||||
|
for (const key of Object.keys(properties)) {
|
||||||
|
returnData.push({
|
||||||
|
name: `${key} - (${properties[key].type})`,
|
||||||
|
value: `${key}|${properties[key].type}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
returnData.sort((a, b) => {
|
||||||
|
if (a.name.toLocaleLowerCase() < b.name.toLocaleLowerCase()) { return -1; }
|
||||||
|
if (a.name.toLocaleLowerCase() > b.name.toLocaleLowerCase()) { return 1; }
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
return returnData;
|
||||||
|
},
|
||||||
|
async getBlockTypes(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||||
|
return getBlockTypes();
|
||||||
|
},
|
||||||
|
async getPropertySelectValues(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||||
|
const [name, type] = (this.getCurrentNodeParameter('&key') as string).split('|');
|
||||||
|
const databaseId = this.getCurrentNodeParameter('databaseId') as string;
|
||||||
|
const resource = this.getCurrentNodeParameter('resource') as string;
|
||||||
|
const operation = this.getCurrentNodeParameter('operation') as string;
|
||||||
|
const { properties } = await notionApiRequest.call(this, 'GET', `/databases/${databaseId}`);
|
||||||
|
const useNames = (resource === 'databasePage' && operation === 'getAll');
|
||||||
|
return (properties[name][type].options).map((option: IDataObject) => ({ name: option.name, value: (['select', 'multi_select'].includes(type) && useNames) ? option.name : option.id }));
|
||||||
|
},
|
||||||
|
async getUsers(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||||
|
const returnData: INodePropertyOptions[] = [];
|
||||||
|
const users = await notionApiRequestAllItems.call(this, 'results', 'GET', '/users');
|
||||||
|
for (const user of users) {
|
||||||
|
returnData.push({
|
||||||
|
name: user.name,
|
||||||
|
value: user.id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return returnData;
|
||||||
|
},
|
||||||
|
async getDatabaseIdFromPage(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||||
|
const returnData: INodePropertyOptions[] = [];
|
||||||
|
const pageId = this.getCurrentNodeParameter('pageId') as string;
|
||||||
|
const { parent: { database_id: databaseId } } = await notionApiRequest.call(this, 'GET', `/pages/${pageId}`);
|
||||||
|
const { properties } = await notionApiRequest.call(this, 'GET', `/databases/${databaseId}`);
|
||||||
|
for (const key of Object.keys(properties)) {
|
||||||
|
//remove parameters that cannot be set from the API.
|
||||||
|
if (!['created_time', 'last_edited_time', 'created_by', 'last_edited_by', 'formula', 'files'].includes(properties[key].type)) {
|
||||||
|
returnData.push({
|
||||||
|
name: `${key} - (${properties[key].type})`,
|
||||||
|
value: `${key}|${properties[key].type}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
returnData.sort((a, b) => {
|
||||||
|
if (a.name.toLocaleLowerCase() < b.name.toLocaleLowerCase()) { return -1; }
|
||||||
|
if (a.name.toLocaleLowerCase() > b.name.toLocaleLowerCase()) { return 1; }
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
return returnData;
|
||||||
|
},
|
||||||
|
|
||||||
|
async getDatabaseOptionsFromPage(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||||
|
const pageId = this.getCurrentNodeParameter('pageId') as string;
|
||||||
|
const [name, type] = (this.getCurrentNodeParameter('&key') as string).split('|');
|
||||||
|
const { parent: { database_id: databaseId } } = await notionApiRequest.call(this, 'GET', `/pages/${pageId}`);
|
||||||
|
const { properties } = await notionApiRequest.call(this, 'GET', `/databases/${databaseId}`);
|
||||||
|
return (properties[name][type].options).map((option: IDataObject) => ({ name: option.name, value: option.id }));
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
||||||
|
const items = this.getInputData();
|
||||||
|
const returnData: IDataObject[] = [];
|
||||||
|
const length = items.length as unknown as number;
|
||||||
|
let responseData;
|
||||||
|
const qs: IDataObject = {};
|
||||||
|
const timezone = this.getTimezone();
|
||||||
|
|
||||||
|
const resource = this.getNodeParameter('resource', 0) as string;
|
||||||
|
const operation = this.getNodeParameter('operation', 0) as string;
|
||||||
|
|
||||||
|
if (resource === 'block') {
|
||||||
|
|
||||||
|
if (operation === 'append') {
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
const blockId = this.getNodeParameter('blockId', i) as string;
|
||||||
|
const body: IDataObject = {
|
||||||
|
children: formatBlocks(this.getNodeParameter('blockUi.blockValues', i, []) as IDataObject[]),
|
||||||
|
};
|
||||||
|
const block = await notionApiRequest.call(this, 'PATCH', `/blocks/${blockId}/children`, body);
|
||||||
|
returnData.push(block);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (operation === 'getAll') {
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
const blockId = this.getNodeParameter('blockId', i) as string;
|
||||||
|
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
|
||||||
|
if (returnAll) {
|
||||||
|
responseData = await notionApiRequestAllItems.call(this, 'results', 'GET', `/blocks/${blockId}/children`, {});
|
||||||
|
} else {
|
||||||
|
qs.page_size = this.getNodeParameter('limit', i) as number;
|
||||||
|
responseData = await notionApiRequest.call(this, 'GET', `/blocks/${blockId}/children`, {});
|
||||||
|
responseData = responseData.results;
|
||||||
|
}
|
||||||
|
returnData.push.apply(returnData, responseData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resource === 'database') {
|
||||||
|
|
||||||
|
if (operation === 'get') {
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
const databaseId = this.getNodeParameter('databaseId', i) as string;
|
||||||
|
responseData = await notionApiRequest.call(this, 'GET', `/databases/${databaseId}`);
|
||||||
|
returnData.push(responseData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (operation === 'getAll') {
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
const body: IDataObject = {
|
||||||
|
page_size: 100,
|
||||||
|
filter: { property: 'object', value: 'database' },
|
||||||
|
};
|
||||||
|
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
|
||||||
|
if (returnAll) {
|
||||||
|
responseData = await notionApiRequestAllItems.call(this, 'results', 'POST', `/search`, body);
|
||||||
|
} else {
|
||||||
|
body['page_size'] = this.getNodeParameter('limit', i) as number;
|
||||||
|
responseData = await notionApiRequest.call(this, 'POST', `/search`, body);
|
||||||
|
responseData = responseData.results;
|
||||||
|
}
|
||||||
|
returnData.push.apply(returnData, responseData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resource === 'databasePage') {
|
||||||
|
|
||||||
|
if (operation === 'create') {
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
const simple = this.getNodeParameter('simple', i) as boolean;
|
||||||
|
// tslint:disable-next-line: no-any
|
||||||
|
const body: { [key: string]: any } = {
|
||||||
|
parent: {},
|
||||||
|
properties: {},
|
||||||
|
};
|
||||||
|
body.parent['database_id'] = this.getNodeParameter('databaseId', i) as string;
|
||||||
|
const properties = this.getNodeParameter('propertiesUi.propertyValues', i, []) as IDataObject[];
|
||||||
|
if (properties.length !== 0) {
|
||||||
|
body.properties = mapProperties(properties, timezone) as IDataObject;
|
||||||
|
}
|
||||||
|
body.children = formatBlocks(this.getNodeParameter('blockUi.blockValues', i, []) as IDataObject[]);
|
||||||
|
responseData = await notionApiRequest.call(this, 'POST', '/pages', body);
|
||||||
|
if (simple === true) {
|
||||||
|
responseData = simplifyObjects(responseData);
|
||||||
|
}
|
||||||
|
returnData.push.apply(returnData, Array.isArray(responseData) ? responseData : [responseData]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (operation === 'getAll') {
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
const simple = this.getNodeParameter('simple', 0) as boolean;
|
||||||
|
const databaseId = this.getNodeParameter('databaseId', i) as string;
|
||||||
|
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
|
||||||
|
const filters = this.getNodeParameter('options.filter', i, {}) as IDataObject;
|
||||||
|
const sort = this.getNodeParameter('options.sort.sortValue', i, []) as IDataObject[];
|
||||||
|
const body: IDataObject = {
|
||||||
|
filter: {},
|
||||||
|
};
|
||||||
|
if (filters.singleCondition) {
|
||||||
|
body['filter'] = mapFilters([filters.singleCondition] as IDataObject[], timezone);
|
||||||
|
}
|
||||||
|
if (filters.multipleCondition) {
|
||||||
|
const { or, and } = (filters.multipleCondition as IDataObject).condition as IDataObject;
|
||||||
|
if (Array.isArray(or) && or.length !== 0) {
|
||||||
|
Object.assign(body.filter, { or: (or as IDataObject[]).map((data) => mapFilters([data], timezone)) });
|
||||||
|
}
|
||||||
|
if (Array.isArray(and) && and.length !== 0) {
|
||||||
|
Object.assign(body.filter, { and: (and as IDataObject[]).map((data) => mapFilters([data], timezone)) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!Object.keys(body.filter as IDataObject).length) {
|
||||||
|
delete body.filter;
|
||||||
|
}
|
||||||
|
if (sort) {
|
||||||
|
//@ts-expect-error
|
||||||
|
body['sorts'] = mapSorting(sort);
|
||||||
|
}
|
||||||
|
if (returnAll) {
|
||||||
|
responseData = await notionApiRequestAllItems.call(this, 'results', 'POST', `/databases/${databaseId}/query`, body, {});
|
||||||
|
} else {
|
||||||
|
body.page_size = this.getNodeParameter('limit', i) as number;
|
||||||
|
responseData = await notionApiRequest.call(this, 'POST', `/databases/${databaseId}/query`, body, qs);
|
||||||
|
responseData = responseData.results;
|
||||||
|
}
|
||||||
|
if (simple === true) {
|
||||||
|
responseData = simplifyObjects(responseData);
|
||||||
|
}
|
||||||
|
returnData.push.apply(returnData, responseData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (operation === 'update') {
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
const pageId = this.getNodeParameter('pageId', i) as string;
|
||||||
|
const simple = this.getNodeParameter('simple', i) as boolean;
|
||||||
|
const properties = this.getNodeParameter('propertiesUi.propertyValues', i, []) as IDataObject[];
|
||||||
|
// tslint:disable-next-line: no-any
|
||||||
|
const body: { [key: string]: any } = {
|
||||||
|
properties: {},
|
||||||
|
};
|
||||||
|
if (properties.length !== 0) {
|
||||||
|
body.properties = mapProperties(properties, timezone) as IDataObject;
|
||||||
|
}
|
||||||
|
responseData = await notionApiRequest.call(this, 'PATCH', `/pages/${pageId}`, body);
|
||||||
|
if (simple === true) {
|
||||||
|
responseData = simplifyObjects(responseData);
|
||||||
|
}
|
||||||
|
returnData.push.apply(returnData, Array.isArray(responseData) ? responseData : [responseData]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resource === 'user') {
|
||||||
|
|
||||||
|
if (operation === 'get') {
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
const userId = this.getNodeParameter('userId', i) as string;
|
||||||
|
responseData = await notionApiRequest.call(this, 'GET', `/users/${userId}`);
|
||||||
|
returnData.push(responseData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (operation === 'getAll') {
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
|
||||||
|
if (returnAll) {
|
||||||
|
responseData = await notionApiRequestAllItems.call(this, 'results', 'GET', '/users');
|
||||||
|
} else {
|
||||||
|
qs.limit = this.getNodeParameter('limit', i) as number;
|
||||||
|
responseData = await notionApiRequestAllItems.call(this, 'results', 'GET', '/users');
|
||||||
|
responseData = responseData.splice(0, qs.limit);
|
||||||
|
}
|
||||||
|
returnData.push.apply(returnData, responseData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resource === 'page') {
|
||||||
|
|
||||||
|
if (operation === 'create') {
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
const simple = this.getNodeParameter('simple', i) as boolean;
|
||||||
|
// tslint:disable-next-line: no-any
|
||||||
|
const body: { [key: string]: any } = {
|
||||||
|
parent: {},
|
||||||
|
properties: {},
|
||||||
|
};
|
||||||
|
body.parent['page_id'] = this.getNodeParameter('pageId', i) as string;
|
||||||
|
body.properties = formatTitle(this.getNodeParameter('title', i) as string);
|
||||||
|
body.children = formatBlocks(this.getNodeParameter('blockUi.blockValues', i, []) as IDataObject[]);
|
||||||
|
responseData = await notionApiRequest.call(this, 'POST', '/pages', body);
|
||||||
|
if (simple === true) {
|
||||||
|
responseData = simplifyObjects(responseData);
|
||||||
|
}
|
||||||
|
returnData.push.apply(returnData, Array.isArray(responseData) ? responseData : [responseData]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (operation === 'get') {
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
const pageId = this.getNodeParameter('pageId', i) as string;
|
||||||
|
const simple = this.getNodeParameter('simple', i) as boolean;
|
||||||
|
responseData = await notionApiRequest.call(this, 'GET', `/pages/${pageId}`);
|
||||||
|
if (simple === true) {
|
||||||
|
responseData = simplifyObjects(responseData);
|
||||||
|
}
|
||||||
|
returnData.push.apply(returnData, Array.isArray(responseData) ? responseData : [responseData]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (operation === 'search') {
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
const text = this.getNodeParameter('text', i) as string;
|
||||||
|
const options = this.getNodeParameter('options', i) as IDataObject;
|
||||||
|
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
|
||||||
|
const simple = this.getNodeParameter('simple', i) as boolean;
|
||||||
|
const body: IDataObject = {};
|
||||||
|
|
||||||
|
if (text) {
|
||||||
|
body['query'] = text;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.filter) {
|
||||||
|
const filter = (options.filter as IDataObject || {}).filters as IDataObject[] || [];
|
||||||
|
body['filter'] = filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.sort) {
|
||||||
|
const sort = (options.sort as IDataObject || {}).sortValue as IDataObject || {};
|
||||||
|
body['sort'] = sort;
|
||||||
|
}
|
||||||
|
if (returnAll) {
|
||||||
|
responseData = await notionApiRequestAllItems.call(this, 'results', 'POST', '/search', body);
|
||||||
|
} else {
|
||||||
|
qs.limit = this.getNodeParameter('limit', i) as number;
|
||||||
|
responseData = await notionApiRequestAllItems.call(this, 'results', 'POST', '/search', body);
|
||||||
|
responseData = responseData.splice(0, qs.limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (simple === true) {
|
||||||
|
responseData = simplifyObjects(responseData);
|
||||||
|
}
|
||||||
|
|
||||||
|
returnData.push.apply(returnData, responseData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return [this.helpers.returnJsonArray(returnData)];
|
||||||
|
}
|
||||||
|
}
|
188
packages/nodes-base/nodes/Notion/NotionTrigger.node.ts
Normal file
188
packages/nodes-base/nodes/Notion/NotionTrigger.node.ts
Normal file
|
@ -0,0 +1,188 @@
|
||||||
|
import {
|
||||||
|
IPollFunctions,
|
||||||
|
} from 'n8n-core';
|
||||||
|
|
||||||
|
import {
|
||||||
|
IDataObject,
|
||||||
|
ILoadOptionsFunctions,
|
||||||
|
INodeExecutionData,
|
||||||
|
INodePropertyOptions,
|
||||||
|
INodeType,
|
||||||
|
INodeTypeDescription,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
import {
|
||||||
|
notionApiRequest,
|
||||||
|
simplifyObjects,
|
||||||
|
} from './GenericFunctions';
|
||||||
|
|
||||||
|
import * as moment from 'moment';
|
||||||
|
|
||||||
|
export class NotionTrigger implements INodeType {
|
||||||
|
description: INodeTypeDescription = {
|
||||||
|
displayName: 'Notion Trigger (Beta)',
|
||||||
|
name: 'notionTrigger',
|
||||||
|
icon: 'file:notion.svg',
|
||||||
|
group: ['trigger'],
|
||||||
|
version: 1,
|
||||||
|
description: 'Starts the workflow when Notion events occur',
|
||||||
|
subtitle: '={{$parameter["event"]}}',
|
||||||
|
defaults: {
|
||||||
|
name: 'Notion Trigger',
|
||||||
|
color: '#000000',
|
||||||
|
},
|
||||||
|
credentials: [
|
||||||
|
{
|
||||||
|
name: 'notionApi',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
polling: true,
|
||||||
|
inputs: [],
|
||||||
|
outputs: ['main'],
|
||||||
|
properties: [
|
||||||
|
{
|
||||||
|
displayName: 'Event',
|
||||||
|
name: 'event',
|
||||||
|
type: 'options',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Page Added to Database',
|
||||||
|
value: 'pageAddedToDatabase',
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// name: 'Record Updated',
|
||||||
|
// value: 'recordUpdated',
|
||||||
|
// },
|
||||||
|
],
|
||||||
|
required: true,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Database',
|
||||||
|
name: 'databaseId',
|
||||||
|
type: 'options',
|
||||||
|
typeOptions: {
|
||||||
|
loadOptionsMethod: 'getDatabases',
|
||||||
|
},
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
event: [
|
||||||
|
'pageAddedToDatabase',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
required: true,
|
||||||
|
description: 'The ID of this database.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Simple',
|
||||||
|
name: 'simple',
|
||||||
|
type: 'boolean',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
event: [
|
||||||
|
'pageAddedToDatabase',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: true,
|
||||||
|
description: 'When set to true a simplify version of the response will be used else the raw data.',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
methods = {
|
||||||
|
loadOptions: {
|
||||||
|
async getDatabases(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||||
|
const returnData: INodePropertyOptions[] = [];
|
||||||
|
const { results: databases } = await notionApiRequest.call(this, 'POST', `/search`, { page_size: 100, filter: { property: 'object', value: 'database' } });
|
||||||
|
for (const database of databases) {
|
||||||
|
returnData.push({
|
||||||
|
name: database.title[0].plain_text,
|
||||||
|
value: database.id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
returnData.sort((a, b) => {
|
||||||
|
if (a.name.toLocaleLowerCase() < b.name.toLocaleLowerCase()) { return -1; }
|
||||||
|
if (a.name.toLocaleLowerCase() > b.name.toLocaleLowerCase()) { return 1; }
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
return returnData;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
async poll(this: IPollFunctions): Promise<INodeExecutionData[][] | null> {
|
||||||
|
const webhookData = this.getWorkflowStaticData('node');
|
||||||
|
const databaseId = this.getNodeParameter('databaseId') as string;
|
||||||
|
const event = this.getNodeParameter('event') as string;
|
||||||
|
const simple = this.getNodeParameter('simple') as boolean;
|
||||||
|
|
||||||
|
const now = moment().utc().format();
|
||||||
|
|
||||||
|
const startDate = webhookData.lastTimeChecked as string || now;
|
||||||
|
|
||||||
|
const endDate = now;
|
||||||
|
|
||||||
|
webhookData.lastTimeChecked = endDate;
|
||||||
|
|
||||||
|
const sortProperty = (event === 'pageAddedToDatabase') ? 'created_time' : 'last_edited_time';
|
||||||
|
|
||||||
|
const body: IDataObject = {
|
||||||
|
page_size: 1,
|
||||||
|
sorts: [
|
||||||
|
{
|
||||||
|
timestamp: sortProperty,
|
||||||
|
direction: 'descending',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
let records: IDataObject[] = [];
|
||||||
|
|
||||||
|
let hasMore = true;
|
||||||
|
|
||||||
|
//get last record
|
||||||
|
let { results: data } = await notionApiRequest.call(this, 'POST', `/databases/${databaseId}/query`, body);
|
||||||
|
|
||||||
|
if (this.getMode() === 'manual') {
|
||||||
|
if (simple === true) {
|
||||||
|
data = simplifyObjects(data);
|
||||||
|
}
|
||||||
|
if (Array.isArray(data) && data.length) {
|
||||||
|
return [this.helpers.returnJsonArray(data)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if something changed after the last check
|
||||||
|
if (Object.keys(data[0]).length !== 0 && webhookData.lastRecordProccesed !== data[0].id) {
|
||||||
|
do {
|
||||||
|
body.page_size = 10;
|
||||||
|
const { results, has_more, next_cursor } = await notionApiRequest.call(this, 'POST', `/databases/${databaseId}/query`, body);
|
||||||
|
records.push.apply(records, results);
|
||||||
|
hasMore = has_more;
|
||||||
|
if (next_cursor !== null) {
|
||||||
|
body['start_cursor'] = next_cursor;
|
||||||
|
}
|
||||||
|
} while (!moment(records[records.length - 1][sortProperty] as string).isSameOrBefore(startDate) && hasMore === true);
|
||||||
|
|
||||||
|
if (this.getMode() !== 'manual') {
|
||||||
|
records = records.filter((record: IDataObject) => moment(record[sortProperty] as string).isBetween(startDate, endDate));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (simple === true) {
|
||||||
|
records = simplifyObjects(records);
|
||||||
|
}
|
||||||
|
|
||||||
|
webhookData.lastRecordProccesed = data[0].id;
|
||||||
|
|
||||||
|
if (Array.isArray(records) && records.length) {
|
||||||
|
return [this.helpers.returnJsonArray(records)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
332
packages/nodes-base/nodes/Notion/PageDescription.ts
Normal file
332
packages/nodes-base/nodes/Notion/PageDescription.ts
Normal file
|
@ -0,0 +1,332 @@
|
||||||
|
import {
|
||||||
|
INodeProperties,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
import {
|
||||||
|
blocks,
|
||||||
|
} from './Blocks';
|
||||||
|
|
||||||
|
export const pageOperations = [
|
||||||
|
{
|
||||||
|
displayName: 'Operation',
|
||||||
|
name: 'operation',
|
||||||
|
type: 'options',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'page',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Create',
|
||||||
|
value: 'create',
|
||||||
|
description: 'Create a page',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Get',
|
||||||
|
value: 'get',
|
||||||
|
description: 'Get a page',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Search',
|
||||||
|
value: 'search',
|
||||||
|
description: 'Text search of pages',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
default: 'create',
|
||||||
|
description: 'The operation to perform.',
|
||||||
|
},
|
||||||
|
] as INodeProperties[];
|
||||||
|
|
||||||
|
export const pageFields = [
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* page:create */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
{
|
||||||
|
displayName: 'Parent Page ID',
|
||||||
|
name: 'pageId',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'page',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'create',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
description: 'The ID of the parent page that this child page belongs to.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Title',
|
||||||
|
name: 'title',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'page',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'create',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
description: 'Page title. Appears at the top of the page and can be found via Quick Find.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Simple',
|
||||||
|
name: 'simple',
|
||||||
|
type: 'boolean',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'page',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'create',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: true,
|
||||||
|
description: 'When set to true a simplify version of the response will be used else the raw data.',
|
||||||
|
},
|
||||||
|
...blocks('page', 'create'),
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* page:get */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
{
|
||||||
|
displayName: 'Page ID',
|
||||||
|
name: 'pageId',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'page',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'get',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Simple',
|
||||||
|
name: 'simple',
|
||||||
|
type: 'boolean',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'page',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'get',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: true,
|
||||||
|
description: 'When set to true a simplify version of the response will be used else the raw data.',
|
||||||
|
},
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* page:search */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
{
|
||||||
|
displayName: 'Search Text',
|
||||||
|
name: 'text',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'page',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'search',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
description: 'The text to search for.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Return All',
|
||||||
|
name: 'returnAll',
|
||||||
|
type: 'boolean',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'page',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'search',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
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: [
|
||||||
|
'page',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'search',
|
||||||
|
],
|
||||||
|
returnAll: [
|
||||||
|
false,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
typeOptions: {
|
||||||
|
minValue: 1,
|
||||||
|
maxValue: 100,
|
||||||
|
},
|
||||||
|
default: 50,
|
||||||
|
description: 'How many results to return.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Simple',
|
||||||
|
name: 'simple',
|
||||||
|
type: 'boolean',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'page',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'search',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: true,
|
||||||
|
description: 'When set to true a simplify version of the response will be used else the raw data.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Options',
|
||||||
|
name: 'options',
|
||||||
|
type: 'collection',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'page',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'search',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: {},
|
||||||
|
placeholder: 'Add Field',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
displayName: 'Filters',
|
||||||
|
name: 'filter',
|
||||||
|
placeholder: 'Add Filter',
|
||||||
|
type: 'fixedCollection',
|
||||||
|
typeOptions: {
|
||||||
|
multipleValues: false,
|
||||||
|
},
|
||||||
|
default: {},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
displayName: 'Filter',
|
||||||
|
name: 'filters',
|
||||||
|
values: [
|
||||||
|
{
|
||||||
|
displayName: 'Property',
|
||||||
|
name: 'property',
|
||||||
|
type: 'options',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Object',
|
||||||
|
value: 'object',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
default: 'object',
|
||||||
|
description: 'The name of the property to filter by.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Value',
|
||||||
|
name: 'value',
|
||||||
|
type: 'options',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Database',
|
||||||
|
value: 'database',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Page',
|
||||||
|
value: 'page',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
default: '',
|
||||||
|
description: 'The value of the property to filter by.',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Sort',
|
||||||
|
name: 'sort',
|
||||||
|
placeholder: 'Add Sort',
|
||||||
|
type: 'fixedCollection',
|
||||||
|
typeOptions: {
|
||||||
|
multipleValues: false,
|
||||||
|
},
|
||||||
|
default: {},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
displayName: 'Sort',
|
||||||
|
name: 'sortValue',
|
||||||
|
values: [
|
||||||
|
{
|
||||||
|
displayName: 'Direction',
|
||||||
|
name: 'direction',
|
||||||
|
type: 'options',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Ascending',
|
||||||
|
value: 'ascending',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Descending',
|
||||||
|
value: 'descending',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
default: '',
|
||||||
|
description: 'The direction to sort.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Timestamp',
|
||||||
|
name: 'timestamp',
|
||||||
|
type: 'options',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Last Edited Time',
|
||||||
|
value: 'last_edited_time',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
default: 'last_edited_time',
|
||||||
|
description: `The name of the timestamp to sort against.`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
] as INodeProperties[];
|
100
packages/nodes-base/nodes/Notion/UserDescription.ts
Normal file
100
packages/nodes-base/nodes/Notion/UserDescription.ts
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
import {
|
||||||
|
INodeProperties,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
export const userOperations = [
|
||||||
|
{
|
||||||
|
displayName: 'Operation',
|
||||||
|
name: 'operation',
|
||||||
|
type: 'options',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'user',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Get',
|
||||||
|
value: 'get',
|
||||||
|
description: 'Get a user',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Get All',
|
||||||
|
value: 'getAll',
|
||||||
|
description: 'Get all users',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
default: 'get',
|
||||||
|
description: 'The operation to perform.',
|
||||||
|
},
|
||||||
|
] as INodeProperties[];
|
||||||
|
|
||||||
|
export const userFields = [
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* user:get */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
{
|
||||||
|
displayName: 'User ID',
|
||||||
|
name: 'userId',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'user',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'get',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* 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: 100,
|
||||||
|
},
|
||||||
|
default: 50,
|
||||||
|
description: 'How many results to return.',
|
||||||
|
},
|
||||||
|
] as INodeProperties[];
|
1
packages/nodes-base/nodes/Notion/notion.svg
Normal file
1
packages/nodes-base/nodes/Notion/notion.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg height="2500" width="2500" xmlns="http://www.w3.org/2000/svg" viewBox="12 0.18999999999999906 487.619 510.941"><path d="M96.085 91.118c15.81 12.845 21.741 11.865 51.43 9.884l279.888-16.806c5.936 0 1-5.922-.98-6.906L379.94 43.686c-8.907-6.915-20.773-14.834-43.516-12.853L65.408 50.6c-9.884.98-11.858 5.922-7.922 9.883zm16.804 65.228v294.491c0 15.827 7.909 21.748 25.71 20.769l307.597-17.799c17.81-.979 19.794-11.865 19.794-24.722V136.57c0-12.836-4.938-19.758-15.84-18.77l-321.442 18.77c-11.863.997-15.82 6.931-15.82 19.776zm303.659 15.797c1.972 8.903 0 17.798-8.92 18.799l-14.82 2.953v217.412c-12.868 6.916-24.734 10.87-34.622 10.87-15.831 0-19.796-4.945-31.654-19.76l-96.944-152.19v147.248l30.677 6.922s0 17.78-24.75 17.78l-68.23 3.958c-1.982-3.958 0-13.832 6.921-15.81l17.805-4.935V210.7l-24.721-1.981c-1.983-8.903 2.955-21.74 16.812-22.736l73.195-4.934 100.889 154.171V198.836l-25.723-2.952c-1.974-10.884 5.927-18.787 15.819-19.767zM42.653 23.919l281.9-20.76c34.618-2.969 43.525-.98 65.283 14.825l89.986 63.247c14.848 10.876 19.797 13.837 19.797 25.693v346.883c0 21.74-7.92 34.597-35.608 36.564L136.64 510.14c-20.785.991-30.677-1.971-41.562-15.815l-66.267-85.978C16.938 392.52 12 380.68 12 366.828V58.495c0-17.778 7.922-32.608 30.653-34.576z" fill-rule="evenodd"/></svg>
|
After Width: | Height: | Size: 1.2 KiB |
|
@ -414,10 +414,12 @@ export class Orbit implements INodeType {
|
||||||
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
|
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
|
||||||
const body: IDataObject = {
|
const body: IDataObject = {
|
||||||
type: 'post',
|
type: 'post',
|
||||||
|
activity_type: 'post',
|
||||||
url,
|
url,
|
||||||
};
|
};
|
||||||
if (additionalFields.publishedAt) {
|
if (additionalFields.publishedAt) {
|
||||||
body.occurred_at = additionalFields.publishedAt as string;
|
body.occurred_at = additionalFields.publishedAt as string;
|
||||||
|
delete body.publishedAt;
|
||||||
}
|
}
|
||||||
|
|
||||||
responseData = await orbitApiRequest.call(this, 'POST', `/${workspaceId}/members/${memberId}/activities/`, body);
|
responseData = await orbitApiRequest.call(this, 'POST', `/${workspaceId}/members/${memberId}/activities/`, body);
|
||||||
|
|
|
@ -1035,11 +1035,11 @@ export class Slack implements INodeType {
|
||||||
if (operation === 'get') {
|
if (operation === 'get') {
|
||||||
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
|
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
|
||||||
|
|
||||||
const body: IDataObject = {};
|
const qs: IDataObject = {};
|
||||||
|
|
||||||
Object.assign(body, additionalFields);
|
Object.assign(qs, additionalFields);
|
||||||
|
|
||||||
responseData = await slackApiRequest.call(this, 'POST', '/users.profile.get', body);
|
responseData = await slackApiRequest.call(this, 'POST', '/users.profile.get', undefined, qs);
|
||||||
|
|
||||||
responseData = responseData.profile;
|
responseData = responseData.profile;
|
||||||
}
|
}
|
||||||
|
|
|
@ -445,6 +445,37 @@ export class Spotify implements INodeType {
|
||||||
placeholder: 'spotify:track:0xE4LEFzSNGsz1F6kvXsHU',
|
placeholder: 'spotify:track:0xE4LEFzSNGsz1F6kvXsHU',
|
||||||
description: `The track's Spotify URI or its ID. The track to add/delete from the playlist.`,
|
description: `The track's Spotify URI or its ID. The track to add/delete from the playlist.`,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Additional Fields',
|
||||||
|
name: 'additionalFields',
|
||||||
|
type: 'collection',
|
||||||
|
placeholder: 'Add Field',
|
||||||
|
default: {},
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'playlist',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'add',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
displayName: 'Position',
|
||||||
|
name: 'position',
|
||||||
|
type: 'number',
|
||||||
|
typeOptions: {
|
||||||
|
minValue: 0,
|
||||||
|
},
|
||||||
|
default: 0,
|
||||||
|
placeholder: '0',
|
||||||
|
description: `The new track's position in the playlist.`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
// -----------------------------------------------------
|
// -----------------------------------------------------
|
||||||
// Track Operations
|
// Track Operations
|
||||||
// Get a Track, Get a Track's Audio Features
|
// Get a Track, Get a Track's Audio Features
|
||||||
|
@ -918,15 +949,19 @@ export class Spotify implements INodeType {
|
||||||
requestMethod = 'POST';
|
requestMethod = 'POST';
|
||||||
|
|
||||||
const trackId = this.getNodeParameter('trackID', i) as string;
|
const trackId = this.getNodeParameter('trackID', i) as string;
|
||||||
|
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
|
||||||
|
|
||||||
qs = {
|
qs = {
|
||||||
uris: trackId,
|
uris: trackId,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (additionalFields.position !== undefined) {
|
||||||
|
qs.position = additionalFields.position;
|
||||||
|
}
|
||||||
|
|
||||||
endpoint = `/playlists/${id}/tracks`;
|
endpoint = `/playlists/${id}/tracks`;
|
||||||
|
|
||||||
responseData = await spotifyApiRequest.call(this, requestMethod, endpoint, body, qs);
|
responseData = await spotifyApiRequest.call(this, requestMethod, endpoint, body, qs);
|
||||||
|
|
||||||
}
|
}
|
||||||
} else if (operation === 'getUserPlaylists') {
|
} else if (operation === 'getUserPlaylists') {
|
||||||
requestMethod = 'GET';
|
requestMethod = 'GET';
|
||||||
|
|
|
@ -285,7 +285,7 @@ export const entryFields = [
|
||||||
alwaysOpenEditWindow: true,
|
alwaysOpenEditWindow: true,
|
||||||
},
|
},
|
||||||
default: '',
|
default: '',
|
||||||
description: 'JSON query to filter the data.<a href="https://strapi.io/documentation/v3.x/content-api/parameters.html#filters" target="_blank"> Info</a>',
|
description: 'JSON query to filter the data. <a href="https://strapi.io/documentation/developer-docs/latest/developer-resources/content-api/content-api.html#filters" target="_blank">More info</a>.',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
|
@ -137,7 +137,7 @@ export class Strava implements INodeType {
|
||||||
|
|
||||||
responseData = await stravaApiRequestAllItems.call(this, 'GET', `/activities`, {}, qs);
|
responseData = await stravaApiRequestAllItems.call(this, 'GET', `/activities`, {}, qs);
|
||||||
} else {
|
} else {
|
||||||
qs.limit = this.getNodeParameter('limit', i) as number;
|
qs.per_page = this.getNodeParameter('limit', i) as number;
|
||||||
|
|
||||||
responseData = await stravaApiRequest.call(this, 'GET', `/activities`, {}, qs);
|
responseData = await stravaApiRequest.call(this, 'GET', `/activities`, {}, qs);
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ export class TogglTrigger implements INodeType {
|
||||||
icon: 'file:toggl.png',
|
icon: 'file:toggl.png',
|
||||||
group: ['trigger'],
|
group: ['trigger'],
|
||||||
version: 1,
|
version: 1,
|
||||||
description: 'Starts the workflow when Toggl events occure',
|
description: 'Starts the workflow when Toggl events occur',
|
||||||
defaults: {
|
defaults: {
|
||||||
name: 'Toggl',
|
name: 'Toggl',
|
||||||
color: '#00FF00',
|
color: '#00FF00',
|
||||||
|
|
|
@ -7,6 +7,10 @@ import {
|
||||||
IDataObject, NodeApiError, NodeOperationError,
|
IDataObject, NodeApiError, NodeOperationError,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
import {
|
||||||
|
OptionsWithUri,
|
||||||
|
} from 'request';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Make an API request to Twilio
|
* Make an API request to Twilio
|
||||||
*
|
*
|
||||||
|
@ -17,7 +21,14 @@ import {
|
||||||
* @returns {Promise<any>}
|
* @returns {Promise<any>}
|
||||||
*/
|
*/
|
||||||
export async function twilioApiRequest(this: IHookFunctions | IExecuteFunctions, method: string, endpoint: string, body: IDataObject, query?: IDataObject): Promise<any> { // tslint:disable-line:no-any
|
export async function twilioApiRequest(this: IHookFunctions | IExecuteFunctions, method: string, endpoint: string, body: IDataObject, query?: IDataObject): Promise<any> { // tslint:disable-line:no-any
|
||||||
const credentials = this.getCredentials('twilioApi');
|
const credentials = this.getCredentials('twilioApi') as {
|
||||||
|
accountSid: string;
|
||||||
|
authType: 'authToken' | 'apiKey';
|
||||||
|
authToken: string;
|
||||||
|
apiKeySid: string;
|
||||||
|
apiKeySecret: string;
|
||||||
|
};
|
||||||
|
|
||||||
if (credentials === undefined) {
|
if (credentials === undefined) {
|
||||||
throw new NodeOperationError(this.getNode(), 'No credentials got returned!');
|
throw new NodeOperationError(this.getNode(), 'No credentials got returned!');
|
||||||
}
|
}
|
||||||
|
@ -26,18 +37,26 @@ export async function twilioApiRequest(this: IHookFunctions | IExecuteFunctions,
|
||||||
query = {};
|
query = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
const options = {
|
const options: OptionsWithUri = {
|
||||||
method,
|
method,
|
||||||
form: body,
|
form: body,
|
||||||
qs: query,
|
qs: query,
|
||||||
uri: `https://api.twilio.com/2010-04-01/Accounts/${credentials.accountSid}${endpoint}`,
|
uri: `https://api.twilio.com/2010-04-01/Accounts/${credentials.accountSid}${endpoint}`,
|
||||||
auth: {
|
|
||||||
user: credentials.accountSid as string,
|
|
||||||
pass: credentials.authToken as string,
|
|
||||||
},
|
|
||||||
json: true,
|
json: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (credentials.authType === 'apiKey') {
|
||||||
|
options.auth = {
|
||||||
|
user: credentials.apiKeySid,
|
||||||
|
password: credentials.apiKeySecret,
|
||||||
|
};
|
||||||
|
} else if (credentials.authType === 'authToken') {
|
||||||
|
options.auth = {
|
||||||
|
user: credentials.accountSid,
|
||||||
|
pass: credentials.authToken,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return await this.helpers.request(options);
|
return await this.helpers.request(options);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
@ -41,7 +41,7 @@ export class Zulip implements INodeType {
|
||||||
description: INodeTypeDescription = {
|
description: INodeTypeDescription = {
|
||||||
displayName: 'Zulip',
|
displayName: 'Zulip',
|
||||||
name: 'zulip',
|
name: 'zulip',
|
||||||
icon: 'file:zulip.png',
|
icon: 'file:zulip.svg',
|
||||||
group: ['output'],
|
group: ['output'],
|
||||||
version: 1,
|
version: 1,
|
||||||
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 2.3 KiB |
1
packages/nodes-base/nodes/Zulip/zulip.svg
Normal file
1
packages/nodes-base/nodes/Zulip/zulip.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg width="256" height="256" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid"><defs><linearGradient x1="50%" y1="0%" x2="50%" y2="100%" id="a"><stop stop-color="#24ADFF" offset="0%"/><stop stop-color="#7B71FF" offset="100%"/></linearGradient></defs><path d="M128 0c70.692 0 128 57.308 128 128 0 70.692-57.308 128-128 128C57.308 256 0 198.692 0 128 0 57.308 57.308 0 128 0zm-6.32 118.222l-45.892 40.979c-4.728 3.72-7.83 9.86-7.83 16.766 0 11.279 8.274 20.508 18.386 20.508h86.247c10.112 0 18.386-9.23 18.386-20.508 0-11.28-8.274-20.507-18.386-20.507H107.3c-.968 0-1.58-1.16-1.108-2.104l16.833-33.703c.615-.983-.493-2.161-1.345-1.43zm50.91-58.86H86.345c-10.112 0-18.386 9.227-18.386 20.508 0 11.279 8.274 20.508 18.386 20.508h65.292c.968 0 1.58 1.16 1.108 2.103l-16.834 33.704c-.615.983.494 2.161 1.346 1.43l45.892-40.984c4.727-3.723 7.829-9.86 7.829-16.767 0-11.278-8.274-20.507-18.386-20.501z" fill="url(#a)"/></svg>
|
After Width: | Height: | Size: 934 B |
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "n8n-nodes-base",
|
"name": "n8n-nodes-base",
|
||||||
"version": "0.116.0",
|
"version": "0.118.0",
|
||||||
"description": "Base nodes of n8n",
|
"description": "Base nodes of n8n",
|
||||||
"license": "SEE LICENSE IN LICENSE.md",
|
"license": "SEE LICENSE IN LICENSE.md",
|
||||||
"homepage": "https://n8n.io",
|
"homepage": "https://n8n.io",
|
||||||
|
@ -172,6 +172,8 @@
|
||||||
"dist/credentials/NasaApi.credentials.js",
|
"dist/credentials/NasaApi.credentials.js",
|
||||||
"dist/credentials/NextCloudApi.credentials.js",
|
"dist/credentials/NextCloudApi.credentials.js",
|
||||||
"dist/credentials/NextCloudOAuth2Api.credentials.js",
|
"dist/credentials/NextCloudOAuth2Api.credentials.js",
|
||||||
|
"dist/credentials/NotionApi.credentials.js",
|
||||||
|
"dist/credentials/NotionOAuth2Api.credentials.js",
|
||||||
"dist/credentials/OAuth1Api.credentials.js",
|
"dist/credentials/OAuth1Api.credentials.js",
|
||||||
"dist/credentials/OAuth2Api.credentials.js",
|
"dist/credentials/OAuth2Api.credentials.js",
|
||||||
"dist/credentials/OpenWeatherMapApi.credentials.js",
|
"dist/credentials/OpenWeatherMapApi.credentials.js",
|
||||||
|
@ -396,6 +398,7 @@
|
||||||
"dist/nodes/Hubspot/HubspotTrigger.node.js",
|
"dist/nodes/Hubspot/HubspotTrigger.node.js",
|
||||||
"dist/nodes/HumanticAI/HumanticAi.node.js",
|
"dist/nodes/HumanticAI/HumanticAi.node.js",
|
||||||
"dist/nodes/Hunter/Hunter.node.js",
|
"dist/nodes/Hunter/Hunter.node.js",
|
||||||
|
"dist/nodes/ICalendar.node.js",
|
||||||
"dist/nodes/If.node.js",
|
"dist/nodes/If.node.js",
|
||||||
"dist/nodes/Iterable/Iterable.node.js",
|
"dist/nodes/Iterable/Iterable.node.js",
|
||||||
"dist/nodes/Intercom/Intercom.node.js",
|
"dist/nodes/Intercom/Intercom.node.js",
|
||||||
|
@ -450,6 +453,8 @@
|
||||||
"dist/nodes/Nasa/Nasa.node.js",
|
"dist/nodes/Nasa/Nasa.node.js",
|
||||||
"dist/nodes/NextCloud/NextCloud.node.js",
|
"dist/nodes/NextCloud/NextCloud.node.js",
|
||||||
"dist/nodes/NoOp.node.js",
|
"dist/nodes/NoOp.node.js",
|
||||||
|
"dist/nodes/Notion/Notion.node.js",
|
||||||
|
"dist/nodes/Notion/NotionTrigger.node.js",
|
||||||
"dist/nodes/OpenThesaurus/OpenThesaurus.node.js",
|
"dist/nodes/OpenThesaurus/OpenThesaurus.node.js",
|
||||||
"dist/nodes/OpenWeatherMap.node.js",
|
"dist/nodes/OpenWeatherMap.node.js",
|
||||||
"dist/nodes/Orbit/Orbit.node.js",
|
"dist/nodes/Orbit/Orbit.node.js",
|
||||||
|
@ -588,7 +593,7 @@
|
||||||
"@types/xml2js": "^0.4.3",
|
"@types/xml2js": "^0.4.3",
|
||||||
"gulp": "^4.0.0",
|
"gulp": "^4.0.0",
|
||||||
"jest": "^26.4.2",
|
"jest": "^26.4.2",
|
||||||
"n8n-workflow": "~0.57.0",
|
"n8n-workflow": "~0.59.0",
|
||||||
"ts-jest": "^26.3.0",
|
"ts-jest": "^26.3.0",
|
||||||
"tslint": "^6.1.2",
|
"tslint": "^6.1.2",
|
||||||
"typescript": "~3.9.7"
|
"typescript": "~3.9.7"
|
||||||
|
@ -610,6 +615,7 @@
|
||||||
"glob-promise": "^3.4.0",
|
"glob-promise": "^3.4.0",
|
||||||
"gm": "^1.23.1",
|
"gm": "^1.23.1",
|
||||||
"iconv-lite": "^0.6.2",
|
"iconv-lite": "^0.6.2",
|
||||||
|
"ics": "^2.27.0",
|
||||||
"imap-simple": "^4.3.0",
|
"imap-simple": "^4.3.0",
|
||||||
"iso-639-1": "^2.1.3",
|
"iso-639-1": "^2.1.3",
|
||||||
"jsonwebtoken": "^8.5.1",
|
"jsonwebtoken": "^8.5.1",
|
||||||
|
@ -625,7 +631,7 @@
|
||||||
"mqtt": "4.2.6",
|
"mqtt": "4.2.6",
|
||||||
"mssql": "^6.2.0",
|
"mssql": "^6.2.0",
|
||||||
"mysql2": "~2.2.0",
|
"mysql2": "~2.2.0",
|
||||||
"n8n-core": "~0.70.0",
|
"n8n-core": "~0.72.0",
|
||||||
"nodemailer": "^6.5.0",
|
"nodemailer": "^6.5.0",
|
||||||
"pdf-parse": "^1.1.1",
|
"pdf-parse": "^1.1.1",
|
||||||
"pg": "^8.3.0",
|
"pg": "^8.3.0",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "n8n-workflow",
|
"name": "n8n-workflow",
|
||||||
"version": "0.57.0",
|
"version": "0.59.0",
|
||||||
"description": "Workflow base code of n8n",
|
"description": "Workflow base code of n8n",
|
||||||
"license": "SEE LICENSE IN LICENSE.md",
|
"license": "SEE LICENSE IN LICENSE.md",
|
||||||
"homepage": "https://n8n.io",
|
"homepage": "https://n8n.io",
|
||||||
|
@ -38,6 +38,7 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"lodash.get": "^4.4.2",
|
"lodash.get": "^4.4.2",
|
||||||
|
"lodash.isequal": "^4.5.0",
|
||||||
"riot-tmpl": "^3.0.8",
|
"riot-tmpl": "^3.0.8",
|
||||||
"xml2js": "^0.4.23"
|
"xml2js": "^0.4.23"
|
||||||
},
|
},
|
||||||
|
|
|
@ -59,7 +59,7 @@ export class Expression {
|
||||||
* @returns {(NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[])}
|
* @returns {(NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[])}
|
||||||
* @memberof Workflow
|
* @memberof Workflow
|
||||||
*/
|
*/
|
||||||
resolveSimpleParameterValue(parameterValue: NodeParameterValue, runExecutionData: IRunExecutionData | null, runIndex: number, itemIndex: number, activeNodeName: string, connectionInputData: INodeExecutionData[], mode: WorkflowExecuteMode, returnObjectAsString = false, selfData = {}): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] {
|
resolveSimpleParameterValue(parameterValue: NodeParameterValue, siblingParameters: INodeParameters, runExecutionData: IRunExecutionData | null, runIndex: number, itemIndex: number, activeNodeName: string, connectionInputData: INodeExecutionData[], mode: WorkflowExecuteMode, returnObjectAsString = false, selfData = {}): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] {
|
||||||
// Check if it is an expression
|
// Check if it is an expression
|
||||||
if (typeof parameterValue !== 'string' || parameterValue.charAt(0) !== '=') {
|
if (typeof parameterValue !== 'string' || parameterValue.charAt(0) !== '=') {
|
||||||
// Is no expression so return value
|
// Is no expression so return value
|
||||||
|
@ -72,7 +72,7 @@ export class Expression {
|
||||||
parameterValue = parameterValue.substr(1);
|
parameterValue = parameterValue.substr(1);
|
||||||
|
|
||||||
// Generate a data proxy which allows to query workflow data
|
// Generate a data proxy which allows to query workflow data
|
||||||
const dataProxy = new WorkflowDataProxy(this.workflow, runExecutionData, runIndex, itemIndex, activeNodeName, connectionInputData, mode, -1, selfData);
|
const dataProxy = new WorkflowDataProxy(this.workflow, runExecutionData, runIndex, itemIndex, activeNodeName, connectionInputData, siblingParameters, mode, -1, selfData);
|
||||||
const data = dataProxy.getDataProxy();
|
const data = dataProxy.getDataProxy();
|
||||||
|
|
||||||
// Execute the expression
|
// Execute the expression
|
||||||
|
@ -179,17 +179,17 @@ export class Expression {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Helper function which resolves a parameter value depending on if it is simply or not
|
// Helper function which resolves a parameter value depending on if it is simply or not
|
||||||
const resolveParameterValue = (value: NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[]) => {
|
const resolveParameterValue = (value: NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[], siblingParameters: INodeParameters) => {
|
||||||
if (isComplexParameter(value)) {
|
if (isComplexParameter(value)) {
|
||||||
return this.getParameterValue(value, runExecutionData, runIndex, itemIndex, activeNodeName, connectionInputData, mode, returnObjectAsString, selfData);
|
return this.getParameterValue(value, runExecutionData, runIndex, itemIndex, activeNodeName, connectionInputData, mode, returnObjectAsString, selfData);
|
||||||
} else {
|
} else {
|
||||||
return this.resolveSimpleParameterValue(value as NodeParameterValue, runExecutionData, runIndex, itemIndex, activeNodeName, connectionInputData, mode, returnObjectAsString, selfData);
|
return this.resolveSimpleParameterValue(value as NodeParameterValue, siblingParameters, runExecutionData, runIndex, itemIndex, activeNodeName, connectionInputData, mode, returnObjectAsString, selfData);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Check if it value is a simple one that we can get it resolved directly
|
// Check if it value is a simple one that we can get it resolved directly
|
||||||
if (!isComplexParameter(parameterValue)) {
|
if (!isComplexParameter(parameterValue)) {
|
||||||
return this.resolveSimpleParameterValue(parameterValue as NodeParameterValue, runExecutionData, runIndex, itemIndex, activeNodeName, connectionInputData, mode, returnObjectAsString, selfData);
|
return this.resolveSimpleParameterValue(parameterValue as NodeParameterValue, {}, runExecutionData, runIndex, itemIndex, activeNodeName, connectionInputData, mode, returnObjectAsString, selfData);
|
||||||
}
|
}
|
||||||
|
|
||||||
// The parameter value is complex so resolve depending on type
|
// The parameter value is complex so resolve depending on type
|
||||||
|
@ -198,7 +198,7 @@ export class Expression {
|
||||||
// Data is an array
|
// Data is an array
|
||||||
const returnData = [];
|
const returnData = [];
|
||||||
for (const item of parameterValue) {
|
for (const item of parameterValue) {
|
||||||
returnData.push(resolveParameterValue(item));
|
returnData.push(resolveParameterValue(item, {}));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (returnObjectAsString === true && typeof returnData === 'object') {
|
if (returnObjectAsString === true && typeof returnData === 'object') {
|
||||||
|
@ -212,7 +212,7 @@ export class Expression {
|
||||||
// Data is an object
|
// Data is an object
|
||||||
const returnData: INodeParameters = {};
|
const returnData: INodeParameters = {};
|
||||||
for (const key of Object.keys(parameterValue)) {
|
for (const key of Object.keys(parameterValue)) {
|
||||||
returnData[key] = resolveParameterValue((parameterValue as INodeParameters)[key]);
|
returnData[key] = resolveParameterValue((parameterValue as INodeParameters)[key], parameterValue as INodeParameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (returnObjectAsString === true && typeof returnData === 'object') {
|
if (returnObjectAsString === true && typeof returnData === 'object') {
|
||||||
|
|
|
@ -21,7 +21,7 @@ import {
|
||||||
Workflow
|
Workflow
|
||||||
} from './Workflow';
|
} from './Workflow';
|
||||||
|
|
||||||
import { get } from 'lodash';
|
import { get, isEqual } from 'lodash';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -296,6 +296,10 @@ export function displayParameter(nodeValues: INodeParameters, parameter: INodePr
|
||||||
values.push.apply(values, value);
|
values.push.apply(values, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (values.some(v => (typeof v) === 'string' && (v as string).charAt(0) === '=')) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (values.length === 0 || !parameter.displayOptions.show[propertyName].some(v => values.includes(v))) {
|
if (values.length === 0 || !parameter.displayOptions.show[propertyName].some(v => values.includes(v))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -581,7 +585,9 @@ export function getNodeParameters(nodePropertiesArray: INodeProperties[], nodeVa
|
||||||
nodeParameters[nodeProperties.name] = nodeValues[nodeProperties.name] || nodeProperties.default;
|
nodeParameters[nodeProperties.name] = nodeValues[nodeProperties.name] || nodeProperties.default;
|
||||||
}
|
}
|
||||||
nodeParametersFull[nodeProperties.name] = nodeParameters[nodeProperties.name];
|
nodeParametersFull[nodeProperties.name] = nodeParameters[nodeProperties.name];
|
||||||
} else if (nodeValues[nodeProperties.name] !== nodeProperties.default || (nodeValues[nodeProperties.name] !== undefined && parentType === 'collection')) {
|
} else if ((nodeValues[nodeProperties.name] !== nodeProperties.default && typeof nodeValues[nodeProperties.name] !== 'object') ||
|
||||||
|
(typeof nodeValues[nodeProperties.name] === 'object' && !isEqual(nodeValues[nodeProperties.name], nodeProperties.default)) ||
|
||||||
|
(nodeValues[nodeProperties.name] !== undefined && parentType === 'collection')) {
|
||||||
// Set only if it is different to the default value
|
// Set only if it is different to the default value
|
||||||
nodeParameters[nodeProperties.name] = nodeValues[nodeProperties.name];
|
nodeParameters[nodeProperties.name] = nodeValues[nodeProperties.name];
|
||||||
nodeParametersFull[nodeProperties.name] = nodeParameters[nodeProperties.name];
|
nodeParametersFull[nodeProperties.name] = nodeParameters[nodeProperties.name];
|
||||||
|
@ -606,9 +612,14 @@ export function getNodeParameters(nodePropertiesArray: INodeProperties[], nodeVa
|
||||||
if (nodeValues[nodeProperties.name] !== undefined) {
|
if (nodeValues[nodeProperties.name] !== undefined) {
|
||||||
nodeParameters[nodeProperties.name] = nodeValues[nodeProperties.name];
|
nodeParameters[nodeProperties.name] = nodeValues[nodeProperties.name];
|
||||||
} else if (returnDefaults === true) {
|
} else if (returnDefaults === true) {
|
||||||
// Does not have values defined but defaults should be returned which is in the
|
// Does not have values defined but defaults should be returned
|
||||||
// case of a collection with multipleValues always an empty array
|
if (Array.isArray(nodeProperties.default)) {
|
||||||
nodeParameters[nodeProperties.name] = [];
|
nodeParameters[nodeProperties.name] = JSON.parse(JSON.stringify(nodeProperties.default));
|
||||||
|
} else {
|
||||||
|
// As it is probably wrong for many nodes, do we keep on returning an empty array if
|
||||||
|
// anything else than an array is set as default
|
||||||
|
nodeParameters[nodeProperties.name] = [];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
nodeParametersFull[nodeProperties.name] = nodeParameters[nodeProperties.name];
|
nodeParametersFull[nodeProperties.name] = nodeParameters[nodeProperties.name];
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
import {
|
import {
|
||||||
IDataObject,
|
IDataObject,
|
||||||
INodeExecutionData,
|
INodeExecutionData,
|
||||||
|
INodeParameters,
|
||||||
IRunExecutionData,
|
IRunExecutionData,
|
||||||
IWorkflowDataProxyData,
|
IWorkflowDataProxyData,
|
||||||
NodeHelpers,
|
NodeHelpers,
|
||||||
|
NodeParameterValue,
|
||||||
Workflow,
|
Workflow,
|
||||||
WorkflowExecuteMode,
|
WorkflowExecuteMode,
|
||||||
} from './';
|
} from './';
|
||||||
|
@ -18,12 +20,13 @@ export class WorkflowDataProxy {
|
||||||
private itemIndex: number;
|
private itemIndex: number;
|
||||||
private activeNodeName: string;
|
private activeNodeName: string;
|
||||||
private connectionInputData: INodeExecutionData[];
|
private connectionInputData: INodeExecutionData[];
|
||||||
|
private siblingParameters: INodeParameters;
|
||||||
private mode: WorkflowExecuteMode;
|
private mode: WorkflowExecuteMode;
|
||||||
private selfData: IDataObject;
|
private selfData: IDataObject;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
constructor(workflow: Workflow, runExecutionData: IRunExecutionData | null, runIndex: number, itemIndex: number, activeNodeName: string, connectionInputData: INodeExecutionData[], mode: WorkflowExecuteMode, defaultReturnRunIndex = -1, selfData = {}) {
|
constructor(workflow: Workflow, runExecutionData: IRunExecutionData | null, runIndex: number, itemIndex: number, activeNodeName: string, connectionInputData: INodeExecutionData[], siblingParameters: INodeParameters, mode: WorkflowExecuteMode, defaultReturnRunIndex = -1, selfData = {}) {
|
||||||
this.workflow = workflow;
|
this.workflow = workflow;
|
||||||
this.runExecutionData = runExecutionData;
|
this.runExecutionData = runExecutionData;
|
||||||
this.defaultReturnRunIndex = defaultReturnRunIndex;
|
this.defaultReturnRunIndex = defaultReturnRunIndex;
|
||||||
|
@ -31,6 +34,7 @@ export class WorkflowDataProxy {
|
||||||
this.itemIndex = itemIndex;
|
this.itemIndex = itemIndex;
|
||||||
this.activeNodeName = activeNodeName;
|
this.activeNodeName = activeNodeName;
|
||||||
this.connectionInputData = connectionInputData;
|
this.connectionInputData = connectionInputData;
|
||||||
|
this.siblingParameters = siblingParameters;
|
||||||
this.mode = mode;
|
this.mode = mode;
|
||||||
this.selfData = selfData;
|
this.selfData = selfData;
|
||||||
}
|
}
|
||||||
|
@ -108,12 +112,22 @@ export class WorkflowDataProxy {
|
||||||
get(target, name, receiver) {
|
get(target, name, receiver) {
|
||||||
name = name.toString();
|
name = name.toString();
|
||||||
|
|
||||||
if (!node.parameters.hasOwnProperty(name)) {
|
let returnValue: INodeParameters | NodeParameterValue | NodeParameterValue[] | INodeParameters[];
|
||||||
// Parameter does not exist on node
|
if (name[0] === '&') {
|
||||||
throw new Error(`Could not find parameter "${name}" on node "${nodeName}"`);
|
const key = name.slice(1);
|
||||||
}
|
if (!that.siblingParameters.hasOwnProperty(key)) {
|
||||||
|
throw new Error(`Could not find sibling parameter "${key}" on node "${nodeName}"`);
|
||||||
|
|
||||||
const returnValue = node.parameters[name];
|
}
|
||||||
|
returnValue = that.siblingParameters[key];
|
||||||
|
} else {
|
||||||
|
if (!node.parameters.hasOwnProperty(name)) {
|
||||||
|
// Parameter does not exist on node
|
||||||
|
throw new Error(`Could not find parameter "${name}" on node "${nodeName}"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
returnValue = node.parameters[name];
|
||||||
|
}
|
||||||
|
|
||||||
if (typeof returnValue === 'string' && returnValue.charAt(0) === '=') {
|
if (typeof returnValue === 'string' && returnValue.charAt(0) === '=') {
|
||||||
// The found value is an expression so resolve it
|
// The found value is an expression so resolve it
|
||||||
|
@ -361,7 +375,7 @@ export class WorkflowDataProxy {
|
||||||
},
|
},
|
||||||
$item: (itemIndex: number, runIndex?: number) => {
|
$item: (itemIndex: number, runIndex?: number) => {
|
||||||
const defaultReturnRunIndex = runIndex === undefined ? -1 : runIndex;
|
const defaultReturnRunIndex = runIndex === undefined ? -1 : runIndex;
|
||||||
const dataProxy = new WorkflowDataProxy(this.workflow, this.runExecutionData, this.runIndex, itemIndex, this.activeNodeName, this.connectionInputData, that.mode, defaultReturnRunIndex);
|
const dataProxy = new WorkflowDataProxy(this.workflow, this.runExecutionData, this.runIndex, itemIndex, this.activeNodeName, this.connectionInputData, that.siblingParameters, that.mode, defaultReturnRunIndex);
|
||||||
return dataProxy.getDataProxy();
|
return dataProxy.getDataProxy();
|
||||||
},
|
},
|
||||||
$items: (nodeName?: string, outputIndex?: number, runIndex?: number) => {
|
$items: (nodeName?: string, outputIndex?: number, runIndex?: number) => {
|
||||||
|
|
|
@ -2174,7 +2174,7 @@ describe('Workflow', () => {
|
||||||
typeOptions: {
|
typeOptions: {
|
||||||
multipleValues: true,
|
multipleValues: true,
|
||||||
},
|
},
|
||||||
default: {},
|
default: [],
|
||||||
options: [
|
options: [
|
||||||
{
|
{
|
||||||
displayName: 'string1',
|
displayName: 'string1',
|
||||||
|
@ -2195,17 +2195,13 @@ describe('Workflow', () => {
|
||||||
},
|
},
|
||||||
output: {
|
output: {
|
||||||
noneDisplayedFalse: {
|
noneDisplayedFalse: {
|
||||||
defaultsFalse: {
|
defaultsFalse: {},
|
||||||
// collection1: [],
|
|
||||||
},
|
|
||||||
defaultsTrue: {
|
defaultsTrue: {
|
||||||
collection1: [],
|
collection1: [],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
noneDisplayedTrue: {
|
noneDisplayedTrue: {
|
||||||
defaultsFalse: {
|
defaultsFalse: {},
|
||||||
// collection1: [],
|
|
||||||
},
|
|
||||||
defaultsTrue: {
|
defaultsTrue: {
|
||||||
collection1: [],
|
collection1: [],
|
||||||
},
|
},
|
||||||
|
@ -2677,7 +2673,7 @@ describe('Workflow', () => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: 'complex type "fixedCollection" with "multipleValues: true". Which contains complex type "fixedCollection" with "multipleValues: true". One value set.',
|
description: 'complex type "fixedCollection" with "multipleValues: true". Which contains complex type "fixedCollection" with "multipleValues: true". One value set.',
|
||||||
input: {
|
input: {
|
||||||
nodePropertiesArray: [
|
nodePropertiesArray: [
|
||||||
{
|
{
|
||||||
|
@ -2814,6 +2810,302 @@ describe('Workflow', () => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
description: 'complex type "fixedCollection" with "multipleValues: true". Which contains parameters which get displayed on a parameter with a default expression with relative parameter references.',
|
||||||
|
input: {
|
||||||
|
nodePropertiesArray: [
|
||||||
|
{
|
||||||
|
displayName: 'Values1',
|
||||||
|
name: 'values1',
|
||||||
|
type: 'fixedCollection',
|
||||||
|
typeOptions: {
|
||||||
|
multipleValues: true,
|
||||||
|
},
|
||||||
|
description: 'The value to set.',
|
||||||
|
default: {},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
displayName: 'Options1',
|
||||||
|
name: 'options1',
|
||||||
|
values: [
|
||||||
|
{
|
||||||
|
displayName: 'Key',
|
||||||
|
name: 'key',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Type',
|
||||||
|
name: 'type',
|
||||||
|
type: 'hidden',
|
||||||
|
default: '={{$parameter["&key"].split("|")[1]}}',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Title Value',
|
||||||
|
name: 'titleValue',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
type: [
|
||||||
|
'title',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
type: 'string',
|
||||||
|
default: 'defaultTitle',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Title Number',
|
||||||
|
name: 'numberValue',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
type: [
|
||||||
|
'number',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
type: 'number',
|
||||||
|
default: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
nodeValues: {
|
||||||
|
values1: {
|
||||||
|
options1: [
|
||||||
|
{
|
||||||
|
key: 'asdf|title',
|
||||||
|
titleValue: 'different',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
noneDisplayedFalse: {
|
||||||
|
defaultsFalse: {
|
||||||
|
values1: {
|
||||||
|
options1: [
|
||||||
|
{
|
||||||
|
key: 'asdf|title',
|
||||||
|
titleValue: 'different',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultsTrue: {
|
||||||
|
values1: {
|
||||||
|
options1: [
|
||||||
|
{
|
||||||
|
key: 'asdf|title',
|
||||||
|
type: '={{$parameter["&key"].split("|")[1]}}',
|
||||||
|
// This is not great that it displays this theoretically hidden parameter
|
||||||
|
// but because we can not resolve the values for now
|
||||||
|
numberValue: 1,
|
||||||
|
titleValue: 'different',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
noneDisplayedTrue: {
|
||||||
|
defaultsFalse: {
|
||||||
|
values1: {
|
||||||
|
options1: [
|
||||||
|
{
|
||||||
|
key: 'asdf|title',
|
||||||
|
titleValue: 'different',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultsTrue: {
|
||||||
|
values1: {
|
||||||
|
options1: [
|
||||||
|
{
|
||||||
|
key: 'asdf|title',
|
||||||
|
type: '={{$parameter["&key"].split("|")[1]}}',
|
||||||
|
titleValue: 'different',
|
||||||
|
numberValue: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'complex type "fixedCollection" with "multipleValues: true". Which contains parameter of type "multiOptions" and has so an array default value',
|
||||||
|
input: {
|
||||||
|
nodePropertiesArray: [
|
||||||
|
{
|
||||||
|
name: 'values',
|
||||||
|
displayName: 'Values',
|
||||||
|
type: 'fixedCollection',
|
||||||
|
typeOptions: {
|
||||||
|
multipleValues: true,
|
||||||
|
},
|
||||||
|
default: {},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'propertyValues',
|
||||||
|
displayName: 'Property',
|
||||||
|
values: [
|
||||||
|
{
|
||||||
|
displayName: 'Options',
|
||||||
|
name: 'multiSelectValue',
|
||||||
|
type: 'multiOptions',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Value1',
|
||||||
|
value: 'value1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Value2',
|
||||||
|
value: 'value2',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
default: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
nodeValues: {
|
||||||
|
values: {
|
||||||
|
propertyValues: [
|
||||||
|
{
|
||||||
|
multiSelectValue: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
noneDisplayedFalse: {
|
||||||
|
defaultsFalse: {
|
||||||
|
values: {
|
||||||
|
propertyValues: [
|
||||||
|
{
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultsTrue: {
|
||||||
|
values: {
|
||||||
|
propertyValues: [
|
||||||
|
{
|
||||||
|
multiSelectValue: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
noneDisplayedTrue: {
|
||||||
|
defaultsFalse: {
|
||||||
|
values: {
|
||||||
|
propertyValues: [
|
||||||
|
{
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultsTrue: {
|
||||||
|
values: {
|
||||||
|
propertyValues: [
|
||||||
|
{
|
||||||
|
multiSelectValue: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'complex type "fixedCollection" with "multipleValues: true". Which contains parameter of type "string" with "multipleValues: true" and a custom default value',
|
||||||
|
input: {
|
||||||
|
nodePropertiesArray: [
|
||||||
|
{
|
||||||
|
name: 'values',
|
||||||
|
displayName: 'Values',
|
||||||
|
type: 'fixedCollection',
|
||||||
|
typeOptions: {
|
||||||
|
multipleValues: true,
|
||||||
|
},
|
||||||
|
default: {},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'propertyValues',
|
||||||
|
displayName: 'Property',
|
||||||
|
values: [
|
||||||
|
{
|
||||||
|
displayName: 'MultiString',
|
||||||
|
name: 'multiString',
|
||||||
|
type: 'string',
|
||||||
|
typeOptions: {
|
||||||
|
multipleValues: true,
|
||||||
|
},
|
||||||
|
default: ['value1'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
nodeValues: {
|
||||||
|
values: {
|
||||||
|
propertyValues: [
|
||||||
|
{
|
||||||
|
multiString: ['value1'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
noneDisplayedFalse: {
|
||||||
|
defaultsFalse: {
|
||||||
|
values: {
|
||||||
|
propertyValues: [
|
||||||
|
{
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultsTrue: {
|
||||||
|
values: {
|
||||||
|
propertyValues: [
|
||||||
|
{
|
||||||
|
multiString: ['value1'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
noneDisplayedTrue: {
|
||||||
|
defaultsFalse: {
|
||||||
|
values: {
|
||||||
|
propertyValues: [
|
||||||
|
{
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultsTrue: {
|
||||||
|
values: {
|
||||||
|
propertyValues: [
|
||||||
|
{
|
||||||
|
multiString: ['value1'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue