mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
chore: merge master
This commit is contained in:
commit
080a421f02
2
.github/workflows/e2e-tests-pr.yml
vendored
2
.github/workflows/e2e-tests-pr.yml
vendored
|
@ -47,5 +47,5 @@ jobs:
|
|||
run: exit 0
|
||||
|
||||
- name: Fail job if run-e2e-tests failed
|
||||
if: ${{ github.event.review.state != 'approved' || needs.run-e2e-tests.result == 'failure' }}
|
||||
if: ${{ (github.event.review.state != 'approved' && github.event.review.state != 'commented') || needs.run-e2e-tests.result == 'failure' }}
|
||||
run: exit 1
|
||||
|
|
|
@ -50,7 +50,7 @@ describe('NDV', () => {
|
|||
workflowPage.getters.canvasNodes().last().dblclick();
|
||||
ndv.getters.inputSelect().click();
|
||||
ndv.getters.inputOption().last().click();
|
||||
ndv.getters.inputDataContainer().find('[class*=schema_]').should('exist')
|
||||
ndv.getters.inputDataContainer().find('[class*=schema_]').should('exist');
|
||||
ndv.getters.inputDataContainer().should('contain', 'start');
|
||||
});
|
||||
|
||||
|
@ -96,10 +96,20 @@ describe('NDV', () => {
|
|||
ndv.getters.container().should('be.visible');
|
||||
workflowPage.actions.saveWorkflowUsingKeyboardShortcut();
|
||||
workflowPage.getters.isWorkflowSaved();
|
||||
})
|
||||
});
|
||||
|
||||
describe('test output schema view', () => {
|
||||
const schemaKeys = ['id', 'name', 'email', 'notes', 'country', 'created', 'objectValue', 'prop1', 'prop2'];
|
||||
const schemaKeys = [
|
||||
'id',
|
||||
'name',
|
||||
'email',
|
||||
'notes',
|
||||
'country',
|
||||
'created',
|
||||
'objectValue',
|
||||
'prop1',
|
||||
'prop2',
|
||||
];
|
||||
function setupSchemaWorkflow() {
|
||||
cy.createFixtureWorkflow('Test_workflow_schema_test.json', `NDV test schema view ${uuid()}`);
|
||||
workflowPage.actions.zoomToFit();
|
||||
|
@ -108,41 +118,62 @@ describe('NDV', () => {
|
|||
}
|
||||
|
||||
it('should switch to output schema view and validate it', () => {
|
||||
setupSchemaWorkflow()
|
||||
setupSchemaWorkflow();
|
||||
ndv.getters.outputDisplayMode().children().should('have.length', 3);
|
||||
ndv.getters.outputDisplayMode().find('[class*=active]').should('contain', 'Table');
|
||||
ndv.getters.outputDisplayMode().contains('Schema').click();
|
||||
ndv.getters.outputDisplayMode().find('[class*=active]').should('contain', 'Schema');
|
||||
|
||||
schemaKeys.forEach((key) => {
|
||||
ndv.getters.outputPanel().find('[data-test-id=run-data-schema-item]').contains(key).should('exist');
|
||||
ndv.getters
|
||||
.outputPanel()
|
||||
.find('[data-test-id=run-data-schema-item]')
|
||||
.contains(key)
|
||||
.should('exist');
|
||||
});
|
||||
});
|
||||
it('should preserve schema view after execution', () => {
|
||||
setupSchemaWorkflow()
|
||||
setupSchemaWorkflow();
|
||||
ndv.getters.outputDisplayMode().contains('Schema').click();
|
||||
ndv.actions.execute();
|
||||
ndv.getters.outputDisplayMode().find('[class*=active]').should('contain', 'Schema');
|
||||
})
|
||||
});
|
||||
it('should collapse and expand nested schema object', () => {
|
||||
setupSchemaWorkflow()
|
||||
const expandedObjectProps = ['prop1', 'prop2'];;
|
||||
const getObjectValueItem = () => ndv.getters.outputPanel().find('[data-test-id=run-data-schema-item]').filter(':contains("objectValue")');
|
||||
setupSchemaWorkflow();
|
||||
const expandedObjectProps = ['prop1', 'prop2'];
|
||||
const getObjectValueItem = () =>
|
||||
ndv.getters
|
||||
.outputPanel()
|
||||
.find('[data-test-id=run-data-schema-item]')
|
||||
.filter(':contains("objectValue")');
|
||||
ndv.getters.outputDisplayMode().contains('Schema').click();
|
||||
|
||||
expandedObjectProps.forEach((key) => {
|
||||
ndv.getters.outputPanel().find('[data-test-id=run-data-schema-item]').contains(key).should('be.visible');
|
||||
ndv.getters
|
||||
.outputPanel()
|
||||
.find('[data-test-id=run-data-schema-item]')
|
||||
.contains(key)
|
||||
.should('be.visible');
|
||||
});
|
||||
getObjectValueItem().find('label').click();
|
||||
expandedObjectProps.forEach((key) => {
|
||||
ndv.getters.outputPanel().find('[data-test-id=run-data-schema-item]').contains(key).should('not.be.visible');
|
||||
ndv.getters
|
||||
.outputPanel()
|
||||
.find('[data-test-id=run-data-schema-item]')
|
||||
.contains(key)
|
||||
.should('not.be.visible');
|
||||
});
|
||||
})
|
||||
});
|
||||
it('should not display pagination for schema', () => {
|
||||
setupSchemaWorkflow()
|
||||
setupSchemaWorkflow();
|
||||
ndv.getters.backToCanvas().click();
|
||||
workflowPage.getters.canvasNodeByName('Set').click();
|
||||
workflowPage.actions.addNodeToCanvas('Customer Datastore (n8n training)', true, true, 'Get All People');
|
||||
workflowPage.actions.addNodeToCanvas(
|
||||
'Customer Datastore (n8n training)',
|
||||
true,
|
||||
true,
|
||||
'Get All People',
|
||||
);
|
||||
ndv.actions.execute();
|
||||
ndv.getters.outputPanel().contains('25 items').should('exist');
|
||||
ndv.getters.outputPanel().find('[class*=_pagination]').should('exist');
|
||||
|
@ -150,9 +181,12 @@ describe('NDV', () => {
|
|||
ndv.getters.outputPanel().find('[class*=_pagination]').should('not.exist');
|
||||
ndv.getters.outputDisplayMode().contains('JSON').click();
|
||||
ndv.getters.outputPanel().find('[class*=_pagination]').should('exist');
|
||||
})
|
||||
});
|
||||
it('should display large schema', () => {
|
||||
cy.createFixtureWorkflow('Test_workflow_schema_test_pinned_data.json', `NDV test schema view ${uuid()}`);
|
||||
cy.createFixtureWorkflow(
|
||||
'Test_workflow_schema_test_pinned_data.json',
|
||||
`NDV test schema view ${uuid()}`,
|
||||
);
|
||||
workflowPage.actions.zoomToFit();
|
||||
workflowPage.actions.openNode('Set');
|
||||
|
||||
|
@ -160,8 +194,11 @@ describe('NDV', () => {
|
|||
ndv.getters.outputPanel().find('[class*=_pagination]').should('exist');
|
||||
ndv.getters.outputDisplayMode().contains('Schema').click();
|
||||
ndv.getters.outputPanel().find('[class*=_pagination]').should('not.exist');
|
||||
ndv.getters.outputPanel().find('[data-test-id=run-data-schema-item] [data-test-id=run-data-schema-item]').should('have.length', 20);
|
||||
})
|
||||
ndv.getters
|
||||
.outputPanel()
|
||||
.find('[data-test-id=run-data-schema-item] [data-test-id=run-data-schema-item]')
|
||||
.should('have.length', 20);
|
||||
});
|
||||
});
|
||||
|
||||
it('can link and unlink run selectors between input and output', () => {
|
||||
|
@ -170,11 +207,13 @@ describe('NDV', () => {
|
|||
workflowPage.actions.executeWorkflow();
|
||||
workflowPage.actions.openNode('Set3');
|
||||
|
||||
ndv.getters.inputRunSelector()
|
||||
ndv.getters
|
||||
.inputRunSelector()
|
||||
.should('exist')
|
||||
.find('input')
|
||||
.should('include.value', '2 of 2 (6 items)');
|
||||
ndv.getters.outputRunSelector()
|
||||
ndv.getters
|
||||
.outputRunSelector()
|
||||
.should('exist')
|
||||
.find('input')
|
||||
.should('include.value', '2 of 2 (6 items)');
|
||||
|
@ -183,23 +222,20 @@ describe('NDV', () => {
|
|||
ndv.actions.switchOutputMode('Table');
|
||||
|
||||
ndv.actions.changeOutputRunSelector('1 of 2 (6 items)');
|
||||
ndv.getters.inputRunSelector()
|
||||
.find('input')
|
||||
.should('include.value', '1 of 2 (6 items)');
|
||||
ndv.getters.inputRunSelector().find('input').should('include.value', '1 of 2 (6 items)');
|
||||
ndv.getters.inputTbodyCell(1, 0).should('have.text', '1111');
|
||||
ndv.getters.outputTbodyCell(1, 0).should('have.text', '1111');
|
||||
|
||||
ndv.getters.inputTbodyCell(1, 0).click(); // remove tooltip
|
||||
ndv.actions.changeInputRunSelector('2 of 2 (6 items)');
|
||||
ndv.getters.outputRunSelector()
|
||||
.find('input')
|
||||
.should('include.value', '2 of 2 (6 items)');
|
||||
ndv.getters.outputRunSelector().find('input').should('include.value', '2 of 2 (6 items)');
|
||||
|
||||
// unlink
|
||||
ndv.actions.toggleOutputRunLinking();
|
||||
ndv.getters.inputTbodyCell(1, 0).click(); // remove tooltip
|
||||
ndv.actions.changeOutputRunSelector('1 of 2 (6 items)');
|
||||
ndv.getters.inputRunSelector()
|
||||
ndv.getters
|
||||
.inputRunSelector()
|
||||
.should('exist')
|
||||
.find('input')
|
||||
.should('include.value', '2 of 2 (6 items)');
|
||||
|
@ -207,24 +243,18 @@ describe('NDV', () => {
|
|||
// link again
|
||||
ndv.actions.toggleOutputRunLinking();
|
||||
ndv.getters.inputTbodyCell(1, 0).click(); // remove tooltip
|
||||
ndv.getters.inputRunSelector()
|
||||
.find('input')
|
||||
.should('include.value', '1 of 2 (6 items)');
|
||||
|
||||
ndv.getters.inputRunSelector().find('input').should('include.value', '1 of 2 (6 items)');
|
||||
|
||||
// unlink again
|
||||
ndv.actions.toggleInputRunLinking();
|
||||
ndv.getters.inputTbodyCell(1, 0).click(); // remove tooltip
|
||||
ndv.actions.changeInputRunSelector('2 of 2 (6 items)');
|
||||
ndv.getters.outputRunSelector()
|
||||
.find('input')
|
||||
.should('include.value', '1 of 2 (6 items)');
|
||||
ndv.getters.outputRunSelector().find('input').should('include.value', '1 of 2 (6 items)');
|
||||
|
||||
// link again
|
||||
ndv.actions.toggleInputRunLinking();
|
||||
ndv.getters.inputTbodyCell(1, 0).click(); // remove tooltip
|
||||
ndv.getters.outputRunSelector()
|
||||
.find('input')
|
||||
.should('include.value', '2 of 2 (6 items)');
|
||||
ndv.getters.outputRunSelector().find('input').should('include.value', '2 of 2 (6 items)');
|
||||
});
|
||||
|
||||
it('should display parameter hints correctly', () => {
|
||||
|
@ -247,21 +277,19 @@ describe('NDV', () => {
|
|||
input: ' test',
|
||||
},
|
||||
{
|
||||
input: ' '
|
||||
input: ' ',
|
||||
},
|
||||
{
|
||||
input: '<div></div>'
|
||||
input: '<div></div>',
|
||||
},
|
||||
].forEach(({ input, output }) => {
|
||||
if (input) {
|
||||
ndv.actions.typeIntoParameterInput('value', input);
|
||||
}
|
||||
ndv.getters.parameterInput('name').click(); // remove focus from input, hide expression preview
|
||||
|
||||
|
||||
if (input) {
|
||||
ndv.actions.typeIntoParameterInput('value', input);
|
||||
}
|
||||
ndv.getters.parameterInput('name').click(); // remove focus from input, hide expression preview
|
||||
|
||||
ndv.actions.validateExpressionPreview('value', output || input);
|
||||
ndv.getters.parameterInput('value').clear();
|
||||
});
|
||||
ndv.actions.validateExpressionPreview('value', output || input);
|
||||
ndv.getters.parameterInput('value').clear();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -77,22 +77,7 @@
|
|||
"@types/json-diff": "^0.5.1",
|
||||
"@types/jsonwebtoken": "^9.0.1",
|
||||
"@types/localtunnel": "^1.9.0",
|
||||
"@types/lodash.debounce": "^4.0.7",
|
||||
"@types/lodash.difference": "^4",
|
||||
"@types/lodash.get": "^4.4.6",
|
||||
"@types/lodash.intersection": "^4.4.7",
|
||||
"@types/lodash.iteratee": "^4.7.7",
|
||||
"@types/lodash.merge": "^4.6.6",
|
||||
"@types/lodash.omit": "^4.5.7",
|
||||
"@types/lodash.pick": "^4.4.7",
|
||||
"@types/lodash.remove": "^4.7.7",
|
||||
"@types/lodash.set": "^4.3.6",
|
||||
"@types/lodash.split": "^4.4.7",
|
||||
"@types/lodash.unionby": "^4.8.7",
|
||||
"@types/lodash.uniq": "^4.5.7",
|
||||
"@types/lodash.uniqby": "^4.7.7",
|
||||
"@types/lodash.unset": "^4.5.7",
|
||||
"@types/lodash.without": "^4.4.7",
|
||||
"@types/lodash": "^4.14.195",
|
||||
"@types/parseurl": "^1.3.1",
|
||||
"@types/passport-jwt": "^3.0.6",
|
||||
"@types/psl": "^1.1.0",
|
||||
|
@ -109,7 +94,6 @@
|
|||
"@types/yamljs": "^0.2.31",
|
||||
"chokidar": "^3.5.2",
|
||||
"concurrently": "^5.1.0",
|
||||
"lodash.debounce": "^4.0.8",
|
||||
"mock-jwks": "^1.0.9",
|
||||
"nodemon": "^2.0.2",
|
||||
"run-script-os": "^1.0.7",
|
||||
|
@ -161,21 +145,7 @@
|
|||
"jwks-rsa": "^3.0.1",
|
||||
"ldapts": "^4.2.6",
|
||||
"localtunnel": "^2.0.0",
|
||||
"lodash.difference": "^4",
|
||||
"lodash.get": "^4.4.2",
|
||||
"lodash.intersection": "^4.4.0",
|
||||
"lodash.iteratee": "^4.7.0",
|
||||
"lodash.merge": "^4.6.2",
|
||||
"lodash.omit": "^4.5.0",
|
||||
"lodash.pick": "^4.4.0",
|
||||
"lodash.remove": "^4.7.0",
|
||||
"lodash.set": "^4.3.2",
|
||||
"lodash.split": "^4.4.2",
|
||||
"lodash.unionby": "^4.8.0",
|
||||
"lodash.uniq": "^4.5.0",
|
||||
"lodash.uniqby": "^4.7.0",
|
||||
"lodash.unset": "^4.5.2",
|
||||
"lodash.without": "^4.4.0",
|
||||
"lodash": "^4.17.21",
|
||||
"luxon": "^3.3.0",
|
||||
"mysql2": "~2.3.3",
|
||||
"n8n-core": "workspace:*",
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
||||
|
||||
import { Credentials, NodeExecuteFunctions } from 'n8n-core';
|
||||
import get from 'lodash.get';
|
||||
import get from 'lodash/get';
|
||||
|
||||
import type {
|
||||
ICredentialDataDecryptedObject,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
import curlconverter from 'curlconverter';
|
||||
import get from 'lodash.get';
|
||||
import get from 'lodash/get';
|
||||
import type { IDataObject } from 'n8n-workflow';
|
||||
import { jsonParse } from 'n8n-workflow';
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import uniq from 'lodash.uniq';
|
||||
import uniq from 'lodash/uniq';
|
||||
import glob from 'fast-glob';
|
||||
import type { DirectoryLoader, Types } from 'n8n-core';
|
||||
import {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import type { FindManyOptions, UpdateResult } from 'typeorm';
|
||||
import { In } from 'typeorm';
|
||||
import intersection from 'lodash.intersection';
|
||||
import intersection from 'lodash/intersection';
|
||||
import type { INode } from 'n8n-workflow';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ export const reloadNodesAndCredentials = async (
|
|||
push: Push,
|
||||
) => {
|
||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||
const { default: debounce } = await import('lodash.debounce');
|
||||
const { default: debounce } = await import('lodash/debounce');
|
||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||
const { watch } = await import('chokidar');
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||
/* eslint-disable prefer-destructuring */
|
||||
import type express from 'express';
|
||||
import get from 'lodash.get';
|
||||
import get from 'lodash/get';
|
||||
import stream from 'stream';
|
||||
import { promisify } from 'util';
|
||||
|
||||
|
@ -426,7 +426,7 @@ export async function executeWebhook(
|
|||
const binaryData = (response.body as IDataObject)?.binaryData as IBinaryData;
|
||||
if (binaryData?.id) {
|
||||
res.header(response.headers);
|
||||
const stream = NodeExecuteFunctions.getBinaryStream(binaryData.id);
|
||||
const stream = BinaryDataManager.getInstance().getBinaryStream(binaryData.id);
|
||||
void pipeline(stream, res).then(() =>
|
||||
responseCallback(null, { noWebhookResponse: true }),
|
||||
);
|
||||
|
@ -643,10 +643,12 @@ export async function executeWebhook(
|
|||
if (!didSendResponse) {
|
||||
// Send the webhook response manually
|
||||
res.setHeader('Content-Type', binaryData.mimeType);
|
||||
const binaryDataBuffer = await BinaryDataManager.getInstance().retrieveBinaryData(
|
||||
binaryData,
|
||||
);
|
||||
res.end(binaryDataBuffer);
|
||||
if (binaryData.id) {
|
||||
const stream = BinaryDataManager.getInstance().getBinaryStream(binaryData.id);
|
||||
await pipeline(stream, res);
|
||||
} else {
|
||||
res.end(Buffer.from(binaryData.data, BINARY_ENCODING));
|
||||
}
|
||||
|
||||
responseCallback(null, {
|
||||
noWebhookResponse: true,
|
||||
|
|
|
@ -42,7 +42,7 @@ import {
|
|||
WorkflowHooks,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import pick from 'lodash.pick';
|
||||
import pick from 'lodash/pick';
|
||||
import type { FindOptionsWhere } from 'typeorm';
|
||||
import { LessThanOrEqual, In } from 'typeorm';
|
||||
import { DateUtils } from 'typeorm/util/DateUtils';
|
||||
|
|
|
@ -31,7 +31,7 @@ import config from '@/config';
|
|||
import type { WorkflowEntity } from '@db/entities/WorkflowEntity';
|
||||
import type { User } from '@db/entities/User';
|
||||
import { RoleRepository } from '@db/repositories';
|
||||
import omit from 'lodash.omit';
|
||||
import omit from 'lodash/omit';
|
||||
import { PermissionChecker } from './UserManagement/PermissionChecker';
|
||||
import { isWorkflowIdValid } from './utils';
|
||||
import { UserService } from './user/user.service';
|
||||
|
|
|
@ -6,7 +6,7 @@ import type { ITaskData } from 'n8n-workflow';
|
|||
import { sleep } from 'n8n-workflow';
|
||||
import { sep } from 'path';
|
||||
import { diff } from 'json-diff';
|
||||
import pick from 'lodash.pick';
|
||||
import pick from 'lodash/pick';
|
||||
|
||||
import { ActiveExecutions } from '@/ActiveExecutions';
|
||||
import * as Db from '@/Db';
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import pick from 'lodash.pick';
|
||||
import pick from 'lodash/pick';
|
||||
import { Authorized, Get, Post, Put, RestController } from '@/decorators';
|
||||
import { getLdapConfig, getLdapSynchronizations, updateLdapConfig } from '@/Ldap/helpers';
|
||||
import { LdapService } from '@/Ldap/LdapService.ee';
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { readFile } from 'fs/promises';
|
||||
import get from 'lodash.get';
|
||||
import get from 'lodash/get';
|
||||
import { Request } from 'express';
|
||||
import type { INodeTypeDescription, INodeTypeNameVersion } from 'n8n-workflow';
|
||||
import { Authorized, Post, RestController } from '@/decorators';
|
||||
|
|
|
@ -131,14 +131,14 @@ export class PasswordResetController {
|
|||
|
||||
const baseUrl = getInstanceBaseUrl();
|
||||
const { id, firstName, lastName } = user;
|
||||
const url = UserService.generatePasswordResetUrl(user);
|
||||
const url = await UserService.generatePasswordResetUrl(user);
|
||||
|
||||
try {
|
||||
await this.mailer.passwordReset({
|
||||
email,
|
||||
firstName,
|
||||
lastName,
|
||||
passwordResetUrl: url.toString(),
|
||||
passwordResetUrl: url,
|
||||
domain: baseUrl,
|
||||
});
|
||||
} catch (error) {
|
||||
|
|
|
@ -2,11 +2,11 @@ import type { ClientOAuth2Options } from '@n8n/client-oauth2';
|
|||
import { ClientOAuth2 } from '@n8n/client-oauth2';
|
||||
import Csrf from 'csrf';
|
||||
import express from 'express';
|
||||
import get from 'lodash.get';
|
||||
import omit from 'lodash.omit';
|
||||
import set from 'lodash.set';
|
||||
import split from 'lodash.split';
|
||||
import unset from 'lodash.unset';
|
||||
import get from 'lodash/get';
|
||||
import omit from 'lodash/omit';
|
||||
import set from 'lodash/set';
|
||||
import split from 'lodash/split';
|
||||
import unset from 'lodash/unset';
|
||||
import { Credentials, UserSettings } from 'n8n-core';
|
||||
import type {
|
||||
WorkflowExecuteMode,
|
||||
|
|
|
@ -29,7 +29,7 @@ import { WorkflowEntity } from '@/databases/entities/WorkflowEntity';
|
|||
import { WorkflowTagMapping } from '@/databases/entities/WorkflowTagMapping';
|
||||
import { TagEntity } from '@/databases/entities/TagEntity';
|
||||
import { ActiveWorkflowRunner } from '../../ActiveWorkflowRunner';
|
||||
import without from 'lodash.without';
|
||||
import without from 'lodash/without';
|
||||
import type { VersionControllPullOptions } from './types/versionControlPullWorkFolder';
|
||||
import { versionControlFoldersExistCheck } from './versionControlHelper.ee';
|
||||
import { In } from 'typeorm';
|
||||
|
|
|
@ -15,7 +15,7 @@ import {
|
|||
messageEventBusDestinationFromDb,
|
||||
incrementPrometheusMetric,
|
||||
} from '../MessageEventBusDestination/Helpers.ee';
|
||||
import uniqby from 'lodash.uniqby';
|
||||
import uniqby from 'lodash/uniqBy';
|
||||
import type { EventMessageConfirmSource } from '../EventMessageClasses/EventMessageConfirm';
|
||||
import type { EventMessageAuditOptions } from '../EventMessageClasses/EventMessageAudit';
|
||||
import { EventMessageAudit } from '../EventMessageClasses/EventMessageAudit';
|
||||
|
|
|
@ -7,7 +7,7 @@ import { Worker } from 'worker_threads';
|
|||
import { createReadStream, existsSync, rmSync } from 'fs';
|
||||
import readline from 'readline';
|
||||
import { jsonParse, LoggerProxy } from 'n8n-workflow';
|
||||
import remove from 'lodash.remove';
|
||||
import remove from 'lodash/remove';
|
||||
import config from '@/config';
|
||||
import { getEventMessageObjectByType } from '../EventMessageClasses/Helpers';
|
||||
import type { EventMessageReturnMode } from '../MessageEventBus/MessageEventBus';
|
||||
|
|
|
@ -4,7 +4,7 @@ import type { INode, IPinData, JsonObject } from 'n8n-workflow';
|
|||
import { NodeApiError, jsonParse, LoggerProxy, Workflow } from 'n8n-workflow';
|
||||
import type { FindOptionsSelect, FindOptionsWhere, UpdateResult } from 'typeorm';
|
||||
import { In } from 'typeorm';
|
||||
import pick from 'lodash.pick';
|
||||
import pick from 'lodash/pick';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import { ActiveWorkflowRunner } from '@/ActiveWorkflowRunner';
|
||||
import * as Db from '@/Db';
|
||||
|
|
|
@ -5,7 +5,7 @@ import { existsSync } from 'fs';
|
|||
import bodyParser from 'body-parser';
|
||||
import { CronJob } from 'cron';
|
||||
import express from 'express';
|
||||
import set from 'lodash.set';
|
||||
import set from 'lodash/set';
|
||||
import { BinaryDataManager, UserSettings } from 'n8n-core';
|
||||
import type {
|
||||
ICredentialType,
|
||||
|
|
|
@ -38,8 +38,7 @@
|
|||
"@types/cron": "~1.7.1",
|
||||
"@types/crypto-js": "^4.0.1",
|
||||
"@types/express": "^4.17.6",
|
||||
"@types/lodash.get": "^4.4.6",
|
||||
"@types/lodash.pick": "^4.4.7",
|
||||
"@types/lodash": "^4.14.195",
|
||||
"@types/mime-types": "^2.1.0",
|
||||
"@types/request-promise-native": "~1.0.15",
|
||||
"@types/uuid": "^8.3.2"
|
||||
|
@ -54,8 +53,7 @@
|
|||
"file-type": "^16.5.4",
|
||||
"flatted": "^3.2.4",
|
||||
"form-data": "^4.0.0",
|
||||
"lodash.get": "^4.4.2",
|
||||
"lodash.pick": "^4.4.0",
|
||||
"lodash": "^4.17.21",
|
||||
"mime-types": "^2.1.27",
|
||||
"n8n-workflow": "workspace:*",
|
||||
"oauth-1.0a": "^2.2.6",
|
||||
|
|
|
@ -121,7 +121,7 @@ export class BinaryDataManager {
|
|||
throw new Error('Storage mode used to store binary data not available');
|
||||
}
|
||||
|
||||
async retrieveBinaryData(binaryData: IBinaryData): Promise<Buffer> {
|
||||
async getBinaryDataBuffer(binaryData: IBinaryData): Promise<Buffer> {
|
||||
if (binaryData.id) {
|
||||
return this.retrieveBinaryDataByIdentifier(binaryData.id);
|
||||
}
|
||||
|
|
|
@ -79,7 +79,7 @@ import {
|
|||
validateFieldType,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import pick from 'lodash.pick';
|
||||
import pick from 'lodash/pick';
|
||||
import { Agent } from 'https';
|
||||
import { IncomingMessage } from 'http';
|
||||
import { stringify } from 'qs';
|
||||
|
@ -92,7 +92,7 @@ import type {
|
|||
} from '@n8n/client-oauth2';
|
||||
import { ClientOAuth2 } from '@n8n/client-oauth2';
|
||||
import crypto, { createHmac } from 'crypto';
|
||||
import get from 'lodash.get';
|
||||
import get from 'lodash/get';
|
||||
import type { Request, Response } from 'express';
|
||||
import FormData from 'form-data';
|
||||
import path from 'path';
|
||||
|
@ -747,6 +747,8 @@ function convertN8nRequestToAxios(n8nRequest: IHttpRequestOptions): AxiosRequest
|
|||
auth,
|
||||
proxy,
|
||||
url,
|
||||
maxBodyLength: Infinity,
|
||||
maxContentLength: Infinity,
|
||||
} as AxiosRequestConfig;
|
||||
|
||||
axiosRequest.params = n8nRequest.qs;
|
||||
|
@ -915,7 +917,7 @@ export async function getBinaryDataBuffer(
|
|||
inputIndex: number,
|
||||
): Promise<Buffer> {
|
||||
const binaryData = inputData.main[inputIndex]![itemIndex]!.binary![propertyName]!;
|
||||
return BinaryDataManager.getInstance().retrieveBinaryData(binaryData);
|
||||
return BinaryDataManager.getInstance().getBinaryDataBuffer(binaryData);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -37,7 +37,7 @@ import type {
|
|||
WorkflowExecuteMode,
|
||||
} from 'n8n-workflow';
|
||||
import { LoggerProxy as Logger, WorkflowOperationError } from 'n8n-workflow';
|
||||
import get from 'lodash.get';
|
||||
import get from 'lodash/get';
|
||||
import * as NodeExecuteFunctions from './NodeExecuteFunctions';
|
||||
|
||||
export class WorkflowExecute {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import set from 'lodash.set';
|
||||
import set from 'lodash/set';
|
||||
|
||||
import type {
|
||||
ICredentialDataDecryptedObject,
|
||||
|
|
|
@ -84,18 +84,6 @@ export const baseCompletions = (Vue as CodeNodeEditorMixin).extend({
|
|||
label: `${prefix}jmespath()`,
|
||||
info: this.$locale.baseText('codeNodeEditor.completer.$jmespath'),
|
||||
},
|
||||
{
|
||||
label: `${prefix}if()`,
|
||||
info: this.$locale.baseText('codeNodeEditor.completer.$if'),
|
||||
},
|
||||
{
|
||||
label: `${prefix}min()`,
|
||||
info: this.$locale.baseText('codeNodeEditor.completer.$min'),
|
||||
},
|
||||
{
|
||||
label: `${prefix}max()`,
|
||||
info: this.$locale.baseText('codeNodeEditor.completer.$max'),
|
||||
},
|
||||
{
|
||||
label: `${prefix}runIndex`,
|
||||
info: this.$locale.baseText('codeNodeEditor.completer.$runIndex'),
|
||||
|
|
|
@ -5,25 +5,35 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import type { PropType } from 'vue';
|
||||
import { defineComponent } from 'vue';
|
||||
import emitter from '@/mixins/emitter';
|
||||
import type { EventBus } from 'n8n-design-system/utils';
|
||||
import { createEventBus } from 'n8n-design-system/utils';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'IntersectionObserved',
|
||||
mixins: [emitter],
|
||||
props: ['enabled'],
|
||||
props: {
|
||||
enabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
eventBus: {
|
||||
type: Object as PropType<EventBus>,
|
||||
default: () => createEventBus(),
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
if (!this.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.$nextTick(() => {
|
||||
this.$dispatch('IntersectionObserver', 'observe', this.$refs.observed);
|
||||
this.eventBus.emit('observe', this.$refs.observed);
|
||||
});
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (this.enabled) {
|
||||
this.$dispatch('IntersectionObserver', 'unobserve', this.$refs.observed);
|
||||
this.eventBus.emit('unobserve', this.$refs.observed);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -5,11 +5,27 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import type { PropType } from 'vue';
|
||||
import { defineComponent } from 'vue';
|
||||
import type { EventBus } from 'n8n-design-system/utils';
|
||||
import { createEventBus } from 'n8n-design-system/utils';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'IntersectionObserver',
|
||||
props: ['threshold', 'enabled'],
|
||||
props: {
|
||||
threshold: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
enabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
eventBus: {
|
||||
type: Object as PropType<EventBus>,
|
||||
default: () => createEventBus(),
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
observer: null,
|
||||
|
@ -35,13 +51,13 @@ export default defineComponent({
|
|||
});
|
||||
}, options);
|
||||
|
||||
this.$data.observer = observer;
|
||||
this.observer = observer;
|
||||
|
||||
this.$on('observe', (observed: Element) => {
|
||||
this.eventBus.on('observe', (observed: Element) => {
|
||||
observer.observe(observed);
|
||||
});
|
||||
|
||||
this.$on('unobserve', (observed: Element) => {
|
||||
this.eventBus.on('unobserve', (observed: Element) => {
|
||||
observer.unobserve(observed);
|
||||
});
|
||||
},
|
||||
|
|
|
@ -38,18 +38,6 @@
|
|||
@update:modelValue="onInput"
|
||||
@submit="onSubmit"
|
||||
/>
|
||||
<n8n-info-tip v-if="!settingsStore.isSmtpSetup" class="mt-s">
|
||||
<i18n path="settings.users.setupSMTPInfo">
|
||||
<template #link>
|
||||
<a
|
||||
href="https://docs.n8n.io/reference/user-management.html#step-one-smtp"
|
||||
target="_blank"
|
||||
>
|
||||
{{ $locale.baseText('settings.users.setupSMTPInfo.link') }}
|
||||
</a>
|
||||
</template>
|
||||
</i18n>
|
||||
</n8n-info-tip>
|
||||
</template>
|
||||
<template v-if="!showInviteUrls" #footer>
|
||||
<n8n-button
|
||||
|
|
|
@ -166,6 +166,7 @@ import type { IPermissions } from '@/permissions';
|
|||
import { getWorkflowPermissions } from '@/permissions';
|
||||
import { createEventBus } from 'n8n-design-system';
|
||||
import { useCloudPlanStore } from '@/stores';
|
||||
import { nodeViewEventBus } from '@/event-bus';
|
||||
|
||||
const hasChanged = (prev: string[], curr: string[]) => {
|
||||
if (prev.length !== curr.length) {
|
||||
|
@ -445,7 +446,7 @@ export default defineComponent({
|
|||
return;
|
||||
}
|
||||
|
||||
this.$root.$emit('importWorkflowData', { data: workflowData });
|
||||
nodeViewEventBus.emit('importWorkflowData', { data: workflowData });
|
||||
};
|
||||
|
||||
const inputRef = this.$refs.importFile as HTMLInputElement | undefined;
|
||||
|
@ -505,7 +506,7 @@ export default defineComponent({
|
|||
},
|
||||
)) as MessageBoxInputData;
|
||||
|
||||
this.$root.$emit('importWorkflowUrl', { url: promptResponse.value });
|
||||
nodeViewEventBus.emit('importWorkflowUrl', { url: promptResponse.value });
|
||||
} catch (e) {}
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -975,6 +975,8 @@ export default defineComponent({
|
|||
|
||||
<style lang="scss">
|
||||
.node-settings {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
background-color: var(--color-background-xlight);
|
||||
height: 100%;
|
||||
|
@ -1007,7 +1009,6 @@ export default defineComponent({
|
|||
}
|
||||
|
||||
.node-parameters-wrapper {
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
padding: 0 20px 200px 20px;
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
:droppable="droppable"
|
||||
:node="node"
|
||||
:path="path"
|
||||
:event-bus="eventBus"
|
||||
@input="valueChanged"
|
||||
@modalOpenerClick="openExpressionEditorModal"
|
||||
@focus="setFocus"
|
||||
|
@ -390,8 +391,8 @@ import { useCredentialsStore } from '@/stores/credentials.store';
|
|||
import { useSettingsStore } from '@/stores/settings.store';
|
||||
import { htmlEditorEventBus } from '@/event-bus';
|
||||
import Vue from 'vue';
|
||||
|
||||
type ResourceLocatorRef = InstanceType<typeof ResourceLocator>;
|
||||
import type { EventBus } from 'n8n-design-system/utils';
|
||||
import { createEventBus } from 'n8n-design-system/utils';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'parameter-input',
|
||||
|
@ -462,6 +463,10 @@ export default defineComponent({
|
|||
size: 'small',
|
||||
}),
|
||||
},
|
||||
eventBus: {
|
||||
type: Object as PropType<EventBus>,
|
||||
default: () => createEventBus(),
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -1111,9 +1116,7 @@ export default defineComponent({
|
|||
}
|
||||
} else if (command === 'refreshOptions') {
|
||||
if (this.isResourceLocatorParameter) {
|
||||
const resourceLocatorRef = this.$refs.resourceLocator as ResourceLocatorRef | undefined;
|
||||
|
||||
resourceLocatorRef?.$emit('refreshList');
|
||||
this.eventBus.emit('refreshList');
|
||||
}
|
||||
void this.loadRemoteParameterOptions();
|
||||
} else if (command === 'formatHtml') {
|
||||
|
@ -1145,7 +1148,7 @@ export default defineComponent({
|
|||
});
|
||||
},
|
||||
mounted() {
|
||||
this.$on('optionSelected', this.optionSelected);
|
||||
this.eventBus.on('optionSelected', this.optionSelected);
|
||||
|
||||
this.tempValue = this.displayValue as string;
|
||||
if (this.node !== null) {
|
||||
|
@ -1185,6 +1188,9 @@ export default defineComponent({
|
|||
inputFieldRef: this.$refs['inputField'],
|
||||
});
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.eventBus.off('optionSelected', this.optionSelected);
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
:isForCredential="true"
|
||||
:eventSource="eventSource"
|
||||
:hint="!showRequiredErrors ? hint : ''"
|
||||
:event-bus="eventBus"
|
||||
@focus="onFocus"
|
||||
@blur="onBlur"
|
||||
@textInput="valueChanged"
|
||||
|
@ -65,6 +66,7 @@ import { isValueExpression } from '@/utils';
|
|||
import type { INodeParameterResourceLocator, INodeProperties, IParameterLabel } from 'n8n-workflow';
|
||||
import { mapStores } from 'pinia';
|
||||
import { useWorkflowsStore } from '@/stores/workflows.store';
|
||||
import { createEventBus } from 'n8n-design-system/utils';
|
||||
|
||||
type ParamRef = InstanceType<typeof ParameterInputWrapper>;
|
||||
|
||||
|
@ -100,6 +102,7 @@ export default defineComponent({
|
|||
focused: false,
|
||||
blurredEver: false,
|
||||
menuExpanded: false,
|
||||
eventBus: createEventBus(),
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
@ -147,9 +150,7 @@ export default defineComponent({
|
|||
this.menuExpanded = expanded;
|
||||
},
|
||||
optionSelected(command: string) {
|
||||
if (this.$refs.param) {
|
||||
(this.$refs.param as ParamRef).$emit('optionSelected', command);
|
||||
}
|
||||
this.eventBus.emit('optionSelected', command);
|
||||
},
|
||||
valueChanged(parameterData: IUpdateInformation) {
|
||||
this.$emit('change', parameterData);
|
||||
|
|
|
@ -56,6 +56,7 @@
|
|||
:hint="hint"
|
||||
:hide-issues="hideIssues"
|
||||
:label="label"
|
||||
:event-bus="eventBus"
|
||||
@valueChanged="valueChanged"
|
||||
@textInput="onTextInput"
|
||||
@focus="onFocus"
|
||||
|
@ -98,6 +99,7 @@ import { useNDVStore } from '@/stores/ndv.store';
|
|||
import { useSegment } from '@/stores/segment.store';
|
||||
import { externalHooks } from '@/mixins/externalHooks';
|
||||
import { getMappedResult } from '@/utils/mappingUtils';
|
||||
import { createEventBus } from 'n8n-design-system/utils';
|
||||
|
||||
type ParameterInputWrapperRef = InstanceType<typeof ParameterInputWrapper>;
|
||||
|
||||
|
@ -112,7 +114,10 @@ export default defineComponent({
|
|||
ParameterInputWrapper,
|
||||
},
|
||||
setup() {
|
||||
const eventBus = createEventBus();
|
||||
|
||||
return {
|
||||
eventBus,
|
||||
...useToast(),
|
||||
};
|
||||
},
|
||||
|
@ -234,17 +239,14 @@ export default defineComponent({
|
|||
this.menuExpanded = expanded;
|
||||
},
|
||||
optionSelected(command: string) {
|
||||
const paramRef = this.$refs.param as ParameterInputWrapperRef | undefined;
|
||||
paramRef?.$emit('optionSelected', command);
|
||||
this.eventBus.emit('optionSelected', command);
|
||||
},
|
||||
valueChanged(parameterData: IUpdateInformation) {
|
||||
this.$emit('valueChanged', parameterData);
|
||||
},
|
||||
onTextInput(parameterData: IUpdateInformation) {
|
||||
const paramRef = this.$refs.param as ParameterInputWrapperRef | undefined;
|
||||
|
||||
if (isValueExpression(this.parameter, parameterData.value)) {
|
||||
paramRef?.$emit('optionSelected', 'addExpression');
|
||||
this.eventBus.emit('optionSelected', 'addExpression');
|
||||
}
|
||||
},
|
||||
onDrop(newParamValue: string) {
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
:expressionEvaluated="expressionValueComputed"
|
||||
:label="label"
|
||||
:data-test-id="`parameter-input-${parameter.name}`"
|
||||
:event-bus="internalEventBus"
|
||||
@focus="onFocus"
|
||||
@blur="onBlur"
|
||||
@drop="onDrop"
|
||||
|
@ -61,8 +62,8 @@ import type { INodeUi, IUpdateInformation, TargetItem } from '@/Interface';
|
|||
import { workflowHelpers } from '@/mixins/workflowHelpers';
|
||||
import { isValueExpression } from '@/utils';
|
||||
import { useNDVStore } from '@/stores/ndv.store';
|
||||
|
||||
type ParamRef = InstanceType<typeof ParameterInput>;
|
||||
import type { EventBus } from 'n8n-design-system/utils';
|
||||
import { createEventBus } from 'n8n-design-system/utils';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'parameter-input-wrapper',
|
||||
|
@ -71,8 +72,16 @@ export default defineComponent({
|
|||
ParameterInput,
|
||||
InputHint,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
internalEventBus: createEventBus(),
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.$on('optionSelected', this.optionSelected);
|
||||
this.eventBus.on('optionSelected', this.optionSelected);
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.eventBus.off('optionSelected', this.optionSelected);
|
||||
},
|
||||
props: {
|
||||
isReadOnly: {
|
||||
|
@ -124,6 +133,10 @@ export default defineComponent({
|
|||
size: 'small',
|
||||
}),
|
||||
},
|
||||
eventBus: {
|
||||
type: Object as PropType<EventBus>,
|
||||
default: () => createEventBus(),
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapStores(useNDVStore),
|
||||
|
@ -217,9 +230,7 @@ export default defineComponent({
|
|||
this.$emit('drop', data);
|
||||
},
|
||||
optionSelected(command: string) {
|
||||
const paramRef = this.$refs.param as ParamRef | undefined;
|
||||
|
||||
paramRef?.$emit('optionSelected', command);
|
||||
this.internalEventBus.emit('optionSelected', command);
|
||||
},
|
||||
onValueChanged(parameterData: IUpdateInformation) {
|
||||
this.$emit('valueChanged', parameterData);
|
||||
|
|
|
@ -15,11 +15,11 @@
|
|||
:hasMore="currentQueryHasMore"
|
||||
:errorView="currentQueryError"
|
||||
:width="width"
|
||||
:event-bus="eventBus"
|
||||
@input="onListItemSelected"
|
||||
@hide="onDropdownHide"
|
||||
@filter="onSearchFilter"
|
||||
@loadMore="loadResourcesDebounced"
|
||||
ref="dropdown"
|
||||
>
|
||||
<template #error>
|
||||
<div :class="$style.error" data-test-id="rlc-error-container">
|
||||
|
@ -176,8 +176,8 @@ import { useWorkflowsStore } from '@/stores/workflows.store';
|
|||
import { useRootStore } from '@/stores/n8nRoot.store';
|
||||
import { useNDVStore } from '@/stores/ndv.store';
|
||||
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
|
||||
|
||||
type ResourceLocatorDropdownRef = InstanceType<typeof ResourceLocatorDropdown>;
|
||||
import { createEventBus } from 'n8n-design-system/utils';
|
||||
import type { EventBus } from 'n8n-design-system/utils';
|
||||
|
||||
interface IResourceLocatorQuery {
|
||||
results: INodeListSearchItems[];
|
||||
|
@ -256,6 +256,10 @@ export default defineComponent({
|
|||
loadOptionsMethod: {
|
||||
type: String,
|
||||
},
|
||||
eventBus: {
|
||||
type: Object as PropType<EventBus>,
|
||||
default: () => createEventBus(),
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -475,17 +479,20 @@ export default defineComponent({
|
|||
},
|
||||
},
|
||||
mounted() {
|
||||
this.$on('refreshList', this.refreshList);
|
||||
this.eventBus.on('refreshList', this.refreshList);
|
||||
window.addEventListener('resize', this.setWidth);
|
||||
|
||||
useNDVStore().$subscribe((mutation, state) => {
|
||||
// Update the width when main panel dimension change
|
||||
this.setWidth();
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
this.setWidth();
|
||||
}, 0);
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.eventBus.off('refreshList', this.refreshList);
|
||||
window.removeEventListener('resize', this.setWidth);
|
||||
},
|
||||
methods: {
|
||||
|
@ -510,9 +517,8 @@ export default defineComponent({
|
|||
this.trackEvent('User refreshed resource locator list');
|
||||
},
|
||||
onKeyDown(e: MouseEvent) {
|
||||
const dropdownRef = this.$refs.dropdown as ResourceLocatorDropdownRef | undefined;
|
||||
if (dropdownRef && this.showResourceDropdown && !this.isSearchable) {
|
||||
dropdownRef.$emit('keyDown', e);
|
||||
if (this.showResourceDropdown && !this.isSearchable) {
|
||||
this.eventBus.emit('keyDown', e);
|
||||
}
|
||||
},
|
||||
openResource(url: string) {
|
||||
|
|
|
@ -82,6 +82,8 @@
|
|||
import type { IResourceLocatorResultExpanded } from '@/Interface';
|
||||
import { defineComponent } from 'vue';
|
||||
import type { PropType } from 'vue';
|
||||
import type { EventBus } from 'n8n-design-system/utils';
|
||||
import { createEventBus } from 'n8n-design-system/utils';
|
||||
|
||||
const SEARCH_BAR_HEIGHT_PX = 40;
|
||||
const SCROLL_MARGIN_PX = 10;
|
||||
|
@ -120,6 +122,10 @@ export default defineComponent({
|
|||
width: {
|
||||
type: Number,
|
||||
},
|
||||
eventBus: {
|
||||
type: Object as PropType<EventBus>,
|
||||
default: () => createEventBus(),
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -128,7 +134,10 @@ export default defineComponent({
|
|||
};
|
||||
},
|
||||
mounted() {
|
||||
this.$on('keyDown', this.onKeyDown);
|
||||
this.eventBus.on('keyDown', this.onKeyDown);
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.eventBus.off('keyDown', this.onKeyDown);
|
||||
},
|
||||
computed: {
|
||||
sortedResources(): IResourceLocatorResultExpanded[] {
|
||||
|
|
|
@ -1,11 +1,26 @@
|
|||
<template>
|
||||
<div class="__html-display ph-no-capture" v-html="html"></div>
|
||||
<iframe class="__html-display ph-no-capture" :srcdoc="html" />
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import type { PropType } from 'vue';
|
||||
import sanitizeHtml, { defaults, type IOptions as SanitizeOptions } from 'sanitize-html';
|
||||
import type { INodeExecutionData } from 'n8n-workflow';
|
||||
|
||||
const sanitizeOptions: SanitizeOptions = {
|
||||
allowVulnerableTags: false,
|
||||
enforceHtmlBoundary: false,
|
||||
disallowedTagsMode: 'discard',
|
||||
allowedTags: [...defaults.allowedTags, 'style', 'img', 'title'],
|
||||
allowedAttributes: {
|
||||
...defaults.allowedAttributes,
|
||||
'*': ['class', 'style'],
|
||||
},
|
||||
transformTags: {
|
||||
head: '',
|
||||
},
|
||||
};
|
||||
|
||||
export default {
|
||||
name: 'RunDataHtml',
|
||||
props: {
|
||||
|
@ -15,29 +30,8 @@ export default {
|
|||
},
|
||||
computed: {
|
||||
html() {
|
||||
if (!this.inputData) return '';
|
||||
|
||||
return this.scopeCss(this.inputData[0].json.html as string);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* Scope all CSS selectors to prevent user stylesheets from leaking.
|
||||
*/
|
||||
scopeCss(str: string) {
|
||||
const stylesheets = str.match(/<style>([\s\S]*?)<\/style>/g);
|
||||
|
||||
if (!stylesheets) return str;
|
||||
|
||||
const map = stylesheets.reduce<Record<string, string>>((acc, match) => {
|
||||
match.split('\n').forEach((line) => {
|
||||
if (line.endsWith('{')) acc[line] = ['.__html-display', line].join(' ');
|
||||
});
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
return Object.entries(map).reduce((acc, [key, value]) => acc.replace(key, value), str);
|
||||
const markup = (this.inputData?.[0].json.html as string) ?? '';
|
||||
return sanitizeHtml(markup, sanitizeOptions);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
@ -45,6 +39,7 @@ export default {
|
|||
|
||||
<style lang="scss">
|
||||
.__html-display {
|
||||
padding: 0 var(--spacing-s);
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
@observed="onObserved"
|
||||
class="tags-container"
|
||||
:enabled="responsive"
|
||||
:event-bus="intersectionEventBus"
|
||||
>
|
||||
<template>
|
||||
<span class="tags">
|
||||
|
@ -26,6 +27,7 @@
|
|||
:class="{ hidden: tag.hidden }"
|
||||
:data-id="tag.id"
|
||||
:enabled="responsive"
|
||||
:event-bus="intersectionEventBus"
|
||||
v-else
|
||||
>
|
||||
<el-tag :title="tag.name" type="info" size="small" :class="{ hoverable }">
|
||||
|
@ -46,6 +48,7 @@ import IntersectionObserver from './IntersectionObserver.vue';
|
|||
import IntersectionObserved from './IntersectionObserved.vue';
|
||||
import { mapStores } from 'pinia';
|
||||
import { useTagsStore } from '@/stores/tags.store';
|
||||
import { createEventBus } from 'n8n-design-system/utils';
|
||||
|
||||
// random upper limit if none is set to minimize performance impact of observers
|
||||
const DEFAULT_MAX_TAGS_LIMIT = 20;
|
||||
|
@ -62,6 +65,7 @@ export default defineComponent({
|
|||
props: ['tagIds', 'limit', 'clickable', 'responsive', 'hoverable'],
|
||||
data() {
|
||||
return {
|
||||
intersectionEventBus: createEventBus(),
|
||||
visibility: {} as { [id: string]: boolean },
|
||||
};
|
||||
},
|
||||
|
|
|
@ -3,13 +3,14 @@
|
|||
* unsafe onclick attribute
|
||||
*/
|
||||
import { reactive, del, computed, onMounted, onUnmounted, getCurrentInstance } from 'vue';
|
||||
import { globalLinkActionsEventBus } from '@/event-bus';
|
||||
|
||||
const state = reactive({
|
||||
customActions: {} as Record<string, Function>,
|
||||
});
|
||||
|
||||
export default () => {
|
||||
function registerCustomAction(key: string, action: Function) {
|
||||
function registerCustomAction({ key, action }: { key: string; action: Function }) {
|
||||
state.customActions[key] = action;
|
||||
}
|
||||
function unregisterCustomAction(key: string) {
|
||||
|
@ -42,13 +43,15 @@ export default () => {
|
|||
onMounted(() => {
|
||||
const instance = getCurrentInstance();
|
||||
window.addEventListener('click', delegateClick);
|
||||
instance?.proxy.$root.$on('registerGlobalLinkAction', registerCustomAction);
|
||||
|
||||
globalLinkActionsEventBus.on('registerGlobalLinkAction', registerCustomAction);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
const instance = getCurrentInstance();
|
||||
window.removeEventListener('click', delegateClick);
|
||||
instance?.proxy.$root.$off('registerGlobalLinkAction', registerCustomAction);
|
||||
|
||||
globalLinkActionsEventBus.off('registerGlobalLinkAction', registerCustomAction);
|
||||
});
|
||||
|
||||
return {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
export * from './code-node-editor';
|
||||
export * from './data-pinning';
|
||||
export * from './link-actions';
|
||||
export * from './html-editor';
|
||||
export * from './node-view';
|
||||
|
|
3
packages/editor-ui/src/event-bus/link-actions.ts
Normal file
3
packages/editor-ui/src/event-bus/link-actions.ts
Normal file
|
@ -0,0 +1,3 @@
|
|||
import { createEventBus } from 'n8n-design-system';
|
||||
|
||||
export const globalLinkActionsEventBus = createEventBus();
|
|
@ -1,50 +0,0 @@
|
|||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
function broadcast(
|
||||
this: InstanceType<typeof EmitterMixin>,
|
||||
componentName: string,
|
||||
eventName: string,
|
||||
params: any,
|
||||
) {
|
||||
this.$children.forEach((child) => {
|
||||
const name = child.$options.name;
|
||||
|
||||
if (name === componentName) {
|
||||
// eslint-disable-next-line prefer-spread
|
||||
child.$emit.apply(child, [eventName].concat(params) as Parameters<typeof child.$emit>);
|
||||
} else {
|
||||
broadcast.apply(
|
||||
child as InstanceType<typeof EmitterMixin>,
|
||||
[componentName, eventName].concat([params]) as Parameters<typeof broadcast>,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const EmitterMixin = defineComponent({
|
||||
methods: {
|
||||
$dispatch(componentName: string, eventName: string, params: any) {
|
||||
let parent = this.$parent || this.$root;
|
||||
let name = parent.$options.name;
|
||||
|
||||
while (parent && (!name || name !== componentName)) {
|
||||
parent = parent.$parent as InstanceType<typeof EmitterMixin>;
|
||||
|
||||
if (parent) {
|
||||
name = parent.$options.name;
|
||||
}
|
||||
}
|
||||
if (parent) {
|
||||
// eslint-disable-next-line prefer-spread
|
||||
parent.$emit.apply(parent, [eventName].concat(params) as Parameters<typeof parent.$emit>);
|
||||
}
|
||||
},
|
||||
|
||||
$broadcast(componentName: string, eventName: string, params: any) {
|
||||
broadcast.call(this, componentName, eventName, params);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export default EmitterMixin;
|
|
@ -24,7 +24,7 @@ import { TelemetryHelpers } from 'n8n-workflow';
|
|||
|
||||
import { WORKFLOW_SETTINGS_MODAL_KEY } from '@/constants';
|
||||
import { getTriggerNodeServiceName } from '@/utils';
|
||||
import { codeNodeEditorEventBus } from '@/event-bus';
|
||||
import { codeNodeEditorEventBus, globalLinkActionsEventBus } from '@/event-bus';
|
||||
import { mapStores } from 'pinia';
|
||||
import { useUIStore } from '@/stores/ui.store';
|
||||
import { useWorkflowsStore } from '@/stores/workflows.store';
|
||||
|
@ -351,9 +351,12 @@ export const pushConnection = defineComponent({
|
|||
|
||||
let action;
|
||||
if (!isSavingExecutions) {
|
||||
this.$root.$emit('registerGlobalLinkAction', 'open-settings', async () => {
|
||||
if (this.workflowsStore.isNewWorkflow) await this.saveAsNewWorkflow();
|
||||
this.uiStore.openModal(WORKFLOW_SETTINGS_MODAL_KEY);
|
||||
globalLinkActionsEventBus.emit('registerGlobalLinkAction', {
|
||||
key: 'open-settings',
|
||||
action: async () => {
|
||||
if (this.workflowsStore.isNewWorkflow) await this.saveAsNewWorkflow();
|
||||
this.uiStore.openModal(WORKFLOW_SETTINGS_MODAL_KEY);
|
||||
},
|
||||
});
|
||||
|
||||
action =
|
||||
|
|
|
@ -1231,8 +1231,6 @@
|
|||
"settings.users.setupMyAccount": "Set up my owner account",
|
||||
"settings.users.setupToInviteUsers": "To invite users, set up your own account",
|
||||
"settings.users.setupToInviteUsersInfo": "Invited users won’t be able to see workflows and credentials of other users unless you upgrade. <a href=\"https://docs.n8n.io/user-management/\" target=\"_blank\">More info</a> <br /> <br />",
|
||||
"settings.users.setupSMTPInfo": "You will need details of an {link} to complete the setup.",
|
||||
"settings.users.setupSMTPInfo.link": "SMTP server",
|
||||
"settings.users.smtpToAddUsersWarning": "Set up SMTP before adding users (so that n8n can send them invitation emails). <a target=\"_blank\" href=\"https://docs.n8n.io/hosting/authentication/user-management-self-hosted/\">Instructions</a>",
|
||||
"settings.users.transferWorkflowsAndCredentials": "Transfer their workflows and credentials to another user",
|
||||
"settings.users.transferredToUser": "Data transferred to {user}",
|
||||
|
|
|
@ -672,9 +672,12 @@ export default defineComponent({
|
|||
? this.$locale.baseText('nodeView.addOrEnableTriggerNode')
|
||||
: this.$locale.baseText('nodeView.addATriggerNodeFirst');
|
||||
|
||||
this.registerCustomAction('showNodeCreator', () =>
|
||||
this.showTriggerCreator(NODE_CREATOR_OPEN_SOURCES.NO_TRIGGER_EXECUTION_TOOLTIP),
|
||||
);
|
||||
this.registerCustomAction({
|
||||
key: 'showNodeCreator',
|
||||
action: () =>
|
||||
this.showTriggerCreator(NODE_CREATOR_OPEN_SOURCES.NO_TRIGGER_EXECUTION_TOOLTIP),
|
||||
});
|
||||
|
||||
const notice = this.showMessage({
|
||||
type: 'info',
|
||||
title: this.$locale.baseText('nodeView.cantExecuteNoTrigger'),
|
||||
|
@ -1050,7 +1053,7 @@ export default defineComponent({
|
|||
}
|
||||
|
||||
if (this.$router.currentRoute.name === VIEWS.NEW_WORKFLOW) {
|
||||
this.$root.$emit('newWorkflow');
|
||||
nodeViewEventBus.emit('newWorkflow');
|
||||
} else {
|
||||
void this.$router.push({ name: VIEWS.NEW_WORKFLOW });
|
||||
}
|
||||
|
@ -3913,9 +3916,9 @@ export default defineComponent({
|
|||
window.addEventListener('message', this.onPostMessageReceived);
|
||||
window.addEventListener('pageshow', this.onPageShow);
|
||||
|
||||
this.$root.$on('newWorkflow', this.newWorkflow);
|
||||
this.$root.$on('importWorkflowData', this.onImportWorkflowDataEvent);
|
||||
this.$root.$on('importWorkflowUrl', this.onImportWorkflowUrlEvent);
|
||||
nodeViewEventBus.on('newWorkflow', this.newWorkflow);
|
||||
nodeViewEventBus.on('importWorkflowData', this.onImportWorkflowDataEvent);
|
||||
nodeViewEventBus.on('importWorkflowUrl', this.onImportWorkflowUrlEvent);
|
||||
historyBus.on('nodeMove', this.onMoveNode);
|
||||
historyBus.on('revertAddNode', this.onRevertAddNode);
|
||||
historyBus.on('revertRemoveNode', this.onRevertRemoveNode);
|
||||
|
@ -3938,9 +3941,9 @@ export default defineComponent({
|
|||
window.removeEventListener('beforeunload', this.onBeforeUnload);
|
||||
window.removeEventListener('pageshow', this.onPageShow);
|
||||
|
||||
this.$root.$off('newWorkflow', this.newWorkflow);
|
||||
this.$root.$off('importWorkflowData', this.onImportWorkflowDataEvent);
|
||||
this.$root.$off('importWorkflowUrl', this.onImportWorkflowUrlEvent);
|
||||
nodeViewEventBus.off('newWorkflow', this.newWorkflow);
|
||||
nodeViewEventBus.off('importWorkflowData', this.onImportWorkflowDataEvent);
|
||||
nodeViewEventBus.off('importWorkflowUrl', this.onImportWorkflowUrlEvent);
|
||||
historyBus.off('nodeMove', this.onMoveNode);
|
||||
historyBus.off('revertAddNode', this.onRevertAddNode);
|
||||
historyBus.off('revertRemoveNode', this.onRevertRemoveNode);
|
||||
|
@ -3959,9 +3962,9 @@ export default defineComponent({
|
|||
this.instance.destroy();
|
||||
this.uiStore.stateIsDirty = false;
|
||||
window.removeEventListener('message', this.onPostMessageReceived);
|
||||
this.$root.$off('newWorkflow', this.newWorkflow);
|
||||
this.$root.$off('importWorkflowData', this.onImportWorkflowDataEvent);
|
||||
this.$root.$off('importWorkflowUrl', this.onImportWorkflowUrlEvent);
|
||||
nodeViewEventBus.off('newWorkflow', this.newWorkflow);
|
||||
nodeViewEventBus.off('importWorkflowData', this.onImportWorkflowDataEvent);
|
||||
nodeViewEventBus.off('importWorkflowUrl', this.onImportWorkflowUrlEvent);
|
||||
this.workflowsStore.setWorkflowId(PLACEHOLDER_EMPTY_WORKFLOW_ID);
|
||||
},
|
||||
});
|
||||
|
|
|
@ -117,7 +117,7 @@ const repoUrlValidationRules: Array<Rule | RuleGroup> = [
|
|||
{
|
||||
name: 'MATCH_REGEX',
|
||||
config: {
|
||||
regex: /(?:git|ssh|https?|git@[-\w.]+):(\/\/)?(.*?)(\.git)(\/?|\#[-\d\w._]+?)$/,
|
||||
regex: /^(?!https?:\/\/)(?:git|ssh|git@[-\w.]+):(\/\/)?(.*?)(\.git)(\/?|\#[-\d\w._]+?)$/,
|
||||
message: locale.baseText('settings.versionControl.repoUrlInvalid'),
|
||||
},
|
||||
},
|
||||
|
|
|
@ -2,8 +2,8 @@ import type { IDataObject, IExecuteFunctions, ILoadOptionsFunctions } from 'n8n-
|
|||
|
||||
import type { OptionsWithUri } from 'request';
|
||||
|
||||
import flow from 'lodash.flow';
|
||||
import omit from 'lodash.omit';
|
||||
import flow from 'lodash/flow';
|
||||
import omit from 'lodash/omit';
|
||||
|
||||
import type {
|
||||
AllFieldsUi,
|
||||
|
|
|
@ -8,7 +8,7 @@ import type {
|
|||
INodePropertyOptions,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import get from 'lodash.get';
|
||||
import get from 'lodash/get';
|
||||
|
||||
/**
|
||||
* Make an API request to Asana
|
||||
|
|
|
@ -12,7 +12,7 @@ import { jsonParse, NodeOperationError } from 'n8n-workflow';
|
|||
|
||||
import { awsApiRequestSOAP } from './GenericFunctions';
|
||||
|
||||
import get from 'lodash.get';
|
||||
import get from 'lodash/get';
|
||||
|
||||
export class AwsSnsTrigger implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import get from 'lodash.get';
|
||||
import get from 'lodash/get';
|
||||
|
||||
import type {
|
||||
IDataObject,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import get from 'lodash.get';
|
||||
import get from 'lodash/get';
|
||||
|
||||
import { parseString } from 'xml2js';
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import get from 'lodash.get';
|
||||
import get from 'lodash/get';
|
||||
|
||||
import { parseString } from 'xml2js';
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import get from 'lodash.get';
|
||||
import get from 'lodash/get';
|
||||
|
||||
import { parseString } from 'xml2js';
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import get from 'lodash.get';
|
||||
import get from 'lodash/get';
|
||||
|
||||
import { parseString } from 'xml2js';
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ import type {
|
|||
} from 'n8n-workflow';
|
||||
import { NodeApiError } from 'n8n-workflow';
|
||||
|
||||
import get from 'lodash.get';
|
||||
import get from 'lodash/get';
|
||||
|
||||
export async function awsApiRequest(
|
||||
this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions | IWebhookFunctions,
|
||||
|
|
|
@ -16,7 +16,7 @@ import type {
|
|||
} from 'n8n-workflow';
|
||||
import { NodeApiError } from 'n8n-workflow';
|
||||
|
||||
import get from 'lodash.get';
|
||||
import get from 'lodash/get';
|
||||
|
||||
function getEndpointForService(
|
||||
service: string,
|
||||
|
|
|
@ -32,8 +32,8 @@ import type {
|
|||
} from './descriptions/MemberDescription';
|
||||
import { memberFields, memberOperations } from './descriptions/MemberDescription';
|
||||
|
||||
import isEmpty from 'lodash.isempty';
|
||||
import partialRight from 'lodash.partialright';
|
||||
import isEmpty from 'lodash/isEmpty';
|
||||
import partialRight from 'lodash/partialRight';
|
||||
|
||||
export class Bitwarden implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
|
|
|
@ -12,7 +12,7 @@ import type {
|
|||
} from 'n8n-workflow';
|
||||
import { NodeApiError } from 'n8n-workflow';
|
||||
|
||||
import upperFirst from 'lodash.upperfirst';
|
||||
import upperFirst from 'lodash/upperFirst';
|
||||
|
||||
import { createHash } from 'crypto';
|
||||
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
import type { IDataObject, INodeExecutionData } from 'n8n-workflow';
|
||||
|
||||
import difference from 'lodash.difference';
|
||||
import get from 'lodash.get';
|
||||
import intersection from 'lodash.intersection';
|
||||
import isEmpty from 'lodash.isempty';
|
||||
import omit from 'lodash.omit';
|
||||
import unset from 'lodash.unset';
|
||||
import difference from 'lodash/difference';
|
||||
import get from 'lodash/get';
|
||||
import intersection from 'lodash/intersection';
|
||||
import isEmpty from 'lodash/isEmpty';
|
||||
import omit from 'lodash/omit';
|
||||
import unset from 'lodash/unset';
|
||||
import { cloneDeep } from 'lodash';
|
||||
import set from 'lodash.set';
|
||||
import union from 'lodash.union';
|
||||
import set from 'lodash/set';
|
||||
import union from 'lodash/union';
|
||||
import { fuzzyCompare } from '../../utils/utilities';
|
||||
|
||||
type PairToMatch = {
|
||||
|
|
|
@ -14,8 +14,8 @@ import type {
|
|||
} from 'n8n-workflow';
|
||||
import { NodeApiError } from 'n8n-workflow';
|
||||
|
||||
import flow from 'lodash.flow';
|
||||
import omit from 'lodash.omit';
|
||||
import flow from 'lodash/flow';
|
||||
import omit from 'lodash/omit';
|
||||
|
||||
import type {
|
||||
AddressFixedCollection,
|
||||
|
|
|
@ -16,7 +16,7 @@ import { responderFields, respondersOperations } from './ResponderDescription';
|
|||
|
||||
import { jobFields, jobOperations } from './JobDescription';
|
||||
|
||||
import upperFirst from 'lodash.upperfirst';
|
||||
import upperFirst from 'lodash/upperFirst';
|
||||
|
||||
import type { IJob } from './AnalyzerInterface';
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import set from 'lodash.set';
|
||||
import set from 'lodash/set';
|
||||
|
||||
import type {
|
||||
IExecuteFunctions,
|
||||
|
|
|
@ -7,7 +7,7 @@ import type {
|
|||
IHttpRequestOptions,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import get from 'lodash.get';
|
||||
import get from 'lodash/get';
|
||||
|
||||
export async function customerIoApiRequest(
|
||||
this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions,
|
||||
|
|
|
@ -12,7 +12,7 @@ import type {
|
|||
|
||||
import { deepCopy, NodeOperationError } from 'n8n-workflow';
|
||||
|
||||
import set from 'lodash.set';
|
||||
import set from 'lodash/set';
|
||||
|
||||
import moment from 'moment-timezone';
|
||||
|
||||
|
|
10
packages/nodes-base/nodes/DebugHelper/DebugHelper.node.json
Normal file
10
packages/nodes-base/nodes/DebugHelper/DebugHelper.node.json
Normal file
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"node": "n8n-nodes-base.debughelper",
|
||||
"nodeVersion": "1.0",
|
||||
"codexVersion": "1.0",
|
||||
"categories": ["Development"],
|
||||
"resources": {
|
||||
"credentialDocumentation": [],
|
||||
"primaryDocumentation": []
|
||||
}
|
||||
}
|
374
packages/nodes-base/nodes/DebugHelper/DebugHelper.node.ts
Normal file
374
packages/nodes-base/nodes/DebugHelper/DebugHelper.node.ts
Normal file
|
@ -0,0 +1,374 @@
|
|||
import type {
|
||||
IExecuteFunctions,
|
||||
INodeExecutionData,
|
||||
INodeType,
|
||||
INodeTypeDescription,
|
||||
} from 'n8n-workflow';
|
||||
import { NodeApiError, NodeOperationError } from 'n8n-workflow';
|
||||
import {
|
||||
generateCreditCard,
|
||||
generateIPv4,
|
||||
generateIPv6,
|
||||
generateLocation,
|
||||
generateMAC,
|
||||
generateNanoid,
|
||||
generateRandomAddress,
|
||||
generateRandomEmail,
|
||||
generateRandomUser,
|
||||
generateURL,
|
||||
generateUUID,
|
||||
generateVersion,
|
||||
} from './randomData';
|
||||
import { setSeed, array as mfArray } from 'minifaker';
|
||||
import { generateGarbageMemory, runGarbageCollector } from './functions';
|
||||
|
||||
export class DebugHelper implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
displayName: 'DebugHelper',
|
||||
name: 'debugHelper',
|
||||
icon: 'file:DebugHelper.svg',
|
||||
group: ['output'],
|
||||
subtitle: '={{$parameter["category"]}}',
|
||||
description: 'Causes problems intentionally and generates useful data for debugging',
|
||||
version: 1,
|
||||
defaults: {
|
||||
name: 'DebugHelper',
|
||||
},
|
||||
inputs: ['main'],
|
||||
outputs: ['main'],
|
||||
credentials: [],
|
||||
properties: [
|
||||
{
|
||||
displayName: 'Category',
|
||||
name: 'category',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
options: [
|
||||
{
|
||||
name: 'Do Nothing',
|
||||
value: 'doNothing',
|
||||
description: 'Does nothing',
|
||||
},
|
||||
{
|
||||
name: 'Throw Error',
|
||||
value: 'throwError',
|
||||
description: 'Throws an error with the specified type and message',
|
||||
},
|
||||
{
|
||||
name: 'Out Of Memory',
|
||||
value: 'oom',
|
||||
description: 'Generates a large amount of memory to cause an out of memory error',
|
||||
},
|
||||
{
|
||||
name: 'Generate Random Data',
|
||||
value: 'randomData',
|
||||
description: 'Generates random data sets',
|
||||
},
|
||||
],
|
||||
default: 'throwError',
|
||||
},
|
||||
{
|
||||
displayName: 'Error Type',
|
||||
name: 'throwErrorType',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
options: [
|
||||
{
|
||||
name: 'NodeApiError',
|
||||
value: 'NodeApiError',
|
||||
},
|
||||
{
|
||||
name: 'NodeOperationError',
|
||||
value: 'NodeOperationError',
|
||||
},
|
||||
{
|
||||
name: 'Error',
|
||||
value: 'Error',
|
||||
},
|
||||
],
|
||||
default: 'NodeApiError',
|
||||
displayOptions: {
|
||||
show: {
|
||||
category: ['throwError'],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Error Message',
|
||||
name: 'throwErrorMessage',
|
||||
type: 'string',
|
||||
default: 'Node has thrown an error',
|
||||
description: 'The message to send as part of the error',
|
||||
displayOptions: {
|
||||
show: {
|
||||
category: ['throwError'],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Memory Size to Generate',
|
||||
name: 'memorySizeValue',
|
||||
type: 'number',
|
||||
default: 10,
|
||||
description: 'The approximate amount of memory to generate. Be generous...',
|
||||
displayOptions: {
|
||||
show: {
|
||||
category: ['oom'],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Data Type',
|
||||
name: 'randomDataType',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
options: [
|
||||
{
|
||||
name: 'Address',
|
||||
value: 'address',
|
||||
},
|
||||
{
|
||||
name: 'Coordinates',
|
||||
value: 'latLong',
|
||||
},
|
||||
{
|
||||
name: 'Credit Card',
|
||||
value: 'creditCard',
|
||||
},
|
||||
{
|
||||
name: 'Email',
|
||||
value: 'email',
|
||||
},
|
||||
{
|
||||
name: 'IPv4',
|
||||
value: 'ipv4',
|
||||
},
|
||||
{
|
||||
name: 'IPv6',
|
||||
value: 'ipv6',
|
||||
},
|
||||
{
|
||||
name: 'MAC',
|
||||
value: 'macAddress',
|
||||
},
|
||||
{
|
||||
name: 'NanoIds',
|
||||
value: 'nanoid',
|
||||
},
|
||||
{
|
||||
name: 'URL',
|
||||
value: 'url',
|
||||
},
|
||||
{
|
||||
name: 'User Data',
|
||||
value: 'user',
|
||||
},
|
||||
{
|
||||
name: 'UUID',
|
||||
value: 'uuid',
|
||||
},
|
||||
{
|
||||
name: 'Version',
|
||||
value: 'semver',
|
||||
},
|
||||
],
|
||||
default: 'user',
|
||||
displayOptions: {
|
||||
show: {
|
||||
category: ['randomData'],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'NanoId Alphabet',
|
||||
name: 'nanoidAlphabet',
|
||||
type: 'string',
|
||||
default: '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',
|
||||
description: 'The alphabet to use for generating the nanoIds',
|
||||
displayOptions: {
|
||||
show: {
|
||||
category: ['randomData'],
|
||||
randomDataType: ['nanoid'],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'NanoId Length',
|
||||
name: 'nanoidLength',
|
||||
type: 'string',
|
||||
default: '16',
|
||||
description: 'The length of each nanoIds',
|
||||
displayOptions: {
|
||||
show: {
|
||||
category: ['randomData'],
|
||||
randomDataType: ['nanoid'],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Seed',
|
||||
name: 'randomDataSeed',
|
||||
type: 'string',
|
||||
default: '',
|
||||
placeholder: 'Leave empty for random seed',
|
||||
description:
|
||||
'If set, seed to use for generating the data (same seed will generate the same data)',
|
||||
displayOptions: {
|
||||
show: {
|
||||
category: ['randomData'],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Number of Items to Generate',
|
||||
name: 'randomDataCount',
|
||||
type: 'number',
|
||||
default: 10,
|
||||
description: 'The number of random data items to generate into an array',
|
||||
displayOptions: {
|
||||
show: {
|
||||
category: ['randomData'],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Output as Single Array',
|
||||
name: 'randomDataSingleArray',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: 'Whether to output a single array instead of multiple items',
|
||||
displayOptions: {
|
||||
show: {
|
||||
category: ['randomData'],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
||||
const items = this.getInputData();
|
||||
const returnData: INodeExecutionData[] = [];
|
||||
const category = this.getNodeParameter('category', 0) as string;
|
||||
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
try {
|
||||
switch (category) {
|
||||
case 'doNothing':
|
||||
// as it says on the tin...
|
||||
break;
|
||||
case 'throwError':
|
||||
const throwErrorType = this.getNodeParameter('throwErrorType', 0) as string;
|
||||
const throwErrorMessage = this.getNodeParameter('throwErrorMessage', 0) as string;
|
||||
switch (throwErrorType) {
|
||||
case 'NodeApiError':
|
||||
throw new NodeApiError(
|
||||
this.getNode(),
|
||||
{ message: throwErrorMessage },
|
||||
{ message: throwErrorMessage },
|
||||
);
|
||||
case 'NodeOperationError':
|
||||
throw new NodeOperationError(this.getNode(), throwErrorMessage, {
|
||||
message: throwErrorMessage,
|
||||
});
|
||||
case 'Error':
|
||||
// eslint-disable-next-line n8n-nodes-base/node-execute-block-wrong-error-thrown
|
||||
throw new Error(throwErrorMessage);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
case 'oom':
|
||||
const memorySizeValue = this.getNodeParameter('memorySizeValue', 0) as number;
|
||||
runGarbageCollector();
|
||||
const memUsed = generateGarbageMemory(memorySizeValue);
|
||||
items[i].json = memUsed;
|
||||
returnData.push(items[i]);
|
||||
break;
|
||||
case 'randomData':
|
||||
const randomDataType = this.getNodeParameter('randomDataType', 0) as string;
|
||||
const randomDataCount = this.getNodeParameter('randomDataCount', 0) as number;
|
||||
const randomDataSeed = this.getNodeParameter('randomDataSeed', 0) as string;
|
||||
const randomDataSingleArray = this.getNodeParameter(
|
||||
'randomDataSingleArray',
|
||||
0,
|
||||
) as boolean;
|
||||
const newItem: INodeExecutionData = {
|
||||
json: {},
|
||||
pairedItem: { item: i },
|
||||
};
|
||||
if (randomDataSeed !== '') {
|
||||
setSeed(randomDataSeed);
|
||||
}
|
||||
|
||||
let randomFn: () => any = generateRandomUser;
|
||||
switch (randomDataType) {
|
||||
case 'user':
|
||||
randomFn = generateRandomUser;
|
||||
break;
|
||||
case 'email':
|
||||
randomFn = generateRandomEmail;
|
||||
break;
|
||||
case 'address':
|
||||
randomFn = generateRandomAddress;
|
||||
break;
|
||||
case 'creditCard':
|
||||
randomFn = generateCreditCard;
|
||||
break;
|
||||
case 'uuid':
|
||||
randomFn = generateUUID;
|
||||
break;
|
||||
case 'macAddress':
|
||||
randomFn = generateMAC;
|
||||
break;
|
||||
case 'ipv4':
|
||||
randomFn = generateIPv4;
|
||||
break;
|
||||
case 'ipv6':
|
||||
randomFn = generateIPv6;
|
||||
break;
|
||||
case 'latLong':
|
||||
randomFn = generateLocation;
|
||||
break;
|
||||
case 'semver':
|
||||
randomFn = generateVersion;
|
||||
break;
|
||||
case 'url':
|
||||
randomFn = generateURL;
|
||||
break;
|
||||
case 'nanoid':
|
||||
const nanoidAlphabet = this.getNodeParameter('nanoidAlphabet', 0) as string;
|
||||
const nanoidLength = this.getNodeParameter('nanoidLength', 0) as string;
|
||||
randomFn = () => generateNanoid(nanoidAlphabet, nanoidLength);
|
||||
break;
|
||||
}
|
||||
const generatedItems = mfArray(randomDataCount, randomFn);
|
||||
if (randomDataSingleArray) {
|
||||
newItem.json = { generatedItems };
|
||||
returnData.push(newItem);
|
||||
} else {
|
||||
for (const generatedItem of generatedItems) {
|
||||
returnData.push({
|
||||
json: generatedItem,
|
||||
pairedItem: { item: i },
|
||||
});
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} catch (error) {
|
||||
if (this.continueOnFail()) {
|
||||
const executionErrorData = this.helpers.constructExecutionMetaData(
|
||||
this.helpers.returnJsonArray({ error: error.message }),
|
||||
{ itemData: { item: i } },
|
||||
);
|
||||
returnData.push(...executionErrorData);
|
||||
continue;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
return this.prepareOutputData(returnData);
|
||||
}
|
||||
}
|
28
packages/nodes-base/nodes/DebugHelper/DebugHelper.svg
Normal file
28
packages/nodes-base/nodes/DebugHelper/DebugHelper.svg
Normal file
|
@ -0,0 +1,28 @@
|
|||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg height="800px" width="800px" version="1.1" id="Layer_1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 512 512" xml:space="preserve">
|
||||
<path style="fill:#F4A026;" d="M345,167c32.953,0,46.333,40.047,46.333,73v100.333c0,74.375-60.292,134.667-134.667,134.667l0,0
|
||||
C182.292,475,122,414.708,122,340.333V240c0-32.953,13.38-73,46.333-73H345z"/>
|
||||
<path style="fill:#CA463D;" d="M309,200.333H204.333C185.372,200.333,170,184.961,170,166l0,0c0-39.948,32.385-72.333,72.333-72.333
|
||||
H271c39.948,0,72.333,32.385,72.333,72.333l0,0C343.333,184.961,327.961,200.333,309,200.333z"/>
|
||||
<path d="M498.667,290.667H404V240c0-1.016,0.313-2.036,0.291-3.055C452.4,231.927,480,200.272,480,148.333v-12
|
||||
c0-7.364-5.971-13.333-13.333-13.333s-13.333,5.969-13.333,13.333v12c0,38.399-17.005,58.821-51.885,62.167
|
||||
c-6.069-27.025-20.875-50.381-45.537-55.7c-3.745-28.54-21.413-52.689-46.115-65.227c10.321-10.501,16.871-24.887,16.871-40.74V37
|
||||
c0-7.364-5.971-13.333-13.333-13.333S300,29.636,300,37v11.833C300,66.203,285.536,80,268.167,80h-23
|
||||
c-17.369,0-31.833-13.797-31.833-31.167V37c0-7.364-5.971-13.333-13.333-13.333S186.667,29.636,186.667,37v11.833
|
||||
c0,15.853,6.549,30.239,16.871,40.741c-24.701,12.537-42.453,36.687-46.199,65.227c-24.695,5.324-39.465,28.736-45.519,55.808
|
||||
c-35.759-2.96-53.153-23.403-53.153-62.276v-12c0-7.364-5.971-13.333-13.333-13.333S32,128.969,32,136.333v12
|
||||
c0,52.415,27.439,84.168,76.375,88.739C108.353,238.048,108,239.025,108,240v50.667H13.333C5.971,290.667,0,296.636,0,304
|
||||
c0,7.364,5.971,13.333,13.333,13.333H108v23c0,10.628,1.469,20.993,3.608,30.992C60.659,374.777,32,406.773,32,460.333v12
|
||||
c0,7.364,5.971,13.333,13.333,13.333s13.333-5.969,13.333-13.333v-12c0-41.795,20.151-62.291,61.565-62.649
|
||||
c22.451,53.208,75.151,90.649,136.435,90.649c61.276,0,113.971-37.432,136.425-90.629c40.519,0.784,60.241,21.283,60.241,62.629v12
|
||||
c0,7.364,5.971,13.333,13.333,13.333S480,479.697,480,472.333v-12c0-53.1-28.823-85.013-78.96-88.921
|
||||
c2.151-10.025,2.96-20.421,2.96-31.079v-23h94.667c7.363,0,13.333-5.969,13.333-13.333C512,296.636,506.029,290.667,498.667,290.667
|
||||
z M242.333,106.667h2.833h23H271c32.532,0,59,26.468,59,59c0,11.58-9.42,21-21,21H204.333c-11.58,0-21-9.42-21-21
|
||||
C183.333,133.135,209.801,106.667,242.333,106.667z M134.667,340.333V240c0-20.793,6.948-50.531,24.483-58.035
|
||||
c6.627,18.368,24.56,31.368,45.184,31.368h38.333v247.521C182.333,453.891,134.667,402.501,134.667,340.333z M269.333,461.007
|
||||
V213.333H309c20.624,0,37.891-13,44.517-31.368c17.535,7.504,23.816,37.241,23.816,58.035v100.333
|
||||
C377.333,402.96,330.307,454.653,269.333,461.007z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 2.7 KiB |
30
packages/nodes-base/nodes/DebugHelper/functions.ts
Normal file
30
packages/nodes-base/nodes/DebugHelper/functions.ts
Normal file
|
@ -0,0 +1,30 @@
|
|||
import { setFlagsFromString } from 'v8';
|
||||
import { runInNewContext } from 'vm';
|
||||
|
||||
export const runGarbageCollector = () => {
|
||||
try {
|
||||
setFlagsFromString('--expose_gc');
|
||||
const gc = runInNewContext('gc'); // nocommit
|
||||
gc();
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
};
|
||||
|
||||
export const generateGarbageMemory = (sizeInMB: number, onHeap = true) => {
|
||||
const divider = onHeap ? 8 : 1;
|
||||
const size = Math.max(1, Math.floor((sizeInMB * 1024 * 1024) / divider));
|
||||
if (onHeap) {
|
||||
// arrays are allocated on the heap
|
||||
// size in this case is only an approximation...
|
||||
const array = Array(size);
|
||||
array.fill(0);
|
||||
} else {
|
||||
const array = new Uint8Array(size);
|
||||
array.fill(0);
|
||||
}
|
||||
// const used = process.memoryUsage().heapUsed / 1024 / 1024;
|
||||
// const external = process.memoryUsage().external / 1024 / 1024;
|
||||
// console.log(`heap: ${used} MB / external: ${external} MB`);
|
||||
return { ...process.memoryUsage() };
|
||||
};
|
101
packages/nodes-base/nodes/DebugHelper/randomData.ts
Normal file
101
packages/nodes-base/nodes/DebugHelper/randomData.ts
Normal file
|
@ -0,0 +1,101 @@
|
|||
import {
|
||||
firstName,
|
||||
lastName,
|
||||
streetAddress,
|
||||
cityName,
|
||||
zipCode,
|
||||
state,
|
||||
country,
|
||||
password,
|
||||
creditCardNumber,
|
||||
creditCardCVV,
|
||||
email,
|
||||
boolean,
|
||||
uuid,
|
||||
nanoId,
|
||||
domainUrl,
|
||||
semver,
|
||||
latLong,
|
||||
macAddress,
|
||||
ip,
|
||||
ipv6,
|
||||
number,
|
||||
} from 'minifaker';
|
||||
import 'minifaker/locales/en';
|
||||
|
||||
export function generateRandomUser() {
|
||||
return {
|
||||
uid: uuid.v4(),
|
||||
email: email(),
|
||||
firstname: firstName(),
|
||||
lastname: lastName(),
|
||||
password: password(),
|
||||
};
|
||||
}
|
||||
|
||||
export function generateRandomAddress() {
|
||||
return {
|
||||
firstname: firstName(),
|
||||
lastname: lastName(),
|
||||
street: streetAddress(),
|
||||
city: cityName(),
|
||||
zip: zipCode({ format: '#####' }),
|
||||
state: state(),
|
||||
country: country(),
|
||||
};
|
||||
}
|
||||
|
||||
export function generateRandomEmail() {
|
||||
return {
|
||||
email: email(),
|
||||
confirmed: boolean(),
|
||||
};
|
||||
}
|
||||
|
||||
export function generateUUID() {
|
||||
return { uuid: uuid.v4() };
|
||||
}
|
||||
|
||||
export function generateNanoid(customAlphabet: string, length: string) {
|
||||
return { nanoId: nanoId.customAlphabet(customAlphabet, parseInt(length, 10))().toString() };
|
||||
}
|
||||
|
||||
export function generateCreditCard() {
|
||||
return {
|
||||
type: boolean() ? 'MasterCard' : 'Visa',
|
||||
number: creditCardNumber(),
|
||||
ccv: creditCardCVV(),
|
||||
exp: `${number({ min: 1, max: 12, float: false }).toString().padStart(2, '0')}/${number({
|
||||
min: 1,
|
||||
max: 40,
|
||||
float: false,
|
||||
})
|
||||
.toString()
|
||||
.padStart(2, '0')}`,
|
||||
holder_name: `${firstName()} ${lastName()}`,
|
||||
};
|
||||
}
|
||||
|
||||
export function generateURL() {
|
||||
return { url: domainUrl() };
|
||||
}
|
||||
|
||||
export function generateIPv4() {
|
||||
return { ip: ip() };
|
||||
}
|
||||
|
||||
export function generateIPv6() {
|
||||
return { ipv6: ipv6() };
|
||||
}
|
||||
|
||||
export function generateMAC() {
|
||||
return { mac: macAddress() };
|
||||
}
|
||||
|
||||
export function generateLocation() {
|
||||
return { location: latLong() };
|
||||
}
|
||||
|
||||
export function generateVersion() {
|
||||
return { version: semver() };
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
import flow from 'lodash.flow';
|
||||
import sortBy from 'lodash.sortby';
|
||||
import uniqBy from 'lodash.uniqby';
|
||||
import flow from 'lodash/flow';
|
||||
import sortBy from 'lodash/sortBy';
|
||||
import uniqBy from 'lodash/uniqBy';
|
||||
|
||||
export type DocumentProperties = {
|
||||
customProperty: Array<{ field: string; value: string }>;
|
||||
|
|
|
@ -13,7 +13,7 @@ import { documentFields, documentOperations, indexFields, indexOperations } from
|
|||
|
||||
import type { DocumentGetAllOptions, FieldsUiValues } from './types';
|
||||
|
||||
import omit from 'lodash.omit';
|
||||
import omit from 'lodash/omit';
|
||||
|
||||
export class Elasticsearch implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
|
|
|
@ -21,8 +21,8 @@ import { connect as imapConnect, getParts } from 'imap-simple';
|
|||
import type { Source as ParserSource } from 'mailparser';
|
||||
import { simpleParser } from 'mailparser';
|
||||
|
||||
import isEmpty from 'lodash.isempty';
|
||||
import find from 'lodash.find';
|
||||
import isEmpty from 'lodash/isEmpty';
|
||||
import find from 'lodash/find';
|
||||
|
||||
export async function parseRawEmail(
|
||||
this: ITriggerFunctions,
|
||||
|
|
|
@ -20,8 +20,8 @@ import { connect as imapConnect, getParts } from 'imap-simple';
|
|||
import type { Source as ParserSource } from 'mailparser';
|
||||
import { simpleParser } from 'mailparser';
|
||||
|
||||
import isEmpty from 'lodash.isempty';
|
||||
import find from 'lodash.find';
|
||||
import isEmpty from 'lodash/isEmpty';
|
||||
import find from 'lodash/find';
|
||||
import type { ICredentialsDataImap } from '../../../credentials/Imap.credentials';
|
||||
import { isCredentialsDataImap } from '../../../credentials/Imap.credentials';
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ import { campaignFields, campaignOperations } from './CampaignDescription';
|
|||
|
||||
import { contactListFields, contactListOperations } from './ContactListDescription';
|
||||
|
||||
import isEmpty from 'lodash.isempty';
|
||||
import isEmpty from 'lodash/isEmpty';
|
||||
|
||||
export class Emelia implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
|
|
|
@ -16,7 +16,7 @@ import type {
|
|||
|
||||
import type { OptionsWithUri } from 'request';
|
||||
|
||||
import omit from 'lodash.omit';
|
||||
import { omit } from 'lodash';
|
||||
|
||||
export async function freshserviceApiRequest(
|
||||
this: IExecuteFunctions | IHookFunctions | ILoadOptionsFunctions,
|
||||
|
|
|
@ -15,7 +15,7 @@ import type {
|
|||
ViewsResponse,
|
||||
} from './types';
|
||||
|
||||
import omit from 'lodash.omit';
|
||||
import omit from 'lodash/omit';
|
||||
|
||||
export async function freshworksCrmApiRequest(
|
||||
this: IExecuteFunctions | ILoadOptionsFunctions,
|
||||
|
|
|
@ -35,8 +35,8 @@ import {
|
|||
loadWebinarSessions,
|
||||
} from './GenericFunctions';
|
||||
|
||||
import isEmpty from 'lodash.isempty';
|
||||
import omit from 'lodash.omit';
|
||||
import isEmpty from 'lodash/isEmpty';
|
||||
import omit from 'lodash/omit';
|
||||
|
||||
import moment from 'moment-timezone';
|
||||
|
||||
|
|
|
@ -17,13 +17,14 @@ import { NodeApiError, NodeOperationError } from 'n8n-workflow';
|
|||
|
||||
import { DateTime } from 'luxon';
|
||||
|
||||
import isEmpty from 'lodash.isempty';
|
||||
import isEmpty from 'lodash/isEmpty';
|
||||
|
||||
export interface IEmail {
|
||||
from?: string;
|
||||
to?: string;
|
||||
cc?: string;
|
||||
bcc?: string;
|
||||
replyTo?: string;
|
||||
inReplyTo?: string;
|
||||
reference?: string;
|
||||
subject: string;
|
||||
|
@ -222,6 +223,7 @@ export async function encodeEmail(email: IEmail) {
|
|||
to: email.to,
|
||||
cc: email.cc,
|
||||
bcc: email.bcc,
|
||||
replyTo: email.replyTo,
|
||||
inReplyTo: email.inReplyTo,
|
||||
references: email.reference,
|
||||
subject: email.subject,
|
||||
|
@ -509,7 +511,7 @@ export function unescapeSnippets(items: INodeExecutionData[]) {
|
|||
return result;
|
||||
}
|
||||
|
||||
export async function replayToEmail(
|
||||
export async function replyToEmail(
|
||||
this: IExecuteFunctions,
|
||||
items: INodeExecutionData[],
|
||||
gmailId: string,
|
||||
|
|
|
@ -28,7 +28,7 @@ import { labelFields, labelOperations } from './LabelDescription';
|
|||
|
||||
import { draftFields, draftOperations } from './DraftDescription';
|
||||
|
||||
import isEmpty from 'lodash.isempty';
|
||||
import isEmpty from 'lodash/isEmpty';
|
||||
|
||||
import { oldVersionNotice } from '../../../../utils/descriptions';
|
||||
|
||||
|
|
|
@ -143,6 +143,14 @@ export const draftFields: INodeProperties[] = [
|
|||
placeholder: 'info@example.com',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Send Replies To',
|
||||
name: 'replyTo',
|
||||
type: 'string',
|
||||
placeholder: 'reply@example.com',
|
||||
default: '',
|
||||
description: 'The email address that the reply message is sent to',
|
||||
},
|
||||
{
|
||||
displayName: 'Attachments',
|
||||
name: 'attachmentsUi',
|
||||
|
|
|
@ -21,7 +21,7 @@ import {
|
|||
prepareEmailBody,
|
||||
prepareEmailsInput,
|
||||
prepareQuery,
|
||||
replayToEmail,
|
||||
replyToEmail,
|
||||
simplifyOutput,
|
||||
unescapeSnippets,
|
||||
} from '../GenericFunctions';
|
||||
|
@ -282,6 +282,7 @@ export class GmailV2 implements INodeType {
|
|||
const to = prepareEmailsInput.call(this, sendTo, 'To', i);
|
||||
let cc = '';
|
||||
let bcc = '';
|
||||
let replyTo = '';
|
||||
|
||||
if (options.ccList) {
|
||||
cc = prepareEmailsInput.call(this, options.ccList as string, 'CC', i);
|
||||
|
@ -291,6 +292,10 @@ export class GmailV2 implements INodeType {
|
|||
bcc = prepareEmailsInput.call(this, options.bccList as string, 'BCC', i);
|
||||
}
|
||||
|
||||
if (options.replyTo) {
|
||||
replyTo = prepareEmailsInput.call(this, options.replyTo as string, 'ReplyTo', i);
|
||||
}
|
||||
|
||||
let attachments: IDataObject[] = [];
|
||||
|
||||
if (options.attachmentsUi) {
|
||||
|
@ -323,6 +328,7 @@ export class GmailV2 implements INodeType {
|
|||
to,
|
||||
cc,
|
||||
bcc,
|
||||
replyTo,
|
||||
subject: this.getNodeParameter('subject', i) as string,
|
||||
...prepareEmailBody.call(this, i),
|
||||
attachments,
|
||||
|
@ -340,7 +346,7 @@ export class GmailV2 implements INodeType {
|
|||
const messageIdGmail = this.getNodeParameter('messageId', i) as string;
|
||||
const options = this.getNodeParameter('options', i);
|
||||
|
||||
responseData = await replayToEmail.call(this, items, messageIdGmail, options, i);
|
||||
responseData = await replyToEmail.call(this, items, messageIdGmail, options, i);
|
||||
}
|
||||
if (operation === 'get') {
|
||||
//https://developers.google.com/gmail/api/v1/reference/users/messages/get
|
||||
|
@ -514,6 +520,7 @@ export class GmailV2 implements INodeType {
|
|||
let to = '';
|
||||
let cc = '';
|
||||
let bcc = '';
|
||||
let replyTo = '';
|
||||
|
||||
if (options.sendTo) {
|
||||
to += prepareEmailsInput.call(this, options.sendTo as string, 'To', i);
|
||||
|
@ -527,6 +534,10 @@ export class GmailV2 implements INodeType {
|
|||
bcc = prepareEmailsInput.call(this, options.bccList as string, 'BCC', i);
|
||||
}
|
||||
|
||||
if (options.replyTo) {
|
||||
replyTo = prepareEmailsInput.call(this, options.replyTo as string, 'ReplyTo', i);
|
||||
}
|
||||
|
||||
let attachments: IDataObject[] = [];
|
||||
if (options.attachmentsUi) {
|
||||
attachments = await prepareEmailAttachments.call(
|
||||
|
@ -547,6 +558,7 @@ export class GmailV2 implements INodeType {
|
|||
to,
|
||||
cc,
|
||||
bcc,
|
||||
replyTo,
|
||||
subject: this.getNodeParameter('subject', i) as string,
|
||||
...prepareEmailBody.call(this, i),
|
||||
attachments,
|
||||
|
@ -741,7 +753,7 @@ export class GmailV2 implements INodeType {
|
|||
const messageIdGmail = this.getNodeParameter('messageId', i) as string;
|
||||
const options = this.getNodeParameter('options', i);
|
||||
|
||||
responseData = await replayToEmail.call(this, items, messageIdGmail, options, i);
|
||||
responseData = await replyToEmail.call(this, items, messageIdGmail, options, i);
|
||||
}
|
||||
if (operation === 'trash') {
|
||||
//https://developers.google.com/gmail/api/reference/rest/v1/users.threads/trash
|
||||
|
|
|
@ -225,6 +225,19 @@ export const messageFields: INodeProperties[] = [
|
|||
default: '',
|
||||
description: "The name that will be shown in recipients' inboxes",
|
||||
},
|
||||
{
|
||||
displayName: 'Send Replies To',
|
||||
name: 'replyTo',
|
||||
type: 'string',
|
||||
placeholder: 'reply@example.com',
|
||||
default: '',
|
||||
description: 'The email address that the reply message is sent to',
|
||||
displayOptions: {
|
||||
hide: {
|
||||
'/operation': ['reply'],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Reply to Sender Only',
|
||||
name: 'replyToSenderOnly',
|
||||
|
|
|
@ -3,8 +3,8 @@ import { apiRequest } from './v2/transport';
|
|||
import type { SheetDataRow, SheetRangeData } from './v2/helpers/GoogleSheets.types';
|
||||
|
||||
import * as XLSX from 'xlsx';
|
||||
import isEqual from 'lodash.isequal';
|
||||
import zip from 'lodash.zip';
|
||||
import isEqual from 'lodash/isEqual';
|
||||
import zip from 'lodash/zip';
|
||||
|
||||
export const BINARY_MIME_TYPE = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ import { googleApiRequest } from './GenericFunctions';
|
|||
|
||||
import { utils as xlsxUtils } from 'xlsx';
|
||||
|
||||
import get from 'lodash.get';
|
||||
import get from 'lodash/get';
|
||||
|
||||
export interface ISheetOptions {
|
||||
scope: string[];
|
||||
|
|
|
@ -7,7 +7,7 @@ import type {
|
|||
import { NodeOperationError } from 'n8n-workflow';
|
||||
import { apiRequest } from '../transport';
|
||||
import { utils as xlsxUtils } from 'xlsx';
|
||||
import get from 'lodash.get';
|
||||
import get from 'lodash/get';
|
||||
import type {
|
||||
ILookupValues,
|
||||
ISheetUpdateData,
|
||||
|
|
|
@ -10,7 +10,7 @@ import type {
|
|||
} from 'n8n-workflow';
|
||||
import { NodeApiError } from 'n8n-workflow';
|
||||
|
||||
import get from 'lodash.get';
|
||||
import get from 'lodash/get';
|
||||
|
||||
export async function helpscoutApiRequest(
|
||||
this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions | IHookFunctions,
|
||||
|
|
|
@ -8,7 +8,7 @@ import type {
|
|||
} from 'n8n-workflow';
|
||||
import { NodeOperationError } from 'n8n-workflow';
|
||||
|
||||
import get from 'lodash.get';
|
||||
import get from 'lodash/get';
|
||||
|
||||
type Cheerio = ReturnType<typeof cheerio>;
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import type { IDataObject, INodeExecutionData, IOAuth2Options } from 'n8n-workflow';
|
||||
import type { OptionsWithUri } from 'request-promise-native';
|
||||
|
||||
import set from 'lodash.set';
|
||||
import set from 'lodash/set';
|
||||
|
||||
export type BodyParameter = { name: string; value: string };
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ import type {
|
|||
} from 'n8n-workflow';
|
||||
import { NodeApiError, NodeOperationError } from 'n8n-workflow';
|
||||
|
||||
import get from 'lodash.get';
|
||||
import get from 'lodash/get';
|
||||
|
||||
export const eventID: { [key: string]: string } = {
|
||||
create_client: '1',
|
||||
|
|
|
@ -12,16 +12,16 @@ import type {
|
|||
} from 'n8n-workflow';
|
||||
import { NodeOperationError } from 'n8n-workflow';
|
||||
|
||||
import get from 'lodash.get';
|
||||
import isEmpty from 'lodash.isempty';
|
||||
import isEqual from 'lodash.isequal';
|
||||
import isObject from 'lodash.isobject';
|
||||
import lt from 'lodash.lt';
|
||||
import merge from 'lodash.merge';
|
||||
import pick from 'lodash.pick';
|
||||
import reduce from 'lodash.reduce';
|
||||
import set from 'lodash.set';
|
||||
import unset from 'lodash.unset';
|
||||
import get from 'lodash/get';
|
||||
import isEmpty from 'lodash/isEmpty';
|
||||
import isEqual from 'lodash/isEqual';
|
||||
import isObject from 'lodash/isObject';
|
||||
import lt from 'lodash/lt';
|
||||
import merge from 'lodash/merge';
|
||||
import pick from 'lodash/pick';
|
||||
import reduce from 'lodash/reduce';
|
||||
import set from 'lodash/set';
|
||||
import unset from 'lodash/unset';
|
||||
|
||||
const compareItems = (
|
||||
obj: INodeExecutionData,
|
||||
|
|
|
@ -7,7 +7,7 @@ import type {
|
|||
} from 'n8n-workflow';
|
||||
import { NodeOperationError } from 'n8n-workflow';
|
||||
|
||||
import get from 'lodash.get';
|
||||
import get from 'lodash/get';
|
||||
|
||||
type AggregationType =
|
||||
| 'append'
|
||||
|
|
|
@ -12,16 +12,16 @@ import type {
|
|||
} from 'n8n-workflow';
|
||||
import { NodeOperationError, deepCopy } from 'n8n-workflow';
|
||||
|
||||
import get from 'lodash.get';
|
||||
import isEmpty from 'lodash.isempty';
|
||||
import isEqual from 'lodash.isequal';
|
||||
import isObject from 'lodash.isobject';
|
||||
import lt from 'lodash.lt';
|
||||
import merge from 'lodash.merge';
|
||||
import pick from 'lodash.pick';
|
||||
import reduce from 'lodash.reduce';
|
||||
import set from 'lodash.set';
|
||||
import unset from 'lodash.unset';
|
||||
import get from 'lodash/get';
|
||||
import isEmpty from 'lodash/isEmpty';
|
||||
import isEqual from 'lodash/isEqual';
|
||||
import isObject from 'lodash/isObject';
|
||||
import lt from 'lodash/lt';
|
||||
import merge from 'lodash/merge';
|
||||
import pick from 'lodash/pick';
|
||||
import reduce from 'lodash/reduce';
|
||||
import set from 'lodash/set';
|
||||
import unset from 'lodash/unset';
|
||||
|
||||
const compareItems = (
|
||||
obj: INodeExecutionData,
|
||||
|
|
|
@ -7,7 +7,7 @@ import type {
|
|||
} from 'n8n-workflow';
|
||||
import { NodeOperationError } from 'n8n-workflow';
|
||||
|
||||
import get from 'lodash.get';
|
||||
import get from 'lodash/get';
|
||||
|
||||
type AggregationType =
|
||||
| 'append'
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import type { Readable } from 'stream';
|
||||
import mergeWith from 'lodash.mergewith';
|
||||
import mergeWith from 'lodash/mergeWith';
|
||||
|
||||
import type {
|
||||
IBinaryKeyData,
|
||||
|
|
|
@ -9,21 +9,21 @@ import type {
|
|||
IWebhookFunctions,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import set from 'lodash.set';
|
||||
import concat from 'lodash.concat';
|
||||
import split from 'lodash.split';
|
||||
import every from 'lodash.every';
|
||||
import toString from 'lodash.tostring';
|
||||
import toNumber from 'lodash.tonumber';
|
||||
import isString from 'lodash.isstring';
|
||||
import compact from 'lodash.compact';
|
||||
import first from 'lodash.first';
|
||||
import last from 'lodash.last';
|
||||
import clone from 'lodash.clone';
|
||||
import some from 'lodash.some';
|
||||
import isArray from 'lodash.isarray';
|
||||
import trim from 'lodash.trim';
|
||||
import escapeRegExp from 'lodash.escaperegexp';
|
||||
import set from 'lodash/set';
|
||||
import concat from 'lodash/concat';
|
||||
import split from 'lodash/split';
|
||||
import every from 'lodash/every';
|
||||
import toString from 'lodash/toString';
|
||||
import toNumber from 'lodash/toNumber';
|
||||
import isString from 'lodash/isString';
|
||||
import compact from 'lodash/compact';
|
||||
import first from 'lodash/first';
|
||||
import last from 'lodash/last';
|
||||
import clone from 'lodash/clone';
|
||||
import some from 'lodash/some';
|
||||
import isArray from 'lodash/isArray';
|
||||
import trim from 'lodash/trim';
|
||||
import escapeRegExp from 'lodash/escapeRegExp';
|
||||
|
||||
export async function koBoToolboxApiRequest(
|
||||
this: IExecuteFunctions | IWebhookFunctions | IHookFunctions | ILoadOptionsFunctions,
|
||||
|
|
|
@ -22,8 +22,8 @@ import {
|
|||
|
||||
import { lemlistApiRequest, lemlistApiRequestAllItems } from './GenericFunctions';
|
||||
|
||||
import isEmpty from 'lodash.isempty';
|
||||
import omit from 'lodash.omit';
|
||||
import isEmpty from 'lodash/isEmpty';
|
||||
import omit from 'lodash/omit';
|
||||
|
||||
export class Lemlist implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
|
|
|
@ -12,7 +12,7 @@ import type {
|
|||
} from 'n8n-workflow';
|
||||
import { NodeApiError } from 'n8n-workflow';
|
||||
|
||||
import get from 'lodash.get';
|
||||
import get from 'lodash/get';
|
||||
|
||||
import { query } from './Queries';
|
||||
|
||||
|
|
|
@ -113,9 +113,6 @@ export class LinkedIn implements INodeType {
|
|||
};
|
||||
|
||||
if (shareMediaCategory === 'IMAGE') {
|
||||
if (additionalFields.description) {
|
||||
description = additionalFields.description as string;
|
||||
}
|
||||
if (additionalFields.title) {
|
||||
title = additionalFields.title as string;
|
||||
}
|
||||
|
@ -145,7 +142,6 @@ export class LinkedIn implements INodeType {
|
|||
media: {
|
||||
title,
|
||||
id: image,
|
||||
description,
|
||||
},
|
||||
},
|
||||
commentary: text,
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue