Skip to content

Commit

Permalink
Merge branch 'master' into ADO-1555-use-new-search-enpoint
Browse files Browse the repository at this point in the history
* master:
  ci: Validate docs urls for langchain nodes as well (no-changelog) (#8271)
  fix: Small ui fixes to workflow cred setup modal (no-changelog) (#8280)
  fix: Fix issue with API key being required for the Qdrant Node (#8237)
  feat(Coda Node): Add User-Agent for requests to Coda (no-changelog) (#7771)
  fix(FTP Node): FTP connection failed due to missing password credential in node (#8131)
  ci: Fix lint setup in `chat` package (no-changelog) (#8275)
  ci: Fix codecov reporting, and include all packages (no-changelog) (#8276)
  fix(core): Fix test webhook deregistration (#8247)
  docs: Update links in deprecated langchain nodes to avoid linting errors (no-changelog) (#8273)
  fix: Fix user reinvites on FE and BE (#8261)
  docs: Update primaryDocumentation urls for nodes updated in #7651 (no-changelog) (#8270)
  feat: Add Chat Trigger node (#7409)
  fix(Github Trigger Node): Enforce SSL validation by default (#8265)
  fix(editor): Tweaking button sizes in execution preview (#8206)
  fix(core): Avoid wrapping `ExecutionBaseError` to prevent misreporting to Sentry (no-changelog) (#8262)
  • Loading branch information
MiloradFilipovic committed Jan 10, 2024
2 parents 71b9c93 + f208a6e commit 77849c2
Show file tree
Hide file tree
Showing 138 changed files with 2,915 additions and 830 deletions.
9 changes: 6 additions & 3 deletions .github/scripts/package.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
{
"dependencies": {
"cacheable-lookup": "6.1.0",
"conventional-changelog": "^4.0.0",
"glob": "^10.3.0",
"semver": "^7.5.4",
"tempfile": "^5.0.0",
"debug": "4.3.4",
"glob": "10.3.10",
"p-limit": "3.1.0",
"semver": "7.5.4",
"tempfile": "5.0.0",
"typescript": "*"
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
#!/usr/bin/env node

const packages = ['nodes-base', '@n8n/nodes-langchain'];
const concurrency = 20;
let exitCode = 0;

const debug = require('debug')('n8n');
const path = require('path');
const https = require('https');
const glob = require('fast-glob');
const glob = require('glob');
const pLimit = require('p-limit');
const Lookup = require('cacheable-lookup').default;

const nodesBaseDir = path.resolve(__dirname, '../packages/nodes-base');
const agent = new https.Agent({ keepAlive: true, keepAliveMsecs: 5000 });
new Lookup().install(agent);
const limiter = pLimit(concurrency);

const validateUrl = async (kind, name, documentationUrl) =>
new Promise((resolve, reject) => {
Expand All @@ -22,21 +30,26 @@ const validateUrl = async (kind, name, documentationUrl) =>
port: 443,
path: url.pathname,
method: 'HEAD',
agent,
},
(res) => {
debug('✓', kind, name);
resolve([name, res.statusCode]);
},
(res) => resolve([name, res.statusCode]),
)
.on('error', (e) => reject(e))
.end();
});

const checkLinks = async (kind) => {
let types = require(path.join(nodesBaseDir, `dist/types/${kind}.json`));
const checkLinks = async (baseDir, kind) => {
let types = require(path.join(baseDir, `dist/types/${kind}.json`));
if (kind === 'nodes')
types = types.filter(({ codex }) => !!codex?.resources?.primaryDocumentation);
const limit = pLimit(30);
debug(kind, types.length);

const statuses = await Promise.all(
types.map((type) =>
limit(() => {
limiter(() => {
const documentationUrl =
kind === 'credentials'
? type.documentationUrl
Expand All @@ -55,10 +68,13 @@ const checkLinks = async (kind) => {

if (missingDocs.length) console.log('Documentation URL missing for %s', kind, missingDocs);
if (invalidUrls.length) console.log('Documentation URL invalid for %s', kind, invalidUrls);
if (missingDocs.length || invalidUrls.length) process.exit(1);
if (missingDocs.length || invalidUrls.length) exitCode = 1;
};

(async () => {
await checkLinks('credentials');
await checkLinks('nodes');
for (const packageName of packages) {
const baseDir = path.resolve(__dirname, '../../packages', packageName);
await Promise.all([checkLinks(baseDir, 'credentials'), checkLinks(baseDir, 'nodes')]);
if (exitCode !== 0) process.exit(exitCode);
}
})();
6 changes: 4 additions & 2 deletions .github/workflows/check-documentation-urls.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,12 @@ jobs:
run: pnpm install --frozen-lockfile

- name: Build nodes-base
run: pnpm --filter @n8n/client-oauth2 --filter n8n-workflow --filter n8n-core --filter n8n-nodes-base build
run: pnpm --filter @n8n/client-oauth2 --filter n8n-workflow --filter n8n-core --filter n8n-nodes-base --filter @n8n/n8n-nodes-langchain build

- run: npm install --prefix=.github/scripts --no-package-lock

- name: Test URLs
run: node scripts/validate-docs-links.js
run: node .github/scripts/validate-docs-links.js

- name: Notify Slack on failure
uses: act10ns/slack@v2.0.0
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/units-tests-reusable.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,4 +70,4 @@ jobs:
if: ${{ inputs.collectCoverage == 'true' }}
uses: codecov/codecov-action@v3
with:
files: packages/@n8n/client-oauth2/coverage/cobertura-coverage.xml,packages/cli/coverage/cobertura-coverage.xml,packages/core/coverage/cobertura-coverage.xml,packages/design-system/coverage/cobertura-coverage.xml,packages/editor-ui/coverage/cobertura-coverage.xml,packages/nodes-base/coverage/cobertura-coverage.xml,packages/workflow/coverage/cobertura-coverage.xml
files: packages/@n8n/chat/coverage/cobertura-coverage.xml,packages/@n8n/nodes-langchain/coverage/cobertura-coverage.xml,packages/@n8n/permissions/coverage/cobertura-coverage.xml,packages/@n8n/client-oauth2/coverage/cobertura-coverage.xml,packages/cli/coverage/cobertura-coverage.xml,packages/core/coverage/cobertura-coverage.xml,packages/design-system/coverage/cobertura-coverage.xml,packages/editor-ui/coverage/cobertura-coverage.xml,packages/nodes-base/coverage/cobertura-coverage.xml,packages/workflow/coverage/cobertura-coverage.xml
5 changes: 3 additions & 2 deletions cypress/composables/modals/workflow-credential-setup-modal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@

export const getWorkflowCredentialsModal = () => cy.getByTestId('setup-workflow-credentials-modal');

export const getContinueButton = () => cy.getByTestId('continue-button');

/**
* Actions
*/

export const closeModal = () =>
getWorkflowCredentialsModal().find("button[aria-label='Close this dialog']").click();
export const closeModalFromContinueButton = () => getContinueButton().click();
2 changes: 1 addition & 1 deletion cypress/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export const INSTANCE_MEMBERS = [

export const MANUAL_TRIGGER_NODE_NAME = 'Manual Trigger';
export const MANUAL_TRIGGER_NODE_DISPLAY_NAME = 'When clicking "Test Workflow"';
export const MANUAL_CHAT_TRIGGER_NODE_NAME = 'Manual Chat Trigger';
export const MANUAL_CHAT_TRIGGER_NODE_NAME = 'Chat Trigger';
export const SCHEDULE_TRIGGER_NODE_NAME = 'Schedule Trigger';
export const CODE_NODE_NAME = 'Code';
export const SET_NODE_NAME = 'Set';
Expand Down
11 changes: 6 additions & 5 deletions cypress/e2e/34-template-credentials-setup.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ describe('Template credentials setup', () => {
templateWorkflowPage.actions.clickUseThisWorkflowButton();

templateCredentialsSetupPage.getters
.title(`Setup 'Promote new Shopify products on Twitter and Telegram' template`)
.title(`Set up 'Promote new Shopify products on Twitter and Telegram' template`)
.should('be.visible');
});

Expand All @@ -53,23 +53,23 @@ describe('Template credentials setup', () => {
clickUseWorkflowButtonByTitle('Promote new Shopify products on Twitter and Telegram');

templateCredentialsSetupPage.getters
.title(`Setup 'Promote new Shopify products on Twitter and Telegram' template`)
.title(`Set up 'Promote new Shopify products on Twitter and Telegram' template`)
.should('be.visible');
});

it('can be opened with a direct url', () => {
templateCredentialsSetupPage.visitTemplateCredentialSetupPage(testTemplate.id);

templateCredentialsSetupPage.getters
.title(`Setup 'Promote new Shopify products on Twitter and Telegram' template`)
.title(`Set up 'Promote new Shopify products on Twitter and Telegram' template`)
.should('be.visible');
});

it('has all the elements on page', () => {
templateCredentialsSetupPage.visitTemplateCredentialSetupPage(testTemplate.id);

templateCredentialsSetupPage.getters
.title(`Setup 'Promote new Shopify products on Twitter and Telegram' template`)
.title(`Set up 'Promote new Shopify products on Twitter and Telegram' template`)
.should('be.visible');

templateCredentialsSetupPage.getters
Expand Down Expand Up @@ -195,7 +195,8 @@ describe('Template credentials setup', () => {
templateCredentialsSetupPage.fillInDummyCredentialsForAppWithConfirm('X (Formerly Twitter)');
templateCredentialsSetupPage.fillInDummyCredentialsForApp('Telegram');

setupCredsModal.closeModal();
setupCredsModal.closeModalFromContinueButton();
setupCredsModal.getWorkflowCredentialsModal().should('not.exist');

// Focus the canvas so the copy to clipboard works
workflowPage.getters.canvasNodes().eq(0).realClick();
Expand Down
2 changes: 1 addition & 1 deletion cypress/e2e/4-node-creator.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ describe('Node Creator', () => {
nodeCreatorFeature.actions.openNodeCreator();

nodeCreatorFeature.getters.searchBar().find('input').type('manual');
nodeCreatorFeature.getters.creatorItem().should('have.length', 3);
nodeCreatorFeature.getters.creatorItem().should('have.length', 2);
nodeCreatorFeature.getters.searchBar().find('input').clear().type('manual123');
nodeCreatorFeature.getters.creatorItem().should('have.length', 0);
nodeCreatorFeature.getters
Expand Down
2 changes: 1 addition & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ const config = {
}, {}),
setupFilesAfterEnv: ['jest-expect-message'],
collectCoverage: process.env.COVERAGE_ENABLED === 'true',
coverageReporters: [process.env.COVERAGE_REPORT === 'true' ? 'text' : 'text-summary'],
coverageReporters: ['text-summary'],
collectCoverageFrom: ['src/**/*.ts'],
};

Expand Down
41 changes: 0 additions & 41 deletions packages/@n8n/chat/.eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,45 +7,4 @@ module.exports = {
extends: ['@n8n_io/eslint-config/frontend'],

...sharedOptions(__dirname, 'frontend'),

rules: {
'n8n-local-rules/dangerously-use-html-string-missing': 'off',

// TODO: Remove these
'id-denylist': 'warn',
'import/extensions': 'warn',
'import/no-default-export': 'warn',
'import/order': 'off',
'import/no-cycle': 'warn',
'import/no-duplicates': 'warn',
'@typescript-eslint/ban-types': 'warn',
'@typescript-eslint/dot-notation': 'warn',
'@typescript-eslint/lines-between-class-members': 'warn',
'@typescript-eslint/member-delimiter-style': 'warn',
'@typescript-eslint/naming-convention': 'warn',
'@typescript-eslint/no-empty-interface': 'warn',
'@typescript-eslint/no-for-in-array': 'warn',
'@typescript-eslint/no-loop-func': 'warn',
'@typescript-eslint/no-non-null-assertion': 'warn',
'@typescript-eslint/no-shadow': 'warn',
'@typescript-eslint/no-this-alias': 'warn',
'@typescript-eslint/no-unnecessary-boolean-literal-compare': 'warn',
'@typescript-eslint/no-unnecessary-type-assertion': 'warn',
'@typescript-eslint/no-unsafe-argument': 'warn',
'@typescript-eslint/no-unsafe-call': 'warn',
'@typescript-eslint/no-unsafe-return': 'warn',
'@typescript-eslint/no-unused-expressions': 'warn',
'@typescript-eslint/no-unused-vars': 'warn',
'@typescript-eslint/no-use-before-define': 'warn',
'@typescript-eslint/no-var-requires': 'warn',
'@typescript-eslint/prefer-nullish-coalescing': 'warn',
'@typescript-eslint/prefer-optional-chain': 'warn',
'@typescript-eslint/restrict-plus-operands': 'warn',
'@typescript-eslint/unbound-method': 'warn',
'@typescript-eslint/ban-ts-comment': ['warn', { 'ts-ignore': true }],
'@typescript-eslint/no-redundant-type-constituents': 'warn',
'@typescript-eslint/no-base-to-string': 'warn',
'@typescript-eslint/no-explicit-any': 'warn',
'@typescript-eslint/no-unsafe-enum-comparison': 'warn',
},
};
25 changes: 21 additions & 4 deletions packages/@n8n/chat/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
This is an embeddable Chat widget for n8n. It allows the execution of AI-Powered Workflows through a Chat window.

## Prerequisites
Create a n8n workflow which you want to execute via chat. The workflow has to be triggered using a **Webhook** node and return data using the **Respond to Webhook** node.
Create a n8n workflow which you want to execute via chat. The workflow has to be triggered using a **Chat Trigger** node.

Open the **Webhook** node and add your domain to the **Domain Allowlist** field. This makes sure that only requests from your domain are accepted.
Open the **Chat Trigger** node and add your domain to the **Allowed Origins (CORS)** field. This makes sure that only requests from your domain are accepted.

[See example workflow](https://github.com/n8n-io/n8n/blob/master/packages/%40n8n/chat/resources/workflow.json)

Expand All @@ -17,8 +17,6 @@ Each request is accompanied by an `action` query parameter, where `action` can b
- `loadPreviousSession` - When the user opens the Chatbot again and the previous chat session should be loaded
- `sendMessage` - When the user sends a message

We use the `Switch` node to handle the different actions.

## Installation

Open the **Webhook** node and replace `YOUR_PRODUCTION_WEBHOOK_URL` with your production URL. This is the URL that the Chat widget will use to send requests to.
Expand Down Expand Up @@ -106,6 +104,10 @@ createChat({
},
target: '#n8n-chat',
mode: 'window',
chatInputKey: 'chatInput',
chatSessionKey: 'sessionId',
metadata: {},
showWelcomeScreen: false,
defaultLanguage: 'en',
initialMessages: [
'Hi there! 👋',
Expand Down Expand Up @@ -148,6 +150,21 @@ createChat({
- In `window` mode, the Chat window will be embedded in the target element as a chat toggle button and a fixed size chat window.
- In `fullscreen` mode, the Chat will take up the entire width and height of its target container.

### `showWelcomeScreen`
- **Type**: `boolean`
- **Default**: `false`
- **Description**: Whether to show the welcome screen when the Chat window is opened.

### `chatSessionKey`
- **Type**: `string`
- **Default**: `'sessionId'`
- **Description**: The key to use for sending the chat history session ID for the AI Memory node.

### `chatInputKey`
- **Type**: `string`
- **Default**: `'chatInput'`
- **Description**: The key to use for sending the chat input for the AI Agent node.

### `defaultLanguage`
- **Type**: `string`
- **Default**: `'en'`
Expand Down
21 changes: 21 additions & 0 deletions packages/@n8n/chat/build.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { defineBuildConfig } from 'unbuild';

export default defineBuildConfig({
entries: [
{
builder: 'mkdist',
format: 'esm',
input: './src',
outDir: './tmp/lib',
},
{
builder: 'mkdist',
format: 'cjs',
input: './src',
outDir: './tmp/cjs',
},
],
clean: true,
declaration: true,
failOnWarn: false,
});
17 changes: 12 additions & 5 deletions packages/@n8n/chat/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,23 @@
"version": "0.6.0",
"scripts": {
"dev": "pnpm run storybook",
"build": "pnpm type-check && pnpm build:vite && pnpm build:prepare",
"build:vite": "vite build && npm run build:vite:full",
"build": "pnpm type-check && pnpm build:vite && pnpm run build:individual && npm run build:prepare",
"build:full": "pnpm type-check && pnpm build:vite && pnpm build:vite:full && pnpm run build:individual && npm run build:prepare",
"build:vite": "vite build",
"build:vite:full": "INCLUDE_VUE=true vite build",
"build:individual": "unbuild",
"build:prepare": "node scripts/postbuild.js",
"build:pack": "node scripts/pack.js",
"preview": "vite preview",
"test:dev": "vitest",
"test": "vitest run --coverage",
"type-check": "vue-tsc --noEmit -p tsconfig.json --composite false",
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore --ignore-path .eslintignore",
"lint": "eslint . --ext .js,.ts,.vue --quiet",
"lintfix": "eslint . --ext .js,.ts,.vue --fix",
"format": "prettier --write src/",
"storybook": "storybook dev -p 6006 --no-open",
"build:storybook": "storybook build",
"release": "pnpm run build && cd dist && pnpm publish"
"release": "pnpm run build:full && cd dist && pnpm publish"
},
"main": "./chat.umd.cjs",
"module": "./chat.es.js",
Expand All @@ -29,6 +32,10 @@
"./style.css": {
"import": "./style.css",
"require": "./style.css"
},
"./*": {
"import": "./*",
"require": "./*"
}
},
"dependencies": {
Expand All @@ -39,8 +46,8 @@
},
"devDependencies": {
"@iconify-json/mdi": "^1.1.54",
"n8n-design-system": "workspace:*",
"shelljs": "^0.8.5",
"unbuild": "^2.0.0",
"unplugin-icons": "^0.17.0",
"vite-plugin-dts": "^3.6.4"
},
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added packages/@n8n/chat/resources/images/windowed.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 77849c2

Please sign in to comment.