Merge branch 'master' of github.com:n8n-io/n8n
|
@ -1,49 +0,0 @@
|
||||||
FROM node:12.16-alpine as builder
|
|
||||||
# FROM node:12.16-alpine
|
|
||||||
|
|
||||||
# Update everything and install needed dependencies
|
|
||||||
RUN apk add --update graphicsmagick tzdata git tini su-exec
|
|
||||||
|
|
||||||
USER root
|
|
||||||
|
|
||||||
# Install all needed dependencies
|
|
||||||
RUN apk --update add --virtual build-dependencies python build-base ca-certificates && \
|
|
||||||
npm_config_user=root npm install -g full-icu lerna
|
|
||||||
|
|
||||||
ENV NODE_ICU_DATA /usr/local/lib/node_modules/full-icu
|
|
||||||
|
|
||||||
WORKDIR /data
|
|
||||||
|
|
||||||
COPY lerna.json .
|
|
||||||
COPY package.json .
|
|
||||||
COPY packages/cli/ ./packages/cli/
|
|
||||||
COPY packages/core/ ./packages/core/
|
|
||||||
COPY packages/editor-ui/ ./packages/editor-ui/
|
|
||||||
COPY packages/nodes-base/ ./packages/nodes-base/
|
|
||||||
COPY packages/workflow/ ./packages/workflow/
|
|
||||||
RUN rm -rf node_modules packages/*/node_modules packages/*/dist
|
|
||||||
|
|
||||||
RUN npm install --loglevel notice
|
|
||||||
RUN lerna bootstrap --hoist
|
|
||||||
RUN npm run build
|
|
||||||
|
|
||||||
|
|
||||||
FROM node:12.16-alpine
|
|
||||||
|
|
||||||
WORKDIR /data
|
|
||||||
|
|
||||||
# Install all needed dependencies
|
|
||||||
RUN npm_config_user=root npm install -g full-icu
|
|
||||||
|
|
||||||
USER root
|
|
||||||
|
|
||||||
ENV NODE_ICU_DATA /usr/local/lib/node_modules/full-icu
|
|
||||||
|
|
||||||
COPY --from=builder /data ./
|
|
||||||
|
|
||||||
RUN apk add --update graphicsmagick tzdata git tini su-exec
|
|
||||||
|
|
||||||
COPY docker/images/n8n-dev/docker-entrypoint.sh /docker-entrypoint.sh
|
|
||||||
ENTRYPOINT ["tini", "--", "/docker-entrypoint.sh"]
|
|
||||||
|
|
||||||
EXPOSE 5678/tcp
|
|
|
@ -2,6 +2,18 @@
|
||||||
|
|
||||||
This list shows all the versions which include breaking changes and how to upgrade.
|
This list shows all the versions which include breaking changes and how to upgrade.
|
||||||
|
|
||||||
|
## 0.105.0
|
||||||
|
|
||||||
|
### What changed?
|
||||||
|
In the Hubspot Trigger, now multiple events can be provided and the field `App ID` was so moved to the credentials.
|
||||||
|
|
||||||
|
### When is action necessary?
|
||||||
|
If you are using the Hubspot Trigger node.
|
||||||
|
|
||||||
|
### How to upgrade:
|
||||||
|
Open the Hubspot Trigger and set the events again. Also open the credentials `Hubspot Developer API` and set your APP ID.
|
||||||
|
|
||||||
|
|
||||||
## 0.104.0
|
## 0.104.0
|
||||||
|
|
||||||
### What changed?
|
### What changed?
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "n8n",
|
"name": "n8n",
|
||||||
"version": "0.104.2",
|
"version": "0.106.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",
|
||||||
|
@ -102,10 +102,10 @@
|
||||||
"localtunnel": "^2.0.0",
|
"localtunnel": "^2.0.0",
|
||||||
"lodash.get": "^4.4.2",
|
"lodash.get": "^4.4.2",
|
||||||
"mysql2": "~2.1.0",
|
"mysql2": "~2.1.0",
|
||||||
"n8n-core": "~0.61.0",
|
"n8n-core": "~0.62.0",
|
||||||
"n8n-editor-ui": "~0.74.0",
|
"n8n-editor-ui": "~0.76.0",
|
||||||
"n8n-nodes-base": "~0.101.0",
|
"n8n-nodes-base": "~0.103.0",
|
||||||
"n8n-workflow": "~0.50.0",
|
"n8n-workflow": "~0.51.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",
|
||||||
|
|
|
@ -277,7 +277,7 @@ export class ActiveWorkflowRunner {
|
||||||
path = node.parameters.path as string;
|
path = node.parameters.path as string;
|
||||||
|
|
||||||
if (node.parameters.path === undefined) {
|
if (node.parameters.path === undefined) {
|
||||||
path = workflow.expression.getSimpleParameterValue(node, webhookData.webhookDescription['path']) as string | undefined;
|
path = workflow.expression.getSimpleParameterValue(node, webhookData.webhookDescription['path'], mode) as string | undefined;
|
||||||
|
|
||||||
if (path === undefined) {
|
if (path === undefined) {
|
||||||
// TODO: Use a proper logger
|
// TODO: Use a proper logger
|
||||||
|
@ -286,7 +286,7 @@ export class ActiveWorkflowRunner {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const isFullPath: boolean = workflow.expression.getSimpleParameterValue(node, webhookData.webhookDescription['isFullPath'], false) as boolean;
|
const isFullPath: boolean = workflow.expression.getSimpleParameterValue(node, webhookData.webhookDescription['isFullPath'], mode, false) as boolean;
|
||||||
|
|
||||||
const webhook = {
|
const webhook = {
|
||||||
workflowId: webhookData.workflowId,
|
workflowId: webhookData.workflowId,
|
||||||
|
|
|
@ -14,6 +14,7 @@ import {
|
||||||
INodeTypes,
|
INodeTypes,
|
||||||
NodeHelpers,
|
NodeHelpers,
|
||||||
Workflow,
|
Workflow,
|
||||||
|
WorkflowExecuteMode,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
@ -101,7 +102,7 @@ export class CredentialsHelper extends ICredentialsHelper {
|
||||||
* @returns {ICredentialDataDecryptedObject}
|
* @returns {ICredentialDataDecryptedObject}
|
||||||
* @memberof CredentialsHelper
|
* @memberof CredentialsHelper
|
||||||
*/
|
*/
|
||||||
getDecrypted(name: string, type: string, raw?: boolean, expressionResolveValues?: ICredentialsExpressionResolveValues): ICredentialDataDecryptedObject {
|
getDecrypted(name: string, type: string, mode: WorkflowExecuteMode, raw?: boolean, expressionResolveValues?: ICredentialsExpressionResolveValues): ICredentialDataDecryptedObject {
|
||||||
const credentials = this.getCredentials(name, type);
|
const credentials = this.getCredentials(name, type);
|
||||||
|
|
||||||
const decryptedDataOriginal = credentials.getData(this.encryptionKey);
|
const decryptedDataOriginal = credentials.getData(this.encryptionKey);
|
||||||
|
@ -110,7 +111,7 @@ export class CredentialsHelper extends ICredentialsHelper {
|
||||||
return decryptedDataOriginal;
|
return decryptedDataOriginal;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.applyDefaultsAndOverwrites(decryptedDataOriginal, type, expressionResolveValues);
|
return this.applyDefaultsAndOverwrites(decryptedDataOriginal, type, mode, expressionResolveValues);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -122,7 +123,7 @@ export class CredentialsHelper extends ICredentialsHelper {
|
||||||
* @returns {ICredentialDataDecryptedObject}
|
* @returns {ICredentialDataDecryptedObject}
|
||||||
* @memberof CredentialsHelper
|
* @memberof CredentialsHelper
|
||||||
*/
|
*/
|
||||||
applyDefaultsAndOverwrites(decryptedDataOriginal: ICredentialDataDecryptedObject, type: string, expressionResolveValues?: ICredentialsExpressionResolveValues): ICredentialDataDecryptedObject {
|
applyDefaultsAndOverwrites(decryptedDataOriginal: ICredentialDataDecryptedObject, type: string, mode: WorkflowExecuteMode, expressionResolveValues?: ICredentialsExpressionResolveValues): ICredentialDataDecryptedObject {
|
||||||
const credentialsProperties = this.getCredentialsProperties(type);
|
const credentialsProperties = this.getCredentialsProperties(type);
|
||||||
|
|
||||||
// Add the default credential values
|
// Add the default credential values
|
||||||
|
@ -137,7 +138,7 @@ export class CredentialsHelper extends ICredentialsHelper {
|
||||||
if (expressionResolveValues) {
|
if (expressionResolveValues) {
|
||||||
try {
|
try {
|
||||||
const workflow = new Workflow({ nodes: Object.values(expressionResolveValues.workflow.nodes), connections: expressionResolveValues.workflow.connectionsBySourceNode, active: false, nodeTypes: expressionResolveValues.workflow.nodeTypes });
|
const workflow = new Workflow({ nodes: Object.values(expressionResolveValues.workflow.nodes), connections: expressionResolveValues.workflow.connectionsBySourceNode, active: false, nodeTypes: expressionResolveValues.workflow.nodeTypes });
|
||||||
decryptedData = workflow.expression.getParameterValue(decryptedData as INodeParameters, expressionResolveValues.runExecutionData, expressionResolveValues.runIndex, expressionResolveValues.itemIndex, expressionResolveValues.node.name, expressionResolveValues.connectionInputData, false, decryptedData) as ICredentialDataDecryptedObject;
|
decryptedData = workflow.expression.getParameterValue(decryptedData as INodeParameters, expressionResolveValues.runExecutionData, expressionResolveValues.runIndex, expressionResolveValues.itemIndex, expressionResolveValues.node.name, expressionResolveValues.connectionInputData, mode, false, decryptedData) as ICredentialDataDecryptedObject;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
e.message += ' [Error resolving credentials]';
|
e.message += ' [Error resolving credentials]';
|
||||||
throw e;
|
throw e;
|
||||||
|
@ -154,7 +155,7 @@ export class CredentialsHelper extends ICredentialsHelper {
|
||||||
const workflow = new Workflow({ nodes: [node!], connections: {}, active: false, nodeTypes: mockNodeTypes });
|
const workflow = new Workflow({ nodes: [node!], connections: {}, active: false, nodeTypes: mockNodeTypes });
|
||||||
|
|
||||||
// Resolve expressions if any are set
|
// Resolve expressions if any are set
|
||||||
decryptedData = workflow.expression.getComplexParameterValue(node!, decryptedData as INodeParameters, undefined, decryptedData) as ICredentialDataDecryptedObject;
|
decryptedData = workflow.expression.getComplexParameterValue(node!, decryptedData as INodeParameters, mode, undefined, decryptedData) as ICredentialDataDecryptedObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load and apply the credentials overwrites if any exist
|
// Load and apply the credentials overwrites if any exist
|
||||||
|
|
|
@ -82,6 +82,7 @@ import {
|
||||||
IRunData,
|
IRunData,
|
||||||
IWorkflowCredentials,
|
IWorkflowCredentials,
|
||||||
Workflow,
|
Workflow,
|
||||||
|
WorkflowExecuteMode,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
@ -1078,9 +1079,10 @@ class App {
|
||||||
[result.name as string]: result as ICredentialsEncrypted,
|
[result.name as string]: result as ICredentialsEncrypted,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
const mode: WorkflowExecuteMode = 'internal';
|
||||||
const credentialsHelper = new CredentialsHelper(workflowCredentials, encryptionKey);
|
const credentialsHelper = new CredentialsHelper(workflowCredentials, encryptionKey);
|
||||||
const decryptedDataOriginal = credentialsHelper.getDecrypted(result.name, result.type, true);
|
const decryptedDataOriginal = credentialsHelper.getDecrypted(result.name, result.type, mode, true);
|
||||||
const oauthCredentials = credentialsHelper.applyDefaultsAndOverwrites(decryptedDataOriginal, result.type);
|
const oauthCredentials = credentialsHelper.applyDefaultsAndOverwrites(decryptedDataOriginal, result.type, mode);
|
||||||
|
|
||||||
const signatureMethod = _.get(oauthCredentials, 'signatureMethod') as string;
|
const signatureMethod = _.get(oauthCredentials, 'signatureMethod') as string;
|
||||||
|
|
||||||
|
@ -1168,9 +1170,10 @@ class App {
|
||||||
[result.name as string]: result as ICredentialsEncrypted,
|
[result.name as string]: result as ICredentialsEncrypted,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
const mode: WorkflowExecuteMode = 'internal';
|
||||||
const credentialsHelper = new CredentialsHelper(workflowCredentials, encryptionKey);
|
const credentialsHelper = new CredentialsHelper(workflowCredentials, encryptionKey);
|
||||||
const decryptedDataOriginal = credentialsHelper.getDecrypted(result.name, result.type, true);
|
const decryptedDataOriginal = credentialsHelper.getDecrypted(result.name, result.type, mode, true);
|
||||||
const oauthCredentials = credentialsHelper.applyDefaultsAndOverwrites(decryptedDataOriginal, result.type);
|
const oauthCredentials = credentialsHelper.applyDefaultsAndOverwrites(decryptedDataOriginal, result.type, mode);
|
||||||
|
|
||||||
const options: OptionsWithUrl = {
|
const options: OptionsWithUrl = {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
@ -1239,9 +1242,10 @@ class App {
|
||||||
[result.name as string]: result as ICredentialsEncrypted,
|
[result.name as string]: result as ICredentialsEncrypted,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
const mode: WorkflowExecuteMode = 'internal';
|
||||||
const credentialsHelper = new CredentialsHelper(workflowCredentials, encryptionKey);
|
const credentialsHelper = new CredentialsHelper(workflowCredentials, encryptionKey);
|
||||||
const decryptedDataOriginal = credentialsHelper.getDecrypted(result.name, result.type, true);
|
const decryptedDataOriginal = credentialsHelper.getDecrypted(result.name, result.type, mode, true);
|
||||||
const oauthCredentials = credentialsHelper.applyDefaultsAndOverwrites(decryptedDataOriginal, result.type);
|
const oauthCredentials = credentialsHelper.applyDefaultsAndOverwrites(decryptedDataOriginal, result.type, mode);
|
||||||
|
|
||||||
const token = new csrf();
|
const token = new csrf();
|
||||||
// Generate a CSRF prevention token and send it as a OAuth2 state stringma/ERR
|
// Generate a CSRF prevention token and send it as a OAuth2 state stringma/ERR
|
||||||
|
@ -1336,9 +1340,10 @@ class App {
|
||||||
[result.name as string]: result as ICredentialsEncrypted,
|
[result.name as string]: result as ICredentialsEncrypted,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
const mode: WorkflowExecuteMode = 'internal';
|
||||||
const credentialsHelper = new CredentialsHelper(workflowCredentials, encryptionKey);
|
const credentialsHelper = new CredentialsHelper(workflowCredentials, encryptionKey);
|
||||||
const decryptedDataOriginal = credentialsHelper.getDecrypted(result.name, result.type, true);
|
const decryptedDataOriginal = credentialsHelper.getDecrypted(result.name, result.type, mode, true);
|
||||||
const oauthCredentials = credentialsHelper.applyDefaultsAndOverwrites(decryptedDataOriginal, result.type);
|
const oauthCredentials = credentialsHelper.applyDefaultsAndOverwrites(decryptedDataOriginal, result.type, mode);
|
||||||
|
|
||||||
const token = new csrf();
|
const token = new csrf();
|
||||||
if (decryptedDataOriginal.csrfSecret === undefined || !token.verify(decryptedDataOriginal.csrfSecret as string, state.token)) {
|
if (decryptedDataOriginal.csrfSecret === undefined || !token.verify(decryptedDataOriginal.csrfSecret as string, state.token)) {
|
||||||
|
|
|
@ -115,8 +115,8 @@ export function getWorkflowWebhooksBasic(workflow: Workflow): IWebhookData[] {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the responseMode
|
// Get the responseMode
|
||||||
const responseMode = workflow.expression.getSimpleParameterValue(workflowStartNode, webhookData.webhookDescription['responseMode'], 'onReceived');
|
const responseMode = workflow.expression.getSimpleParameterValue(workflowStartNode, webhookData.webhookDescription['responseMode'], executionMode, 'onReceived');
|
||||||
const responseCode = workflow.expression.getSimpleParameterValue(workflowStartNode, webhookData.webhookDescription['responseCode'], 200) as number;
|
const responseCode = workflow.expression.getSimpleParameterValue(workflowStartNode, webhookData.webhookDescription['responseCode'], executionMode, 200) as number;
|
||||||
|
|
||||||
if (!['onReceived', 'lastNode'].includes(responseMode as string)) {
|
if (!['onReceived', 'lastNode'].includes(responseMode as string)) {
|
||||||
// If the mode is not known we error. Is probably best like that instead of using
|
// If the mode is not known we error. Is probably best like that instead of using
|
||||||
|
@ -174,7 +174,7 @@ export function getWorkflowWebhooksBasic(workflow: Workflow): IWebhookData[] {
|
||||||
await WorkflowHelpers.saveStaticData(workflow);
|
await WorkflowHelpers.saveStaticData(workflow);
|
||||||
|
|
||||||
if (webhookData.webhookDescription['responseHeaders'] !== undefined) {
|
if (webhookData.webhookDescription['responseHeaders'] !== undefined) {
|
||||||
const responseHeaders = workflow.expression.getComplexParameterValue(workflowStartNode, webhookData.webhookDescription['responseHeaders'], undefined) as {
|
const responseHeaders = workflow.expression.getComplexParameterValue(workflowStartNode, webhookData.webhookDescription['responseHeaders'], executionMode, undefined) as {
|
||||||
entries?: Array<{
|
entries?: Array<{
|
||||||
name: string;
|
name: string;
|
||||||
value: string;
|
value: string;
|
||||||
|
@ -328,7 +328,7 @@ export function getWorkflowWebhooksBasic(workflow: Workflow): IWebhookData[] {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
const responseData = workflow.expression.getSimpleParameterValue(workflowStartNode, webhookData.webhookDescription['responseData'], 'firstEntryJson');
|
const responseData = workflow.expression.getSimpleParameterValue(workflowStartNode, webhookData.webhookDescription['responseData'], executionMode, 'firstEntryJson');
|
||||||
|
|
||||||
if (didSendResponse === false) {
|
if (didSendResponse === false) {
|
||||||
let data: IDataObject | IDataObject[];
|
let data: IDataObject | IDataObject[];
|
||||||
|
@ -343,13 +343,13 @@ export function getWorkflowWebhooksBasic(workflow: Workflow): IWebhookData[] {
|
||||||
|
|
||||||
data = returnData.data!.main[0]![0].json;
|
data = returnData.data!.main[0]![0].json;
|
||||||
|
|
||||||
const responsePropertyName = workflow.expression.getSimpleParameterValue(workflowStartNode, webhookData.webhookDescription['responsePropertyName'], undefined);
|
const responsePropertyName = workflow.expression.getSimpleParameterValue(workflowStartNode, webhookData.webhookDescription['responsePropertyName'], executionMode, undefined);
|
||||||
|
|
||||||
if (responsePropertyName !== undefined) {
|
if (responsePropertyName !== undefined) {
|
||||||
data = get(data, responsePropertyName as string) as IDataObject;
|
data = get(data, responsePropertyName as string) as IDataObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
const responseContentType = workflow.expression.getSimpleParameterValue(workflowStartNode, webhookData.webhookDescription['responseContentType'], undefined);
|
const responseContentType = workflow.expression.getSimpleParameterValue(workflowStartNode, webhookData.webhookDescription['responseContentType'], executionMode, undefined);
|
||||||
|
|
||||||
if (responseContentType !== undefined) {
|
if (responseContentType !== undefined) {
|
||||||
// Send the webhook response manually to be able to set the content-type
|
// Send the webhook response manually to be able to set the content-type
|
||||||
|
@ -382,7 +382,7 @@ export function getWorkflowWebhooksBasic(workflow: Workflow): IWebhookData[] {
|
||||||
didSendResponse = true;
|
didSendResponse = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const responseBinaryPropertyName = workflow.expression.getSimpleParameterValue(workflowStartNode, webhookData.webhookDescription['responseBinaryPropertyName'], 'data');
|
const responseBinaryPropertyName = workflow.expression.getSimpleParameterValue(workflowStartNode, webhookData.webhookDescription['responseBinaryPropertyName'], executionMode, 'data');
|
||||||
|
|
||||||
if (responseBinaryPropertyName === undefined && didSendResponse === false) {
|
if (responseBinaryPropertyName === undefined && didSendResponse === false) {
|
||||||
responseCallback(new Error('No "responseBinaryPropertyName" is set.'), {});
|
responseCallback(new Error('No "responseBinaryPropertyName" is set.'), {});
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "n8n-core",
|
"name": "n8n-core",
|
||||||
"version": "0.61.0",
|
"version": "0.62.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.50.0",
|
"n8n-workflow": "~0.51.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",
|
||||||
|
|
|
@ -299,7 +299,7 @@ export function returnJsonArray(jsonData: IDataObject | IDataObject[]): INodeExe
|
||||||
* @param {IWorkflowExecuteAdditionalData} additionalData
|
* @param {IWorkflowExecuteAdditionalData} additionalData
|
||||||
* @returns {(ICredentialDataDecryptedObject | undefined)}
|
* @returns {(ICredentialDataDecryptedObject | undefined)}
|
||||||
*/
|
*/
|
||||||
export function getCredentials(workflow: Workflow, node: INode, type: string, additionalData: IWorkflowExecuteAdditionalData, runExecutionData?: IRunExecutionData | null, runIndex?: number, connectionInputData?: INodeExecutionData[], itemIndex?: number): ICredentialDataDecryptedObject | undefined {
|
export function getCredentials(workflow: Workflow, node: INode, type: string, additionalData: IWorkflowExecuteAdditionalData, mode: WorkflowExecuteMode, runExecutionData?: IRunExecutionData | null, runIndex?: number, connectionInputData?: INodeExecutionData[], itemIndex?: number): ICredentialDataDecryptedObject | undefined {
|
||||||
|
|
||||||
// Get the NodeType as it has the information if the credentials are required
|
// Get the NodeType as it has the information if the credentials are required
|
||||||
const nodeType = workflow.nodeTypes.getByName(node.type);
|
const nodeType = workflow.nodeTypes.getByName(node.type);
|
||||||
|
@ -353,7 +353,7 @@ export function getCredentials(workflow: Workflow, node: INode, type: string, ad
|
||||||
|
|
||||||
const name = node.credentials[type];
|
const name = node.credentials[type];
|
||||||
|
|
||||||
const decryptedDataObject = additionalData.credentialsHelper.getDecrypted(name, type, false, expressionResolveValues);
|
const decryptedDataObject = additionalData.credentialsHelper.getDecrypted(name, type, mode, false, expressionResolveValues);
|
||||||
|
|
||||||
return decryptedDataObject;
|
return decryptedDataObject;
|
||||||
}
|
}
|
||||||
|
@ -387,7 +387,7 @@ export function getNode(node: INode): INode {
|
||||||
* @param {*} [fallbackValue]
|
* @param {*} [fallbackValue]
|
||||||
* @returns {(NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] | object)}
|
* @returns {(NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] | object)}
|
||||||
*/
|
*/
|
||||||
export function getNodeParameter(workflow: Workflow, runExecutionData: IRunExecutionData | null, runIndex: number, connectionInputData: INodeExecutionData[], node: INode, parameterName: string, itemIndex: number, fallbackValue?: any): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] | object { //tslint:disable-line:no-any
|
export function getNodeParameter(workflow: Workflow, runExecutionData: IRunExecutionData | null, runIndex: number, connectionInputData: INodeExecutionData[], node: INode, parameterName: string, itemIndex: number, mode: WorkflowExecuteMode, fallbackValue?: any): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] | object { //tslint:disable-line:no-any
|
||||||
const nodeType = workflow.nodeTypes.getByName(node.type);
|
const nodeType = workflow.nodeTypes.getByName(node.type);
|
||||||
if (nodeType === undefined) {
|
if (nodeType === undefined) {
|
||||||
throw new Error(`Node type "${node.type}" is not known so can not return paramter value!`);
|
throw new Error(`Node type "${node.type}" is not known so can not return paramter value!`);
|
||||||
|
@ -401,7 +401,7 @@ export function getNodeParameter(workflow: Workflow, runExecutionData: IRunExecu
|
||||||
|
|
||||||
let returnData;
|
let returnData;
|
||||||
try {
|
try {
|
||||||
returnData = workflow.expression.getParameterValue(value, runExecutionData, runIndex, itemIndex, node.name, connectionInputData);
|
returnData = workflow.expression.getParameterValue(value, runExecutionData, runIndex, itemIndex, node.name, connectionInputData, mode);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
e.message += ` [Error in parameter: "${parameterName}"]`;
|
e.message += ` [Error in parameter: "${parameterName}"]`;
|
||||||
throw e;
|
throw e;
|
||||||
|
@ -436,7 +436,7 @@ export function continueOnFail(node: INode): boolean {
|
||||||
* @param {boolean} [isTest]
|
* @param {boolean} [isTest]
|
||||||
* @returns {(string | undefined)}
|
* @returns {(string | undefined)}
|
||||||
*/
|
*/
|
||||||
export function getNodeWebhookUrl(name: string, workflow: Workflow, node: INode, additionalData: IWorkflowExecuteAdditionalData, isTest?: boolean): string | undefined {
|
export function getNodeWebhookUrl(name: string, workflow: Workflow, node: INode, additionalData: IWorkflowExecuteAdditionalData, mode: WorkflowExecuteMode, isTest?: boolean): string | undefined {
|
||||||
let baseUrl = additionalData.webhookBaseUrl;
|
let baseUrl = additionalData.webhookBaseUrl;
|
||||||
if (isTest === true) {
|
if (isTest === true) {
|
||||||
baseUrl = additionalData.webhookTestBaseUrl;
|
baseUrl = additionalData.webhookTestBaseUrl;
|
||||||
|
@ -447,12 +447,12 @@ export function getNodeWebhookUrl(name: string, workflow: Workflow, node: INode,
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const path = workflow.expression.getSimpleParameterValue(node, webhookDescription['path']);
|
const path = workflow.expression.getSimpleParameterValue(node, webhookDescription['path'], mode);
|
||||||
if (path === undefined) {
|
if (path === undefined) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const isFullPath: boolean = workflow.expression.getSimpleParameterValue(node, webhookDescription['isFullPath'], false) as boolean;
|
const isFullPath: boolean = workflow.expression.getSimpleParameterValue(node, webhookDescription['isFullPath'], mode, false) as boolean;
|
||||||
return NodeHelpers.getNodeWebhookUrl(baseUrl, workflow.id!, node, path.toString(), isFullPath);
|
return NodeHelpers.getNodeWebhookUrl(baseUrl, workflow.id!, node, path.toString(), isFullPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -538,7 +538,7 @@ export function getExecutePollFunctions(workflow: Workflow, node: INode, additio
|
||||||
throw new Error('Overwrite NodeExecuteFunctions.getExecutePullFunctions.__emit function!');
|
throw new Error('Overwrite NodeExecuteFunctions.getExecutePullFunctions.__emit function!');
|
||||||
},
|
},
|
||||||
getCredentials(type: string): ICredentialDataDecryptedObject | undefined {
|
getCredentials(type: string): ICredentialDataDecryptedObject | undefined {
|
||||||
return getCredentials(workflow, node, type, additionalData);
|
return getCredentials(workflow, node, type, additionalData, mode);
|
||||||
},
|
},
|
||||||
getMode: (): WorkflowExecuteMode => {
|
getMode: (): WorkflowExecuteMode => {
|
||||||
return mode;
|
return mode;
|
||||||
|
@ -552,7 +552,7 @@ export function getExecutePollFunctions(workflow: Workflow, node: INode, additio
|
||||||
const runIndex = 0;
|
const runIndex = 0;
|
||||||
const connectionInputData: INodeExecutionData[] = [];
|
const connectionInputData: INodeExecutionData[] = [];
|
||||||
|
|
||||||
return getNodeParameter(workflow, runExecutionData, runIndex, connectionInputData, node, parameterName, itemIndex, fallbackValue);
|
return getNodeParameter(workflow, runExecutionData, runIndex, connectionInputData, node, parameterName, itemIndex, mode, fallbackValue);
|
||||||
},
|
},
|
||||||
getRestApiUrl: (): string => {
|
getRestApiUrl: (): string => {
|
||||||
return additionalData.restApiUrl;
|
return additionalData.restApiUrl;
|
||||||
|
@ -601,7 +601,7 @@ export function getExecuteTriggerFunctions(workflow: Workflow, node: INode, addi
|
||||||
throw new Error('Overwrite NodeExecuteFunctions.getExecuteTriggerFunctions.emit function!');
|
throw new Error('Overwrite NodeExecuteFunctions.getExecuteTriggerFunctions.emit function!');
|
||||||
},
|
},
|
||||||
getCredentials(type: string): ICredentialDataDecryptedObject | undefined {
|
getCredentials(type: string): ICredentialDataDecryptedObject | undefined {
|
||||||
return getCredentials(workflow, node, type, additionalData);
|
return getCredentials(workflow, node, type, additionalData, mode);
|
||||||
},
|
},
|
||||||
getNode: () => {
|
getNode: () => {
|
||||||
return getNode(node);
|
return getNode(node);
|
||||||
|
@ -615,7 +615,7 @@ export function getExecuteTriggerFunctions(workflow: Workflow, node: INode, addi
|
||||||
const runIndex = 0;
|
const runIndex = 0;
|
||||||
const connectionInputData: INodeExecutionData[] = [];
|
const connectionInputData: INodeExecutionData[] = [];
|
||||||
|
|
||||||
return getNodeParameter(workflow, runExecutionData, runIndex, connectionInputData, node, parameterName, itemIndex, fallbackValue);
|
return getNodeParameter(workflow, runExecutionData, runIndex, connectionInputData, node, parameterName, itemIndex, mode, fallbackValue);
|
||||||
},
|
},
|
||||||
getRestApiUrl: (): string => {
|
getRestApiUrl: (): string => {
|
||||||
return additionalData.restApiUrl;
|
return additionalData.restApiUrl;
|
||||||
|
@ -667,7 +667,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);
|
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);
|
||||||
|
@ -676,7 +676,7 @@ export function getExecuteFunctions(workflow: Workflow, runExecutionData: IRunEx
|
||||||
return NodeHelpers.getContext(runExecutionData, type, node);
|
return NodeHelpers.getContext(runExecutionData, type, node);
|
||||||
},
|
},
|
||||||
getCredentials(type: string, itemIndex?: number): ICredentialDataDecryptedObject | undefined {
|
getCredentials(type: string, itemIndex?: number): ICredentialDataDecryptedObject | undefined {
|
||||||
return getCredentials(workflow, node, type, additionalData, runExecutionData, runIndex, connectionInputData, itemIndex);
|
return getCredentials(workflow, node, type, additionalData, mode, runExecutionData, runIndex, connectionInputData, itemIndex);
|
||||||
},
|
},
|
||||||
getInputData: (inputIndex = 0, inputName = 'main') => {
|
getInputData: (inputIndex = 0, inputName = 'main') => {
|
||||||
|
|
||||||
|
@ -700,7 +700,7 @@ export function getExecuteFunctions(workflow: Workflow, runExecutionData: IRunEx
|
||||||
return inputData[inputName][inputIndex] as INodeExecutionData[];
|
return inputData[inputName][inputIndex] as INodeExecutionData[];
|
||||||
},
|
},
|
||||||
getNodeParameter: (parameterName: string, itemIndex: number, fallbackValue?: any): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] | object => { //tslint:disable-line:no-any
|
getNodeParameter: (parameterName: string, itemIndex: number, fallbackValue?: any): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] | object => { //tslint:disable-line:no-any
|
||||||
return getNodeParameter(workflow, runExecutionData, runIndex, connectionInputData, node, parameterName, itemIndex, fallbackValue);
|
return getNodeParameter(workflow, runExecutionData, runIndex, connectionInputData, node, parameterName, itemIndex, mode, fallbackValue);
|
||||||
},
|
},
|
||||||
getMode: (): WorkflowExecuteMode => {
|
getMode: (): WorkflowExecuteMode => {
|
||||||
return mode;
|
return mode;
|
||||||
|
@ -718,7 +718,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);
|
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 {
|
||||||
|
@ -765,13 +765,13 @@ 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);
|
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);
|
||||||
},
|
},
|
||||||
getCredentials(type: string): ICredentialDataDecryptedObject | undefined {
|
getCredentials(type: string): ICredentialDataDecryptedObject | undefined {
|
||||||
return getCredentials(workflow, node, type, additionalData, runExecutionData, runIndex, connectionInputData, itemIndex);
|
return getCredentials(workflow, node, type, additionalData, mode, runExecutionData, runIndex, connectionInputData, itemIndex);
|
||||||
},
|
},
|
||||||
getInputData: (inputIndex = 0, inputName = 'main') => {
|
getInputData: (inputIndex = 0, inputName = 'main') => {
|
||||||
if (!inputData.hasOwnProperty(inputName)) {
|
if (!inputData.hasOwnProperty(inputName)) {
|
||||||
|
@ -811,13 +811,13 @@ export function getExecuteSingleFunctions(workflow: Workflow, runExecutionData:
|
||||||
return getTimezone(workflow, additionalData);
|
return getTimezone(workflow, additionalData);
|
||||||
},
|
},
|
||||||
getNodeParameter: (parameterName: string, fallbackValue?: any): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] | object => { //tslint:disable-line:no-any
|
getNodeParameter: (parameterName: string, fallbackValue?: any): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] | object => { //tslint:disable-line:no-any
|
||||||
return getNodeParameter(workflow, runExecutionData, runIndex, connectionInputData, node, parameterName, itemIndex, fallbackValue);
|
return getNodeParameter(workflow, runExecutionData, runIndex, connectionInputData, node, parameterName, itemIndex, mode, fallbackValue);
|
||||||
},
|
},
|
||||||
getWorkflow: () => {
|
getWorkflow: () => {
|
||||||
return getWorkflowMetadata(workflow);
|
return getWorkflowMetadata(workflow);
|
||||||
},
|
},
|
||||||
getWorkflowDataProxy: (): IWorkflowDataProxyData => {
|
getWorkflowDataProxy: (): IWorkflowDataProxyData => {
|
||||||
const dataProxy = new WorkflowDataProxy(workflow, runExecutionData, runIndex, itemIndex, node.name, connectionInputData);
|
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 {
|
||||||
|
@ -851,7 +851,7 @@ export function getLoadOptionsFunctions(workflow: Workflow, node: INode, additio
|
||||||
return ((workflow: Workflow, node: INode) => {
|
return ((workflow: Workflow, node: INode) => {
|
||||||
const that = {
|
const that = {
|
||||||
getCredentials(type: string): ICredentialDataDecryptedObject | undefined {
|
getCredentials(type: string): ICredentialDataDecryptedObject | undefined {
|
||||||
return getCredentials(workflow, node, type, additionalData);
|
return getCredentials(workflow, node, type, additionalData, 'internal');
|
||||||
},
|
},
|
||||||
getCurrentNodeParameter: (parameterName: string): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] | object | undefined => {
|
getCurrentNodeParameter: (parameterName: string): NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[] | object | undefined => {
|
||||||
const nodeParameters = additionalData.currentNodeParameters;
|
const nodeParameters = additionalData.currentNodeParameters;
|
||||||
|
@ -872,7 +872,7 @@ export function getLoadOptionsFunctions(workflow: Workflow, node: INode, additio
|
||||||
const runIndex = 0;
|
const runIndex = 0;
|
||||||
const connectionInputData: INodeExecutionData[] = [];
|
const connectionInputData: INodeExecutionData[] = [];
|
||||||
|
|
||||||
return getNodeParameter(workflow, runExecutionData, runIndex, connectionInputData, node, parameterName, itemIndex, fallbackValue);
|
return getNodeParameter(workflow, runExecutionData, runIndex, connectionInputData, node, parameterName, itemIndex, 'internal' as WorkflowExecuteMode, fallbackValue);
|
||||||
},
|
},
|
||||||
getTimezone: (): string => {
|
getTimezone: (): string => {
|
||||||
return getTimezone(workflow, additionalData);
|
return getTimezone(workflow, additionalData);
|
||||||
|
@ -910,7 +910,7 @@ export function getExecuteHookFunctions(workflow: Workflow, node: INode, additio
|
||||||
return ((workflow: Workflow, node: INode) => {
|
return ((workflow: Workflow, node: INode) => {
|
||||||
const that = {
|
const that = {
|
||||||
getCredentials(type: string): ICredentialDataDecryptedObject | undefined {
|
getCredentials(type: string): ICredentialDataDecryptedObject | undefined {
|
||||||
return getCredentials(workflow, node, type, additionalData);
|
return getCredentials(workflow, node, type, additionalData, mode);
|
||||||
},
|
},
|
||||||
getMode: (): WorkflowExecuteMode => {
|
getMode: (): WorkflowExecuteMode => {
|
||||||
return mode;
|
return mode;
|
||||||
|
@ -924,10 +924,10 @@ export function getExecuteHookFunctions(workflow: Workflow, node: INode, additio
|
||||||
const runIndex = 0;
|
const runIndex = 0;
|
||||||
const connectionInputData: INodeExecutionData[] = [];
|
const connectionInputData: INodeExecutionData[] = [];
|
||||||
|
|
||||||
return getNodeParameter(workflow, runExecutionData, runIndex, connectionInputData, node, parameterName, itemIndex, fallbackValue);
|
return getNodeParameter(workflow, runExecutionData, runIndex, connectionInputData, node, parameterName, itemIndex, mode, fallbackValue);
|
||||||
},
|
},
|
||||||
getNodeWebhookUrl: (name: string): string | undefined => {
|
getNodeWebhookUrl: (name: string): string | undefined => {
|
||||||
return getNodeWebhookUrl(name, workflow, node, additionalData, isTest);
|
return getNodeWebhookUrl(name, workflow, node, additionalData, mode, isTest);
|
||||||
},
|
},
|
||||||
getTimezone: (): string => {
|
getTimezone: (): string => {
|
||||||
return getTimezone(workflow, additionalData);
|
return getTimezone(workflow, additionalData);
|
||||||
|
@ -984,7 +984,7 @@ export function getExecuteWebhookFunctions(workflow: Workflow, node: INode, addi
|
||||||
return additionalData.httpRequest.body;
|
return additionalData.httpRequest.body;
|
||||||
},
|
},
|
||||||
getCredentials(type: string): ICredentialDataDecryptedObject | undefined {
|
getCredentials(type: string): ICredentialDataDecryptedObject | undefined {
|
||||||
return getCredentials(workflow, node, type, additionalData);
|
return getCredentials(workflow, node, type, additionalData, mode);
|
||||||
},
|
},
|
||||||
getHeaderData(): object {
|
getHeaderData(): object {
|
||||||
if (additionalData.httpRequest === undefined) {
|
if (additionalData.httpRequest === undefined) {
|
||||||
|
@ -1004,7 +1004,7 @@ export function getExecuteWebhookFunctions(workflow: Workflow, node: INode, addi
|
||||||
const runIndex = 0;
|
const runIndex = 0;
|
||||||
const connectionInputData: INodeExecutionData[] = [];
|
const connectionInputData: INodeExecutionData[] = [];
|
||||||
|
|
||||||
return getNodeParameter(workflow, runExecutionData, runIndex, connectionInputData, node, parameterName, itemIndex, fallbackValue);
|
return getNodeParameter(workflow, runExecutionData, runIndex, connectionInputData, node, parameterName, itemIndex, mode, fallbackValue);
|
||||||
},
|
},
|
||||||
getParamsData(): object {
|
getParamsData(): object {
|
||||||
if (additionalData.httpRequest === undefined) {
|
if (additionalData.httpRequest === undefined) {
|
||||||
|
@ -1031,7 +1031,7 @@ export function getExecuteWebhookFunctions(workflow: Workflow, node: INode, addi
|
||||||
return additionalData.httpResponse;
|
return additionalData.httpResponse;
|
||||||
},
|
},
|
||||||
getNodeWebhookUrl: (name: string): string | undefined => {
|
getNodeWebhookUrl: (name: string): string | undefined => {
|
||||||
return getNodeWebhookUrl(name, workflow, node, additionalData);
|
return getNodeWebhookUrl(name, workflow, node, additionalData, mode);
|
||||||
},
|
},
|
||||||
getTimezone: (): string => {
|
getTimezone: (): string => {
|
||||||
return getTimezone(workflow, additionalData);
|
return getTimezone(workflow, additionalData);
|
||||||
|
|
|
@ -301,7 +301,7 @@ class NodeTypesClass implements INodeTypes {
|
||||||
startsWith: (value1: NodeParameterValue, value2: NodeParameterValue) => (value1 as string).startsWith(value2 as string),
|
startsWith: (value1: NodeParameterValue, value2: NodeParameterValue) => (value1 as string).startsWith(value2 as string),
|
||||||
isEmpty: (value1: NodeParameterValue) => [undefined, null, ''].includes(value1 as string),
|
isEmpty: (value1: NodeParameterValue) => [undefined, null, ''].includes(value1 as string),
|
||||||
regex: (value1: NodeParameterValue, value2: NodeParameterValue) => {
|
regex: (value1: NodeParameterValue, value2: NodeParameterValue) => {
|
||||||
const regexMatch = (value2 || '').toString().match(new RegExp('^/(.*?)/([gimy]*)$'));
|
const regexMatch = (value2 || '').toString().match(new RegExp('^/(.*?)/([gimusy]*)$'));
|
||||||
|
|
||||||
let regex: RegExp;
|
let regex: RegExp;
|
||||||
if (!regexMatch) {
|
if (!regexMatch) {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "n8n-editor-ui",
|
"name": "n8n-editor-ui",
|
||||||
"version": "0.74.0",
|
"version": "0.76.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.50.0",
|
"n8n-workflow": "~0.51.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",
|
||||||
|
|
|
@ -147,7 +147,7 @@ export default Vue.extend({
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 80%;
|
width: 80%;
|
||||||
height: 80%;
|
height: 80%;
|
||||||
margin: 6em auto;
|
margin: 3em auto;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
@media (max-height: 720px) {
|
@media (max-height: 720px) {
|
||||||
|
|
|
@ -138,7 +138,7 @@ export default mixins(nodeBase, workflowHelpers).extend({
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.nodeType !== null && this.nodeType.subtitle !== undefined) {
|
if (this.nodeType !== null && this.nodeType.subtitle !== undefined) {
|
||||||
return this.workflow.expression.getSimpleParameterValue(this.data as INode, this.nodeType.subtitle) as string | undefined;
|
return this.workflow.expression.getSimpleParameterValue(this.data as INode, this.nodeType.subtitle, 'internal') as string | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.data.parameters.operation !== undefined) {
|
if (this.data.parameters.operation !== undefined) {
|
||||||
|
|
|
@ -376,7 +376,7 @@ export default mixins(
|
||||||
return returnData;
|
return returnData;
|
||||||
}
|
}
|
||||||
|
|
||||||
const dataProxy = new WorkflowDataProxy(workflow, runExecutionData, runIndex, itemIndex, nodeName, connectionInputData);
|
const dataProxy = new WorkflowDataProxy(workflow, runExecutionData, runIndex, itemIndex, nodeName, connectionInputData, 'manual');
|
||||||
const proxy = dataProxy.getDataProxy();
|
const proxy = dataProxy.getDataProxy();
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
|
|
|
@ -362,7 +362,7 @@ export const workflowHelpers = mixins(
|
||||||
connectionInputData = [];
|
connectionInputData = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
return workflow.expression.getParameterValue(expression, runExecutionData, runIndex, itemIndex, activeNode.name, connectionInputData, true);
|
return workflow.expression.getParameterValue(expression, runExecutionData, runIndex, itemIndex, activeNode.name, connectionInputData, 'manual', true);
|
||||||
},
|
},
|
||||||
|
|
||||||
// Saves the currently loaded workflow to the database.
|
// Saves the currently loaded workflow to the database.
|
||||||
|
|
|
@ -58,7 +58,11 @@ h1, h2, h3, h4, h5, h6 {
|
||||||
-webkit-box-shadow: none;
|
-webkit-box-shadow: none;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
|
|
||||||
@media (max-height: 720px) {
|
@media (max-height: 1050px) {
|
||||||
|
margin: 4em auto !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-height: 930px) {
|
||||||
margin: 1em auto !important;
|
margin: 1em auto !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
33
packages/nodes-base/credentials/DiscourseApi.credentials.ts
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
import {
|
||||||
|
ICredentialType,
|
||||||
|
NodePropertyTypes,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
export class DiscourseApi implements ICredentialType {
|
||||||
|
name = 'discourseApi';
|
||||||
|
displayName = 'Discourse API';
|
||||||
|
documentationUrl = 'discourse';
|
||||||
|
properties = [
|
||||||
|
{
|
||||||
|
displayName: 'URL',
|
||||||
|
name: 'url',
|
||||||
|
required: true,
|
||||||
|
type: 'string' as NodePropertyTypes,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'API Key',
|
||||||
|
name: 'apiKey',
|
||||||
|
required: true,
|
||||||
|
type: 'string' as NodePropertyTypes,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Username',
|
||||||
|
name: 'username',
|
||||||
|
required: true,
|
||||||
|
type: 'string' as NodePropertyTypes,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
|
@ -14,7 +14,7 @@ export class GoogleApi implements ICredentialType {
|
||||||
name: 'email',
|
name: 'email',
|
||||||
type: 'string' as NodePropertyTypes,
|
type: 'string' as NodePropertyTypes,
|
||||||
default: '',
|
default: '',
|
||||||
description: 'The Google Service account similar to user-808@project.iam.gserviceaccount.com.<br />See the <a href="https://github.com/jovotech/learn-jovo/blob/master/tutorials/google-spreadsheet-private-cms/README.md#google-api-console">tutorial</a> on how to create one.',
|
description: 'The Google Service account similar to user-808@project.iam.gserviceaccount.com.',
|
||||||
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -20,5 +20,13 @@ export class HubspotDeveloperApi implements ICredentialType {
|
||||||
type: 'string' as NodePropertyTypes,
|
type: 'string' as NodePropertyTypes,
|
||||||
default: '',
|
default: '',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
displayName: 'App ID',
|
||||||
|
name: 'appId',
|
||||||
|
type: 'string' as NodePropertyTypes,
|
||||||
|
required: true,
|
||||||
|
default: '',
|
||||||
|
description: 'The App ID',
|
||||||
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,5 +42,12 @@ export class MySql implements ICredentialType {
|
||||||
type: 'number' as NodePropertyTypes,
|
type: 'number' as NodePropertyTypes,
|
||||||
default: 3306,
|
default: 3306,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Connect Timeout',
|
||||||
|
name: 'connectTimeout',
|
||||||
|
type: 'number' as NodePropertyTypes,
|
||||||
|
default: 10000,
|
||||||
|
description: 'The milliseconds before a timeout occurs during the initial connection to the MySQL server.',
|
||||||
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
import {
|
||||||
|
ICredentialType,
|
||||||
|
NodePropertyTypes,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
const scopes = [
|
||||||
|
'identity',
|
||||||
|
'edit',
|
||||||
|
'history',
|
||||||
|
'mysubreddits',
|
||||||
|
'read',
|
||||||
|
'save',
|
||||||
|
'submit',
|
||||||
|
];
|
||||||
|
|
||||||
|
// https://github.com/reddit-archive/reddit/wiki/OAuth2
|
||||||
|
|
||||||
|
export class RedditOAuth2Api implements ICredentialType {
|
||||||
|
name = 'redditOAuth2Api';
|
||||||
|
extends = [
|
||||||
|
'oAuth2Api',
|
||||||
|
];
|
||||||
|
displayName = 'Reddit OAuth2 API';
|
||||||
|
documentationUrl = 'reddit';
|
||||||
|
properties = [
|
||||||
|
{
|
||||||
|
displayName: 'Auth URI Query Parameters',
|
||||||
|
name: 'authQueryParameters',
|
||||||
|
type: 'hidden' as NodePropertyTypes,
|
||||||
|
default: 'response_type=code',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Auth URI Query Parameters',
|
||||||
|
name: 'authQueryParameters',
|
||||||
|
type: 'hidden' as NodePropertyTypes,
|
||||||
|
default: 'grant_type=authorization_code',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Auth URI Query Parameters',
|
||||||
|
name: 'authQueryParameters',
|
||||||
|
type: 'hidden' as NodePropertyTypes,
|
||||||
|
default: 'duration=permanent',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Authorization URL',
|
||||||
|
name: 'authUrl',
|
||||||
|
type: 'hidden' as NodePropertyTypes,
|
||||||
|
default: 'https://www.reddit.com/api/v1/authorize',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Access Token URL',
|
||||||
|
name: 'accessTokenUrl',
|
||||||
|
type: 'hidden' as NodePropertyTypes,
|
||||||
|
default: 'https://www.reddit.com/api/v1/access_token',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Scope',
|
||||||
|
name: 'scope',
|
||||||
|
type: 'hidden' as NodePropertyTypes,
|
||||||
|
default: scopes.join(' '),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Authentication',
|
||||||
|
name: 'authentication',
|
||||||
|
type: 'hidden' as NodePropertyTypes,
|
||||||
|
default: 'header',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
|
@ -6,6 +6,7 @@ import {
|
||||||
export class SecurityScorecardApi implements ICredentialType {
|
export class SecurityScorecardApi implements ICredentialType {
|
||||||
name = 'securityScorecardApi';
|
name = 'securityScorecardApi';
|
||||||
displayName = 'SecurityScorecard API';
|
displayName = 'SecurityScorecard API';
|
||||||
|
documentationUrl = 'securityScorecard';
|
||||||
properties = [
|
properties = [
|
||||||
{
|
{
|
||||||
displayName: 'API Key',
|
displayName: 'API Key',
|
||||||
|
|
19
packages/nodes-base/credentials/TapfiliateApi.credentials.ts
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
import {
|
||||||
|
ICredentialType,
|
||||||
|
NodePropertyTypes,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
export class TapfiliateApi implements ICredentialType {
|
||||||
|
name = 'tapfiliateApi';
|
||||||
|
displayName = 'Tapfiliate API';
|
||||||
|
documentationUrl = 'tapfiliate';
|
||||||
|
properties = [
|
||||||
|
{
|
||||||
|
displayName: 'API Key',
|
||||||
|
name: 'apiKey',
|
||||||
|
required: true,
|
||||||
|
type: 'string' as NodePropertyTypes,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
87
packages/nodes-base/credentials/TimescaleDb.credentials.ts
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
import {
|
||||||
|
ICredentialType,
|
||||||
|
NodePropertyTypes,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
export class TimescaleDb implements ICredentialType {
|
||||||
|
name = 'timescaleDb';
|
||||||
|
displayName = 'TimescaleDB';
|
||||||
|
documentationUrl = 'timescaleDb';
|
||||||
|
properties = [
|
||||||
|
{
|
||||||
|
displayName: 'Host',
|
||||||
|
name: 'host',
|
||||||
|
type: 'string' as NodePropertyTypes,
|
||||||
|
default: 'localhost',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Database',
|
||||||
|
name: 'database',
|
||||||
|
type: 'string' as NodePropertyTypes,
|
||||||
|
default: 'postgres',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'User',
|
||||||
|
name: 'user',
|
||||||
|
type: 'string' as NodePropertyTypes,
|
||||||
|
default: 'postgres',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Password',
|
||||||
|
name: 'password',
|
||||||
|
type: 'string' as NodePropertyTypes,
|
||||||
|
typeOptions: {
|
||||||
|
password: true,
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Ignore SSL Issues',
|
||||||
|
name: 'allowUnauthorizedCerts',
|
||||||
|
type: 'boolean' as NodePropertyTypes,
|
||||||
|
default: false,
|
||||||
|
description: 'Connect even if SSL certificate validation is not possible.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'SSL',
|
||||||
|
name: 'ssl',
|
||||||
|
type: 'options' as NodePropertyTypes,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
allowUnauthorizedCerts: [
|
||||||
|
false,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'disable',
|
||||||
|
value: 'disable',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'allow',
|
||||||
|
value: 'allow',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'require',
|
||||||
|
value: 'require',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'verify (not implemented)',
|
||||||
|
value: 'verify',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'verify-full (not implemented)',
|
||||||
|
value: 'verify-full',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
default: 'disable',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Port',
|
||||||
|
name: 'port',
|
||||||
|
type: 'number' as NodePropertyTypes,
|
||||||
|
default: 5432,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
|
@ -9,7 +9,6 @@ const scopes = [
|
||||||
'forms:read',
|
'forms:read',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
export class TypeformOAuth2Api implements ICredentialType {
|
export class TypeformOAuth2Api implements ICredentialType {
|
||||||
name = 'typeformOAuth2Api';
|
name = 'typeformOAuth2Api';
|
||||||
extends = [
|
extends = [
|
||||||
|
@ -36,7 +35,7 @@ export class TypeformOAuth2Api implements ICredentialType {
|
||||||
displayName: 'Scope',
|
displayName: 'Scope',
|
||||||
name: 'scope',
|
name: 'scope',
|
||||||
type: 'hidden' as NodePropertyTypes,
|
type: 'hidden' as NodePropertyTypes,
|
||||||
default: scopes.join(','),
|
default: scopes.join(' '),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Auth URI Query Parameters',
|
displayName: 'Auth URI Query Parameters',
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import {
|
import {
|
||||||
IExecuteFunctions,
|
IExecuteFunctions,
|
||||||
} from 'n8n-core';
|
} from 'n8n-core';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
IDataObject,
|
IDataObject,
|
||||||
ILoadOptionsFunctions,
|
ILoadOptionsFunctions,
|
||||||
|
@ -9,27 +10,40 @@ import {
|
||||||
INodeType,
|
INodeType,
|
||||||
INodeTypeDescription,
|
INodeTypeDescription,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
affinityApiRequest,
|
affinityApiRequest,
|
||||||
affinityApiRequestAllItems,
|
affinityApiRequestAllItems,
|
||||||
} from './GenericFunctions';
|
} from './GenericFunctions';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
organizationFields,
|
organizationFields,
|
||||||
organizationOperations,
|
organizationOperations,
|
||||||
} from './OrganizationDescription';
|
} from './OrganizationDescription';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
personFields,
|
personFields,
|
||||||
personOperations,
|
personOperations,
|
||||||
} from './PersonDescription';
|
} from './PersonDescription';
|
||||||
|
|
||||||
|
import {
|
||||||
|
listFields,
|
||||||
|
listOperations,
|
||||||
|
} from './ListDescription';
|
||||||
|
|
||||||
|
import {
|
||||||
|
listEntryFields,
|
||||||
|
listEntryOperations,
|
||||||
|
} from './ListEntryDescription';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
IOrganization,
|
IOrganization,
|
||||||
} from './OrganizationInterface';
|
} from './OrganizationInterface';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
IPerson,
|
IPerson,
|
||||||
} from './PersonInterface';
|
} from './PersonInterface';
|
||||||
|
|
||||||
import { snakeCase } from 'change-case';
|
|
||||||
|
|
||||||
export class Affinity implements INodeType {
|
export class Affinity implements INodeType {
|
||||||
description: INodeTypeDescription = {
|
description: INodeTypeDescription = {
|
||||||
displayName: 'Affinity',
|
displayName: 'Affinity',
|
||||||
|
@ -57,6 +71,14 @@ export class Affinity implements INodeType {
|
||||||
name: 'resource',
|
name: 'resource',
|
||||||
type: 'options',
|
type: 'options',
|
||||||
options: [
|
options: [
|
||||||
|
{
|
||||||
|
name: 'List',
|
||||||
|
value: 'list',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'List Entry',
|
||||||
|
value: 'listEntry',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'Organization',
|
name: 'Organization',
|
||||||
value: 'organization',
|
value: 'organization',
|
||||||
|
@ -69,6 +91,10 @@ export class Affinity implements INodeType {
|
||||||
default: 'organization',
|
default: 'organization',
|
||||||
description: 'Resource to consume.',
|
description: 'Resource to consume.',
|
||||||
},
|
},
|
||||||
|
...listOperations,
|
||||||
|
...listFields,
|
||||||
|
...listEntryOperations,
|
||||||
|
...listEntryFields,
|
||||||
...organizationOperations,
|
...organizationOperations,
|
||||||
...organizationFields,
|
...organizationFields,
|
||||||
...personOperations,
|
...personOperations,
|
||||||
|
@ -101,7 +127,7 @@ export class Affinity implements INodeType {
|
||||||
for (const person of persons) {
|
for (const person of persons) {
|
||||||
let personName = `${person.first_name} ${person.last_name}`;
|
let personName = `${person.first_name} ${person.last_name}`;
|
||||||
if (person.primary_email !== null) {
|
if (person.primary_email !== null) {
|
||||||
personName+= ` (${person.primary_email})`;
|
personName += ` (${person.primary_email})`;
|
||||||
}
|
}
|
||||||
const personId = person.id;
|
const personId = person.id;
|
||||||
returnData.push({
|
returnData.push({
|
||||||
|
@ -111,6 +137,19 @@ export class Affinity implements INodeType {
|
||||||
}
|
}
|
||||||
return returnData;
|
return returnData;
|
||||||
},
|
},
|
||||||
|
// Get all the available lists to display them to user so that he can
|
||||||
|
// select them easily
|
||||||
|
async getLists(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||||
|
const returnData: INodePropertyOptions[] = [];
|
||||||
|
const lists = await affinityApiRequest.call(this, 'GET', `/lists`);
|
||||||
|
for (const list of lists) {
|
||||||
|
returnData.push({
|
||||||
|
name: list.name,
|
||||||
|
value: list.id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return returnData;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -123,6 +162,59 @@ export class Affinity implements INodeType {
|
||||||
const resource = this.getNodeParameter('resource', 0) as string;
|
const resource = this.getNodeParameter('resource', 0) as string;
|
||||||
const operation = this.getNodeParameter('operation', 0) as string;
|
const operation = this.getNodeParameter('operation', 0) as string;
|
||||||
for (let i = 0; i < length; i++) {
|
for (let i = 0; i < length; i++) {
|
||||||
|
if (resource === 'list') {
|
||||||
|
//https://api-docs.affinity.co/#get-a-specific-list
|
||||||
|
if (operation === 'get') {
|
||||||
|
const listId = this.getNodeParameter('listId', i) as string;
|
||||||
|
responseData = await affinityApiRequest.call(this, 'GET', `/lists/${listId}`, {}, qs);
|
||||||
|
}
|
||||||
|
//https://api-docs.affinity.co/#get-all-lists
|
||||||
|
if (operation === 'getAll') {
|
||||||
|
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
|
||||||
|
responseData = await affinityApiRequest.call(this, 'GET', `/lists`, {}, qs);
|
||||||
|
if (returnAll === false) {
|
||||||
|
const limit = this.getNodeParameter('limit', i) as number;
|
||||||
|
responseData = responseData.splice(0, limit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (resource === 'listEntry') {
|
||||||
|
//https://api-docs.affinity.co/#create-a-new-list-entry
|
||||||
|
if (operation === 'create') {
|
||||||
|
const listId = this.getNodeParameter('listId', i) as string;
|
||||||
|
const entityId = this.getNodeParameter('entityId', i) as string;
|
||||||
|
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
|
||||||
|
const body: IDataObject = {
|
||||||
|
entity_id: parseInt(entityId, 10),
|
||||||
|
};
|
||||||
|
Object.assign(body, additionalFields);
|
||||||
|
responseData = await affinityApiRequest.call(this, 'POST', `/lists/${listId}/list-entries`, body);
|
||||||
|
}
|
||||||
|
//https://api-docs.affinity.co/#get-a-specific-list-entry
|
||||||
|
if (operation === 'get') {
|
||||||
|
const listId = this.getNodeParameter('listId', i) as string;
|
||||||
|
const listEntryId = this.getNodeParameter('listEntryId', i) as string;
|
||||||
|
responseData = await affinityApiRequest.call(this, 'GET', `/lists/${listId}/list-entries/${listEntryId}`, {}, qs);
|
||||||
|
}
|
||||||
|
//https://api-docs.affinity.co/#get-all-list-entries
|
||||||
|
if (operation === 'getAll') {
|
||||||
|
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
|
||||||
|
const listId = this.getNodeParameter('listId', i) as string;
|
||||||
|
if (returnAll === true) {
|
||||||
|
responseData = await affinityApiRequestAllItems.call(this, 'list_entries', 'GET', `/lists/${listId}/list-entries`, {}, qs);
|
||||||
|
} else {
|
||||||
|
qs.page_size = this.getNodeParameter('limit', i) as number;
|
||||||
|
responseData = await affinityApiRequest.call(this, 'GET', `/lists/${listId}/list-entries`, {}, qs);
|
||||||
|
responseData = responseData.list_entries;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//https://api-docs.affinity.co/#delete-a-specific-list-entry
|
||||||
|
if (operation === 'delete') {
|
||||||
|
const listId = this.getNodeParameter('listId', i) as string;
|
||||||
|
const listEntryId = this.getNodeParameter('listEntryId', i) as string;
|
||||||
|
responseData = await affinityApiRequest.call(this, 'DELETE', `/lists/${listId}/list-entries/${listEntryId}`, {}, qs);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (resource === 'person') {
|
if (resource === 'person') {
|
||||||
//https://api-docs.affinity.co/#create-a-new-person
|
//https://api-docs.affinity.co/#create-a-new-person
|
||||||
if (operation === 'create') {
|
if (operation === 'create') {
|
||||||
|
@ -166,7 +258,7 @@ export class Affinity implements INodeType {
|
||||||
if (options.withInteractionDates) {
|
if (options.withInteractionDates) {
|
||||||
qs.with_interaction_dates = options.withInteractionDates as boolean;
|
qs.with_interaction_dates = options.withInteractionDates as boolean;
|
||||||
}
|
}
|
||||||
responseData = await affinityApiRequest.call(this,'GET', `/persons/${personId}`, {}, qs);
|
responseData = await affinityApiRequest.call(this, 'GET', `/persons/${personId}`, {}, qs);
|
||||||
}
|
}
|
||||||
//https://api-docs.affinity.co/#search-for-persons
|
//https://api-docs.affinity.co/#search-for-persons
|
||||||
if (operation === 'getAll') {
|
if (operation === 'getAll') {
|
||||||
|
@ -230,7 +322,7 @@ export class Affinity implements INodeType {
|
||||||
if (options.withInteractionDates) {
|
if (options.withInteractionDates) {
|
||||||
qs.with_interaction_dates = options.withInteractionDates as boolean;
|
qs.with_interaction_dates = options.withInteractionDates as boolean;
|
||||||
}
|
}
|
||||||
responseData = await affinityApiRequest.call(this,'GET', `/organizations/${organizationId}`, {}, qs);
|
responseData = await affinityApiRequest.call(this, 'GET', `/organizations/${organizationId}`, {}, qs);
|
||||||
}
|
}
|
||||||
//https://api-docs.affinity.co/#search-for-organizations
|
//https://api-docs.affinity.co/#search-for-organizations
|
||||||
if (operation === 'getAll') {
|
if (operation === 'getAll') {
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
import { OptionsWithUri } from 'request';
|
import {
|
||||||
|
OptionsWithUri,
|
||||||
|
} from 'request';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
BINARY_ENCODING,
|
BINARY_ENCODING,
|
||||||
|
@ -6,7 +8,11 @@ import {
|
||||||
ILoadOptionsFunctions,
|
ILoadOptionsFunctions,
|
||||||
} from 'n8n-core';
|
} from 'n8n-core';
|
||||||
|
|
||||||
import { IDataObject, IHookFunctions, IWebhookFunctions } from 'n8n-workflow';
|
import {
|
||||||
|
IDataObject,
|
||||||
|
IHookFunctions,
|
||||||
|
IWebhookFunctions,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
export async function affinityApiRequest(this: IExecuteFunctions | IWebhookFunctions | IHookFunctions | ILoadOptionsFunctions, method: string, resource: string, body: any = {}, query: IDataObject = {}, uri?: string, option: IDataObject = {}): Promise<any> { // tslint:disable-line:no-any
|
export async function affinityApiRequest(this: IExecuteFunctions | IWebhookFunctions | IHookFunctions | ILoadOptionsFunctions, method: string, resource: string, body: any = {}, query: IDataObject = {}, uri?: string, option: IDataObject = {}): Promise<any> { // tslint:disable-line:no-any
|
||||||
|
|
||||||
|
@ -43,7 +49,7 @@ export async function affinityApiRequest(this: IExecuteFunctions | IWebhookFunct
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error.response) {
|
if (error.response) {
|
||||||
const errorMessage = error.response.body.message || error.response.body.description || error.message;
|
const errorMessage = error.response.body.message || error.response.body.description || error.message;
|
||||||
throw new Error(`Affinity error response: ${errorMessage}`);
|
throw new Error(`Affinity error response [${error.statusCode}]: ${errorMessage}`);
|
||||||
}
|
}
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
100
packages/nodes-base/nodes/Affinity/ListDescription.ts
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
import {
|
||||||
|
INodeProperties,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
export const listOperations = [
|
||||||
|
{
|
||||||
|
displayName: 'Operation',
|
||||||
|
name: 'operation',
|
||||||
|
type: 'options',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'list',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Get',
|
||||||
|
value: 'get',
|
||||||
|
description: 'Get a list',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Get All',
|
||||||
|
value: 'getAll',
|
||||||
|
description: 'Get all lists',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
default: 'get',
|
||||||
|
description: 'The operation to perform.',
|
||||||
|
},
|
||||||
|
] as INodeProperties[];
|
||||||
|
|
||||||
|
export const listFields = [
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* list:get */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
{
|
||||||
|
displayName: 'List ID',
|
||||||
|
name: 'listId',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
default: '',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'list',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'get',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
description: 'The unique id of the list object to be retrieved.',
|
||||||
|
},
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* list:getAll */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
{
|
||||||
|
displayName: 'Return All',
|
||||||
|
name: 'returnAll',
|
||||||
|
type: 'boolean',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'list',
|
||||||
|
],
|
||||||
|
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: [
|
||||||
|
'list',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
returnAll: [
|
||||||
|
false,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
typeOptions: {
|
||||||
|
minValue: 1,
|
||||||
|
maxValue: 10,
|
||||||
|
},
|
||||||
|
default: 5,
|
||||||
|
description: 'How many results to return.',
|
||||||
|
},
|
||||||
|
] as INodeProperties[];
|
263
packages/nodes-base/nodes/Affinity/ListEntryDescription.ts
Normal file
|
@ -0,0 +1,263 @@
|
||||||
|
import {
|
||||||
|
INodeProperties,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
export const listEntryOperations = [
|
||||||
|
{
|
||||||
|
displayName: 'Operation',
|
||||||
|
name: 'operation',
|
||||||
|
type: 'options',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'listEntry',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Create',
|
||||||
|
value: 'create',
|
||||||
|
description: 'Create a list entry',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Delete',
|
||||||
|
value: 'delete',
|
||||||
|
description: 'Delete a list entry',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Get',
|
||||||
|
value: 'get',
|
||||||
|
description: 'Get a list entry',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Get All',
|
||||||
|
value: 'getAll',
|
||||||
|
description: 'Get all list entries',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
default: 'create',
|
||||||
|
description: 'The operation to perform.',
|
||||||
|
},
|
||||||
|
] as INodeProperties[];
|
||||||
|
|
||||||
|
export const listEntryFields = [
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* listEntry:create */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
{
|
||||||
|
displayName: 'List ID',
|
||||||
|
name: 'listId',
|
||||||
|
type: 'options',
|
||||||
|
required: true,
|
||||||
|
typeOptions: {
|
||||||
|
loadOptionsMethod: 'getLists',
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'listEntry',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'create',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
description: 'The unique id of the list whose list entries are to be retrieved.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Entity ID',
|
||||||
|
name: 'entityId',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
default: '',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'listEntry',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'create',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
description: 'The unique id of the entity (person, organization, or opportunity) to add to this list.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Additional Fields',
|
||||||
|
name: 'additionalFields',
|
||||||
|
type: 'collection',
|
||||||
|
placeholder: 'Add Field',
|
||||||
|
default: {},
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'listEntry',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'create',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
displayName: 'Creator ID',
|
||||||
|
name: 'creator_id',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
description: `The id of a Person resource who should be recorded as adding the entry to the list. <br/>
|
||||||
|
Must be a person who can access Affinity. If not provided the creator defaults to the owner of the API key.`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* listEntry:get */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
{
|
||||||
|
displayName: 'List ID',
|
||||||
|
name: 'listId',
|
||||||
|
type: 'options',
|
||||||
|
required: true,
|
||||||
|
typeOptions: {
|
||||||
|
loadOptionsMethod: 'getLists',
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'listEntry',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'get',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
description: 'The unique id of the list that contains the specified list_entry_id.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'List Entry ID',
|
||||||
|
name: 'listEntryId',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
default: '',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'listEntry',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'get',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
description: 'The unique id of the list entry object to be retrieved.',
|
||||||
|
},
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* listEntry:getAll */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
{
|
||||||
|
displayName: 'List ID',
|
||||||
|
name: 'listId',
|
||||||
|
type: 'options',
|
||||||
|
typeOptions: {
|
||||||
|
loadOptionsMethod: 'getLists',
|
||||||
|
},
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'listEntry',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: 'The unique id of the list whose list entries are to be retrieved.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Return All',
|
||||||
|
name: 'returnAll',
|
||||||
|
type: 'boolean',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'listEntry',
|
||||||
|
],
|
||||||
|
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: [
|
||||||
|
'listEntry',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
returnAll: [
|
||||||
|
false,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
typeOptions: {
|
||||||
|
minValue: 1,
|
||||||
|
maxValue: 10,
|
||||||
|
},
|
||||||
|
default: 5,
|
||||||
|
description: 'How many results to return.',
|
||||||
|
},
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* listEntry:delete */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
{
|
||||||
|
displayName: 'List ID',
|
||||||
|
name: 'listId',
|
||||||
|
type: 'options',
|
||||||
|
typeOptions: {
|
||||||
|
loadOptionsMethod: 'getLists',
|
||||||
|
},
|
||||||
|
required: true,
|
||||||
|
default: '',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'listEntry',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'delete',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
description: 'The unique id of the list that contains the specified list_entry_id.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'List Entry ID',
|
||||||
|
name: 'listEntryId',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
default: '',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'listEntry',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'delete',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
description: 'The unique id of the list entry object to be deleted.',
|
||||||
|
},
|
||||||
|
] as INodeProperties[];
|
|
@ -1,4 +1,6 @@
|
||||||
import { INodeProperties } from 'n8n-workflow';
|
import {
|
||||||
|
INodeProperties,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
export const organizationOperations = [
|
export const organizationOperations = [
|
||||||
{
|
{
|
||||||
|
@ -46,9 +48,9 @@ export const organizationOperations = [
|
||||||
|
|
||||||
export const organizationFields = [
|
export const organizationFields = [
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
/* organization:create */
|
/* organization:create */
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
{
|
{
|
||||||
displayName: 'Name',
|
displayName: 'Name',
|
||||||
name: 'name',
|
name: 'name',
|
||||||
|
@ -114,9 +116,9 @@ export const organizationFields = [
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
/* organization:update */
|
/* organization:update */
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
{
|
{
|
||||||
displayName: 'Organization ID',
|
displayName: 'Organization ID',
|
||||||
name: 'organizationId',
|
name: 'organizationId',
|
||||||
|
@ -178,9 +180,9 @@ export const organizationFields = [
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
/* organization:get */
|
/* organization:get */
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
{
|
{
|
||||||
displayName: 'Organization ID',
|
displayName: 'Organization ID',
|
||||||
name: 'organizationId',
|
name: 'organizationId',
|
||||||
|
@ -225,9 +227,9 @@ export const organizationFields = [
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
/* organization:getAll */
|
/* organization:getAll */
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
{
|
{
|
||||||
displayName: 'Return All',
|
displayName: 'Return All',
|
||||||
name: 'returnAll',
|
name: 'returnAll',
|
||||||
|
@ -302,9 +304,9 @@ export const organizationFields = [
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
/* organization:delete */
|
/* organization:delete */
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
{
|
{
|
||||||
displayName: 'Organization ID',
|
displayName: 'Organization ID',
|
||||||
name: 'organizationId',
|
name: 'organizationId',
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
import { INodeProperties } from 'n8n-workflow';
|
import {
|
||||||
|
INodeProperties,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
export const personOperations = [
|
export const personOperations = [
|
||||||
{
|
{
|
||||||
|
@ -46,9 +48,9 @@ export const personOperations = [
|
||||||
|
|
||||||
export const personFields = [
|
export const personFields = [
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
/* person:create */
|
/* person:create */
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
{
|
{
|
||||||
displayName: 'Fist Name',
|
displayName: 'Fist Name',
|
||||||
name: 'firstName',
|
name: 'firstName',
|
||||||
|
@ -136,9 +138,9 @@ export const personFields = [
|
||||||
placeholder: 'info@example.com',
|
placeholder: 'info@example.com',
|
||||||
default: [],
|
default: [],
|
||||||
},
|
},
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
/* person:update */
|
/* person:update */
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
{
|
{
|
||||||
displayName: 'Person ID',
|
displayName: 'Person ID',
|
||||||
name: 'personId',
|
name: 'personId',
|
||||||
|
@ -222,9 +224,9 @@ export const personFields = [
|
||||||
placeholder: 'info@example.com',
|
placeholder: 'info@example.com',
|
||||||
default: [],
|
default: [],
|
||||||
},
|
},
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
/* person:get */
|
/* person:get */
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
{
|
{
|
||||||
displayName: 'Person ID',
|
displayName: 'Person ID',
|
||||||
name: 'personId',
|
name: 'personId',
|
||||||
|
@ -269,9 +271,9 @@ export const personFields = [
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
/* person:getAll */
|
/* person:getAll */
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
{
|
{
|
||||||
displayName: 'Return All',
|
displayName: 'Return All',
|
||||||
name: 'returnAll',
|
name: 'returnAll',
|
||||||
|
@ -346,9 +348,9 @@ export const personFields = [
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
/* person:delete */
|
/* person:delete */
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
{
|
{
|
||||||
displayName: 'Person ID',
|
displayName: 'Person ID',
|
||||||
name: 'personId',
|
name: 'personId',
|
||||||
|
|
|
@ -11,6 +11,7 @@ import {
|
||||||
|
|
||||||
import {
|
import {
|
||||||
apiRequestAllItems,
|
apiRequestAllItems,
|
||||||
|
downloadRecordAttachments,
|
||||||
} from './GenericFunctions';
|
} from './GenericFunctions';
|
||||||
|
|
||||||
import * as moment from 'moment';
|
import * as moment from 'moment';
|
||||||
|
@ -64,6 +65,28 @@ export class AirtableTrigger implements INodeType {
|
||||||
because without this field trigger will not work correctly.`,
|
because without this field trigger will not work correctly.`,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Download Attachments',
|
||||||
|
name: 'downloadAttachments',
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
description: `When set to true the attachment fields define in 'Download Fields' will be downloaded.`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Download Fields',
|
||||||
|
name: 'downloadFieldNames',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
downloadAttachments: [
|
||||||
|
true,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: `Name of the fields of type 'attachment' that should be downloaded. Multiple ones can be defined separated by comma. Case sensitive.`,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Additional Fields',
|
displayName: 'Additional Fields',
|
||||||
name: 'additionalFields',
|
name: 'additionalFields',
|
||||||
|
@ -100,6 +123,7 @@ export class AirtableTrigger implements INodeType {
|
||||||
};
|
};
|
||||||
|
|
||||||
async poll(this: IPollFunctions): Promise<INodeExecutionData[][] | null> {
|
async poll(this: IPollFunctions): Promise<INodeExecutionData[][] | null> {
|
||||||
|
const downloadAttachments = this.getNodeParameter('downloadAttachments', 0) as boolean;
|
||||||
|
|
||||||
const webhookData = this.getWorkflowStaticData('node');
|
const webhookData = this.getWorkflowStaticData('node');
|
||||||
|
|
||||||
|
@ -149,6 +173,12 @@ export class AirtableTrigger implements INodeType {
|
||||||
throw new Error(`The Field "${triggerField}" does not exist.`);
|
throw new Error(`The Field "${triggerField}" does not exist.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (downloadAttachments === true) {
|
||||||
|
const downloadFieldNames = (this.getNodeParameter('downloadFieldNames', 0) as string).split(',');
|
||||||
|
const data = await downloadRecordAttachments.call(this, records, downloadFieldNames);
|
||||||
|
return [data];
|
||||||
|
}
|
||||||
|
|
||||||
return [this.helpers.returnJsonArray(records)];
|
return [this.helpers.returnJsonArray(records)];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -130,7 +130,7 @@ export async function apiRequestAllItems(this: IHookFunctions | IExecuteFunction
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function downloadRecordAttachments(this: IExecuteFunctions, records: IRecord[], fieldNames: string[]): Promise<INodeExecutionData[]> {
|
export async function downloadRecordAttachments(this: IExecuteFunctions | IPollFunctions, records: IRecord[], fieldNames: string[]): Promise<INodeExecutionData[]> {
|
||||||
const elements: INodeExecutionData[] = [];
|
const elements: INodeExecutionData[] = [];
|
||||||
for (const record of records) {
|
for (const record of records) {
|
||||||
const element: INodeExecutionData = { json: {}, binary: {} };
|
const element: INodeExecutionData = { json: {}, binary: {} };
|
||||||
|
|
|
@ -106,6 +106,10 @@ export class Asana implements INodeType {
|
||||||
name: 'Task Tag',
|
name: 'Task Tag',
|
||||||
value: 'taskTag',
|
value: 'taskTag',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'Task Project',
|
||||||
|
value: 'taskProject',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'User',
|
name: 'User',
|
||||||
value: 'user',
|
value: 'user',
|
||||||
|
@ -921,6 +925,16 @@ export class Asana implements INodeType {
|
||||||
default: '',
|
default: '',
|
||||||
description: 'The task notes',
|
description: 'The task notes',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Project IDs',
|
||||||
|
name: 'projects',
|
||||||
|
type: 'multiOptions',
|
||||||
|
typeOptions: {
|
||||||
|
loadOptionsMethod: 'getProjects',
|
||||||
|
},
|
||||||
|
default: [],
|
||||||
|
description: 'The project to filter tasks on.',
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -1093,6 +1107,161 @@ export class Asana implements INodeType {
|
||||||
description: 'The ID of the comment to be removed',
|
description: 'The ID of the comment to be removed',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// ----------------------------------
|
||||||
|
// taskProject
|
||||||
|
// ----------------------------------
|
||||||
|
{
|
||||||
|
displayName: 'Operation',
|
||||||
|
name: 'operation',
|
||||||
|
type: 'options',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'taskProject',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Add',
|
||||||
|
value: 'add',
|
||||||
|
description: 'Add a task to a project',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Remove',
|
||||||
|
value: 'remove',
|
||||||
|
description: 'Remove a task from a project',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
default: 'add',
|
||||||
|
description: 'The operation to perform.',
|
||||||
|
},
|
||||||
|
// ----------------------------------
|
||||||
|
// taskProject:add
|
||||||
|
// ----------------------------------
|
||||||
|
{
|
||||||
|
displayName: 'Task ID',
|
||||||
|
name: 'id',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
operation: [
|
||||||
|
'add',
|
||||||
|
],
|
||||||
|
resource: [
|
||||||
|
'taskProject',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
description: 'The ID of the task to add the project to',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Project ID',
|
||||||
|
name: 'project',
|
||||||
|
type: 'options',
|
||||||
|
typeOptions: {
|
||||||
|
loadOptionsMethod: 'getProjects',
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
operation: [
|
||||||
|
'add',
|
||||||
|
],
|
||||||
|
resource: [
|
||||||
|
'taskProject',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
description: 'The project where the task will be added',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Additional Fields',
|
||||||
|
name: 'additionalFields',
|
||||||
|
type: 'collection',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'taskProject',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'add',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: {},
|
||||||
|
description: 'Other properties to set',
|
||||||
|
placeholder: 'Add Field',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
displayName: 'Insert After',
|
||||||
|
name: 'insert_after',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
description: 'A task in the project to insert the task after, or null to insert at the beginning of the list.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Insert Before',
|
||||||
|
name: 'insert_before',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
description: 'A task in the project to insert the task before, or null to insert at the end of the list.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Section',
|
||||||
|
name: 'section',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
description: 'A section in the project to insert the task into. The task will be inserted at the bottom of the section.',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
// ----------------------------------
|
||||||
|
// taskProject:remove
|
||||||
|
// ----------------------------------
|
||||||
|
{
|
||||||
|
displayName: 'Task ID',
|
||||||
|
name: 'id',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
operation: [
|
||||||
|
'remove',
|
||||||
|
],
|
||||||
|
resource: [
|
||||||
|
'taskProject',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
description: 'The ID of the task to add the project to',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Project ID',
|
||||||
|
name: 'project',
|
||||||
|
type: 'options',
|
||||||
|
typeOptions: {
|
||||||
|
loadOptionsMethod: 'getProjects',
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
operation: [
|
||||||
|
'remove',
|
||||||
|
],
|
||||||
|
resource: [
|
||||||
|
'taskProject',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
description: 'The project where the task will be removed from',
|
||||||
|
},
|
||||||
// ----------------------------------
|
// ----------------------------------
|
||||||
// taskTag
|
// taskTag
|
||||||
// ----------------------------------
|
// ----------------------------------
|
||||||
|
@ -1952,7 +2121,49 @@ export class Asana implements INodeType {
|
||||||
responseData = { success: true };
|
responseData = { success: true };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (resource === 'taskProject') {
|
||||||
|
if (operation === 'add') {
|
||||||
|
|
||||||
|
// ----------------------------------
|
||||||
|
// taskProject:add
|
||||||
|
// ----------------------------------
|
||||||
|
|
||||||
|
const taskId = this.getNodeParameter('id', i) as string;
|
||||||
|
|
||||||
|
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
|
||||||
|
|
||||||
|
requestMethod = 'POST';
|
||||||
|
|
||||||
|
endpoint = `/tasks/${taskId}/addProject`;
|
||||||
|
|
||||||
|
body.project = this.getNodeParameter('project', i) as string;
|
||||||
|
|
||||||
|
Object.assign(body, additionalFields);
|
||||||
|
|
||||||
|
responseData = await asanaApiRequest.call(this, requestMethod, endpoint, body, qs);
|
||||||
|
|
||||||
|
responseData = { success: true };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (operation === 'remove') {
|
||||||
|
|
||||||
|
// ----------------------------------
|
||||||
|
// taskProject:remove
|
||||||
|
// ----------------------------------
|
||||||
|
|
||||||
|
const taskId = this.getNodeParameter('id', i) as string;
|
||||||
|
|
||||||
|
requestMethod = 'POST';
|
||||||
|
|
||||||
|
endpoint = `/tasks/${taskId}/removeProject`;
|
||||||
|
|
||||||
|
body.project = this.getNodeParameter('project', i) as string;
|
||||||
|
|
||||||
|
responseData = await asanaApiRequest.call(this, requestMethod, endpoint, body, qs);
|
||||||
|
|
||||||
|
responseData = { success: true };
|
||||||
|
}
|
||||||
|
}
|
||||||
if (resource === 'user') {
|
if (resource === 'user') {
|
||||||
if (operation === 'get') {
|
if (operation === 'get') {
|
||||||
// ----------------------------------
|
// ----------------------------------
|
||||||
|
|
|
@ -76,7 +76,7 @@ export async function asanaApiRequest(this: IHookFunctions | IExecuteFunctions |
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function asanaApiRequestAllItems(this: IExecuteFunctions | ILoadOptionsFunctions ,method: string, endpoint: string, body: any = {}, query: IDataObject = {}): Promise<any> { // tslint:disable-line:no-any
|
export async function asanaApiRequestAllItems(this: IExecuteFunctions | ILoadOptionsFunctions, method: string, endpoint: string, body: any = {}, query: IDataObject = {}): Promise<any> { // tslint:disable-line:no-any
|
||||||
|
|
||||||
const returnData: IDataObject[] = [];
|
const returnData: IDataObject[] = [];
|
||||||
|
|
||||||
|
@ -95,12 +95,12 @@ export async function asanaApiRequestAllItems(this: IExecuteFunctions | ILoadOpt
|
||||||
return returnData;
|
return returnData;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getWorkspaces(this: ILoadOptionsFunctions): Promise < INodePropertyOptions[] > {
|
export async function getWorkspaces(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||||
const endpoint = '/workspaces';
|
const endpoint = '/workspaces';
|
||||||
const responseData = await asanaApiRequestAllItems.call(this, 'GET', endpoint, {});
|
const responseData = await asanaApiRequestAllItems.call(this, 'GET', endpoint, {});
|
||||||
|
|
||||||
const returnData: INodePropertyOptions[] = [];
|
const returnData: INodePropertyOptions[] = [];
|
||||||
for(const workspaceData of responseData) {
|
for (const workspaceData of responseData) {
|
||||||
if (workspaceData.resource_type !== 'workspace') {
|
if (workspaceData.resource_type !== 'workspace') {
|
||||||
// Not sure if for some reason also ever other resources
|
// Not sure if for some reason also ever other resources
|
||||||
// get returned but just in case filter them out
|
// get returned but just in case filter them out
|
||||||
|
@ -113,7 +113,7 @@ export async function getWorkspaces(this: ILoadOptionsFunctions): Promise < INod
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
returnData.sort((a, b) => {
|
returnData.sort((a, b) => {
|
||||||
if (a.name < b.name) { return -1; }
|
if (a.name < b.name) { return -1; }
|
||||||
if (a.name > b.name) { return 1; }
|
if (a.name > b.name) { return 1; }
|
||||||
return 0;
|
return 0;
|
||||||
|
|
16
packages/nodes-base/nodes/Compression.node.json
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"node": "n8n-nodes-base.compression",
|
||||||
|
"nodeVersion": "1.0",
|
||||||
|
"codexVersion": "1.0",
|
||||||
|
"categories": [
|
||||||
|
"Core Nodes",
|
||||||
|
"Data & Storage"
|
||||||
|
],
|
||||||
|
"resources": {
|
||||||
|
"primaryDocumentation": [
|
||||||
|
{
|
||||||
|
"url": "https://docs.n8n.io/nodes/n8n-nodes-base.compression/"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
216
packages/nodes-base/nodes/Discourse/CategoryDescription.ts
Normal file
|
@ -0,0 +1,216 @@
|
||||||
|
import {
|
||||||
|
INodeProperties,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
export const categoryOperations = [
|
||||||
|
{
|
||||||
|
displayName: 'Operation',
|
||||||
|
name: 'operation',
|
||||||
|
type: 'options',
|
||||||
|
description: 'Choose an operation',
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'category',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Create',
|
||||||
|
value: 'create',
|
||||||
|
description: 'Create a category',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Get All',
|
||||||
|
value: 'getAll',
|
||||||
|
description: 'Get all categories',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Update',
|
||||||
|
value: 'update',
|
||||||
|
description: 'Update a category',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
default: 'create',
|
||||||
|
},
|
||||||
|
] as INodeProperties[];
|
||||||
|
|
||||||
|
export const categoryFields: INodeProperties[] = [
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* category:create */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
{
|
||||||
|
displayName: 'Name',
|
||||||
|
name: 'name',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'category',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'create',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: 'Name of the category.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Color',
|
||||||
|
name: 'color',
|
||||||
|
type: 'color',
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'category',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'create',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '0000FF',
|
||||||
|
description: 'Color of the category.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Text Color',
|
||||||
|
name: 'textColor',
|
||||||
|
type: 'color',
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'category',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'create',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '0000FF',
|
||||||
|
description: 'Text color of the category.',
|
||||||
|
},
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* category:getAll */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
{
|
||||||
|
displayName: 'Return All',
|
||||||
|
name: 'returnAll',
|
||||||
|
type: 'boolean',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'category',
|
||||||
|
],
|
||||||
|
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: [
|
||||||
|
'category',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
returnAll: [
|
||||||
|
false,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
typeOptions: {
|
||||||
|
minValue: 1,
|
||||||
|
maxValue: 100,
|
||||||
|
},
|
||||||
|
default: 50,
|
||||||
|
description: 'How many results to return.',
|
||||||
|
},
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* category:update */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
{
|
||||||
|
displayName: 'Category ID',
|
||||||
|
name: 'categoryId',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'category',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'update',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: 'ID of the category.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Name',
|
||||||
|
name: 'name',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'category',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'update',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: 'New name of the category.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Update Fields',
|
||||||
|
name: 'updateFields',
|
||||||
|
type: 'collection',
|
||||||
|
placeholder: 'Add Field',
|
||||||
|
default: {},
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'category',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'update',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
displayName: 'Color',
|
||||||
|
name: 'color',
|
||||||
|
type: 'color',
|
||||||
|
default: '0000FF',
|
||||||
|
description: 'Color of the category',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Text Color',
|
||||||
|
name: 'textColor',
|
||||||
|
type: 'color',
|
||||||
|
default: '0000FF',
|
||||||
|
description: 'Text color of the category',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
20
packages/nodes-base/nodes/Discourse/Discourse.node.json
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"node": "n8n-nodes-base.discourse",
|
||||||
|
"nodeVersion": "1.0",
|
||||||
|
"codexVersion": "1.0",
|
||||||
|
"categories": [
|
||||||
|
"Communication"
|
||||||
|
],
|
||||||
|
"resources": {
|
||||||
|
"credentialDocumentation": [
|
||||||
|
{
|
||||||
|
"url": "https://docs.n8n.io/credentials/discourse"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryDocumentation": [
|
||||||
|
{
|
||||||
|
"url": "https://docs.n8n.io/nodes/n8n-nodes-base.discourse/"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
500
packages/nodes-base/nodes/Discourse/Discourse.node.ts
Normal file
|
@ -0,0 +1,500 @@
|
||||||
|
import {
|
||||||
|
IExecuteFunctions,
|
||||||
|
} from 'n8n-core';
|
||||||
|
|
||||||
|
import {
|
||||||
|
IDataObject,
|
||||||
|
ILoadOptionsFunctions,
|
||||||
|
INodeExecutionData,
|
||||||
|
INodePropertyOptions,
|
||||||
|
INodeType,
|
||||||
|
INodeTypeDescription,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
import {
|
||||||
|
discourseApiRequest,
|
||||||
|
} from './GenericFunctions';
|
||||||
|
|
||||||
|
import {
|
||||||
|
postFields,
|
||||||
|
postOperations,
|
||||||
|
} from './PostDescription';
|
||||||
|
|
||||||
|
import {
|
||||||
|
categoryFields,
|
||||||
|
categoryOperations,
|
||||||
|
} from './CategoryDescription';
|
||||||
|
|
||||||
|
import {
|
||||||
|
groupFields,
|
||||||
|
groupOperations,
|
||||||
|
} from './GroupDescription';
|
||||||
|
|
||||||
|
// import {
|
||||||
|
// searchFields,
|
||||||
|
// searchOperations,
|
||||||
|
// } from './SearchDescription';
|
||||||
|
|
||||||
|
import {
|
||||||
|
userFields,
|
||||||
|
userOperations,
|
||||||
|
} from './UserDescription';
|
||||||
|
|
||||||
|
import {
|
||||||
|
userGroupFields,
|
||||||
|
userGroupOperations,
|
||||||
|
} from './UserGroupDescription';
|
||||||
|
|
||||||
|
//import * as moment from 'moment';
|
||||||
|
|
||||||
|
export class Discourse implements INodeType {
|
||||||
|
description: INodeTypeDescription = {
|
||||||
|
displayName: 'Discourse',
|
||||||
|
name: 'discourse',
|
||||||
|
icon: 'file:discourse.svg',
|
||||||
|
group: ['input'],
|
||||||
|
version: 1,
|
||||||
|
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
||||||
|
description: 'Consume Discourse API.',
|
||||||
|
defaults: {
|
||||||
|
name: 'Discourse',
|
||||||
|
color: '#000000',
|
||||||
|
},
|
||||||
|
inputs: ['main'],
|
||||||
|
outputs: ['main'],
|
||||||
|
credentials: [
|
||||||
|
{
|
||||||
|
name: 'discourseApi',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
properties: [
|
||||||
|
{
|
||||||
|
displayName: 'Resource',
|
||||||
|
name: 'resource',
|
||||||
|
type: 'options',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Category',
|
||||||
|
value: 'category',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Group',
|
||||||
|
value: 'group',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Post',
|
||||||
|
value: 'post',
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// name: 'Search',
|
||||||
|
// value: 'search',
|
||||||
|
// },
|
||||||
|
{
|
||||||
|
name: 'User',
|
||||||
|
value: 'user',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'User Group',
|
||||||
|
value: 'userGroup',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
default: 'post',
|
||||||
|
description: 'The resource to operate on.',
|
||||||
|
},
|
||||||
|
...categoryOperations,
|
||||||
|
...categoryFields,
|
||||||
|
...groupOperations,
|
||||||
|
...groupFields,
|
||||||
|
...postOperations,
|
||||||
|
...postFields,
|
||||||
|
// ...searchOperations,
|
||||||
|
// ...searchFields,
|
||||||
|
...userOperations,
|
||||||
|
...userFields,
|
||||||
|
...userGroupOperations,
|
||||||
|
...userGroupFields,
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
methods = {
|
||||||
|
loadOptions: {
|
||||||
|
// Get all the calendars to display them to user so that he can
|
||||||
|
// select them easily
|
||||||
|
async getCategories(
|
||||||
|
this: ILoadOptionsFunctions,
|
||||||
|
): Promise<INodePropertyOptions[]> {
|
||||||
|
const returnData: INodePropertyOptions[] = [];
|
||||||
|
const { category_list } = await discourseApiRequest.call(
|
||||||
|
this,
|
||||||
|
'GET',
|
||||||
|
`/categories.json`,
|
||||||
|
);
|
||||||
|
for (const category of category_list.categories) {
|
||||||
|
returnData.push({
|
||||||
|
name: category.name,
|
||||||
|
value: category.id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return returnData;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
||||||
|
const items = this.getInputData();
|
||||||
|
const returnData: IDataObject[] = [];
|
||||||
|
const length = (items.length as unknown) as number;
|
||||||
|
const qs: IDataObject = {};
|
||||||
|
let responseData;
|
||||||
|
const resource = this.getNodeParameter('resource', 0) as string;
|
||||||
|
const operation = this.getNodeParameter('operation', 0) as string;
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
if (resource === 'category') {
|
||||||
|
//https://docs.discourse.org/#tag/Categories/paths/~1categories.json/post
|
||||||
|
if (operation === 'create') {
|
||||||
|
const name = this.getNodeParameter('name', i) as string;
|
||||||
|
const color = this.getNodeParameter('color', i) as string;
|
||||||
|
const textColor = this.getNodeParameter('textColor', i) as string;
|
||||||
|
|
||||||
|
const body: IDataObject = {
|
||||||
|
name,
|
||||||
|
color,
|
||||||
|
text_color: textColor,
|
||||||
|
};
|
||||||
|
|
||||||
|
responseData = await discourseApiRequest.call(
|
||||||
|
this,
|
||||||
|
'POST',
|
||||||
|
`/categories.json`,
|
||||||
|
body,
|
||||||
|
);
|
||||||
|
|
||||||
|
responseData = responseData.category;
|
||||||
|
}
|
||||||
|
//https://docs.discourse.org/#tag/Categories/paths/~1categories.json/get
|
||||||
|
if (operation === 'getAll') {
|
||||||
|
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
|
||||||
|
|
||||||
|
responseData = await discourseApiRequest.call(
|
||||||
|
this,
|
||||||
|
'GET',
|
||||||
|
`/categories.json`,
|
||||||
|
{},
|
||||||
|
qs,
|
||||||
|
);
|
||||||
|
|
||||||
|
responseData = responseData.category_list.categories;
|
||||||
|
|
||||||
|
if (returnAll === false) {
|
||||||
|
const limit = this.getNodeParameter('limit', i) as number;
|
||||||
|
responseData = responseData.splice(0, limit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//https://docs.discourse.org/#tag/Categories/paths/~1categories~1{id}/put
|
||||||
|
if (operation === 'update') {
|
||||||
|
const categoryId = this.getNodeParameter('categoryId', i) as string;
|
||||||
|
|
||||||
|
const name = this.getNodeParameter('name', i) as string;
|
||||||
|
|
||||||
|
const updateFields = this.getNodeParameter('updateFields', i) as IDataObject;
|
||||||
|
|
||||||
|
const body: IDataObject = {
|
||||||
|
name,
|
||||||
|
};
|
||||||
|
|
||||||
|
Object.assign(body, updateFields);
|
||||||
|
|
||||||
|
responseData = await discourseApiRequest.call(
|
||||||
|
this,
|
||||||
|
'PUT',
|
||||||
|
`/categories/${categoryId}.json`,
|
||||||
|
body,
|
||||||
|
);
|
||||||
|
|
||||||
|
responseData = responseData.category;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (resource === 'group') {
|
||||||
|
//https://docs.discourse.org/#tag/Posts/paths/~1posts.json/post
|
||||||
|
if (operation === 'create') {
|
||||||
|
const name = this.getNodeParameter('name', i) as string;
|
||||||
|
|
||||||
|
const body: IDataObject = {
|
||||||
|
name,
|
||||||
|
};
|
||||||
|
|
||||||
|
responseData = await discourseApiRequest.call(
|
||||||
|
this,
|
||||||
|
'POST',
|
||||||
|
`/admin/groups.json`,
|
||||||
|
{ group: body },
|
||||||
|
);
|
||||||
|
|
||||||
|
responseData = responseData.basic_group;
|
||||||
|
}
|
||||||
|
//https://docs.discourse.org/#tag/Groups/paths/~1groups~1{name}.json/get
|
||||||
|
if (operation === 'get') {
|
||||||
|
const name = this.getNodeParameter('name', i) as string;
|
||||||
|
|
||||||
|
responseData = await discourseApiRequest.call(
|
||||||
|
this,
|
||||||
|
'GET',
|
||||||
|
`/groups/${name}`,
|
||||||
|
{},
|
||||||
|
qs,
|
||||||
|
);
|
||||||
|
|
||||||
|
responseData = responseData.group;
|
||||||
|
|
||||||
|
}
|
||||||
|
//https://docs.discourse.org/#tag/Groups/paths/~1groups.json/get
|
||||||
|
if (operation === 'getAll') {
|
||||||
|
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
|
||||||
|
|
||||||
|
responseData = await discourseApiRequest.call(
|
||||||
|
this,
|
||||||
|
'GET',
|
||||||
|
`/groups.json`,
|
||||||
|
{},
|
||||||
|
qs,
|
||||||
|
);
|
||||||
|
|
||||||
|
responseData = responseData.groups;
|
||||||
|
|
||||||
|
if (returnAll === false) {
|
||||||
|
const limit = this.getNodeParameter('limit', i) as number;
|
||||||
|
responseData = responseData.splice(0, limit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//https://docs.discourse.org/#tag/Posts/paths/~1posts~1{id}.json/put
|
||||||
|
if (operation === 'update') {
|
||||||
|
const groupId = this.getNodeParameter('groupId', i) as string;
|
||||||
|
|
||||||
|
const name = this.getNodeParameter('name', i) as string;
|
||||||
|
|
||||||
|
const body: IDataObject = {
|
||||||
|
name,
|
||||||
|
};
|
||||||
|
|
||||||
|
responseData = await discourseApiRequest.call(
|
||||||
|
this,
|
||||||
|
'PUT',
|
||||||
|
`/groups/${groupId}.json`,
|
||||||
|
{ group: body },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (resource === 'post') {
|
||||||
|
//https://docs.discourse.org/#tag/Posts/paths/~1posts.json/post
|
||||||
|
if (operation === 'create') {
|
||||||
|
const content = this.getNodeParameter('content', i) as string;
|
||||||
|
const title = this.getNodeParameter('title', i) as string;
|
||||||
|
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
|
||||||
|
|
||||||
|
const body: IDataObject = {
|
||||||
|
title,
|
||||||
|
raw: content,
|
||||||
|
};
|
||||||
|
|
||||||
|
Object.assign(body, additionalFields);
|
||||||
|
|
||||||
|
responseData = await discourseApiRequest.call(
|
||||||
|
this,
|
||||||
|
'POST',
|
||||||
|
`/posts.json`,
|
||||||
|
body,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
//https://docs.discourse.org/#tag/Posts/paths/~1posts~1{id}.json/get
|
||||||
|
if (operation === 'get') {
|
||||||
|
const postId = this.getNodeParameter('postId', i) as string;
|
||||||
|
|
||||||
|
responseData = await discourseApiRequest.call(
|
||||||
|
this,
|
||||||
|
'GET',
|
||||||
|
`/posts/${postId}`,
|
||||||
|
{},
|
||||||
|
qs,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
//https://docs.discourse.org/#tag/Posts/paths/~1posts.json/get
|
||||||
|
if (operation === 'getAll') {
|
||||||
|
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
|
||||||
|
|
||||||
|
responseData = await discourseApiRequest.call(
|
||||||
|
this,
|
||||||
|
'GET',
|
||||||
|
`/posts.json`,
|
||||||
|
{},
|
||||||
|
qs,
|
||||||
|
);
|
||||||
|
|
||||||
|
responseData = responseData.latest_posts;
|
||||||
|
|
||||||
|
if (returnAll === false) {
|
||||||
|
const limit = this.getNodeParameter('limit', i) as number;
|
||||||
|
responseData = responseData.splice(0, limit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//https://docs.discourse.org/#tag/Posts/paths/~1posts~1{id}.json/put
|
||||||
|
if (operation === 'update') {
|
||||||
|
const postId = this.getNodeParameter('postId', i) as string;
|
||||||
|
|
||||||
|
const content = this.getNodeParameter('content', i) as string;
|
||||||
|
|
||||||
|
const updateFields = this.getNodeParameter('updateFields', i) as IDataObject;
|
||||||
|
|
||||||
|
const body: IDataObject = {
|
||||||
|
raw: content,
|
||||||
|
};
|
||||||
|
|
||||||
|
Object.assign(body, updateFields);
|
||||||
|
|
||||||
|
responseData = await discourseApiRequest.call(
|
||||||
|
this,
|
||||||
|
'PUT',
|
||||||
|
`/posts/${postId}.json`,
|
||||||
|
body,
|
||||||
|
);
|
||||||
|
|
||||||
|
responseData = responseData.post;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO figure how to paginate the results
|
||||||
|
// if (resource === 'search') {
|
||||||
|
// //https://docs.discourse.org/#tag/Search/paths/~1search~1query/get
|
||||||
|
// if (operation === 'query') {
|
||||||
|
// qs.term = this.getNodeParameter('term', i) as string;
|
||||||
|
|
||||||
|
// const simple = this.getNodeParameter('simple', i) as boolean;
|
||||||
|
|
||||||
|
// const updateFields = this.getNodeParameter('updateFields', i) as IDataObject;
|
||||||
|
|
||||||
|
// Object.assign(qs, updateFields);
|
||||||
|
|
||||||
|
// qs.page = 1;
|
||||||
|
|
||||||
|
// responseData = await discourseApiRequest.call(
|
||||||
|
// this,
|
||||||
|
// 'GET',
|
||||||
|
// `/search/query`,
|
||||||
|
// {},
|
||||||
|
// qs,
|
||||||
|
// );
|
||||||
|
|
||||||
|
// if (simple === true) {
|
||||||
|
// const response = [];
|
||||||
|
// for (const key of Object.keys(responseData)) {
|
||||||
|
// console.log(key)
|
||||||
|
// for (const data of responseData[key]) {
|
||||||
|
// response.push(Object.assign(data, { __type: key }));
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// responseData = response;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
if (resource === 'user') {
|
||||||
|
//https://docs.discourse.org/#tag/Users/paths/~1users/post
|
||||||
|
if (operation === 'create') {
|
||||||
|
const name = this.getNodeParameter('name', i) as string;
|
||||||
|
const email = this.getNodeParameter('email', i) as string;
|
||||||
|
const password = this.getNodeParameter('password', i) as string;
|
||||||
|
const username = this.getNodeParameter('username', i) as string;
|
||||||
|
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
|
||||||
|
|
||||||
|
const body: IDataObject = {
|
||||||
|
name,
|
||||||
|
password,
|
||||||
|
email,
|
||||||
|
username,
|
||||||
|
};
|
||||||
|
|
||||||
|
Object.assign(body, additionalFields);
|
||||||
|
|
||||||
|
responseData = await discourseApiRequest.call(
|
||||||
|
this,
|
||||||
|
'POST',
|
||||||
|
`/users.json`,
|
||||||
|
body,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
//https://docs.discourse.org/#tag/Users/paths/~1users~1{username}.json/get
|
||||||
|
if (operation === 'get') {
|
||||||
|
const by = this.getNodeParameter('by', i) as string;
|
||||||
|
let endpoint = '';
|
||||||
|
if (by === 'username') {
|
||||||
|
const username = this.getNodeParameter('username', i) as string;
|
||||||
|
endpoint = `/users/${username}`;
|
||||||
|
} else if (by === 'externalId') {
|
||||||
|
const externalId = this.getNodeParameter('externalId', i) as string;
|
||||||
|
endpoint = `/u/by-external/${externalId}.json`;
|
||||||
|
}
|
||||||
|
|
||||||
|
responseData = await discourseApiRequest.call(
|
||||||
|
this,
|
||||||
|
'GET',
|
||||||
|
endpoint,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
//https://docs.discourse.org/#tag/Users/paths/~1admin~1users~1{id}.json/delete
|
||||||
|
if (operation === 'getAll') {
|
||||||
|
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
|
||||||
|
const flag = this.getNodeParameter('flag', i) as boolean;
|
||||||
|
|
||||||
|
responseData = await discourseApiRequest.call(
|
||||||
|
this,
|
||||||
|
'GET',
|
||||||
|
`/admin/users/list/${flag}.json`,
|
||||||
|
{},
|
||||||
|
qs,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (returnAll === false) {
|
||||||
|
const limit = this.getNodeParameter('limit', i) as number;
|
||||||
|
responseData = responseData.splice(0, limit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (resource === 'userGroup') {
|
||||||
|
//https://docs.discourse.org/#tag/Groups/paths/~1groups~1{group_id}~1members.json/put
|
||||||
|
if (operation === 'add') {
|
||||||
|
const usernames = this.getNodeParameter('usernames', i) as string;
|
||||||
|
const groupId = this.getNodeParameter('groupId', i) as string;
|
||||||
|
const body: IDataObject = {
|
||||||
|
usernames,
|
||||||
|
};
|
||||||
|
|
||||||
|
responseData = await discourseApiRequest.call(
|
||||||
|
this,
|
||||||
|
'PUT',
|
||||||
|
`/groups/${groupId}/members.json`,
|
||||||
|
body,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
//https://docs.discourse.org/#tag/Groups/paths/~1groups~1{group_id}~1members.json/delete
|
||||||
|
if (operation === 'remove') {
|
||||||
|
const usernames = this.getNodeParameter('usernames', i) as string;
|
||||||
|
const groupId = this.getNodeParameter('groupId', i) as string;
|
||||||
|
const body: IDataObject = {
|
||||||
|
usernames,
|
||||||
|
};
|
||||||
|
|
||||||
|
responseData = await discourseApiRequest.call(
|
||||||
|
this,
|
||||||
|
'DELETE',
|
||||||
|
`/groups/${groupId}/members.json`,
|
||||||
|
body,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Array.isArray(responseData)) {
|
||||||
|
returnData.push.apply(returnData, responseData as IDataObject[]);
|
||||||
|
} else if (responseData !== undefined) {
|
||||||
|
returnData.push(responseData as IDataObject);
|
||||||
|
}
|
||||||
|
return [this.helpers.returnJsonArray(returnData)];
|
||||||
|
}
|
||||||
|
}
|
64
packages/nodes-base/nodes/Discourse/GenericFunctions.ts
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
import {
|
||||||
|
OptionsWithUri,
|
||||||
|
} from 'request';
|
||||||
|
|
||||||
|
import {
|
||||||
|
IExecuteFunctions,
|
||||||
|
IExecuteSingleFunctions,
|
||||||
|
ILoadOptionsFunctions,
|
||||||
|
} from 'n8n-core';
|
||||||
|
|
||||||
|
import {
|
||||||
|
IDataObject,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
export async function discourseApiRequest(this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, method: string, path: string, body: any = {}, qs: IDataObject = {}, option = {}): Promise<any> { // tslint:disable-line:no-any
|
||||||
|
|
||||||
|
const credentials = this.getCredentials('discourseApi') as IDataObject;
|
||||||
|
|
||||||
|
const options: OptionsWithUri = {
|
||||||
|
headers: {
|
||||||
|
'Api-Key': credentials.apiKey,
|
||||||
|
'Api-Username': credentials.username,
|
||||||
|
},
|
||||||
|
method,
|
||||||
|
body,
|
||||||
|
qs,
|
||||||
|
uri: `${credentials.url}${path}`,
|
||||||
|
json: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (Object.keys(body).length === 0) {
|
||||||
|
delete options.body;
|
||||||
|
}
|
||||||
|
//@ts-ignore
|
||||||
|
return await this.helpers.request.call(this, options);
|
||||||
|
} catch (error) {
|
||||||
|
if (error.response && error.response.body && error.response.body.errors) {
|
||||||
|
|
||||||
|
const errors = error.response.body.errors;
|
||||||
|
// Try to return the error prettier
|
||||||
|
throw new Error(
|
||||||
|
`Discourse error response [${error.statusCode}]: ${errors.join('|')}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function discourseApiRequestAllItems(this: IExecuteFunctions | ILoadOptionsFunctions, method: string, endpoint: string, body: any = {}, query: IDataObject = {}): Promise<any> { // tslint:disable-line:no-any
|
||||||
|
|
||||||
|
const returnData: IDataObject[] = [];
|
||||||
|
|
||||||
|
let responseData;
|
||||||
|
query.page = 1;
|
||||||
|
do {
|
||||||
|
responseData = await discourseApiRequest.call(this, method, endpoint, body, query);
|
||||||
|
returnData.push.apply(returnData, responseData);
|
||||||
|
query.page++;
|
||||||
|
} while (
|
||||||
|
responseData.length !== 0
|
||||||
|
);
|
||||||
|
return returnData;
|
||||||
|
}
|
153
packages/nodes-base/nodes/Discourse/GroupDescription.ts
Normal file
|
@ -0,0 +1,153 @@
|
||||||
|
import {
|
||||||
|
INodeProperties,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
export const groupOperations = [
|
||||||
|
{
|
||||||
|
displayName: 'Operation',
|
||||||
|
name: 'operation',
|
||||||
|
type: 'options',
|
||||||
|
description: 'Choose an operation',
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'group',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Create',
|
||||||
|
value: 'create',
|
||||||
|
description: 'Create a group',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Get',
|
||||||
|
value: 'get',
|
||||||
|
description: 'Get a group',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Get All',
|
||||||
|
value: 'getAll',
|
||||||
|
description: 'Get all groups',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Update',
|
||||||
|
value: 'update',
|
||||||
|
description: 'Update a group',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
default: 'create',
|
||||||
|
},
|
||||||
|
] as INodeProperties[];
|
||||||
|
|
||||||
|
export const groupFields: INodeProperties[] = [
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* group:create & get */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
{
|
||||||
|
displayName: 'Name',
|
||||||
|
name: 'name',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'group',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'get',
|
||||||
|
'create',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: 'Name of the group.',
|
||||||
|
},
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* group:getAll */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
{
|
||||||
|
displayName: 'Return All',
|
||||||
|
name: 'returnAll',
|
||||||
|
type: 'boolean',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'group',
|
||||||
|
],
|
||||||
|
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: [
|
||||||
|
'group',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
returnAll: [
|
||||||
|
false,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
typeOptions: {
|
||||||
|
minValue: 1,
|
||||||
|
maxValue: 100,
|
||||||
|
},
|
||||||
|
default: 50,
|
||||||
|
description: 'How many results to return.',
|
||||||
|
},
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* group:update */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
{
|
||||||
|
displayName: 'Group ID',
|
||||||
|
name: 'groupId',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'group',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'update',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: 'ID of the group to update.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Name',
|
||||||
|
name: 'name',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'group',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'update',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: 'New name of the group.',
|
||||||
|
},
|
||||||
|
];
|
270
packages/nodes-base/nodes/Discourse/PostDescription.ts
Normal file
|
@ -0,0 +1,270 @@
|
||||||
|
import {
|
||||||
|
INodeProperties,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
export const postOperations = [
|
||||||
|
{
|
||||||
|
displayName: 'Operation',
|
||||||
|
name: 'operation',
|
||||||
|
type: 'options',
|
||||||
|
description: 'Choose an operation',
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'post',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Create',
|
||||||
|
value: 'create',
|
||||||
|
description: 'Create a post',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Get',
|
||||||
|
value: 'get',
|
||||||
|
description: 'Get a post',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Get All',
|
||||||
|
value: 'getAll',
|
||||||
|
description: 'Get all posts',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Update',
|
||||||
|
value: 'update',
|
||||||
|
description: 'Update a post',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
default: 'create',
|
||||||
|
},
|
||||||
|
] as INodeProperties[];
|
||||||
|
|
||||||
|
export const postFields: INodeProperties[] = [
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* post:create */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
{
|
||||||
|
displayName: 'Title',
|
||||||
|
name: 'title',
|
||||||
|
type: 'string',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'post',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'create',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: 'Title of the post.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Content',
|
||||||
|
name: 'content',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
typeOptions: {
|
||||||
|
alwaysOpenEditWindow: true,
|
||||||
|
},
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'post',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'create',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: 'Content of the post.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Additional Fields',
|
||||||
|
name: 'additionalFields',
|
||||||
|
type: 'collection',
|
||||||
|
placeholder: 'Add Field',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
operation: [
|
||||||
|
'create',
|
||||||
|
],
|
||||||
|
resource: [
|
||||||
|
'post',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: {},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
displayName: 'Category ID',
|
||||||
|
name: 'category',
|
||||||
|
type: 'options',
|
||||||
|
typeOptions: {
|
||||||
|
loadOptionsMethod: 'getCategories',
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: 'ID of the category',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Reply To Post Number',
|
||||||
|
name: 'reply_to_post_number',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
description: 'The number of the post to reply to',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Topic ID',
|
||||||
|
name: 'topic_id',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
description: 'ID of the topic',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* post:get */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
{
|
||||||
|
displayName: 'Post ID',
|
||||||
|
name: 'postId',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'post',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'get',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: 'ID of the post.',
|
||||||
|
},
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* post:getAll */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
{
|
||||||
|
displayName: 'Return All',
|
||||||
|
name: 'returnAll',
|
||||||
|
type: 'boolean',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'post',
|
||||||
|
],
|
||||||
|
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: [
|
||||||
|
'post',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
returnAll: [
|
||||||
|
false,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
typeOptions: {
|
||||||
|
minValue: 1,
|
||||||
|
maxValue: 100,
|
||||||
|
},
|
||||||
|
default: 50,
|
||||||
|
description: 'How many results to return.',
|
||||||
|
},
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* post:update */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
{
|
||||||
|
displayName: 'Post ID',
|
||||||
|
name: 'postId',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'post',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'update',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: 'ID of the post.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Content',
|
||||||
|
name: 'content',
|
||||||
|
type: 'string',
|
||||||
|
typeOptions: {
|
||||||
|
alwaysOpenEditWindow: true,
|
||||||
|
},
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'post',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'update',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: 'Content of the post. HTML is supported.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Update Fields',
|
||||||
|
name: 'updateFields',
|
||||||
|
type: 'collection',
|
||||||
|
placeholder: 'Add Field',
|
||||||
|
default: {},
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'post',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'update',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
displayName: 'Edit Reason',
|
||||||
|
name: 'edit_reason',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Cooked',
|
||||||
|
name: 'cooked',
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
69
packages/nodes-base/nodes/Discourse/SearchDescription.ts
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
import {
|
||||||
|
INodeProperties,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
export const searchOperations = [
|
||||||
|
{
|
||||||
|
displayName: 'Operation',
|
||||||
|
name: 'operation',
|
||||||
|
type: 'options',
|
||||||
|
description: 'Choose an operation',
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'search',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Query',
|
||||||
|
value: 'query',
|
||||||
|
description: 'Search for something',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
default: 'query',
|
||||||
|
},
|
||||||
|
] as INodeProperties[];
|
||||||
|
|
||||||
|
export const searchFields: INodeProperties[] = [
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* search:query */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
{
|
||||||
|
displayName: 'Term',
|
||||||
|
name: 'term',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'search',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'query',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: 'Term to search for.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Simple',
|
||||||
|
name: 'simple',
|
||||||
|
type: 'boolean',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'search',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'query',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: true,
|
||||||
|
description: 'When set to true a simplify version of the response will be used else the raw data.',
|
||||||
|
},
|
||||||
|
];
|
308
packages/nodes-base/nodes/Discourse/UserDescription.ts
Normal file
|
@ -0,0 +1,308 @@
|
||||||
|
import {
|
||||||
|
INodeProperties,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
export const userOperations = [
|
||||||
|
{
|
||||||
|
displayName: 'Operation',
|
||||||
|
name: 'operation',
|
||||||
|
type: 'options',
|
||||||
|
description: 'Choose an operation',
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'user',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Create',
|
||||||
|
value: 'create',
|
||||||
|
description: 'Create a user',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Get',
|
||||||
|
value: 'get',
|
||||||
|
description: 'Get a user',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Get All',
|
||||||
|
value: 'getAll',
|
||||||
|
description: 'Get all users',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
default: 'create',
|
||||||
|
},
|
||||||
|
] as INodeProperties[];
|
||||||
|
|
||||||
|
export const userFields: INodeProperties[] = [
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* user:create */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
{
|
||||||
|
displayName: 'Name',
|
||||||
|
name: 'name',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'user',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'create',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: 'Name of the user to create.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Email',
|
||||||
|
name: 'email',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'user',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'create',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: 'Email of the user to create.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Username',
|
||||||
|
name: 'username',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'user',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'create',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: `The username of the user to create.`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Password',
|
||||||
|
name: 'password',
|
||||||
|
type: 'string',
|
||||||
|
typeOptions: {
|
||||||
|
password: true,
|
||||||
|
},
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'user',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'create',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: `The password of the user to create.`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Additional Fields',
|
||||||
|
name: 'additionalFields',
|
||||||
|
type: 'collection',
|
||||||
|
placeholder: 'Add Field',
|
||||||
|
default: {},
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'user',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'create',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
displayName: 'Active',
|
||||||
|
name: 'active',
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Approved',
|
||||||
|
name: 'approved',
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* user:get */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
{
|
||||||
|
displayName: 'By',
|
||||||
|
name: 'by',
|
||||||
|
type: 'options',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Username',
|
||||||
|
value: 'username',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'SSO External ID',
|
||||||
|
value: 'externalId',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'user',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'get',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: 'username',
|
||||||
|
description: 'What to search by.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Username',
|
||||||
|
name: 'username',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'user',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'get',
|
||||||
|
],
|
||||||
|
by: [
|
||||||
|
'username',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: `The username of the user to return.`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'SSO External ID',
|
||||||
|
name: 'externalId',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'user',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'get',
|
||||||
|
],
|
||||||
|
by: [
|
||||||
|
'externalId',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: `Discourse SSO external ID.`,
|
||||||
|
},
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* user:getAll */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
{
|
||||||
|
displayName: 'Flag',
|
||||||
|
name: 'flag',
|
||||||
|
type: 'options',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Active',
|
||||||
|
value: 'active',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Blocked',
|
||||||
|
value: 'blocked',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'New',
|
||||||
|
value: 'new',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Staff',
|
||||||
|
value: 'staff',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Suspect',
|
||||||
|
value: 'suspect',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'user',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: `User flags to search for.`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
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.',
|
||||||
|
},
|
||||||
|
];
|
116
packages/nodes-base/nodes/Discourse/UserGroupDescription.ts
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
import {
|
||||||
|
INodeProperties,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
export const userGroupOperations = [
|
||||||
|
{
|
||||||
|
displayName: 'Operation',
|
||||||
|
name: 'operation',
|
||||||
|
type: 'options',
|
||||||
|
description: 'Choose an operation',
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'userGroup',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Add',
|
||||||
|
value: 'add',
|
||||||
|
description: 'Create a user to group',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Remove',
|
||||||
|
value: 'remove',
|
||||||
|
description: 'Remove user from group',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
default: 'add',
|
||||||
|
},
|
||||||
|
] as INodeProperties[];
|
||||||
|
|
||||||
|
export const userGroupFields: INodeProperties[] = [
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* userGroup:add */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
{
|
||||||
|
displayName: 'Usernames',
|
||||||
|
name: 'usernames',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'userGroup',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'add',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: 'Usernames to add to group. Multiples can be defined separated by comma',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Group ID',
|
||||||
|
name: 'groupId',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'userGroup',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'add',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: 'ID of the group.',
|
||||||
|
},
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* userGroup:remove */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
{
|
||||||
|
displayName: 'Usernames',
|
||||||
|
name: 'usernames',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'userGroup',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'remove',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: 'Usernames to remove from group. Multiples can be defined separated by comma.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Group ID',
|
||||||
|
name: 'groupId',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'userGroup',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'remove',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: 'ID of the group to remove.',
|
||||||
|
},
|
||||||
|
|
||||||
|
];
|
1
packages/nodes-base/nodes/Discourse/discourse.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -1 104 106"><defs><style>.cls-1{fill:#231f20;}.cls-2{fill:#fff9ae;}.cls-3{fill:#00aeef;}.cls-4{fill:#00a94f;}.cls-5{fill:#f15d22;}.cls-6{fill:#e31b23;}</style></defs><title>Discourse_logo</title><g id="Layer_2" data-name="Layer 2"><g id="Layer_3" data-name="Layer 3"><path class="cls-1" d="M51.87,0C23.71,0,0,22.83,0,51c0,.91,0,52.81,0,52.81l51.86-.05c28.16,0,51-23.71,51-51.87S80,0,51.87,0Z"/><path class="cls-2" d="M52.37,19.74A31.62,31.62,0,0,0,24.58,66.41l-5.72,18.4L39.4,80.17a31.61,31.61,0,1,0,13-60.43Z"/><path class="cls-3" d="M77.45,32.12a31.6,31.6,0,0,1-38.05,48L18.86,84.82l20.91-2.47A31.6,31.6,0,0,0,77.45,32.12Z"/><path class="cls-4" d="M71.63,26.29A31.6,31.6,0,0,1,38.8,78L18.86,84.82,39.4,80.17A31.6,31.6,0,0,0,71.63,26.29Z"/><path class="cls-5" d="M26.47,67.11a31.61,31.61,0,0,1,51-35A31.61,31.61,0,0,0,24.58,66.41l-5.72,18.4Z"/><path class="cls-6" d="M24.58,66.41A31.61,31.61,0,0,1,71.63,26.29a31.61,31.61,0,0,0-49,39.63l-3.76,18.9Z"/></g></g></svg>
|
After Width: | Height: | Size: 1,017 B |
|
@ -1,6 +1,6 @@
|
||||||
import {
|
import {
|
||||||
INodeProperties,
|
INodeProperties,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
export const companyOperations = [
|
export const companyOperations = [
|
||||||
{
|
{
|
||||||
|
@ -63,9 +63,9 @@ export const companyOperations = [
|
||||||
|
|
||||||
export const companyFields = [
|
export const companyFields = [
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
/* company:create */
|
/* company:create */
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
{
|
{
|
||||||
displayName: 'Name',
|
displayName: 'Name',
|
||||||
name: 'name',
|
name: 'name',
|
||||||
|
@ -157,6 +157,41 @@ export const companyFields = [
|
||||||
default: '',
|
default: '',
|
||||||
description: 'The country/region in which the company or organization is located.',
|
description: 'The country/region in which the company or organization is located.',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Custom Properties',
|
||||||
|
name: 'customPropertiesUi',
|
||||||
|
placeholder: 'Add Custom Property',
|
||||||
|
type: 'fixedCollection',
|
||||||
|
typeOptions: {
|
||||||
|
multipleValues: true,
|
||||||
|
},
|
||||||
|
default: {},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'customPropertiesValues',
|
||||||
|
displayName: 'Custom Property',
|
||||||
|
values: [
|
||||||
|
{
|
||||||
|
displayName: 'Property',
|
||||||
|
name: 'property',
|
||||||
|
type: 'options',
|
||||||
|
typeOptions: {
|
||||||
|
loadOptionsMethod: 'getCompanyCustomProperties',
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: 'Name of the property.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Value',
|
||||||
|
name: 'value',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
description: 'Value of the property',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Description',
|
displayName: 'Description',
|
||||||
name: 'description',
|
name: 'description',
|
||||||
|
@ -370,7 +405,7 @@ export const companyFields = [
|
||||||
description: 'The main website of the company or organization. This property is used to identify unique companies. Powered by HubSpot Insights.',
|
description: 'The main website of the company or organization. This property is used to identify unique companies. Powered by HubSpot Insights.',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Year Founded',
|
displayName: 'Year Founded',
|
||||||
name: 'yearFounded',
|
name: 'yearFounded',
|
||||||
type: 'string',
|
type: 'string',
|
||||||
default: '',
|
default: '',
|
||||||
|
@ -378,9 +413,10 @@ export const companyFields = [
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
/* -------------------------------------------------------------------------- */
|
|
||||||
/* company:update */
|
/* -------------------------------------------------------------------------- */
|
||||||
/* -------------------------------------------------------------------------- */
|
/* company:update */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
{
|
{
|
||||||
displayName: 'Company ID',
|
displayName: 'Company ID',
|
||||||
name: 'companyId',
|
name: 'companyId',
|
||||||
|
@ -473,6 +509,41 @@ export const companyFields = [
|
||||||
default: '',
|
default: '',
|
||||||
description: 'The country/region in which the company or organization is located.',
|
description: 'The country/region in which the company or organization is located.',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Custom Properties',
|
||||||
|
name: 'customPropertiesUi',
|
||||||
|
placeholder: 'Add Custom Property',
|
||||||
|
type: 'fixedCollection',
|
||||||
|
typeOptions: {
|
||||||
|
multipleValues: true,
|
||||||
|
},
|
||||||
|
default: {},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'customPropertiesValues',
|
||||||
|
displayName: 'Custom Property',
|
||||||
|
values: [
|
||||||
|
{
|
||||||
|
displayName: 'Property',
|
||||||
|
name: 'property',
|
||||||
|
type: 'options',
|
||||||
|
typeOptions: {
|
||||||
|
loadOptionsMethod: 'getCompanyCustomProperties',
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: 'Name of the property.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Value',
|
||||||
|
name: 'value',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
description: 'Value of the property',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Description',
|
displayName: 'Description',
|
||||||
name: 'description',
|
name: 'description',
|
||||||
|
@ -692,7 +763,7 @@ export const companyFields = [
|
||||||
description: 'The main website of the company or organization. This property is used to identify unique companies. Powered by HubSpot Insights.',
|
description: 'The main website of the company or organization. This property is used to identify unique companies. Powered by HubSpot Insights.',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Year Founded',
|
displayName: 'Year Founded',
|
||||||
name: 'yearFounded',
|
name: 'yearFounded',
|
||||||
type: 'string',
|
type: 'string',
|
||||||
default: '',
|
default: '',
|
||||||
|
@ -700,9 +771,10 @@ export const companyFields = [
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
/* -------------------------------------------------------------------------- */
|
|
||||||
/* company:get */
|
/* -------------------------------------------------------------------------- */
|
||||||
/* -------------------------------------------------------------------------- */
|
/* company:get */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
{
|
{
|
||||||
displayName: 'Company ID',
|
displayName: 'Company ID',
|
||||||
name: 'companyId',
|
name: 'companyId',
|
||||||
|
@ -747,9 +819,10 @@ export const companyFields = [
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
/* -------------------------------------------------------------------------- */
|
|
||||||
/* company:getAll */
|
/* -------------------------------------------------------------------------- */
|
||||||
/* -------------------------------------------------------------------------- */
|
/* company:getAll */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
{
|
{
|
||||||
displayName: 'Return All',
|
displayName: 'Return All',
|
||||||
name: 'returnAll',
|
name: 'returnAll',
|
||||||
|
@ -838,9 +911,10 @@ export const companyFields = [
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
/* -------------------------------------------------------------------------- */
|
|
||||||
/* company:delete */
|
/* -------------------------------------------------------------------------- */
|
||||||
/* -------------------------------------------------------------------------- */
|
/* company:delete */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
{
|
{
|
||||||
displayName: 'Company ID',
|
displayName: 'Company ID',
|
||||||
name: 'companyId',
|
name: 'companyId',
|
||||||
|
@ -859,9 +933,10 @@ export const companyFields = [
|
||||||
default: '',
|
default: '',
|
||||||
description: 'Unique identifier for a particular company',
|
description: 'Unique identifier for a particular company',
|
||||||
},
|
},
|
||||||
/* -------------------------------------------------------------------------- */
|
|
||||||
/* company:getRecentlyCreated company:getRecentlyModifie */
|
/* -------------------------------------------------------------------------- */
|
||||||
/* -------------------------------------------------------------------------- */
|
/* company:getRecentlyCreated company:getRecentlyModifie */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
{
|
{
|
||||||
displayName: 'Return All',
|
displayName: 'Return All',
|
||||||
name: 'returnAll',
|
name: 'returnAll',
|
||||||
|
@ -939,9 +1014,10 @@ export const companyFields = [
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
/* -------------------------------------------------------------------------- */
|
|
||||||
/* company:searchByDomain */
|
/* -------------------------------------------------------------------------- */
|
||||||
/* -------------------------------------------------------------------------- */
|
/* company:searchByDomain */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
{
|
{
|
||||||
displayName: 'Domain',
|
displayName: 'Domain',
|
||||||
name: 'domain',
|
name: 'domain',
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import {
|
import {
|
||||||
INodeProperties,
|
INodeProperties,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
export const contactOperations = [
|
export const contactOperations = [
|
||||||
{
|
{
|
||||||
|
@ -53,9 +53,9 @@ export const contactOperations = [
|
||||||
|
|
||||||
export const contactFields = [
|
export const contactFields = [
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
/* contact:upsert */
|
/* contact:upsert */
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
{
|
{
|
||||||
displayName: 'Email',
|
displayName: 'Email',
|
||||||
name: 'email',
|
name: 'email',
|
||||||
|
@ -121,7 +121,7 @@ export const contactFields = [
|
||||||
name: 'associatedCompanyId',
|
name: 'associatedCompanyId',
|
||||||
type: 'options',
|
type: 'options',
|
||||||
typeOptions: {
|
typeOptions: {
|
||||||
loadOptionsMethod:'getCompanies' ,
|
loadOptionsMethod: 'getCompanies',
|
||||||
},
|
},
|
||||||
default: '',
|
default: '',
|
||||||
description: 'Companies associated with the ticket',
|
description: 'Companies associated with the ticket',
|
||||||
|
@ -501,9 +501,10 @@ export const contactFields = [
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
/* -------------------------------------------------------------------------- */
|
|
||||||
/* contact:get */
|
/* -------------------------------------------------------------------------- */
|
||||||
/* -------------------------------------------------------------------------- */
|
/* contact:get */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
{
|
{
|
||||||
displayName: 'Contact ID',
|
displayName: 'Contact ID',
|
||||||
name: 'contactId',
|
name: 'contactId',
|
||||||
|
@ -603,9 +604,10 @@ export const contactFields = [
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
/* -------------------------------------------------------------------------- */
|
|
||||||
/* contact:getAll */
|
/* -------------------------------------------------------------------------- */
|
||||||
/* -------------------------------------------------------------------------- */
|
/* contact:getAll */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
{
|
{
|
||||||
displayName: 'Return All',
|
displayName: 'Return All',
|
||||||
name: 'returnAll',
|
name: 'returnAll',
|
||||||
|
@ -728,9 +730,10 @@ export const contactFields = [
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
/* -------------------------------------------------------------------------- */
|
|
||||||
/* contact:delete */
|
/* -------------------------------------------------------------------------- */
|
||||||
/* -------------------------------------------------------------------------- */
|
/* contact:delete */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
{
|
{
|
||||||
displayName: 'Contact ID',
|
displayName: 'Contact ID',
|
||||||
name: 'contactId',
|
name: 'contactId',
|
||||||
|
@ -749,9 +752,10 @@ export const contactFields = [
|
||||||
default: '',
|
default: '',
|
||||||
description: 'Unique identifier for a particular contact',
|
description: 'Unique identifier for a particular contact',
|
||||||
},
|
},
|
||||||
/* -------------------------------------------------------------------------- */
|
|
||||||
/* contact:getRecentlyCreatedUpdated */
|
/* -------------------------------------------------------------------------- */
|
||||||
/* -------------------------------------------------------------------------- */
|
/* contact:getRecentlyCreatedUpdated */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
{
|
{
|
||||||
displayName: 'Return All',
|
displayName: 'Return All',
|
||||||
name: 'returnAll',
|
name: 'returnAll',
|
||||||
|
@ -875,9 +879,9 @@ export const contactFields = [
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
//*-------------------------------------------------------------------------- */
|
//*-------------------------------------------------------------------------- */
|
||||||
/* contact:search */
|
/* contact:search */
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
{
|
{
|
||||||
displayName: 'Return All',
|
displayName: 'Return All',
|
||||||
name: 'returnAll',
|
name: 'returnAll',
|
||||||
|
|
|
@ -120,6 +120,7 @@ export const contactListFields = [
|
||||||
},
|
},
|
||||||
default: '',
|
default: '',
|
||||||
},
|
},
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
/* contactList:remove */
|
/* contactList:remove */
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import {
|
import {
|
||||||
IDataObject,
|
IDataObject,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
export interface IAssociation {
|
export interface IAssociation {
|
||||||
associatedCompanyIds?: number[];
|
associatedCompanyIds?: number[];
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import {
|
import {
|
||||||
INodeProperties,
|
INodeProperties,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
export const formOperations = [
|
export const formOperations = [
|
||||||
{
|
{
|
||||||
|
@ -33,9 +33,9 @@ export const formOperations = [
|
||||||
|
|
||||||
export const formFields = [
|
export const formFields = [
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
/* form:submit */
|
/* form:submit */
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
{
|
{
|
||||||
displayName: 'Form',
|
displayName: 'Form',
|
||||||
name: 'formId',
|
name: 'formId',
|
||||||
|
@ -301,9 +301,10 @@ export const formFields = [
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
/* -------------------------------------------------------------------------- */
|
|
||||||
/* form:getFields */
|
/* -------------------------------------------------------------------------- */
|
||||||
/* -------------------------------------------------------------------------- */
|
/* form:getFields */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
{
|
{
|
||||||
displayName: 'Form',
|
displayName: 'Form',
|
||||||
name: 'formId',
|
name: 'formId',
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import {
|
import {
|
||||||
IDataObject,
|
IDataObject,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
export interface IContext {
|
export interface IContext {
|
||||||
goToWebinarWebinarKey?: string;
|
goToWebinarWebinarKey?: string;
|
||||||
|
|
|
@ -63,7 +63,7 @@ export class Hubspot implements INodeType {
|
||||||
description: INodeTypeDescription = {
|
description: INodeTypeDescription = {
|
||||||
displayName: 'HubSpot',
|
displayName: 'HubSpot',
|
||||||
name: 'hubspot',
|
name: 'hubspot',
|
||||||
icon: 'file:hubspot.png',
|
icon: 'file:hubspot.svg',
|
||||||
group: ['output'],
|
group: ['output'],
|
||||||
version: 1,
|
version: 1,
|
||||||
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
||||||
|
@ -526,6 +526,26 @@ export class Hubspot implements INodeType {
|
||||||
}
|
}
|
||||||
return returnData;
|
return returnData;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Get all the company custom properties to display them to user so that he can
|
||||||
|
// select them easily
|
||||||
|
async getCompanyCustomProperties(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||||
|
const returnData: INodePropertyOptions[] = [];
|
||||||
|
const endpoint = '/properties/v2/companies/properties';
|
||||||
|
const properties = await hubspotApiRequest.call(this, 'GET', endpoint, {});
|
||||||
|
for (const property of properties) {
|
||||||
|
if (property.hubspotDefined === null) {
|
||||||
|
const propertyName = property.label;
|
||||||
|
const propertyId = property.name;
|
||||||
|
returnData.push({
|
||||||
|
name: propertyName,
|
||||||
|
value: propertyId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return returnData;
|
||||||
|
},
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
/* DEAL */
|
/* DEAL */
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
|
@ -1535,6 +1555,18 @@ export class Hubspot implements INodeType {
|
||||||
value: additionalFields.yearFounded,
|
value: additionalFields.yearFounded,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (additionalFields.customPropertiesUi) {
|
||||||
|
const customProperties = (additionalFields.customPropertiesUi as IDataObject).customPropertiesValues as IDataObject[];
|
||||||
|
|
||||||
|
if (customProperties) {
|
||||||
|
for (const customProperty of customProperties) {
|
||||||
|
body.push({
|
||||||
|
name: customProperty.property,
|
||||||
|
value: customProperty.value,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
const endpoint = '/companies/v2/companies';
|
const endpoint = '/companies/v2/companies';
|
||||||
responseData = await hubspotApiRequest.call(this, 'POST', endpoint, { properties: body });
|
responseData = await hubspotApiRequest.call(this, 'POST', endpoint, { properties: body });
|
||||||
}
|
}
|
||||||
|
@ -1747,6 +1779,18 @@ export class Hubspot implements INodeType {
|
||||||
value: updateFields.yearFounded,
|
value: updateFields.yearFounded,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (updateFields.customPropertiesUi) {
|
||||||
|
const customProperties = (updateFields.customPropertiesUi as IDataObject).customPropertiesValues as IDataObject[];
|
||||||
|
|
||||||
|
if (customProperties) {
|
||||||
|
for (const customProperty of customProperties) {
|
||||||
|
body.push({
|
||||||
|
name: customProperty.property,
|
||||||
|
value: customProperty.value,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
const endpoint = `/companies/v2/companies/${companyId}`;
|
const endpoint = `/companies/v2/companies/${companyId}`;
|
||||||
responseData = await hubspotApiRequest.call(this, 'PUT', endpoint, { properties: body });
|
responseData = await hubspotApiRequest.call(this, 'PUT', endpoint, { properties: body });
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,27 +5,36 @@ import {
|
||||||
|
|
||||||
import {
|
import {
|
||||||
IDataObject,
|
IDataObject,
|
||||||
|
ILoadOptionsFunctions,
|
||||||
|
INodePropertyOptions,
|
||||||
INodeType,
|
INodeType,
|
||||||
INodeTypeDescription,
|
INodeTypeDescription,
|
||||||
IWebhookResponseData,
|
IWebhookResponseData,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
companyFields,
|
||||||
|
contactFields,
|
||||||
|
dealFields,
|
||||||
hubspotApiRequest,
|
hubspotApiRequest,
|
||||||
|
propertyEvents,
|
||||||
} from './GenericFunctions';
|
} from './GenericFunctions';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
createHash,
|
createHash,
|
||||||
} from 'crypto';
|
} from 'crypto';
|
||||||
|
|
||||||
|
import {
|
||||||
|
capitalCase,
|
||||||
|
} from 'change-case';
|
||||||
|
|
||||||
export class HubspotTrigger implements INodeType {
|
export class HubspotTrigger implements INodeType {
|
||||||
description: INodeTypeDescription = {
|
description: INodeTypeDescription = {
|
||||||
displayName: 'HubSpot Trigger',
|
displayName: 'HubSpot Trigger',
|
||||||
name: 'hubspotTrigger',
|
name: 'hubspotTrigger',
|
||||||
icon: 'file:hubspot.png',
|
icon: 'file:hubspot.svg',
|
||||||
group: ['trigger'],
|
group: ['trigger'],
|
||||||
version: 1,
|
version: 1,
|
||||||
subtitle: '={{($parameter["appId"]) ? $parameter["event"] : ""}}',
|
|
||||||
description: 'Starts the workflow when HubSpot events occur.',
|
description: 'Starts the workflow when HubSpot events occur.',
|
||||||
defaults: {
|
defaults: {
|
||||||
name: 'Hubspot Trigger',
|
name: 'Hubspot Trigger',
|
||||||
|
@ -55,87 +64,132 @@ export class HubspotTrigger implements INodeType {
|
||||||
],
|
],
|
||||||
properties: [
|
properties: [
|
||||||
{
|
{
|
||||||
displayName: 'App ID',
|
displayName: 'Events',
|
||||||
name: 'appId',
|
name: 'eventsUi',
|
||||||
type: 'string',
|
type: 'fixedCollection',
|
||||||
default: '',
|
typeOptions: {
|
||||||
required: true,
|
multipleValues: true,
|
||||||
description: 'App ID',
|
},
|
||||||
},
|
placeholder: 'Add Event',
|
||||||
{
|
default: {},
|
||||||
displayName: 'Event',
|
|
||||||
name: 'event',
|
|
||||||
type: 'options',
|
|
||||||
options: [
|
options: [
|
||||||
{
|
{
|
||||||
name: 'contact.creation',
|
displayName: 'Event',
|
||||||
value: 'contact.creation',
|
name: 'eventValues',
|
||||||
description: `To get notified if any contact is created in a customer's account.`,
|
values: [
|
||||||
},
|
{
|
||||||
{
|
displayName: 'Name',
|
||||||
name: 'contact.deletion',
|
name: 'name',
|
||||||
value: 'contact.deletion',
|
type: 'options',
|
||||||
description: `To get notified if any contact is deleted in a customer's account.`,
|
options: [
|
||||||
},
|
{
|
||||||
{
|
name: 'Contact Created',
|
||||||
name: 'contact.privacyDeletion',
|
value: 'contact.creation',
|
||||||
value: 'contact.privacyDeletion',
|
description: `To get notified if any contact is created in a customer's account.`,
|
||||||
description: `To get notified if a contact is deleted for privacy compliance reasons. `,
|
},
|
||||||
},
|
{
|
||||||
{
|
name: 'Contact Deleted',
|
||||||
name: 'contact.propertyChange',
|
value: 'contact.deletion',
|
||||||
value: 'contact.propertyChange',
|
description: `To get notified if any contact is deleted in a customer's account.`,
|
||||||
description: `to get notified if a specified property is changed for any contact in a customer's account. `,
|
},
|
||||||
},
|
{
|
||||||
{
|
name: 'Contact Privacy Deleted',
|
||||||
name: 'company.creation',
|
value: 'contact.privacyDeletion',
|
||||||
value: 'company.creation',
|
description: `To get notified if a contact is deleted for privacy compliance reasons. `,
|
||||||
description: `To get notified if any company is created in a customer's account.`,
|
},
|
||||||
},
|
{
|
||||||
{
|
name: 'Contact Property Changed',
|
||||||
name: 'company.deletion',
|
value: 'contact.propertyChange',
|
||||||
value: 'company.deletion',
|
description: `to get notified if a specified property is changed for any contact in a customer's account. `,
|
||||||
description: `To get notified if any company is deleted in a customer's account.`,
|
},
|
||||||
},
|
{
|
||||||
{
|
name: 'Company Created',
|
||||||
name: 'company.propertyChange',
|
value: 'company.creation',
|
||||||
value: 'company.propertyChange',
|
description: `To get notified if any company is created in a customer's account.`,
|
||||||
description: `To get notified if a specified property is changed for any company in a customer's account.`,
|
},
|
||||||
},
|
{
|
||||||
{
|
name: 'Company Deleted',
|
||||||
name: 'deal.creation',
|
value: 'company.deletion',
|
||||||
value: 'deal.creation',
|
description: `To get notified if any company is deleted in a customer's account.`,
|
||||||
description: `To get notified if any deal is created in a customer's account.`,
|
},
|
||||||
},
|
{
|
||||||
{
|
name: 'Company Property Changed',
|
||||||
name: 'deal.deletion',
|
value: 'company.propertyChange',
|
||||||
value: 'deal.deletion',
|
description: `To get notified if a specified property is changed for any company in a customer's account.`,
|
||||||
description: `To get notified if any deal is deleted in a customer's account.`,
|
},
|
||||||
},
|
{
|
||||||
{
|
name: 'Deal Created',
|
||||||
name: 'deal.propertyChange',
|
value: 'deal.creation',
|
||||||
value: 'deal.propertyChange',
|
description: `To get notified if any deal is created in a customer's account.`,
|
||||||
description: `To get notified if a specified property is changed for any deal in a customer's account.`,
|
},
|
||||||
},
|
{
|
||||||
],
|
name: 'Deal Deleted',
|
||||||
default: 'contact.creation',
|
value: 'deal.deletion',
|
||||||
required: true,
|
description: `To get notified if any deal is deleted in a customer's account.`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Property',
|
name: 'Deal Property Changed',
|
||||||
name: 'property',
|
value: 'deal.propertyChange',
|
||||||
type: 'string',
|
description: `To get notified if a specified property is changed for any deal in a customer's account.`,
|
||||||
displayOptions: {
|
},
|
||||||
show: {
|
],
|
||||||
event: [
|
default: 'contact.creation',
|
||||||
'contact.propertyChange',
|
required: true,
|
||||||
'company.propertyChange',
|
},
|
||||||
'deal.propertyChange',
|
{
|
||||||
|
displayName: 'Property',
|
||||||
|
name: 'property',
|
||||||
|
type: 'options',
|
||||||
|
typeOptions: {
|
||||||
|
loadOptionsMethod: 'getContactProperties',
|
||||||
|
},
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
name: [
|
||||||
|
'contact.propertyChange',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Property',
|
||||||
|
name: 'property',
|
||||||
|
type: 'options',
|
||||||
|
typeOptions: {
|
||||||
|
loadOptionsMethod: 'getCompanyProperties',
|
||||||
|
},
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
name: [
|
||||||
|
'company.propertyChange',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Property',
|
||||||
|
name: 'property',
|
||||||
|
type: 'options',
|
||||||
|
typeOptions: {
|
||||||
|
loadOptionsMethod: 'getDealProperties',
|
||||||
|
},
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
name: [
|
||||||
|
'deal.propertyChange',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
],
|
||||||
default: '',
|
|
||||||
required: true,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Additional Fields',
|
displayName: 'Additional Fields',
|
||||||
|
@ -156,7 +210,62 @@ export class HubspotTrigger implements INodeType {
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
methods = {
|
||||||
|
loadOptions: {
|
||||||
|
// Get all the available contacts to display them to user so that he can
|
||||||
|
// select them easily
|
||||||
|
async getContactProperties(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||||
|
const returnData: INodePropertyOptions[] = [];
|
||||||
|
for (const field of contactFields) {
|
||||||
|
returnData.push({
|
||||||
|
name: capitalCase(field.label),
|
||||||
|
value: field.id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
returnData.sort((a, b) => {
|
||||||
|
if (a.name < b.name) { return -1; }
|
||||||
|
if (a.name > b.name) { return 1; }
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
return returnData;
|
||||||
|
},
|
||||||
|
// Get all the available companies to display them to user so that he can
|
||||||
|
// select them easily
|
||||||
|
async getCompanyProperties(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||||
|
const returnData: INodePropertyOptions[] = [];
|
||||||
|
for (const field of companyFields) {
|
||||||
|
returnData.push({
|
||||||
|
name: capitalCase(field.label),
|
||||||
|
value: field.id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
returnData.sort((a, b) => {
|
||||||
|
if (a.name < b.name) { return -1; }
|
||||||
|
if (a.name > b.name) { return 1; }
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
return returnData;
|
||||||
|
},
|
||||||
|
// Get all the available deals to display them to user so that he can
|
||||||
|
// select them easily
|
||||||
|
async getDealProperties(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||||
|
const returnData: INodePropertyOptions[] = [];
|
||||||
|
for (const field of dealFields) {
|
||||||
|
returnData.push({
|
||||||
|
name: capitalCase(field.label),
|
||||||
|
value: field.id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
returnData.sort((a, b) => {
|
||||||
|
if (a.name < b.name) { return -1; }
|
||||||
|
if (a.name > b.name) { return 1; }
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
return returnData;
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// @ts-ignore (because of request)
|
// @ts-ignore (because of request)
|
||||||
|
@ -165,80 +274,78 @@ export class HubspotTrigger implements INodeType {
|
||||||
async checkExists(this: IHookFunctions): Promise<boolean> {
|
async checkExists(this: IHookFunctions): Promise<boolean> {
|
||||||
// Check all the webhooks which exist already if it is identical to the
|
// Check all the webhooks which exist already if it is identical to the
|
||||||
// one that is supposed to get created.
|
// one that is supposed to get created.
|
||||||
const app = parseInt(this.getNodeParameter('appId') as string, 10);
|
const currentWebhookUrl = this.getNodeWebhookUrl('default') as string;
|
||||||
const event = this.getNodeParameter('event') as string;
|
const { appId } = this.getCredentials('hubspotDeveloperApi') as IDataObject;
|
||||||
const webhookUrlUi = this.getNodeWebhookUrl('default') as string;
|
|
||||||
let endpoint = `/webhooks/v1/${app}/settings`;
|
try {
|
||||||
const { webhookUrl , appId } = await hubspotApiRequest.call(this, 'GET', endpoint, {});
|
const { targetUrl } = await hubspotApiRequest.call(this, 'GET', `/webhooks/v3/${appId}/settings`, {});
|
||||||
endpoint = `/webhooks/v1/${app}/subscriptions`;
|
if (targetUrl !== currentWebhookUrl) {
|
||||||
const subscriptions = await hubspotApiRequest.call(this, 'GET', endpoint, {});
|
throw new Error(`The APP ID ${appId} already has a target url ${targetUrl}. Delete it or use another APP ID before executing the trigger. Due to Hubspot API limitations, you can have just one trigger per APP.`);
|
||||||
for (const subscription of subscriptions) {
|
}
|
||||||
if (webhookUrl === webhookUrlUi
|
} catch (error) {
|
||||||
&& appId === app
|
if (error.statusCode === 404) {
|
||||||
&& subscription.subscriptionDetails.subscriptionType === event
|
return false;
|
||||||
&& subscription.enabled === true) {
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// if the app is using the current webhook url. Delete everything and create it again with the current events
|
||||||
|
|
||||||
|
const { results: subscriptions } = await hubspotApiRequest.call(this, 'GET', `/webhooks/v3/${appId}/subscriptions`, {});
|
||||||
|
|
||||||
|
// delete all subscriptions
|
||||||
|
for (const subscription of subscriptions) {
|
||||||
|
await hubspotApiRequest.call(this, 'DELETE', `/webhooks/v3/${appId}/subscriptions/${subscription.id}`, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
await hubspotApiRequest.call(this, 'DELETE', `/webhooks/v3/${appId}/settings`, {});
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
async create(this: IHookFunctions): Promise<boolean> {
|
async create(this: IHookFunctions): Promise<boolean> {
|
||||||
const webhookUrl = this.getNodeWebhookUrl('default');
|
const webhookUrl = this.getNodeWebhookUrl('default');
|
||||||
const app = this.getNodeParameter('appId') as string;
|
const { appId } = this.getCredentials('hubspotDeveloperApi') as IDataObject;
|
||||||
const event = this.getNodeParameter('event') as string;
|
const events = (this.getNodeParameter('eventsUi') as IDataObject || {}).eventValues as IDataObject[] || [];
|
||||||
const additionalFields = this.getNodeParameter('additionalFields') as IDataObject;
|
const additionalFields = this.getNodeParameter('additionalFields') as IDataObject;
|
||||||
const propertyEvents = [
|
let endpoint = `/webhooks/v3/${appId}/settings`;
|
||||||
'contact.propertyChange',
|
|
||||||
'company.propertyChange',
|
|
||||||
'deal.propertyChange',
|
|
||||||
];
|
|
||||||
let endpoint = `/webhooks/v1/${app}/settings`;
|
|
||||||
let body: IDataObject = {
|
let body: IDataObject = {
|
||||||
webhookUrl,
|
targetUrl: webhookUrl,
|
||||||
maxConcurrentRequests: additionalFields.maxConcurrentRequests || 5,
|
maxConcurrentRequests: additionalFields.maxConcurrentRequests || 5,
|
||||||
};
|
};
|
||||||
|
|
||||||
await hubspotApiRequest.call(this, 'PUT', endpoint, body);
|
await hubspotApiRequest.call(this, 'PUT', endpoint, body);
|
||||||
|
|
||||||
endpoint = `/webhooks/v1/${app}/subscriptions`;
|
endpoint = `/webhooks/v3/${appId}/subscriptions`;
|
||||||
body = {
|
|
||||||
subscriptionDetails: {
|
if (Array.isArray(events) && events.length === 0) {
|
||||||
subscriptionType: event,
|
throw new Error(`You must define at least one event`);
|
||||||
},
|
|
||||||
enabled: true,
|
|
||||||
};
|
|
||||||
if (propertyEvents.includes(event)) {
|
|
||||||
const property = this.getNodeParameter('property') as string;
|
|
||||||
//@ts-ignore
|
|
||||||
body.subscriptionDetails.propertyName = property;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const responseData = await hubspotApiRequest.call(this, 'POST', endpoint, body);
|
for (const event of events) {
|
||||||
|
body = {
|
||||||
if (responseData.id === undefined) {
|
eventType: event.name,
|
||||||
// Required data is missing so was not successful
|
active: true,
|
||||||
return false;
|
};
|
||||||
|
if (propertyEvents.includes(event.name as string)) {
|
||||||
|
const property = event.property;
|
||||||
|
body.propertyName = property;
|
||||||
|
}
|
||||||
|
await hubspotApiRequest.call(this, 'POST', endpoint, body);
|
||||||
}
|
}
|
||||||
|
|
||||||
const webhookData = this.getWorkflowStaticData('node');
|
|
||||||
webhookData.webhookId = responseData.id as string;
|
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
async delete(this: IHookFunctions): Promise<boolean> {
|
async delete(this: IHookFunctions): Promise<boolean> {
|
||||||
const webhookData = this.getWorkflowStaticData('node');
|
const { appId } = this.getCredentials('hubspotDeveloperApi') as IDataObject;
|
||||||
const app = this.getNodeParameter('appId') as string;
|
|
||||||
if (webhookData.webhookId !== undefined) {
|
|
||||||
const endpoint = `/webhooks/v1/${app}/subscriptions/${webhookData.webhookId}`;
|
|
||||||
|
|
||||||
const body = {};
|
const { results: subscriptions } = await hubspotApiRequest.call(this, 'GET', `/webhooks/v3/${appId}/subscriptions`, {});
|
||||||
|
|
||||||
try {
|
for (const subscription of subscriptions) {
|
||||||
await hubspotApiRequest.call(this, 'DELETE', endpoint, body);
|
await hubspotApiRequest.call(this, 'DELETE', `/webhooks/v3/${appId}/subscriptions/${subscription.id}`, {});
|
||||||
} catch (e) {
|
}
|
||||||
return false;
|
|
||||||
}
|
try {
|
||||||
// Remove from the static workflow data so that it is clear
|
await hubspotApiRequest.call(this, 'DELETE', `/webhooks/v3/${appId}/settings`, {});
|
||||||
// that no webhooks are registred anymore
|
} catch (e) {
|
||||||
delete webhookData.webhookId;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
@ -265,7 +372,7 @@ export class HubspotTrigger implements INodeType {
|
||||||
|
|
||||||
if (credentials.clientSecret !== '') {
|
if (credentials.clientSecret !== '') {
|
||||||
const hash = `${credentials!.clientSecret}${JSON.stringify(bodyData)}`;
|
const hash = `${credentials!.clientSecret}${JSON.stringify(bodyData)}`;
|
||||||
const signature = createHash('sha256').update(hash).digest('hex');
|
const signature = createHash('sha256').update(hash).digest('hex');
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
if (signature !== headerData['x-hubspot-signature']) {
|
if (signature !== headerData['x-hubspot-signature']) {
|
||||||
return {};
|
return {};
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import {
|
import {
|
||||||
INodeProperties,
|
INodeProperties,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
export const ticketOperations = [
|
export const ticketOperations = [
|
||||||
{
|
{
|
||||||
|
@ -48,9 +48,9 @@ export const ticketOperations = [
|
||||||
|
|
||||||
export const ticketFields = [
|
export const ticketFields = [
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
/* ticket:create */
|
/* ticket:create */
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
{
|
{
|
||||||
displayName: 'Pipeline ID',
|
displayName: 'Pipeline ID',
|
||||||
name: 'pipelineId',
|
name: 'pipelineId',
|
||||||
|
@ -70,7 +70,7 @@ export const ticketFields = [
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
default: '',
|
default: '',
|
||||||
description: 'The ID of the pipeline the ticket is in. ',
|
description: 'The ID of the pipeline the ticket is in.',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Stage ID',
|
displayName: 'Stage ID',
|
||||||
|
@ -94,7 +94,7 @@ export const ticketFields = [
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
default: '',
|
default: '',
|
||||||
description: 'The ID of the pipeline the ticket is in. ',
|
description: 'The ID of the pipeline the ticket is in.',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Ticket Name',
|
displayName: 'Ticket Name',
|
||||||
|
@ -112,7 +112,7 @@ export const ticketFields = [
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
default: '',
|
default: '',
|
||||||
description: 'The ID of the pipeline the ticket is in. ',
|
description: 'The ID of the pipeline the ticket is in.',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Additional Fields',
|
displayName: 'Additional Fields',
|
||||||
|
@ -136,7 +136,7 @@ export const ticketFields = [
|
||||||
name: 'associatedCompanyIds',
|
name: 'associatedCompanyIds',
|
||||||
type: 'multiOptions',
|
type: 'multiOptions',
|
||||||
typeOptions: {
|
typeOptions: {
|
||||||
loadOptionsMethod:'getCompanies' ,
|
loadOptionsMethod: 'getCompanies',
|
||||||
},
|
},
|
||||||
default: [],
|
default: [],
|
||||||
description: 'Companies associated with the ticket',
|
description: 'Companies associated with the ticket',
|
||||||
|
@ -146,7 +146,7 @@ export const ticketFields = [
|
||||||
name: 'associatedContactIds',
|
name: 'associatedContactIds',
|
||||||
type: 'multiOptions',
|
type: 'multiOptions',
|
||||||
typeOptions: {
|
typeOptions: {
|
||||||
loadOptionsMethod:'getContacts' ,
|
loadOptionsMethod: 'getContacts',
|
||||||
},
|
},
|
||||||
default: [],
|
default: [],
|
||||||
description: 'Contacts associated with the ticket',
|
description: 'Contacts associated with the ticket',
|
||||||
|
@ -228,9 +228,9 @@ export const ticketFields = [
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
/* ticket:update */
|
/* ticket:update */
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
{
|
{
|
||||||
displayName: 'Ticket ID',
|
displayName: 'Ticket ID',
|
||||||
name: 'ticketId',
|
name: 'ticketId',
|
||||||
|
@ -247,7 +247,7 @@ export const ticketFields = [
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
default: '',
|
default: '',
|
||||||
description: 'Unique identifier for a particular ticket',
|
description: 'Unique identifier for a particular ticket.',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Update Fields',
|
displayName: 'Update Fields',
|
||||||
|
@ -271,20 +271,20 @@ export const ticketFields = [
|
||||||
name: 'associatedCompanyIds',
|
name: 'associatedCompanyIds',
|
||||||
type: 'multiOptions',
|
type: 'multiOptions',
|
||||||
typeOptions: {
|
typeOptions: {
|
||||||
loadOptionsMethod:'getCompanies' ,
|
loadOptionsMethod: 'getCompanies',
|
||||||
},
|
},
|
||||||
default: [],
|
default: [],
|
||||||
description: 'Companies associated with the ticket',
|
description: 'Companies associated with the ticket.',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Contact Ids',
|
displayName: 'Contact Ids',
|
||||||
name: 'associatedContactIds',
|
name: 'associatedContactIds',
|
||||||
type: 'multiOptions',
|
type: 'multiOptions',
|
||||||
typeOptions: {
|
typeOptions: {
|
||||||
loadOptionsMethod:'getContacts' ,
|
loadOptionsMethod: 'getContacts',
|
||||||
},
|
},
|
||||||
default: [],
|
default: [],
|
||||||
description: 'Contact associated with the ticket',
|
description: 'Contact associated with the ticket.',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Category',
|
displayName: 'Category',
|
||||||
|
@ -294,21 +294,21 @@ export const ticketFields = [
|
||||||
loadOptionsMethod: 'getTicketCategories',
|
loadOptionsMethod: 'getTicketCategories',
|
||||||
},
|
},
|
||||||
default: '',
|
default: '',
|
||||||
description: 'Main reason customer reached out for help',
|
description: 'Main reason customer reached out for help.',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Close Date',
|
displayName: 'Close Date',
|
||||||
name: 'closeDate',
|
name: 'closeDate',
|
||||||
type: 'dateTime',
|
type: 'dateTime',
|
||||||
default: '',
|
default: '',
|
||||||
description: 'The date the ticket was closed',
|
description: 'The date the ticket was closed.',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Create Date',
|
displayName: 'Create Date',
|
||||||
name: 'createDate',
|
name: 'createDate',
|
||||||
type: 'dateTime',
|
type: 'dateTime',
|
||||||
default: '',
|
default: '',
|
||||||
description: 'the date the ticket was created',
|
description: 'The date the ticket was created.',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Description',
|
displayName: 'Description',
|
||||||
|
@ -328,7 +328,7 @@ export const ticketFields = [
|
||||||
loadOptionsMethod: 'getTicketPipelines',
|
loadOptionsMethod: 'getTicketPipelines',
|
||||||
},
|
},
|
||||||
default: '',
|
default: '',
|
||||||
description: 'The ID of the pipeline the ticket is in. ',
|
description: 'The ID of the pipeline the ticket is in.',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Priority',
|
displayName: 'Priority',
|
||||||
|
@ -358,7 +358,7 @@ export const ticketFields = [
|
||||||
loadOptionsMethod: 'getTicketSources',
|
loadOptionsMethod: 'getTicketSources',
|
||||||
},
|
},
|
||||||
default: '',
|
default: '',
|
||||||
description: 'Channel where ticket was originally submitted',
|
description: 'Channel where ticket was originally submitted.',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Ticket Name',
|
displayName: 'Ticket Name',
|
||||||
|
@ -380,176 +380,179 @@ export const ticketFields = [
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
/* -------------------------------------------------------------------------- */
|
|
||||||
/* ticket:get */
|
/* -------------------------------------------------------------------------- */
|
||||||
/* -------------------------------------------------------------------------- */
|
/* ticket:get */
|
||||||
{
|
/* -------------------------------------------------------------------------- */
|
||||||
displayName: 'Ticket ID',
|
{
|
||||||
name: 'ticketId',
|
displayName: 'Ticket ID',
|
||||||
type: 'string',
|
name: 'ticketId',
|
||||||
required: true,
|
type: 'string',
|
||||||
displayOptions: {
|
required: true,
|
||||||
show: {
|
displayOptions: {
|
||||||
resource: [
|
show: {
|
||||||
'ticket',
|
resource: [
|
||||||
],
|
'ticket',
|
||||||
operation: [
|
],
|
||||||
'get',
|
operation: [
|
||||||
],
|
'get',
|
||||||
},
|
],
|
||||||
},
|
|
||||||
default: '',
|
|
||||||
description: 'Unique identifier for a particular ticket',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
displayName: 'Additional Fields',
|
|
||||||
name: 'additionalFields',
|
|
||||||
type: 'collection',
|
|
||||||
placeholder: 'Add Field',
|
|
||||||
default: {},
|
|
||||||
displayOptions: {
|
|
||||||
show: {
|
|
||||||
resource: [
|
|
||||||
'ticket',
|
|
||||||
],
|
|
||||||
operation: [
|
|
||||||
'get',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
displayName: 'Include Deleted',
|
|
||||||
name: 'includeDeleted',
|
|
||||||
type: 'boolean',
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
displayName: 'Properties',
|
|
||||||
name: 'properties',
|
|
||||||
type: 'multiOptions',
|
|
||||||
typeOptions: {
|
|
||||||
loadOptionsMethod: 'getTicketProperties',
|
|
||||||
},
|
},
|
||||||
default: [],
|
},
|
||||||
description: `Used to include specific ticket properties in the results.<br/>
|
default: '',
|
||||||
|
description: 'Unique identifier for a particular ticket',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Additional Fields',
|
||||||
|
name: 'additionalFields',
|
||||||
|
type: 'collection',
|
||||||
|
placeholder: 'Add Field',
|
||||||
|
default: {},
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'ticket',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'get',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
displayName: 'Include Deleted',
|
||||||
|
name: 'includeDeleted',
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Properties',
|
||||||
|
name: 'properties',
|
||||||
|
type: 'multiOptions',
|
||||||
|
typeOptions: {
|
||||||
|
loadOptionsMethod: 'getTicketProperties',
|
||||||
|
},
|
||||||
|
default: [],
|
||||||
|
description: `Used to include specific ticket properties in the results.<br/>
|
||||||
By default, the results will only include ticket ID and will not include the values for any properties for your tickets.<br/>
|
By default, the results will only include ticket ID and will not include the values for any properties for your tickets.<br/>
|
||||||
Including this parameter will include the data for the specified property in the results.<br/>
|
Including this parameter will include the data for the specified property in the results.<br/>
|
||||||
You can include this parameter multiple times to request multiple properties separed by ,.`,
|
You can include this parameter multiple times to request multiple properties separed by ,.`,
|
||||||
},
|
|
||||||
{
|
|
||||||
displayName: 'Properties With History',
|
|
||||||
name: 'propertiesWithHistory',
|
|
||||||
type: 'string',
|
|
||||||
default: '',
|
|
||||||
description: `Works similarly to properties=, but this parameter will include the history for the specified property,<br/>
|
|
||||||
instead of just including the current value. Use this parameter when you need the full history of changes to a property's value.`,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
/* -------------------------------------------------------------------------- */
|
|
||||||
/* ticket:getAll */
|
|
||||||
/* -------------------------------------------------------------------------- */
|
|
||||||
{
|
|
||||||
displayName: 'Return All',
|
|
||||||
name: 'returnAll',
|
|
||||||
type: 'boolean',
|
|
||||||
displayOptions: {
|
|
||||||
show: {
|
|
||||||
resource: [
|
|
||||||
'ticket',
|
|
||||||
],
|
|
||||||
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: [
|
|
||||||
'ticket',
|
|
||||||
],
|
|
||||||
operation: [
|
|
||||||
'getAll',
|
|
||||||
],
|
|
||||||
returnAll: [
|
|
||||||
false,
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
typeOptions: {
|
|
||||||
minValue: 1,
|
|
||||||
maxValue: 250,
|
|
||||||
},
|
|
||||||
default: 100,
|
|
||||||
description: 'How many results to return.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
displayName: 'Additional Fields',
|
|
||||||
name: 'additionalFields',
|
|
||||||
type: 'collection',
|
|
||||||
placeholder: 'Add Field',
|
|
||||||
default: {},
|
|
||||||
displayOptions: {
|
|
||||||
show: {
|
|
||||||
resource: [
|
|
||||||
'ticket',
|
|
||||||
],
|
|
||||||
operation: [
|
|
||||||
'getAll',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
displayName: 'Properties',
|
|
||||||
name: 'properties',
|
|
||||||
type: 'multiOptions',
|
|
||||||
typeOptions: {
|
|
||||||
loadOptionsMethod: 'getTicketProperties',
|
|
||||||
},
|
},
|
||||||
default: [],
|
{
|
||||||
description: `Used to include specific ticket properties in the results.<br/>
|
displayName: 'Properties With History',
|
||||||
|
name: 'propertiesWithHistory',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
description: `Works similarly to properties=, but this parameter will include the history for the specified property,<br/>
|
||||||
|
instead of just including the current value. Use this parameter when you need the full history of changes to a property's value.`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* ticket:getAll */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
{
|
||||||
|
displayName: 'Return All',
|
||||||
|
name: 'returnAll',
|
||||||
|
type: 'boolean',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'ticket',
|
||||||
|
],
|
||||||
|
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: [
|
||||||
|
'ticket',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
returnAll: [
|
||||||
|
false,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
typeOptions: {
|
||||||
|
minValue: 1,
|
||||||
|
maxValue: 250,
|
||||||
|
},
|
||||||
|
default: 100,
|
||||||
|
description: 'How many results to return.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Additional Fields',
|
||||||
|
name: 'additionalFields',
|
||||||
|
type: 'collection',
|
||||||
|
placeholder: 'Add Field',
|
||||||
|
default: {},
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'ticket',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
displayName: 'Properties',
|
||||||
|
name: 'properties',
|
||||||
|
type: 'multiOptions',
|
||||||
|
typeOptions: {
|
||||||
|
loadOptionsMethod: 'getTicketProperties',
|
||||||
|
},
|
||||||
|
default: [],
|
||||||
|
description: `Used to include specific ticket properties in the results.<br/>
|
||||||
By default, the results will only include ticket ID and will not include the values for any properties for your tickets.<br/>
|
By default, the results will only include ticket ID and will not include the values for any properties for your tickets.<br/>
|
||||||
Including this parameter will include the data for the specified property in the results.<br/>
|
Including this parameter will include the data for the specified property in the results.<br/>
|
||||||
You can include this parameter multiple times to request multiple properties separed by ,.`,
|
You can include this parameter multiple times to request multiple properties separed by ,.`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Properties With History',
|
displayName: 'Properties With History',
|
||||||
name: 'propertiesWithHistory',
|
name: 'propertiesWithHistory',
|
||||||
type: 'string',
|
type: 'string',
|
||||||
default: '',
|
default: '',
|
||||||
description: `Works similarly to properties=, but this parameter will include the history for the specified property,<br/>
|
description: `Works similarly to properties=, but this parameter will include the history for the specified property,<br/>
|
||||||
instead of just including the current value. Use this parameter when you need the full history of changes to a property's value.`,
|
instead of just including the current value. Use this parameter when you need the full history of changes to a property's value.`,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
/* -------------------------------------------------------------------------- */
|
|
||||||
/* ticket:delete */
|
/* -------------------------------------------------------------------------- */
|
||||||
/* -------------------------------------------------------------------------- */
|
/* ticket:delete */
|
||||||
{
|
/* -------------------------------------------------------------------------- */
|
||||||
displayName: 'Ticket ID',
|
{
|
||||||
name: 'ticketId',
|
displayName: 'Ticket ID',
|
||||||
type: 'string',
|
name: 'ticketId',
|
||||||
required: true,
|
type: 'string',
|
||||||
displayOptions: {
|
required: true,
|
||||||
show: {
|
displayOptions: {
|
||||||
resource: [
|
show: {
|
||||||
'ticket',
|
resource: [
|
||||||
],
|
'ticket',
|
||||||
operation: [
|
],
|
||||||
'delete',
|
operation: [
|
||||||
],
|
'delete',
|
||||||
},
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: 'Unique identifier for a particular ticket',
|
||||||
},
|
},
|
||||||
default: '',
|
|
||||||
description: 'Unique identifier for a particular ticket',
|
|
||||||
},
|
|
||||||
] as INodeProperties[];
|
] as INodeProperties[];
|
||||||
|
|
Before Width: | Height: | Size: 1,022 B |
1
packages/nodes-base/nodes/Hubspot/hubspot.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 62.883 69.883" fill="#fff" fill-rule="evenodd" stroke="#000" stroke-linecap="round" stroke-linejoin="round"><use xlink:href="#A" x="2.442" y="2.442"/><symbol id="A" overflow="visible"><path d="M55.504 30.401a16.26 16.26 0 0 0-5.904-5.864c-1.865-1.084-3.794-1.773-5.972-2.07v-7.798c2.161-.895 3.558-3.018 3.525-5.357a5.86 5.86 0 0 0-5.859-5.889 5.91 5.91 0 0 0-5.908 5.889c0 2.393 1.27 4.434 3.452 5.357v7.754c-1.808.262-3.562.812-5.195 1.631L12.769 8.247c.146-.552.273-1.123.273-1.724C13.042 2.92 10.122 0 6.519 0A6.52 6.52 0 0 0 0 6.524c0 3.604 2.92 6.524 6.524 6.524a6.47 6.47 0 0 0 3.35-.952l1.367 1.035 18.726 13.501c-.991.908-1.914 1.943-2.651 3.105-1.494 2.368-2.407 4.971-2.407 7.813v.586c.007 1.927.354 3.838 1.025 5.645.566 1.543 1.396 2.949 2.427 4.219l-6.221 6.235c-1.841-.684-3.906-.23-5.298 1.162-.947.942-1.48 2.227-1.475 3.565s.527 2.612 1.479 3.564 2.227 1.48 3.565 1.48a5.01 5.01 0 0 0 3.565-1.48c.942-.952 1.479-2.227 1.475-3.564a5.03 5.03 0 0 0-.234-1.514l6.426-6.426a16.09 16.09 0 0 0 2.856 1.563 16.7 16.7 0 0 0 6.685 1.406h.439a15.76 15.76 0 0 0 7.627-1.929 15.77 15.77 0 0 0 5.977-5.63c1.499-2.393 2.319-5.044 2.319-7.959v-.146c0-2.866-.664-5.508-2.051-7.93zm-7.847 13.487c-1.743 1.938-3.75 3.135-6.016 3.135h-.43c-1.294 0-2.564-.356-3.799-1.011a8.79 8.79 0 0 1-3.33-3.032c-.898-1.27-1.387-2.656-1.387-4.126v-.439c0-1.445.278-2.817.977-4.111.747-1.465 1.758-2.515 3.101-3.389a7.6 7.6 0 0 1 4.297-1.294h.147c1.416 0 2.769.278 4.038.928 1.286.677 2.378 1.67 3.174 2.886a9.18 9.18 0 0 1 1.421 4.053l.034.913c0 1.987-.762 3.828-2.28 5.498z" stroke="none" fill="#f8761f" fill-rule="nonzero"/></symbol></svg>
|
After Width: | Height: | Size: 1.7 KiB |
|
@ -270,7 +270,7 @@ export class If implements INodeType {
|
||||||
startsWith: (value1: NodeParameterValue, value2: NodeParameterValue) => (value1 as string).startsWith(value2 as string),
|
startsWith: (value1: NodeParameterValue, value2: NodeParameterValue) => (value1 as string).startsWith(value2 as string),
|
||||||
isEmpty: (value1: NodeParameterValue) => [undefined, null, ''].includes(value1 as string),
|
isEmpty: (value1: NodeParameterValue) => [undefined, null, ''].includes(value1 as string),
|
||||||
regex: (value1: NodeParameterValue, value2: NodeParameterValue) => {
|
regex: (value1: NodeParameterValue, value2: NodeParameterValue) => {
|
||||||
const regexMatch = (value2 || '').toString().match(new RegExp('^/(.*?)/([gimy]*)$'));
|
const regexMatch = (value2 || '').toString().match(new RegExp('^/(.*?)/([gimusy]*)$'));
|
||||||
|
|
||||||
let regex: RegExp;
|
let regex: RegExp;
|
||||||
if (!regexMatch) {
|
if (!regexMatch) {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import {
|
import {
|
||||||
OptionsWithUri,
|
OptionsWithUri,
|
||||||
} from 'request';
|
} from 'request';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
IExecuteFunctions,
|
IExecuteFunctions,
|
||||||
|
@ -14,7 +14,7 @@ import {
|
||||||
IDataObject,
|
IDataObject,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
export async function jiraSoftwareCloudApiRequest(this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, endpoint: string, method: string, body: any = {}, query?: IDataObject, uri?: string): Promise<any> { // tslint:disable-line:no-any
|
export async function jiraSoftwareCloudApiRequest(this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, endpoint: string, method: string, body: any = {}, query?: IDataObject, uri?: string, option: IDataObject = {}): Promise<any> { // tslint:disable-line:no-any
|
||||||
let data; let domain;
|
let data; let domain;
|
||||||
|
|
||||||
const jiraVersion = this.getNodeParameter('jiraVersion', 0) as string;
|
const jiraVersion = this.getNodeParameter('jiraVersion', 0) as string;
|
||||||
|
@ -43,6 +43,7 @@ export async function jiraSoftwareCloudApiRequest(this: IHookFunctions | IExecut
|
||||||
Authorization: `Basic ${data}`,
|
Authorization: `Basic ${data}`,
|
||||||
Accept: 'application/json',
|
Accept: 'application/json',
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
|
'X-Atlassian-Token': 'no-check',
|
||||||
},
|
},
|
||||||
method,
|
method,
|
||||||
qs: query,
|
qs: query,
|
||||||
|
@ -51,6 +52,18 @@ export async function jiraSoftwareCloudApiRequest(this: IHookFunctions | IExecut
|
||||||
json: true,
|
json: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (Object.keys(option).length !== 0) {
|
||||||
|
Object.assign(options, option);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Object.keys(body).length === 0) {
|
||||||
|
delete options.body;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Object.keys(query || {}).length === 0) {
|
||||||
|
delete options.qs;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return await this.helpers.request!(options);
|
return await this.helpers.request!(options);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -82,7 +95,7 @@ export async function jiraSoftwareCloudApiRequestAllItems(this: IHookFunctions |
|
||||||
query.startAt = 0;
|
query.startAt = 0;
|
||||||
body.startAt = 0;
|
body.startAt = 0;
|
||||||
query.maxResults = 100;
|
query.maxResults = 100;
|
||||||
body.maxResults = 100;
|
body.maxResults = 100;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
responseData = await jiraSoftwareCloudApiRequest.call(this, endpoint, method, body, query);
|
responseData = await jiraSoftwareCloudApiRequest.call(this, endpoint, method, body, query);
|
||||||
|
@ -106,7 +119,7 @@ export function validateJSON(json: string | undefined): any { // tslint:disable-
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function eventExists (currentEvents : string[], webhookEvents: string[]) {
|
export function eventExists(currentEvents: string[], webhookEvents: string[]) {
|
||||||
for (const currentEvent of currentEvents) {
|
for (const currentEvent of currentEvents) {
|
||||||
if (!webhookEvents.includes(currentEvent)) {
|
if (!webhookEvents.includes(currentEvent)) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -115,7 +128,7 @@ export function eventExists (currentEvents : string[], webhookEvents: string[])
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getId (url: string) {
|
export function getId(url: string) {
|
||||||
return url.split('/').pop();
|
return url.split('/').pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
266
packages/nodes-base/nodes/Jira/IssueAttachmentDescription.ts
Normal file
|
@ -0,0 +1,266 @@
|
||||||
|
import {
|
||||||
|
INodeProperties,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
export const issueAttachmentOperations = [
|
||||||
|
{
|
||||||
|
displayName: 'Operation',
|
||||||
|
name: 'operation',
|
||||||
|
type: 'options',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'issueAttachment',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Add',
|
||||||
|
value: 'add',
|
||||||
|
description: 'Add attachment to issue',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Get',
|
||||||
|
value: 'get',
|
||||||
|
description: 'Get an attachment',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Get All',
|
||||||
|
value: 'getAll',
|
||||||
|
description: 'Get all attachments',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Remove',
|
||||||
|
value: 'remove',
|
||||||
|
description: 'Remove an attachment',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
default: 'add',
|
||||||
|
description: 'The operation to perform.',
|
||||||
|
},
|
||||||
|
] as INodeProperties[];
|
||||||
|
|
||||||
|
export const issueAttachmentFields = [
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* issueAttachment:add */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
{
|
||||||
|
displayName: 'Issue Key',
|
||||||
|
name: 'issueKey',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'issueAttachment',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'add',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: 'Issue Key',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Binary Property',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'issueAttachment',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'add',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
name: 'binaryPropertyName',
|
||||||
|
type: 'string',
|
||||||
|
default: 'data',
|
||||||
|
description: 'Object property name which holds binary data.',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* issueAttachment:get */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
{
|
||||||
|
displayName: 'Attachment ID',
|
||||||
|
name: 'attachmentId',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'issueAttachment',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'get',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: 'The ID of the attachment.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Download',
|
||||||
|
name: 'download',
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'issueAttachment',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'get',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Binary Property',
|
||||||
|
name: 'binaryProperty',
|
||||||
|
type: 'string',
|
||||||
|
default: 'data',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'issueAttachment',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'get',
|
||||||
|
],
|
||||||
|
download: [
|
||||||
|
true,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
description: 'Object property name which holds binary data.',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* issueAttachment:getAll */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
{
|
||||||
|
displayName: 'Issue Key',
|
||||||
|
name: 'issueKey',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'issueAttachment',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: 'Issue Key',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Return All',
|
||||||
|
name: 'returnAll',
|
||||||
|
type: 'boolean',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'issueAttachment',
|
||||||
|
],
|
||||||
|
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: [
|
||||||
|
'issueAttachment',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
returnAll: [
|
||||||
|
false,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
typeOptions: {
|
||||||
|
minValue: 1,
|
||||||
|
maxValue: 100,
|
||||||
|
},
|
||||||
|
default: 50,
|
||||||
|
description: 'How many results to return.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Download',
|
||||||
|
name: 'download',
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'issueAttachment',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Binary Property',
|
||||||
|
name: 'binaryProperty',
|
||||||
|
type: 'string',
|
||||||
|
default: 'data',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'issueAttachment',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
download: [
|
||||||
|
true,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
description: 'Object property name which holds binary data.',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* issueAttachment:remove */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
{
|
||||||
|
displayName: 'Attachment ID',
|
||||||
|
name: 'attachmentId',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'issueAttachment',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'remove',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: 'The ID of the attachment.',
|
||||||
|
},
|
||||||
|
] as INodeProperties[];
|
|
@ -63,9 +63,9 @@ export const issueOperations = [
|
||||||
|
|
||||||
export const issueFields = [
|
export const issueFields = [
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
/* issue:create */
|
/* issue:create */
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
{
|
{
|
||||||
displayName: 'Project',
|
displayName: 'Project',
|
||||||
name: 'project',
|
name: 'project',
|
||||||
|
@ -155,7 +155,6 @@ export const issueFields = [
|
||||||
loadOptionsMethod: 'getUsers',
|
loadOptionsMethod: 'getUsers',
|
||||||
},
|
},
|
||||||
default: '',
|
default: '',
|
||||||
required : false,
|
|
||||||
description: 'Assignee',
|
description: 'Assignee',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -163,9 +162,46 @@ export const issueFields = [
|
||||||
name: 'description',
|
name: 'description',
|
||||||
type: 'string',
|
type: 'string',
|
||||||
default: '',
|
default: '',
|
||||||
required : false,
|
|
||||||
description: 'Description',
|
description: 'Description',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Custom Fields',
|
||||||
|
name: 'customFieldsUi',
|
||||||
|
type: 'fixedCollection',
|
||||||
|
default: '',
|
||||||
|
placeholder: 'Add Custom Field',
|
||||||
|
typeOptions: {
|
||||||
|
multipleValues: true,
|
||||||
|
},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'customFieldsValues',
|
||||||
|
displayName: 'Custom Field',
|
||||||
|
values: [
|
||||||
|
{
|
||||||
|
displayName: 'Field ID',
|
||||||
|
name: 'fieldId',
|
||||||
|
type: 'options',
|
||||||
|
typeOptions: {
|
||||||
|
loadOptionsMethod: 'getCustomFields',
|
||||||
|
loadOptionsDependsOn: [
|
||||||
|
'project',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
description: 'ID of the field to set.',
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Field Value',
|
||||||
|
name: 'fieldValue',
|
||||||
|
type: 'string',
|
||||||
|
description: 'Value of the field to set.',
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Labels',
|
displayName: 'Labels',
|
||||||
name: 'labels',
|
name: 'labels',
|
||||||
|
@ -174,9 +210,8 @@ export const issueFields = [
|
||||||
loadOptionsMethod: 'getLabels',
|
loadOptionsMethod: 'getLabels',
|
||||||
},
|
},
|
||||||
default: [],
|
default: [],
|
||||||
required : false,
|
|
||||||
description: 'Labels',
|
description: 'Labels',
|
||||||
displayOptions: {
|
displayOptions: {
|
||||||
show: {
|
show: {
|
||||||
'/jiraVersion': [
|
'/jiraVersion': [
|
||||||
'cloud',
|
'cloud',
|
||||||
|
@ -189,9 +224,8 @@ export const issueFields = [
|
||||||
name: 'serverLabels',
|
name: 'serverLabels',
|
||||||
type: 'string',
|
type: 'string',
|
||||||
default: [],
|
default: [],
|
||||||
required : false,
|
|
||||||
description: 'Labels',
|
description: 'Labels',
|
||||||
displayOptions: {
|
displayOptions: {
|
||||||
show: {
|
show: {
|
||||||
'/jiraVersion': [
|
'/jiraVersion': [
|
||||||
'server',
|
'server',
|
||||||
|
@ -206,7 +240,6 @@ export const issueFields = [
|
||||||
displayName: 'Parent Issue Key',
|
displayName: 'Parent Issue Key',
|
||||||
name: 'parentIssueKey',
|
name: 'parentIssueKey',
|
||||||
type: 'string',
|
type: 'string',
|
||||||
required: false,
|
|
||||||
default: '',
|
default: '',
|
||||||
description: 'Parent Issue Key',
|
description: 'Parent Issue Key',
|
||||||
},
|
},
|
||||||
|
@ -218,7 +251,6 @@ export const issueFields = [
|
||||||
loadOptionsMethod: 'getPriorities',
|
loadOptionsMethod: 'getPriorities',
|
||||||
},
|
},
|
||||||
default: '',
|
default: '',
|
||||||
required : false,
|
|
||||||
description: 'Priority',
|
description: 'Priority',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -226,16 +258,15 @@ export const issueFields = [
|
||||||
name: 'updateHistory',
|
name: 'updateHistory',
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
default: false,
|
default: false,
|
||||||
required : false,
|
|
||||||
description: `Whether the project in which the issue is created is added to the user's<br/>
|
description: `Whether the project in which the issue is created is added to the user's<br/>
|
||||||
Recently viewed project list, as shown under Projects in Jira.`,
|
Recently viewed project list, as shown under Projects in Jira.`,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
/* issue:update */
|
/* issue:update */
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
{
|
{
|
||||||
displayName: 'Issue Key',
|
displayName: 'Issue Key',
|
||||||
name: 'issueKey',
|
name: 'issueKey',
|
||||||
|
@ -279,7 +310,6 @@ export const issueFields = [
|
||||||
loadOptionsMethod: 'getUsers',
|
loadOptionsMethod: 'getUsers',
|
||||||
},
|
},
|
||||||
default: '',
|
default: '',
|
||||||
required : false,
|
|
||||||
description: 'Assignee',
|
description: 'Assignee',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -287,14 +317,50 @@ export const issueFields = [
|
||||||
name: 'description',
|
name: 'description',
|
||||||
type: 'string',
|
type: 'string',
|
||||||
default: '',
|
default: '',
|
||||||
required : false,
|
|
||||||
description: 'Description',
|
description: 'Description',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Custom Fields',
|
||||||
|
name: 'customFieldsUi',
|
||||||
|
type: 'fixedCollection',
|
||||||
|
default: '',
|
||||||
|
placeholder: 'Add Custom Field',
|
||||||
|
typeOptions: {
|
||||||
|
multipleValues: true,
|
||||||
|
},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'customFieldsValues',
|
||||||
|
displayName: 'Custom Field',
|
||||||
|
values: [
|
||||||
|
{
|
||||||
|
displayName: 'Field ID',
|
||||||
|
name: 'fieldId',
|
||||||
|
type: 'options',
|
||||||
|
typeOptions: {
|
||||||
|
loadOptionsMethod: 'getCustomFields',
|
||||||
|
loadOptionsDependsOn: [
|
||||||
|
'issueKey',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
description: 'ID of the field to set.',
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Field Value',
|
||||||
|
name: 'fieldValue',
|
||||||
|
type: 'string',
|
||||||
|
description: 'Value of the field to set.',
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Issue Type',
|
displayName: 'Issue Type',
|
||||||
name: 'issueType',
|
name: 'issueType',
|
||||||
type: 'options',
|
type: 'options',
|
||||||
required: false,
|
|
||||||
typeOptions: {
|
typeOptions: {
|
||||||
loadOptionsMethod: 'getIssueTypes',
|
loadOptionsMethod: 'getIssueTypes',
|
||||||
},
|
},
|
||||||
|
@ -309,9 +375,8 @@ export const issueFields = [
|
||||||
loadOptionsMethod: 'getLabels',
|
loadOptionsMethod: 'getLabels',
|
||||||
},
|
},
|
||||||
default: [],
|
default: [],
|
||||||
required : false,
|
|
||||||
description: 'Labels',
|
description: 'Labels',
|
||||||
displayOptions: {
|
displayOptions: {
|
||||||
show: {
|
show: {
|
||||||
'/jiraVersion': [
|
'/jiraVersion': [
|
||||||
'cloud',
|
'cloud',
|
||||||
|
@ -324,9 +389,8 @@ export const issueFields = [
|
||||||
name: 'serverLabels',
|
name: 'serverLabels',
|
||||||
type: 'string',
|
type: 'string',
|
||||||
default: [],
|
default: [],
|
||||||
required : false,
|
|
||||||
description: 'Labels',
|
description: 'Labels',
|
||||||
displayOptions: {
|
displayOptions: {
|
||||||
show: {
|
show: {
|
||||||
'/jiraVersion': [
|
'/jiraVersion': [
|
||||||
'server',
|
'server',
|
||||||
|
@ -341,7 +405,6 @@ export const issueFields = [
|
||||||
displayName: 'Parent Issue Key',
|
displayName: 'Parent Issue Key',
|
||||||
name: 'parentIssueKey',
|
name: 'parentIssueKey',
|
||||||
type: 'string',
|
type: 'string',
|
||||||
required: false,
|
|
||||||
default: '',
|
default: '',
|
||||||
description: 'Parent Issue Key',
|
description: 'Parent Issue Key',
|
||||||
},
|
},
|
||||||
|
@ -353,14 +416,12 @@ export const issueFields = [
|
||||||
loadOptionsMethod: 'getPriorities',
|
loadOptionsMethod: 'getPriorities',
|
||||||
},
|
},
|
||||||
default: '',
|
default: '',
|
||||||
required : false,
|
|
||||||
description: 'Priority',
|
description: 'Priority',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Summary',
|
displayName: 'Summary',
|
||||||
name: 'summary',
|
name: 'summary',
|
||||||
type: 'string',
|
type: 'string',
|
||||||
required: false,
|
|
||||||
default: '',
|
default: '',
|
||||||
description: 'Summary',
|
description: 'Summary',
|
||||||
},
|
},
|
||||||
|
@ -371,16 +432,15 @@ export const issueFields = [
|
||||||
typeOptions: {
|
typeOptions: {
|
||||||
loadOptionsMethod: 'getTransitions',
|
loadOptionsMethod: 'getTransitions',
|
||||||
},
|
},
|
||||||
required: false,
|
|
||||||
default: '',
|
default: '',
|
||||||
description: 'The ID of the issue status.',
|
description: 'The ID of the issue status.',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
/* issue:delete */
|
/* issue:delete */
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
{
|
{
|
||||||
displayName: 'Issue Key',
|
displayName: 'Issue Key',
|
||||||
name: 'issueKey',
|
name: 'issueKey',
|
||||||
|
@ -418,9 +478,9 @@ export const issueFields = [
|
||||||
description: 'Delete Subtasks',
|
description: 'Delete Subtasks',
|
||||||
},
|
},
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
/* issue:get */
|
/* issue:get */
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
{
|
{
|
||||||
displayName: 'Issue Key',
|
displayName: 'Issue Key',
|
||||||
name: 'issueKey',
|
name: 'issueKey',
|
||||||
|
@ -460,7 +520,6 @@ export const issueFields = [
|
||||||
displayName: 'Expand',
|
displayName: 'Expand',
|
||||||
name: 'expand',
|
name: 'expand',
|
||||||
type: 'string',
|
type: 'string',
|
||||||
required: false,
|
|
||||||
default: '',
|
default: '',
|
||||||
description: `Use expand to include additional information about the issues in the response.<br/>
|
description: `Use expand to include additional information about the issues in the response.<br/>
|
||||||
This parameter accepts a comma-separated list. Expand options include:<br/>
|
This parameter accepts a comma-separated list. Expand options include:<br/>
|
||||||
|
@ -477,7 +536,6 @@ export const issueFields = [
|
||||||
displayName: 'Fields',
|
displayName: 'Fields',
|
||||||
name: 'fields',
|
name: 'fields',
|
||||||
type: 'string',
|
type: 'string',
|
||||||
required: false,
|
|
||||||
default: '',
|
default: '',
|
||||||
description: `A list of fields to return for the issue.<br/>
|
description: `A list of fields to return for the issue.<br/>
|
||||||
This parameter accepts a comma-separated list.<br/>
|
This parameter accepts a comma-separated list.<br/>
|
||||||
|
@ -490,7 +548,6 @@ export const issueFields = [
|
||||||
displayName: 'Fields By Key',
|
displayName: 'Fields By Key',
|
||||||
name: 'fieldsByKey',
|
name: 'fieldsByKey',
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
required: false,
|
|
||||||
default: false,
|
default: false,
|
||||||
description: `Indicates whether fields in fields are referenced by keys rather than IDs.<br/>
|
description: `Indicates whether fields in fields are referenced by keys rather than IDs.<br/>
|
||||||
This parameter is useful where fields have been added by a connect app and a field's key<br/>
|
This parameter is useful where fields have been added by a connect app and a field's key<br/>
|
||||||
|
@ -500,7 +557,6 @@ export const issueFields = [
|
||||||
displayName: 'Properties',
|
displayName: 'Properties',
|
||||||
name: 'properties',
|
name: 'properties',
|
||||||
type: 'string',
|
type: 'string',
|
||||||
required: false,
|
|
||||||
default: '',
|
default: '',
|
||||||
description: `A list of issue properties to return for the issue.<br/>
|
description: `A list of issue properties to return for the issue.<br/>
|
||||||
This parameter accepts a comma-separated list. Allowed values:<br/>
|
This parameter accepts a comma-separated list. Allowed values:<br/>
|
||||||
|
@ -516,7 +572,6 @@ export const issueFields = [
|
||||||
displayName: 'Update History',
|
displayName: 'Update History',
|
||||||
name: 'updateHistory',
|
name: 'updateHistory',
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
required: false,
|
|
||||||
default: false,
|
default: false,
|
||||||
description: `Whether the project in which the issue is created is added to the user's
|
description: `Whether the project in which the issue is created is added to the user's
|
||||||
Recently viewed project list, as shown under Projects in Jira. This also populates the
|
Recently viewed project list, as shown under Projects in Jira. This also populates the
|
||||||
|
@ -525,9 +580,9 @@ export const issueFields = [
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
/* issue:getAll */
|
/* issue:getAll */
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
{
|
{
|
||||||
displayName: 'Return All',
|
displayName: 'Return All',
|
||||||
name: 'returnAll',
|
name: 'returnAll',
|
||||||
|
@ -649,7 +704,6 @@ export const issueFields = [
|
||||||
displayName: 'Fields By Key',
|
displayName: 'Fields By Key',
|
||||||
name: 'fieldsByKey',
|
name: 'fieldsByKey',
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
required: false,
|
|
||||||
default: false,
|
default: false,
|
||||||
description: `Indicates whether fields in fields are referenced by keys rather than IDs.<br/>
|
description: `Indicates whether fields in fields are referenced by keys rather than IDs.<br/>
|
||||||
This parameter is useful where fields have been added by a connect app and a field's key<br/>
|
This parameter is useful where fields have been added by a connect app and a field's key<br/>
|
||||||
|
@ -667,9 +721,9 @@ export const issueFields = [
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
/* issue:changelog */
|
/* issue:changelog */
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
{
|
{
|
||||||
displayName: 'Issue Key',
|
displayName: 'Issue Key',
|
||||||
name: 'issueKey',
|
name: 'issueKey',
|
||||||
|
@ -729,9 +783,9 @@ export const issueFields = [
|
||||||
default: 50,
|
default: 50,
|
||||||
description: 'How many results to return.',
|
description: 'How many results to return.',
|
||||||
},
|
},
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
/* issue:notify */
|
/* issue:notify */
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
{
|
{
|
||||||
displayName: 'Issue Key',
|
displayName: 'Issue Key',
|
||||||
name: 'issueKey',
|
name: 'issueKey',
|
||||||
|
@ -791,7 +845,6 @@ export const issueFields = [
|
||||||
typeOptions: {
|
typeOptions: {
|
||||||
alwaysOpenEditWindow: true,
|
alwaysOpenEditWindow: true,
|
||||||
},
|
},
|
||||||
required: false,
|
|
||||||
default: '',
|
default: '',
|
||||||
description: 'The HTML body of the email notification for the issue.',
|
description: 'The HTML body of the email notification for the issue.',
|
||||||
},
|
},
|
||||||
|
@ -799,7 +852,6 @@ export const issueFields = [
|
||||||
displayName: 'Subject',
|
displayName: 'Subject',
|
||||||
name: 'subject',
|
name: 'subject',
|
||||||
type: 'string',
|
type: 'string',
|
||||||
required: false,
|
|
||||||
default: '',
|
default: '',
|
||||||
description: `The subject of the email notification for the issue. If this is not specified,
|
description: `The subject of the email notification for the issue. If this is not specified,
|
||||||
then the subject is set to the issue key and summary.`,
|
then the subject is set to the issue key and summary.`,
|
||||||
|
@ -811,7 +863,6 @@ export const issueFields = [
|
||||||
typeOptions: {
|
typeOptions: {
|
||||||
alwaysOpenEditWindow: true,
|
alwaysOpenEditWindow: true,
|
||||||
},
|
},
|
||||||
required: false,
|
|
||||||
default: '',
|
default: '',
|
||||||
description: `The subject of the email notification for the issue.
|
description: `The subject of the email notification for the issue.
|
||||||
If this is not specified, then the subject is set to the issue key and summary.`,
|
If this is not specified, then the subject is set to the issue key and summary.`,
|
||||||
|
@ -906,7 +957,6 @@ export const issueFields = [
|
||||||
typeOptions: {
|
typeOptions: {
|
||||||
alwaysOpenEditWindow: true,
|
alwaysOpenEditWindow: true,
|
||||||
},
|
},
|
||||||
required: false,
|
|
||||||
displayOptions: {
|
displayOptions: {
|
||||||
show: {
|
show: {
|
||||||
resource: [
|
resource: [
|
||||||
|
@ -983,7 +1033,6 @@ export const issueFields = [
|
||||||
typeOptions: {
|
typeOptions: {
|
||||||
alwaysOpenEditWindow: true,
|
alwaysOpenEditWindow: true,
|
||||||
},
|
},
|
||||||
required: false,
|
|
||||||
displayOptions: {
|
displayOptions: {
|
||||||
show: {
|
show: {
|
||||||
resource: [
|
resource: [
|
||||||
|
@ -1001,9 +1050,9 @@ export const issueFields = [
|
||||||
description: 'Restricts the notifications to users with the specified permissions.',
|
description: 'Restricts the notifications to users with the specified permissions.',
|
||||||
},
|
},
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
/* issue:transitions */
|
/* issue:transitions */
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
{
|
{
|
||||||
displayName: 'Issue Key',
|
displayName: 'Issue Key',
|
||||||
name: 'issueKey',
|
name: 'issueKey',
|
||||||
|
@ -1043,7 +1092,6 @@ export const issueFields = [
|
||||||
displayName: 'Expand',
|
displayName: 'Expand',
|
||||||
name: 'expand',
|
name: 'expand',
|
||||||
type: 'string',
|
type: 'string',
|
||||||
required: false,
|
|
||||||
default: '',
|
default: '',
|
||||||
description: `Use expand to include additional information about transitions in the response.<br/>
|
description: `Use expand to include additional information about transitions in the response.<br/>
|
||||||
This parameter accepts transitions.fields, which returns information about the fields in the<br/>
|
This parameter accepts transitions.fields, which returns information about the fields in the<br/>
|
||||||
|
@ -1054,7 +1102,6 @@ export const issueFields = [
|
||||||
displayName: 'Transition ID',
|
displayName: 'Transition ID',
|
||||||
name: 'transitionId',
|
name: 'transitionId',
|
||||||
type: 'string',
|
type: 'string',
|
||||||
required: false,
|
|
||||||
default: '',
|
default: '',
|
||||||
description: 'The ID of the transition.',
|
description: 'The ID of the transition.',
|
||||||
},
|
},
|
||||||
|
@ -1062,7 +1109,6 @@ export const issueFields = [
|
||||||
displayName: 'Skip Remote Only Condition',
|
displayName: 'Skip Remote Only Condition',
|
||||||
name: 'skipRemoteOnlyCondition',
|
name: 'skipRemoteOnlyCondition',
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
required: false,
|
|
||||||
default: false,
|
default: false,
|
||||||
description: `Indicates whether transitions with the condition Hide<br/>
|
description: `Indicates whether transitions with the condition Hide<br/>
|
||||||
From User Condition are included in the response.`,
|
From User Condition are included in the response.`,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import {
|
import {
|
||||||
IDataObject,
|
IDataObject,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
export interface IFields {
|
export interface IFields {
|
||||||
assignee?: IDataObject;
|
assignee?: IDataObject;
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
import {
|
import {
|
||||||
|
BINARY_ENCODING,
|
||||||
IExecuteFunctions,
|
IExecuteFunctions,
|
||||||
} from 'n8n-core';
|
} from 'n8n-core';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
IBinaryData,
|
||||||
|
IBinaryKeyData,
|
||||||
IDataObject,
|
IDataObject,
|
||||||
ILoadOptionsFunctions,
|
ILoadOptionsFunctions,
|
||||||
INodeExecutionData,
|
INodeExecutionData,
|
||||||
|
@ -17,10 +20,15 @@ import {
|
||||||
validateJSON,
|
validateJSON,
|
||||||
} from './GenericFunctions';
|
} from './GenericFunctions';
|
||||||
|
|
||||||
|
import {
|
||||||
|
issueAttachmentFields,
|
||||||
|
issueAttachmentOperations,
|
||||||
|
} from './IssueAttachmentDescription';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
issueCommentFields,
|
issueCommentFields,
|
||||||
issueCommentOperations,
|
issueCommentOperations,
|
||||||
} from './IssueCommentDescription';
|
} from './IssueCommentDescription';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
issueFields,
|
issueFields,
|
||||||
|
@ -33,13 +41,13 @@ import {
|
||||||
INotificationRecipients,
|
INotificationRecipients,
|
||||||
INotify,
|
INotify,
|
||||||
NotificationRecipientsRestrictions,
|
NotificationRecipientsRestrictions,
|
||||||
} from './IssueInterface';
|
} from './IssueInterface';
|
||||||
|
|
||||||
export class Jira implements INodeType {
|
export class Jira implements INodeType {
|
||||||
description: INodeTypeDescription = {
|
description: INodeTypeDescription = {
|
||||||
displayName: 'Jira Software',
|
displayName: 'Jira Software',
|
||||||
name: 'jira',
|
name: 'jira',
|
||||||
icon: 'file:jira.png',
|
icon: 'file:jira.svg',
|
||||||
group: ['output'],
|
group: ['output'],
|
||||||
version: 1,
|
version: 1,
|
||||||
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
||||||
|
@ -101,6 +109,11 @@ export class Jira implements INodeType {
|
||||||
value: 'issue',
|
value: 'issue',
|
||||||
description: 'Creates an issue or, where the option to create subtasks is enabled in Jira, a subtask',
|
description: 'Creates an issue or, where the option to create subtasks is enabled in Jira, a subtask',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'Issue Attachment',
|
||||||
|
value: 'issueAttachment',
|
||||||
|
description: 'Add, remove, and get an attachment from an issue.',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'Issue Comment',
|
name: 'Issue Comment',
|
||||||
value: 'issueComment',
|
value: 'issueComment',
|
||||||
|
@ -112,6 +125,8 @@ export class Jira implements INodeType {
|
||||||
},
|
},
|
||||||
...issueOperations,
|
...issueOperations,
|
||||||
...issueFields,
|
...issueFields,
|
||||||
|
...issueAttachmentOperations,
|
||||||
|
...issueAttachmentFields,
|
||||||
...issueCommentOperations,
|
...issueCommentOperations,
|
||||||
...issueCommentFields,
|
...issueCommentFields,
|
||||||
],
|
],
|
||||||
|
@ -176,7 +191,7 @@ export class Jira implements INodeType {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (const issueType of issueTypes) {
|
for (const issueType of issueTypes) {
|
||||||
if (issueType.scope === undefined || issueType.scope.project.id === projectId) {
|
if (issueType.scope !== undefined && issueType.scope.project.id === projectId) {
|
||||||
const issueTypeName = issueType.name;
|
const issueTypeName = issueType.name;
|
||||||
const issueTypeId = issueType.id;
|
const issueTypeId = issueType.id;
|
||||||
|
|
||||||
|
@ -193,7 +208,6 @@ export class Jira implements INodeType {
|
||||||
if (a.name > b.name) { return 1; }
|
if (a.name > b.name) { return 1; }
|
||||||
return 0;
|
return 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
return returnData;
|
return returnData;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -342,6 +356,32 @@ export class Jira implements INodeType {
|
||||||
|
|
||||||
return returnData;
|
return returnData;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Get all the custom fields to display them to user so that he can
|
||||||
|
// select them easily
|
||||||
|
async getCustomFields(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||||
|
const returnData: INodePropertyOptions[] = [];
|
||||||
|
const operation = this.getCurrentNodeParameter('operation') as string;
|
||||||
|
let projectId;
|
||||||
|
if (operation === 'create') {
|
||||||
|
projectId = this.getCurrentNodeParameter('project');
|
||||||
|
} else {
|
||||||
|
const issueKey = this.getCurrentNodeParameter('issueKey');
|
||||||
|
const { fields: { project: { id } } } = await jiraSoftwareCloudApiRequest.call(this, `/api/2/issue/${issueKey}`, 'GET', {}, {});
|
||||||
|
projectId = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fields = await jiraSoftwareCloudApiRequest.call(this, `/api/2/field`, 'GET');
|
||||||
|
for (const field of fields) {
|
||||||
|
if (field.custom === true && field.scope && field.scope.project && field.scope.project.id === projectId) {
|
||||||
|
returnData.push({
|
||||||
|
name: field.name,
|
||||||
|
value: field.id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return returnData;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -356,11 +396,10 @@ export class Jira implements INodeType {
|
||||||
const operation = this.getNodeParameter('operation', 0) as string;
|
const operation = this.getNodeParameter('operation', 0) as string;
|
||||||
const jiraVersion = this.getNodeParameter('jiraVersion', 0) as string;
|
const jiraVersion = this.getNodeParameter('jiraVersion', 0) as string;
|
||||||
|
|
||||||
|
if (resource === 'issue') {
|
||||||
for (let i = 0; i < length; i++) {
|
//https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-rest-api-2-issue-post
|
||||||
if (resource === 'issue') {
|
if (operation === 'create') {
|
||||||
//https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-rest-api-2-issue-post
|
for (let i = 0; i < length; i++) {
|
||||||
if (operation === 'create') {
|
|
||||||
const summary = this.getNodeParameter('summary', i) as string;
|
const summary = this.getNodeParameter('summary', i) as string;
|
||||||
const projectId = this.getNodeParameter('project', i) as string;
|
const projectId = this.getNodeParameter('project', i) as string;
|
||||||
const issueTypeId = this.getNodeParameter('issueType', i) as string;
|
const issueTypeId = this.getNodeParameter('issueType', i) as string;
|
||||||
|
@ -403,6 +442,13 @@ export class Jira implements INodeType {
|
||||||
if (additionalFields.updateHistory) {
|
if (additionalFields.updateHistory) {
|
||||||
qs.updateHistory = additionalFields.updateHistory as boolean;
|
qs.updateHistory = additionalFields.updateHistory as boolean;
|
||||||
}
|
}
|
||||||
|
if (additionalFields.customFieldsUi) {
|
||||||
|
const customFields = (additionalFields.customFieldsUi as IDataObject).customFieldsValues as IDataObject[];
|
||||||
|
if (customFields) {
|
||||||
|
const data = customFields.reduce((obj, value) => Object.assign(obj, { [`${value.fieldId}`]: value.fieldValue }), {});
|
||||||
|
Object.assign(fields, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
const issueTypes = await jiraSoftwareCloudApiRequest.call(this, '/api/2/issuetype', 'GET', body, qs);
|
const issueTypes = await jiraSoftwareCloudApiRequest.call(this, '/api/2/issuetype', 'GET', body, qs);
|
||||||
const subtaskIssues = [];
|
const subtaskIssues = [];
|
||||||
for (const issueType of issueTypes) {
|
for (const issueType of issueTypes) {
|
||||||
|
@ -422,9 +468,12 @@ export class Jira implements INodeType {
|
||||||
}
|
}
|
||||||
body.fields = fields;
|
body.fields = fields;
|
||||||
responseData = await jiraSoftwareCloudApiRequest.call(this, '/api/2/issue', 'POST', body);
|
responseData = await jiraSoftwareCloudApiRequest.call(this, '/api/2/issue', 'POST', body);
|
||||||
|
returnData.push(responseData);
|
||||||
}
|
}
|
||||||
//https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-rest-api-2-issue-issueIdOrKey-put
|
}
|
||||||
if (operation === 'update') {
|
//https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-rest-api-2-issue-issueIdOrKey-put
|
||||||
|
if (operation === 'update') {
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
const issueKey = this.getNodeParameter('issueKey', i) as string;
|
const issueKey = this.getNodeParameter('issueKey', i) as string;
|
||||||
const updateFields = this.getNodeParameter('updateFields', i) as IDataObject;
|
const updateFields = this.getNodeParameter('updateFields', i) as IDataObject;
|
||||||
const body: IIssue = {};
|
const body: IIssue = {};
|
||||||
|
@ -462,6 +511,13 @@ export class Jira implements INodeType {
|
||||||
if (updateFields.description) {
|
if (updateFields.description) {
|
||||||
fields.description = updateFields.description as string;
|
fields.description = updateFields.description as string;
|
||||||
}
|
}
|
||||||
|
if (updateFields.customFieldsUi) {
|
||||||
|
const customFields = (updateFields.customFieldsUi as IDataObject).customFieldsValues as IDataObject[];
|
||||||
|
if (customFields) {
|
||||||
|
const data = customFields.reduce((obj, value) => Object.assign(obj, { [`${value.fieldId}`]: value.fieldValue }), {});
|
||||||
|
Object.assign(fields, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
const issueTypes = await jiraSoftwareCloudApiRequest.call(this, '/api/2/issuetype', 'GET', body);
|
const issueTypes = await jiraSoftwareCloudApiRequest.call(this, '/api/2/issuetype', 'GET', body);
|
||||||
const subtaskIssues = [];
|
const subtaskIssues = [];
|
||||||
for (const issueType of issueTypes) {
|
for (const issueType of issueTypes) {
|
||||||
|
@ -486,10 +542,12 @@ export class Jira implements INodeType {
|
||||||
}
|
}
|
||||||
|
|
||||||
responseData = await jiraSoftwareCloudApiRequest.call(this, `/api/2/issue/${issueKey}`, 'PUT', body);
|
responseData = await jiraSoftwareCloudApiRequest.call(this, `/api/2/issue/${issueKey}`, 'PUT', body);
|
||||||
responseData = { success: true };
|
returnData.push({ success: true });
|
||||||
}
|
}
|
||||||
//https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-rest-api-2-issue-issueIdOrKey-get
|
}
|
||||||
if (operation === 'get') {
|
//https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-rest-api-2-issue-issueIdOrKey-get
|
||||||
|
if (operation === 'get') {
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
const issueKey = this.getNodeParameter('issueKey', i) as string;
|
const issueKey = this.getNodeParameter('issueKey', i) as string;
|
||||||
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
|
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
|
||||||
if (additionalFields.fields) {
|
if (additionalFields.fields) {
|
||||||
|
@ -507,12 +565,13 @@ export class Jira implements INodeType {
|
||||||
if (additionalFields.updateHistory) {
|
if (additionalFields.updateHistory) {
|
||||||
qs.updateHistory = additionalFields.updateHistory as string;
|
qs.updateHistory = additionalFields.updateHistory as string;
|
||||||
}
|
}
|
||||||
|
|
||||||
responseData = await jiraSoftwareCloudApiRequest.call(this, `/api/2/issue/${issueKey}`, 'GET', {}, qs);
|
responseData = await jiraSoftwareCloudApiRequest.call(this, `/api/2/issue/${issueKey}`, 'GET', {}, qs);
|
||||||
|
returnData.push(responseData);
|
||||||
}
|
}
|
||||||
//https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-rest-api-2-search-post
|
}
|
||||||
if (operation === 'getAll') {
|
//https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-rest-api-2-search-post
|
||||||
|
if (operation === 'getAll') {
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
|
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
|
||||||
const options = this.getNodeParameter('options', i) as IDataObject;
|
const options = this.getNodeParameter('options', i) as IDataObject;
|
||||||
const body: IDataObject = {};
|
const body: IDataObject = {};
|
||||||
|
@ -533,21 +592,27 @@ export class Jira implements INodeType {
|
||||||
responseData = await jiraSoftwareCloudApiRequest.call(this, `/api/2/search`, 'POST', body);
|
responseData = await jiraSoftwareCloudApiRequest.call(this, `/api/2/search`, 'POST', body);
|
||||||
responseData = responseData.issues;
|
responseData = responseData.issues;
|
||||||
}
|
}
|
||||||
|
returnData.push.apply(returnData, responseData);
|
||||||
}
|
}
|
||||||
//https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-rest-api-2-issue-issueIdOrKey-changelog-get
|
}
|
||||||
if (operation === 'changelog') {
|
//https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-rest-api-2-issue-issueIdOrKey-changelog-get
|
||||||
|
if (operation === 'changelog') {
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
const issueKey = this.getNodeParameter('issueKey', i) as string;
|
const issueKey = this.getNodeParameter('issueKey', i) as string;
|
||||||
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
|
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
|
||||||
if (returnAll) {
|
if (returnAll) {
|
||||||
responseData = await jiraSoftwareCloudApiRequestAllItems.call(this, 'values',`/api/2/issue/${issueKey}/changelog`, 'GET');
|
responseData = await jiraSoftwareCloudApiRequestAllItems.call(this, 'values', `/api/2/issue/${issueKey}/changelog`, 'GET');
|
||||||
} else {
|
} else {
|
||||||
qs.maxResults = this.getNodeParameter('limit', i) as number;
|
qs.maxResults = this.getNodeParameter('limit', i) as number;
|
||||||
responseData = await jiraSoftwareCloudApiRequest.call(this, `/api/2/issue/${issueKey}/changelog`, 'GET', {}, qs);
|
responseData = await jiraSoftwareCloudApiRequest.call(this, `/api/2/issue/${issueKey}/changelog`, 'GET', {}, qs);
|
||||||
responseData = responseData.values;
|
responseData = responseData.values;
|
||||||
}
|
}
|
||||||
|
returnData.push.apply(returnData, responseData);
|
||||||
}
|
}
|
||||||
//https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-rest-api-2-issue-issueIdOrKey-notify-post
|
}
|
||||||
if (operation === 'notify') {
|
//https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-rest-api-2-issue-issueIdOrKey-notify-post
|
||||||
|
if (operation === 'notify') {
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
const issueKey = this.getNodeParameter('issueKey', i) as string;
|
const issueKey = this.getNodeParameter('issueKey', i) as string;
|
||||||
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
|
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
|
||||||
const jsonActive = this.getNodeParameter('jsonParameters', 0) as boolean;
|
const jsonActive = this.getNodeParameter('jsonParameters', 0) as boolean;
|
||||||
|
@ -606,7 +671,7 @@ export class Jira implements INodeType {
|
||||||
const notificationRecipientsRestrictions: NotificationRecipientsRestrictions = {};
|
const notificationRecipientsRestrictions: NotificationRecipientsRestrictions = {};
|
||||||
if (notificationRecipientsRestrictionsValues) {
|
if (notificationRecipientsRestrictionsValues) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
if (notificationRecipientsRestrictionsValues.groups. length > 0) {
|
if (notificationRecipientsRestrictionsValues.groups.length > 0) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
notificationRecipientsRestrictions.groups = notificationRecipientsRestrictionsValues.groups.map(group => {
|
notificationRecipientsRestrictions.groups = notificationRecipientsRestrictionsValues.groups.map(group => {
|
||||||
return {
|
return {
|
||||||
|
@ -627,10 +692,12 @@ export class Jira implements INodeType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
responseData = await jiraSoftwareCloudApiRequest.call(this, `/api/2/issue/${issueKey}/notify`, 'POST', body, qs);
|
responseData = await jiraSoftwareCloudApiRequest.call(this, `/api/2/issue/${issueKey}/notify`, 'POST', body, qs);
|
||||||
|
returnData.push(responseData);
|
||||||
}
|
}
|
||||||
//https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-rest-api-2-issue-issueIdOrKey-transitions-get
|
}
|
||||||
if (operation === 'transitions') {
|
//https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-rest-api-2-issue-issueIdOrKey-transitions-get
|
||||||
|
if (operation === 'transitions') {
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
const issueKey = this.getNodeParameter('issueKey', i) as string;
|
const issueKey = this.getNodeParameter('issueKey', i) as string;
|
||||||
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
|
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
|
||||||
if (additionalFields.transitionId) {
|
if (additionalFields.transitionId) {
|
||||||
|
@ -644,19 +711,118 @@ export class Jira implements INodeType {
|
||||||
}
|
}
|
||||||
responseData = await jiraSoftwareCloudApiRequest.call(this, `/api/2/issue/${issueKey}/transitions`, 'GET', {}, qs);
|
responseData = await jiraSoftwareCloudApiRequest.call(this, `/api/2/issue/${issueKey}/transitions`, 'GET', {}, qs);
|
||||||
responseData = responseData.transitions;
|
responseData = responseData.transitions;
|
||||||
|
returnData.push.apply(returnData, responseData);
|
||||||
}
|
}
|
||||||
//https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-rest-api-2-issue-issueIdOrKey-delete
|
}
|
||||||
if (operation === 'delete') {
|
//https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-rest-api-2-issue-issueIdOrKey-delete
|
||||||
|
if (operation === 'delete') {
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
const issueKey = this.getNodeParameter('issueKey', i) as string;
|
const issueKey = this.getNodeParameter('issueKey', i) as string;
|
||||||
const deleteSubtasks = this.getNodeParameter('deleteSubtasks', i) as boolean;
|
const deleteSubtasks = this.getNodeParameter('deleteSubtasks', i) as boolean;
|
||||||
qs.deleteSubtasks = deleteSubtasks;
|
qs.deleteSubtasks = deleteSubtasks;
|
||||||
responseData = await jiraSoftwareCloudApiRequest.call(this, `/api/2/issue/${issueKey}`, 'DELETE', {}, qs);
|
responseData = await jiraSoftwareCloudApiRequest.call(this, `/api/2/issue/${issueKey}`, 'DELETE', {}, qs);
|
||||||
|
returnData.push({ success: true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (resource === 'issueComment') {
|
}
|
||||||
//https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issue-comments/#api-rest-api-3-issue-issueidorkey-comment-post
|
if (resource === 'issueAttachment') {
|
||||||
if (operation === 'add') {
|
//https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issue-attachments/#api-rest-api-3-issue-issueidorkey-attachments-post
|
||||||
|
if (operation === 'add') {
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
const binaryPropertyName = this.getNodeParameter('binaryPropertyName', i) as string;
|
||||||
|
const issueKey = this.getNodeParameter('issueKey', i) as string;
|
||||||
|
|
||||||
|
if (items[i].binary === undefined) {
|
||||||
|
throw new Error('No binary data exists on item!');
|
||||||
|
}
|
||||||
|
|
||||||
|
const item = items[i].binary as IBinaryKeyData;
|
||||||
|
|
||||||
|
const binaryData = item[binaryPropertyName] as IBinaryData;
|
||||||
|
|
||||||
|
if (binaryData === undefined) {
|
||||||
|
throw new Error(`No binary data property "${binaryPropertyName}" does not exists on item!`);
|
||||||
|
}
|
||||||
|
|
||||||
|
responseData = await jiraSoftwareCloudApiRequest.call(
|
||||||
|
this,
|
||||||
|
`/api/3/issue/${issueKey}/attachments`,
|
||||||
|
'POST',
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
undefined,
|
||||||
|
{
|
||||||
|
formData: {
|
||||||
|
file: {
|
||||||
|
value: Buffer.from(binaryData.data, BINARY_ENCODING),
|
||||||
|
options: {
|
||||||
|
filename: binaryData.fileName,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
returnData.push.apply(returnData, responseData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issue-attachments/#api-rest-api-3-attachment-id-delete
|
||||||
|
if (operation === 'remove') {
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
const attachmentId = this.getNodeParameter('attachmentId', i) as string;
|
||||||
|
responseData = await jiraSoftwareCloudApiRequest.call(this, `/api/3/attachment/${attachmentId}`, 'DELETE', {}, qs);
|
||||||
|
returnData.push({ success: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issue-attachments/#api-rest-api-3-attachment-id-get
|
||||||
|
if (operation === 'get') {
|
||||||
|
const download = this.getNodeParameter('download', 0) as boolean;
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
const attachmentId = this.getNodeParameter('attachmentId', i) as string;
|
||||||
|
responseData = await jiraSoftwareCloudApiRequest.call(this, `/api/3/attachment/${attachmentId}`, 'GET', {}, qs);
|
||||||
|
returnData.push({ json: responseData });
|
||||||
|
}
|
||||||
|
if (download) {
|
||||||
|
const binaryPropertyName = this.getNodeParameter('binaryProperty', 0) as string;
|
||||||
|
for (const [index, attachment] of returnData.entries()) {
|
||||||
|
returnData[index]['binary'] = {};
|
||||||
|
//@ts-ignore
|
||||||
|
const buffer = await jiraSoftwareCloudApiRequest.call(this, '', 'GET', {}, {}, attachment?.json!.content, { json: false, encoding: null });
|
||||||
|
//@ts-ignore
|
||||||
|
returnData[index]['binary'][binaryPropertyName] = await this.helpers.prepareBinaryData(buffer, attachment.json.filename, attachment.json.mimeType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (operation === 'getAll') {
|
||||||
|
const download = this.getNodeParameter('download', 0) as boolean;
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
const issueKey = this.getNodeParameter('issueKey', i) as string;
|
||||||
|
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
|
||||||
|
const { fields: { attachment } } = await jiraSoftwareCloudApiRequest.call(this, `/api/2/issue/${issueKey}`, 'GET', {}, qs);
|
||||||
|
responseData = attachment;
|
||||||
|
if (returnAll === false) {
|
||||||
|
const limit = this.getNodeParameter('limit', i) as number;
|
||||||
|
responseData = responseData.slice(0, limit);
|
||||||
|
}
|
||||||
|
responseData = responseData.map((data: IDataObject) => ({ json: data }));
|
||||||
|
returnData.push.apply(returnData, responseData);
|
||||||
|
}
|
||||||
|
if (download) {
|
||||||
|
const binaryPropertyName = this.getNodeParameter('binaryProperty', 0) as string;
|
||||||
|
for (const [index, attachment] of returnData.entries()) {
|
||||||
|
returnData[index]['binary'] = {};
|
||||||
|
//@ts-ignore
|
||||||
|
const buffer = await jiraSoftwareCloudApiRequest.call(this, '', 'GET', {}, {}, attachment.json.content, { json: false, encoding: null });
|
||||||
|
//@ts-ignore
|
||||||
|
returnData[index]['binary'][binaryPropertyName] = await this.helpers.prepareBinaryData(buffer, attachment.json.filename, attachment.json.mimeType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resource === 'issueComment') {
|
||||||
|
//https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issue-comments/#api-rest-api-3-issue-issueidorkey-comment-post
|
||||||
|
if (operation === 'add') {
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
const jsonParameters = this.getNodeParameter('jsonParameters', 0) as boolean;
|
const jsonParameters = this.getNodeParameter('jsonParameters', 0) as boolean;
|
||||||
const issueKey = this.getNodeParameter('issueKey', i) as string;
|
const issueKey = this.getNodeParameter('issueKey', i) as string;
|
||||||
const options = this.getNodeParameter('options', i) as IDataObject;
|
const options = this.getNodeParameter('options', i) as IDataObject;
|
||||||
|
@ -697,18 +863,23 @@ export class Jira implements INodeType {
|
||||||
}
|
}
|
||||||
|
|
||||||
responseData = await jiraSoftwareCloudApiRequest.call(this, `/api/3/issue/${issueKey}/comment`, 'POST', body, qs);
|
responseData = await jiraSoftwareCloudApiRequest.call(this, `/api/3/issue/${issueKey}/comment`, 'POST', body, qs);
|
||||||
|
returnData.push(responseData);
|
||||||
}
|
}
|
||||||
//https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-rest-api-2-issue-issueIdOrKey-get
|
}
|
||||||
if (operation === 'get') {
|
//https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-rest-api-2-issue-issueIdOrKey-get
|
||||||
|
if (operation === 'get') {
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
const issueKey = this.getNodeParameter('issueKey', i) as string;
|
const issueKey = this.getNodeParameter('issueKey', i) as string;
|
||||||
const commentId = this.getNodeParameter('commentId', i) as string;
|
const commentId = this.getNodeParameter('commentId', i) as string;
|
||||||
const options = this.getNodeParameter('options', i) as IDataObject;
|
const options = this.getNodeParameter('options', i) as IDataObject;
|
||||||
Object.assign(qs, options);
|
Object.assign(qs, options);
|
||||||
responseData = await jiraSoftwareCloudApiRequest.call(this, `/api/3/issue/${issueKey}/comment/${commentId}`, 'GET', {}, qs);
|
responseData = await jiraSoftwareCloudApiRequest.call(this, `/api/3/issue/${issueKey}/comment/${commentId}`, 'GET', {}, qs);
|
||||||
|
returnData.push(responseData);
|
||||||
}
|
}
|
||||||
//https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issue-comments/#api-rest-api-3-issue-issueidorkey-comment-get
|
}
|
||||||
if (operation === 'getAll') {
|
//https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issue-comments/#api-rest-api-3-issue-issueidorkey-comment-get
|
||||||
|
if (operation === 'getAll') {
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
const issueKey = this.getNodeParameter('issueKey', i) as string;
|
const issueKey = this.getNodeParameter('issueKey', i) as string;
|
||||||
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
|
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
|
||||||
const options = this.getNodeParameter('options', i) as IDataObject;
|
const options = this.getNodeParameter('options', i) as IDataObject;
|
||||||
|
@ -722,16 +893,21 @@ export class Jira implements INodeType {
|
||||||
responseData = await jiraSoftwareCloudApiRequest.call(this, `/api/3/issue/${issueKey}/comment`, 'GET', body, qs);
|
responseData = await jiraSoftwareCloudApiRequest.call(this, `/api/3/issue/${issueKey}/comment`, 'GET', body, qs);
|
||||||
responseData = responseData.comments;
|
responseData = responseData.comments;
|
||||||
}
|
}
|
||||||
|
returnData.push.apply(returnData, responseData);
|
||||||
}
|
}
|
||||||
//https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issue-comments/#api-rest-api-3-issue-issueidorkey-comment-id-delete
|
}
|
||||||
if (operation === 'remove') {
|
//https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issue-comments/#api-rest-api-3-issue-issueidorkey-comment-id-delete
|
||||||
|
if (operation === 'remove') {
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
const issueKey = this.getNodeParameter('issueKey', i) as string;
|
const issueKey = this.getNodeParameter('issueKey', i) as string;
|
||||||
const commentId = this.getNodeParameter('commentId', i) as string;
|
const commentId = this.getNodeParameter('commentId', i) as string;
|
||||||
responseData = await jiraSoftwareCloudApiRequest.call(this, `/api/3/issue/${issueKey}/comment/${commentId}`, 'DELETE', {}, qs);
|
responseData = await jiraSoftwareCloudApiRequest.call(this, `/api/3/issue/${issueKey}/comment/${commentId}`, 'DELETE', {}, qs);
|
||||||
responseData = { success: true };
|
returnData.push({ success: true });
|
||||||
}
|
}
|
||||||
//https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issue-comments/#api-rest-api-3-issue-issueidorkey-comment-id-put
|
}
|
||||||
if (operation === 'update') {
|
//https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issue-comments/#api-rest-api-3-issue-issueidorkey-comment-id-put
|
||||||
|
if (operation === 'update') {
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
const issueKey = this.getNodeParameter('issueKey', i) as string;
|
const issueKey = this.getNodeParameter('issueKey', i) as string;
|
||||||
const commentId = this.getNodeParameter('commentId', i) as string;
|
const commentId = this.getNodeParameter('commentId', i) as string;
|
||||||
const options = this.getNodeParameter('options', i) as IDataObject;
|
const options = this.getNodeParameter('options', i) as IDataObject;
|
||||||
|
@ -771,14 +947,15 @@ export class Jira implements INodeType {
|
||||||
Object.assign(body, { body: json });
|
Object.assign(body, { body: json });
|
||||||
}
|
}
|
||||||
responseData = await jiraSoftwareCloudApiRequest.call(this, `/api/3/issue/${issueKey}/comment/${commentId}`, 'PUT', body, qs);
|
responseData = await jiraSoftwareCloudApiRequest.call(this, `/api/3/issue/${issueKey}/comment/${commentId}`, 'PUT', body, qs);
|
||||||
|
returnData.push(responseData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (Array.isArray(responseData)) {
|
|
||||||
returnData.push.apply(returnData, responseData as IDataObject[]);
|
|
||||||
} else {
|
|
||||||
returnData.push(responseData as IDataObject);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return [this.helpers.returnJsonArray(returnData)];
|
|
||||||
|
if (resource === 'issueAttachment' && (operation === 'getAll' || operation === 'get')) {
|
||||||
|
return this.prepareOutputData(returnData as unknown as INodeExecutionData[]);
|
||||||
|
} else {
|
||||||
|
return [this.helpers.returnJsonArray(returnData)];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -23,7 +23,7 @@ export class JiraTrigger implements INodeType {
|
||||||
description: INodeTypeDescription = {
|
description: INodeTypeDescription = {
|
||||||
displayName: 'Jira Trigger',
|
displayName: 'Jira Trigger',
|
||||||
name: 'jiraTrigger',
|
name: 'jiraTrigger',
|
||||||
icon: 'file:jira.png',
|
icon: 'file:jira.svg',
|
||||||
group: ['trigger'],
|
group: ['trigger'],
|
||||||
version: 1,
|
version: 1,
|
||||||
description: 'Starts the workflow when Jira events occurs.',
|
description: 'Starts the workflow when Jira events occurs.',
|
||||||
|
|
Before Width: | Height: | Size: 872 B |
1
packages/nodes-base/nodes/Jira/jira.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 68.25 71.25" fill="#fff" fill-rule="evenodd" stroke="#000" stroke-linecap="round" stroke-linejoin="round"><use xlink:href="#C" x="3.125" y="3.125"/><defs><linearGradient id="A" x1="91.90%" y1="40.22%" x2="28.49%" y2="81.63%"><stop offset="18%" stop-color="#0052cc"/><stop offset="100%" stop-color="#2684ff"/></linearGradient><linearGradient id="B" x1="8.70%" y1="59.17%" x2="72.26%" y2="17.99%"><stop offset="18%" stop-color="#0052cc"/><stop offset="100%" stop-color="#2684ff"/></linearGradient></defs><symbol id="C" overflow="visible"><g stroke="none" fill-rule="nonzero"><path d="M61.161 30.211L30.95 0 .74 30.211a2.54 2.54 0 0 0 0 3.581l30.211 30.21 30.211-30.21a2.54 2.54 0 0 0 0-3.581zM30.95 41.46l-9.462-9.462 9.462-9.462 9.462 9.462z" fill="#2684ff"/><path d="M30.95 22.599C24.755 16.405 24.724 6.37 30.881.138L10.114 20.774l11.268 11.268z" fill="url(#A)"/><path d="M40.437 31.973L30.95 41.46a15.93 15.93 0 0 1 0 22.536l20.749-20.749z" fill="url(#B)"/></g></symbol></svg>
|
After Width: | Height: | Size: 1 KiB |
145
packages/nodes-base/nodes/Reddit/GenericFunctions.ts
Normal file
|
@ -0,0 +1,145 @@
|
||||||
|
import {
|
||||||
|
IExecuteFunctions,
|
||||||
|
IHookFunctions,
|
||||||
|
} from 'n8n-core';
|
||||||
|
|
||||||
|
import {
|
||||||
|
IDataObject,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
import {
|
||||||
|
OptionsWithUri,
|
||||||
|
} from 'request';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make an authenticated or unauthenticated API request to Reddit.
|
||||||
|
*/
|
||||||
|
export async function redditApiRequest(
|
||||||
|
this: IHookFunctions | IExecuteFunctions,
|
||||||
|
method: string,
|
||||||
|
endpoint: string,
|
||||||
|
qs: IDataObject,
|
||||||
|
): Promise<any> { // tslint:disable-line:no-any
|
||||||
|
|
||||||
|
const resource = this.getNodeParameter('resource', 0) as string;
|
||||||
|
|
||||||
|
const authRequired = ['profile', 'post', 'postComment'].includes(resource);
|
||||||
|
|
||||||
|
qs.api_type = 'json';
|
||||||
|
|
||||||
|
const options: OptionsWithUri = {
|
||||||
|
headers: {
|
||||||
|
'user-agent': 'n8n',
|
||||||
|
},
|
||||||
|
method,
|
||||||
|
uri: authRequired ? `https://oauth.reddit.com/${endpoint}` : `https://www.reddit.com/${endpoint}`,
|
||||||
|
qs,
|
||||||
|
json: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!Object.keys(qs).length) {
|
||||||
|
delete options.qs;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (authRequired) {
|
||||||
|
let response;
|
||||||
|
|
||||||
|
try {
|
||||||
|
response = await this.helpers.requestOAuth2.call(this, 'redditOAuth2Api', options);
|
||||||
|
} catch (error) {
|
||||||
|
if (error.response.body && error.response.body.message) {
|
||||||
|
const message = error.response.body.message;
|
||||||
|
throw new Error(`Reddit error response [${error.statusCode}]: ${message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((response.errors && response.errors.length !== 0) || (response.json && response.json.errors && response.json.errors.length !== 0)) {
|
||||||
|
const errors = response?.errors || response?.json?.errors;
|
||||||
|
const errorMessage = errors.map((error: []) => error.join('-'));
|
||||||
|
|
||||||
|
throw new Error(`Reddit error response [400]: ${errorMessage.join('|')}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
try {
|
||||||
|
return await this.helpers.request.call(this, options);
|
||||||
|
} catch (error) {
|
||||||
|
const errorMessage = error?.response?.body?.message;
|
||||||
|
if (errorMessage) {
|
||||||
|
throw new Error(`Reddit error response [${error.statusCode}]: ${errorMessage}`);
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make an unauthenticated API request to Reddit and return all results.
|
||||||
|
*/
|
||||||
|
export async function redditApiRequestAllItems(
|
||||||
|
this: IHookFunctions | IExecuteFunctions,
|
||||||
|
method: string,
|
||||||
|
endpoint: string,
|
||||||
|
qs: IDataObject,
|
||||||
|
): Promise<any> { // tslint:disable-line:no-any
|
||||||
|
|
||||||
|
let responseData;
|
||||||
|
const returnData: IDataObject[] = [];
|
||||||
|
|
||||||
|
const resource = this.getNodeParameter('resource', 0) as string;
|
||||||
|
const operation = this.getNodeParameter('operation', 0) as string;
|
||||||
|
const returnAll = this.getNodeParameter('returnAll', 0, false) as boolean;
|
||||||
|
|
||||||
|
qs.limit = 100;
|
||||||
|
|
||||||
|
do {
|
||||||
|
responseData = await redditApiRequest.call(this, method, endpoint, qs);
|
||||||
|
if (!Array.isArray(responseData)) {
|
||||||
|
qs.after = responseData.data.after;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (endpoint === 'api/search_subreddits.json') {
|
||||||
|
responseData.subreddits.forEach((child: any) => returnData.push(child)); // tslint:disable-line:no-any
|
||||||
|
} else if (resource === 'postComment' && operation === 'getAll') {
|
||||||
|
responseData[1].data.children.forEach((child: any) => returnData.push(child.data)); // tslint:disable-line:no-any
|
||||||
|
} else {
|
||||||
|
responseData.data.children.forEach((child: any) => returnData.push(child.data)); // tslint:disable-line:no-any
|
||||||
|
}
|
||||||
|
if (qs.limit && returnData.length >= qs.limit && returnAll === false) {
|
||||||
|
return returnData;
|
||||||
|
}
|
||||||
|
|
||||||
|
} while (responseData.data && responseData.data.after);
|
||||||
|
|
||||||
|
return returnData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles a large Reddit listing by returning all items or up to a limit.
|
||||||
|
*/
|
||||||
|
export async function handleListing(
|
||||||
|
this: IExecuteFunctions,
|
||||||
|
i: number,
|
||||||
|
endpoint: string,
|
||||||
|
qs: IDataObject = {},
|
||||||
|
requestMethod: 'GET' | 'POST' = 'GET',
|
||||||
|
): Promise<any> { // tslint:disable-line:no-any
|
||||||
|
|
||||||
|
let responseData;
|
||||||
|
|
||||||
|
const returnAll = this.getNodeParameter('returnAll', i);
|
||||||
|
|
||||||
|
if (returnAll) {
|
||||||
|
responseData = await redditApiRequestAllItems.call(this, requestMethod, endpoint, qs);
|
||||||
|
} else {
|
||||||
|
const limit = this.getNodeParameter('limit', i);
|
||||||
|
qs.limit = limit;
|
||||||
|
responseData = await redditApiRequestAllItems.call(this, requestMethod, endpoint, qs);
|
||||||
|
responseData = responseData.slice(0, limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
return responseData;
|
||||||
|
}
|
231
packages/nodes-base/nodes/Reddit/PostCommentDescription.ts
Normal file
|
@ -0,0 +1,231 @@
|
||||||
|
import {
|
||||||
|
INodeProperties,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
export const postCommentOperations = [
|
||||||
|
{
|
||||||
|
displayName: 'Operation',
|
||||||
|
name: 'operation',
|
||||||
|
type: 'options',
|
||||||
|
default: 'create',
|
||||||
|
description: 'Operation to perform',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Create',
|
||||||
|
value: 'create',
|
||||||
|
description: 'Create a top-level comment in a post',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Get All',
|
||||||
|
value: 'getAll',
|
||||||
|
description: 'Retrieve all comments in a post',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Delete',
|
||||||
|
value: 'delete',
|
||||||
|
description: 'Remove a comment from a post',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Reply',
|
||||||
|
value: 'reply',
|
||||||
|
description: 'Write a reply to a comment in a post',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'postComment',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
] as INodeProperties[];
|
||||||
|
|
||||||
|
export const postCommentFields = [
|
||||||
|
// ----------------------------------
|
||||||
|
// postComment: create
|
||||||
|
// ----------------------------------
|
||||||
|
{
|
||||||
|
displayName: 'Post ID',
|
||||||
|
name: 'postId',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
default: '',
|
||||||
|
description: 'ID of the post to write the comment to. Found in the post URL:<br><code>/r/[subreddit_name]/comments/[post_id]/[post_title]</code>',
|
||||||
|
placeholder: 'l0me7x',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'postComment',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'create',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Comment Text',
|
||||||
|
name: 'commentText',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
default: '',
|
||||||
|
description: 'Text of the comment. Markdown supported.',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'postComment',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'create',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// ----------------------------------
|
||||||
|
// postComment: getAll
|
||||||
|
// ----------------------------------
|
||||||
|
{
|
||||||
|
displayName: 'Subreddit',
|
||||||
|
name: 'subreddit',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
default: '',
|
||||||
|
description: 'The name of subreddit where the post is.',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'postComment',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Post ID',
|
||||||
|
name: 'postId',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
default: '',
|
||||||
|
description: 'ID of the post to get all comments from. Found in the post URL:<br><code>/r/[subreddit_name]/comments/[post_id]/[post_title]</code>',
|
||||||
|
placeholder: 'l0me7x',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'postComment',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Return All',
|
||||||
|
name: 'returnAll',
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
description: 'Return all results.',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'postComment',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Limit',
|
||||||
|
name: 'limit',
|
||||||
|
type: 'number',
|
||||||
|
default: 100,
|
||||||
|
description: 'The number of results to return.',
|
||||||
|
typeOptions: {
|
||||||
|
minValue: 1,
|
||||||
|
maxValue: 100,
|
||||||
|
},
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'postComment',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
returnAll: [
|
||||||
|
false,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// ----------------------------------
|
||||||
|
// postComment: delete
|
||||||
|
// ----------------------------------
|
||||||
|
{
|
||||||
|
displayName: 'Comment ID',
|
||||||
|
name: 'commentId',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
default: '',
|
||||||
|
description: 'ID of the comment to remove. Found in the comment URL:<br><code>/r/[subreddit_name]/comments/[post_id]/[post_title]/[comment_id]</code>',
|
||||||
|
placeholder: 'gla7fmt',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'postComment',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'delete',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// ----------------------------------
|
||||||
|
// postComment: reply
|
||||||
|
// ----------------------------------
|
||||||
|
{
|
||||||
|
displayName: 'Comment ID',
|
||||||
|
name: 'commentId',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
default: '',
|
||||||
|
description: 'ID of the comment to reply to. To be found in the comment URL:<br><code>www.reddit.com/r/[subreddit_name]/comments/[post_id]/[post_title]/[comment_id]</code>',
|
||||||
|
placeholder: 'gl9iroa',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'postComment',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'reply',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Reply Text',
|
||||||
|
name: 'replyText',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
default: '',
|
||||||
|
description: 'Text of the reply. Markdown supported.',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'postComment',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'reply',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
] as INodeProperties[];
|
351
packages/nodes-base/nodes/Reddit/PostDescription.ts
Normal file
|
@ -0,0 +1,351 @@
|
||||||
|
import {
|
||||||
|
INodeProperties,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
export const postOperations = [
|
||||||
|
{
|
||||||
|
displayName: 'Operation',
|
||||||
|
name: 'operation',
|
||||||
|
type: 'options',
|
||||||
|
default: 'create',
|
||||||
|
description: 'Operation to perform',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Create',
|
||||||
|
value: 'create',
|
||||||
|
description: 'Submit a post to a subreddit',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Delete',
|
||||||
|
value: 'delete',
|
||||||
|
description: 'Delete a post from a subreddit',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Get',
|
||||||
|
value: 'get',
|
||||||
|
description: 'Get a post from a subreddit',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Get All',
|
||||||
|
value: 'getAll',
|
||||||
|
description: 'Get all posts from a subreddit',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'post',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
] as INodeProperties[];
|
||||||
|
|
||||||
|
export const postFields = [
|
||||||
|
// ----------------------------------
|
||||||
|
// post: create
|
||||||
|
// ----------------------------------
|
||||||
|
{
|
||||||
|
displayName: 'Subreddit',
|
||||||
|
name: 'subreddit',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
default: '',
|
||||||
|
description: 'Subreddit to create the post in.',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'post',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'create',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Kind',
|
||||||
|
name: 'kind',
|
||||||
|
type: 'options',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Text Post',
|
||||||
|
value: 'self',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Link Post',
|
||||||
|
value: 'link',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Image Post',
|
||||||
|
value: 'image',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
default: 'self',
|
||||||
|
description: 'The kind of the post to create.',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'post',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'create',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Title',
|
||||||
|
name: 'title',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
default: '',
|
||||||
|
description: 'Title of the post, up to 300 characters long.',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'post',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'create',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'URL',
|
||||||
|
name: 'url',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
default: '',
|
||||||
|
description: 'URL of the post.',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'post',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'create',
|
||||||
|
],
|
||||||
|
kind: [
|
||||||
|
'link',
|
||||||
|
'image',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Text',
|
||||||
|
name: 'text',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
default: '',
|
||||||
|
description: 'Text of the post. Markdown supported.',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'post',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'create',
|
||||||
|
],
|
||||||
|
kind: [
|
||||||
|
'self',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Resubmit',
|
||||||
|
name: 'resubmit',
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
description: 'If toggled on, the URL will be posted even if<br>it was already posted to the subreddit before.<br>Otherwise, the re-posting will trigger an error.',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'post',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'create',
|
||||||
|
],
|
||||||
|
kind: [
|
||||||
|
'link',
|
||||||
|
'image',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// ----------------------------------
|
||||||
|
// post: delete
|
||||||
|
// ----------------------------------
|
||||||
|
{
|
||||||
|
displayName: 'Post ID',
|
||||||
|
name: 'postId',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
default: '',
|
||||||
|
description: 'ID of the post to delete. Found in the post URL:<br><code>/r/[subreddit_name]/comments/[post_id]/[post_title]</code>',
|
||||||
|
placeholder: 'gla7fmt',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'post',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'delete',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// ----------------------------------
|
||||||
|
// post: get
|
||||||
|
// ----------------------------------
|
||||||
|
{
|
||||||
|
displayName: 'Subreddit',
|
||||||
|
name: 'subreddit',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
default: '',
|
||||||
|
description: 'The name of subreddit to retrieve the post from.',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'post',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'get',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Post ID',
|
||||||
|
name: 'postId',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
default: '',
|
||||||
|
description: 'ID of the post to retrieve. Found in the post URL:<br><code>/r/[subreddit_name]/comments/[post_id]/[post_title]</code>',
|
||||||
|
placeholder: 'l0me7x',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'post',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'get',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// ----------------------------------
|
||||||
|
// post: getAll
|
||||||
|
// ----------------------------------
|
||||||
|
{
|
||||||
|
displayName: 'Subreddit',
|
||||||
|
name: 'subreddit',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
default: '',
|
||||||
|
description: 'The name of subreddit to retrieve the posts from.',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'post',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Return All',
|
||||||
|
name: 'returnAll',
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
description: 'Return all results.',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'post',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Limit',
|
||||||
|
name: 'limit',
|
||||||
|
type: 'number',
|
||||||
|
default: 100,
|
||||||
|
description: 'The number of results to return.',
|
||||||
|
typeOptions: {
|
||||||
|
minValue: 1,
|
||||||
|
maxValue: 100,
|
||||||
|
},
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'post',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
returnAll: [
|
||||||
|
false,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Filters',
|
||||||
|
name: 'filters',
|
||||||
|
type: 'collection',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'post',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: {},
|
||||||
|
placeholder: 'Add Field',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
displayName: 'Category',
|
||||||
|
name: 'category',
|
||||||
|
type: 'options',
|
||||||
|
required: true,
|
||||||
|
default: 'top',
|
||||||
|
description: 'Category of the posts to retrieve.',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Top Posts',
|
||||||
|
value: 'top',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Hot Posts',
|
||||||
|
value: 'hot',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'New Posts',
|
||||||
|
value: 'new',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Rising Posts',
|
||||||
|
value: 'rising',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
] as INodeProperties[];
|
80
packages/nodes-base/nodes/Reddit/ProfileDescription.ts
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
import {
|
||||||
|
INodeProperties,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
export const profileOperations = [
|
||||||
|
{
|
||||||
|
displayName: 'Operation',
|
||||||
|
name: 'operation',
|
||||||
|
type: 'options',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'profile',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Get',
|
||||||
|
value: 'get',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
default: 'get',
|
||||||
|
description: 'Operation to perform',
|
||||||
|
},
|
||||||
|
] as INodeProperties[];
|
||||||
|
|
||||||
|
|
||||||
|
export const profileFields = [
|
||||||
|
{
|
||||||
|
displayName: 'Details',
|
||||||
|
name: 'details',
|
||||||
|
type: 'options',
|
||||||
|
required: true,
|
||||||
|
default: 'identity',
|
||||||
|
description: 'Details of my account to retrieve.',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Identity',
|
||||||
|
value: 'identity',
|
||||||
|
description: 'Return the identity of the logged-in user',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Blocked Users',
|
||||||
|
value: 'blockedUsers',
|
||||||
|
description: 'Return the blocked users of the logged-in user',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Friends',
|
||||||
|
value: 'friends',
|
||||||
|
description: 'Return the friends of the logged-in user',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Karma',
|
||||||
|
value: 'karma',
|
||||||
|
description: 'Return the subreddit karma for the logged-in user',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Preferences',
|
||||||
|
value: 'prefs',
|
||||||
|
description: 'Return the settings preferences of the logged-in user',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Trophies',
|
||||||
|
value: 'trophies',
|
||||||
|
description: 'Return the trophies of the logged-in user',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'profile',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'get',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
] as INodeProperties[];
|
425
packages/nodes-base/nodes/Reddit/Reddit.node.ts
Normal file
|
@ -0,0 +1,425 @@
|
||||||
|
import {
|
||||||
|
IExecuteFunctions,
|
||||||
|
} from 'n8n-core';
|
||||||
|
|
||||||
|
import {
|
||||||
|
IDataObject,
|
||||||
|
INodeExecutionData,
|
||||||
|
INodeType,
|
||||||
|
INodeTypeDescription,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
import {
|
||||||
|
handleListing,
|
||||||
|
redditApiRequest,
|
||||||
|
} from './GenericFunctions';
|
||||||
|
|
||||||
|
import {
|
||||||
|
postCommentFields,
|
||||||
|
postCommentOperations,
|
||||||
|
} from './PostCommentDescription';
|
||||||
|
|
||||||
|
import {
|
||||||
|
postFields,
|
||||||
|
postOperations,
|
||||||
|
} from './PostDescription';
|
||||||
|
|
||||||
|
import {
|
||||||
|
profileFields,
|
||||||
|
profileOperations,
|
||||||
|
} from './ProfileDescription';
|
||||||
|
|
||||||
|
import {
|
||||||
|
subredditFields,
|
||||||
|
subredditOperations,
|
||||||
|
} from './SubredditDescription';
|
||||||
|
|
||||||
|
import {
|
||||||
|
userFields,
|
||||||
|
userOperations,
|
||||||
|
} from './UserDescription';
|
||||||
|
|
||||||
|
export class Reddit implements INodeType {
|
||||||
|
description: INodeTypeDescription = {
|
||||||
|
displayName: 'Reddit',
|
||||||
|
name: 'reddit',
|
||||||
|
icon: 'file:reddit.svg',
|
||||||
|
group: ['transform'],
|
||||||
|
version: 1,
|
||||||
|
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
||||||
|
description: 'Consume the Reddit API',
|
||||||
|
defaults: {
|
||||||
|
name: 'Reddit',
|
||||||
|
color: '#ff5700',
|
||||||
|
},
|
||||||
|
inputs: ['main'],
|
||||||
|
outputs: ['main'],
|
||||||
|
credentials: [
|
||||||
|
{
|
||||||
|
name: 'redditOAuth2Api',
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'postComment',
|
||||||
|
'post',
|
||||||
|
'profile',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
properties: [
|
||||||
|
{
|
||||||
|
displayName: 'Resource',
|
||||||
|
name: 'resource',
|
||||||
|
type: 'options',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Post',
|
||||||
|
value: 'post',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Post Comment',
|
||||||
|
value: 'postComment',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Profile',
|
||||||
|
value: 'profile',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Subreddit',
|
||||||
|
value: 'subreddit',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'User',
|
||||||
|
value: 'user',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
default: 'post',
|
||||||
|
description: 'Resource to consume',
|
||||||
|
},
|
||||||
|
...postCommentOperations,
|
||||||
|
...postCommentFields,
|
||||||
|
...profileOperations,
|
||||||
|
...profileFields,
|
||||||
|
...subredditOperations,
|
||||||
|
...subredditFields,
|
||||||
|
...postOperations,
|
||||||
|
...postFields,
|
||||||
|
...userOperations,
|
||||||
|
...userFields,
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
||||||
|
const items = this.getInputData();
|
||||||
|
|
||||||
|
const resource = this.getNodeParameter('resource', 0) as string;
|
||||||
|
const operation = this.getNodeParameter('operation', 0) as string;
|
||||||
|
|
||||||
|
let responseData;
|
||||||
|
const returnData: IDataObject[] = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < items.length; i++) {
|
||||||
|
|
||||||
|
// *********************************************************************
|
||||||
|
// post
|
||||||
|
// *********************************************************************
|
||||||
|
|
||||||
|
if (resource === 'post') {
|
||||||
|
|
||||||
|
if (operation === 'create') {
|
||||||
|
// ----------------------------------
|
||||||
|
// post: create
|
||||||
|
// ----------------------------------
|
||||||
|
|
||||||
|
// https://www.reddit.com/dev/api/#POST_api_submit
|
||||||
|
|
||||||
|
const qs: IDataObject = {
|
||||||
|
title: this.getNodeParameter('title', i),
|
||||||
|
sr: this.getNodeParameter('subreddit', i),
|
||||||
|
kind: this.getNodeParameter('kind', i),
|
||||||
|
};
|
||||||
|
|
||||||
|
qs.kind === 'self'
|
||||||
|
? qs.text = this.getNodeParameter('text', i)
|
||||||
|
: qs.url = this.getNodeParameter('url', i);
|
||||||
|
|
||||||
|
if (qs.url) {
|
||||||
|
qs.resubmit = this.getNodeParameter('resubmit', i);
|
||||||
|
}
|
||||||
|
|
||||||
|
responseData = await redditApiRequest.call(this, 'POST', 'api/submit', qs);
|
||||||
|
|
||||||
|
responseData = responseData.json.data;
|
||||||
|
|
||||||
|
} else if (operation === 'delete') {
|
||||||
|
// ----------------------------------
|
||||||
|
// post: delete
|
||||||
|
// ----------------------------------
|
||||||
|
|
||||||
|
// https://www.reddit.com/dev/api/#POST_api_del
|
||||||
|
|
||||||
|
const postTypePrefix = 't3_';
|
||||||
|
|
||||||
|
const qs: IDataObject = {
|
||||||
|
id: postTypePrefix + this.getNodeParameter('postId', i),
|
||||||
|
};
|
||||||
|
|
||||||
|
await redditApiRequest.call(this, 'POST', 'api/del', qs);
|
||||||
|
|
||||||
|
responseData = { success: true };
|
||||||
|
|
||||||
|
} else if (operation === 'get') {
|
||||||
|
// ----------------------------------
|
||||||
|
// post: get
|
||||||
|
// ----------------------------------
|
||||||
|
|
||||||
|
const subreddit = this.getNodeParameter('subreddit', i);
|
||||||
|
const postId = this.getNodeParameter('postId', i) as string;
|
||||||
|
const endpoint = `r/${subreddit}/comments/${postId}.json`;
|
||||||
|
|
||||||
|
responseData = await redditApiRequest.call(this, 'GET', endpoint, {});
|
||||||
|
responseData = responseData[0].data.children[0].data;
|
||||||
|
|
||||||
|
} else if (operation === 'getAll') {
|
||||||
|
// ----------------------------------
|
||||||
|
// post: getAll
|
||||||
|
// ----------------------------------
|
||||||
|
|
||||||
|
// https://www.reddit.com/dev/api/#GET_hot
|
||||||
|
// https://www.reddit.com/dev/api/#GET_new
|
||||||
|
// https://www.reddit.com/dev/api/#GET_rising
|
||||||
|
// https://www.reddit.com/dev/api/#GET_{sort}
|
||||||
|
|
||||||
|
const subreddit = this.getNodeParameter('subreddit', i);
|
||||||
|
let endpoint = `r/${subreddit}.json`;
|
||||||
|
|
||||||
|
const { category } = this.getNodeParameter('filters', i) as { category: string };
|
||||||
|
if (category) {
|
||||||
|
endpoint = `r/${subreddit}/${category}.json`;
|
||||||
|
}
|
||||||
|
|
||||||
|
responseData = await handleListing.call(this, i, endpoint);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (resource === 'postComment') {
|
||||||
|
|
||||||
|
// *********************************************************************
|
||||||
|
// postComment
|
||||||
|
// *********************************************************************
|
||||||
|
|
||||||
|
if (operation === 'create') {
|
||||||
|
// ----------------------------------
|
||||||
|
// postComment: create
|
||||||
|
// ----------------------------------
|
||||||
|
|
||||||
|
// https://www.reddit.com/dev/api/#POST_api_comment
|
||||||
|
|
||||||
|
const postTypePrefix = 't3_';
|
||||||
|
|
||||||
|
const qs: IDataObject = {
|
||||||
|
text: this.getNodeParameter('commentText', i),
|
||||||
|
thing_id: postTypePrefix + this.getNodeParameter('postId', i),
|
||||||
|
};
|
||||||
|
|
||||||
|
responseData = await redditApiRequest.call(this, 'POST', 'api/comment', qs);
|
||||||
|
responseData = responseData.json.data.things[0].data;
|
||||||
|
|
||||||
|
} else if (operation === 'getAll') {
|
||||||
|
// ----------------------------------
|
||||||
|
// postComment: getAll
|
||||||
|
// ----------------------------------
|
||||||
|
|
||||||
|
// https://www.reddit.com/r/{subrreddit}/comments/{postId}.json
|
||||||
|
|
||||||
|
const subreddit = this.getNodeParameter('subreddit', i);
|
||||||
|
const postId = this.getNodeParameter('postId', i) as string;
|
||||||
|
const endpoint = `r/${subreddit}/comments/${postId}.json`;
|
||||||
|
|
||||||
|
responseData = await handleListing.call(this, i, endpoint);
|
||||||
|
|
||||||
|
} else if (operation === 'delete') {
|
||||||
|
// ----------------------------------
|
||||||
|
// postComment: delete
|
||||||
|
// ----------------------------------
|
||||||
|
|
||||||
|
// https://www.reddit.com/dev/api/#POST_api_del
|
||||||
|
|
||||||
|
const commentTypePrefix = 't1_';
|
||||||
|
|
||||||
|
const qs: IDataObject = {
|
||||||
|
id: commentTypePrefix + this.getNodeParameter('commentId', i),
|
||||||
|
};
|
||||||
|
|
||||||
|
await redditApiRequest.call(this, 'POST', 'api/del', qs);
|
||||||
|
|
||||||
|
responseData = { success: true };
|
||||||
|
|
||||||
|
} else if (operation === 'reply') {
|
||||||
|
// ----------------------------------
|
||||||
|
// postComment: reply
|
||||||
|
// ----------------------------------
|
||||||
|
|
||||||
|
// https://www.reddit.com/dev/api/#POST_api_comment
|
||||||
|
|
||||||
|
const commentTypePrefix = 't1_';
|
||||||
|
|
||||||
|
const qs: IDataObject = {
|
||||||
|
text: this.getNodeParameter('replyText', i),
|
||||||
|
thing_id: commentTypePrefix + this.getNodeParameter('commentId', i),
|
||||||
|
};
|
||||||
|
|
||||||
|
responseData = await redditApiRequest.call(this, 'POST', 'api/comment', qs);
|
||||||
|
responseData = responseData.json.data.things[0].data;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (resource === 'profile') {
|
||||||
|
// *********************************************************************
|
||||||
|
// pprofile
|
||||||
|
// *********************************************************************
|
||||||
|
|
||||||
|
if (operation === 'get') {
|
||||||
|
// ----------------------------------
|
||||||
|
// profile: get
|
||||||
|
// ----------------------------------
|
||||||
|
|
||||||
|
// https://www.reddit.com/dev/api/#GET_api_v1_me
|
||||||
|
// https://www.reddit.com/dev/api/#GET_api_v1_me_karma
|
||||||
|
// https://www.reddit.com/dev/api/#GET_api_v1_me_prefs
|
||||||
|
// https://www.reddit.com/dev/api/#GET_api_v1_me_trophies
|
||||||
|
// https://www.reddit.com/dev/api/#GET_prefs_{where}
|
||||||
|
|
||||||
|
const endpoints: { [key: string]: string } = {
|
||||||
|
identity: 'me',
|
||||||
|
blockedUsers: 'me/blocked',
|
||||||
|
friends: 'me/friends',
|
||||||
|
karma: 'me/karma',
|
||||||
|
prefs: 'me/prefs',
|
||||||
|
trophies: 'me/trophies',
|
||||||
|
};
|
||||||
|
|
||||||
|
const details = this.getNodeParameter('details', i) as string;
|
||||||
|
const endpoint = `api/v1/${endpoints[details]}`;
|
||||||
|
responseData = await redditApiRequest.call(this, 'GET', endpoint, {});
|
||||||
|
|
||||||
|
if (details === 'identity') {
|
||||||
|
responseData = responseData.features;
|
||||||
|
} else if (details === 'friends') {
|
||||||
|
responseData = responseData.data.children;
|
||||||
|
if (!responseData.length) {
|
||||||
|
throw new Error('Reddit error response [404]: Not Found');
|
||||||
|
}
|
||||||
|
} else if (details === 'karma') {
|
||||||
|
responseData = responseData.data;
|
||||||
|
if (!responseData.length) {
|
||||||
|
throw new Error('Reddit error response [404]: Not Found');
|
||||||
|
}
|
||||||
|
} else if (details === 'trophies') {
|
||||||
|
responseData = responseData.data.trophies.map((trophy: IDataObject) => trophy.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (resource === 'subreddit') {
|
||||||
|
|
||||||
|
// *********************************************************************
|
||||||
|
// subreddit
|
||||||
|
// *********************************************************************
|
||||||
|
|
||||||
|
if (operation === 'get') {
|
||||||
|
// ----------------------------------
|
||||||
|
// subreddit: get
|
||||||
|
// ----------------------------------
|
||||||
|
|
||||||
|
// https://www.reddit.com/dev/api/#GET_r_{subreddit}_about
|
||||||
|
// https://www.reddit.com/dev/api/#GET_r_{subreddit}_about_rules
|
||||||
|
|
||||||
|
const subreddit = this.getNodeParameter('subreddit', i);
|
||||||
|
const content = this.getNodeParameter('content', i) as string;
|
||||||
|
const endpoint = `r/${subreddit}/about/${content}.json`;
|
||||||
|
|
||||||
|
responseData = await redditApiRequest.call(this, 'GET', endpoint, {});
|
||||||
|
|
||||||
|
if (content === 'rules') {
|
||||||
|
responseData = responseData.rules;
|
||||||
|
} else if (content === 'about') {
|
||||||
|
responseData = responseData.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (operation === 'getAll') {
|
||||||
|
// ----------------------------------
|
||||||
|
// subreddit: getAll
|
||||||
|
// ----------------------------------
|
||||||
|
|
||||||
|
// https://www.reddit.com/dev/api/#GET_api_trending_subreddits
|
||||||
|
// https://www.reddit.com/dev/api/#POST_api_search_subreddits
|
||||||
|
// https://www.reddit.com/r/subreddits.json
|
||||||
|
|
||||||
|
const filters = this.getNodeParameter('filters', i) as IDataObject;
|
||||||
|
|
||||||
|
if (filters.trending) {
|
||||||
|
const returnAll = this.getNodeParameter('returnAll', 0) as boolean;
|
||||||
|
const endpoint = 'api/trending_subreddits.json';
|
||||||
|
responseData = await redditApiRequest.call(this, 'GET', endpoint, {});
|
||||||
|
responseData = responseData.subreddit_names.map((name: string) => ({ name }));
|
||||||
|
if (returnAll === false) {
|
||||||
|
const limit = this.getNodeParameter('limit', 0) as number;
|
||||||
|
responseData = responseData.splice(0, limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (filters.keyword) {
|
||||||
|
const qs: IDataObject = {};
|
||||||
|
qs.query = filters.keyword;
|
||||||
|
|
||||||
|
const endpoint = 'api/search_subreddits.json';
|
||||||
|
responseData = await redditApiRequest.call(this, 'POST', endpoint, qs);
|
||||||
|
|
||||||
|
const returnAll = this.getNodeParameter('returnAll', 0) as boolean;
|
||||||
|
|
||||||
|
if (returnAll === false) {
|
||||||
|
const limit = this.getNodeParameter('limit', 0) as number;
|
||||||
|
responseData = responseData.subreddits.splice(0, limit);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const endpoint = 'r/subreddits.json';
|
||||||
|
responseData = await handleListing.call(this, i, endpoint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (resource === 'user') {
|
||||||
|
// *********************************************************************
|
||||||
|
// user
|
||||||
|
// *********************************************************************
|
||||||
|
|
||||||
|
if (operation === 'get') {
|
||||||
|
|
||||||
|
// ----------------------------------
|
||||||
|
// user: get
|
||||||
|
// ----------------------------------
|
||||||
|
|
||||||
|
// https://www.reddit.com/dev/api/#GET_user_{username}_{where}
|
||||||
|
|
||||||
|
const username = this.getNodeParameter('username', i) as string;
|
||||||
|
const details = this.getNodeParameter('details', i) as string;
|
||||||
|
const endpoint = `user/${username}/${details}.json`;
|
||||||
|
|
||||||
|
responseData = details === 'about'
|
||||||
|
? await redditApiRequest.call(this, 'GET', endpoint, {})
|
||||||
|
: await handleListing.call(this, i, endpoint);
|
||||||
|
|
||||||
|
if (details === 'about') {
|
||||||
|
responseData = responseData.data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Array.isArray(responseData)
|
||||||
|
? returnData.push(...responseData)
|
||||||
|
: returnData.push(responseData);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [this.helpers.returnJsonArray(returnData)];
|
||||||
|
}
|
||||||
|
}
|
162
packages/nodes-base/nodes/Reddit/SubredditDescription.ts
Normal file
|
@ -0,0 +1,162 @@
|
||||||
|
import {
|
||||||
|
INodeProperties,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
export const subredditOperations = [
|
||||||
|
{
|
||||||
|
displayName: 'Operation',
|
||||||
|
name: 'operation',
|
||||||
|
type: 'options',
|
||||||
|
default: 'get',
|
||||||
|
description: 'Operation to perform',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Get',
|
||||||
|
value: 'get',
|
||||||
|
description: 'Retrieve background information about a subreddit.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Get All',
|
||||||
|
value: 'getAll',
|
||||||
|
description: 'Retrieve information about subreddits from all of Reddit.',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'subreddit',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
] as INodeProperties[];
|
||||||
|
|
||||||
|
export const subredditFields = [
|
||||||
|
// ----------------------------------
|
||||||
|
// subreddit: get
|
||||||
|
// ----------------------------------
|
||||||
|
{
|
||||||
|
displayName: 'Content',
|
||||||
|
name: 'content',
|
||||||
|
type: 'options',
|
||||||
|
required: true,
|
||||||
|
default: 'about',
|
||||||
|
description: 'Subreddit content to retrieve.',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'About',
|
||||||
|
value: 'about',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Rules',
|
||||||
|
value: 'rules',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'subreddit',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'get',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Subreddit',
|
||||||
|
name: 'subreddit',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
default: '',
|
||||||
|
description: 'The name of subreddit to retrieve the content from.',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'subreddit',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'get',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// ----------------------------------
|
||||||
|
// subreddit: getAll
|
||||||
|
// ----------------------------------
|
||||||
|
{
|
||||||
|
displayName: 'Return All',
|
||||||
|
name: 'returnAll',
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
description: 'Return all results.',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'subreddit',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Limit',
|
||||||
|
name: 'limit',
|
||||||
|
type: 'number',
|
||||||
|
default: 100,
|
||||||
|
description: 'The number of results to return.',
|
||||||
|
typeOptions: {
|
||||||
|
minValue: 1,
|
||||||
|
maxValue: 100,
|
||||||
|
},
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'subreddit',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
returnAll: [
|
||||||
|
false,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Filters',
|
||||||
|
name: 'filters',
|
||||||
|
type: 'collection',
|
||||||
|
placeholder: 'Add Field',
|
||||||
|
default: {},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
displayName: 'Keyword',
|
||||||
|
name: 'keyword',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
description: 'The keyword for the subreddit search.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Trending',
|
||||||
|
name: 'trending',
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
description: 'Currently trending subreddits in all of Reddit.',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'subreddit',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
] as INodeProperties[];
|
140
packages/nodes-base/nodes/Reddit/UserDescription.ts
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
import {
|
||||||
|
INodeProperties,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
export const userOperations = [
|
||||||
|
{
|
||||||
|
displayName: 'Operation',
|
||||||
|
name: 'operation',
|
||||||
|
type: 'options',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'user',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Get',
|
||||||
|
value: 'get',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
default: 'get',
|
||||||
|
description: 'Operation to perform',
|
||||||
|
},
|
||||||
|
] as INodeProperties[];
|
||||||
|
|
||||||
|
export const userFields = [
|
||||||
|
{
|
||||||
|
displayName: 'Username',
|
||||||
|
name: 'username',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
default: '',
|
||||||
|
description: 'Reddit ID of the user to retrieve.',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'user',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'get',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Details',
|
||||||
|
name: 'details',
|
||||||
|
type: 'options',
|
||||||
|
required: true,
|
||||||
|
default: 'about',
|
||||||
|
description: 'Details of the user to retrieve.',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'About',
|
||||||
|
value: 'about',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Comments',
|
||||||
|
value: 'comments',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Gilded',
|
||||||
|
value: 'gilded',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Overview',
|
||||||
|
value: 'overview',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Submitted',
|
||||||
|
value: 'submitted',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'user',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'get',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Return All',
|
||||||
|
name: 'returnAll',
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
description: 'Return all results.',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'user',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'get',
|
||||||
|
],
|
||||||
|
details: [
|
||||||
|
'overview',
|
||||||
|
'submitted',
|
||||||
|
'comments',
|
||||||
|
'gilded',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Limit',
|
||||||
|
name: 'limit',
|
||||||
|
type: 'number',
|
||||||
|
default: 100,
|
||||||
|
description: 'The number of results to return.',
|
||||||
|
typeOptions: {
|
||||||
|
minValue: 1,
|
||||||
|
maxValue: 100,
|
||||||
|
},
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'user',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'get',
|
||||||
|
],
|
||||||
|
details: [
|
||||||
|
'comments',
|
||||||
|
'gilded',
|
||||||
|
'overview',
|
||||||
|
'submitted',
|
||||||
|
],
|
||||||
|
returnAll: [
|
||||||
|
false,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
] as INodeProperties[];
|
1
packages/nodes-base/nodes/Reddit/reddit.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 513 514" fill="#fff" fill-rule="evenodd" stroke="#000" stroke-linecap="round" stroke-linejoin="round"><use xlink:href="#A" x=".5" y=".5"/><symbol id="A" overflow="visible"><g stroke="none" fill-rule="nonzero"><path d="M0 76.8C0 34.253 34.253 0 76.8 0h358.4C477.747 0 512 34.253 512 76.8v358.4c0 42.547-34.253 76.8-76.8 76.8H76.8C34.253 512 0 477.747 0 435.2z" fill="#ff4500"/><path d="M79 305c0-68.142 78.942-123 177-123s177 54.858 177 123-78.942 123-177 123S79 373.142 79 305z"/><g fill="#ff4500"><path d="M199 347c35 29 79 29 114 0l12 11c-42 35-96 35-138 0z"/><use xlink:href="#C"/><use xlink:href="#C" x="-118"/></g></g><g stroke="#fff" fill="none"><use xlink:href="#C" x="75" y="-160" stroke-width="25"/><path d="M87 282c-45-22-5-92 40-50m298 50c45-22 5-92-40-50m-127-45l24-83 80 16" stroke-width="22"/></g></symbol><defs ><path id="C" d="M287 285a27.94 27.94 0 1 1 56 0 27.94 27.94 0 1 1-56 0z"/></defs></svg>
|
After Width: | Height: | Size: 1,010 B |
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"node": "n8n-nodes-base.securityScorecard",
|
||||||
|
"nodeVersion": "1.0",
|
||||||
|
"codexVersion": "1.0",
|
||||||
|
"categories": [
|
||||||
|
"Development"
|
||||||
|
],
|
||||||
|
"resources": {
|
||||||
|
"credentialDocumentation": [
|
||||||
|
{
|
||||||
|
"url": "https://docs.n8n.io/credentials/securityScorecard"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryDocumentation": [
|
||||||
|
{
|
||||||
|
"url": "https://docs.n8n.io/nodes/n8n-nodes-base.securityScorecard/"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,17 +8,26 @@ import {
|
||||||
|
|
||||||
import {
|
import {
|
||||||
OptionsWithUri,
|
OptionsWithUri,
|
||||||
} from 'request';
|
} from 'request';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Make an API request to SIGNL4
|
* Make an API request to SIGNL4
|
||||||
*
|
*
|
||||||
* @param {IHookFunctions | IExecuteFunctions} this
|
* @param {IHookFunctions | IExecuteFunctions} this
|
||||||
* @param {object} message
|
* @param {string} method
|
||||||
|
* @param {string} contentType
|
||||||
|
* @param {string} body
|
||||||
|
* @param {object} query
|
||||||
|
* @param {string} teamSecret
|
||||||
|
* @param {object} options
|
||||||
* @returns {Promise<any>}
|
* @returns {Promise<any>}
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export async function SIGNL4ApiRequest(this: IExecuteFunctions, method: string, resource: string, body: any = {}, query: IDataObject = {}, uri?: string, option: IDataObject = {}): Promise<any> { // tslint:disable-line:no-any
|
export async function SIGNL4ApiRequest(this: IExecuteFunctions, method: string, body: string, query: IDataObject = {}, option: IDataObject = {}): Promise<any> { // tslint:disable-line:no-any
|
||||||
|
const credentials = this.getCredentials('signl4Api');
|
||||||
|
|
||||||
|
const teamSecret = credentials?.teamSecret as string;
|
||||||
|
|
||||||
let options: OptionsWithUri = {
|
let options: OptionsWithUri = {
|
||||||
headers: {
|
headers: {
|
||||||
|
@ -27,7 +36,7 @@ export async function SIGNL4ApiRequest(this: IExecuteFunctions, method: string,
|
||||||
method,
|
method,
|
||||||
body,
|
body,
|
||||||
qs: query,
|
qs: query,
|
||||||
uri: uri || ``,
|
uri: `https://connect.signl4.com/webhook/${teamSecret}`,
|
||||||
json: true,
|
json: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"node": "n8n-nodes-base.signl4",
|
"node": "n8n-nodes-base.signl4",
|
||||||
"nodeVersion": "1.0",
|
"nodeVersion": "1.1",
|
||||||
"codexVersion": "1.0",
|
"codexVersion": "1.1",
|
||||||
"categories": [
|
"categories": [
|
||||||
"Communication",
|
"Communication",
|
||||||
"Development"
|
"Development"
|
||||||
|
|
|
@ -264,20 +264,18 @@ export class Signl4 implements INodeType {
|
||||||
// Send alert
|
// Send alert
|
||||||
if (operation === 'send') {
|
if (operation === 'send') {
|
||||||
const message = this.getNodeParameter('message', i) as string;
|
const message = this.getNodeParameter('message', i) as string;
|
||||||
const additionalFields = this.getNodeParameter('additionalFields',i) as IDataObject;
|
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
|
||||||
|
|
||||||
const data: IDataObject = {
|
const data: IDataObject = {
|
||||||
message,
|
message,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (additionalFields.alertingScenario) {
|
if (additionalFields.title) {
|
||||||
data['X-S4-AlertingScenario'] = additionalFields.alertingScenario as string;
|
data.title = additionalFields.title as string;
|
||||||
}
|
}
|
||||||
if (additionalFields.externalId) {
|
|
||||||
data['X-S4-ExternalID'] = additionalFields.externalId as string;
|
if (additionalFields.service) {
|
||||||
}
|
data.service = additionalFields.service as string;
|
||||||
if (additionalFields.filtering) {
|
|
||||||
data['X-S4-Filtering'] = (additionalFields.filtering as boolean).toString();
|
|
||||||
}
|
}
|
||||||
if (additionalFields.locationFieldsUi) {
|
if (additionalFields.locationFieldsUi) {
|
||||||
const locationUi = (additionalFields.locationFieldsUi as IDataObject).locationFieldsValues as IDataObject;
|
const locationUi = (additionalFields.locationFieldsUi as IDataObject).locationFieldsValues as IDataObject;
|
||||||
|
@ -285,16 +283,25 @@ export class Signl4 implements INodeType {
|
||||||
data['X-S4-Location'] = `${locationUi.latitude},${locationUi.longitude}`;
|
data['X-S4-Location'] = `${locationUi.latitude},${locationUi.longitude}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (additionalFields.service) {
|
|
||||||
data['X-S4-Service'] = additionalFields.service as string;
|
if (additionalFields.alertingScenario) {
|
||||||
|
data['X-S4-AlertingScenario'] = additionalFields.alertingScenario as string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (additionalFields.filtering) {
|
||||||
|
data['X-S4-Filtering'] = (additionalFields.filtering as boolean).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (additionalFields.externalId) {
|
||||||
|
data['X-S4-ExternalID'] = additionalFields.externalId as string;
|
||||||
|
}
|
||||||
|
|
||||||
data['X-S4-Status'] = 'new';
|
data['X-S4-Status'] = 'new';
|
||||||
if (additionalFields.title) {
|
|
||||||
data['title'] = additionalFields.title as string;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
data['X-S4-SourceSystem'] = 'n8n';
|
||||||
|
|
||||||
|
// Attachments
|
||||||
const attachments = additionalFields.attachmentsUi as IDataObject;
|
const attachments = additionalFields.attachmentsUi as IDataObject;
|
||||||
|
|
||||||
if (attachments) {
|
if (attachments) {
|
||||||
if (attachments.attachmentsBinary && items[i].binary) {
|
if (attachments.attachmentsBinary && items[i].binary) {
|
||||||
|
|
||||||
|
@ -304,14 +311,14 @@ export class Signl4 implements INodeType {
|
||||||
|
|
||||||
if (binaryProperty) {
|
if (binaryProperty) {
|
||||||
|
|
||||||
const supportedFileExtension = ['png', 'jpg', 'txt'];
|
const supportedFileExtension = ['png', 'jpg', 'jpeg', 'bmp', 'gif', 'mp3', 'wav'];
|
||||||
|
|
||||||
if (!supportedFileExtension.includes(binaryProperty.fileExtension as string)) {
|
if (!supportedFileExtension.includes(binaryProperty.fileExtension as string)) {
|
||||||
|
|
||||||
throw new Error(`Invalid extension, just ${supportedFileExtension.join(',')} are supported}`);
|
throw new Error(`Invalid extension, just ${supportedFileExtension.join(',')} are supported}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
data['file'] = {
|
data.attachment = {
|
||||||
value: Buffer.from(binaryProperty.data, BINARY_ENCODING),
|
value: Buffer.from(binaryProperty.data, BINARY_ENCODING),
|
||||||
options: {
|
options: {
|
||||||
filename: binaryProperty.fileName,
|
filename: binaryProperty.fileName,
|
||||||
|
@ -325,18 +332,14 @@ export class Signl4 implements INodeType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const credentials = this.getCredentials('signl4Api');
|
|
||||||
|
|
||||||
const endpoint = `https://connect.signl4.com/webhook/${credentials?.teamSecret}`;
|
|
||||||
|
|
||||||
responseData = await SIGNL4ApiRequest.call(
|
responseData = await SIGNL4ApiRequest.call(
|
||||||
this,
|
this,
|
||||||
'POST',
|
'POST',
|
||||||
'',
|
'',
|
||||||
data,
|
|
||||||
{},
|
|
||||||
endpoint,
|
|
||||||
{},
|
{},
|
||||||
|
{
|
||||||
|
formData: data,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// Resolve alert
|
// Resolve alert
|
||||||
|
@ -348,26 +351,24 @@ export class Signl4 implements INodeType {
|
||||||
|
|
||||||
data['X-S4-Status'] = 'resolved';
|
data['X-S4-Status'] = 'resolved';
|
||||||
|
|
||||||
const credentials = this.getCredentials('signl4Api');
|
data['X-S4-SourceSystem'] = 'n8n';
|
||||||
|
|
||||||
const endpoint = `https://connect.signl4.com/webhook/${credentials?.teamSecret}`;
|
|
||||||
|
|
||||||
responseData = await SIGNL4ApiRequest.call(
|
responseData = await SIGNL4ApiRequest.call(
|
||||||
this,
|
this,
|
||||||
'POST',
|
'POST',
|
||||||
'',
|
'',
|
||||||
data,
|
|
||||||
{},
|
|
||||||
endpoint,
|
|
||||||
{},
|
{},
|
||||||
|
{
|
||||||
|
formData: data,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
if (Array.isArray(responseData)) {
|
||||||
if (Array.isArray(responseData)) {
|
returnData.push.apply(returnData, responseData as IDataObject[]);
|
||||||
returnData.push.apply(returnData, responseData as IDataObject[]);
|
} else if (responseData !== undefined) {
|
||||||
} else if (responseData !== undefined) {
|
returnData.push(responseData as IDataObject);
|
||||||
returnData.push(responseData as IDataObject);
|
}
|
||||||
}
|
}
|
||||||
return [this.helpers.returnJsonArray(returnData)];
|
return [this.helpers.returnJsonArray(returnData)];
|
||||||
}
|
}
|
||||||
|
|
|
@ -487,7 +487,7 @@ export class Switch implements INodeType {
|
||||||
smallerEqual: (value1: NodeParameterValue, value2: NodeParameterValue) => (value1 || 0) <= (value2 || 0),
|
smallerEqual: (value1: NodeParameterValue, value2: NodeParameterValue) => (value1 || 0) <= (value2 || 0),
|
||||||
startsWith: (value1: NodeParameterValue, value2: NodeParameterValue) => (value1 as string).startsWith(value2 as string),
|
startsWith: (value1: NodeParameterValue, value2: NodeParameterValue) => (value1 as string).startsWith(value2 as string),
|
||||||
regex: (value1: NodeParameterValue, value2: NodeParameterValue) => {
|
regex: (value1: NodeParameterValue, value2: NodeParameterValue) => {
|
||||||
const regexMatch = (value2 || '').toString().match(new RegExp('^/(.*?)/([gimy]*)$'));
|
const regexMatch = (value2 || '').toString().match(new RegExp('^/(.*?)/([gimusy]*)$'));
|
||||||
|
|
||||||
let regex: RegExp;
|
let regex: RegExp;
|
||||||
if (!regexMatch) {
|
if (!regexMatch) {
|
||||||
|
|
331
packages/nodes-base/nodes/Tapfiliate/AffiliateDescription.ts
Normal file
|
@ -0,0 +1,331 @@
|
||||||
|
import {
|
||||||
|
INodeProperties,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
export const affiliateOperations = [
|
||||||
|
{
|
||||||
|
displayName: 'Operation',
|
||||||
|
name: 'operation',
|
||||||
|
type: 'options',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'affiliate',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Create',
|
||||||
|
value: 'create',
|
||||||
|
description: 'Create an affiliate',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Delete',
|
||||||
|
value: 'delete',
|
||||||
|
description: 'Delete an affiliate',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Get',
|
||||||
|
value: 'get',
|
||||||
|
description: 'Get an affiliate by ID',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Get All',
|
||||||
|
value: 'getAll',
|
||||||
|
description: 'Get all affiliates',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
default: 'create',
|
||||||
|
description: 'The operation to perform.',
|
||||||
|
},
|
||||||
|
] as INodeProperties[];
|
||||||
|
|
||||||
|
export const affiliateFields = [
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* affiliate:create */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
{
|
||||||
|
displayName: 'Email',
|
||||||
|
name: 'email',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
default: '',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
operation: [
|
||||||
|
'create',
|
||||||
|
],
|
||||||
|
resource: [
|
||||||
|
'affiliate',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
description: `The affiliate’s email.`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'First Name',
|
||||||
|
name: 'firstname',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
operation: [
|
||||||
|
'create',
|
||||||
|
],
|
||||||
|
resource: [
|
||||||
|
'affiliate',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: `The affiliate’s firstname.`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Last Name',
|
||||||
|
name: 'lastname',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
operation: [
|
||||||
|
'create',
|
||||||
|
],
|
||||||
|
resource: [
|
||||||
|
'affiliate',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: `The affiliate’s lastname.`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Additional Fields',
|
||||||
|
name: 'additionalFields',
|
||||||
|
type: 'collection',
|
||||||
|
placeholder: 'Add Field',
|
||||||
|
default: {},
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'affiliate',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'create',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
displayName: 'Address',
|
||||||
|
name: 'addressUi',
|
||||||
|
placeholder: 'Address',
|
||||||
|
type: 'fixedCollection',
|
||||||
|
typeOptions: {
|
||||||
|
multipleValues: false,
|
||||||
|
},
|
||||||
|
default: {},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'addressValues',
|
||||||
|
displayName: 'Address',
|
||||||
|
values: [
|
||||||
|
{
|
||||||
|
displayName: 'Line 1',
|
||||||
|
name: 'address',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Line 2',
|
||||||
|
name: 'address_two',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Postal Code',
|
||||||
|
name: 'postal_code',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'City',
|
||||||
|
name: 'city',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'State',
|
||||||
|
name: 'state',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Country Code',
|
||||||
|
name: 'country',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
description: `The country’s ISO_3166-1 code. <a target="_blank" href="https://en.wikipedia.org/wiki/ISO_3166-1">Codes</a>.`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Company Name',
|
||||||
|
name: 'companyName',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
description: `The affiliate’s company data,`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* affiliate:delete */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
{
|
||||||
|
displayName: 'Affiliate ID',
|
||||||
|
name: 'affiliateId',
|
||||||
|
required: true,
|
||||||
|
type: 'string',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'affiliate',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'delete',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
description: 'The ID of the affiliate.',
|
||||||
|
},
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* affiliate:get */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
{
|
||||||
|
displayName: 'Affiliate ID',
|
||||||
|
name: 'affiliateId',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'affiliate',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'get',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
description: 'The ID of the affiliate.',
|
||||||
|
},
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* affiliate:getAll */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
{
|
||||||
|
displayName: 'Return All',
|
||||||
|
name: 'returnAll',
|
||||||
|
type: 'boolean',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'affiliate',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: false,
|
||||||
|
description: 'If set to true, all the results will be returned.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Limit',
|
||||||
|
name: 'limit',
|
||||||
|
type: 'number',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'affiliate',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
returnAll: [
|
||||||
|
false,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
typeOptions: {
|
||||||
|
minValue: 1,
|
||||||
|
maxValue: 1000,
|
||||||
|
},
|
||||||
|
default: 100,
|
||||||
|
description: 'How many results to return.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Filters',
|
||||||
|
name: 'filters',
|
||||||
|
type: 'collection',
|
||||||
|
placeholder: 'Add Field',
|
||||||
|
default: {},
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'affiliate',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
displayName: 'Affiliate Group ID',
|
||||||
|
name: 'affiliate_group_id',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
description: 'Retrieves affiliates for a certain affiliate group.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Click ID',
|
||||||
|
name: 'click_id',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
description: 'Click ID.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Email',
|
||||||
|
name: 'email',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
description: 'An email address,',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Parent ID',
|
||||||
|
name: 'parentId',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
description: 'Retrieves children for a certain parent affiliate.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Referral Code',
|
||||||
|
name: 'referral_code',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
description: 'An affiliate’s referral code. This corresponds to the value of ref= in their referral link.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Source ID',
|
||||||
|
name: 'source_id',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
description: 'The Source ID.',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
] as INodeProperties[];
|
|
@ -0,0 +1,196 @@
|
||||||
|
import {
|
||||||
|
INodeProperties,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
export const affiliateMetadataOperations = [
|
||||||
|
{
|
||||||
|
displayName: 'Operation',
|
||||||
|
name: 'operation',
|
||||||
|
type: 'options',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'affiliateMetadata',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Add',
|
||||||
|
value: 'add',
|
||||||
|
description: `Add metadata to affiliate`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Remove',
|
||||||
|
value: 'remove',
|
||||||
|
description: `Remove metadata from affiliate`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Update',
|
||||||
|
value: 'update',
|
||||||
|
description: `Update affiliate's metadata`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
default: 'add',
|
||||||
|
description: 'The operation to perform.',
|
||||||
|
},
|
||||||
|
] as INodeProperties[];
|
||||||
|
|
||||||
|
export const affiliateMetadataFields = [
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* affiliateMetadata:add */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
{
|
||||||
|
displayName: 'Affiliate ID',
|
||||||
|
name: 'affiliateId',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'affiliateMetadata',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'add',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
description: 'The ID of the affiliate.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Metadata',
|
||||||
|
name: 'metadataUi',
|
||||||
|
placeholder: 'Add Metadata',
|
||||||
|
type: 'fixedCollection',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'affiliateMetadata',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'add',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
typeOptions: {
|
||||||
|
multipleValues: true,
|
||||||
|
},
|
||||||
|
description: 'Meta data',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'metadataValues',
|
||||||
|
displayName: 'Metadata',
|
||||||
|
values: [
|
||||||
|
{
|
||||||
|
displayName: 'Key',
|
||||||
|
name: 'key',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
description: 'Name of the metadata key to add.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Value',
|
||||||
|
name: 'value',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
description: 'Value to set for the metadata key.',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* ffiliateMetadata:remove */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
{
|
||||||
|
displayName: 'Affiliate ID',
|
||||||
|
name: 'affiliateId',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'affiliateMetadata',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'remove',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
description: 'The ID of the affiliate.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Key',
|
||||||
|
name: 'key',
|
||||||
|
type: 'string',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'affiliateMetadata',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'remove',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: 'Name of the metadata key to remove.',
|
||||||
|
},
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* affiliateMetadata:update */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
{
|
||||||
|
displayName: 'Affiliate ID',
|
||||||
|
name: 'affiliateId',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'affiliateMetadata',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'update',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
description: 'The ID of the affiliate.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Key',
|
||||||
|
name: 'key',
|
||||||
|
type: 'string',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'affiliateMetadata',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'update',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: 'Name of the metadata key to update.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Value',
|
||||||
|
name: 'value',
|
||||||
|
type: 'string',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'affiliateMetadata',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'update',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
description: 'Value to set for the metadata key.',
|
||||||
|
},
|
||||||
|
] as INodeProperties[];
|
78
packages/nodes-base/nodes/Tapfiliate/GenericFunctions.ts
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
import {
|
||||||
|
OptionsWithUri,
|
||||||
|
} from 'request';
|
||||||
|
|
||||||
|
import {
|
||||||
|
IExecuteFunctions,
|
||||||
|
IExecuteSingleFunctions,
|
||||||
|
IHookFunctions,
|
||||||
|
ILoadOptionsFunctions,
|
||||||
|
} from 'n8n-core';
|
||||||
|
|
||||||
|
import {
|
||||||
|
IDataObject,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
export async function tapfiliateApiRequest(this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, method: string, endpoint: string, body: any = {}, qs: IDataObject = {}, uri?: string | undefined, option: IDataObject = {}): Promise<any> { // tslint:disable-line:no-any
|
||||||
|
const credentials = this.getCredentials('tapfiliateApi') as IDataObject;
|
||||||
|
|
||||||
|
const options: OptionsWithUri = {
|
||||||
|
headers: {
|
||||||
|
'Api-Key': credentials.apiKey,
|
||||||
|
},
|
||||||
|
method,
|
||||||
|
qs,
|
||||||
|
body,
|
||||||
|
uri: uri || `https://api.tapfiliate.com/1.6${endpoint}`,
|
||||||
|
json: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (Object.keys(body).length === 0) {
|
||||||
|
delete options.body;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Object.keys(option).length !== 0) {
|
||||||
|
Object.assign(options, option);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return await this.helpers.request!(options);
|
||||||
|
} catch (error) {
|
||||||
|
if (error.statusCode === 404) {
|
||||||
|
throw new Error(
|
||||||
|
`Tapfiliate error response [${error.statusCode}]: Not Found`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error.response && error.response.body && error.response.body.errors) {
|
||||||
|
|
||||||
|
let errors = error.response.body.errors;
|
||||||
|
|
||||||
|
errors = errors.map((e: IDataObject) => e.message);
|
||||||
|
// Try to return the error prettier
|
||||||
|
throw new Error(
|
||||||
|
`Tapfiliate error response [${error.statusCode}]: ${errors.join('|')}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function tapfiliateApiRequestAllItems(this: IExecuteFunctions | ILoadOptionsFunctions, method: string, endpoint: string, body: any = {}, query: IDataObject = {}): Promise<any> { // tslint:disable-line:no-any
|
||||||
|
|
||||||
|
const returnData: IDataObject[] = [];
|
||||||
|
|
||||||
|
let responseData;
|
||||||
|
|
||||||
|
query.page = 1;
|
||||||
|
|
||||||
|
do {
|
||||||
|
responseData = await tapfiliateApiRequest.call(this, method, endpoint, body, query, '', { resolveWithFullResponse: true });
|
||||||
|
returnData.push.apply(returnData, responseData.body);
|
||||||
|
query.page++;
|
||||||
|
|
||||||
|
} while (
|
||||||
|
responseData.headers.link.includes('next')
|
||||||
|
);
|
||||||
|
|
||||||
|
return returnData;
|
||||||
|
}
|
|
@ -0,0 +1,359 @@
|
||||||
|
import {
|
||||||
|
INodeProperties,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
export const programAffiliateOperations = [
|
||||||
|
{
|
||||||
|
displayName: 'Operation',
|
||||||
|
name: 'operation',
|
||||||
|
type: 'options',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'programAffiliate',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Add',
|
||||||
|
value: 'add',
|
||||||
|
description: 'Add affiliate to program',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Approve',
|
||||||
|
value: 'approve',
|
||||||
|
description: 'Approve an affiliate for a program',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Disapprove',
|
||||||
|
value: 'disapprove',
|
||||||
|
description: 'Disapprove an affiliate',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Get',
|
||||||
|
value: 'get',
|
||||||
|
description: 'Get an affiliate in a program',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Get All',
|
||||||
|
value: 'getAll',
|
||||||
|
description: 'Get all affiliates in program',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
default: 'add',
|
||||||
|
description: 'The operation to perform.',
|
||||||
|
},
|
||||||
|
] as INodeProperties[];
|
||||||
|
|
||||||
|
export const programAffiliateFields = [
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* programAffiliate:add */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
{
|
||||||
|
displayName: 'Program ID',
|
||||||
|
name: 'programId',
|
||||||
|
type: 'options',
|
||||||
|
required: true,
|
||||||
|
typeOptions: {
|
||||||
|
loadOptionsMethod: 'getPrograms',
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
operation: [
|
||||||
|
'add',
|
||||||
|
],
|
||||||
|
resource: [
|
||||||
|
'programAffiliate',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
description: `The ID of the Program to add the affiliate to. This ID can be found as part of the URL when viewing the program on the platform.`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Affiliate ID',
|
||||||
|
name: 'affiliateId',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'programAffiliate',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'add',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
description: 'The ID of the affiliate.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Additional Fields',
|
||||||
|
name: 'additionalFields',
|
||||||
|
type: 'collection',
|
||||||
|
placeholder: 'Add Field',
|
||||||
|
default: {},
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'programAffiliate',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'add',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
displayName: 'Approved',
|
||||||
|
name: 'approved',
|
||||||
|
type: 'boolean',
|
||||||
|
default: true,
|
||||||
|
description: `An optional approval status.`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Coupon',
|
||||||
|
name: 'coupon',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
description: 'An optional coupon for this affiliate.',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* programAffiliate:approve */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
{
|
||||||
|
displayName: 'Program ID',
|
||||||
|
name: 'programId',
|
||||||
|
type: 'options',
|
||||||
|
typeOptions: {
|
||||||
|
loadOptionsMethod: 'getPrograms',
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
operation: [
|
||||||
|
'approve',
|
||||||
|
],
|
||||||
|
resource: [
|
||||||
|
'programAffiliate',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
description: `The ID of the Program to add the affiliate to. This ID can be found as part of the URL when viewing the program on the platform.`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Affiliate ID',
|
||||||
|
name: 'affiliateId',
|
||||||
|
type: 'string',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'programAffiliate',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'approve',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
description: 'The ID of the affiliate.',
|
||||||
|
},
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* programAffiliate:disapprove */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
{
|
||||||
|
displayName: 'Program ID',
|
||||||
|
name: 'programId',
|
||||||
|
type: 'options',
|
||||||
|
typeOptions: {
|
||||||
|
loadOptionsMethod: 'getPrograms',
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
operation: [
|
||||||
|
'disapprove',
|
||||||
|
],
|
||||||
|
resource: [
|
||||||
|
'programAffiliate',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
description: `The ID of the Program to add the affiliate to. This ID can be found as part of the URL when viewing the program on the platform.`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Affiliate ID',
|
||||||
|
name: 'affiliateId',
|
||||||
|
type: 'string',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'programAffiliate',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'disapprove',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
description: 'The ID of the affiliate.',
|
||||||
|
},
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* affiliate:get */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
{
|
||||||
|
displayName: 'Program ID',
|
||||||
|
name: 'programId',
|
||||||
|
type: 'options',
|
||||||
|
required: true,
|
||||||
|
typeOptions: {
|
||||||
|
loadOptionsMethod: 'getPrograms',
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
operation: [
|
||||||
|
'get',
|
||||||
|
],
|
||||||
|
resource: [
|
||||||
|
'programAffiliate',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
description: `The ID of the Program to add the affiliate to. This ID can be found as part of the URL when viewing the program on the platform.`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Affiliate ID',
|
||||||
|
name: 'affiliateId',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'programAffiliate',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'get',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
description: 'The ID of the affiliate.',
|
||||||
|
},
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* programAffiliate:getAll */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
{
|
||||||
|
displayName: 'Program ID',
|
||||||
|
name: 'programId',
|
||||||
|
type: 'options',
|
||||||
|
typeOptions: {
|
||||||
|
loadOptionsMethod: 'getPrograms',
|
||||||
|
},
|
||||||
|
required: true,
|
||||||
|
default: '',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
resource: [
|
||||||
|
'programAffiliate',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
description: `The ID of the Program to add the affiliate to. This ID can be found as part of the URL when viewing the program on the platform.`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Return All',
|
||||||
|
name: 'returnAll',
|
||||||
|
type: 'boolean',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'programAffiliate',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: false,
|
||||||
|
description: 'If set to true, all the results will be returned.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Limit',
|
||||||
|
name: 'limit',
|
||||||
|
type: 'number',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'programAffiliate',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
returnAll: [
|
||||||
|
false,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
typeOptions: {
|
||||||
|
minValue: 1,
|
||||||
|
maxValue: 1000,
|
||||||
|
},
|
||||||
|
default: 100,
|
||||||
|
description: 'How many results to return.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Filters',
|
||||||
|
name: 'filters',
|
||||||
|
type: 'collection',
|
||||||
|
placeholder: 'Add Field',
|
||||||
|
default: {},
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'programAffiliate',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
displayName: 'Affiliate Group ID',
|
||||||
|
name: 'affiliate_group_id',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
description: 'Retrieves affiliates for a certain affiliate group.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Email',
|
||||||
|
name: 'email',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
description: 'An email address.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Parent ID',
|
||||||
|
name: 'parentId',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
description: 'Retrieves children for a certain parent affiliate.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Source ID',
|
||||||
|
name: 'source_id',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
description: 'Source ID.',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
] as INodeProperties[];
|
280
packages/nodes-base/nodes/Tapfiliate/Tapfiliate.node.ts
Normal file
|
@ -0,0 +1,280 @@
|
||||||
|
import {
|
||||||
|
IExecuteFunctions,
|
||||||
|
} from 'n8n-core';
|
||||||
|
|
||||||
|
import {
|
||||||
|
IDataObject,
|
||||||
|
ILoadOptionsFunctions,
|
||||||
|
INodeExecutionData,
|
||||||
|
INodePropertyOptions,
|
||||||
|
INodeType,
|
||||||
|
INodeTypeDescription
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
import {
|
||||||
|
affiliateFields,
|
||||||
|
affiliateOperations
|
||||||
|
} from './AffiliateDescription';
|
||||||
|
|
||||||
|
import {
|
||||||
|
affiliateMetadataFields,
|
||||||
|
affiliateMetadataOperations,
|
||||||
|
} from './AffiliateMetadataDescription';
|
||||||
|
|
||||||
|
import {
|
||||||
|
programAffiliateFields,
|
||||||
|
programAffiliateOperations,
|
||||||
|
} from './ProgramAffiliateDescription';
|
||||||
|
|
||||||
|
import {
|
||||||
|
tapfiliateApiRequest,
|
||||||
|
tapfiliateApiRequestAllItems,
|
||||||
|
} from './GenericFunctions';
|
||||||
|
|
||||||
|
export class Tapfiliate implements INodeType {
|
||||||
|
description: INodeTypeDescription = {
|
||||||
|
displayName: 'Tapfiliate',
|
||||||
|
name: 'tapfiliate',
|
||||||
|
icon: 'file:tapfiliate.svg',
|
||||||
|
group: ['transform'],
|
||||||
|
version: 1,
|
||||||
|
subtitle: '={{$parameter["operation"] + ":" + $parameter["resource"]}}',
|
||||||
|
description: 'Consume Tapfiliate API',
|
||||||
|
defaults: {
|
||||||
|
name: 'Tapfiliate',
|
||||||
|
color: '#4a8de8',
|
||||||
|
},
|
||||||
|
inputs: ['main'],
|
||||||
|
outputs: ['main'],
|
||||||
|
credentials: [
|
||||||
|
{
|
||||||
|
name: 'tapfiliateApi',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
properties: [
|
||||||
|
{
|
||||||
|
displayName: 'Resource',
|
||||||
|
name: 'resource',
|
||||||
|
type: 'options',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Affiliate',
|
||||||
|
value: 'affiliate',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Affiliate Metadata',
|
||||||
|
value: 'affiliateMetadata',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Program Affiliate',
|
||||||
|
value: 'programAffiliate',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
default: 'affiliate',
|
||||||
|
required: true,
|
||||||
|
description: 'Resource to consume',
|
||||||
|
},
|
||||||
|
...affiliateOperations,
|
||||||
|
...affiliateFields,
|
||||||
|
...affiliateMetadataOperations,
|
||||||
|
...affiliateMetadataFields,
|
||||||
|
...programAffiliateOperations,
|
||||||
|
...programAffiliateFields,
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
methods = {
|
||||||
|
loadOptions: {
|
||||||
|
// Get custom fields to display to user so that they can select them easily
|
||||||
|
async getPrograms(this: ILoadOptionsFunctions,): Promise<INodePropertyOptions[]> {
|
||||||
|
const returnData: INodePropertyOptions[] = [];
|
||||||
|
const programs = await tapfiliateApiRequestAllItems.call(this, 'GET', '/programs/');
|
||||||
|
for (const program of programs) {
|
||||||
|
returnData.push({
|
||||||
|
name: program.title,
|
||||||
|
value: program.id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return returnData;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
||||||
|
const items = this.getInputData();
|
||||||
|
const length = (items.length as unknown) as number;
|
||||||
|
const qs: IDataObject = {};
|
||||||
|
let responseData;
|
||||||
|
const returnData: IDataObject[] = [];
|
||||||
|
const resource = this.getNodeParameter('resource', 0) as string;
|
||||||
|
const operation = this.getNodeParameter('operation', 0) as string;
|
||||||
|
if (resource === 'affiliate') {
|
||||||
|
if (operation === 'create') {
|
||||||
|
//https://tapfiliate.com/docs/rest/#affiliates-affiliates-collection-post
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
const firstname = this.getNodeParameter('firstname', i) as string;
|
||||||
|
const lastname = this.getNodeParameter('lastname', i) as string;
|
||||||
|
const email = this.getNodeParameter('email', i) as string;
|
||||||
|
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
|
||||||
|
const body: IDataObject = {
|
||||||
|
firstname,
|
||||||
|
lastname,
|
||||||
|
email,
|
||||||
|
};
|
||||||
|
Object.assign(body, additionalFields);
|
||||||
|
|
||||||
|
if (body.addressUi) {
|
||||||
|
body.address = (body.addressUi as IDataObject).addressValues as IDataObject;
|
||||||
|
delete body.addressUi;
|
||||||
|
if ((body.address as IDataObject).country) {
|
||||||
|
(body.address as IDataObject).country = {
|
||||||
|
code: (body.address as IDataObject).country,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (body.companyName) {
|
||||||
|
body.company = {
|
||||||
|
name: body.companyName,
|
||||||
|
};
|
||||||
|
delete body.companyName;
|
||||||
|
}
|
||||||
|
responseData = await tapfiliateApiRequest.call(this, 'POST', '/affiliates/', body);
|
||||||
|
returnData.push(responseData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (operation === 'delete') {
|
||||||
|
//https://tapfiliate.com/docs/rest/#affiliates-affiliate-delete
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
const affiliateId = this.getNodeParameter('affiliateId', i) as string;
|
||||||
|
responseData = await tapfiliateApiRequest.call(this, 'DELETE', `/affiliates/${affiliateId}/`);
|
||||||
|
returnData.push({ success: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (operation === 'get') {
|
||||||
|
//https://tapfiliate.com/docs/rest/#affiliates-affiliate-get
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
const affiliateId = this.getNodeParameter('affiliateId', i) as string;
|
||||||
|
responseData = await tapfiliateApiRequest.call(this, 'GET', `/affiliates/${affiliateId}/`);
|
||||||
|
returnData.push(responseData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (operation === 'getAll') {
|
||||||
|
//https://tapfiliate.com/docs/rest/#affiliates-affiliates-collection-get
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
|
||||||
|
const filters = this.getNodeParameter('filters', i) as IDataObject;
|
||||||
|
Object.assign(qs, filters);
|
||||||
|
if (returnAll) {
|
||||||
|
responseData = await tapfiliateApiRequestAllItems.call(this, 'GET', `/affiliates/`, {}, qs);
|
||||||
|
} else {
|
||||||
|
const limit = this.getNodeParameter('limit', i) as number;
|
||||||
|
responseData = await tapfiliateApiRequest.call(this, 'GET', `/affiliates/`, {}, qs);
|
||||||
|
responseData = responseData.splice(0, limit);
|
||||||
|
}
|
||||||
|
returnData.push.apply(returnData, responseData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (resource === 'affiliateMetadata') {
|
||||||
|
if (operation === 'add') {
|
||||||
|
//https://tapfiliate.com/docs/rest/#affiliates-meta-data-key-put
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
const affiliateId = this.getNodeParameter('affiliateId', i) as string;
|
||||||
|
const metadata = (this.getNodeParameter('metadataUi', i) as IDataObject || {}).metadataValues as IDataObject[] || [];
|
||||||
|
if (metadata.length === 0) {
|
||||||
|
throw new Error('Metadata cannot be empty.');
|
||||||
|
}
|
||||||
|
for (const { key, value } of metadata) {
|
||||||
|
await tapfiliateApiRequest.call(this, 'PUT', `/affiliates/${affiliateId}/meta-data/${key}/`, { value });
|
||||||
|
}
|
||||||
|
returnData.push({ success: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (operation === 'remove') {
|
||||||
|
//https://tapfiliate.com/docs/rest/#affiliates-meta-data-key-delete
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
const affiliateId = this.getNodeParameter('affiliateId', i) as string;
|
||||||
|
const key = this.getNodeParameter('key', i) as string;
|
||||||
|
responseData = await tapfiliateApiRequest.call(this, 'DELETE', `/affiliates/${affiliateId}/meta-data/${key}/`);
|
||||||
|
returnData.push({ success: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (operation === 'update') {
|
||||||
|
//https://tapfiliate.com/docs/rest/#affiliates-notes-collection-get
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
const affiliateId = this.getNodeParameter('affiliateId', i) as string;
|
||||||
|
const key = this.getNodeParameter('key', i) as string;
|
||||||
|
const value = this.getNodeParameter('value', i) as string;
|
||||||
|
responseData = await tapfiliateApiRequest.call(this, 'PUT', `/affiliates/${affiliateId}/meta-data/`, { [key]: value });
|
||||||
|
returnData.push(responseData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (resource === 'programAffiliate') {
|
||||||
|
if (operation === 'add') {
|
||||||
|
//https://tapfiliate.com/docs/rest/#programs-program-affiliates-collection-post
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
const programId = this.getNodeParameter('programId', i) as string;
|
||||||
|
const affiliateId = this.getNodeParameter('affiliateId', i) as string;
|
||||||
|
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
|
||||||
|
const body: IDataObject = {
|
||||||
|
affiliate: {
|
||||||
|
id: affiliateId,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
Object.assign(body, additionalFields);
|
||||||
|
|
||||||
|
responseData = await tapfiliateApiRequest.call(this, 'POST', `/programs/${programId}/affiliates/`, body);
|
||||||
|
returnData.push(responseData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (operation === 'approve') {
|
||||||
|
//https://tapfiliate.com/docs/rest/#programs-approve-an-affiliate-for-a-program-put
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
const programId = this.getNodeParameter('programId', i) as string;
|
||||||
|
const affiliateId = this.getNodeParameter('affiliateId', i) as string;
|
||||||
|
responseData = await tapfiliateApiRequest.call(this, 'PUT', `/programs/${programId}/affiliates/${affiliateId}/approved/`);
|
||||||
|
returnData.push(responseData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (operation === 'disapprove') {
|
||||||
|
//https://tapfiliate.com/docs/rest/#programs-approve-an-affiliate-for-a-program-delete
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
const programId = this.getNodeParameter('programId', i) as string;
|
||||||
|
const affiliateId = this.getNodeParameter('affiliateId', i) as string;
|
||||||
|
responseData = await tapfiliateApiRequest.call(this, 'DELETE', `/programs/${programId}/affiliates/${affiliateId}/approved/`);
|
||||||
|
returnData.push(responseData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (operation === 'get') {
|
||||||
|
//https://tapfiliate.com/docs/rest/#programs-affiliate-in-program-get
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
const programId = this.getNodeParameter('programId', i) as string;
|
||||||
|
const affiliateId = this.getNodeParameter('affiliateId', i) as string;
|
||||||
|
responseData = await tapfiliateApiRequest.call(this, 'GET', `/programs/${programId}/affiliates/${affiliateId}/`);
|
||||||
|
returnData.push(responseData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (operation === 'getAll') {
|
||||||
|
//https://tapfiliate.com/docs/rest/#programs-program-affiliates-collection-get
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
const programId = this.getNodeParameter('programId', i) as string;
|
||||||
|
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
|
||||||
|
const filters = this.getNodeParameter('filters', i) as IDataObject;
|
||||||
|
Object.assign(qs, filters);
|
||||||
|
if (returnAll) {
|
||||||
|
responseData = await tapfiliateApiRequestAllItems.call(this, 'GET', `/programs/${programId}/affiliates/`, {}, qs);
|
||||||
|
} else {
|
||||||
|
const limit = this.getNodeParameter('limit', i) as number;
|
||||||
|
responseData = await tapfiliateApiRequest.call(this, 'GET', `/programs/${programId}/affiliates/`, {}, qs);
|
||||||
|
responseData = responseData.splice(0, limit);
|
||||||
|
}
|
||||||
|
returnData.push.apply(returnData, responseData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return [this.helpers.returnJsonArray(returnData)];
|
||||||
|
}
|
||||||
|
}
|
1
packages/nodes-base/nodes/Tapfiliate/tapfiliate.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg width="900" height="900" xmlns="http://www.w3.org/2000/svg"><defs><linearGradient x1="2.037%" y1="113.141%" x2="106.275%" y2="-7.854%" id="a"><stop stop-color="#2FDFBF" offset="0%"/><stop stop-color="#2FDFBF" offset="0%"/><stop stop-color="#2F74FF" offset="100%"/></linearGradient></defs><path d="M900 289.19c0-113.514-67.164-221.622-178.657-267.568C685.075 6.757 647.463 0 609.851 0 552.09 0 495.67 17.568 448.657 48.649 402.985 17.568 347.91 0 288.806 0c-20.15 0-41.642 2.703-63.134 6.757C118.209 29.73 32.239 113.514 6.716 220.27v2.703C2.686 241.892 0 262.163 0 281.081 0 322.973 9.403 363.514 24.18 400l145.074 363.514c21.492 55.405 64.477 101.35 123.582 122.972 21.492 8.109 42.985 12.163 65.82 13.514h502.389l-90-239.19L878.507 400C891.94 363.514 900 325.676 900 289.19zM95.373 281.08c0-12.162 1.343-25.676 4.03-37.838v-2.702c16.12-68.92 75.224-127.027 145.075-140.541 14.776-2.703 29.552-4.054 42.985-4.054 32.238 0 61.79 8.108 88.656 21.622-13.432 18.918-25.522 37.837-34.925 60.81L188.06 556.757l-76.567-191.892c-9.403-28.379-16.12-56.757-16.12-83.784zm693.134 82.433L666.27 659.459l55.074 145.946H362.687c-12.09 0-24.18-2.702-36.27-8.108-48.357-17.567-76.566-63.513-76.566-110.81 0-13.514 2.686-28.379 8.06-43.244l173.283-428.378c30.896-74.324 103.433-118.92 180-118.92 24.18 0 49.702 4.055 73.88 14.866 75.225 31.08 119.553 102.703 119.553 178.378-1.343 24.325-5.373 50-16.12 74.325z" fill-rule="nonzero" fill="url(#a)"/></svg>
|
After Width: | Height: | Size: 1.4 KiB |
|
@ -19,7 +19,7 @@ export class Telegram implements INodeType {
|
||||||
description: INodeTypeDescription = {
|
description: INodeTypeDescription = {
|
||||||
displayName: 'Telegram',
|
displayName: 'Telegram',
|
||||||
name: 'telegram',
|
name: 'telegram',
|
||||||
icon: 'file:telegram.png',
|
icon: 'file:telegram.svg',
|
||||||
group: ['output'],
|
group: ['output'],
|
||||||
version: 1,
|
version: 1,
|
||||||
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
||||||
|
@ -200,6 +200,16 @@ export class Telegram implements INodeType {
|
||||||
value: 'editMessageText',
|
value: 'editMessageText',
|
||||||
description: 'Edit a text message',
|
description: 'Edit a text message',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'Pin Chat Message',
|
||||||
|
value: 'pinChatMessage',
|
||||||
|
description: 'Pin a chat message',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Unpin Chat Message',
|
||||||
|
value: 'unpinChatMessage',
|
||||||
|
description: 'Unpin a chat message',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'Send Animation',
|
name: 'Send Animation',
|
||||||
value: 'sendAnimation',
|
value: 'sendAnimation',
|
||||||
|
@ -266,6 +276,8 @@ export class Telegram implements INodeType {
|
||||||
'get',
|
'get',
|
||||||
'leave',
|
'leave',
|
||||||
'member',
|
'member',
|
||||||
|
'pinChatMessage',
|
||||||
|
'unpinChatMessage',
|
||||||
'setDescription',
|
'setDescription',
|
||||||
'setTitle',
|
'setTitle',
|
||||||
'sendAnimation',
|
'sendAnimation',
|
||||||
|
@ -288,6 +300,54 @@ export class Telegram implements INodeType {
|
||||||
description: 'Unique identifier for the target chat or username of the target<br />channel (in the format @channelusername).',
|
description: 'Unique identifier for the target chat or username of the target<br />channel (in the format @channelusername).',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// ----------------------------------
|
||||||
|
// message:pinChatMessage
|
||||||
|
// ----------------------------------
|
||||||
|
{
|
||||||
|
displayName: 'Message ID',
|
||||||
|
name: 'messageId',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
operation: [
|
||||||
|
'pinChatMessage',
|
||||||
|
'unpinChatMessage',
|
||||||
|
],
|
||||||
|
resource: [
|
||||||
|
'message',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
required: true,
|
||||||
|
description: 'Unique identifier of the message to pin or unpin.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Additional Fields',
|
||||||
|
name: 'additionalFields',
|
||||||
|
type: 'collection',
|
||||||
|
placeholder: 'Add Field',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
operation: [
|
||||||
|
'pinChatMessage',
|
||||||
|
],
|
||||||
|
resource: [
|
||||||
|
'message',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: {},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
displayName: 'Disable Notification',
|
||||||
|
name: 'disable_notification',
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
description: 'Do not send a notification to all chat members about the new pinned message.',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
// ----------------------------------
|
// ----------------------------------
|
||||||
// chat
|
// chat
|
||||||
|
@ -1583,10 +1643,10 @@ export class Telegram implements INodeType {
|
||||||
body.title = this.getNodeParameter('title', i) as string;
|
body.title = this.getNodeParameter('title', i) as string;
|
||||||
|
|
||||||
}
|
}
|
||||||
// } else if (resource === 'bot') {
|
// } else if (resource === 'bot') {
|
||||||
// if (operation === 'info') {
|
// if (operation === 'info') {
|
||||||
// endpoint = 'getUpdates';
|
// endpoint = 'getUpdates';
|
||||||
// }
|
// }
|
||||||
} else if (resource === 'file') {
|
} else if (resource === 'file') {
|
||||||
|
|
||||||
if (operation === 'get') {
|
if (operation === 'get') {
|
||||||
|
@ -1622,6 +1682,30 @@ export class Telegram implements INodeType {
|
||||||
// Add additional fields and replyMarkup
|
// Add additional fields and replyMarkup
|
||||||
addAdditionalFields.call(this, body, i);
|
addAdditionalFields.call(this, body, i);
|
||||||
|
|
||||||
|
} else if (operation === 'pinChatMessage') {
|
||||||
|
// ----------------------------------
|
||||||
|
// message:pinChatMessage
|
||||||
|
// ----------------------------------
|
||||||
|
|
||||||
|
endpoint = 'pinChatMessage';
|
||||||
|
|
||||||
|
body.chat_id = this.getNodeParameter('chatId', i) as string;
|
||||||
|
body.message_id = this.getNodeParameter('messageId', i) as string;
|
||||||
|
|
||||||
|
const { disable_notification } = this.getNodeParameter('additionalFields', i) as IDataObject;
|
||||||
|
if (disable_notification) {
|
||||||
|
body.disable_notification = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (operation === 'unpinChatMessage') {
|
||||||
|
// ----------------------------------
|
||||||
|
// message:unpinChatMessage
|
||||||
|
// ----------------------------------
|
||||||
|
|
||||||
|
endpoint = 'unpinChatMessage';
|
||||||
|
|
||||||
|
body.chat_id = this.getNodeParameter('chatId', i) as string;
|
||||||
|
body.message_id = this.getNodeParameter('messageId', i) as string;
|
||||||
|
|
||||||
} else if (operation === 'sendAnimation') {
|
} else if (operation === 'sendAnimation') {
|
||||||
// ----------------------------------
|
// ----------------------------------
|
||||||
|
|
|
@ -23,7 +23,7 @@ export class TelegramTrigger implements INodeType {
|
||||||
description: INodeTypeDescription = {
|
description: INodeTypeDescription = {
|
||||||
displayName: 'Telegram Trigger',
|
displayName: 'Telegram Trigger',
|
||||||
name: 'telegramTrigger',
|
name: 'telegramTrigger',
|
||||||
icon: 'file:telegram.png',
|
icon: 'file:telegram.svg',
|
||||||
group: ['trigger'],
|
group: ['trigger'],
|
||||||
version: 1,
|
version: 1,
|
||||||
subtitle: '=Updates: {{$parameter["updates"].join(", ")}}',
|
subtitle: '=Updates: {{$parameter["updates"].join(", ")}}',
|
||||||
|
|
Before Width: | Height: | Size: 1.4 KiB |
1
packages/nodes-base/nodes/Telegram/telegram.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 66 66" fill="#fff" fill-rule="evenodd" stroke="#000" stroke-linecap="round" stroke-linejoin="round"><use xlink:href="#A" x=".5" y=".5"/><symbol id="A" overflow="visible"><g stroke="none" fill-rule="nonzero"><path d="M0 32c0 17.673 14.327 32 32 32s32-14.327 32-32S49.673 0 32 0 0 14.327 0 32" fill="#37aee2"/><path d="M21.661 34.338l3.797 10.508s.475.983.983.983 8.068-7.864 8.068-7.864l8.407-16.237-21.119 9.898z" fill="#c8daea"/><path d="M26.695 37.034l-.729 7.746s-.305 2.373 2.068 0l4.644-4.203" fill="#a9c6d8"/><path d="M21.73 34.712l-7.809-2.545s-.932-.378-.633-1.237c.062-.177.186-.328.559-.588 1.731-1.206 32.028-12.096 32.028-12.096s.856-.288 1.361-.097c.231.088.378.187.503.548.045.132.071.411.068.689-.003.201-.027.386-.045.678-.184 2.978-5.706 25.198-5.706 25.198s-.33 1.3-1.514 1.345c-.432.016-.956-.071-1.582-.61-2.323-1.998-10.352-7.394-12.126-8.58-.1-.067-.129-.154-.146-.239-.025-.125.108-.28.108-.28s13.98-12.427 14.352-13.731c.029-.101-.079-.151-.226-.107-.929.342-17.025 10.506-18.801 11.629-.104.066-.395.023-.395.023"/></g></symbol></svg>
|
After Width: | Height: | Size: 1.1 KiB |
290
packages/nodes-base/nodes/TimescaleDb/TimescaleDb.node.ts
Normal file
|
@ -0,0 +1,290 @@
|
||||||
|
import {
|
||||||
|
IExecuteFunctions,
|
||||||
|
} from 'n8n-core';
|
||||||
|
|
||||||
|
import {
|
||||||
|
IDataObject,
|
||||||
|
INodeExecutionData,
|
||||||
|
INodeType,
|
||||||
|
INodeTypeDescription,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
import {
|
||||||
|
getItemCopy,
|
||||||
|
pgInsert,
|
||||||
|
pgQuery,
|
||||||
|
pgUpdate,
|
||||||
|
} from '../Postgres/Postgres.node.functions';
|
||||||
|
|
||||||
|
import * as pgPromise from 'pg-promise';
|
||||||
|
|
||||||
|
export class TimescaleDb implements INodeType {
|
||||||
|
description: INodeTypeDescription = {
|
||||||
|
displayName: 'TimescaleDB',
|
||||||
|
name: 'timescaleDb',
|
||||||
|
icon: 'file:timescale.svg',
|
||||||
|
group: ['input'],
|
||||||
|
version: 1,
|
||||||
|
description: 'Add and update data in TimescaleDB',
|
||||||
|
defaults: {
|
||||||
|
name: 'TimescaleDB',
|
||||||
|
color: '#fdb515',
|
||||||
|
},
|
||||||
|
inputs: ['main'],
|
||||||
|
outputs: ['main'],
|
||||||
|
credentials: [
|
||||||
|
{
|
||||||
|
name: 'timescaleDb',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
properties: [
|
||||||
|
{
|
||||||
|
displayName: 'Operation',
|
||||||
|
name: 'operation',
|
||||||
|
type: 'options',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Execute Query',
|
||||||
|
value: 'executeQuery',
|
||||||
|
description: 'Execute an SQL query',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Insert',
|
||||||
|
value: 'insert',
|
||||||
|
description: 'Insert rows in database',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Update',
|
||||||
|
value: 'update',
|
||||||
|
description: 'Update rows in database',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
default: 'insert',
|
||||||
|
description: 'The operation to perform.',
|
||||||
|
},
|
||||||
|
|
||||||
|
// ----------------------------------
|
||||||
|
// executeQuery
|
||||||
|
// ----------------------------------
|
||||||
|
{
|
||||||
|
displayName: 'Query',
|
||||||
|
name: 'query',
|
||||||
|
type: 'string',
|
||||||
|
typeOptions: {
|
||||||
|
rows: 5,
|
||||||
|
},
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
operation: [
|
||||||
|
'executeQuery',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
placeholder: 'SELECT id, name FROM product WHERE id < 40',
|
||||||
|
required: true,
|
||||||
|
description: 'The SQL query to execute.',
|
||||||
|
},
|
||||||
|
|
||||||
|
// ----------------------------------
|
||||||
|
// insert
|
||||||
|
// ----------------------------------
|
||||||
|
{
|
||||||
|
displayName: 'Schema',
|
||||||
|
name: 'schema',
|
||||||
|
type: 'string',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
operation: [
|
||||||
|
'insert',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: 'public',
|
||||||
|
required: true,
|
||||||
|
description: 'Name of the schema the table belongs to',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Table',
|
||||||
|
name: 'table',
|
||||||
|
type: 'string',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
operation: [
|
||||||
|
'insert',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
required: true,
|
||||||
|
description: 'Name of the table in which to insert data to.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Columns',
|
||||||
|
name: 'columns',
|
||||||
|
type: 'string',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
operation: [
|
||||||
|
'insert',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
placeholder: 'id,name,description',
|
||||||
|
description:
|
||||||
|
'Comma separated list of the properties which should used as columns for the new rows.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Return Fields',
|
||||||
|
name: 'returnFields',
|
||||||
|
type: 'string',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
operation: [
|
||||||
|
'insert',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '*',
|
||||||
|
description: 'Comma separated list of the fields that the operation will return',
|
||||||
|
},
|
||||||
|
|
||||||
|
// ----------------------------------
|
||||||
|
// update
|
||||||
|
// ----------------------------------
|
||||||
|
{
|
||||||
|
displayName: 'Schema',
|
||||||
|
name: 'schema',
|
||||||
|
type: 'string',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
operation: [
|
||||||
|
'update',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: 'public',
|
||||||
|
required: true,
|
||||||
|
description: 'Name of the schema the table belongs to',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Table',
|
||||||
|
name: 'table',
|
||||||
|
type: 'string',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
operation: [
|
||||||
|
'update',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
required: true,
|
||||||
|
description: 'Name of the table in which to update data in',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Update Key',
|
||||||
|
name: 'updateKey',
|
||||||
|
type: 'string',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
operation: [
|
||||||
|
'update',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: 'id',
|
||||||
|
required: true,
|
||||||
|
description:
|
||||||
|
'Name of the property which decides which rows in the database should be updated. Normally that would be "id".',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Columns',
|
||||||
|
name: 'columns',
|
||||||
|
type: 'string',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
operation: [
|
||||||
|
'update',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
placeholder: 'name,description',
|
||||||
|
description:
|
||||||
|
'Comma separated list of the properties which should used as columns for rows to update.',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
||||||
|
const credentials = this.getCredentials('timescaleDb');
|
||||||
|
|
||||||
|
if (credentials === undefined) {
|
||||||
|
throw new Error('No credentials got returned!');
|
||||||
|
}
|
||||||
|
|
||||||
|
const pgp = pgPromise();
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
host: credentials.host as string,
|
||||||
|
port: credentials.port as number,
|
||||||
|
database: credentials.database as string,
|
||||||
|
user: credentials.user as string,
|
||||||
|
password: credentials.password as string,
|
||||||
|
ssl: !['disable', undefined].includes(credentials.ssl as string | undefined),
|
||||||
|
sslmode: (credentials.ssl as string) || 'disable',
|
||||||
|
};
|
||||||
|
|
||||||
|
const db = pgp(config);
|
||||||
|
|
||||||
|
let returnItems = [];
|
||||||
|
|
||||||
|
const items = this.getInputData();
|
||||||
|
const operation = this.getNodeParameter('operation', 0) as string;
|
||||||
|
|
||||||
|
if (operation === 'executeQuery') {
|
||||||
|
// ----------------------------------
|
||||||
|
// executeQuery
|
||||||
|
// ----------------------------------
|
||||||
|
|
||||||
|
const queryResult = await pgQuery(this.getNodeParameter, pgp, db, items);
|
||||||
|
|
||||||
|
returnItems = this.helpers.returnJsonArray(queryResult as IDataObject[]);
|
||||||
|
} else if (operation === 'insert') {
|
||||||
|
// ----------------------------------
|
||||||
|
// insert
|
||||||
|
// ----------------------------------
|
||||||
|
|
||||||
|
const [insertData, insertItems] = await pgInsert(this.getNodeParameter, pgp, db, items);
|
||||||
|
|
||||||
|
// Add the id to the data
|
||||||
|
for (let i = 0; i < insertData.length; i++) {
|
||||||
|
returnItems.push({
|
||||||
|
json: {
|
||||||
|
...insertData[i],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else if (operation === 'update') {
|
||||||
|
// ----------------------------------
|
||||||
|
// update
|
||||||
|
// ----------------------------------
|
||||||
|
|
||||||
|
const updateItems = await pgUpdate(this.getNodeParameter, pgp, db, items);
|
||||||
|
|
||||||
|
returnItems = this.helpers.returnJsonArray(updateItems);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
await pgp.end();
|
||||||
|
throw new Error(`The operation "${operation}" is not supported!`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close the connection
|
||||||
|
await pgp.end();
|
||||||
|
|
||||||
|
return this.prepareOutputData(returnItems);
|
||||||
|
}
|
||||||
|
}
|
3
packages/nodes-base/nodes/TimescaleDb/timescale.svg
Normal file
After Width: | Height: | Size: 20 KiB |
|
@ -18,6 +18,7 @@ import {
|
||||||
interface IBodyCreateTask {
|
interface IBodyCreateTask {
|
||||||
content: string;
|
content: string;
|
||||||
project_id?: number;
|
project_id?: number;
|
||||||
|
section_id?: number;
|
||||||
parent?: number;
|
parent?: number;
|
||||||
order?: number;
|
order?: number;
|
||||||
label_ids?: number[];
|
label_ids?: number[];
|
||||||
|
@ -273,6 +274,19 @@ export class Todoist implements INodeType {
|
||||||
default: 1,
|
default: 1,
|
||||||
description: 'Task priority from 1 (normal) to 4 (urgent).',
|
description: 'Task priority from 1 (normal) to 4 (urgent).',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Section',
|
||||||
|
name: 'section',
|
||||||
|
type: 'options',
|
||||||
|
typeOptions: {
|
||||||
|
loadOptionsMethod: 'getSections',
|
||||||
|
loadOptionsDependsOn: [
|
||||||
|
'project',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
default: {},
|
||||||
|
description: 'The section you want to operate on.',
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -399,6 +413,29 @@ export class Todoist implements INodeType {
|
||||||
return returnData;
|
return returnData;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Get all the available sections in the selected project, to display them
|
||||||
|
// to user so that he can select one easily
|
||||||
|
async getSections(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||||
|
const returnData: INodePropertyOptions[] = [];
|
||||||
|
|
||||||
|
const projectId = this.getCurrentNodeParameter('project') as number;
|
||||||
|
if (projectId) {
|
||||||
|
const qs: IDataObject = {project_id: projectId};
|
||||||
|
const sections = await todoistApiRequest.call(this, 'GET', '/sections', {}, qs);
|
||||||
|
for (const section of sections) {
|
||||||
|
const sectionName = section.name;
|
||||||
|
const sectionId = section.id;
|
||||||
|
|
||||||
|
returnData.push({
|
||||||
|
name: sectionName,
|
||||||
|
value: sectionId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnData;
|
||||||
|
},
|
||||||
|
|
||||||
// Get all the available labels to display them to user so that he can
|
// Get all the available labels to display them to user so that he can
|
||||||
// select them easily
|
// select them easily
|
||||||
async getLabels(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
async getLabels(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||||
|
@ -458,6 +495,10 @@ export class Todoist implements INodeType {
|
||||||
body.label_ids = labels;
|
body.label_ids = labels;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (options.section) {
|
||||||
|
body.section_id = options.section as number;
|
||||||
|
}
|
||||||
|
|
||||||
responseData = await todoistApiRequest.call(this, 'POST', '/tasks', body);
|
responseData = await todoistApiRequest.call(this, 'POST', '/tasks', body);
|
||||||
}
|
}
|
||||||
if (operation === 'close') {
|
if (operation === 'close') {
|
||||||
|
|
|
@ -8,9 +8,13 @@ import {
|
||||||
INodePropertyOptions,
|
INodePropertyOptions,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
import { OptionsWithUri } from 'request';
|
import {
|
||||||
import { IDataObject } from 'n8n-workflow';
|
OptionsWithUri,
|
||||||
|
} from 'request';
|
||||||
|
|
||||||
|
import {
|
||||||
|
IDataObject,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
// Interface in Typeform
|
// Interface in Typeform
|
||||||
export interface ITypeformDefinition {
|
export interface ITypeformDefinition {
|
||||||
|
|
|
@ -22,7 +22,7 @@ export class TypeformTrigger implements INodeType {
|
||||||
description: INodeTypeDescription = {
|
description: INodeTypeDescription = {
|
||||||
displayName: 'Typeform Trigger',
|
displayName: 'Typeform Trigger',
|
||||||
name: 'typeformTrigger',
|
name: 'typeformTrigger',
|
||||||
icon: 'file:typeform.png',
|
icon: 'file:typeform.svg',
|
||||||
group: ['trigger'],
|
group: ['trigger'],
|
||||||
version: 1,
|
version: 1,
|
||||||
subtitle: '=Form ID: {{$parameter["formId"]}}',
|
subtitle: '=Form ID: {{$parameter["formId"]}}',
|
||||||
|
@ -133,10 +133,10 @@ export class TypeformTrigger implements INodeType {
|
||||||
|
|
||||||
for (const item of items) {
|
for (const item of items) {
|
||||||
if (item.form_id === formId
|
if (item.form_id === formId
|
||||||
&& item.url === webhookUrl) {
|
&& item.url === webhookUrl) {
|
||||||
webhookData.webhookId = item.tag;
|
webhookData.webhookId = item.tag;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
Before Width: | Height: | Size: 988 B |
6
packages/nodes-base/nodes/Typeform/typeform.svg
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="256px" height="257px" viewBox="0 0 256 257" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid">
|
||||||
|
<g>
|
||||||
|
<path d="M121.239531,0.47435412 C154.258847,-2.24419986 177.973534,6.51949224 203.490026,32.0209539 C231.03887,59.5533708 249.354214,95.2328648 254.290235,133.239251 C259.781839,175.505244 252.228399,207.045826 228.963591,230.137302 C206.717472,252.217693 174.224091,259.500956 130.817584,255.872056 L130.817584,255.872056 L129.020628,255.714401 C92.0580678,252.31591 65.9142731,240.197353 40.5830264,214.542641 C13.525519,187.139558 -0.732370213,156.026391 0.0289819699,123.37039 C0.401368488,107.383336 4.93009017,93.7578927 13.6639513,80.3889177 C19.3897229,71.6244438 25.2046788,64.7874057 37.3681164,52.2487147 L37.3681164,52.2487147 L41.4275324,48.0907013 L45.9022443,43.5551431 C74.7290193,14.5793771 93.7072016,2.74118195 121.239531,0.47435412 Z M201.527922,33.9842216 C176.58625,9.05724142 153.671595,0.589198793 121.467288,3.24065127 C94.3271196,5.47519117 75.6884654,17.3319772 46.5188613,46.8760997 L46.5188613,46.8760997 L44.4086816,49.0171264 C30.1966379,63.4648858 23.5365117,70.8518409 17.66965,79.3947543 L17.66965,79.3947543 L16.8159631,80.6541424 L15.9876804,81.9069942 C7.52700788,94.8577977 3.16375473,107.9854 2.80388584,123.435056 C2.06141566,155.281167 15.9979199,185.693018 42.5581255,212.592447 C67.82505,238.182016 93.7827344,249.991613 131.048788,253.106044 L131.048788,253.106044 L132.897415,253.254431 C174.642425,256.464747 205.76515,249.252143 227.008257,228.167291 C249.578581,205.765126 256.927442,175.078792 251.537704,133.596809 C246.682834,96.2152658 228.654224,61.0943485 201.527922,33.9842216 Z M166.984321,93.3903087 L166.984321,106.949394 L138.186877,106.949394 L138.186877,184.662246 L123.842281,184.662246 L123.842281,106.949394 L95.044837,106.949394 L95.044837,93.3903087 L166.984321,93.3903087 Z" fill="#262627" fill-rule="nonzero"></path>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2 KiB |
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "n8n-nodes-base",
|
"name": "n8n-nodes-base",
|
||||||
"version": "0.101.0",
|
"version": "0.103.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",
|
||||||
|
@ -61,6 +61,7 @@
|
||||||
"dist/credentials/CustomerIoApi.credentials.js",
|
"dist/credentials/CustomerIoApi.credentials.js",
|
||||||
"dist/credentials/S3.credentials.js",
|
"dist/credentials/S3.credentials.js",
|
||||||
"dist/credentials/CrateDb.credentials.js",
|
"dist/credentials/CrateDb.credentials.js",
|
||||||
|
"dist/credentials/DiscourseApi.credentials.js",
|
||||||
"dist/credentials/DisqusApi.credentials.js",
|
"dist/credentials/DisqusApi.credentials.js",
|
||||||
"dist/credentials/DriftApi.credentials.js",
|
"dist/credentials/DriftApi.credentials.js",
|
||||||
"dist/credentials/DriftOAuth2Api.credentials.js",
|
"dist/credentials/DriftOAuth2Api.credentials.js",
|
||||||
|
@ -176,6 +177,7 @@
|
||||||
"dist/credentials/QuestDb.credentials.js",
|
"dist/credentials/QuestDb.credentials.js",
|
||||||
"dist/credentials/QuickBaseApi.credentials.js",
|
"dist/credentials/QuickBaseApi.credentials.js",
|
||||||
"dist/credentials/RabbitMQ.credentials.js",
|
"dist/credentials/RabbitMQ.credentials.js",
|
||||||
|
"dist/credentials/RedditOAuth2Api.credentials.js",
|
||||||
"dist/credentials/Redis.credentials.js",
|
"dist/credentials/Redis.credentials.js",
|
||||||
"dist/credentials/RocketchatApi.credentials.js",
|
"dist/credentials/RocketchatApi.credentials.js",
|
||||||
"dist/credentials/RundeckApi.credentials.js",
|
"dist/credentials/RundeckApi.credentials.js",
|
||||||
|
@ -210,8 +212,10 @@
|
||||||
"dist/credentials/SurveyMonkeyOAuth2Api.credentials.js",
|
"dist/credentials/SurveyMonkeyOAuth2Api.credentials.js",
|
||||||
"dist/credentials/TaigaCloudApi.credentials.js",
|
"dist/credentials/TaigaCloudApi.credentials.js",
|
||||||
"dist/credentials/TaigaServerApi.credentials.js",
|
"dist/credentials/TaigaServerApi.credentials.js",
|
||||||
|
"dist/credentials/TapfiliateApi.credentials.js",
|
||||||
"dist/credentials/TelegramApi.credentials.js",
|
"dist/credentials/TelegramApi.credentials.js",
|
||||||
"dist/credentials/TheHiveApi.credentials.js",
|
"dist/credentials/TheHiveApi.credentials.js",
|
||||||
|
"dist/credentials/TimescaleDb.credentials.js",
|
||||||
"dist/credentials/TodoistApi.credentials.js",
|
"dist/credentials/TodoistApi.credentials.js",
|
||||||
"dist/credentials/TodoistOAuth2Api.credentials.js",
|
"dist/credentials/TodoistOAuth2Api.credentials.js",
|
||||||
"dist/credentials/TravisCiApi.credentials.js",
|
"dist/credentials/TravisCiApi.credentials.js",
|
||||||
|
@ -296,6 +300,7 @@
|
||||||
"dist/nodes/CustomerIo/CustomerIoTrigger.node.js",
|
"dist/nodes/CustomerIo/CustomerIoTrigger.node.js",
|
||||||
"dist/nodes/DateTime.node.js",
|
"dist/nodes/DateTime.node.js",
|
||||||
"dist/nodes/Discord/Discord.node.js",
|
"dist/nodes/Discord/Discord.node.js",
|
||||||
|
"dist/nodes/Discourse/Discourse.node.js",
|
||||||
"dist/nodes/Disqus/Disqus.node.js",
|
"dist/nodes/Disqus/Disqus.node.js",
|
||||||
"dist/nodes/Drift/Drift.node.js",
|
"dist/nodes/Drift/Drift.node.js",
|
||||||
"dist/nodes/Dropbox/Dropbox.node.js",
|
"dist/nodes/Dropbox/Dropbox.node.js",
|
||||||
|
@ -422,6 +427,7 @@
|
||||||
"dist/nodes/ReadBinaryFile.node.js",
|
"dist/nodes/ReadBinaryFile.node.js",
|
||||||
"dist/nodes/ReadBinaryFiles.node.js",
|
"dist/nodes/ReadBinaryFiles.node.js",
|
||||||
"dist/nodes/ReadPdf.node.js",
|
"dist/nodes/ReadPdf.node.js",
|
||||||
|
"dist/nodes/Reddit/Reddit.node.js",
|
||||||
"dist/nodes/Redis/Redis.node.js",
|
"dist/nodes/Redis/Redis.node.js",
|
||||||
"dist/nodes/RenameKeys.node.js",
|
"dist/nodes/RenameKeys.node.js",
|
||||||
"dist/nodes/Rocketchat/Rocketchat.node.js",
|
"dist/nodes/Rocketchat/Rocketchat.node.js",
|
||||||
|
@ -457,10 +463,12 @@
|
||||||
"dist/nodes/SurveyMonkey/SurveyMonkeyTrigger.node.js",
|
"dist/nodes/SurveyMonkey/SurveyMonkeyTrigger.node.js",
|
||||||
"dist/nodes/Taiga/Taiga.node.js",
|
"dist/nodes/Taiga/Taiga.node.js",
|
||||||
"dist/nodes/Taiga/TaigaTrigger.node.js",
|
"dist/nodes/Taiga/TaigaTrigger.node.js",
|
||||||
|
"dist/nodes/Tapfiliate/Tapfiliate.node.js",
|
||||||
"dist/nodes/Telegram/Telegram.node.js",
|
"dist/nodes/Telegram/Telegram.node.js",
|
||||||
"dist/nodes/Telegram/TelegramTrigger.node.js",
|
"dist/nodes/Telegram/TelegramTrigger.node.js",
|
||||||
"dist/nodes/TheHive/TheHive.node.js",
|
"dist/nodes/TheHive/TheHive.node.js",
|
||||||
"dist/nodes/TheHive/TheHiveTrigger.node.js",
|
"dist/nodes/TheHive/TheHiveTrigger.node.js",
|
||||||
|
"dist/nodes/TimescaleDb/TimescaleDb.node.js",
|
||||||
"dist/nodes/Todoist/Todoist.node.js",
|
"dist/nodes/Todoist/Todoist.node.js",
|
||||||
"dist/nodes/Toggl/TogglTrigger.node.js",
|
"dist/nodes/Toggl/TogglTrigger.node.js",
|
||||||
"dist/nodes/TravisCi/TravisCi.node.js",
|
"dist/nodes/TravisCi/TravisCi.node.js",
|
||||||
|
@ -521,7 +529,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.50.0",
|
"n8n-workflow": "~0.51.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"
|
||||||
|
@ -555,7 +563,7 @@
|
||||||
"mqtt": "4.2.1",
|
"mqtt": "4.2.1",
|
||||||
"mssql": "^6.2.0",
|
"mssql": "^6.2.0",
|
||||||
"mysql2": "~2.1.0",
|
"mysql2": "~2.1.0",
|
||||||
"n8n-core": "~0.61.0",
|
"n8n-core": "~0.62.0",
|
||||||
"nodemailer": "^6.4.6",
|
"nodemailer": "^6.4.6",
|
||||||
"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.50.0",
|
"version": "0.51.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",
|
||||||
|
|