diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 667f1df9c6..a4f667ac46 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -7,3 +7,7 @@ # refactor(editor): Apply Prettier (no-changelog) #4920 5ca2148c7ed06c90f999508928b7a51f9ac7a788 + +# refactor: Run lintfix (no-changelog) (#7537) + +62c096710fab2f7e886518abdbded34b55e93f62 diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index bd399104db..dfcc0615a7 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1 +1,16 @@ -Github issue / Community forum post (link here to close automatically): +## Summary +> Describe what the PR does and how to test. Photos and videos are recommended. + + + +## Related tickets and issues +> Include links to **Linear ticket** or Github issue or Community forum post. Important in order to close *automatically* and provide context to reviewers. + + + +## Review / Merge checklist +- [ ] PR title and summary are descriptive. **Remember, the title automatically goes into the changelog. Use `(no-changelog)` otherwise.** ([conventions](https://github.com/n8n-io/n8n/blob/master/.github/pull_request_title_conventions.md)) +- [ ] [Docs updated](https://github.com/n8n-io/n8n-docs) or follow-up ticket created. +- [ ] Tests included. + > A bug is not considered fixed, unless a test is added to prevent it from happening again. + > A feature is not complete without tests. \ No newline at end of file diff --git a/.github/pull_request_title_conventions.md b/.github/pull_request_title_conventions.md new file mode 100644 index 0000000000..f6f762048f --- /dev/null +++ b/.github/pull_request_title_conventions.md @@ -0,0 +1,112 @@ +# PR Title Convention + +We have very precise rules over how Pull Requests (to the `master` branch) must be formatted. This format basically follows the [Angular Commit Message Convention](https://github.com/angular/angular/blob/master/CONTRIBUTING.md#commit). It leads to easier to read commit history and allows for automated generation of release notes: + +A PR title consists of these elements: + +``` +(): + │ │ │ + │ │ └─⫸ Summary: In imperative present tense. + | | Capitalized + | | No period at the end. + │ │ + │ └─⫸ Scope: API|core|editor|* Node + │ + └─⫸ Type: build|ci|docs|feat|fix|perf|refactor|test +``` + +- PR title + - type + - scope (*optional*) + - summary +- PR description + - body (optional) + - blank line + - footer (optional) + +The structure looks like this: + +### **Type** + +Must be one of the following: + +- `feat` - A new feature +- `fix` - A bug fix +- `perf` - A code change that improves performance +- `test` - Adding missing tests or correcting existing tests +- `docs` - Documentation only changes +- `refactor` - A code change that neither fixes a bug nor adds a feature +- `build` - Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm) +- `ci` - Changes to our CI configuration files and scripts (e.g. Github actions) + +If the prefix is `feat`, `fix` or `perf`, it will appear in the changelog. However if there is any BREAKING CHANGE (see Footer section below), the commit will always appear in the changelog. + +### **Scope (optional)** + +The scope should specify the place of the commit change as long as the commit clearly addresses one of the following supported scopes. (Otherwise, omit the scope!) + +- `API` - changes to the *public* API +- `core` - changes to the core / private API / backend of n8n +- `editor` - changes to the Editor UI +- `* Node` - changes to a specific node or trigger node (”`*`” to be replaced with the node name, not its display name), e.g. + - mattermost → Mattermost Node + - microsoftToDo → Microsoft To Do Node + - n8n → n8n Node + +### **Summary** + +The summary contains succinct description of the change: + +- use the imperative, present tense: "change" not "changed" nor "changes" +- capitalize the first letter +- *no* dot (.) at the end +- do *not* include Linear ticket IDs etc. (e.g. N8N-1234) +- suffix with “(no-changelog)” for commits / PRs that should not get mentioned in the changelog. + +### **Body (optional)** + +Just as in the **summary**, use the imperative, present tense: "change" not "changed" nor "changes". The body should include the motivation for the change and contrast this with previous behavior. + +### **Footer (optional)** + +The footer can contain information about breaking changes and deprecations and is also the place to [reference GitHub issues](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword), Linear tickets, and other PRs that this commit closes or is related to. For example: + +``` +BREAKING CHANGE: + + + + +Fixes # +``` + +or + +``` +DEPRECATED: + + + + +Closes # +``` + +A Breaking Change section should start with the phrase "`BREAKING CHANGE:` " followed by a summary of the breaking change, a blank line, and a detailed description of the breaking change that also includes migration instructions. + +> 💡 A breaking change can additionally also be marked by adding a “`!`” to the header, right before the “`:`”, e.g. `feat(editor)!: Remove support for dark mode` +> +> This makes locating breaking changes easier when just skimming through commit messages. + +> 💡 The breaking changes must also be added to the [packages/cli/BREAKING-CHANGES.md](https://github.com/n8n-io/n8n/blob/master/packages/cli/BREAKING-CHANGES.md) file located in the n8n repository. + +Similarly, a Deprecation section should start with "`DEPRECATED:` " followed by a short description of what is deprecated, a blank line, and a detailed description of the deprecation that also mentions the recommended update path. + +### **Revert commits** + +If the commit reverts a previous commit, it should begin with `revert:` , followed by the header of the reverted commit. + +The content of the commit message body should contain: + +- information about the SHA of the commit being reverted in the following format: `This reverts commit `, +- a clear description of the reason for reverting the commit message. \ No newline at end of file diff --git a/.github/scripts/bump-versions.mjs b/.github/scripts/bump-versions.mjs index c1c110272c..8e7acc21eb 100644 --- a/.github/scripts/bump-versions.mjs +++ b/.github/scripts/bump-versions.mjs @@ -43,7 +43,7 @@ for (const packageName in packageMap) { packageJson.version = packageMap[packageName].nextVersion = isDirty || - Object.keys(packageJson.dependencies).some( + Object.keys(packageJson.dependencies || {}).some( (dependencyName) => packageMap[dependencyName]?.isDirty, ) ? semver.inc(version, releaseType) diff --git a/.github/scripts/package.json b/.github/scripts/package.json index 6033dd191c..5798df6321 100644 --- a/.github/scripts/package.json +++ b/.github/scripts/package.json @@ -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": "*" } } diff --git a/scripts/validate-docs-links.js b/.github/scripts/validate-docs-links.js similarity index 60% rename from scripts/validate-docs-links.js rename to .github/scripts/validate-docs-links.js index aab9a23380..2335b62eed 100644 --- a/scripts/validate-docs-links.js +++ b/.github/scripts/validate-docs-links.js @@ -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) => { @@ -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 @@ -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); + } })(); diff --git a/.github/workflows/check-documentation-urls.yml b/.github/workflows/check-documentation-urls.yml index 4d5da5131e..99bd412105 100644 --- a/.github/workflows/check-documentation-urls.yml +++ b/.github/workflows/check-documentation-urls.yml @@ -27,16 +27,18 @@ 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 - - name: Test URLS - run: node scripts/validate-docs-links.js + - run: npm install --prefix=.github/scripts --no-package-lock + + - name: Test URLs + run: node .github/scripts/validate-docs-links.js - name: Notify Slack on failure uses: act10ns/slack@v2.0.0 if: failure() with: status: ${{ job.status }} - channel: '#updates-build-alerts' + channel: '#mission-docs' webhook-url: ${{ secrets.SLACK_WEBHOOK_URL }} message: Documentation URLs check failed (${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) diff --git a/.github/workflows/check-pr-title.yml b/.github/workflows/check-pr-title.yml index 516e4b2275..c857fcfdc1 100644 --- a/.github/workflows/check-pr-title.yml +++ b/.github/workflows/check-pr-title.yml @@ -30,6 +30,6 @@ jobs: - name: Validate PR title id: validate_pr_title - uses: n8n-io/validate-n8n-pull-request-title@v1.1 + uses: n8n-io/validate-n8n-pull-request-title@v1.3 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/checklist.yml b/.github/workflows/checklist.yml deleted file mode 100644 index 6262918456..0000000000 --- a/.github/workflows/checklist.yml +++ /dev/null @@ -1,22 +0,0 @@ -name: PR Checklist - -on: - pull_request_target: - types: - - opened - - synchronize - branches: - - master - -jobs: - checklist_job: - runs-on: ubuntu-latest - name: Checklist job - steps: - - name: Checkout - uses: actions/checkout@v3.5.3 - - name: Checklist - uses: wyozi/contextual-qa-checklist-action@master - with: - gh-token: ${{ secrets.GITHUB_TOKEN }} - comment-footer: Make sure to check off this list before asking for review. diff --git a/.github/workflows/ci-master.yml b/.github/workflows/ci-master.yml index 1c76725066..749ac2f3ea 100644 --- a/.github/workflows/ci-master.yml +++ b/.github/workflows/ci-master.yml @@ -13,7 +13,7 @@ jobs: strategy: matrix: - node-version: [18.x, 20.5] + node-version: [18.x, 20.x] steps: - uses: actions/checkout@v3.5.3 @@ -44,11 +44,12 @@ jobs: needs: install-and-build strategy: matrix: - node-version: [18.x, 20.5] + node-version: [18.x, 20.x] with: ref: ${{ inputs.branch }} nodeVersion: ${{ matrix.node-version }} - cacheKey: ${{ github.sha }}-base:${{ matrix.node-version }}-test-lint + cacheKey: ${{ github.sha }}-base:${{ matrix.node-version }}-test-lint + collectCoverage: true lint: name: Lint changes @@ -56,7 +57,7 @@ jobs: needs: install-and-build strategy: matrix: - node-version: [18.x, 20.5] + node-version: [18.x, 20.x] steps: - uses: actions/checkout@v3.5.3 with: @@ -95,6 +96,6 @@ jobs: if: failure() with: status: ${{ job.status }} - channel: '#updates-build-alerts' + channel: '#alerts-build' webhook-url: ${{ secrets.SLACK_WEBHOOK_URL }} message: Master branch (build or test or lint) failed (${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) diff --git a/.github/workflows/ci-postgres-mysql.yml b/.github/workflows/ci-postgres-mysql.yml index 2e8465351e..f42afc4937 100644 --- a/.github/workflows/ci-postgres-mysql.yml +++ b/.github/workflows/ci-postgres-mysql.yml @@ -6,7 +6,11 @@ on: workflow_dispatch: pull_request: paths: - - packages/cli/src/databases/migrations/** + - packages/cli/src/databases/** + +concurrency: + group: db-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true jobs: build: @@ -22,7 +26,7 @@ jobs: - run: pnpm install --frozen-lockfile - name: Build Backend - run: pnpm --filter @n8n/client-oauth2 --filter n8n-workflow --filter n8n-core --filter n8n build + run: pnpm build:backend - name: Cache build artifacts uses: actions/cache/save@v3.3.1 @@ -61,7 +65,7 @@ jobs: - name: Test MySQL working-directory: packages/cli - run: DB_TABLE_PREFIX=test_ pnpm test:mysql --runInBand + run: pnpm test:mysql postgres: name: Postgres @@ -94,7 +98,7 @@ jobs: - name: Test Postgres working-directory: packages/cli - run: DB_POSTGRESDB_SCHEMA=alt_schema DB_TABLE_PREFIX=test_ pnpm test:postgres --runInBand + run: pnpm test:postgres notify-on-failure: name: Notify Slack on failure @@ -106,6 +110,6 @@ jobs: if: failure() && github.ref == 'refs/heads/master' with: status: ${{ job.status }} - channel: '#updates-build-alerts' + channel: '#alerts-build' webhook-url: ${{ secrets.SLACK_WEBHOOK_URL }} message: Postgres or MySQL tests failed (${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) diff --git a/.github/workflows/docker-images-nightly.yml b/.github/workflows/docker-images-nightly.yml index 2be6e40617..ce7a651783 100644 --- a/.github/workflows/docker-images-nightly.yml +++ b/.github/workflows/docker-images-nightly.yml @@ -35,6 +35,11 @@ on: description: 'URL to call after Docker Image got built successfully.' required: false default: '' + include-arm64: + description: 'Include ARM64 support' + type: boolean + required: true + default: false jobs: build: @@ -76,7 +81,7 @@ jobs: build-args: | N8N_RELEASE_TYPE=nightly file: ./docker/images/n8n-custom/Dockerfile - platforms: linux/amd64 + platforms: ${{ github.event.inputs.include-arm64 == 'true' && 'linux/amd64,linux/arm64' || 'linux/amd64' }} provenance: false push: true tags: ${{ secrets.DOCKER_USERNAME }}/n8n:${{ github.event.inputs.tag || 'nightly' }} diff --git a/.github/workflows/e2e-reusable.yml b/.github/workflows/e2e-reusable.yml index c7de60e416..597653a5a6 100644 --- a/.github/workflows/e2e-reusable.yml +++ b/.github/workflows/e2e-reusable.yml @@ -40,7 +40,7 @@ on: containers: description: 'Number of containers to run tests in.' required: false - default: '[1, 2, 3, 4, 5, 6, 7, 8]' + default: '[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]' type: string pr_number: description: 'PR number to run tests for.' @@ -99,6 +99,8 @@ jobs: runTests: false install: false build: pnpm build + env: + VUE_APP_MAX_PINNED_DATA_SIZE: 16384 - name: Cypress install run: pnpm cypress:install diff --git a/.github/workflows/e2e-tests-pr.yml b/.github/workflows/e2e-tests-pr.yml index 3a3328cdb7..cdb3ac0fb7 100644 --- a/.github/workflows/e2e-tests-pr.yml +++ b/.github/workflows/e2e-tests-pr.yml @@ -6,6 +6,10 @@ on: branch: - 'master' +concurrency: + group: e2e-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + jobs: run-e2e-tests: name: E2E [Electron/Node 18] diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml index 1d699ca27e..e7400adecb 100644 --- a/.github/workflows/e2e-tests.yml +++ b/.github/workflows/e2e-tests.yml @@ -60,7 +60,7 @@ jobs: if: failure() with: status: ${{ job.status }} - channel: '#updates-build-alerts' + channel: '#alerts-build' webhook-url: ${{ secrets.SLACK_WEBHOOK_URL }} message: E2E failure for branch `${{ inputs.branch || 'master' }}` deployed by ${{ inputs.user || 'schedule' }} (${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) diff --git a/.github/workflows/release-publish.yml b/.github/workflows/release-publish.yml index ae71319d9d..75a6b12400 100644 --- a/.github/workflows/release-publish.yml +++ b/.github/workflows/release-publish.yml @@ -36,6 +36,9 @@ jobs: - name: Build run: pnpm build + - name: Dry-run publishing + run: pnpm publish -r --no-git-checks --dry-run + - name: Publish to NPM run: | echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}" > ~/.npmrc diff --git a/.github/workflows/release-push-to-channel.yml b/.github/workflows/release-push-to-channel.yml index 5e4100c29a..6b7fdeb17a 100644 --- a/.github/workflows/release-push-to-channel.yml +++ b/.github/workflows/release-push-to-channel.yml @@ -39,4 +39,17 @@ jobs: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - - run: docker buildx imagetools create -t n8nio/n8n:${{ github.event.inputs.release-channel }} n8nio/n8n:${{ github.event.inputs.version }} + - run: docker buildx imagetools create -t ${{ secrets.DOCKER_USERNAME }}/n8n:${{ github.event.inputs.release-channel }} ${{ secrets.DOCKER_USERNAME }}/n8n:${{ github.event.inputs.version }} + + release-to-github-container-registry: + name: Release to GitHub Container Registry + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - uses: docker/login-action@v2.2.0 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - run: docker buildx imagetools create -t ghcr.io/${{ github.repository_owner }}/n8n:${{ github.event.inputs.release-channel }} ghcr.io/${{ github.repository_owner }}/n8n:${{ github.event.inputs.version }} diff --git a/.github/workflows/test-workflows.yml b/.github/workflows/test-workflows.yml index 9da8c93125..55a232b9c2 100644 --- a/.github/workflows/test-workflows.yml +++ b/.github/workflows/test-workflows.yml @@ -45,7 +45,7 @@ jobs: working-directory: n8n run: | pnpm install --frozen-lockfile - pnpm --filter @n8n/client-oauth2 --filter n8n-workflow --filter n8n-core --filter n8n-nodes-base --filter n8n build + pnpm build:backend shell: bash - name: Import credentials @@ -96,7 +96,7 @@ jobs: if: failure() with: status: ${{ job.status }} - channel: '#updates-build-alerts' + channel: '#alerts-build' webhook-url: ${{ secrets.SLACK_WEBHOOK_URL }} message: | 🛑 Workflow test failed 🛑: diff --git a/.github/workflows/units-tests-reusable.yml b/.github/workflows/units-tests-reusable.yml index 508ec7d019..af33eec316 100644 --- a/.github/workflows/units-tests-reusable.yml +++ b/.github/workflows/units-tests-reusable.yml @@ -18,11 +18,17 @@ on: required: false default: '' type: string + collectCoverage: + required: false + default: 'false' + type: string jobs: unit-test: name: Unit tests runs-on: ubuntu-latest + env: + COVERAGE_ENABLED: ${{ inputs.collectCoverage }} steps: - uses: actions/checkout@v3.5.3 with: @@ -51,10 +57,17 @@ jobs: path: ./packages/**/dist key: ${{ inputs.cacheKey }} - - name: Test - run: pnpm test + - name: Test Backend + run: pnpm test:backend + + - name: Test Nodes + run: pnpm test:nodes + + - name: Test Frontend + run: pnpm test:frontend - name: Upload coverage to Codecov + 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 diff --git a/CHANGELOG.md b/CHANGELOG.md index ddaf945357..bc07f55eb2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,444 @@ +# [1.24.0](https://github.com/n8n-io/n8n/compare/n8n@1.23.0...n8n@1.24.0) (2024-01-10) + + +### Bug Fixes + +* **core:** Do not add Authentication header when `authentication` type is `body` ([#8201](https://github.com/n8n-io/n8n/issues/8201)) ([ac1c642](https://github.com/n8n-io/n8n/commit/ac1c642fddfac3b0ed1144c7eccd7c88fbd5a1a5)) +* **core:** Fix test webhook deregistration ([#8247](https://github.com/n8n-io/n8n/issues/8247)) ([5032bf0](https://github.com/n8n-io/n8n/commit/5032bf0e346dccf7cade17a1518b3031118af5e1)) +* **editor:** Items count display in running workflow ([#8148](https://github.com/n8n-io/n8n/issues/8148)) ([8a3c87f](https://github.com/n8n-io/n8n/commit/8a3c87f69c20de7c713dff021e390ea4ea32b103)), closes [/github.com/n8n-io/n8n/pull/7763/files#diff-f5dae80a64b9951bb6691f1b9d439423cf84fa0cc9601b3f2c00904f3135e8deR48](https://github.com//github.com/n8n-io/n8n/pull/7763/files/issues/diff-f5dae80a64b9951bb6691f1b9d439423cf84fa0cc9601b3f2c00904f3135e8deR48) +* **editor:** Only load suggested templates for owners ([#8228](https://github.com/n8n-io/n8n/issues/8228)) ([8f22a26](https://github.com/n8n-io/n8n/commit/8f22a265d607047eff22ba957d627bbec7da7900)) +* **editor:** Tweaking button sizes in execution preview ([#8206](https://github.com/n8n-io/n8n/issues/8206)) ([9d40ae8](https://github.com/n8n-io/n8n/commit/9d40ae8b74594d4368591a62f9b39dde28efc64d)) +* **editor:** Unify canvas button positioning ([#8258](https://github.com/n8n-io/n8n/issues/8258)) ([b6c42cc](https://github.com/n8n-io/n8n/commit/b6c42cc08408d9d7cc49cc84245b4ad515fa3e6a)) +* **editor:** Vertically center workflow preview loading state ([#8231](https://github.com/n8n-io/n8n/issues/8231)) ([2d6e406](https://github.com/n8n-io/n8n/commit/2d6e406e215188dbbbeb593ac09ccad3914aaf81)) +* Fix issue with API key being required for the Qdrant Node ([#8237](https://github.com/n8n-io/n8n/issues/8237)) ([4401db3](https://github.com/n8n-io/n8n/commit/4401db3a2fad3464a5498e9a86fc6bba4f9c9f95)) +* Fix template credential setup for nodes that dont have credentials ([#8208](https://github.com/n8n-io/n8n/issues/8208)) ([cd3f5b5](https://github.com/n8n-io/n8n/commit/cd3f5b5b1f48e42cb6fa5ebcc15527c28502ceb9)) +* Fix user reinvites on FE and BE ([#8261](https://github.com/n8n-io/n8n/issues/8261)) ([0dabe5c](https://github.com/n8n-io/n8n/commit/0dabe5c74e5ad0969d4691b3db4a1e796ed8a08c)) +* **FTP Node:** FTP connection failed due to missing password credential in node ([#8131](https://github.com/n8n-io/n8n/issues/8131)) ([e056aa9](https://github.com/n8n-io/n8n/commit/e056aa9c4dd6c6a7717202029b25f4f65ddecb0d)) +* **Github Trigger Node:** Enforce SSL validation by default ([#8265](https://github.com/n8n-io/n8n/issues/8265)) ([1387541](https://github.com/n8n-io/n8n/commit/1387541e336e7311ba9c43907fa95d3196fae2eb)) +* Make params panel double width for all SQL nodes ([#8236](https://github.com/n8n-io/n8n/issues/8236)) ([048b588](https://github.com/n8n-io/n8n/commit/048b588852f5fed1c976889ba54ef564ca7f4894)) +* **Monday.com Node:** Migrate to api 2023-10 ([#8254](https://github.com/n8n-io/n8n/issues/8254)) ([ccde38a](https://github.com/n8n-io/n8n/commit/ccde38a8a8d65a21bf4d38ef7b09a5ffa3c7ad2d)) +* **MySQL Node:** Only escape table names when needed ([#8246](https://github.com/n8n-io/n8n/issues/8246)) ([3b01eb6](https://github.com/n8n-io/n8n/commit/3b01eb60c98d51d0d7572342b8d6d40763293719)) +* **Nextcloud Node:** Throw an actual error if server responded with Fatal error ([#8234](https://github.com/n8n-io/n8n/issues/8234)) ([b201ff8](https://github.com/n8n-io/n8n/commit/b201ff8f23b2bac6b00d5c16d91b4b2931f45ade)) +* **NocoDB Node:** Download attachments ([#8235](https://github.com/n8n-io/n8n/issues/8235)) ([43e8e5e](https://github.com/n8n-io/n8n/commit/43e8e5e540b9fcbca663fcf17a78a7aba2abb475)) +* **Postgres Node:** Stop marking autogenerated columns as required ([#8230](https://github.com/n8n-io/n8n/issues/8230)) ([bed04ec](https://github.com/n8n-io/n8n/commit/bed04ec122234b4329a5e415bf3627c115b3f32e)), closes [#7084](https://github.com/n8n-io/n8n/issues/7084) +* Resolve expressions in credentials following paired item ([#8250](https://github.com/n8n-io/n8n/issues/8250)) ([ccb2b07](https://github.com/n8n-io/n8n/commit/ccb2b076f8240b0712949b72ec57ae72a36ef62d)) +* **Set Node:** Field not excluded if dot notation disabled and field was set by using drag-and-drop from ui ([#8233](https://github.com/n8n-io/n8n/issues/8233)) ([cda49a4](https://github.com/n8n-io/n8n/commit/cda49a4747ef4369ce7a971872c6fb8a74f4156d)) +* Store workflow settings when saving an execution ([#8288](https://github.com/n8n-io/n8n/issues/8288)) ([8a7c629](https://github.com/n8n-io/n8n/commit/8a7c629ea183f75f9916003edf11cb8aeef445eb)) +* **Webhook Node:** Fix handling of form-data files ([#8256](https://github.com/n8n-io/n8n/issues/8256)) ([fc29030](https://github.com/n8n-io/n8n/commit/fc2903096e6e64e5b2a14593418d5651e07ca9ee)) + + +### Features + +* Add Chat Trigger node ([#7409](https://github.com/n8n-io/n8n/issues/7409)) ([af49e95](https://github.com/n8n-io/n8n/commit/af49e95cc7ccf70f233f9bd1e34fbb57f7f08ccf)) +* **core:** Cache test webhook registrations ([#8176](https://github.com/n8n-io/n8n/issues/8176)) ([22a5f52](https://github.com/n8n-io/n8n/commit/22a5f5258da0a973e1ad44c0d3d4f0fda1d23444)), closes [#8155](https://github.com/n8n-io/n8n/issues/8155) +* **core:** Validate shutdown handlers on startup ([#8260](https://github.com/n8n-io/n8n/issues/8260)) ([3b996a7](https://github.com/n8n-io/n8n/commit/3b996a7da0137a75c3047656a4bc8cc336ebfc1e)) +* **editor:** Add fullscreen view to code editor ([#8084](https://github.com/n8n-io/n8n/issues/8084)) ([071e6d6](https://github.com/n8n-io/n8n/commit/071e6d6b6e32b7196f34043710c23331ad28fac0)) +* **editor:** Update copy: `Execute` --> `Test` ([#8137](https://github.com/n8n-io/n8n/issues/8137)) ([df5d07b](https://github.com/n8n-io/n8n/commit/df5d07bcb8beba760bc17118b36ccd531bc3c755)) +* **Google Sheets Node:** Add "By Name" option to selector for Sheets ([#8241](https://github.com/n8n-io/n8n/issues/8241)) ([dce28f9](https://github.com/n8n-io/n8n/commit/dce28f9cb98db33bf22bcfee181f8e9ca64dd2bc)) +* **HTTP Request Node:** Interval Between Requests option for pagination ([#8224](https://github.com/n8n-io/n8n/issues/8224)) ([270328c](https://github.com/n8n-io/n8n/commit/270328ccf6e5502adc092f6f85d146ffb98e1208)) +* Implement MistralCloud Chat & Embeddings nodes ([#8239](https://github.com/n8n-io/n8n/issues/8239)) ([d37b908](https://github.com/n8n-io/n8n/commit/d37b9084b2c657d8b5b8bae6dbb51b428db26e1e)) +* **MongoDB Node:** Add support for TLS ([#8266](https://github.com/n8n-io/n8n/issues/8266)) ([e796e7f](https://github.com/n8n-io/n8n/commit/e796e7f06d73a74a403000c53942d56cab91781b)) +* **Switch Node:** Overhaul ([#7855](https://github.com/n8n-io/n8n/issues/7855)) ([f4092a9](https://github.com/n8n-io/n8n/commit/f4092a9e49f66845612420ba59a013796ed80d45)) + + +### Performance Improvements + +* **core:** Improve caching service ([#8213](https://github.com/n8n-io/n8n/issues/8213)) ([f53c482](https://github.com/n8n-io/n8n/commit/f53c482939db938c47523ac11a9538e35e1926a9)), closes [#7747](https://github.com/n8n-io/n8n/issues/7747) +* **core:** Optimize workflow activation errors ([#8242](https://github.com/n8n-io/n8n/issues/8242)) ([f293956](https://github.com/n8n-io/n8n/commit/f2939568cf399e67214e89bc7f859323aaeda8dd)) + + + +# [1.23.0](https://github.com/n8n-io/n8n/compare/n8n@1.22.0...n8n@1.23.0) (2024-01-03) + + +### Bug Fixes + +* **Asana Node:** Omit body from GET, HEAD, and DELETE requests ([#8057](https://github.com/n8n-io/n8n/issues/8057)) ([15ffd4f](https://github.com/n8n-io/n8n/commit/15ffd4fb9f967302e2444a873a804d2ccb64e748)) +* **core:** Better input validation for the changeRole endpoint ([#8189](https://github.com/n8n-io/n8n/issues/8189)) ([cfe9525](https://github.com/n8n-io/n8n/commit/cfe9525dd4e2dbf2496bd86ad854bb744b5dc8fe)) +* **core:** Fix issue that pinnedData is not used with Test-Webhooks ([#8123](https://github.com/n8n-io/n8n/issues/8123)) ([fa8bd8b](https://github.com/n8n-io/n8n/commit/fa8bd8b9eb202989229028cb6975cd2b50e5eef9)) +* **core:** Handle empty executions table in pruning in migrations ([#8121](https://github.com/n8n-io/n8n/issues/8121)) ([ffaa30d](https://github.com/n8n-io/n8n/commit/ffaa30ddc4ee312f44726c17a7ec91b5551092ad)) +* **core:** Remove circular dependency in WorkflowService and ActiveWorkflowRunner ([#8128](https://github.com/n8n-io/n8n/issues/8128)) ([21788d9](https://github.com/n8n-io/n8n/commit/21788d9153fb730965dabbce64c50c3b929ee728)), closes [#8122](https://github.com/n8n-io/n8n/issues/8122) +* **core:** Use pinned data only for manual mode ([#8164](https://github.com/n8n-io/n8n/issues/8164)) ([ea7e76f](https://github.com/n8n-io/n8n/commit/ea7e76fa3b3dc1f37b0415e14ea5ff90b8017b9a)) +* **Discord Node:** Remove unnecessary requirement on parameters ([#8060](https://github.com/n8n-io/n8n/issues/8060)) ([ef3a577](https://github.com/n8n-io/n8n/commit/ef3a57719eb42777502cafdd38009e6cb5b484ce)) +* **editor:** Avoid sanitizing output to search node data ([#8126](https://github.com/n8n-io/n8n/issues/8126)) ([c83d9f4](https://github.com/n8n-io/n8n/commit/c83d9f45bab986eb930e9da69eec970d3a72263d)) +* **editor:** Enable explicit undo keyboard shortcut across all code editors ([#8178](https://github.com/n8n-io/n8n/issues/8178)) ([cf7f668](https://github.com/n8n-io/n8n/commit/cf7f6688bac5dd31dc3a45df4ecce579939141e2)), closes [#5297](https://github.com/n8n-io/n8n/issues/5297) +* **editor:** Fix operation change failing in certain conditions ([#8114](https://github.com/n8n-io/n8n/issues/8114)) ([711fa2b](https://github.com/n8n-io/n8n/commit/711fa2b9251154b50d8e5e7cd9a857ccb2c0bec6)), closes [/github.com/n8n-io/n8n/blob/7806a65229878a473f5526bad0b94614e8bfa8aa/packages/workflow/src/NodeHelpers.ts#L786](https://github.com//github.com/n8n-io/n8n/blob/7806a65229878a473f5526bad0b94614e8bfa8aa/packages/workflow/src/NodeHelpers.ts/issues/L786) +* **editor:** Fix templates view layout ([#8196](https://github.com/n8n-io/n8n/issues/8196)) ([d01e42a](https://github.com/n8n-io/n8n/commit/d01e42a2aabedfd4c0f79799bbfc9b1a235d4233)) +* **editor:** Fix UI urls when hosted behind a path prefix ([#8198](https://github.com/n8n-io/n8n/issues/8198)) ([5c078f1](https://github.com/n8n-io/n8n/commit/5c078f1b3d78c7038bfdbb083fd029ef61bf2dfc)), closes [#8061](https://github.com/n8n-io/n8n/issues/8061) +* **editor:** Prevent browser zoom when scrolling inside sticky edit mode ([#8116](https://github.com/n8n-io/n8n/issues/8116)) ([e928210](https://github.com/n8n-io/n8n/commit/e928210ccdc00ad8a38e3f96ba5145c35e7b007b)) +* **editor:** Prevent canvas undo/redo when NDV is open ([#8118](https://github.com/n8n-io/n8n/issues/8118)) ([39e45d8](https://github.com/n8n-io/n8n/commit/39e45d8b929d474f1e7587329b003fe15b61636d)) +* **editor:** Prevent storing pairedItem data inside of pinData ([#8173](https://github.com/n8n-io/n8n/issues/8173)) ([405e267](https://github.com/n8n-io/n8n/commit/405e26757e2591b42a4bfeedd1fea997fbbb08c9)) +* **GitHub Node:** Fix issue that File->Get did not run once per item ([#8190](https://github.com/n8n-io/n8n/issues/8190)) ([11cda41](https://github.com/n8n-io/n8n/commit/11cda41214100a1a3e65309434ab8be3ccef1898)) +* **Invoice Ninja Node:** Fix issue with custom invoice numbers not working with v5 ([#8200](https://github.com/n8n-io/n8n/issues/8200)) ([3b6ae2d](https://github.com/n8n-io/n8n/commit/3b6ae2d0a510a57b27fc1a44cb3e710e2a783800)) +* **Microsoft Excel 365 Node:** Ensure arg is string during worksheet table search ([#8154](https://github.com/n8n-io/n8n/issues/8154)) ([8e873ca](https://github.com/n8n-io/n8n/commit/8e873ca2f3c73ddd7bbef2218d8da82032f66cec)) +* **Notion Node:** Ensure arg is string during page ID extraction ([#8153](https://github.com/n8n-io/n8n/issues/8153)) ([e94b8a6](https://github.com/n8n-io/n8n/commit/e94b8a6c30aaa2e59117d5a0cc03e1590d7ea8ca)) +* **Redis Trigger Node:** Activating a workflow with a Redis trigger fails ([#8129](https://github.com/n8n-io/n8n/issues/8129)) ([a169b74](https://github.com/n8n-io/n8n/commit/a169b7406279de43dbd3fd7d13166d987c60d01a)) +* **Schedule Trigger Node:** Use the correct `moment` import ([#8185](https://github.com/n8n-io/n8n/issues/8185)) ([17a4e2e](https://github.com/n8n-io/n8n/commit/17a4e2ea80c664e248c136b7e66eef710ccba7f2)), closes [#8184](https://github.com/n8n-io/n8n/issues/8184) +* Show public API upgrade CTA when feature is not enabled ([#8109](https://github.com/n8n-io/n8n/issues/8109)) ([e9c7fd7](https://github.com/n8n-io/n8n/commit/e9c7fd73975ced504d5a16a0dbbc313f15ccd8ab)) + + +### Features + +* **core:** Add closeFunction support to Sub-Nodes ([#7708](https://github.com/n8n-io/n8n/issues/7708)) ([bec0fae](https://github.com/n8n-io/n8n/commit/bec0faed9e51fe6ea20ab3b07b4dfa849b28516b)) +* **core:** Add user.profile.beforeUpdate hook ([#8144](https://github.com/n8n-io/n8n/issues/8144)) ([e126ed7](https://github.com/n8n-io/n8n/commit/e126ed74f3d9ed3dae72252cb8c9e8a6f7620808)) +* **core:** Improvements/overhaul for nodes working with binary data ([#7651](https://github.com/n8n-io/n8n/issues/7651)) ([5e16dd4](https://github.com/n8n-io/n8n/commit/5e16dd4ab4457acf21d3d7a3566d07944ff7f857)) +* **core:** Remove discontinued crypto-js ([#8104](https://github.com/n8n-io/n8n/issues/8104)) ([01e9a79](https://github.com/n8n-io/n8n/commit/01e9a79238bbd2c14ae77a12e54fc1c6f41e1246)) +* **core:** Unify application components shutdown ([#8097](https://github.com/n8n-io/n8n/issues/8097)) ([3a881be](https://github.com/n8n-io/n8n/commit/3a881be6c25b3e16d8c53227dc851cb420f5f1bf)) +* **editor:** Add node execution status indicator to output panel ([#8124](https://github.com/n8n-io/n8n/issues/8124)) ([ab74bad](https://github.com/n8n-io/n8n/commit/ab74bade05cb30e7fa65a491789a3df3ab7bf8b8)) +* **editor:** Add template Id to workflow metadata ([#8088](https://github.com/n8n-io/n8n/issues/8088)) ([517b050](https://github.com/n8n-io/n8n/commit/517b050d0ae1a64987ac00d5795c5e59ed176f3f)) +* **Home Assistant Node:** Use the new Home Assistant logo ([#8150](https://github.com/n8n-io/n8n/issues/8150)) ([518a99e](https://github.com/n8n-io/n8n/commit/518a99e5287dc648edafd34a4dd27c9205eb8629)) +* **Qdrant Vector Store Node:** Qdrant vector store support ([#8080](https://github.com/n8n-io/n8n/issues/8080)) ([66460f6](https://github.com/n8n-io/n8n/commit/66460f66b0b02ae6f342e52500b29fe8b724e1dc)) +* **Wordpress Node:** Add option to ignore error when using self signed certificates ([#8199](https://github.com/n8n-io/n8n/issues/8199)) ([65c8e12](https://github.com/n8n-io/n8n/commit/65c8e12b96ac8c1c53d3879d91982ca834f3cda1)) + + + +# [1.22.0](https://github.com/n8n-io/n8n/compare/n8n@1.21.0...n8n@1.22.0) (2023-12-21) + + +### Bug Fixes + +* **ActiveCampaign Node:** Fix pagination issue when loading tags ([#8017](https://github.com/n8n-io/n8n/issues/8017)) ([1943857](https://github.com/n8n-io/n8n/commit/19438572312cf9354c333aeb52ccbf1ab81fc51f)) +* **core:** Close db connection gracefully when exiting ([#8045](https://github.com/n8n-io/n8n/issues/8045)) ([e69707e](https://github.com/n8n-io/n8n/commit/e69707efd4bd947fdf6b9c66f373da63d34f41e9)) +* **core:** Consider timeout in shutdown an error ([#8050](https://github.com/n8n-io/n8n/issues/8050)) ([4cae976](https://github.com/n8n-io/n8n/commit/4cae976a3b428bd528fe71ef0b240c0fd6e23bbf)) +* **core:** Do not display error when stopping jobless execution in queue mode ([#8007](https://github.com/n8n-io/n8n/issues/8007)) ([8e6b951](https://github.com/n8n-io/n8n/commit/8e6b951a76e08b9ee9740fdd853f77553ad60cd6)) +* **core:** Fix shutdown if terminating before hooks are initialized ([#8047](https://github.com/n8n-io/n8n/issues/8047)) ([6ae2f5e](https://github.com/n8n-io/n8n/commit/6ae2f5efea65e23029475ccdc5a65ec7c8152423)) +* **core:** Handle multiple termination signals correctly ([#8046](https://github.com/n8n-io/n8n/issues/8046)) ([67bd8ad](https://github.com/n8n-io/n8n/commit/67bd8ad698bd0afe6ff7183d75da8bca4085598e)) +* **core:** Initialize queue once in queue mode ([#8025](https://github.com/n8n-io/n8n/issues/8025)) ([53c0b49](https://github.com/n8n-io/n8n/commit/53c0b49d15047461e3b65baed65c9d76dff99539)) +* **core:** Prevent axios from force setting a form-urlencoded content-type ([#8117](https://github.com/n8n-io/n8n/issues/8117)) ([bba9576](https://github.com/n8n-io/n8n/commit/bba95761e2f2b54af1fcab8a7b1d626ca10d537e)), closes [/github.com/axios/axios/blob/v1.x/lib/core/dispatchRequest.js#L45-L47](https://github.com//github.com/axios/axios/blob/v1.x/lib/core/dispatchRequest.js/issues/L45-L47) +* **core:** Remove circular references before serializing executions in public API ([#8043](https://github.com/n8n-io/n8n/issues/8043)) ([989888d](https://github.com/n8n-io/n8n/commit/989888d9bcec6f4eb3c811ce10d480737d96b102)), closes [#8030](https://github.com/n8n-io/n8n/issues/8030) +* **core:** Restore workflow ID during execution creation ([#8031](https://github.com/n8n-io/n8n/issues/8031)) ([c5e6ba8](https://github.com/n8n-io/n8n/commit/c5e6ba8cdd4a8f117ccc2e89e55497117156d8af)), closes [/github.com/n8n-io/n8n/pull/8002/files#diff-c8cbb62ca9ab2ae45e5f565cd8c63fff6475809a6241ea0b90acc575615224](https://github.com//github.com/n8n-io/n8n/pull/8002/files/issues/diff-c8cbb62ca9ab2ae45e5f565cd8c63fff6475809a6241ea0b90acc575615224) +* **core:** Use relative imports for dynamic imports in SecurityAuditService ([#8086](https://github.com/n8n-io/n8n/issues/8086)) ([785bf99](https://github.com/n8n-io/n8n/commit/785bf9974e38ea84c016e210a3108f4af567510d)), closes [#8085](https://github.com/n8n-io/n8n/issues/8085) +* **editor:** Add back credential `use` permission ([#8023](https://github.com/n8n-io/n8n/issues/8023)) ([329e5bf](https://github.com/n8n-io/n8n/commit/329e5bf9eed8556aba2bbd50bad9dbd6d3b373ad)) +* **editor:** Cleanup Executions page component ([#8053](https://github.com/n8n-io/n8n/issues/8053)) ([2689c37](https://github.com/n8n-io/n8n/commit/2689c37e87c5b3ae5029121f4d3dc878841e8844)) +* **editor:** Disable auto scroll and list size check when clicking on executions ([#7983](https://github.com/n8n-io/n8n/issues/7983)) ([fcb8b91](https://github.com/n8n-io/n8n/commit/fcb8b91f37e1fb0ef42f411c84390180e1ed7bbe)) +* **editor:** Ensure execution data overrides pinned data when copying in executions view ([#8009](https://github.com/n8n-io/n8n/issues/8009)) ([1d1cb0d](https://github.com/n8n-io/n8n/commit/1d1cb0d3c530856e0c26d8f146f60b2555625ab6)) +* **editor:** Fix copy/paste issue when switch node is in workflow ([#8103](https://github.com/n8n-io/n8n/issues/8103)) ([4b86926](https://github.com/n8n-io/n8n/commit/4b86926752fb1304a46385cb46bdf34fda0d53b6)) +* **editor:** Make keyboard shortcuts more strict; don't accept extra Ctrl/Alt/Shift keys ([#8024](https://github.com/n8n-io/n8n/issues/8024)) ([8df49e1](https://github.com/n8n-io/n8n/commit/8df49e134d886267f9f7475573d013371220dcac)) +* **editor:** Show credential share info only to appropriate users ([#8020](https://github.com/n8n-io/n8n/issues/8020)) ([b29b4d4](https://github.com/n8n-io/n8n/commit/b29b4d442bb0617aa516748ec48379eae0996cf0)) +* **editor:** Turn off executions list auto-refresh after leaving the page ([#8005](https://github.com/n8n-io/n8n/issues/8005)) ([e3c363d](https://github.com/n8n-io/n8n/commit/e3c363d72cf4ee49086d012f92a7b34be958482f)) +* **editor:** Update image sizes in template description not to be full width always ([#8037](https://github.com/n8n-io/n8n/issues/8037)) ([63a6e7e](https://github.com/n8n-io/n8n/commit/63a6e7e0340e1b00719f212ac620600a90d70ef1)) +* **HTTP Request Node:** Do not create circular references in HTTP request node output ([#8030](https://github.com/n8n-io/n8n/issues/8030)) ([5b7ea16](https://github.com/n8n-io/n8n/commit/5b7ea16d9a20880c72779b02620e99ebe9f3617a)) +* Stop binary data restoration from preventing execution from finishing ([#8082](https://github.com/n8n-io/n8n/issues/8082)) ([5ffff1b](https://github.com/n8n-io/n8n/commit/5ffff1bb22691c09c5ca8b3ada2a19d5ce155a0b)) +* Upgrade axios to address CVE-2023-45857 ([#7713](https://github.com/n8n-io/n8n/issues/7713)) ([64eb9bb](https://github.com/n8n-io/n8n/commit/64eb9bbc3624ee8f2fa90812711ad568926fdca8)) + + +### Features + +* Add config option to prefer GET request over LIST when using Hashicorp Vault ([#8049](https://github.com/n8n-io/n8n/issues/8049)) ([439a22d](https://github.com/n8n-io/n8n/commit/439a22d68f7bf32f281b1078b71607307640a09b)) +* Add option to `returnIntermediateSteps` for AI agents ([#8113](https://github.com/n8n-io/n8n/issues/8113)) ([7806a65](https://github.com/n8n-io/n8n/commit/7806a65229878a473f5526bad0b94614e8bfa8aa)) +* **core:** Add N8N_GRACEFUL_SHUTDOWN_TIMEOUT env var ([#8068](https://github.com/n8n-io/n8n/issues/8068)) ([614f488](https://github.com/n8n-io/n8n/commit/614f48838626e2af8e3f2e76ee4a144af2d40f72)) +* **editor:** Add lead enrichment suggestions to workflow list ([#8042](https://github.com/n8n-io/n8n/issues/8042)) ([36a923c](https://github.com/n8n-io/n8n/commit/36a923cf7bd4d42b8f8decbf01255c41d6dc1671)), closes [-update-workflows-list-page-to-show-fake-door-templates#comment-b6644c99](https://github.com/-update-workflows-list-page-to-show-fake-door-templates/issues/comment-b6644c99) +* **editor:** Finalize workers view ([#8052](https://github.com/n8n-io/n8n/issues/8052)) ([edfa784](https://github.com/n8n-io/n8n/commit/edfa78414d6bce901becc05e9d860f2521139688)) +* **editor:** Gracefully ignore invalid payloads in postMessage handler ([#8096](https://github.com/n8n-io/n8n/issues/8096)) ([9d22c7a](https://github.com/n8n-io/n8n/commit/9d22c7a2782a1908f81bcf80260cd91cb296e239)) +* **editor:** Upgrade frontend tooling to address a few vulnerabilities ([#8100](https://github.com/n8n-io/n8n/issues/8100)) ([19b7f1f](https://github.com/n8n-io/n8n/commit/19b7f1ffb17dcd6ac77839f97c2544f60f4ad55e)) +* **Filter Node:** Overhaul UI by adding the new filter component ([#8016](https://github.com/n8n-io/n8n/issues/8016)) ([3d53052](https://github.com/n8n-io/n8n/commit/3d530522f828dfc985ae98e4bb551aa3a2bd44c6)) +* **Respond to Webhook Node:** Overhaul with improvements like returning all items ([#8093](https://github.com/n8n-io/n8n/issues/8093)) ([32d397e](https://github.com/n8n-io/n8n/commit/32d397eff315fdc77677c0b134a7a25bcd8ca5d0)) + + +### Performance Improvements + +* **editor:** Improve canvas rendering performance ([#8022](https://github.com/n8n-io/n8n/issues/8022)) ([b780436](https://github.com/n8n-io/n8n/commit/b780436a6b445dc5951217b5a1f2c61b34961757)) + + + +# [1.21.0](https://github.com/n8n-io/n8n/compare/n8n@1.20.0...n8n@1.21.0) (2023-12-13) + + +### Bug Fixes + +* **core:** Ensure inviter and invitee are set correctly in invite link ([#7943](https://github.com/n8n-io/n8n/issues/7943)) ([386bd61](https://github.com/n8n-io/n8n/commit/386bd619676e54e960ca0af3ff47fa3b9c16c813)), closes [4#a39f9e5ba64a48b58a71d81c837e8227](https://github.com/4/issues/a39f9e5ba64a48b58a71d81c837e8227) [4#f6a177d32bde4b57ae2da0b8e454](https://github.com/4/issues/f6a177d32bde4b57ae2da0b8e454) [4#fef2d36ab02247e1a0f65a74f6fb534](https://github.com/4/issues/fef2d36ab02247e1a0f65a74f6fb534) +* **core:** Fix user comparison in same-user subworkflow caller policy ([#7913](https://github.com/n8n-io/n8n/issues/7913)) ([92bab72](https://github.com/n8n-io/n8n/commit/92bab72cffb1083b495d211d0a31920e83e66769)) +* **core:** Perform multi-main leader check against key ID ([#7964](https://github.com/n8n-io/n8n/issues/7964)) ([1a87f70](https://github.com/n8n-io/n8n/commit/1a87f70e8404218308072ee2f35c6ba2af34c23f)), closes [4#a39f9e5ba64a48b58a71d81c837e8227](https://github.com/4/issues/a39f9e5ba64a48b58a71d81c837e8227) [4#f6a177d32bde4b57ae2da0b8e454](https://github.com/4/issues/f6a177d32bde4b57ae2da0b8e454) [4#fef2d36ab02247e1a0f65a74f6fb534](https://github.com/4/issues/fef2d36ab02247e1a0f65a74f6fb534) +* **core:** Prevent workflow history saving error from happening ([#7812](https://github.com/n8n-io/n8n/issues/7812)) ([e5581ce](https://github.com/n8n-io/n8n/commit/e5581ce8023e21d3dcf140099f3a53e5ffb4584f)) +* **editor:** Add missing string for worker in log streaming ([#7971](https://github.com/n8n-io/n8n/issues/7971)) ([148bc1d](https://github.com/n8n-io/n8n/commit/148bc1d303af3aafd73e73e11c3dd9cefd40a1dd)), closes [4#a39f9e5ba64a48b58a71d81c837e8227](https://github.com/4/issues/a39f9e5ba64a48b58a71d81c837e8227) [4#f6a177d32bde4b57ae2da0b8e454](https://github.com/4/issues/f6a177d32bde4b57ae2da0b8e454) [4#fef2d36ab02247e1a0f65a74f6fb534](https://github.com/4/issues/fef2d36ab02247e1a0f65a74f6fb534) +* **editor:** Allow SSH protocol in git repository URL for environments ([#7944](https://github.com/n8n-io/n8n/issues/7944)) ([bc1c72f](https://github.com/n8n-io/n8n/commit/bc1c72f992a47a9c263aec175ca820088cf340ec)), closes [4#a39f9e5ba64a48b58a71d81c837e8227](https://github.com/4/issues/a39f9e5ba64a48b58a71d81c837e8227) [4#f6a177d32bde4b57ae2da0b8e454](https://github.com/4/issues/f6a177d32bde4b57ae2da0b8e454) [4#fef2d36ab02247e1a0f65a74f6fb534](https://github.com/4/issues/fef2d36ab02247e1a0f65a74f6fb534) +* **editor:** Fix bug with node names with certain characters ([#8013](https://github.com/n8n-io/n8n/issues/8013)) ([26f0d57](https://github.com/n8n-io/n8n/commit/26f0d57f5fb71a06c92968a4997cceae62f32312)) +* **editor:** Fix Webhook URL expansion icon ([#8011](https://github.com/n8n-io/n8n/issues/8011)) ([b00b905](https://github.com/n8n-io/n8n/commit/b00b9057a42f23cd9c4bb6675a3e6134610bf81b)) +* **editor:** Prevent opening NDV search if `/` is typed in a contenteditable element ([#7968](https://github.com/n8n-io/n8n/issues/7968)) ([e8a493f](https://github.com/n8n-io/n8n/commit/e8a493f71863e6a5d2685b48a61a0d11daf5edc5)) +* **editor:** Return early in ws message handler if no 'command' keyword is found ([#7946](https://github.com/n8n-io/n8n/issues/7946)) ([5b2defc](https://github.com/n8n-io/n8n/commit/5b2defc867a0627a861bf0fb98abfd99f8efe934)) +* Ensure external hooks post workflow execute run in queue mode ([#7947](https://github.com/n8n-io/n8n/issues/7947)) ([3ba7deb](https://github.com/n8n-io/n8n/commit/3ba7deb337963d40ae70f40ffb2f4eb23cac89b7)), closes [4#a39f9e5ba64a48b58a71d81c837e8227](https://github.com/4/issues/a39f9e5ba64a48b58a71d81c837e8227) [4#f6a177d32bde4b57ae2da0b8e454](https://github.com/4/issues/f6a177d32bde4b57ae2da0b8e454) [4#fef2d36ab02247e1a0f65a74f6fb534](https://github.com/4/issues/fef2d36ab02247e1a0f65a74f6fb534) +* **FileMaker Node:** Prevent erroring on zero fields loaded ([#7955](https://github.com/n8n-io/n8n/issues/7955)) ([10ad386](https://github.com/n8n-io/n8n/commit/10ad3866048ad06d0e8455ed2c52c618ae9e5032)), closes [4#a39f9e5ba64a48b58a71d81c837e8227](https://github.com/4/issues/a39f9e5ba64a48b58a71d81c837e8227) [4#f6a177d32bde4b57ae2da0b8e454](https://github.com/4/issues/f6a177d32bde4b57ae2da0b8e454) [4#fef2d36ab02247e1a0f65a74f6fb534](https://github.com/4/issues/fef2d36ab02247e1a0f65a74f6fb534) +* Fix issue preventing secrets from loading if the path contains - or / ([#7988](https://github.com/n8n-io/n8n/issues/7988)) ([0ac9594](https://github.com/n8n-io/n8n/commit/0ac959463f25187c5be4116a2209411afd903d87)) +* **Google Sheets Node:** Prevent erroring on zero sheet search results ([#7957](https://github.com/n8n-io/n8n/issues/7957)) ([9b877a9](https://github.com/n8n-io/n8n/commit/9b877a942787c855c3a3a011c19c5d1d30b8da67)), closes [4#a39f9e5ba64a48b58a71d81c837e8227](https://github.com/4/issues/a39f9e5ba64a48b58a71d81c837e8227) [4#f6a177d32bde4b57ae2da0b8e454](https://github.com/4/issues/f6a177d32bde4b57ae2da0b8e454) [4#fef2d36ab02247e1a0f65a74f6fb534](https://github.com/4/issues/fef2d36ab02247e1a0f65a74f6fb534) +* **Google Sheets Node:** Prevent erroring when fetching mapping columns ([#7972](https://github.com/n8n-io/n8n/issues/7972)) ([29a1066](https://github.com/n8n-io/n8n/commit/29a10668d17cdeb8b0e93c912f59c5976b6fc6c6)), closes [4#a39f9e5ba64a48b58a71d81c837e8227](https://github.com/4/issues/a39f9e5ba64a48b58a71d81c837e8227) [4#f6a177d32bde4b57ae2da0b8e454](https://github.com/4/issues/f6a177d32bde4b57ae2da0b8e454) [4#fef2d36ab02247e1a0f65a74f6fb534](https://github.com/4/issues/fef2d36ab02247e1a0f65a74f6fb534) +* **Postgres Node:** Do not include id column in upsert fields selection if it's not unique ([#7975](https://github.com/n8n-io/n8n/issues/7975)) ([435392c](https://github.com/n8n-io/n8n/commit/435392cbfe150c5e85d092686b3b7e20273421cc)) +* **Postgres Trigger Node:** Increase manual trigger timeout from 30 to 60 seconds ([#8015](https://github.com/n8n-io/n8n/issues/8015)) ([09a5729](https://github.com/n8n-io/n8n/commit/09a5729305a8072f5e98a320c85ad1c83a6946ed)) +* Restrict updating/deleting of shared but not owned credentials ([#7950](https://github.com/n8n-io/n8n/issues/7950)) ([42e828d](https://github.com/n8n-io/n8n/commit/42e828d5c655e54b6a4ec83c398c684996b9cc3e)), closes [4#a39f9e5ba64a48b58a71d81c837e8227](https://github.com/4/issues/a39f9e5ba64a48b58a71d81c837e8227) [4#f6a177d32bde4b57ae2da0b8e454](https://github.com/4/issues/f6a177d32bde4b57ae2da0b8e454) [4#fef2d36ab02247e1a0f65a74f6fb534](https://github.com/4/issues/fef2d36ab02247e1a0f65a74f6fb534) +* **Webhook Node:** Binary data handling ([#7804](https://github.com/n8n-io/n8n/issues/7804)) ([565b409](https://github.com/n8n-io/n8n/commit/565b409a82ca6173efd19f26a5f5b27a359a3b87)) +* **Webhook Node:** Do not create binary data when there is no data in the request ([#8000](https://github.com/n8n-io/n8n/issues/8000)) ([70f0755](https://github.com/n8n-io/n8n/commit/70f0755278e0a2bdb61c29623f27623b65473ab4)), closes [/github.com/n8n-io/n8n/pull/7804/files#r1422641833](https://github.com//github.com/n8n-io/n8n/pull/7804/files/issues/r1422641833) + + +### Features + +* Add config option for external secret update interval ([#7995](https://github.com/n8n-io/n8n/issues/7995)) ([b6c1c04](https://github.com/n8n-io/n8n/commit/b6c1c04b541d0944c5baac1ab021539c8f020f10)) +* AI nodes usability fixes + Summarization Chain V2 ([#7949](https://github.com/n8n-io/n8n/issues/7949)) ([dcf1286](https://github.com/n8n-io/n8n/commit/dcf12867b3c49596cd214812caee3292d2e794de)), closes [4#a39f9e5ba64a48b58a71d81c837e8227](https://github.com/4/issues/a39f9e5ba64a48b58a71d81c837e8227) [4#f6a177d32bde4b57ae2da0b8e454](https://github.com/4/issues/f6a177d32bde4b57ae2da0b8e454) [4#fef2d36ab02247e1a0f65a74f6fb534](https://github.com/4/issues/fef2d36ab02247e1a0f65a74f6fb534) +* Data transformation nodes and actions in Nodes Panel ([#7760](https://github.com/n8n-io/n8n/issues/7760)) ([675ec21](https://github.com/n8n-io/n8n/commit/675ec21d335af2b2c9598bc2bec18194506ef71a)) +* **editor:** Add AppCues tracking for onboarding event ([#7945](https://github.com/n8n-io/n8n/issues/7945)) ([04cabaf](https://github.com/n8n-io/n8n/commit/04cabafef7acbc30cba647732e2ca8ae8a02d29a)), closes [4#a39f9e5ba64a48b58a71d81c837e8227](https://github.com/4/issues/a39f9e5ba64a48b58a71d81c837e8227) [4#f6a177d32bde4b57ae2da0b8e454](https://github.com/4/issues/f6a177d32bde4b57ae2da0b8e454) [4#fef2d36ab02247e1a0f65a74f6fb534](https://github.com/4/issues/fef2d36ab02247e1a0f65a74f6fb534) +* **editor:** Add option to disable NDV in workflow previews ([#7990](https://github.com/n8n-io/n8n/issues/7990)) ([393afef](https://github.com/n8n-io/n8n/commit/393afef1747f168d5fa42be2424fd02125f1bbac)), closes [4#a39f9e5ba64a48b58a71d81c837e8227](https://github.com/4/issues/a39f9e5ba64a48b58a71d81c837e8227) [4#f6a177d32bde4b57ae2da0b8e454](https://github.com/4/issues/f6a177d32bde4b57ae2da0b8e454) [4#fef2d36ab02247e1a0f65a74f6fb534](https://github.com/4/issues/fef2d36ab02247e1a0f65a74f6fb534) +* **editor:** Filter component + implement in If node ([#7490](https://github.com/n8n-io/n8n/issues/7490)) ([8a53434](https://github.com/n8n-io/n8n/commit/8a5343401dd355436120a9a424ae455e80b50da6)) +* **editor:** Show template credential setup based on feature flag ([#7989](https://github.com/n8n-io/n8n/issues/7989)) ([08ee307](https://github.com/n8n-io/n8n/commit/08ee3072093fb26b14b48e2b35d8c8d018317f13)) +* **Google Ads Node:** Update to support v15 ([#7962](https://github.com/n8n-io/n8n/issues/7962)) ([7f01269](https://github.com/n8n-io/n8n/commit/7f0126915aae514a0ab515a4baf5582da2aeb1e3)) +* Introduce advanced permissions ([#7844](https://github.com/n8n-io/n8n/issues/7844)) ([dbd62a4](https://github.com/n8n-io/n8n/commit/dbd62a4992ab8aca59e3cb50d3d970454e462238)) +* **Local File Trigger Node:** Add polling option typically good to watch network files/folders ([#7942](https://github.com/n8n-io/n8n/issues/7942)) ([2fbdfec](https://github.com/n8n-io/n8n/commit/2fbdfec0c0a3f5da64764e7821e84db30b664e49)) +* **n8n Form Trigger Node:** Improvements ([#7571](https://github.com/n8n-io/n8n/issues/7571)) ([953a58f](https://github.com/n8n-io/n8n/commit/953a58f18bfdd36fa8b526ca6213631aacab49cb)) + + + +# [1.20.0](https://github.com/n8n-io/n8n/compare/n8n@1.19.0...n8n@1.20.0) (2023-12-06) + + +### Bug Fixes + +* **AWS DynamoDB Node:** Improve error message parsing ([#7793](https://github.com/n8n-io/n8n/issues/7793)) ([5ba5ed8](https://github.com/n8n-io/n8n/commit/5ba5ed8e3c8ba2f909859bde129d92576fbda46f)) +* **core:** Allow grace period for binary data deletion after manual execution ([#7889](https://github.com/n8n-io/n8n/issues/7889)) ([61d8aeb](https://github.com/n8n-io/n8n/commit/61d8aebeaf6487269b252b353fdf16dcb67f41ff)) +* **core:** Consolidate ownership and sharing data on workflows and credentials ([#7920](https://github.com/n8n-io/n8n/issues/7920)) ([38b88b9](https://github.com/n8n-io/n8n/commit/38b88b946bab67dc1a964bb3c980a627d4a32595)) +* **core:** Fix hard deletes stopping if database query throws ([#7848](https://github.com/n8n-io/n8n/issues/7848)) ([46dd4d3](https://github.com/n8n-io/n8n/commit/46dd4d3105db3a15c81903ae81c9bbb21a45397b)) +* **core:** Make sure mfa secret and recovery codes are not returned on login ([#7936](https://github.com/n8n-io/n8n/issues/7936)) ([f5502cc](https://github.com/n8n-io/n8n/commit/f5502cc628f6b348f7fe3325b96ec9dc3360beaf)), closes [/github.com/n8n-io/n8n/pull/6994/files#diff-95a87cb029a3d26e6722df2e68132453fc254fc1f4540cbdaa95cfdbda1893deL91](https://github.com//github.com/n8n-io/n8n/pull/6994/files/issues/diff-95a87cb029a3d26e6722df2e68132453fc254fc1f4540cbdaa95cfdbda1893deL91) [4#a39f9e5ba64a48b58a71d81c837e8227](https://github.com/4/issues/a39f9e5ba64a48b58a71d81c837e8227) [4#f6a177d32bde4b57ae2da0b8e454](https://github.com/4/issues/f6a177d32bde4b57ae2da0b8e454) [4#fef2d36ab02247e1a0f65a74f6fb534](https://github.com/4/issues/fef2d36ab02247e1a0f65a74f6fb534) +* **editor:** Fix deletion of last execution at execution preview ([#7883](https://github.com/n8n-io/n8n/issues/7883)) ([ce2d388](https://github.com/n8n-io/n8n/commit/ce2d388f059c0bb32d27f4b29e901d1a70083610)) +* **editor:** Replace isInstanceOwner checks with scopes where applicable ([#7858](https://github.com/n8n-io/n8n/issues/7858)) ([132d691](https://github.com/n8n-io/n8n/commit/132d691cbf983f60293c7423de0077fb7c97e0af)) +* **Google Sheets Node:** Fix issue with paired items not being set correctly ([#7862](https://github.com/n8n-io/n8n/issues/7862)) ([5207a2f](https://github.com/n8n-io/n8n/commit/5207a2fe5210e40d3b2aedd95182a18e497c72ab)) +* **Notion Node:** Fix broken Notion node parameters ([#7864](https://github.com/n8n-io/n8n/issues/7864)) ([51d1f5b](https://github.com/n8n-io/n8n/commit/51d1f5b82070542d45c3d57387343959a3f0abb2)), closes [#7791](https://github.com/n8n-io/n8n/issues/7791) + + +### Features + +* **BambooHR Node:** Add support for Only Current on company reports ([#7878](https://github.com/n8n-io/n8n/issues/7878)) ([4175801](https://github.com/n8n-io/n8n/commit/4175801c90ad4f744d1a7c331d4fb20891ed2e9e)) +* **core:** Allow admin creation ([#7837](https://github.com/n8n-io/n8n/issues/7837)) ([476806e](https://github.com/n8n-io/n8n/commit/476806ebb0f31f656992fb67aba37116f10e1475)) +* **editor:** Add sections to create node panel ([#7831](https://github.com/n8n-io/n8n/issues/7831)) ([39fa8d2](https://github.com/n8n-io/n8n/commit/39fa8d21bbee5d870b2620ec65401a5ca134c4f1)) +* **editor:** Open template credential setup from collection ([#7882](https://github.com/n8n-io/n8n/issues/7882)) ([627ddb9](https://github.com/n8n-io/n8n/commit/627ddb91fb6c00796671a1f72f59a251cd89004d)) +* **editor:** Select credentials in template setup if theres only one ([#7879](https://github.com/n8n-io/n8n/issues/7879)) ([fe3417a](https://github.com/n8n-io/n8n/commit/fe3417a615534a01cb0c7b5e8f47bc18abd5cd4d)) + + +### Performance Improvements + +* **editor:** Improve node rendering performance when opening large workflows ([#7904](https://github.com/n8n-io/n8n/issues/7904)) ([a8049a0](https://github.com/n8n-io/n8n/commit/a8049a0def21506ebf4fb1d3b69ae28ec35fdc21)), closes [#7901](https://github.com/n8n-io/n8n/issues/7901) [4#a39f9e5ba64a48b58a71d81c837e8227](https://github.com/4/issues/a39f9e5ba64a48b58a71d81c837e8227) [4#f6a177d32bde4b57ae2da0b8e454](https://github.com/4/issues/f6a177d32bde4b57ae2da0b8e454) [4#fef2d36ab02247e1a0f65a74f6fb534](https://github.com/4/issues/fef2d36ab02247e1a0f65a74f6fb534) +* **editor:** Improve performance when opening large workflows with node issues ([#7901](https://github.com/n8n-io/n8n/issues/7901)) ([4bd7ae2](https://github.com/n8n-io/n8n/commit/4bd7ae29f7c82b8817420e617a123024147c6c70)) + + + +# [1.19.0](https://github.com/n8n-io/n8n/compare/n8n@1.18.0...n8n@1.19.0) (2023-11-29) + + +### Bug Fixes + +* **core:** Ensure member and admin cannot be promoted to owner ([#7830](https://github.com/n8n-io/n8n/issues/7830)) ([9b87a59](https://github.com/n8n-io/n8n/commit/9b87a596ca4aec462faedcca1ba4655b168bc3bc)), closes [/linear.app/n8n/issue/PAY-985/add-user-role-modification-endpoint#comment-62355f6](https://github.com//linear.app/n8n/issue/PAY-985/add-user-role-modification-endpoint/issues/comment-62355f6) +* **core:** Prevent error messages due to statistics about data loading ([#7824](https://github.com/n8n-io/n8n/issues/7824)) ([847f6ac](https://github.com/n8n-io/n8n/commit/847f6ac771316eea270d2e83adac5d8a6483475a)) +* **core:** Tighten checks for multi-main setup usage ([#7788](https://github.com/n8n-io/n8n/issues/7788)) ([fdb2c18](https://github.com/n8n-io/n8n/commit/fdb2c18ecc49d1e8759e809d2e2c2e4aa17009da)) +* **core:** Use AbortController to notify nodes to abort execution ([#6141](https://github.com/n8n-io/n8n/issues/6141)) ([d2c18c5](https://github.com/n8n-io/n8n/commit/d2c18c57274cac02e70cf1cc9e533a6ca98f0ec6)) +* **editor:** Add telemetry to workflow history ([#7811](https://github.com/n8n-io/n8n/issues/7811)) ([d497041](https://github.com/n8n-io/n8n/commit/d4970410e1ba328b05ddc23abcbf33c719de5624)) +* **editor:** Allow owners and admins to share workflows and credentials they don't own ([#7833](https://github.com/n8n-io/n8n/issues/7833)) ([3ab3ec9](https://github.com/n8n-io/n8n/commit/3ab3ec9da88f7b7ae07a98d7ef7c4f9892079048)) +* **editor:** Disable context menu actions in read-only mode ([#7789](https://github.com/n8n-io/n8n/issues/7789)) ([902beff](https://github.com/n8n-io/n8n/commit/902beffce51d547094ea249d1fbbb70a879165d6)) +* **editor:** Fix cloud plan data loading on instance ([#7841](https://github.com/n8n-io/n8n/issues/7841)) ([8b99384](https://github.com/n8n-io/n8n/commit/8b99384367161a47b3de13b7e83bcf6d07e3bf19)) +* **editor:** Fix credential icon for old node type version ([#7843](https://github.com/n8n-io/n8n/issues/7843)) ([4074107](https://github.com/n8n-io/n8n/commit/40741075117dd8018ca1c6d03c050c3959142ebd)) +* **editor:** Fix icon for unknown node type ([#7842](https://github.com/n8n-io/n8n/issues/7842)) ([28ac5a7](https://github.com/n8n-io/n8n/commit/28ac5a750eb28e9ab41611a76fa5fb9c30ce64dc)) +* **editor:** Fix mouse position in workflow previews ([#7853](https://github.com/n8n-io/n8n/issues/7853)) ([c063398](https://github.com/n8n-io/n8n/commit/c0633987bfd6de24db0efc4bcb73adba9e9b6a74)) +* **editor:** Show nice error when environment is not set up ([#7778](https://github.com/n8n-io/n8n/issues/7778)) ([5835e05](https://github.com/n8n-io/n8n/commit/5835e055d39921cdf2aa9799e427931bec8e5e2c)) +* **editor:** Suppress dev server websocket messages in workflow view ([#7808](https://github.com/n8n-io/n8n/issues/7808)) ([685ffd7](https://github.com/n8n-io/n8n/commit/685ffd74137199f5e10145a33f3e0f03dabc2e7f)) +* **Google Sheets Node:** Read operation execute for each item ([#7800](https://github.com/n8n-io/n8n/issues/7800)) ([d548872](https://github.com/n8n-io/n8n/commit/d5488725a83f6705b95c9de9d8736adf1b870134)) +* **HTTP Request Node:** Enable expressions for binary input data fields ([#7782](https://github.com/n8n-io/n8n/issues/7782)) ([6208af0](https://github.com/n8n-io/n8n/commit/6208af07eb393b0fb8483b8ae4949a63423cc409)) +* **Microsoft SQL Node:** Prevent double escaping table name ([#7801](https://github.com/n8n-io/n8n/issues/7801)) ([73ec753](https://github.com/n8n-io/n8n/commit/73ec7533ce4724940c2b23f677a9dfcf75de6a16)) + + +### Features + +* Add AI tool building capabilities ([#7336](https://github.com/n8n-io/n8n/issues/7336)) ([87def60](https://github.com/n8n-io/n8n/commit/87def60979bd6525b59df4bd811571a2afe83bec)) +* Add initial scope checks via decorators ([#7737](https://github.com/n8n-io/n8n/issues/7737)) ([a37f1cb](https://github.com/n8n-io/n8n/commit/a37f1cb0bad87c486c387383f8179aa42f0b9e1a)) +* Add user role select to users list settings page ([#7796](https://github.com/n8n-io/n8n/issues/7796)) ([137e238](https://github.com/n8n-io/n8n/commit/137e23853fdbd3e62037a6cb7f742811af41a03d)) +* Ado 1296 spike credential setup in templates ([#7786](https://github.com/n8n-io/n8n/issues/7786)) ([aae45b0](https://github.com/n8n-io/n8n/commit/aae45b043b9e1427f9ffc44ef88d719782fccee5)) +* **core:** Add Support for custom CORS origins for webhooks ([#7455](https://github.com/n8n-io/n8n/issues/7455)) ([99a9ea4](https://github.com/n8n-io/n8n/commit/99a9ea497a3d21739f911da5c88c076f60471bed)) +* **core:** Allow user role modification ([#7797](https://github.com/n8n-io/n8n/issues/7797)) ([7a86d36](https://github.com/n8n-io/n8n/commit/7a86d3606852fcbc537533af24eef34279b229c6)) +* **core:** Set up endpoint for all existing roles with license flag ([#7834](https://github.com/n8n-io/n8n/issues/7834)) ([2356fb0](https://github.com/n8n-io/n8n/commit/2356fb0f0c247271ffa00d1cf25460e06212f1c4)) +* **editor:** Add node name and version to NDV node settings ([#7731](https://github.com/n8n-io/n8n/issues/7731)) ([da85198](https://github.com/n8n-io/n8n/commit/da851986f6f7cd4375b06c28a149dcb375fe8b83)) +* **editor:** Add routing middleware, permission checks, RBAC store, RBAC component ([#7702](https://github.com/n8n-io/n8n/issues/7702)) ([67a8891](https://github.com/n8n-io/n8n/commit/67a88914f2f2d11c413e7f627d659333d8419af8)) +* **editor:** Replace middleware for Role checks with Scope checks ([#7847](https://github.com/n8n-io/n8n/issues/7847)) ([72852a6](https://github.com/n8n-io/n8n/commit/72852a60eb15cbf45ebcdd390770c4cd9929a457)) +* **editor:** Show avatars for users currently working on the same workflow ([#7763](https://github.com/n8n-io/n8n/issues/7763)) ([77bc8ec](https://github.com/n8n-io/n8n/commit/77bc8ecd4b1552f7253bc1348087db518ce7ce07)) +* **Notion Node:** Option to simplify output in getChildBlocks operation ([#7791](https://github.com/n8n-io/n8n/issues/7791)) ([d667bca](https://github.com/n8n-io/n8n/commit/d667bca658a2b79fa5d0afba9ef25f26a10cdfc2)) +* **Slack Node:** Add support for getting the profile of a user ([#7829](https://github.com/n8n-io/n8n/issues/7829)) ([90bb6ba](https://github.com/n8n-io/n8n/commit/90bb6ba4174a71f0d42e8dc9f009b879ca9d4616)) + + + +# [1.18.0](https://github.com/n8n-io/n8n/compare/n8n@1.17.0...n8n@1.18.0) (2023-11-22) + + +### Bug Fixes + +* **core:** Account for non-ASCII chars in filename on binary data download ([#7742](https://github.com/n8n-io/n8n/issues/7742)) ([b4ebb1a](https://github.com/n8n-io/n8n/commit/b4ebb1a28dc87c297721299a635e836dcaa273b7)) +* **core:** Correct permissions for getstatus ([#7724](https://github.com/n8n-io/n8n/issues/7724)) ([f96c1d2](https://github.com/n8n-io/n8n/commit/f96c1d204400028c55a2120d0569180379c0649f)) +* **core:** Ensure failed executions are saved in queue mode ([#7744](https://github.com/n8n-io/n8n/issues/7744)) ([b7c5c74](https://github.com/n8n-io/n8n/commit/b7c5c7406f6f978bbd84737de34114e9492ae5f6)) +* **core:** Guard against node not found on cancelling test webhook ([#7750](https://github.com/n8n-io/n8n/issues/7750)) ([6be453b](https://github.com/n8n-io/n8n/commit/6be453b716eff14df420ef565ea1b5ffb3ce73f0)) +* **editor:** Handle permission edge cases (empty scopes) ([#7723](https://github.com/n8n-io/n8n/issues/7723)) ([e2ffd39](https://github.com/n8n-io/n8n/commit/e2ffd397fc0ab8d88128ba78d02c5df003af4a9d)) +* **editor:** Make sure LineController is registered with chart.js ([#7730](https://github.com/n8n-io/n8n/issues/7730)) ([ebee1a5](https://github.com/n8n-io/n8n/commit/ebee1a590873aa56c23fd610616196ee27fe657a)) +* **editor:** Move workerview entry into settings menu ([#7761](https://github.com/n8n-io/n8n/issues/7761)) ([366cd67](https://github.com/n8n-io/n8n/commit/366cd672a74649a19fc927e0327ae7c19ed5a1fc)) +* **editor:** Only show push to git menu item to owners ([#7766](https://github.com/n8n-io/n8n/issues/7766)) ([0d3d33d](https://github.com/n8n-io/n8n/commit/0d3d33dd1f2354248ac341a0c9f2553812f404e0)) +* **editor:** Show v1 banner dismiss button if owner ([#7722](https://github.com/n8n-io/n8n/issues/7722)) ([44d3b3e](https://github.com/n8n-io/n8n/commit/44d3b3ed7ee77715006591a4f49049388fcd4035)) +* **editor:** Use project diagram icon for worker view ([#7764](https://github.com/n8n-io/n8n/issues/7764)) ([ff0b651](https://github.com/n8n-io/n8n/commit/ff0b6511f74831c499ab032910dfa9cf38356e8c)) +* **editor:** Validate user info before submiting ([#7608](https://github.com/n8n-io/n8n/issues/7608)) ([2064f7f](https://github.com/n8n-io/n8n/commit/2064f7f251913a0cc22b4e27bb38df921f711109)) +* **GitHub Node:** Fix issue preventing file edits on branches ([#7734](https://github.com/n8n-io/n8n/issues/7734)) ([ce002a6](https://github.com/n8n-io/n8n/commit/ce002a6cc672d1e13cc3d3470add78781d1ef20e)) +* **Google Sheets Node:** Check for `null` before destructuring ([#7729](https://github.com/n8n-io/n8n/issues/7729)) ([5d4a52d](https://github.com/n8n-io/n8n/commit/5d4a52d3b7e35924e1a8c9a2c808418bdf224d2c)) +* **Item Lists Node:** Don't check same type in remove duplicates operation ([#7678](https://github.com/n8n-io/n8n/issues/7678)) ([4f30764](https://github.com/n8n-io/n8n/commit/4f307646f3a5691331c7c610c62f562921a005f8)) +* **JotForm Trigger Node:** Fix iteration on form loader ([#7751](https://github.com/n8n-io/n8n/issues/7751)) ([82f3202](https://github.com/n8n-io/n8n/commit/82f3202a2de2863f01abe3cf84d6f37eba4fb6fa)) + + +### Features + +* Add Creator hub link to Templates page ([#7721](https://github.com/n8n-io/n8n/issues/7721)) ([4dbae0e](https://github.com/n8n-io/n8n/commit/4dbae0e2e95d1b5f46cfc50a5a9fc6bb761defde)) +* **core:** Coordinate manual workflow activation and deactivation in multi-main scenario ([#7643](https://github.com/n8n-io/n8n/issues/7643)) ([4c40825](https://github.com/n8n-io/n8n/commit/4c4082503c916d654758da738321f9e78a098ce5)), closes [#7566](https://github.com/n8n-io/n8n/issues/7566) +* **editor:** Add node context menu ([#7620](https://github.com/n8n-io/n8n/issues/7620)) ([8d12c1a](https://github.com/n8n-io/n8n/commit/8d12c1ad8d9283764647836bdd50224259d506e9)) +* **editor:** Node IO filter ([#7503](https://github.com/n8n-io/n8n/issues/7503)) ([1881765](https://github.com/n8n-io/n8n/commit/18817651ec5d9ed5e774379ae5cf8f57c5461e43)) + + + +# [1.17.0](https://github.com/n8n-io/n8n/compare/n8n@1.16.0...n8n@1.17.0) (2023-11-15) + + +### Bug Fixes + +* **Convert to/from binary data Node:** Better mime type defaults ([#7693](https://github.com/n8n-io/n8n/issues/7693)) ([9b3be0c](https://github.com/n8n-io/n8n/commit/9b3be0cfd8b0b58903d89ea3bf0b73be579a4f89)) +* **core:** Consider subworkflows successfully run when in waiting state ([#7699](https://github.com/n8n-io/n8n/issues/7699)) ([0e00dab](https://github.com/n8n-io/n8n/commit/0e00dab9f5d5a6622cdc22fa8bfbecc039f6b67a)) +* **core:** Fix named parameter resolution in migrations ([#7688](https://github.com/n8n-io/n8n/issues/7688)) ([4441ed5](https://github.com/n8n-io/n8n/commit/4441ed51169e8be930c548b17f54147ff6bd8e7d)), closes [#7628](https://github.com/n8n-io/n8n/issues/7628) +* **core:** Initialize JWT Secret before it's used anywhere ([#7707](https://github.com/n8n-io/n8n/issues/7707)) ([3460eb5](https://github.com/n8n-io/n8n/commit/3460eb5eeba95e51ccdac05084daf883c9750022)) +* **core:** Reduce memory usage in credentials risk auditing ([#7663](https://github.com/n8n-io/n8n/issues/7663)) ([9fd6319](https://github.com/n8n-io/n8n/commit/9fd6319583d0446e41de4fb80d4bc5a6c5e1ca07)) +* **Date & Time Node:** Add fromFormat option to solve ambiguous date strings ([#7675](https://github.com/n8n-io/n8n/issues/7675)) ([d2d11e0](https://github.com/n8n-io/n8n/commit/d2d11e0208e8a20145910bbdd02e7b273fb0aa13)) +* **editor:** Fix resource mapper component being truncated ([#7664](https://github.com/n8n-io/n8n/issues/7664)) ([00dff50](https://github.com/n8n-io/n8n/commit/00dff50140d12e37bfeecdf1300ff313c179ec89)) +* **editor:** More securely clear executions tab auto refresh timer ([#7685](https://github.com/n8n-io/n8n/issues/7685)) ([37dd658](https://github.com/n8n-io/n8n/commit/37dd658dc5bc1128c91d86105bf7f49dfcf96985)) +* **editor:** Redirect to workflow editor after saving in debug mode ([#7645](https://github.com/n8n-io/n8n/issues/7645)) ([020042e](https://github.com/n8n-io/n8n/commit/020042ef1a329e805035061fbf6743bde892e3b1)) +* **Google Sheets Node:** Append exceeding grid limits ([#7684](https://github.com/n8n-io/n8n/issues/7684)) ([88efb99](https://github.com/n8n-io/n8n/commit/88efb9958711bac446b6a698dfba50afd2b46132)) +* **HTTP Request Node:** Support generic credentials when using pagination ([#7686](https://github.com/n8n-io/n8n/issues/7686)) ([48b240b](https://github.com/n8n-io/n8n/commit/48b240b0269858adb8fde8abb8a7211b2a3e78e0)), closes [#7653](https://github.com/n8n-io/n8n/issues/7653) +* **HubSpot Node:** Fetching available parameters fails when using expressions ([#7672](https://github.com/n8n-io/n8n/issues/7672)) ([a9ab738](https://github.com/n8n-io/n8n/commit/a9ab73896e6a42b2fd5df296c9ee95ac82936b7e)) +* **HubSpot Node:** Update deal owner on Hubspot Deal ([#7673](https://github.com/n8n-io/n8n/issues/7673)) ([3c0734b](https://github.com/n8n-io/n8n/commit/3c0734bd2d92e9d2b9e99658c2e14710f57f36ef)) +* **Spreadsheet File Node:** Read file as utf-8 in v1 ([#7701](https://github.com/n8n-io/n8n/issues/7701)) ([786b4ad](https://github.com/n8n-io/n8n/commit/786b4adcce910fa52104550d90a688c4046628f9)) + + +### Features + +* **core:** Expression function $ifEmpty ([#7660](https://github.com/n8n-io/n8n/issues/7660)) ([1c7225e](https://github.com/n8n-io/n8n/commit/1c7225ebdb1d92ce45313bbab27b0839d963fc4c)) +* **Date & Time Node:** Option to include other fields in output item ([#7661](https://github.com/n8n-io/n8n/issues/7661)) ([aea3c50](https://github.com/n8n-io/n8n/commit/aea3c501313debaf1cf2b194023a534f829290ea)) +* **Discord Node:** Overhaul ([#5351](https://github.com/n8n-io/n8n/issues/5351)) ([6a53c2a](https://github.com/n8n-io/n8n/commit/6a53c2a375ca71ffad1491da5ae7e6ec461a1a56)) +* **Discourse Node:** Add new options to Get Users ([#7674](https://github.com/n8n-io/n8n/issues/7674)) ([2e8c841](https://github.com/n8n-io/n8n/commit/2e8c841277c2ba45ab2ab3e823135f2b15a7e570)) +* **editor:** Add color selector to sticky node ([#7453](https://github.com/n8n-io/n8n/issues/7453)) ([8359364](https://github.com/n8n-io/n8n/commit/8359364536809e667be86f4b4df0838c94a801d7)) +* **editor:** Add HTTP request nodes for credentials without a node ([#7157](https://github.com/n8n-io/n8n/issues/7157)) ([14035e1](https://github.com/n8n-io/n8n/commit/14035e1244fee5bc49b9afe57d63d9e887f25dd0)) +* **editor:** Add workflow filters to querystring ([#7456](https://github.com/n8n-io/n8n/issues/7456)) ([afd637b](https://github.com/n8n-io/n8n/commit/afd637b5eab2bba33fd9ec8b24104bef5e2a4cc0)) +* **editor:** Adds a EE view to show worker details and job status ([#7600](https://github.com/n8n-io/n8n/issues/7600)) ([cbc6909](https://github.com/n8n-io/n8n/commit/cbc690907fa36e2fde0218dd6f7737d00498c674)) +* **GitLab Node:** Add support for pagination on getIssues ([#7529](https://github.com/n8n-io/n8n/issues/7529)) ([0a0798e](https://github.com/n8n-io/n8n/commit/0a0798e48500b0c159aa37deae7ce5d144f4f4c7)) +* **OpenAI Node:** Add dall-e-3 support ([#7655](https://github.com/n8n-io/n8n/issues/7655)) ([a9c7188](https://github.com/n8n-io/n8n/commit/a9c7188c4d9d3a020cb26647c9030f6ffd47a35a)) +* **RabbitMQ Trigger Node:** Add exchange and routing key options ([#7547](https://github.com/n8n-io/n8n/issues/7547)) ([5aee2b7](https://github.com/n8n-io/n8n/commit/5aee2b768f7743c6508c518bab35206577035380)) +* **Telegram Node:** Add support for markdownv2 ([#7679](https://github.com/n8n-io/n8n/issues/7679)) ([819b3a7](https://github.com/n8n-io/n8n/commit/819b3a746a1cfbb785c97d0c681734211a599852)) +* **Venafi TLS Protect Cloud Node:** Add region parameter to Venafi protect cloud ([#7689](https://github.com/n8n-io/n8n/issues/7689)) ([a08fca5](https://github.com/n8n-io/n8n/commit/a08fca51d928b7bfb7c0081287a38274048892bb)) + + +### Performance Improvements + +* **core:** Lazyload security audit reporters ([#7696](https://github.com/n8n-io/n8n/issues/7696)) ([b2ca050](https://github.com/n8n-io/n8n/commit/b2ca0500311d85742ef8abf8f9f0d1436e6d9ba1)) + + + +# [1.16.0](https://github.com/n8n-io/n8n/compare/n8n@1.15.1...n8n@1.16.0) (2023-11-08) + + +### Bug Fixes + +* **core:** Comply with custom default for workflow saving settings ([#7634](https://github.com/n8n-io/n8n/issues/7634)) ([48c068f](https://github.com/n8n-io/n8n/commit/48c068f97b6c7df08fec9fd9d80a0e7eaacc95f5)) +* **core:** Decrease reset password token expire time ([#7598](https://github.com/n8n-io/n8n/issues/7598)) ([2aa7f63](https://github.com/n8n-io/n8n/commit/2aa7f6375a01625980278aee714bdc06002b0948)) +* **core:** Ensure `init` before checking leader or follower in multi-main scenario ([#7621](https://github.com/n8n-io/n8n/issues/7621)) ([a994ba5](https://github.com/n8n-io/n8n/commit/a994ba5e8d7092edeae05e7aa5fdfbb9fd854034)) +* **core:** Ensure pruning starts only after migrations have completed ([#7626](https://github.com/n8n-io/n8n/issues/7626)) ([f748de9](https://github.com/n8n-io/n8n/commit/f748de9567ed1ecebea0ee35e9c71f8ea0e2d450)) +* **core:** Fix accessor error when running partial execution ([#7618](https://github.com/n8n-io/n8n/issues/7618)) ([26361df](https://github.com/n8n-io/n8n/commit/26361dfcd31c9952c8ef109314ca88f5f03e40f4)), closes [#6229](https://github.com/n8n-io/n8n/issues/6229) +* **core:** Make password-reset urls valid only for single-use ([#7622](https://github.com/n8n-io/n8n/issues/7622)) ([6031424](https://github.com/n8n-io/n8n/commit/60314248f4b021f451eb744184fe150ddc03bc6e)) +* **Crypto Node:** Fix issue with value not appearing for Sign action ([#7619](https://github.com/n8n-io/n8n/issues/7619)) ([5df583f](https://github.com/n8n-io/n8n/commit/5df583f783731e46500600e6a23ff3b7fdfb4e52)) +* **editor:** Allow overriding theme from query params ([#7591](https://github.com/n8n-io/n8n/issues/7591)) ([2854a0c](https://github.com/n8n-io/n8n/commit/2854a0cf467258c6dacc15c2b200cf6480b6ecef)) +* **editor:** Fix issue that frontend breaks with unkown nodes ([#7596](https://github.com/n8n-io/n8n/issues/7596)) ([db56a9e](https://github.com/n8n-io/n8n/commit/db56a9ee37e8b041ea8958fc8400b9e5b6b81316)) +* **editor:** Fix local storage flags defaulting to undefined string ([#7603](https://github.com/n8n-io/n8n/issues/7603)) ([151e60f](https://github.com/n8n-io/n8n/commit/151e60f829663e79982aae6ac1cd8489f3083224)) +* **editor:** Fix workflow history prune time limit (getting hours instead of days) ([#7644](https://github.com/n8n-io/n8n/issues/7644)) ([3d5a485](https://github.com/n8n-io/n8n/commit/3d5a485bcf7fef4c6b7d96df3a77c041178951a6)) +* **editor:** Hide not supported node options ([#7597](https://github.com/n8n-io/n8n/issues/7597)) ([b532a7b](https://github.com/n8n-io/n8n/commit/b532a7bdb7d33d5ffb20665dfde58cb664d39b4a)) +* **editor:** Remove unknown credentials on pasting workflow ([#7582](https://github.com/n8n-io/n8n/issues/7582)) ([d633753](https://github.com/n8n-io/n8n/commit/d63375368713b31e15735721c7a7603fe08a6645)) +* **editor:** Reset canvas zoom before workspace reset in node view ([#7625](https://github.com/n8n-io/n8n/issues/7625)) ([78b84af](https://github.com/n8n-io/n8n/commit/78b84af8d1cfed005c7d9c715d832e8c91fd9e3f)) +* **editor:** Zoom in/out on canvas the same amount on scroll/gesture ([#7602](https://github.com/n8n-io/n8n/issues/7602)) ([c92402a](https://github.com/n8n-io/n8n/commit/c92402a3cabfdc227f3c929bc7731d42f4516776)) +* **Facebook Lead Ads Trigger Node:** Fix issue with missing scope for business management ([#7616](https://github.com/n8n-io/n8n/issues/7616)) ([32b85ba](https://github.com/n8n-io/n8n/commit/32b85ba2fec6e74d8648be7e718b52140c1bc4fc)) + + +### Features + +* **core:** Add the node version to telemetry in node_graph_string ([#7449](https://github.com/n8n-io/n8n/issues/7449)) ([59dc36a](https://github.com/n8n-io/n8n/commit/59dc36abd9141a863cb41c17a9115410b27bdb16)) +* **core:** Coordinate workflow activation in multiple main scenario in internal API ([#7566](https://github.com/n8n-io/n8n/issues/7566)) ([c857e42](https://github.com/n8n-io/n8n/commit/c857e42677ef0d415caf66f00d7af029546dfd79)) +* **core:** Initial support for two-way communication over websockets ([#7570](https://github.com/n8n-io/n8n/issues/7570)) ([ac87701](https://github.com/n8n-io/n8n/commit/ac877014eda83eb2ee61c87f29e2583f3fbfd125)) +* **core:** Log executed migrations with info level ([#7586](https://github.com/n8n-io/n8n/issues/7586)) ([7dac9ab](https://github.com/n8n-io/n8n/commit/7dac9ab82c2f91cfbb66a57f175c1865e8c8107a)) +* **core:** Rate limit forgot password endpoint ([#7604](https://github.com/n8n-io/n8n/issues/7604)) ([5790e25](https://github.com/n8n-io/n8n/commit/5790e251b8072679d7c061e2d2fa1f4229e03cf8)) +* **LinkedIn Node:** Add support for Article thumbnails ([#7489](https://github.com/n8n-io/n8n/issues/7489)) ([e6d3d1a](https://github.com/n8n-io/n8n/commit/e6d3d1a4c2dd6a860e935df4b0ce3f13e23030c7)) +* **NocoDB Node:** Add new data apis and workspace support ([#7329](https://github.com/n8n-io/n8n/issues/7329)) ([da2d2a8](https://github.com/n8n-io/n8n/commit/da2d2a83bbfb05db3a10aef99bfde3ccaf160d60)) + + + +## [1.15.1](https://github.com/n8n-io/n8n/compare/n8n@1.14.0...n8n@1.15.1) (2023-11-02) + + +### Bug Fixes + +* **core:** Ensure execution deletion in worker lifecycle hook ([#7481](https://github.com/n8n-io/n8n/issues/7481)) ([742c8a8](https://github.com/n8n-io/n8n/commit/742c8a8534098522fe103fad09fa95f70c460b3d)) +* **core:** Fix data encryption on credentials import ([#7560](https://github.com/n8n-io/n8n/issues/7560)) ([b350568](https://github.com/n8n-io/n8n/commit/b350568505d48ec880fe98d2b62ef090d5399c5f)) +* **core:** Fix issue that prevents owner logging in when using ldap ([#7408](https://github.com/n8n-io/n8n/issues/7408)) ([479f902](https://github.com/n8n-io/n8n/commit/479f90231d0a03c69b17189384812b5a1d81ef3d)) +* **core:** Handle missing resultData in runData ([#7523](https://github.com/n8n-io/n8n/issues/7523)) ([1055bd3](https://github.com/n8n-io/n8n/commit/1055bd3762b90b013a300bd87e3fa902e902cb9e)) +* **core:** Permission check for subworkflow properly checking for workflow settings ([#7576](https://github.com/n8n-io/n8n/issues/7576)) ([437c95e](https://github.com/n8n-io/n8n/commit/437c95e84e144cc77f2866a74d6b670c415895cd)) +* **core:** Prevent executions from becoming forever running ([#7569](https://github.com/n8n-io/n8n/issues/7569)) ([9bdb85c](https://github.com/n8n-io/n8n/commit/9bdb85c4ced96fde75435e334dc757d6c7679926)) +* **core:** Upgrade crypto-js to address CVE-2023-46233 ([#7519](https://github.com/n8n-io/n8n/issues/7519)) ([65e5593](https://github.com/n8n-io/n8n/commit/65e559323371e8235b92e2134d9908d69043fac4)) +* **editor:** Do not truncate form inputs ([#7528](https://github.com/n8n-io/n8n/issues/7528)) ([ae616f1](https://github.com/n8n-io/n8n/commit/ae616f146bc2ce8d37f8cf5116c6c4c8682a91a6)) +* **editor:** Fix NDV close after using input select ([#7544](https://github.com/n8n-io/n8n/issues/7544)) ([3b5e181](https://github.com/n8n-io/n8n/commit/3b5e181e66f8d7e3860e3078dae7cbb20e92551a)) +* **editor:** Fix NDV unexpected re-render ([#7532](https://github.com/n8n-io/n8n/issues/7532)) ([2853fcf](https://github.com/n8n-io/n8n/commit/2853fcff735fd0b98c19c1192349ef2c659d2493)) +* **editor:** Fix route component caching, incorrect use of array reduce method and enable WF history feature ([#7434](https://github.com/n8n-io/n8n/issues/7434)) ([12a89e6](https://github.com/n8n-io/n8n/commit/12a89e6d1441f81380d5e477274a5e2d3eb29f2d)) +* **editor:** Fixes the issue that Switch Node can not be created ([#7516](https://github.com/n8n-io/n8n/issues/7516)) ([df89685](https://github.com/n8n-io/n8n/commit/df89685e1548219f4c06614287abafbc96697817)) +* **editor:** Handle `localStorage` being blocked/unavailable ([#7348](https://github.com/n8n-io/n8n/issues/7348)) ([c05bc67](https://github.com/n8n-io/n8n/commit/c05bc6728d3227af4931ddcda5ed8bc6a3539dd0)) +* Fix dark mode small issues ([#7573](https://github.com/n8n-io/n8n/issues/7573)) ([1d81afc](https://github.com/n8n-io/n8n/commit/1d81afcbdf17166f3ebf468673e3ba348ae7fecb)) +* **Jira Software Node:** Handle missing issue types in issue types loader ([#7534](https://github.com/n8n-io/n8n/issues/7534)) ([9762705](https://github.com/n8n-io/n8n/commit/9762705833c809fd2781de179279a15c1be988eb)) +* **Switch Node:** Allow sortable Switch rules ([#7555](https://github.com/n8n-io/n8n/issues/7555)) ([7a56e58](https://github.com/n8n-io/n8n/commit/7a56e58a608132ef795d8c5cdaccb8caa49c0e8f)) + + +### Features + +* **core:** Add optional Error-Output ([#7460](https://github.com/n8n-io/n8n/issues/7460)) ([655efea](https://github.com/n8n-io/n8n/commit/655efeaf669e9722895b66fef47f000507459210)) +* **core:** Make queue mode settings configurable ([#7526](https://github.com/n8n-io/n8n/issues/7526)) ([3d95b24](https://github.com/n8n-io/n8n/commit/3d95b243e935e4eba97a418d05fa687169ab7d07)) +* **core:** Set up leader selection for multiple main instances ([#7527](https://github.com/n8n-io/n8n/issues/7527)) ([442c73e](https://github.com/n8n-io/n8n/commit/442c73e63bb54f50657a511d88912a80cab64c7f)) +* **editor:** Implement the `UserStack` design system component ([#7559](https://github.com/n8n-io/n8n/issues/7559)) ([ce14f62](https://github.com/n8n-io/n8n/commit/ce14f6266b30caadb477b08d4257b82c769a74c3)) +* **HTTP Request Node:** Add pagination support ([#5993](https://github.com/n8n-io/n8n/issues/5993)) ([cc2bd2e](https://github.com/n8n-io/n8n/commit/cc2bd2e19c8b75320b236de215d389220fbe24ae)) +* **HTTP Request Node:** Update icon and default color ([#7572](https://github.com/n8n-io/n8n/issues/7572)) ([ff279ab](https://github.com/n8n-io/n8n/commit/ff279ab4112435c341b84081d68b976ff03bf261)) +* **n8n Form Trigger Node:** Add text area and password input types ([#7474](https://github.com/n8n-io/n8n/issues/7474)) ([b72040a](https://github.com/n8n-io/n8n/commit/b72040aa5423aa6cb16dea2e7c6ea6439376b653)) +* **editor:** Dark mode is here! You can change it under personal settings.([#6980](https://github.com/n8n-io/n8n/pull/6980)) ([0746783](https://github.com/n8n-io/n8n/commit/0746783e027ebe6715588a68db399a34e0211a96)) + + + +# [1.15.0](https://github.com/n8n-io/n8n/compare/n8n@1.14.0...n8n@1.15.0) (2023-11-02) + + +### Bug Fixes + +* **core:** Ensure execution deletion in worker lifecycle hook ([#7481](https://github.com/n8n-io/n8n/issues/7481)) ([742c8a8](https://github.com/n8n-io/n8n/commit/742c8a8534098522fe103fad09fa95f70c460b3d)) +* **core:** Fix data encryption on credentials import ([#7560](https://github.com/n8n-io/n8n/issues/7560)) ([b350568](https://github.com/n8n-io/n8n/commit/b350568505d48ec880fe98d2b62ef090d5399c5f)) +* **core:** Fix issue that prevents owner logging in when using ldap ([#7408](https://github.com/n8n-io/n8n/issues/7408)) ([479f902](https://github.com/n8n-io/n8n/commit/479f90231d0a03c69b17189384812b5a1d81ef3d)) +* **core:** Handle missing resultData in runData ([#7523](https://github.com/n8n-io/n8n/issues/7523)) ([1055bd3](https://github.com/n8n-io/n8n/commit/1055bd3762b90b013a300bd87e3fa902e902cb9e)) +* **core:** Permission check for subworkflow properly checking for workflow settings ([#7576](https://github.com/n8n-io/n8n/issues/7576)) ([437c95e](https://github.com/n8n-io/n8n/commit/437c95e84e144cc77f2866a74d6b670c415895cd)) +* **core:** Prevent executions from becoming forever running ([#7569](https://github.com/n8n-io/n8n/issues/7569)) ([9bdb85c](https://github.com/n8n-io/n8n/commit/9bdb85c4ced96fde75435e334dc757d6c7679926)) +* **core:** Upgrade crypto-js to address CVE-2023-46233 ([#7519](https://github.com/n8n-io/n8n/issues/7519)) ([65e5593](https://github.com/n8n-io/n8n/commit/65e559323371e8235b92e2134d9908d69043fac4)) +* **editor:** Do not truncate form inputs ([#7528](https://github.com/n8n-io/n8n/issues/7528)) ([ae616f1](https://github.com/n8n-io/n8n/commit/ae616f146bc2ce8d37f8cf5116c6c4c8682a91a6)) +* **editor:** Fix NDV close after using input select ([#7544](https://github.com/n8n-io/n8n/issues/7544)) ([3b5e181](https://github.com/n8n-io/n8n/commit/3b5e181e66f8d7e3860e3078dae7cbb20e92551a)) +* **editor:** Fix NDV unexpected re-render ([#7532](https://github.com/n8n-io/n8n/issues/7532)) ([2853fcf](https://github.com/n8n-io/n8n/commit/2853fcff735fd0b98c19c1192349ef2c659d2493)) +* **editor:** Fix route component caching, incorrect use of array reduce method and enable WF history feature ([#7434](https://github.com/n8n-io/n8n/issues/7434)) ([12a89e6](https://github.com/n8n-io/n8n/commit/12a89e6d1441f81380d5e477274a5e2d3eb29f2d)) +* **editor:** Fixes the issue that Switch Node can not be created ([#7516](https://github.com/n8n-io/n8n/issues/7516)) ([df89685](https://github.com/n8n-io/n8n/commit/df89685e1548219f4c06614287abafbc96697817)) +* **editor:** Handle `localStorage` being blocked/unavailable ([#7348](https://github.com/n8n-io/n8n/issues/7348)) ([c05bc67](https://github.com/n8n-io/n8n/commit/c05bc6728d3227af4931ddcda5ed8bc6a3539dd0)) +* Fix dark mode small issues ([#7573](https://github.com/n8n-io/n8n/issues/7573)) ([1d81afc](https://github.com/n8n-io/n8n/commit/1d81afcbdf17166f3ebf468673e3ba348ae7fecb)) +* **Jira Software Node:** Handle missing issue types in issue types loader ([#7534](https://github.com/n8n-io/n8n/issues/7534)) ([9762705](https://github.com/n8n-io/n8n/commit/9762705833c809fd2781de179279a15c1be988eb)) +* **Switch Node:** Allow sortable Switch rules ([#7555](https://github.com/n8n-io/n8n/issues/7555)) ([7a56e58](https://github.com/n8n-io/n8n/commit/7a56e58a608132ef795d8c5cdaccb8caa49c0e8f)) + + +### Features + +* **core:** Add optional Error-Output ([#7460](https://github.com/n8n-io/n8n/issues/7460)) ([655efea](https://github.com/n8n-io/n8n/commit/655efeaf669e9722895b66fef47f000507459210)) +* **core:** Make queue mode settings configurable ([#7526](https://github.com/n8n-io/n8n/issues/7526)) ([3d95b24](https://github.com/n8n-io/n8n/commit/3d95b243e935e4eba97a418d05fa687169ab7d07)) +* **core:** Set up leader selection for multiple main instances ([#7527](https://github.com/n8n-io/n8n/issues/7527)) ([442c73e](https://github.com/n8n-io/n8n/commit/442c73e63bb54f50657a511d88912a80cab64c7f)) +* **editor:** Implement the `UserStack` design system component ([#7559](https://github.com/n8n-io/n8n/issues/7559)) ([ce14f62](https://github.com/n8n-io/n8n/commit/ce14f6266b30caadb477b08d4257b82c769a74c3)) +* **HTTP Request Node:** Add pagination support ([#5993](https://github.com/n8n-io/n8n/issues/5993)) ([cc2bd2e](https://github.com/n8n-io/n8n/commit/cc2bd2e19c8b75320b236de215d389220fbe24ae)) +* **HTTP Request Node:** Update icon and default color ([#7572](https://github.com/n8n-io/n8n/issues/7572)) ([ff279ab](https://github.com/n8n-io/n8n/commit/ff279ab4112435c341b84081d68b976ff03bf261)) +* **n8n Form Trigger Node:** Add text area and password input types ([#7474](https://github.com/n8n-io/n8n/issues/7474)) ([b72040a](https://github.com/n8n-io/n8n/commit/b72040aa5423aa6cb16dea2e7c6ea6439376b653)) +* * **editor:** Dark mode is here! You can change it under personal settings.([#6980](https://github.com/n8n-io/n8n/pull/6980)) ([0746783](https://github.com/n8n-io/n8n/commit/0746783e027ebe6715588a68db399a34e0211a96)) + + + # [1.14.0](https://github.com/n8n-io/n8n/compare/n8n@1.13.0...n8n@1.14.0) (2023-10-25) diff --git a/CHECKLIST.yml b/CHECKLIST.yml deleted file mode 100644 index a1c3fa8407..0000000000 --- a/CHECKLIST.yml +++ /dev/null @@ -1,50 +0,0 @@ -paths: - 'packages/**': - - If fixing bug, added test to cover scenario. - - If addressing forum or Github issue, added link to description. - 'packages/**/*.ts': - - Added unit tests to cover new or updated functionality. - '**/*.vue': - - Used composition API for all new components. - - Added component or unit tests to cover functionality. - - # cli - 'packages/cli/src/databases/migrations/**': - - Requested review from at least two engineers on migration. - - Avoided irreversible data migrations. - - Avoided deleting or updating data keys. - - Wrote 'down' migration if possible. - 'n8n/packages/cli/src/api/**': - - Added integration tests for new endpoints. - - # editor ui - 'packages/editor-ui/**/*.vue': - - Added E2E if adding new features. - - Used design system tokens (colors, spacings...) where possible. - 'packages/editor-ui/src/mixins/restApi.ts': - - Avoided adding new methods. Only deleted from here. - 'packages/editor-ui/src/mixins/**': - - Avoided adding new mixins (use composables instead). Only removed code from here. - 'packages/editor-ui/src/views/NodeView.vue': - - Avoided adding code here. Only refactored to make it smaller. - 'packages/editor-ui/src/hooks/**': - - Avoided adding new hooks. Only refactored to move hooks to relevant store instead. - - # nodes-base - 'packages/nodes-base/nodes/**': - - Added workflow tests for nodes if possible. - 'packages/nodes-base/package.json': - - Avoided adding dependencies for nodes if not absolutely necessary. - - # design-system - 'packages/design-system/**/*.vue': - - Used design system tokens (colors, spacings...) where possible. - - Updated Storybook with new component or updated functionality. - - # e2e - 'cypress/e2e/**': - - Avoided chaining commands more than two or three times (to avoid flakiness because only last one will be retried). - - Spoofed endpoints that are not critical for the test (to avoid flakiness). - - Picked most efficient path to start the test (for example skipped account setup and starting at /workflow/new for a canvas test). - - Avoided adding waits on time (use request intercepts instead). - - Ensured each spec does not depend on any another spec to pass. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 330fb3096a..f24240b850 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -5,24 +5,27 @@ Great that you are here and you want to contribute to n8n ## Contents - [Contributing to n8n](#contributing-to-n8n) - - [Contents](#contents) - - [Code of conduct](#code-of-conduct) - - [Directory structure](#directory-structure) - - [Development setup](#development-setup) - - [Requirements](#requirements) - - [Node.js](#nodejs) - - [pnpm](#pnpm) - - [pnpm workspaces](#pnpm-workspaces) - - [corepack](#corepack) - - [Build tools](#build-tools) - - [Actual n8n setup](#actual-n8n-setup) - - [Start](#start) - - [Development cycle](#development-cycle) - - [Test suite](#test-suite) - - [Releasing](#releasing) - - [Create custom nodes](#create-custom-nodes) - - [Extend documentation](#extend-documentation) - - [Contributor License Agreement](#contributor-license-agreement) + - [Contents](#contents) + - [Code of conduct](#code-of-conduct) + - [Directory structure](#directory-structure) + - [Development setup](#development-setup) + - [Requirements](#requirements) + - [Node.js](#nodejs) + - [pnpm](#pnpm) + - [pnpm workspaces](#pnpm-workspaces) + - [corepack](#corepack) + - [Build tools](#build-tools) + - [Actual n8n setup](#actual-n8n-setup) + - [Start](#start) + - [Development cycle](#development-cycle) + - [Test suite](#test-suite) + - [Unit tests](#unit-tests) + - [E2E tests](#e2e-tests) + - [Releasing](#releasing) + - [Create custom nodes](#create-custom-nodes) + - [Extend documentation](#extend-documentation) + - [Contribute workflow templates](#contribute-workflow-templates) + - [Contributor License Agreement](#contributor-license-agreement) ## Code of conduct @@ -186,7 +189,9 @@ automatically build your code, restart the backend and refresh the frontend ### Test suite -The tests can be started via: +#### Unit tests + +Unit tests can be started via: ``` pnpm test @@ -196,6 +201,16 @@ If that gets executed in one of the package folders it will only run the tests of this package. If it gets executed in the n8n-root folder it will run all tests of all packages. +#### E2E tests + +E2E tests can be started via one of the following commands: + +- `pnpm test:e2e:ui`: Start n8n and run e2e tests interactively using built UI code. Does not react to code changes (i.e. runs `pnpm start` and `cypress open`) +- `pnpm test:e2e:dev`: Start n8n in development mode and run e2e tests interactively. Reacts to code changes (i.e. runs `pnpm dev` and `cypress open`) +- `pnpm test:e2e:all`: Start n8n and run e2e tests headless (i.e. runs `pnpm start` and `cypress run --headless`) + +⚠️ Remember to stop your dev server before. Otherwise port binding will fail. + ## Releasing To start a release, trigger [this workflow](https://github.com/n8n-io/n8n/actions/workflows/release-create-pr.yml) with the SemVer release type, and select a branch to cut this release from. This workflow will then: @@ -220,6 +235,14 @@ Learn about [building nodes](https://docs.n8n.io/integrations/creating-nodes/) t The repository for the n8n documentation on [docs.n8n.io](https://docs.n8n.io) can be found [here](https://github.com/n8n-io/n8n-docs). +## Contribute workflow templates + +You can submit your workflows to n8n's template library. + +n8n is working on a creator program, and developing a marketplace of templates. This is an ongoing project, and details are likely to change. + +Refer to [n8n Creator hub](https://www.notion.so/n8n/n8n-Creator-hub-7bd2cbe0fce0449198ecb23ff4a2f76f) for information on how to submit templates and become a creator. + ## Contributor License Agreement That we do not have any potential problems later it is sadly necessary to sign a [Contributor License Agreement](CONTRIBUTOR_LICENSE_AGREEMENT.md). That can be done literally with the push of a button. diff --git a/README.md b/README.md index 85a7058a77..145ecab8c6 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ # n8n - Workflow automation tool -n8n is an extendable workflow automation tool. With a [fair-code](http://faircode.io) distribution model, n8n +n8n is an extendable workflow automation tool. With a [fair-code](https://faircode.io) distribution model, n8n will always have visible source code, be available to self-host, and allow you to add your own custom functions, logic and apps. n8n's node-based approach makes it highly versatile, enabling you to connect anything to everything. @@ -94,7 +94,7 @@ development environment ready in minutes. ## License -n8n is [fair-code](http://faircode.io) distributed under the +n8n is [fair-code](https://faircode.io) distributed under the [**Sustainable Use License**](https://github.com/n8n-io/n8n/blob/master/packages/cli/LICENSE.md) and the [**n8n Enterprise License**](https://github.com/n8n-io/n8n/blob/master/packages/cli/LICENSE_EE.md). diff --git a/cypress.config.js b/cypress.config.js index cdcae02e65..f01672c6f9 100644 --- a/cypress.config.js +++ b/cypress.config.js @@ -19,4 +19,9 @@ module.exports = defineConfig({ experimentalInteractiveRunEvents: true, experimentalSessionAndOrigin: true, }, + env: { + MAX_PINNED_DATA_SIZE: process.env.VUE_APP_MAX_PINNED_DATA_SIZE + ? parseInt(process.env.VUE_APP_MAX_PINNED_DATA_SIZE, 10) + : 16 * 1024, + }, }); diff --git a/cypress/composables/featureFlags.ts b/cypress/composables/featureFlags.ts new file mode 100644 index 0000000000..ea8e95064e --- /dev/null +++ b/cypress/composables/featureFlags.ts @@ -0,0 +1,12 @@ +export const overrideFeatureFlag = (name: string, value: boolean | string) => { + cy.window().then((win) => { + // If feature flags hasn't been initialized yet, we store the override + // in local storage and it gets loaded when the feature flags are + // initialized. + win.localStorage.setItem('N8N_EXPERIMENT_OVERRIDES', JSON.stringify({ [name]: value })); + + if (win.featureFlags) { + win.featureFlags.override(name, value); + } + }); +}; diff --git a/cypress/composables/modals/chat-modal.ts b/cypress/composables/modals/chat-modal.ts new file mode 100644 index 0000000000..268419f3c8 --- /dev/null +++ b/cypress/composables/modals/chat-modal.ts @@ -0,0 +1,48 @@ +/** + * Getters + */ + +export function getManualChatModal() { + return cy.getByTestId('lmChat-modal'); +} + +export function getManualChatInput() { + return cy.getByTestId('workflow-chat-input'); +} + +export function getManualChatSendButton() { + return getManualChatModal().getByTestId('workflow-chat-send-button'); +} + +export function getManualChatMessages() { + return getManualChatModal().get('.messages .message'); +} + +export function getManualChatModalCloseButton() { + return getManualChatModal().get('.el-dialog__close'); +} + +export function getManualChatModalLogs() { + return getManualChatModal().getByTestId('lm-chat-logs'); +} + +export function getManualChatModalLogsTree() { + return getManualChatModalLogs().getByTestId('lm-chat-logs-tree'); +} + +export function getManualChatModalLogsEntries() { + return getManualChatModalLogs().getByTestId('lm-chat-logs-entry'); +} + +/** + * Actions + */ + +export function sendManualChatMessage(message: string) { + getManualChatInput().type(message); + getManualChatSendButton().click(); +} + +export function closeManualChatModal() { + getManualChatModalCloseButton().click(); +} diff --git a/cypress/composables/modals/credential-modal.ts b/cypress/composables/modals/credential-modal.ts new file mode 100644 index 0000000000..bfcbf89251 --- /dev/null +++ b/cypress/composables/modals/credential-modal.ts @@ -0,0 +1,54 @@ +/** + * Getters + */ + +export function getCredentialConnectionParameterInputs() { + return cy.getByTestId('credential-connection-parameter'); +} + +export function getCredentialConnectionParameterInputByName(name: string) { + return cy.getByTestId(`parameter-input-${name}`); +} + +export function getEditCredentialModal() { + return cy.getByTestId('editCredential-modal', { timeout: 5000 }); +} + +export function getCredentialSaveButton() { + return cy.getByTestId('credential-save-button', { timeout: 5000 }); +} + +export function getCredentialDeleteButton() { + return cy.getByTestId('credential-delete-button'); +} + +export function getCredentialModalCloseButton() { + return getEditCredentialModal().find('.el-dialog__close').first(); +} + +/** + * Actions + */ + +export function setCredentialConnectionParameterInputByName(name: string, value: string) { + getCredentialConnectionParameterInputByName(name).type(value); +} + +export function saveCredential() { + getCredentialSaveButton().click({ force: true }); +} + +export function closeCredentialModal() { + getCredentialModalCloseButton().click(); +} + +export function setCredentialValues(values: Record, save = true) { + Object.entries(values).forEach(([key, value]) => { + setCredentialConnectionParameterInputByName(key, value); + }); + + if (save) { + saveCredential(); + closeCredentialModal(); + } +} diff --git a/cypress/composables/modals/workflow-credential-setup-modal.ts b/cypress/composables/modals/workflow-credential-setup-modal.ts new file mode 100644 index 0000000000..88bbd13348 --- /dev/null +++ b/cypress/composables/modals/workflow-credential-setup-modal.ts @@ -0,0 +1,13 @@ +/** + * Getters + */ + +export const getWorkflowCredentialsModal = () => cy.getByTestId('setup-workflow-credentials-modal'); + +export const getContinueButton = () => cy.getByTestId('continue-button'); + +/** + * Actions + */ + +export const closeModalFromContinueButton = () => getContinueButton().click(); diff --git a/cypress/composables/ndv.ts b/cypress/composables/ndv.ts new file mode 100644 index 0000000000..02edb29d55 --- /dev/null +++ b/cypress/composables/ndv.ts @@ -0,0 +1,73 @@ +/** + * Getters + */ + +export function getCredentialSelect(eq = 0) { + return cy.getByTestId('node-credentials-select').eq(eq); +} + +export function getCreateNewCredentialOption() { + return cy.getByTestId('node-credentials-select-item-new'); +} + +export function getBackToCanvasButton() { + return cy.getByTestId('back-to-canvas'); +} + +export function getExecuteNodeButton() { + return cy.getByTestId('node-execute-button'); +} + +export function getParameterInputByName(name: string) { + return cy.getByTestId(`parameter-input-${name}`); +} + +export function getInputPanel() { + return cy.getByTestId('input-panel'); +} + +export function getMainPanel() { + return cy.getByTestId('node-parameters'); +} + +export function getOutputPanel() { + return cy.getByTestId('output-panel'); +} + +export function getOutputPanelDataContainer() { + return getOutputPanel().getByTestId('ndv-data-container'); +} + +export function getOutputPanelTable() { + return getOutputPanelDataContainer().get('table'); +} + +/** + * Actions + */ + +export function openCredentialSelect(eq = 0) { + getCredentialSelect(eq).click(); +} + +export function setCredentialByName(name: string) { + openCredentialSelect(); + getCredentialSelect().contains(name).click(); +} + +export function clickCreateNewCredential() { + openCredentialSelect(); + getCreateNewCredentialOption().click(); +} + +export function clickGetBackToCanvas() { + getBackToCanvasButton().click(); +} + +export function clickExecuteNode() { + getExecuteNodeButton().click(); +} + +export function setParameterInputByName(name: string, value: string) { + getParameterInputByName(name).clear().type(value); +} diff --git a/cypress/composables/setup-template-form-step.ts b/cypress/composables/setup-template-form-step.ts new file mode 100644 index 0000000000..6f01662783 --- /dev/null +++ b/cypress/composables/setup-template-form-step.ts @@ -0,0 +1,14 @@ +/** + * Getters + */ + +export const getFormStep = () => cy.getByTestId('setup-credentials-form-step'); + +export const getStepHeading = ($el: JQuery) => + cy.wrap($el).findChildByTestId('credential-step-heading'); + +export const getStepDescription = ($el: JQuery) => + cy.wrap($el).findChildByTestId('credential-step-description'); + +export const getCreateAppCredentialsButton = (appName: string) => + cy.get(`button:contains("Create new ${appName} credential")`); diff --git a/cypress/composables/setup-workflow-credentials-button.ts b/cypress/composables/setup-workflow-credentials-button.ts new file mode 100644 index 0000000000..6b1b9b69d4 --- /dev/null +++ b/cypress/composables/setup-workflow-credentials-button.ts @@ -0,0 +1,5 @@ +/** + * Getters + */ + +export const getSetupWorkflowCredentialsButton = () => cy.get(`button:contains("Set up template")`); diff --git a/cypress/composables/versions.ts b/cypress/composables/versions.ts new file mode 100644 index 0000000000..f96ea8152f --- /dev/null +++ b/cypress/composables/versions.ts @@ -0,0 +1,32 @@ +/** + * Getters + */ + +export function getVersionUpdatesPanelOpenButton() { + return cy.getByTestId('version-updates-panel-button'); +} + +export function getVersionUpdatesPanel() { + return cy.getByTestId('version-updates-panel'); +} + +export function getVersionUpdatesPanelCloseButton() { + return getVersionUpdatesPanel().get('.el-drawer__close-btn').first(); +} + +export function getVersionCard() { + return cy.getByTestId('version-card'); +} + +/** + * Actions + */ + +export function openVersionUpdatesPanel() { + getVersionUpdatesPanelOpenButton().click(); + getVersionUpdatesPanel().should('be.visible'); +} + +export function closeVersionUpdatesPanel() { + getVersionUpdatesPanelCloseButton().click(); +} diff --git a/cypress/composables/workflow.ts b/cypress/composables/workflow.ts new file mode 100644 index 0000000000..b1810943a3 --- /dev/null +++ b/cypress/composables/workflow.ts @@ -0,0 +1,142 @@ +import { ROUTES } from '../constants'; +import { getManualChatModal } from './modals/chat-modal'; + +/** + * Types + */ + +export type EndpointType = + | 'ai_chain' + | 'ai_document' + | 'ai_embedding' + | 'ai_languageModel' + | 'ai_memory' + | 'ai_outputParser' + | 'ai_tool' + | 'ai_retriever' + | 'ai_textSplitter' + | 'ai_vectorRetriever' + | 'ai_vectorStore'; + +/** + * Getters + */ + +export function getAddInputEndpointByType(nodeName: string, endpointType: EndpointType) { + return cy.get( + `.add-input-endpoint[data-jtk-scope-${endpointType}][data-endpoint-name="${nodeName}"]`, + ); +} + +export function getNodeCreatorItems() { + return cy.getByTestId('item-iterator-item'); +} + +export function getExecuteWorkflowButton() { + return cy.getByTestId('execute-workflow-button'); +} + +export function getManualChatButton() { + return cy.getByTestId('workflow-chat-button'); +} + +export function getNodes() { + return cy.getByTestId('canvas-node'); +} + +export function getNodeByName(name: string) { + return cy.getByTestId('canvas-node').filter(`[data-name="${name}"]`).eq(0); +} + +export function getConnectionBySourceAndTarget(source: string, target: string) { + return cy + .get('.jtk-connector') + .filter(`[data-source-node="${source}"][data-target-node="${target}"]`) + .eq(0); +} + +export function getNodeCreatorSearchBar() { + return cy.getByTestId('node-creator-search-bar'); +} + +export function getNodeCreatorPlusButton() { + return cy.getByTestId('node-creator-plus-button'); +} + +/** + * Actions + */ + +export function addNodeToCanvas( + nodeDisplayName: string, + plusButtonClick = true, + preventNdvClose?: boolean, + action?: string, +) { + if (plusButtonClick) { + getNodeCreatorPlusButton().click(); + } + + getNodeCreatorSearchBar().type(nodeDisplayName); + getNodeCreatorSearchBar().type('{enter}'); + cy.wait(500); + cy.get('body').then((body) => { + if (body.find('[data-test-id=node-creator]').length > 0) { + if (action) { + cy.contains(action).click(); + } else { + // Select the first action + cy.get('[data-keyboard-nav-type="action"]').eq(0).click(); + } + } + }); + + if (!preventNdvClose) cy.get('body').type('{esc}'); +} + +export function navigateToNewWorkflowPage(preventNodeViewUnload = true) { + cy.visit(ROUTES.NEW_WORKFLOW_PAGE); + cy.waitForLoad(); + cy.window().then((win) => { + win.preventNodeViewBeforeUnload = preventNodeViewUnload; + }); +} + +export function addSupplementalNodeToParent( + nodeName: string, + endpointType: EndpointType, + parentNodeName: string, +) { + getAddInputEndpointByType(parentNodeName, endpointType).click({ force: true }); + getNodeCreatorItems().contains(nodeName).click(); + getConnectionBySourceAndTarget(parentNodeName, nodeName).should('exist'); +} + +export function addLanguageModelNodeToParent(nodeName: string, parentNodeName: string) { + addSupplementalNodeToParent(nodeName, 'ai_languageModel', parentNodeName); +} + +export function addMemoryNodeToParent(nodeName: string, parentNodeName: string) { + addSupplementalNodeToParent(nodeName, 'ai_memory', parentNodeName); +} + +export function addToolNodeToParent(nodeName: string, parentNodeName: string) { + addSupplementalNodeToParent(nodeName, 'ai_tool', parentNodeName); +} + +export function addOutputParserNodeToParent(nodeName: string, parentNodeName: string) { + addSupplementalNodeToParent(nodeName, 'ai_outputParser', parentNodeName); +} + +export function clickExecuteWorkflowButton() { + getExecuteWorkflowButton().click(); +} + +export function clickManualChatButton() { + getManualChatButton().click(); + getManualChatModal().should('be.visible'); +} + +export function openNode(nodeName: string) { + getNodeByName(nodeName).dblclick(); +} diff --git a/cypress/constants.ts b/cypress/constants.ts index 352dbb36c3..d37dd37574 100644 --- a/cypress/constants.ts +++ b/cypress/constants.ts @@ -12,6 +12,13 @@ export const INSTANCE_OWNER = { lastName: randLastName(), }; +export const INSTANCE_ADMIN = { + email: 'admin@n8n.io', + password: DEFAULT_USER_PASSWORD, + firstName: randFirstName(), + lastName: randLastName(), +}; + export const INSTANCE_MEMBERS = [ { email: 'rebecca@n8n.io', @@ -28,12 +35,13 @@ export const INSTANCE_MEMBERS = [ ]; export const MANUAL_TRIGGER_NODE_NAME = 'Manual Trigger'; -export const MANUAL_TRIGGER_NODE_DISPLAY_NAME = 'When clicking "Execute Workflow"'; +export const MANUAL_TRIGGER_NODE_DISPLAY_NAME = 'When clicking "Test Workflow"'; +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'; export const EDIT_FIELDS_SET_NODE_NAME = 'Edit Fields'; -export const IF_NODE_NAME = 'IF'; +export const IF_NODE_NAME = 'If'; export const MERGE_NODE_NAME = 'Merge'; export const SWITCH_NODE_NAME = 'Switch'; export const GMAIL_NODE_NAME = 'Gmail'; @@ -41,6 +49,14 @@ export const TRELLO_NODE_NAME = 'Trello'; export const NOTION_NODE_NAME = 'Notion'; export const PIPEDRIVE_NODE_NAME = 'Pipedrive'; export const HTTP_REQUEST_NODE_NAME = 'HTTP Request'; +export const AGENT_NODE_NAME = 'AI Agent'; +export const BASIC_LLM_CHAIN_NODE_NAME = 'Basic LLM Chain'; +export const AI_MEMORY_WINDOW_BUFFER_MEMORY_NODE_NAME = 'Window Buffer Memory'; +export const AI_TOOL_CALCULATOR_NODE_NAME = 'Calculator'; +export const AI_TOOL_CODE_NODE_NAME = 'Custom Code Tool'; +export const AI_TOOL_WIKIPEDIA_NODE_NAME = 'Wikipedia'; +export const AI_LANGUAGE_MODEL_OPENAI_CHAT_MODEL_NODE_NAME = 'OpenAI Chat Model'; +export const AI_OUTPUT_PARSER_AUTO_FIXING_NODE_NAME = 'Auto-fixing Output Parser'; export const META_KEY = Cypress.platform === 'darwin' ? '{meta}' : '{ctrl}'; @@ -48,3 +64,7 @@ export const NEW_GOOGLE_ACCOUNT_NAME = 'Gmail account'; export const NEW_TRELLO_ACCOUNT_NAME = 'Trello account'; export const NEW_NOTION_ACCOUNT_NAME = 'Notion account'; export const NEW_QUERY_AUTH_ACCOUNT_NAME = 'Query Auth account'; + +export const ROUTES = { + NEW_WORKFLOW_PAGE: '/workflow/new', +}; diff --git a/cypress/e2e/10-undo-redo.cy.ts b/cypress/e2e/10-undo-redo.cy.ts index d986fe6577..4182c75507 100644 --- a/cypress/e2e/10-undo-redo.cy.ts +++ b/cypress/e2e/10-undo-redo.cy.ts @@ -1,12 +1,14 @@ import { CODE_NODE_NAME, SET_NODE_NAME, EDIT_FIELDS_SET_NODE_NAME } from './../constants'; import { SCHEDULE_TRIGGER_NODE_NAME } from '../constants'; import { WorkflowPage as WorkflowPageClass } from '../pages/workflow'; +import { MessageBox as MessageBoxClass } from '../pages/modals/message-box'; import { NDV } from '../pages/ndv'; // Suite-specific constants const CODE_NODE_NEW_NAME = 'Something else'; const WorkflowPage = new WorkflowPageClass(); +const messageBox = new MessageBoxClass(); const ndv = new NDV(); describe('Undo/Redo', () => { @@ -44,7 +46,7 @@ describe('Undo/Redo', () => { WorkflowPage.getters .canvasNodeByName('Code') .should('have.css', 'left', '860px') - .should('have.css', 'top', '220px') + .should('have.css', 'top', '220px'); WorkflowPage.actions.hitUndo(); WorkflowPage.getters.canvasNodes().should('have.have.length', 2); @@ -62,16 +64,13 @@ describe('Undo/Redo', () => { WorkflowPage.getters .canvasNodeByName('Code') .should('have.css', 'left', '860px') - .should('have.css', 'top', '220px') + .should('have.css', 'top', '220px'); }); - it('should undo/redo deleting node using delete button', () => { + it('should undo/redo deleting node using context menu', () => { WorkflowPage.actions.addNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME); WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME); - WorkflowPage.getters - .canvasNodeByName(CODE_NODE_NAME) - .find('[data-test-id=delete-node-button]') - .click({ force: true }); + WorkflowPage.actions.deleteNodeFromContextMenu(CODE_NODE_NAME); WorkflowPage.getters.canvasNodes().should('have.have.length', 1); WorkflowPage.getters.nodeConnections().should('have.length', 0); WorkflowPage.actions.hitUndo(); @@ -137,21 +136,21 @@ describe('Undo/Redo', () => { WorkflowPage.getters .canvasNodeByName('Code') .should('have.css', 'left', '740px') - .should('have.css', 'top', '320px') + .should('have.css', 'top', '320px'); WorkflowPage.actions.hitUndo(); WorkflowPage.getters .canvasNodeByName('Code') .should('have.css', 'left', '640px') - .should('have.css', 'top', '220px') + .should('have.css', 'top', '220px'); WorkflowPage.actions.hitRedo(); WorkflowPage.getters .canvasNodeByName('Code') .should('have.css', 'left', '740px') - .should('have.css', 'top', '320px') + .should('have.css', 'top', '320px'); }); - it('should undo/redo deleting a connection by pressing delete button', () => { + it('should undo/redo deleting a connection using context menu', () => { WorkflowPage.actions.addNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME); WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME); WorkflowPage.getters.nodeConnections().realHover(); @@ -177,14 +176,10 @@ describe('Undo/Redo', () => { WorkflowPage.getters.nodeConnections().should('have.length', 0); }); - it('should undo/redo disabling a node using disable button', () => { + it('should undo/redo disabling a node using context menu', () => { WorkflowPage.actions.addNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME); WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME); - WorkflowPage.getters - .canvasNodes() - .last() - .find('[data-test-id="disable-node-button"]') - .click({ force: true }); + WorkflowPage.actions.disableNode(CODE_NODE_NAME); WorkflowPage.getters.disabledNodes().should('have.length', 1); WorkflowPage.actions.hitUndo(); WorkflowPage.getters.disabledNodes().should('have.length', 0); @@ -252,11 +247,7 @@ describe('Undo/Redo', () => { it('should undo/redo duplicating a node', () => { WorkflowPage.actions.addNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME); WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME); - WorkflowPage.getters - .canvasNodes() - .last() - .find('[data-test-id="duplicate-node-button"]') - .click({ force: true }); + WorkflowPage.actions.duplicateNode(CODE_NODE_NAME); WorkflowPage.actions.hitUndo(); WorkflowPage.getters.canvasNodes().should('have.length', 2); WorkflowPage.actions.hitRedo(); @@ -276,9 +267,6 @@ describe('Undo/Redo', () => { }); it('should undo/redo multiple steps', () => { - const initialPosition = {left: '420px', top: '220px'}; - const movedPosition = {left: '540px', top: '360px'}; - WorkflowPage.actions.addNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME); WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME); // WorkflowPage.actions.addNodeToCanvas(SET_NODE_NAME); @@ -289,48 +277,115 @@ describe('Undo/Redo', () => { // Disable last node WorkflowPage.getters.canvasNodes().last().click(); WorkflowPage.actions.hitDisableNodeShortcut(); + // Move first one - WorkflowPage.getters.canvasNodes() - .first() - .should('have.css', 'left', initialPosition.left) - .should('have.css', 'top', initialPosition.top) + WorkflowPage.actions + .getNodePosition(WorkflowPage.getters.canvasNodes().first()) + .then((initialPosition) => { + WorkflowPage.getters.canvasNodes().first().click(); + cy.drag('[data-test-id="canvas-node"].jtk-drag-selected', [50, 150], { + clickToFinish: true, + }); + WorkflowPage.getters + .canvasNodes() + .first() + .should('have.css', 'left', `${initialPosition.left + 120}px`) + .should('have.css', 'top', `${initialPosition.top + 140}px`); - WorkflowPage.getters.canvasNodes().first().click(); - cy.drag('[data-test-id="canvas-node"].jtk-drag-selected', [50, 150], { clickToFinish: true }); - WorkflowPage.getters.canvasNodes() - .first() - .should('have.css', 'left', movedPosition.left) - .should('have.css', 'top', movedPosition.top) - // Delete the set node - WorkflowPage.getters.canvasNodeByName(EDIT_FIELDS_SET_NODE_NAME).click().click(); - cy.get('body').type('{backspace}'); + // Delete the set node + WorkflowPage.getters.canvasNodeByName(EDIT_FIELDS_SET_NODE_NAME).click().click(); + cy.get('body').type('{backspace}'); - // First undo: Should return deleted node - WorkflowPage.actions.hitUndo(); + // First undo: Should return deleted node + WorkflowPage.actions.hitUndo(); + WorkflowPage.getters.canvasNodes().should('have.length', 4); + WorkflowPage.getters.nodeConnections().should('have.length', 3); + // Second undo: Should move first node to it's original position + WorkflowPage.actions.hitUndo(); + WorkflowPage.getters + .canvasNodes() + .first() + .should('have.css', 'left', `${initialPosition.left}px`) + .should('have.css', 'top', `${initialPosition.top}px`); + // Third undo: Should enable last node + WorkflowPage.actions.hitUndo(); + WorkflowPage.getters.disabledNodes().should('have.length', 0); + + // First redo: Should disable last node + WorkflowPage.actions.hitRedo(); + WorkflowPage.getters.disabledNodes().should('have.length', 1); + // Second redo: Should move the first node + WorkflowPage.actions.hitRedo(); + WorkflowPage.getters + .canvasNodes() + .first() + .should('have.css', 'left', `${initialPosition.left + 120}px`) + .should('have.css', 'top', `${initialPosition.top + 140}px`); + // Third redo: Should delete the Set node + WorkflowPage.actions.hitRedo(); + WorkflowPage.getters.canvasNodes().should('have.length', 3); + WorkflowPage.getters.nodeConnections().should('have.length', 2); + }); + }); + + it('should be able to copy and paste pinned data nodes in workflows with dynamic Switch node', () => { + cy.fixture('Test_workflow_form_switch.json').then((data) => { + cy.get('body').paste(JSON.stringify(data)); + }); + WorkflowPage.actions.zoomToFit(); + + WorkflowPage.getters.canvasNodes().should('have.length', 2); + WorkflowPage.getters.nodeConnections().should('have.length', 1); + cy.get(WorkflowPage.getters.getEndpointSelector('input', 'Switch')).should('have.length', 1); + cy.get(WorkflowPage.getters.getEndpointSelector('input', 'Switch')) + .should('have.css', 'left', `637px`) + .should('have.css', 'top', `501px`); + + cy.fixture('Test_workflow_form_switch.json').then((data) => { + cy.get('body').paste(JSON.stringify(data)); + }); WorkflowPage.getters.canvasNodes().should('have.length', 4); - WorkflowPage.getters.nodeConnections().should('have.length', 3); - // Second undo: Should move first node to it's original position - WorkflowPage.actions.hitUndo(); - WorkflowPage.getters.canvasNodes() - .first() - .should('have.css', 'left', initialPosition.left) - .should('have.css', 'top', initialPosition.top) - // Third undo: Should enable last node - WorkflowPage.actions.hitUndo(); - WorkflowPage.getters.disabledNodes().should('have.length', 0); - - // First redo: Should disable last node - WorkflowPage.actions.hitRedo(); - WorkflowPage.getters.disabledNodes().should('have.length', 1); - // Second redo: Should move the first node - WorkflowPage.actions.hitRedo(); - WorkflowPage.getters.canvasNodes() - .first() - .should('have.css', 'left', movedPosition.left) - .should('have.css', 'top', movedPosition.top) - // Third redo: Should delete the Set node - WorkflowPage.actions.hitRedo(); - WorkflowPage.getters.canvasNodes().should('have.length', 3); WorkflowPage.getters.nodeConnections().should('have.length', 2); + + WorkflowPage.actions.hitUndo(); + + WorkflowPage.getters.canvasNodes().should('have.length', 2); + WorkflowPage.getters.nodeConnections().should('have.length', 1); + cy.get(WorkflowPage.getters.getEndpointSelector('input', 'Switch')).should('have.length', 1); + cy.get(WorkflowPage.getters.getEndpointSelector('input', 'Switch')) + .should('have.css', 'left', `637px`) + .should('have.css', 'top', `501px`); + }); + + it('should not undo/redo when NDV or a modal is open', () => { + WorkflowPage.actions.addInitialNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME, { keepNdvOpen: true }); + // Try while NDV is open + WorkflowPage.actions.hitUndo(); + WorkflowPage.getters.canvasNodes().should('have.have.length', 1); + ndv.getters.backToCanvas().click(); + // Try while modal is open + cy.getByTestId('menu-item').contains('About n8n').click({ force: true }); + cy.getByTestId('about-modal').should('be.visible'); + WorkflowPage.actions.hitUndo(); + WorkflowPage.getters.canvasNodes().should('have.have.length', 1); + cy.getByTestId('close-about-modal-button').click(); + // Should work now + WorkflowPage.actions.hitUndo(); + WorkflowPage.getters.canvasNodes().should('have.have.length', 0); + }); + + it('should not undo/redo when NDV or a prompt is open', () => { + WorkflowPage.actions.addInitialNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME, { keepNdvOpen: false }); + WorkflowPage.getters.workflowMenu().click(); + WorkflowPage.getters.workflowMenuItemImportFromURLItem().should('be.visible'); + WorkflowPage.getters.workflowMenuItemImportFromURLItem().click(); + // Try while prompt is open + messageBox.getters.header().click(); + WorkflowPage.actions.hitUndo(); + WorkflowPage.getters.canvasNodes().should('have.have.length', 1); + // Close prompt and try again + messageBox.actions.cancel(); + WorkflowPage.actions.hitUndo(); + WorkflowPage.getters.canvasNodes().should('have.have.length', 0); }); }); diff --git a/cypress/e2e/11-inline-expression-editor.cy.ts b/cypress/e2e/11-inline-expression-editor.cy.ts index f7e192e06c..7f1b97b03c 100644 --- a/cypress/e2e/11-inline-expression-editor.cy.ts +++ b/cypress/e2e/11-inline-expression-editor.cy.ts @@ -67,6 +67,6 @@ describe('Inline expression editor', () => { WorkflowPage.getters.inlineExpressionEditorInput().type('{{'); // Resolving $parameter is slow, especially on CI runner WorkflowPage.getters.inlineExpressionEditorInput().type('$parameter["operation"]'); - WorkflowPage.getters.inlineExpressionEditorOutput().contains(/^get$/); + WorkflowPage.getters.inlineExpressionEditorOutput().should('have.text', 'getAll'); }); }); diff --git a/cypress/e2e/12-canvas-actions.cy.ts b/cypress/e2e/12-canvas-actions.cy.ts index c34a918970..91f6b65884 100644 --- a/cypress/e2e/12-canvas-actions.cy.ts +++ b/cypress/e2e/12-canvas-actions.cy.ts @@ -134,7 +134,7 @@ describe('Canvas Actions', () => { .canvasNodes() .last() .should('have.css', 'left', '860px') - .should('have.css', 'top', '220px') + .should('have.css', 'top', '220px'); }); it('should delete connections by pressing the delete button', () => { @@ -163,21 +163,29 @@ describe('Canvas Actions', () => { .find('[data-test-id="execute-node-button"]') .click({ force: true }); WorkflowPage.getters.successToast().should('contain', 'Node executed successfully'); + WorkflowPage.actions.executeNode(CODE_NODE_NAME); + WorkflowPage.getters.successToast().should('contain', 'Node executed successfully'); }); it('should copy selected nodes', () => { WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME); WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME); WorkflowPage.actions.selectAll(); + WorkflowPage.actions.hitCopy(); WorkflowPage.getters.successToast().should('contain', 'Copied!'); + + WorkflowPage.actions.copyNode(CODE_NODE_NAME); + WorkflowPage.getters.successToast().should('contain', 'Copied!'); }); - it('should select all nodes', () => { + it('should select/deselect all nodes', () => { WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME); WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME); WorkflowPage.actions.selectAll(); WorkflowPage.getters.selectedNodes().should('have.length', 2); + WorkflowPage.actions.deselectAll(); + WorkflowPage.getters.selectedNodes().should('have.length', 0); }); it('should select nodes using arrow keys', () => { @@ -205,22 +213,21 @@ describe('Canvas Actions', () => { WorkflowPage.getters .canvasNodes() .last() - .findChildByTestId('disable-node-button').as('disableNodeButton'); - cy.drag('@disableNodeButton', [200, 200]); + .findChildByTestId('execute-node-button') + .as('executeNodeButton'); + cy.drag('@executeNodeButton', [200, 200]); WorkflowPage.actions.testLassoSelection([100, 100], [200, 200]); }); it('should not break lasso selection with multiple clicks on node action buttons', () => { WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME); WorkflowPage.actions.testLassoSelection([100, 100], [200, 200]); - WorkflowPage.getters - .canvasNodes() - .last().as('lastNode'); - cy.get('@lastNode').findChildByTestId('disable-node-button').as('disableNodeButton'); + WorkflowPage.getters.canvasNodes().last().as('lastNode'); + cy.get('@lastNode').findChildByTestId('execute-node-button').as('executeNodeButton'); for (let i = 0; i < 20; i++) { cy.get('@lastNode').realHover(); - cy.get('@disableNodeButton').should('be.visible'); - cy.get('@disableNodeButton').realTouch(); + cy.get('@executeNodeButton').should('be.visible'); + cy.get('@executeNodeButton').realTouch(); cy.getByTestId('execute-workflow-button').realHover(); WorkflowPage.actions.testLassoSelection([100, 100], [200, 200]); } diff --git a/cypress/e2e/12-canvas.cy.ts b/cypress/e2e/12-canvas.cy.ts index 9e2b8abe06..359f0cf851 100644 --- a/cypress/e2e/12-canvas.cy.ts +++ b/cypress/e2e/12-canvas.cy.ts @@ -16,12 +16,13 @@ const NDVDialog = new NDV(); const DEFAULT_ZOOM_FACTOR = 1; const ZOOM_IN_X1_FACTOR = 1.25; // Zoom in factor after one click const ZOOM_IN_X2_FACTOR = 1.5625; // Zoom in factor after two clicks -const ZOOM_OUT_X1_FACTOR = 0.75; -const ZOOM_OUT_X2_FACTOR = 0.5625; +const ZOOM_OUT_X1_FACTOR = 0.8; +const ZOOM_OUT_X2_FACTOR = 0.64; -const PINCH_ZOOM_IN_FACTOR = 1.32; -const PINCH_ZOOM_OUT_FACTOR = 0.4752; +const PINCH_ZOOM_IN_FACTOR = 1.05702; +const PINCH_ZOOM_OUT_FACTOR = 0.946058; const RENAME_NODE_NAME = 'Something else'; +const RENAME_NODE_NAME2 = 'Something different'; describe('Canvas Node Manipulation and Navigation', () => { beforeEach(() => { @@ -30,22 +31,30 @@ describe('Canvas Node Manipulation and Navigation', () => { it('should add switch node and test connections', () => { const desiredOutputs = 4; + WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME, true); WorkflowPage.actions.addNodeToCanvas(SWITCH_NODE_NAME, true, true); for (let i = 0; i < desiredOutputs; i++) { - cy.contains('Add Routing Rule').click() + cy.contains('Add Routing Rule').click(); } - NDVDialog.actions.close() + NDVDialog.actions.close(); for (let i = 0; i < desiredOutputs; i++) { WorkflowPage.getters.canvasNodePlusEndpointByName(SWITCH_NODE_NAME, i).click({ force: true }); WorkflowPage.getters.nodeCreatorSearchBar().should('be.visible'); WorkflowPage.actions.addNodeToCanvas(EDIT_FIELDS_SET_NODE_NAME, false); WorkflowPage.actions.zoomToFit(); } + WorkflowPage.getters.nodeViewBackground().click({ force: true }); + WorkflowPage.getters.canvasNodePlusEndpointByName(`${EDIT_FIELDS_SET_NODE_NAME}3`).click(); + WorkflowPage.actions.addNodeToCanvas(SWITCH_NODE_NAME, false); WorkflowPage.actions.saveWorkflowOnButtonClick(); cy.reload(); cy.waitForLoad(); + // Make sure outputless switch was connected correctly + cy.get( + `[data-target-node="${SWITCH_NODE_NAME}1"][data-source-node="${EDIT_FIELDS_SET_NODE_NAME}3"]`, + ).should('be.visible'); // Make sure all connections are there after reload for (let i = 0; i < desiredOutputs; i++) { const setName = `${EDIT_FIELDS_SET_NODE_NAME}${i > 0 ? i : ''}`; @@ -121,13 +130,10 @@ describe('Canvas Node Manipulation and Navigation', () => { cy.get('.jtk-connector').should('have.length', 4); }); - it('should delete node using node action button', () => { + it('should delete node using context menu', () => { WorkflowPage.actions.addNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME); WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME); - WorkflowPage.getters - .canvasNodeByName(CODE_NODE_NAME) - .find('[data-test-id=delete-node-button]') - .click({ force: true }); + WorkflowPage.actions.deleteNodeFromContextMenu(CODE_NODE_NAME); WorkflowPage.getters.canvasNodes().should('have.length', 1); WorkflowPage.getters.nodeConnections().should('have.length', 0); }); @@ -154,13 +160,38 @@ describe('Canvas Node Manipulation and Navigation', () => { WorkflowPage.getters.nodeConnections().should('have.length', 1); }); - it('should delete multiple nodes', () => { + it('should delete multiple nodes (context menu or shortcut)', () => { WorkflowPage.actions.addNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME); WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME); cy.wait(500); WorkflowPage.actions.selectAll(); cy.get('body').type('{backspace}'); WorkflowPage.getters.canvasNodes().should('have.length', 0); + + WorkflowPage.actions.addNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME); + WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME); + cy.wait(500); + WorkflowPage.actions.selectAllFromContextMenu(); + WorkflowPage.actions.openContextMenu(); + WorkflowPage.actions.contextMenuAction('delete'); + WorkflowPage.getters.canvasNodes().should('have.length', 0); + }); + + it('should delete multiple nodes (context menu or shortcut)', () => { + WorkflowPage.actions.addNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME); + WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME); + cy.wait(500); + WorkflowPage.actions.selectAll(); + cy.get('body').type('{backspace}'); + WorkflowPage.getters.canvasNodes().should('have.length', 0); + + WorkflowPage.actions.addNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME); + WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME); + cy.wait(500); + WorkflowPage.actions.selectAllFromContextMenu(); + WorkflowPage.actions.openContextMenu(); + WorkflowPage.actions.contextMenuAction('delete'); + WorkflowPage.getters.canvasNodes().should('have.length', 0); }); it('should move node', () => { @@ -168,12 +199,13 @@ describe('Canvas Node Manipulation and Navigation', () => { WorkflowPage.getters.canvasNodeByName(MANUAL_TRIGGER_NODE_DISPLAY_NAME).click(); WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME); WorkflowPage.actions.zoomToFit(); + cy.drag('[data-test-id="canvas-node"].jtk-drag-selected', [50, 150], { clickToFinish: true }); WorkflowPage.getters .canvasNodes() .last() .should('have.css', 'left', '740px') - .should('have.css', 'top', '320px') + .should('have.css', 'top', '320px'); }); it('should zoom in', () => { @@ -214,8 +246,8 @@ describe('Canvas Node Manipulation and Navigation', () => { ); }); - it('should zoom using pinch to zoom', () => { - WorkflowPage.actions.pinchToZoom(2, 'zoomIn'); + it('should zoom using scroll or pinch gesture', () => { + WorkflowPage.actions.pinchToZoom(1, 'zoomIn'); WorkflowPage.getters .nodeView() .should( @@ -224,7 +256,11 @@ describe('Canvas Node Manipulation and Navigation', () => { `matrix(${PINCH_ZOOM_IN_FACTOR}, 0, 0, ${PINCH_ZOOM_IN_FACTOR}, 0, 0)`, ); - WorkflowPage.actions.pinchToZoom(4, 'zoomOut'); + WorkflowPage.actions.pinchToZoom(1, 'zoomOut'); + // Zoom in 1x + Zoom out 1x should reset to default (=1) + WorkflowPage.getters.nodeView().should('have.css', 'transform', `matrix(1, 0, 0, 1, 0, 0)`); + + WorkflowPage.actions.pinchToZoom(1, 'zoomOut'); WorkflowPage.getters .nodeView() .should( @@ -259,39 +295,42 @@ describe('Canvas Node Manipulation and Navigation', () => { WorkflowPage.getters.canvasNodes().last().should('be.visible'); }); - it('should disable node by pressing the disable button', () => { - WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME); - WorkflowPage.getters.canvasNodeByName(MANUAL_TRIGGER_NODE_DISPLAY_NAME).click(); - WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME); - WorkflowPage.getters - .canvasNodes() - .last() - .find('[data-test-id="disable-node-button"]') - .click({ force: true }); - WorkflowPage.getters.disabledNodes().should('have.length', 1); - }); - - it('should disable node using keyboard shortcut', () => { + it('should disable node (context menu or shortcut)', () => { WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME); WorkflowPage.getters.canvasNodeByName(MANUAL_TRIGGER_NODE_DISPLAY_NAME).click(); WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME); WorkflowPage.getters.canvasNodes().last().click(); WorkflowPage.actions.hitDisableNodeShortcut(); WorkflowPage.getters.disabledNodes().should('have.length', 1); + + WorkflowPage.actions.disableNode(CODE_NODE_NAME); + WorkflowPage.getters.disabledNodes().should('have.length', 0); }); - it('should disable multiple nodes', () => { + it('should disable multiple nodes (context menu or shortcut)', () => { WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME); WorkflowPage.getters.canvasNodeByName(MANUAL_TRIGGER_NODE_DISPLAY_NAME).click(); WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME); cy.get('body').type('{esc}'); cy.get('body').type('{esc}'); WorkflowPage.actions.selectAll(); + + // Keyboard shortcut WorkflowPage.actions.hitDisableNodeShortcut(); WorkflowPage.getters.disabledNodes().should('have.length', 2); + WorkflowPage.actions.hitDisableNodeShortcut(); + WorkflowPage.getters.disabledNodes().should('have.length', 0); + + // Context menu + WorkflowPage.actions.openContextMenu(); + WorkflowPage.actions.contextMenuAction('toggle_activation'); + WorkflowPage.getters.disabledNodes().should('have.length', 2); + WorkflowPage.actions.openContextMenu(); + WorkflowPage.actions.contextMenuAction('toggle_activation'); + WorkflowPage.getters.disabledNodes().should('have.length', 0); }); - it('should rename node using keyboard shortcut', () => { + it('should rename node (context menu or shortcut)', () => { WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME); WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME); WorkflowPage.getters.canvasNodes().last().click(); @@ -300,19 +339,25 @@ describe('Canvas Node Manipulation and Navigation', () => { cy.get('body').type(RENAME_NODE_NAME); cy.get('body').type('{enter}'); WorkflowPage.getters.canvasNodeByName(RENAME_NODE_NAME).should('exist'); + + WorkflowPage.actions.renameNode(RENAME_NODE_NAME); + cy.get('.rename-prompt').should('be.visible'); + cy.get('body').type(RENAME_NODE_NAME2); + cy.get('body').type('{enter}'); + WorkflowPage.getters.canvasNodeByName(RENAME_NODE_NAME2).should('exist'); }); - it('should duplicate node', () => { + it('should duplicate nodes (context menu or shortcut)', () => { WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME); WorkflowPage.getters.canvasNodeByName(MANUAL_TRIGGER_NODE_DISPLAY_NAME).click(); WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME); - WorkflowPage.getters - .canvasNodes() - .last() - .find('[data-test-id="duplicate-node-button"]') - .click({ force: true }); + WorkflowPage.actions.duplicateNode(CODE_NODE_NAME); WorkflowPage.getters.canvasNodes().should('have.length', 3); WorkflowPage.getters.nodeConnections().should('have.length', 1); + + WorkflowPage.actions.selectAll(); + WorkflowPage.actions.hitDuplicateNodeShortcut(); + WorkflowPage.getters.canvasNodes().should('have.length', 5); }); // ADO-1240: Connections would get deleted after activating and deactivating NodeView @@ -344,5 +389,47 @@ describe('Canvas Node Manipulation and Navigation', () => { cy.waitForLoad(); WorkflowPage.getters.canvasNodes().should('have.length', 2); cy.get('.rect-input-endpoint.jtk-endpoint-connected').should('have.length', 1); - }) + }); + + it('should remove unknown credentials on pasting workflow', () => { + cy.fixture('workflow-with-unknown-credentials.json').then((data) => { + cy.get('body').paste(JSON.stringify(data)); + + WorkflowPage.getters.canvasNodes().should('have.have.length', 2); + + WorkflowPage.actions.openNodeFromContextMenu('n8n'); + cy.get('[class*=hasIssues]').should('have.length', 1); + NDVDialog.actions.close(); + }); + }); + + it('should render connections correctly if unkown nodes are present', () => { + const unknownNodeName = 'Unknown node'; + cy.createFixtureWorkflow('workflow-with-unknown-nodes.json', 'Unknown nodes'); + + WorkflowPage.getters.canvasNodeByName(`${unknownNodeName} 1`).should('exist'); + WorkflowPage.getters.canvasNodeByName(`${unknownNodeName} 2`).should('exist'); + WorkflowPage.actions.zoomToFit(); + + cy.draganddrop( + WorkflowPage.getters.getEndpointSelector('plus', `${unknownNodeName} 1`), + WorkflowPage.getters.getEndpointSelector('input', EDIT_FIELDS_SET_NODE_NAME), + ); + + cy.draganddrop( + WorkflowPage.getters.getEndpointSelector('plus', `${unknownNodeName} 2`), + WorkflowPage.getters.getEndpointSelector('input', `${EDIT_FIELDS_SET_NODE_NAME}1`), + ); + + WorkflowPage.actions.executeWorkflow(); + cy.contains('Unrecognized node type').should('be.visible'); + + WorkflowPage.actions.deselectAll(); + WorkflowPage.actions.deleteNodeFromContextMenu(`${unknownNodeName} 1`); + WorkflowPage.actions.deleteNodeFromContextMenu(`${unknownNodeName} 2`); + + WorkflowPage.actions.executeWorkflow(); + + cy.contains('Unrecognized node type').should('not.exist'); + }); }); diff --git a/cypress/e2e/13-pinning.cy.ts b/cypress/e2e/13-pinning.cy.ts index 9283d07923..7648ee7648 100644 --- a/cypress/e2e/13-pinning.cy.ts +++ b/cypress/e2e/13-pinning.cy.ts @@ -3,6 +3,7 @@ import { MANUAL_TRIGGER_NODE_NAME, PIPEDRIVE_NODE_NAME, EDIT_FIELDS_SET_NODE_NAME, + BACKEND_BASE_URL, } from '../constants'; import { WorkflowPage, NDV } from '../pages'; @@ -62,13 +63,77 @@ describe('Data pinning', () => { workflowPage.actions.saveWorkflowOnButtonClick(); - cy.reload(); workflowPage.actions.openNode('Schedule Trigger'); ndv.getters.outputTableHeaders().first().should('include.text', 'test'); ndv.getters.outputTbodyCell(1, 0).should('include.text', 1); }); + it('Should be duplicating pin data when duplicating node', () => { + workflowPage.actions.addInitialNodeToCanvas('Schedule Trigger'); + workflowPage.actions.addNodeToCanvas(EDIT_FIELDS_SET_NODE_NAME, true, true); + ndv.getters.container().should('be.visible'); + ndv.getters.pinDataButton().should('not.exist'); + ndv.getters.editPinnedDataButton().should('be.visible'); + + ndv.actions.setPinnedData([{ test: 1 }]); + ndv.actions.close(); + + workflowPage.actions.duplicateNode(EDIT_FIELDS_SET_NODE_NAME); + + workflowPage.actions.saveWorkflowOnButtonClick(); + + workflowPage.actions.openNode('Edit Fields1'); + + ndv.getters.outputTableHeaders().first().should('include.text', 'test'); + ndv.getters.outputTbodyCell(1, 0).should('include.text', 1); + }); + + it('Should be able to pin data from canvas (context menu or shortcut)', () => { + workflowPage.actions.addInitialNodeToCanvas('Schedule Trigger'); + workflowPage.actions.addNodeToCanvas(EDIT_FIELDS_SET_NODE_NAME); + workflowPage.actions.openContextMenu(EDIT_FIELDS_SET_NODE_NAME, 'overflow-button'); + workflowPage.getters + .contextMenuAction('toggle_pin') + .parent() + .should('have.class', 'is-disabled'); + + // Unpin using context menu + workflowPage.actions.openNode(EDIT_FIELDS_SET_NODE_NAME); + ndv.actions.setPinnedData([{ test: 1 }]); + ndv.actions.close(); + workflowPage.actions.pinNode(EDIT_FIELDS_SET_NODE_NAME); + workflowPage.actions.openNode(EDIT_FIELDS_SET_NODE_NAME); + ndv.getters.nodeOutputHint().should('exist'); + ndv.actions.close(); + + // Unpin using shortcut + workflowPage.actions.openNode(EDIT_FIELDS_SET_NODE_NAME); + ndv.actions.setPinnedData([{ test: 1 }]); + ndv.actions.close(); + workflowPage.getters.canvasNodeByName(EDIT_FIELDS_SET_NODE_NAME).click(); + workflowPage.actions.hitPinNodeShortcut(); + workflowPage.actions.openNode(EDIT_FIELDS_SET_NODE_NAME); + ndv.getters.nodeOutputHint().should('exist'); + }); + + it('Should show an error when maximum pin data size is exceeded', () => { + workflowPage.actions.addInitialNodeToCanvas('Schedule Trigger'); + workflowPage.actions.addNodeToCanvas(EDIT_FIELDS_SET_NODE_NAME, true, true); + ndv.getters.container().should('be.visible'); + ndv.getters.pinDataButton().should('not.exist'); + ndv.getters.editPinnedDataButton().should('be.visible'); + + ndv.actions.setPinnedData([ + { + test: '1'.repeat(Cypress.env('MAX_PINNED_DATA_SIZE')), + }, + ]); + workflowPage.getters + .errorToast() + .should('contain', 'Workflow has reached the maximum allowed pinned data size'); + }); + it('Should be able to reference paired items in a node located before pinned data', () => { workflowPage.actions.addInitialNodeToCanvas(MANUAL_TRIGGER_NODE_NAME); workflowPage.actions.addNodeToCanvas(HTTP_REQUEST_NODE_NAME, true, true); @@ -86,10 +151,25 @@ describe('Data pinning', () => { cy.get('div').contains(output).should('be.visible'); }); + + it('should use pin data in manual executions that are started by a webhook', () => { + cy.createFixtureWorkflow('Test_workflow_webhook_with_pin_data.json', 'Test'); + + workflowPage.actions.executeWorkflow(); + + cy.request('GET', `${BACKEND_BASE_URL}/webhook-test/b0d79ddb-df2d-49b1-8555-9fa2b482608f`).then((response) => { + expect(response.status).to.eq(200); + }); + + workflowPage.actions.openNode('End'); + + ndv.getters.outputTableRow(1).should('exist') + ndv.getters.outputTableRow(1).should('have.text', 'pin-overwritten'); + }); }); function setExpressionOnStringValueInSet(expression: string) { - cy.get('button').contains('Execute node').click(); + cy.get('button').contains('Test step').click(); cy.get('.fixed-collection-parameter > :nth-child(2) > .button > span').click(); ndv.getters.nthParam(4).contains('Expression').invoke('show').click(); diff --git a/cypress/e2e/14-mapping.cy.ts b/cypress/e2e/14-mapping.cy.ts index ef3b3aeb60..c547383e3e 100644 --- a/cypress/e2e/14-mapping.cy.ts +++ b/cypress/e2e/14-mapping.cy.ts @@ -80,14 +80,14 @@ describe('Data mapping', () => { .parameterExpressionPreview('value') .should('include.text', '0') .invoke('css', 'color') - .should('equal', 'rgb(125, 125, 135)'); + .should('equal', 'rgb(113, 116, 122)'); ndv.getters.inputTbodyCell(2, 0).realHover(); ndv.getters .parameterExpressionPreview('value') .should('include.text', '1') .invoke('css', 'color') - .should('equal', 'rgb(125, 125, 135)'); + .should('equal', 'rgb(113, 116, 122)'); ndv.actions.execute(); @@ -96,14 +96,14 @@ describe('Data mapping', () => { .parameterExpressionPreview('value') .should('include.text', '0') .invoke('css', 'color') - .should('equal', 'rgb(125, 125, 135)'); // todo update color + .should('equal', 'rgb(113, 116, 122)'); // todo update color ndv.getters.outputTbodyCell(2, 0).realHover(); ndv.getters .parameterExpressionPreview('value') .should('include.text', '1') .invoke('css', 'color') - .should('equal', 'rgb(125, 125, 135)'); + .should('equal', 'rgb(113, 116, 122)'); }); it('maps expressions from json view', () => { @@ -235,11 +235,8 @@ describe('Data mapping', () => { ndv.actions.close(); - workflowPage.actions.addNodeToCanvas('Item Lists'); - workflowPage.actions.openNode('Item Lists'); - - ndv.getters.parameterInput('operation').click(); - getVisibleSelect().find('li').contains('Sort').click(); + workflowPage.actions.addNodeToCanvas('Sort'); + workflowPage.actions.openNode('Sort'); ndv.getters.nodeParameters().find('button').contains('Add Field To Sort By').click(); diff --git a/cypress/e2e/16-form-trigger-node.cy.ts b/cypress/e2e/16-form-trigger-node.cy.ts index 27198001a8..8226df6b33 100644 --- a/cypress/e2e/16-form-trigger-node.cy.ts +++ b/cypress/e2e/16-form-trigger-node.cy.ts @@ -1,7 +1,5 @@ import { WorkflowPage, NDV } from '../pages'; -import { v4 as uuid } from 'uuid'; -import { getPopper, getVisiblePopper, getVisibleSelect } from '../utils'; -import { META_KEY } from '../constants'; +import { getVisibleSelect } from '../utils'; const workflowPage = new WorkflowPage(); const ndv = new NDV(); @@ -13,10 +11,7 @@ describe('n8n Form Trigger', () => { it("add node by clicking on 'On form submission'", () => { workflowPage.getters.canvasPlusButton().click(); - cy.get('#node-view-root > div:nth-child(2) > div > div > aside ') - .find('span') - .contains('On form submission') - .click(); + workflowPage.getters.nodeCreatorNodeItems().contains('On form submission').click(); ndv.getters.parameterInput('formTitle').type('Test Form'); ndv.getters.parameterInput('formDescription').type('Test Form Description'); ndv.getters.parameterInput('fieldLabel').type('Test Field 1'); @@ -76,12 +71,25 @@ describe('n8n Form Trigger', () => { ) .find('input') .type('Option 2'); - //add optionall submitted message - cy.get('.param-options > .button').click(); - cy.get('.indent > .parameter-item') - .find('input') + + //add optional submitted message + cy.get('.param-options').click(); + cy.contains('span', 'Text to Show') + .should('exist') + .parent() + .parent() + .next() + .children() + .children() + .children() + .children() + .children() + .children() + .children() + .first() .clear() .type('Your test form was successfully submitted'); + ndv.getters.backToCanvas().click(); workflowPage.getters.nodeIssuesByName('n8n Form Trigger').should('not.exist'); }); diff --git a/cypress/e2e/16-webhook-node.cy.ts b/cypress/e2e/16-webhook-node.cy.ts index c32a1407dc..da43a7cf4b 100644 --- a/cypress/e2e/16-webhook-node.cy.ts +++ b/cypress/e2e/16-webhook-node.cy.ts @@ -187,12 +187,14 @@ describe('Webhook Trigger node', async () => { ndv.getters.backToCanvas().click(); - workflowPage.actions.addNodeToCanvas('Convert to/from binary data'); + workflowPage.actions.addNodeToCanvas('Convert to File'); workflowPage.actions.zoomToFit(); - workflowPage.actions.openNode('Convert to/from binary data'); + workflowPage.actions.openNode('Convert to File'); + cy.getByTestId('parameter-input-operation').click(); + getVisibleSelect().find('.option-headline').contains('Convert to JSON').click(); cy.getByTestId('parameter-input-mode').click(); - getVisibleSelect().find('.option-headline').contains('JSON to Binary').click(); + getVisibleSelect().find('.option-headline').contains('Each Item to Separate File').click(); ndv.getters.backToCanvas().click(); workflowPage.actions.executeWorkflow(); diff --git a/cypress/e2e/17-sharing.cy.ts b/cypress/e2e/17-sharing.cy.ts index 40454dde3f..71f41250ec 100644 --- a/cypress/e2e/17-sharing.cy.ts +++ b/cypress/e2e/17-sharing.cy.ts @@ -1,4 +1,4 @@ -import { INSTANCE_MEMBERS, INSTANCE_OWNER } from '../constants'; +import { INSTANCE_MEMBERS, INSTANCE_OWNER, INSTANCE_ADMIN } from '../constants'; import { CredentialsModal, CredentialsPage, @@ -7,6 +7,7 @@ import { WorkflowSharingModal, WorkflowsPage, } from '../pages'; +import { getVisibleSelect } from '../utils'; /** * User U1 - Instance owner @@ -59,6 +60,7 @@ describe('Sharing', { disableAutoLogin: true }, () => { cy.visit(workflowsPage.url); workflowsPage.getters.createWorkflowButton().click(); cy.createFixtureWorkflow('Test_workflow_1.json', 'Workflow W2'); + workflowPage.actions.saveWorkflowOnButtonClick(); cy.url().then((url) => { workflowW2Url = url; }); @@ -96,6 +98,26 @@ describe('Sharing', { disableAutoLogin: true }, () => { ndv.actions.close(); }); + it('should open W1, add node using C2 as U2', () => { + cy.signin(INSTANCE_MEMBERS[0]); + + cy.visit(workflowsPage.url); + workflowsPage.getters.workflowCards().should('have.length', 2); + workflowsPage.getters.workflowCard('Workflow W1').click(); + workflowPage.actions.addNodeToCanvas('Airtable', true, true); + ndv.getters.credentialInput().find('input').should('have.value', 'Credential C2'); + ndv.actions.close(); + workflowPage.actions.saveWorkflowOnButtonClick(); + + workflowPage.actions.openNode('Notion'); + ndv.getters + .credentialInput() + .find('input') + .should('have.value', 'Credential C1') + .should('be.enabled'); + ndv.actions.close(); + }); + it('should not have access to W2, as U3', () => { cy.signin(INSTANCE_MEMBERS[1]); @@ -128,4 +150,41 @@ describe('Sharing', { disableAutoLogin: true }, () => { credentialsPage.getters.credentialCard('Credential C2').click(); credentialsModal.getters.testSuccessTag().should('be.visible'); }); + + it('should work for admin role on credentials created by others (also can share it with themselves)', () => { + cy.signin(INSTANCE_MEMBERS[0]); + + cy.visit(credentialsPage.url); + credentialsPage.getters.createCredentialButton().click(); + credentialsModal.getters.newCredentialTypeOption('Notion API').click(); + credentialsModal.getters.newCredentialTypeButton().click({ force: true }); + credentialsModal.getters.connectionParameter('Internal Integration Secret').type('1234567890'); + credentialsModal.actions.setName('Credential C3'); + credentialsModal.actions.save(); + credentialsModal.actions.close(); + + cy.signout(); + cy.signin(INSTANCE_ADMIN); + cy.visit(credentialsPage.url); + credentialsPage.getters.credentialCard('Credential C3').click(); + credentialsModal.getters.testSuccessTag().should('be.visible'); + cy.get('input').should('not.have.length'); + credentialsModal.actions.changeTab('Sharing'); + cy.contains( + 'You can view this credential because you have permission to read and share', + ).should('be.visible'); + + credentialsModal.getters.usersSelect().click(); + cy.getByTestId('user-email') + .filter(':visible') + .should('have.length', 3) + .contains(INSTANCE_ADMIN.email) + .should('have.length', 1); + getVisibleSelect().contains(INSTANCE_OWNER.email.toLowerCase()).click(); + + credentialsModal.actions.addUser(INSTANCE_MEMBERS[1].email); + credentialsModal.actions.addUser(INSTANCE_ADMIN.email); + credentialsModal.actions.saveSharing(); + credentialsModal.actions.close(); + }); }); diff --git a/cypress/e2e/18-user-management.cy.ts b/cypress/e2e/18-user-management.cy.ts index 8c714eaa6c..91ab059f83 100644 --- a/cypress/e2e/18-user-management.cy.ts +++ b/cypress/e2e/18-user-management.cy.ts @@ -1,6 +1,7 @@ -import { INSTANCE_MEMBERS, INSTANCE_OWNER } from '../constants'; -import { SettingsUsersPage, WorkflowPage } from '../pages'; +import { INSTANCE_MEMBERS, INSTANCE_OWNER, INSTANCE_ADMIN } from '../constants'; +import { MainSidebar, SettingsSidebar, SettingsUsersPage, WorkflowPage } from '../pages'; import { PersonalSettingsPage } from '../pages/settings-personal'; +import { getVisibleSelect } from '../utils'; /** * User A - Instance owner @@ -25,9 +26,13 @@ const updatedPersonalData = { const usersSettingsPage = new SettingsUsersPage(); const workflowPage = new WorkflowPage(); const personalSettingsPage = new PersonalSettingsPage(); +const settingsSidebar = new SettingsSidebar(); +const mainSidebar = new MainSidebar(); describe('User Management', { disableAutoLogin: true }, () => { - before(() => cy.enableFeature('sharing')); + before(() => { + cy.enableFeature('sharing'); + }); it('should prevent non-owners to access UM settings', () => { usersSettingsPage.actions.loginAndVisit( @@ -44,7 +49,7 @@ describe('User Management', { disableAutoLogin: true }, () => { it('should properly render UM settings page for instance owners', () => { usersSettingsPage.actions.loginAndVisit(INSTANCE_OWNER.email, INSTANCE_OWNER.password, true); // All items in user list should be there - usersSettingsPage.getters.userListItems().should('have.length', 3); + usersSettingsPage.getters.userListItems().should('have.length', 4); // List item for current user should have the `Owner` badge usersSettingsPage.getters .userItem(INSTANCE_OWNER.email) @@ -53,6 +58,93 @@ describe('User Management', { disableAutoLogin: true }, () => { // Other users list items should contain action pop-up list usersSettingsPage.getters.userActionsToggle(INSTANCE_MEMBERS[0].email).should('exist'); usersSettingsPage.getters.userActionsToggle(INSTANCE_MEMBERS[1].email).should('exist'); + usersSettingsPage.getters.userActionsToggle(INSTANCE_ADMIN.email).should('exist'); + }); + + it('should be able to change user role to Admin and back', () => { + cy.enableFeature('advancedPermissions'); + + usersSettingsPage.actions.loginAndVisit(INSTANCE_OWNER.email, INSTANCE_OWNER.password, true); + + // Change role from Member to Admin + usersSettingsPage.getters + .userRoleSelect(INSTANCE_MEMBERS[0].email) + .find('input') + .should('contain.value', 'Member'); + usersSettingsPage.getters.userRoleSelect(INSTANCE_MEMBERS[0].email).click(); + getVisibleSelect().find('li').contains('Admin').click(); + usersSettingsPage.getters + .userRoleSelect(INSTANCE_MEMBERS[0].email) + .find('input') + .should('contain.value', 'Admin'); + + usersSettingsPage.actions.loginAndVisit( + INSTANCE_MEMBERS[0].email, + INSTANCE_MEMBERS[0].password, + true, + ); + + // Change role from Admin to Member, then back to Admin + usersSettingsPage.getters + .userRoleSelect(INSTANCE_ADMIN.email) + .find('input') + .should('contain.value', 'Admin'); + + usersSettingsPage.getters.userRoleSelect(INSTANCE_ADMIN.email).click(); + getVisibleSelect().find('li').contains('Member').click(); + usersSettingsPage.getters + .userRoleSelect(INSTANCE_ADMIN.email) + .find('input') + .should('contain.value', 'Member'); + + usersSettingsPage.actions.loginAndVisit(INSTANCE_ADMIN.email, INSTANCE_ADMIN.password, false); + usersSettingsPage.actions.loginAndVisit( + INSTANCE_MEMBERS[0].email, + INSTANCE_MEMBERS[0].password, + true, + ); + + usersSettingsPage.getters.userRoleSelect(INSTANCE_ADMIN.email).click(); + getVisibleSelect().find('li').contains('Admin').click(); + usersSettingsPage.getters + .userRoleSelect(INSTANCE_ADMIN.email) + .find('input') + .should('contain.value', 'Admin'); + + usersSettingsPage.actions.loginAndVisit(INSTANCE_ADMIN.email, INSTANCE_ADMIN.password, true); + usersSettingsPage.getters.userRoleSelect(INSTANCE_MEMBERS[0].email).click(); + getVisibleSelect().find('li').contains('Member').click(); + usersSettingsPage.getters + .userRoleSelect(INSTANCE_MEMBERS[0].email) + .find('input') + .should('contain.value', 'Member'); + + cy.disableFeature('advancedPermissions'); + }); + + it('should be able to change theme', () => { + personalSettingsPage.actions.loginAndVisit(INSTANCE_OWNER.email, INSTANCE_OWNER.password); + + personalSettingsPage.actions.changeTheme('Dark'); + cy.get('body').should('have.attr', 'data-theme', 'dark'); + settingsSidebar.actions.back(); + mainSidebar.getters + .logo() + .should('have.attr', 'src') + .then((src) => { + expect(src).to.include('/n8n-dev-logo-dark-mode.svg'); + }); + + cy.visit(personalSettingsPage.url); + personalSettingsPage.actions.changeTheme('Light'); + cy.get('body').should('have.attr', 'data-theme', 'light'); + settingsSidebar.actions.back(); + mainSidebar.getters + .logo() + .should('have.attr', 'src') + .then((src) => { + expect(src).to.include('/n8n-dev-logo.svg'); + }); }); it('should delete user and their data', () => { diff --git a/cypress/e2e/19-execution.cy.ts b/cypress/e2e/19-execution.cy.ts index 0fcea7069c..2598b3f0f9 100644 --- a/cypress/e2e/19-execution.cy.ts +++ b/cypress/e2e/19-execution.cy.ts @@ -1,7 +1,8 @@ import { v4 as uuid } from 'uuid'; -import { NDV, WorkflowPage as WorkflowPageClass, WorkflowsPage } from '../pages'; +import { NDV, WorkflowExecutionsTab, WorkflowPage as WorkflowPageClass } from '../pages'; const workflowPage = new WorkflowPageClass(); +const executionsTab = new WorkflowExecutionsTab(); const ndv = new NDV(); describe('Execution', () => { @@ -112,10 +113,6 @@ describe('Execution', () => { .canvasNodeByName('Manual') .within(() => cy.get('.fa-check')) .should('exist'); - workflowPage.getters - .canvasNodeByName('Wait') - .within(() => cy.get('.fa-check')) - .should('exist'); workflowPage.getters .canvasNodeByName('Wait') .within(() => cy.get('.fa-sync-alt').should('not.visible')); @@ -191,10 +188,6 @@ describe('Execution', () => { .canvasNodeByName('Webhook') .within(() => cy.get('.fa-check')) .should('exist'); - workflowPage.getters - .canvasNodeByName('Wait') - .within(() => cy.get('.fa-check')) - .should('exist'); workflowPage.getters .canvasNodeByName('Set') .within(() => cy.get('.fa-check')) @@ -267,10 +260,6 @@ describe('Execution', () => { .canvasNodeByName('Webhook') .within(() => cy.get('.fa-check')) .should('exist'); - workflowPage.getters - .canvasNodeByName('Wait') - .within(() => cy.get('.fa-check')) - .should('exist'); workflowPage.getters .canvasNodeByName('Wait') .within(() => cy.get('.fa-sync-alt').should('not.visible')); @@ -286,4 +275,139 @@ describe('Execution', () => { // Check success toast (works because Cypress waits enough for the element to show after the http request node has finished) workflowPage.getters.successToast().should('be.visible'); }); + + describe('execution preview', () => { + it('when deleting the last execution, it should show empty state', () => { + workflowPage.actions.addInitialNodeToCanvas('Manual Trigger'); + workflowPage.actions.executeWorkflow(); + executionsTab.actions.switchToExecutionsTab(); + + executionsTab.actions.deleteExecutionInPreview(); + + executionsTab.getters.successfulExecutionListItems().should('have.length', 0); + workflowPage.getters.successToast().contains('Execution deleted'); + }); + }); + + describe('connections should be colored differently for pinned data', () => { + beforeEach(() => { + cy.createFixtureWorkflow('Schedule_pinned.json', `Schedule pinned ${uuid()}`); + workflowPage.actions.deselectAll(); + workflowPage.getters.zoomToFitButton().click(); + + workflowPage.getters + .getConnectionBetweenNodes('Schedule Trigger', 'Edit Fields') + .should('have.class', 'success') + .should('have.class', 'pinned') + .should('not.have.class', 'has-run'); + + workflowPage.getters + .getConnectionBetweenNodes('Schedule Trigger', 'Edit Fields1') + .should('have.class', 'success') + .should('have.class', 'pinned') + .should('not.have.class', 'has-run'); + + workflowPage.getters + .getConnectionBetweenNodes('Edit Fields5', 'Edit Fields6') + .should('not.have.class', 'success') + .should('not.have.class', 'pinned'); + + workflowPage.getters + .getConnectionBetweenNodes('Edit Fields7', 'Edit Fields9') + .should('have.class', 'success') + .should('have.class', 'pinned') + .should('not.have.class', 'has-run'); + + workflowPage.getters + .getConnectionBetweenNodes('Edit Fields1', 'Edit Fields2') + .should('not.have.class', 'success') + .should('not.have.class', 'pinned'); + + workflowPage.getters + .getConnectionBetweenNodes('Edit Fields2', 'Edit Fields3') + .should('have.class', 'success') + .should('have.class', 'pinned') + .should('not.have.class', 'has-run'); + }); + + it('when executing the workflow', () => { + workflowPage.actions.executeWorkflow(); + + workflowPage.getters + .getConnectionBetweenNodes('Schedule Trigger', 'Edit Fields') + .should('have.class', 'success') + .should('have.class', 'pinned') + .should('have.class', 'has-run'); + + workflowPage.getters + .getConnectionBetweenNodes('Schedule Trigger', 'Edit Fields1') + .should('have.class', 'success') + .should('have.class', 'pinned') + .should('have.class', 'has-run'); + + workflowPage.getters + .getConnectionBetweenNodes('Edit Fields5', 'Edit Fields6') + .should('have.class', 'success') + .should('not.have.class', 'pinned') + .should('not.have.class', 'has-run'); + + workflowPage.getters + .getConnectionBetweenNodes('Edit Fields7', 'Edit Fields9') + .should('have.class', 'success') + .should('have.class', 'pinned') + .should('have.class', 'has-run'); + + workflowPage.getters + .getConnectionBetweenNodes('Edit Fields1', 'Edit Fields2') + .should('have.class', 'success') + .should('not.have.class', 'pinned') + .should('not.have.class', 'has-run'); + + workflowPage.getters + .getConnectionBetweenNodes('Edit Fields2', 'Edit Fields3') + .should('have.class', 'success') + .should('have.class', 'pinned') + .should('have.class', 'has-run'); + }); + + it('when executing a node', () => { + workflowPage.actions.executeNode('Edit Fields3'); + + workflowPage.getters + .getConnectionBetweenNodes('Schedule Trigger', 'Edit Fields') + .should('have.class', 'success') + .should('have.class', 'pinned') + .should('have.class', 'has-run'); + + workflowPage.getters + .getConnectionBetweenNodes('Schedule Trigger', 'Edit Fields1') + .should('have.class', 'success') + .should('have.class', 'pinned') + .should('have.class', 'has-run'); + + workflowPage.getters + .getConnectionBetweenNodes('Edit Fields5', 'Edit Fields6') + .should('not.have.class', 'success') + .should('not.have.class', 'pinned') + .should('not.have.class', 'has-run'); + + workflowPage.getters + .getConnectionBetweenNodes('Edit Fields7', 'Edit Fields9') + .should('have.class', 'success') + .should('have.class', 'pinned') + .should('not.have.class', 'has-run'); + + workflowPage.getters + .getConnectionBetweenNodes('Edit Fields1', 'Edit Fields2') + .should('have.class', 'success') + .should('not.have.class', 'pinned') + .should('not.have.class', 'has-run'); + + workflowPage.getters + .getConnectionBetweenNodes('Edit Fields2', 'Edit Fields3') + .should('have.class', 'success') + .should('have.class', 'pinned') + .should('have.class', 'has-run'); + }); + }); }); diff --git a/cypress/e2e/2-credentials.cy.ts b/cypress/e2e/2-credentials.cy.ts index c30225429d..ca1ca6e014 100644 --- a/cypress/e2e/2-credentials.cy.ts +++ b/cypress/e2e/2-credentials.cy.ts @@ -42,7 +42,7 @@ describe('Credentials', () => { credentialsPage.getters.credentialCards().should('have.length', 1); }); - it('should create a new credential using Add Credential button', () => { + it.skip('should create a new credential using Add Credential button', () => { credentialsPage.getters.createCredentialButton().click(); credentialsModal.getters.newCredentialModal().should('be.visible'); @@ -60,7 +60,7 @@ describe('Credentials', () => { credentialsPage.getters.credentialCards().should('have.length', 2); }); - it('should search credentials', () => { + it.skip('should search credentials', () => { // Search by name credentialsPage.actions.search('Notion'); credentialsPage.getters.credentialCards().should('have.length', 1); diff --git a/cypress/e2e/20-workflow-executions.cy.ts b/cypress/e2e/20-workflow-executions.cy.ts index bdc7c3b711..b44b9337a7 100644 --- a/cypress/e2e/20-workflow-executions.cy.ts +++ b/cypress/e2e/20-workflow-executions.cy.ts @@ -1,18 +1,20 @@ import { WorkflowPage } from '../pages'; import { WorkflowExecutionsTab } from '../pages/workflow-executions-tab'; +import type { RouteHandler } from 'cypress/types/net-stubbing'; const workflowPage = new WorkflowPage(); const executionsTab = new WorkflowExecutionsTab(); +const executionsRefreshInterval = 4000; // Test suite for executions tab describe('Current Workflow Executions', () => { beforeEach(() => { workflowPage.actions.visit(); cy.createFixtureWorkflow('Test_workflow_4_executions_view.json', `My test workflow`); - createMockExecutions(); }); it('should render executions tab correctly', () => { + createMockExecutions(); cy.intercept('GET', '/rest/executions?filter=*').as('getExecutions'); cy.intercept('GET', '/rest/executions-current?filter=*').as('getCurrentExecutions'); @@ -29,6 +31,45 @@ describe('Current Workflow Executions', () => { .invoke('attr', 'class') .should('match', /_active_/); }); + + it('should not redirect back to execution tab when request is not done before leaving the page', () => { + cy.intercept('GET', '/rest/executions?filter=*'); + cy.intercept('GET', '/rest/executions-current?filter=*'); + + executionsTab.actions.switchToExecutionsTab(); + executionsTab.actions.switchToEditorTab(); + cy.wait(executionsRefreshInterval); + cy.url().should('not.include', '/executions'); + executionsTab.actions.switchToExecutionsTab(); + executionsTab.actions.switchToEditorTab(); + executionsTab.actions.switchToExecutionsTab(); + executionsTab.actions.switchToEditorTab(); + executionsTab.actions.switchToExecutionsTab(); + executionsTab.actions.switchToEditorTab(); + cy.wait(executionsRefreshInterval); + cy.url().should('not.include', '/executions'); + executionsTab.actions.switchToExecutionsTab(); + cy.wait(1000); + executionsTab.actions.switchToEditorTab(); + cy.wait(executionsRefreshInterval); + cy.url().should('not.include', '/executions'); + }); + + it('should not redirect back to execution tab when slow request is not done before leaving the page', () => { + const throttleResponse: RouteHandler = (req) => { + return new Promise((resolve) => { + setTimeout(() => resolve(req.continue()), 2000); + }); + }; + + cy.intercept('GET', '/rest/executions?filter=*', throttleResponse); + cy.intercept('GET', '/rest/executions-current?filter=*', throttleResponse); + + executionsTab.actions.switchToExecutionsTab(); + executionsTab.actions.switchToEditorTab(); + cy.wait(executionsRefreshInterval); + cy.url().should('not.include', '/executions'); + }); }); const createMockExecutions = () => { diff --git a/cypress/e2e/24-ndv-paired-item.cy.ts b/cypress/e2e/24-ndv-paired-item.cy.ts index d1509db9e6..58fa0fdb63 100644 --- a/cypress/e2e/24-ndv-paired-item.cy.ts +++ b/cypress/e2e/24-ndv-paired-item.cy.ts @@ -19,7 +19,7 @@ describe('NDV', () => { workflowPage.actions.executeWorkflow(); - workflowPage.actions.openNode('Item Lists'); + workflowPage.actions.openNode('Sort'); ndv.getters.inputPanel().contains('6 items').should('exist'); ndv.getters.outputPanel().contains('6 items').should('exist'); @@ -92,7 +92,7 @@ describe('NDV', () => { ndv.getters.outputHoveringItem().should('have.text', '1000'); ndv.getters.parameterExpressionPreview('value').should('include.text', '1000'); - ndv.actions.selectInputNode('Item Lists'); + ndv.actions.selectInputNode('Sort'); ndv.actions.changeOutputRunSelector('1 of 2 (6 items)'); ndv.getters.backToCanvas().realHover(); // reset to default hover diff --git a/cypress/e2e/25-stickies.cy.ts b/cypress/e2e/25-stickies.cy.ts index ac94f882dd..dea3fa4fde 100644 --- a/cypress/e2e/25-stickies.cy.ts +++ b/cypress/e2e/25-stickies.cy.ts @@ -1,4 +1,7 @@ +import { META_KEY } from '../constants'; import { WorkflowPage as WorkflowPageClass } from '../pages/workflow'; +import { getPopper } from '../utils'; +import { Interception } from 'cypress/types/net-stubbing'; const workflowPage = new WorkflowPageClass(); @@ -29,6 +32,19 @@ describe('Canvas Actions', () => { workflowPage.getters.addStickyButton().should('not.be.visible'); addDefaultSticky(); + workflowPage.actions.deselectAll(); + workflowPage.actions.addStickyFromContextMenu(); + workflowPage.actions.hitAddStickyShortcut(); + + workflowPage.getters.stickies().should('have.length', 3); + + // Should not add a sticky for ctrl+shift+s + cy.get('body') + .type(META_KEY, { delay: 500, release: false }) + .type('{shift}', { release: false }) + .type('s'); + + workflowPage.getters.stickies().should('have.length', 3); workflowPage.getters .stickies() .eq(0) @@ -66,6 +82,32 @@ describe('Canvas Actions', () => { workflowPage.getters.stickies().should('have.length', 0); }); + it('change sticky color', () => { + workflowPage.actions.addSticky(); + + workflowPage.getters.stickies().should('have.length', 1); + + workflowPage.actions.toggleColorPalette(); + + getPopper().should('be.visible'); + + workflowPage.actions.pickColor(2); + + workflowPage.actions.toggleColorPalette(); + + getPopper().should('not.be.visible'); + + workflowPage.actions.saveWorkflowOnButtonClick(); + + cy.wait('@createWorkflow').then((interception: Interception) => { + const { request } = interception; + const color = request.body?.nodes[0]?.parameters?.color; + expect(color).to.equal(2); + }); + + workflowPage.getters.stickies().should('have.length', 1); + }); + it('edits sticky and updates content as markdown', () => { workflowPage.actions.addSticky(); @@ -84,8 +126,11 @@ describe('Canvas Actions', () => { moveSticky({ top: 200, left: 200 }); - dragRightEdge({ left: 200, top: 200, height: 160, width: 240 }, 100); - dragRightEdge({ left: 200, top: 200, height: 160, width: 240 }, -50); + cy.drag('[data-test-id="sticky"] [data-dir="right"]', [100, 100]); + checkStickiesStyle(100, 20, 160, 346); + + cy.drag('[data-test-id="sticky"] [data-dir="right"]', [-50, -50]); + checkStickiesStyle(100, 20, 160, 302); }); it('expands/shrinks sticky from the left edge', () => { @@ -205,27 +250,6 @@ type Position = { left: number; }; -type BoundingBox = { - height: number; - width: number; - top: number; - left: number; -}; - -function dragRightEdge(curr: BoundingBox, move: number) { - workflowPage.getters - .stickies() - .first() - .then(($el) => { - const { left, top, height, width } = curr; - cy.drag(`[data-test-id="sticky"] [data-dir="right"]`, [left + width + move, 0], { - abs: true, - }); - stickyShouldBePositionedCorrectly({ top, left }); - stickyShouldHaveCorrectSize([height, width * 1.5 + move]); - }); -} - function shouldHaveOneSticky() { workflowPage.getters.stickies().should('have.length', 1); } diff --git a/cypress/e2e/26-resource-locator.cy.ts b/cypress/e2e/26-resource-locator.cy.ts index c556ba983e..9cea4e25a3 100644 --- a/cypress/e2e/26-resource-locator.cy.ts +++ b/cypress/e2e/26-resource-locator.cy.ts @@ -1,5 +1,5 @@ import { WorkflowPage, NDV, CredentialsModal } from '../pages'; -import { getPopper, getVisiblePopper, getVisibleSelect } from '../utils'; +import { getVisiblePopper, getVisibleSelect } from '../utils'; const workflowPage = new WorkflowPage(); const ndv = new NDV(); @@ -16,7 +16,7 @@ describe('Resource Locator', () => { it('should render both RLC components in google sheets', () => { workflowPage.actions.addInitialNodeToCanvas('Manual'); - workflowPage.actions.addNodeToCanvas('Google Sheets', true, true); + workflowPage.actions.addNodeToCanvas('Google Sheets', true, true, 'Update row in sheet'); ndv.getters.resourceLocator('documentId').should('be.visible'); ndv.getters.resourceLocator('sheetName').should('be.visible'); ndv.getters @@ -31,7 +31,7 @@ describe('Resource Locator', () => { it('should show appropriate error when credentials are not set', () => { workflowPage.actions.addInitialNodeToCanvas('Manual'); - workflowPage.actions.addNodeToCanvas('Google Sheets', true, true); + workflowPage.actions.addNodeToCanvas('Google Sheets', true, true, 'Update row in sheet'); ndv.getters.resourceLocator('documentId').should('be.visible'); ndv.getters.resourceLocatorInput('documentId').click(); ndv.getters.resourceLocatorErrorMessage().should('contain', NO_CREDENTIALS_MESSAGE); @@ -39,7 +39,7 @@ describe('Resource Locator', () => { it('should show appropriate error when credentials are not valid', () => { workflowPage.actions.addInitialNodeToCanvas('Manual'); - workflowPage.actions.addNodeToCanvas('Google Sheets', true, true); + workflowPage.actions.addNodeToCanvas('Google Sheets', true, true, 'Update row in sheet'); workflowPage.getters.nodeCredentialsSelect().click(); // Add oAuth credentials getVisibleSelect().find('li').last().click(); @@ -54,7 +54,7 @@ describe('Resource Locator', () => { it('should reset resource locator when dependent field is changed', () => { workflowPage.actions.addInitialNodeToCanvas('Manual'); - workflowPage.actions.addNodeToCanvas('Google Sheets', true, true); + workflowPage.actions.addNodeToCanvas('Google Sheets', true, true, 'Update row in sheet'); ndv.actions.setRLCValue('documentId', '123'); ndv.actions.setRLCValue('sheetName', '123'); ndv.actions.setRLCValue('documentId', '321'); @@ -66,6 +66,8 @@ describe('Resource Locator', () => { workflowPage.actions.addInitialNodeToCanvas('E2e Test', { action: 'Resource Locator' }); ndv.getters.resourceLocatorInput('rlc').click(); + + cy.getByTestId('rlc-item').should('exist'); getVisiblePopper() .should('have.length', 1) .findChildByTestId('rlc-item') @@ -73,9 +75,11 @@ describe('Resource Locator', () => { ndv.actions.setInvalidExpression({ fieldName: 'fieldId' }); - ndv.getters.container().click(); // remove focus from input, hide expression preview + ndv.getters.nodeParameters().click(); // remove focus from input, hide expression preview ndv.getters.resourceLocatorInput('rlc').click(); + + cy.getByTestId('rlc-item').should('exist'); getVisiblePopper() .should('have.length', 1) .findChildByTestId('rlc-item') diff --git a/cypress/e2e/27-cloud.cy.ts b/cypress/e2e/27-cloud.cy.ts new file mode 100644 index 0000000000..965bc5bccf --- /dev/null +++ b/cypress/e2e/27-cloud.cy.ts @@ -0,0 +1,119 @@ +import { + BannerStack, + MainSidebar, + WorkflowPage, + visitPublicApiPage, + getPublicApiUpgradeCTA, +} from '../pages'; +import planData from '../fixtures/Plan_data_opt_in_trial.json'; +import { INSTANCE_OWNER } from '../constants'; + +const mainSidebar = new MainSidebar(); +const bannerStack = new BannerStack(); +const workflowPage = new WorkflowPage(); + +describe('Cloud', { disableAutoLogin: true }, () => { + before(() => { + const now = new Date(); + const fiveDaysFromNow = new Date(now.getTime() + 5 * 24 * 60 * 60 * 1000); + planData.expirationDate = fiveDaysFromNow.toJSON(); + }); + + describe('BannerStack', () => { + it('should render trial banner for opt-in cloud user', () => { + cy.intercept('GET', '/rest/admin/cloud-plan', { + body: planData, + }).as('getPlanData'); + + cy.intercept('GET', '/rest/settings', (req) => { + req.on('response', (res) => { + res.send({ + data: { ...res.body.data, deployment: { type: 'cloud' }, n8nMetadata: { userId: 1 } }, + }); + }); + }).as('loadSettings'); + + cy.signin({ email: INSTANCE_OWNER.email, password: INSTANCE_OWNER.password }); + + cy.visit(workflowPage.url); + + cy.wait('@getPlanData'); + + bannerStack.getters.banner().should('be.visible'); + + mainSidebar.actions.signout(); + + bannerStack.getters.banner().should('not.be.visible'); + + cy.signin({ email: INSTANCE_OWNER.email, password: INSTANCE_OWNER.password }); + + cy.visit(workflowPage.url); + + bannerStack.getters.banner().should('be.visible'); + + mainSidebar.actions.signout(); + }); + + it('should not render opt-in-trial banner for non cloud deployment', () => { + cy.intercept('GET', '/rest/settings', (req) => { + req.on('response', (res) => { + res.send({ + data: { ...res.body.data, deployment: { type: 'default' } }, + }); + }); + }).as('loadSettings'); + + cy.signin({ email: INSTANCE_OWNER.email, password: INSTANCE_OWNER.password }); + + cy.visit(workflowPage.url); + + bannerStack.getters.banner().should('not.be.visible'); + + mainSidebar.actions.signout(); + }); + }); + + describe('Admin Home', () => { + it('Should show admin button', () => { + cy.intercept('GET', '/rest/settings', (req) => { + req.on('response', (res) => { + res.send({ + data: { ...res.body.data, deployment: { type: 'cloud' }, n8nMetadata: { userId: 1 } }, + }); + }); + }).as('loadSettings'); + + cy.signin({ email: INSTANCE_OWNER.email, password: INSTANCE_OWNER.password }); + + cy.visit(workflowPage.url); + + mainSidebar.getters.adminPanel().should('be.visible'); + }); + }); + + describe('Public API', () => { + it('Should show upgrade CTA for Public API if user is trialing', () => { + cy.intercept('GET', '/rest/admin/cloud-plan', { + body: planData, + }).as('getPlanData'); + + cy.intercept('GET', '/rest/settings', (req) => { + req.on('response', (res) => { + res.send({ + data: { + ...res.body.data, + deployment: { type: 'cloud' }, + n8nMetadata: { userId: 1 }, + }, + }); + }); + }).as('loadSettings'); + + cy.signin({ email: INSTANCE_OWNER.email, password: INSTANCE_OWNER.password }); + + visitPublicApiPage(); + + getPublicApiUpgradeCTA().should('be.visible'); + }); + }); +}); diff --git a/cypress/e2e/27-opt-in-trial-banner.cy.ts b/cypress/e2e/27-opt-in-trial-banner.cy.ts deleted file mode 100644 index 0f66236bb0..0000000000 --- a/cypress/e2e/27-opt-in-trial-banner.cy.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { BannerStack, MainSidebar, WorkflowPage } from '../pages'; -import planData from '../fixtures/Plan_data_opt_in_trial.json'; -import { INSTANCE_OWNER } from '../constants'; - -const mainSidebar = new MainSidebar(); -const bannerStack = new BannerStack(); -const workflowPage = new WorkflowPage(); - -describe('BannerStack', { disableAutoLogin: true }, () => { - before(() => { - const now = new Date(); - const fiveDaysFromNow = new Date(now.getTime() + 5 * 24 * 60 * 60 * 1000); - planData.expirationDate = fiveDaysFromNow.toJSON(); - }); - - it('should render trial banner for opt-in cloud user', () => { - cy.intercept('GET', '/rest/settings', (req) => { - req.on('response', (res) => { - res.send({ - data: { ...res.body.data, deployment: { type: 'cloud' }, n8nMetadata: { userId: 1 } }, - }); - }); - }).as('loadSettings'); - - cy.intercept('GET', '/rest/admin/cloud-plan', { - body: planData, - }).as('getPlanData'); - - cy.signin({ email: INSTANCE_OWNER.email, password: INSTANCE_OWNER.password }); - - cy.visit(workflowPage.url); - - cy.wait('@getPlanData'); - - bannerStack.getters.banner().should('be.visible'); - - mainSidebar.actions.signout(); - - bannerStack.getters.banner().should('not.be.visible'); - - cy.signin({ email: INSTANCE_OWNER.email, password: INSTANCE_OWNER.password }); - - cy.visit(workflowPage.url); - - bannerStack.getters.banner().should('be.visible'); - - mainSidebar.actions.signout(); - }); - - it('should not render opt-in-trial banner for non cloud deployment', () => { - cy.intercept('GET', '/rest/settings', (req) => { - req.on('response', (res) => { - res.send({ - data: { ...res.body.data, deployment: { type: 'default' } }, - }); - }); - }).as('loadSettings'); - - cy.signin({ email: INSTANCE_OWNER.email, password: INSTANCE_OWNER.password }); - - cy.visit(workflowPage.url); - - bannerStack.getters.banner().should('not.be.visible'); - - mainSidebar.actions.signout(); - }); -}); diff --git a/cypress/e2e/27-two-factor-authentication.cy.ts b/cypress/e2e/27-two-factor-authentication.cy.ts index 5e93909331..91f6ca57a2 100644 --- a/cypress/e2e/27-two-factor-authentication.cy.ts +++ b/cypress/e2e/27-two-factor-authentication.cy.ts @@ -1,5 +1,5 @@ import { MainSidebar } from './../pages/sidebar/main-sidebar'; -import { INSTANCE_OWNER, BACKEND_BASE_URL } from '../constants'; +import { INSTANCE_OWNER, INSTANCE_ADMIN, BACKEND_BASE_URL } from '../constants'; import { SigninPage } from '../pages'; import { PersonalSettingsPage } from '../pages/settings-personal'; import { MfaLoginPage } from '../pages/mfa-login'; @@ -19,6 +19,16 @@ const user = { mfaRecoveryCodes: [RECOVERY_CODE], }; +const admin = { + email: INSTANCE_ADMIN.email, + password: INSTANCE_ADMIN.password, + firstName: 'Admin', + lastName: 'B', + mfaEnabled: false, + mfaSecret: MFA_SECRET, + mfaRecoveryCodes: [RECOVERY_CODE], +}; + const mfaLoginPage = new MfaLoginPage(); const signinPage = new SigninPage(); const personalSettingsPage = new PersonalSettingsPage(); @@ -30,6 +40,7 @@ describe('Two-factor authentication', () => { cy.request('POST', `${BACKEND_BASE_URL}/rest/e2e/reset`, { owner: user, members: [], + admin, }); cy.on('uncaught:exception', (err, runnable) => { expect(err.message).to.include('Not logged in'); diff --git a/cypress/e2e/28-debug.cy.ts b/cypress/e2e/28-debug.cy.ts index 5795ffc257..699f07d53f 100644 --- a/cypress/e2e/28-debug.cy.ts +++ b/cypress/e2e/28-debug.cy.ts @@ -12,14 +12,11 @@ const ndv = new NDV(); const executionsTab = new WorkflowExecutionsTab(); describe('Debug', () => { + beforeEach(() => { + cy.enableFeature('debugInEditor'); + }); + it('should be able to debug executions', () => { - cy.intercept('GET', '/rest/settings', (req) => { - req.on('response', (res) => { - res.send({ - data: { ...res.body.data, enterprise: { debugInEditor: true } }, - }); - }); - }).as('loadSettings'); cy.intercept('GET', '/rest/executions?filter=*').as('getExecutions'); cy.intercept('GET', '/rest/executions/*').as('getExecution'); cy.intercept('GET', '/rest/executions-current?filter=*').as('getCurrentExecutions'); @@ -47,6 +44,7 @@ describe('Debug', () => { cy.wait(['@getExecutions', '@getCurrentExecutions']); executionsTab.getters.executionDebugButton().should('have.text', 'Debug in editor').click(); + cy.url().should('include', '/debug'); cy.get('.el-notification').contains('Execution data imported').should('be.visible'); cy.get('.matching-pinned-nodes-confirmation').should('not.exist'); @@ -56,6 +54,8 @@ describe('Debug', () => { ndv.actions.close(); workflowPage.actions.saveWorkflowUsingKeyboardShortcut(); + cy.url().should('not.include', '/debug'); + workflowPage.actions.executeWorkflow(); cy.wait(['@postWorkflowRun']); @@ -87,6 +87,7 @@ describe('Debug', () => { confirmDialog = cy.get('.matching-pinned-nodes-confirmation').filter(':visible'); confirmDialog.find('li').should('have.length', 2); confirmDialog.get('.btn--confirm').click(); + cy.url().should('include', '/debug'); workflowPage.getters.canvasNodes().first().should('have.descendants', '.node-pin-data-icon'); workflowPage.getters @@ -104,6 +105,7 @@ describe('Debug', () => { workflowPage.getters.canvasNodePlusEndpointByName(EDIT_FIELDS_SET_NODE_NAME).click(); workflowPage.actions.addNodeToCanvas(IF_NODE_NAME, false); workflowPage.actions.saveWorkflowUsingKeyboardShortcut(); + cy.url().should('not.include', '/debug'); executionsTab.actions.switchToExecutionsTab(); cy.wait(['@getExecutions', '@getCurrentExecutions']); @@ -112,6 +114,8 @@ describe('Debug', () => { confirmDialog = cy.get('.matching-pinned-nodes-confirmation').filter(':visible'); confirmDialog.find('li').should('have.length', 1); confirmDialog.get('.btn--confirm').click(); + cy.url().should('include', '/debug'); + workflowPage.getters.canvasNodes().last().find('.node-info-icon').should('be.empty'); workflowPage.getters.canvasNodes().first().dblclick(); @@ -119,7 +123,10 @@ describe('Debug', () => { ndv.actions.close(); workflowPage.actions.saveWorkflowUsingKeyboardShortcut(); + cy.url().should('not.include', '/debug'); + workflowPage.actions.executeWorkflow(); + workflowPage.actions.zoomToFit(); workflowPage.actions.deleteNode(IF_NODE_NAME); executionsTab.actions.switchToExecutionsTab(); @@ -128,5 +135,6 @@ describe('Debug', () => { cy.wait(['@getExecution']); executionsTab.getters.executionDebugButton().should('have.text', 'Copy to editor').click(); cy.get('.el-notification').contains("Some execution data wasn't imported").should('be.visible'); + cy.url().should('include', '/debug'); }); }); diff --git a/cypress/e2e/29-templates.cy.ts b/cypress/e2e/29-templates.cy.ts index 609f9cac0e..1df5a7c8c7 100644 --- a/cypress/e2e/29-templates.cy.ts +++ b/cypress/e2e/29-templates.cy.ts @@ -2,11 +2,21 @@ import { TemplatesPage } from '../pages/templates'; import { WorkflowPage } from '../pages/workflow'; import OnboardingWorkflow from '../fixtures/Onboarding_workflow.json'; +import WorkflowTemplate from '../fixtures/Workflow_template_write_http_query.json'; +import { TemplateWorkflowPage } from '../pages/template-workflow'; const templatesPage = new TemplatesPage(); const workflowPage = new WorkflowPage(); +const templateWorkflowPage = new TemplateWorkflowPage(); describe('Templates', () => { + beforeEach(() => { + cy.intercept('GET', '**/api/templates/search?page=1&rows=20&category=&search=', { fixture: 'templates_search/all_templates_search_response.json' }).as('searchRequest'); + cy.intercept('GET', '**/api/templates/search?page=1&rows=20&category=Sales*', { fixture: 'templates_search/sales_templates_search_response.json' }).as('categorySearchRequest'); + cy.intercept('GET', '**/api/templates/workflows/*', { fixture: 'templates_search/test_template_preview.json' }).as('singleTemplateRequest'); + cy.intercept('GET', '**/api/workflows/templates/*', { fixture: 'templates_search/test_template_import.json' }).as('singleTemplateRequest'); + }); + it('can open onboarding flow', () => { templatesPage.actions.openOnboardingFlow(1234, OnboardingWorkflow.name, OnboardingWorkflow); cy.url().then(($url) => { @@ -26,9 +36,104 @@ describe('Templates', () => { cy.url().then(($url) => { expect($url).to.include('/workflow/new?templateId=1234'); }); - + workflowPage.getters.canvasNodes().should('have.length', 4); workflowPage.getters.stickies().should('have.length', 1); workflowPage.actions.shouldHaveWorkflowName(OnboardingWorkflow.name); }); + + it('should save template id with the workflow', () => { + cy.visit(templatesPage.url); + cy.get('.el-skeleton.n8n-loading').should('not.exist'); + templatesPage.getters.firstTemplateCard().should('exist'); + templatesPage.getters.templatesLoadingContainer().should('not.exist'); + templatesPage.getters.firstTemplateCard().click(); + cy.url().should('include', '/templates/'); + + cy.url().then(($url) => { + const templateId = $url.split('/').pop(); + + templatesPage.getters.useTemplateButton().click(); + cy.url().should('include', '/workflow/new'); + workflowPage.actions.saveWorkflowOnButtonClick(); + + workflowPage.actions.selectAll(); + workflowPage.actions.hitCopy(); + + cy.grantBrowserPermissions('clipboardReadWrite', 'clipboardSanitizedWrite'); + // Check workflow JSON by copying it to clipboard + cy.readClipboard().then((workflowJSON) => { + expect(workflowJSON).to.contain(`"templateId": "${templateId}"`); + }); + }); + }); + + it('can open template with images and hides workflow screenshots', () => { + templateWorkflowPage.actions.openTemplate(WorkflowTemplate); + + templateWorkflowPage.getters.description().find('img').should('have.length', 1); + }); + + + it('renders search elements correctly', () => { + cy.visit(templatesPage.url); + templatesPage.getters.searchInput().should('exist'); + templatesPage.getters.allCategoriesFilter().should('exist'); + templatesPage.getters.categoryFilters().should('have.length.greaterThan', 1); + templatesPage.getters.templateCards().should('have.length.greaterThan', 0); + }); + + it('can filter templates by category', () => { + cy.visit(templatesPage.url); + templatesPage.getters.templatesLoadingContainer().should('not.exist'); + templatesPage.getters.expandCategoriesButton().click(); + templatesPage.getters.categoryFilter('sales').should('exist'); + let initialTemplateCount = 0; + let initialCollectionCount = 0; + + templatesPage.getters.templateCountLabel().then(($el) => { + initialTemplateCount = parseInt($el.text().replace(/\D/g, ''), 10); + templatesPage.getters.collectionCountLabel().then(($el) => { + initialCollectionCount = parseInt($el.text().replace(/\D/g, ''), 10); + + templatesPage.getters.categoryFilter('sales').click(); + templatesPage.getters.templatesLoadingContainer().should('not.exist'); + + // Should have less templates and collections after selecting a category + templatesPage.getters.templateCountLabel().should(($el) => { + expect(parseInt($el.text().replace(/\D/g, ''), 10)).to.be.lessThan(initialTemplateCount); + }); + templatesPage.getters.collectionCountLabel().should(($el) => { + expect(parseInt($el.text().replace(/\D/g, ''), 10)).to.be.lessThan(initialCollectionCount); + }); + }); + }); + }); + + it('should preserve search query in URL', () => { + cy.visit(templatesPage.url); + templatesPage.getters.templatesLoadingContainer().should('not.exist'); + templatesPage.getters.expandCategoriesButton().click(); + templatesPage.getters.categoryFilter('sales').should('exist'); + templatesPage.getters.categoryFilter('sales').click(); + templatesPage.getters.searchInput().type('auto'); + + cy.url().should('include', '?categories='); + cy.url().should('include', '&search='); + + cy.reload(); + + // Should preserve search query in URL + cy.url().should('include', '?categories='); + cy.url().should('include', '&search='); + + // Sales category should still be selected + templatesPage.getters.categoryFilter('sales').find('label').should('have.class', 'is-checked'); + // Search input should still have the search query + templatesPage.getters.searchInput().should('have.value', 'auto'); + // Sales checkbox should be pushed to the top + templatesPage.getters.categoryFilters().eq(1).then(($el) => { + expect($el.text()).to.equal('Sales'); + }); + }); }); diff --git a/cypress/e2e/30-editor-after-route-changes.cy.ts b/cypress/e2e/30-editor-after-route-changes.cy.ts new file mode 100644 index 0000000000..656d7e9b78 --- /dev/null +++ b/cypress/e2e/30-editor-after-route-changes.cy.ts @@ -0,0 +1,218 @@ +import { + CODE_NODE_NAME, + EDIT_FIELDS_SET_NODE_NAME, + IF_NODE_NAME, + INSTANCE_OWNER, + SCHEDULE_TRIGGER_NODE_NAME, +} from '../constants'; +import { + WorkflowExecutionsTab, + WorkflowPage as WorkflowPageClass, + WorkflowHistoryPage, +} from '../pages'; + +const workflowPage = new WorkflowPageClass(); +const executionsTab = new WorkflowExecutionsTab(); +const workflowHistoryPage = new WorkflowHistoryPage(); + +const createNewWorkflowAndActivate = () => { + workflowPage.actions.visit(); + workflowPage.actions.addNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME); + workflowPage.actions.saveWorkflowOnButtonClick(); + workflowPage.actions.activateWorkflow(); + cy.get('.el-notification .el-notification--error').should('not.exist'); +}; + +const editWorkflowAndDeactivate = () => { + workflowPage.getters.canvasNodePlusEndpointByName(SCHEDULE_TRIGGER_NODE_NAME).click(); + workflowPage.getters.nodeCreatorSearchBar().should('be.visible'); + workflowPage.actions.addNodeToCanvas(EDIT_FIELDS_SET_NODE_NAME, false); + cy.get('.jtk-connector').should('have.length', 1); + workflowPage.actions.saveWorkflowOnButtonClick(); + workflowPage.getters.activatorSwitch().click(); + workflowPage.actions.zoomToFit(); + cy.get('.el-notification .el-notification--error').should('not.exist'); +}; + +const editWorkflowMoreAndActivate = () => { + cy.drag(workflowPage.getters.getEndpointSelector('plus', EDIT_FIELDS_SET_NODE_NAME), [200, 200], { + realMouse: true, + }); + workflowPage.getters.nodeCreatorSearchBar().should('be.visible'); + + workflowPage.actions.addNodeToCanvas(CODE_NODE_NAME, false); + workflowPage.getters.nodeViewBackground().click(600, 200, { force: true }); + cy.get('.jtk-connector').should('have.length', 2); + workflowPage.actions.zoomToFit(); + workflowPage.actions.saveWorkflowOnButtonClick(); + + workflowPage.actions.addNodeToCanvas(IF_NODE_NAME); + workflowPage.getters.nodeViewBackground().click(600, 200, { force: true }); + cy.get('.jtk-connector').should('have.length', 2); + + const position = { + top: 0, + left: 0, + }; + workflowPage.getters + .canvasNodeByName(IF_NODE_NAME) + .click() + .then(($element) => { + position.top = $element.position().top; + position.left = $element.position().left; + }); + + cy.drag('[data-test-id="canvas-node"].jtk-drag-selected', [50, 200], { clickToFinish: true }); + workflowPage.getters + .canvasNodes() + .last() + .then(($element) => { + const finalPosition = { + top: $element.position().top, + left: $element.position().left, + }; + + expect(finalPosition.top).to.be.greaterThan(position.top); + expect(finalPosition.left).to.be.greaterThan(position.left); + }); + + cy.draganddrop( + workflowPage.getters.getEndpointSelector('output', CODE_NODE_NAME), + workflowPage.getters.getEndpointSelector('input', IF_NODE_NAME), + ); + cy.get('.jtk-connector').should('have.length', 3); + + workflowPage.actions.saveWorkflowOnButtonClick(); + workflowPage.getters.activatorSwitch().click(); + cy.get('.el-notification .el-notification--error').should('not.exist'); +}; + +const switchBetweenEditorAndHistory = () => { + workflowPage.getters.workflowHistoryButton().click(); + cy.wait(['@getHistory']); + cy.wait(['@getVersion']); + + cy.intercept('GET', '/rest/workflows/*').as('workflowGet'); + workflowHistoryPage.getters.workflowHistoryCloseButton().click(); + cy.wait(['@workflowGet']); + cy.wait(1000); + + workflowPage.getters.canvasNodes().first().should('be.visible'); + workflowPage.getters.canvasNodes().last().should('be.visible'); +}; + +const switchBetweenEditorAndWorkflowlist = () => { + cy.getByTestId('menu-item').first().click(); + cy.wait(['@getUsers', '@getWorkflows', '@getActiveWorkflows', '@getCredentials']); + + cy.getByTestId('resources-list-item').first().click(); + + workflowPage.getters.canvasNodes().first().should('be.visible'); + workflowPage.getters.canvasNodes().last().should('be.visible'); +}; + +const zoomInAndCheckNodes = () => { + cy.getByTestId('zoom-in-button').click(); + cy.getByTestId('zoom-in-button').click(); + cy.getByTestId('zoom-in-button').click(); + cy.getByTestId('zoom-in-button').click(); + + workflowPage.getters.canvasNodes().first().should('not.be.visible'); + workflowPage.getters.canvasNodes().last().should('not.be.visible'); +}; + +describe('Editor actions should work', () => { + beforeEach(() => { + cy.enableFeature('debugInEditor'); + cy.enableFeature('workflowHistory'); + cy.signin({ email: INSTANCE_OWNER.email, password: INSTANCE_OWNER.password }); + createNewWorkflowAndActivate(); + }); + + it('after saving a new workflow', () => { + editWorkflowAndDeactivate(); + editWorkflowMoreAndActivate(); + }); + + it('after switching between Editor and Executions', () => { + cy.intercept('GET', '/rest/executions?filter=*').as('getExecutions'); + cy.intercept('GET', '/rest/executions-current?filter=*').as('getCurrentExecutions'); + + executionsTab.actions.switchToExecutionsTab(); + cy.wait(['@getExecutions', '@getCurrentExecutions']); + cy.wait(500); + executionsTab.actions.switchToEditorTab(); + editWorkflowAndDeactivate(); + editWorkflowMoreAndActivate(); + }); + + it('after switching between Editor and Debug', () => { + cy.intercept('GET', '/rest/executions?filter=*').as('getExecutions'); + cy.intercept('GET', '/rest/executions/*').as('getExecution'); + cy.intercept('GET', '/rest/executions-current?filter=*').as('getCurrentExecutions'); + cy.intercept('POST', '/rest/workflows/run').as('postWorkflowRun'); + + editWorkflowAndDeactivate(); + workflowPage.actions.executeWorkflow(); + cy.wait(['@postWorkflowRun']); + + executionsTab.actions.switchToExecutionsTab(); + cy.wait(['@getExecutions', '@getCurrentExecutions']); + + executionsTab.getters.executionListItems().should('have.length', 1).first().click(); + cy.wait(['@getExecution']); + + executionsTab.getters.executionDebugButton().should('have.text', 'Copy to editor').click(); + editWorkflowMoreAndActivate(); + }); + + it('after switching between Editor and Workflow history', () => { + cy.intercept('GET', '/rest/workflow-history/workflow/*/version/*').as('getVersion'); + cy.intercept('GET', '/rest/workflow-history/workflow/*').as('getHistory'); + + editWorkflowAndDeactivate(); + workflowPage.getters.workflowHistoryButton().click(); + cy.wait(['@getHistory']); + cy.wait(['@getVersion']); + + cy.intercept('GET', '/rest/workflows/*').as('workflowGet'); + workflowHistoryPage.getters.workflowHistoryCloseButton().click(); + cy.wait(['@workflowGet']); + cy.wait(1000); + + editWorkflowMoreAndActivate(); + }); +}); + +describe('Editor zoom should work after route changes', () => { + beforeEach(() => { + cy.enableFeature('debugInEditor'); + cy.enableFeature('workflowHistory'); + cy.signin({ email: INSTANCE_OWNER.email, password: INSTANCE_OWNER.password }); + workflowPage.actions.visit(); + cy.createFixtureWorkflow('Lots_of_nodes.json', `Lots of nodes`); + workflowPage.actions.saveWorkflowOnButtonClick(); + }); + + it('after switching between Editor and Workflow history and Workflow list', () => { + cy.intercept('GET', '/rest/workflow-history/workflow/*/version/*').as('getVersion'); + cy.intercept('GET', '/rest/workflow-history/workflow/*').as('getHistory'); + cy.intercept('GET', '/rest/users').as('getUsers'); + cy.intercept('GET', '/rest/workflows').as('getWorkflows'); + cy.intercept('GET', '/rest/active-workflows').as('getActiveWorkflows'); + cy.intercept('GET', '/rest/credentials').as('getCredentials'); + + switchBetweenEditorAndHistory(); + zoomInAndCheckNodes(); + switchBetweenEditorAndHistory(); + switchBetweenEditorAndHistory(); + zoomInAndCheckNodes(); + switchBetweenEditorAndWorkflowlist(); + zoomInAndCheckNodes(); + switchBetweenEditorAndWorkflowlist(); + switchBetweenEditorAndWorkflowlist(); + zoomInAndCheckNodes(); + switchBetweenEditorAndHistory(); + switchBetweenEditorAndWorkflowlist(); + }); +}); diff --git a/cypress/e2e/30-if-node.cy.ts b/cypress/e2e/30-if-node.cy.ts new file mode 100644 index 0000000000..95ed1e9a0d --- /dev/null +++ b/cypress/e2e/30-if-node.cy.ts @@ -0,0 +1,58 @@ +import { IF_NODE_NAME } from '../constants'; +import { WorkflowPage, NDV } from '../pages'; + +const workflowPage = new WorkflowPage(); +const ndv = new NDV(); + +const FILTER_PARAM_NAME = 'conditions'; + +describe('If Node (filter component)', () => { + beforeEach(() => { + workflowPage.actions.visit(); + }); + + it('should be able to create and delete multiple conditions', () => { + workflowPage.actions.addInitialNodeToCanvas(IF_NODE_NAME, { keepNdvOpen: true }); + + // Default state + ndv.getters.filterComponent(FILTER_PARAM_NAME).should('exist'); + ndv.getters.filterConditions(FILTER_PARAM_NAME).should('have.length', 1); + ndv.getters + .filterConditionOperator(FILTER_PARAM_NAME) + .find('input') + .should('have.value', 'is equal to'); + + // Add + ndv.actions.addFilterCondition(FILTER_PARAM_NAME); + ndv.getters.filterConditionLeft(FILTER_PARAM_NAME, 0).find('input').type('first left'); + ndv.getters.filterConditionLeft(FILTER_PARAM_NAME, 1).find('input').type('second left'); + ndv.actions.addFilterCondition(FILTER_PARAM_NAME); + ndv.getters.filterConditions(FILTER_PARAM_NAME).should('have.length', 3); + + // Delete + ndv.actions.removeFilterCondition(FILTER_PARAM_NAME, 0); + ndv.getters.filterConditions(FILTER_PARAM_NAME).should('have.length', 2); + ndv.getters + .filterConditionLeft(FILTER_PARAM_NAME, 0) + .find('input') + .should('have.value', 'second left'); + ndv.actions.removeFilterCondition(FILTER_PARAM_NAME, 1); + ndv.getters.filterConditions(FILTER_PARAM_NAME).should('have.length', 1); + }); + + it('should correctly evaluate conditions', () => { + cy.fixture('Test_workflow_filter.json').then((data) => { + cy.get('body').paste(JSON.stringify(data)); + }); + + workflowPage.actions.zoomToFit(); + workflowPage.actions.executeWorkflow(); + + workflowPage.actions.openNode('Then'); + ndv.getters.outputPanel().contains('3 items').should('exist'); + ndv.actions.close(); + + workflowPage.actions.openNode('Else'); + ndv.getters.outputPanel().contains('1 item').should('exist'); + }); +}); diff --git a/cypress/e2e/30-langchain.cy.ts b/cypress/e2e/30-langchain.cy.ts new file mode 100644 index 0000000000..9140acdef2 --- /dev/null +++ b/cypress/e2e/30-langchain.cy.ts @@ -0,0 +1,278 @@ +import { + AGENT_NODE_NAME, + MANUAL_CHAT_TRIGGER_NODE_NAME, + AI_LANGUAGE_MODEL_OPENAI_CHAT_MODEL_NODE_NAME, + MANUAL_TRIGGER_NODE_NAME, + AI_MEMORY_WINDOW_BUFFER_MEMORY_NODE_NAME, + AI_TOOL_CALCULATOR_NODE_NAME, + AI_OUTPUT_PARSER_AUTO_FIXING_NODE_NAME, + AI_TOOL_CODE_NODE_NAME, + AI_TOOL_WIKIPEDIA_NODE_NAME, + BASIC_LLM_CHAIN_NODE_NAME, +} from './../constants'; +import { createMockNodeExecutionData, runMockWorkflowExcution } from '../utils'; +import { + addLanguageModelNodeToParent, + addMemoryNodeToParent, + addNodeToCanvas, + addOutputParserNodeToParent, + addToolNodeToParent, + clickManualChatButton, + navigateToNewWorkflowPage, + openNode, +} from '../composables/workflow'; +import { + clickCreateNewCredential, + clickExecuteNode, + clickGetBackToCanvas, + getOutputPanelTable, + setParameterInputByName, +} from '../composables/ndv'; +import { setCredentialValues } from '../composables/modals/credential-modal'; +import { + closeManualChatModal, + getManualChatMessages, + getManualChatModalLogs, + getManualChatModalLogsEntries, + getManualChatModalLogsTree, + sendManualChatMessage, +} from '../composables/modals/chat-modal'; + +describe('Langchain Integration', () => { + beforeEach(() => { + navigateToNewWorkflowPage(); + }); + + it('should add nodes to all Agent node input types', () => { + addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME, true); + addNodeToCanvas(AGENT_NODE_NAME, true); + + addLanguageModelNodeToParent(AI_LANGUAGE_MODEL_OPENAI_CHAT_MODEL_NODE_NAME, AGENT_NODE_NAME); + clickGetBackToCanvas(); + + addMemoryNodeToParent(AI_MEMORY_WINDOW_BUFFER_MEMORY_NODE_NAME, AGENT_NODE_NAME); + clickGetBackToCanvas(); + + addToolNodeToParent(AI_TOOL_CALCULATOR_NODE_NAME, AGENT_NODE_NAME); + clickGetBackToCanvas(); + + addOutputParserNodeToParent(AI_OUTPUT_PARSER_AUTO_FIXING_NODE_NAME, AGENT_NODE_NAME); + clickGetBackToCanvas(); + }); + + it('should add multiple tool nodes to Agent node tool input type', () => { + addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME, true); + addNodeToCanvas(AGENT_NODE_NAME, true); + + [ + AI_TOOL_CALCULATOR_NODE_NAME, + AI_TOOL_CODE_NODE_NAME, + AI_TOOL_CODE_NODE_NAME, + AI_TOOL_CODE_NODE_NAME, + AI_TOOL_WIKIPEDIA_NODE_NAME, + ].forEach((tool) => { + addToolNodeToParent(tool, AGENT_NODE_NAME); + clickGetBackToCanvas(); + }); + }); + + it('should be able to open and execute Basic LLM Chain node', () => { + addNodeToCanvas(MANUAL_CHAT_TRIGGER_NODE_NAME, true); + addNodeToCanvas(BASIC_LLM_CHAIN_NODE_NAME, true); + + addLanguageModelNodeToParent( + AI_LANGUAGE_MODEL_OPENAI_CHAT_MODEL_NODE_NAME, + BASIC_LLM_CHAIN_NODE_NAME, + ); + + clickCreateNewCredential(); + setCredentialValues({ + apiKey: 'sk_test_123', + }); + clickGetBackToCanvas(); + + openNode(BASIC_LLM_CHAIN_NODE_NAME); + + const inputMessage = 'Hello!'; + const outputMessage = 'Hi there! How can I assist you today?'; + + setParameterInputByName('prompt', inputMessage); + + runMockWorkflowExcution({ + trigger: () => clickExecuteNode(), + runData: [ + createMockNodeExecutionData(BASIC_LLM_CHAIN_NODE_NAME, { + jsonData: { + main: { output: outputMessage }, + }, + metadata: { + subRun: [{ node: AI_LANGUAGE_MODEL_OPENAI_CHAT_MODEL_NODE_NAME, runIndex: 0 }], + }, + }), + ], + lastNodeExecuted: BASIC_LLM_CHAIN_NODE_NAME, + }); + + getOutputPanelTable().should('contain', 'output'); + getOutputPanelTable().should('contain', outputMessage); + }); + + it('should be able to open and execute Agent node', () => { + addNodeToCanvas(MANUAL_CHAT_TRIGGER_NODE_NAME, true); + addNodeToCanvas(AGENT_NODE_NAME, true); + + addLanguageModelNodeToParent(AI_LANGUAGE_MODEL_OPENAI_CHAT_MODEL_NODE_NAME, AGENT_NODE_NAME); + + clickCreateNewCredential(); + setCredentialValues({ + apiKey: 'sk_test_123', + }); + clickGetBackToCanvas(); + + openNode(AGENT_NODE_NAME); + + const inputMessage = 'Hello!'; + const outputMessage = 'Hi there! How can I assist you today?'; + + setParameterInputByName('text', inputMessage); + + runMockWorkflowExcution({ + trigger: () => clickExecuteNode(), + runData: [ + createMockNodeExecutionData(AGENT_NODE_NAME, { + jsonData: { + main: { output: outputMessage }, + }, + metadata: { + subRun: [{ node: AI_LANGUAGE_MODEL_OPENAI_CHAT_MODEL_NODE_NAME, runIndex: 0 }], + }, + }), + ], + lastNodeExecuted: AGENT_NODE_NAME, + }); + + getOutputPanelTable().should('contain', 'output'); + getOutputPanelTable().should('contain', outputMessage); + }); + + it('should add and use Manual Chat Trigger node together with Agent node', () => { + addNodeToCanvas(MANUAL_CHAT_TRIGGER_NODE_NAME, true); + addNodeToCanvas(AGENT_NODE_NAME, true); + + addLanguageModelNodeToParent(AI_LANGUAGE_MODEL_OPENAI_CHAT_MODEL_NODE_NAME, AGENT_NODE_NAME); + + clickCreateNewCredential(); + setCredentialValues({ + apiKey: 'sk_test_123', + }); + clickGetBackToCanvas(); + + clickManualChatButton(); + + getManualChatModalLogs().should('not.exist'); + + const inputMessage = 'Hello!'; + const outputMessage = 'Hi there! How can I assist you today?'; + + runMockWorkflowExcution({ + trigger: () => { + sendManualChatMessage(inputMessage); + }, + runData: [ + createMockNodeExecutionData(MANUAL_CHAT_TRIGGER_NODE_NAME, { + jsonData: { + main: { input: inputMessage }, + }, + }), + createMockNodeExecutionData(AI_LANGUAGE_MODEL_OPENAI_CHAT_MODEL_NODE_NAME, { + jsonData: { + ai_languageModel: { + response: { + generations: [ + { + text: `{ + "action": "Final Answer", + "action_input": "${outputMessage}" +}`, + message: { + lc: 1, + type: 'constructor', + id: ['langchain', 'schema', 'AIMessage'], + kwargs: { + content: `{ + "action": "Final Answer", + "action_input": "${outputMessage}" +}`, + additional_kwargs: {}, + }, + }, + generationInfo: { finish_reason: 'stop' }, + }, + ], + llmOutput: { + tokenUsage: { + completionTokens: 26, + promptTokens: 519, + totalTokens: 545, + }, + }, + }, + }, + }, + inputOverride: { + ai_languageModel: [ + [ + { + json: { + messages: [ + { + lc: 1, + type: 'constructor', + id: ['langchain', 'schema', 'SystemMessage'], + kwargs: { + content: + 'Assistant is a large language model trained by OpenAI.\n\nAssistant is designed to be able to assist with a wide range of tasks, from answering simple questions to providing in-depth explanations and discussions on a wide range of topics. As a language model, Assistant is able to generate human-like text based on the input it receives, allowing it to engage in natural-sounding conversations and provide responses that are coherent and relevant to the topic at hand.\n\nAssistant is constantly learning and improving, and its capabilities are constantly evolving. It is able to process and understand large amounts of text, and can use this knowledge to provide accurate and informative responses to a wide range of questions. Additionally, Assistant is able to generate its own text based on the input it receives, allowing it to engage in discussions and provide explanations and descriptions on a wide range of topics.\n\nOverall, Assistant is a powerful system that can help with a wide range of tasks and provide valuable insights and information on a wide range of topics. Whether you need help with a specific question or just want to have a conversation about a particular topic, Assistant is here to assist. However, above all else, all responses must adhere to the format of RESPONSE FORMAT INSTRUCTIONS.', + additional_kwargs: {}, + }, + }, + { + lc: 1, + type: 'constructor', + id: ['langchain', 'schema', 'HumanMessage'], + kwargs: { + content: + 'TOOLS\n------\nAssistant can ask the user to use tools to look up information that may be helpful in answering the users original question. The tools the human can use are:\n\n\n\nRESPONSE FORMAT INSTRUCTIONS\n----------------------------\n\nOutput a JSON markdown code snippet containing a valid JSON object in one of two formats:\n\n**Option 1:**\nUse this if you want the human to use a tool.\nMarkdown code snippet formatted in the following schema:\n\n```json\n{\n "action": string, // The action to take. Must be one of []\n "action_input": string // The input to the action. May be a stringified object.\n}\n```\n\n**Option #2:**\nUse this if you want to respond directly and conversationally to the human. Markdown code snippet formatted in the following schema:\n\n```json\n{\n "action": "Final Answer",\n "action_input": string // You should put what you want to return to use here and make sure to use valid json newline characters.\n}\n```\n\nFor both options, remember to always include the surrounding markdown code snippet delimiters (begin with "```json" and end with "```")!\n\n\nUSER\'S INPUT\n--------------------\nHere is the user\'s input (remember to respond with a markdown code snippet of a json blob with a single action, and NOTHING else):\n\nHello!', + additional_kwargs: {}, + }, + }, + ], + options: { stop: ['Observation:'], promptIndex: 0 }, + }, + }, + ], + ], + }, + }), + createMockNodeExecutionData(AGENT_NODE_NAME, { + jsonData: { + main: { output: 'Hi there! How can I assist you today?' }, + }, + metadata: { + subRun: [{ node: AI_LANGUAGE_MODEL_OPENAI_CHAT_MODEL_NODE_NAME, runIndex: 0 }], + }, + }), + ], + lastNodeExecuted: AGENT_NODE_NAME, + }); + + const messages = getManualChatMessages(); + messages.should('have.length', 2); + messages.should('contain', inputMessage); + messages.should('contain', outputMessage); + + getManualChatModalLogsTree().should('be.visible'); + getManualChatModalLogsEntries().should('have.length', 1); + + closeManualChatModal(); + }); +}); diff --git a/cypress/e2e/30-workflow-filters.cy.ts b/cypress/e2e/30-workflow-filters.cy.ts new file mode 100644 index 0000000000..634f95ba06 --- /dev/null +++ b/cypress/e2e/30-workflow-filters.cy.ts @@ -0,0 +1,118 @@ +import { WorkflowsPage as WorkflowsPageClass } from '../pages/workflows'; +import { WorkflowPage as WorkflowPageClass } from '../pages/workflow'; + +import { MainSidebar } from '../pages'; +import { INSTANCE_OWNER } from '../constants'; + +const WorkflowsPage = new WorkflowsPageClass(); +const WorkflowPages = new WorkflowPageClass(); +const mainSidebar = new MainSidebar(); + +describe.skip('Workflow filters', () => { + before(() => { + cy.enableFeature('sharing', true); + }); + + beforeEach(() => { + cy.visit(WorkflowsPage.url); + }); + + it('Should filter by tags', () => { + cy.visit(WorkflowsPage.url); + WorkflowsPage.getters.newWorkflowButtonCard().click(); + cy.createFixtureWorkflow('Test_workflow_1.json', `Workflow 1`); + cy.visit(WorkflowsPage.url); + WorkflowsPage.getters.createWorkflowButton().click(); + cy.createFixtureWorkflow('Test_workflow_2.json', `Workflow 2`); + cy.visit(WorkflowsPage.url); + + WorkflowsPage.getters.workflowFilterButton().click(); + WorkflowsPage.getters.workflowTagsDropdown().click(); + WorkflowsPage.getters.workflowTagItem('other-tag-1').click(); + cy.get('body').click(0, 0); + + WorkflowsPage.getters.workflowCards().should('have.length', 1); + WorkflowsPage.getters.workflowCard('Workflow 2').should('contain.text', 'Workflow 2'); + mainSidebar.actions.goToSettings(); + cy.go('back'); + + WorkflowsPage.getters.workflowCards().should('have.length', 1); + WorkflowsPage.getters.workflowCard('Workflow 2').should('contain.text', 'Workflow 2'); + WorkflowsPage.getters.workflowResetFilters().click(); + + WorkflowsPage.getters.workflowCards().each(($el) => { + const workflowName = $el.find('[data-test-id="workflow-card-name"]').text(); + + WorkflowsPage.getters.workflowCardActions(workflowName).click(); + WorkflowsPage.getters.workflowDeleteButton().click(); + + cy.get('button').contains('delete').click(); + }); + }); + + it('Should filter by status', () => { + cy.visit(WorkflowsPage.url); + WorkflowsPage.getters.newWorkflowButtonCard().click(); + cy.createFixtureWorkflow('Test_workflow_1.json', `Workflow 1`); + cy.visit(WorkflowsPage.url); + WorkflowsPage.getters.createWorkflowButton().click(); + cy.createFixtureWorkflow('Test_workflow_3.json', `Workflow 3`); + WorkflowPages.getters.activatorSwitch().click(); + cy.visit(WorkflowsPage.url); + + WorkflowsPage.getters.workflowFilterButton().click(); + WorkflowsPage.getters.workflowStatusDropdown().click(); + WorkflowsPage.getters.workflowStatusItem('Active').click(); + cy.get('body').click(0, 0); + + WorkflowsPage.getters.workflowCards().should('have.length', 1); + WorkflowsPage.getters.workflowCard('Workflow 3').should('contain.text', 'Workflow 3'); + mainSidebar.actions.goToSettings(); + cy.go('back'); + + WorkflowsPage.getters.workflowCards().should('have.length', 1); + WorkflowsPage.getters.workflowCard('Workflow 3').should('contain.text', 'Workflow 3'); + WorkflowsPage.getters.workflowResetFilters().click(); + + WorkflowsPage.getters.workflowCards().each(($el) => { + const workflowName = $el.find('[data-test-id="workflow-card-name"]').text(); + + WorkflowsPage.getters.workflowCardActions(workflowName).click(); + WorkflowsPage.getters.workflowDeleteButton().click(); + + cy.get('button').contains('delete').click(); + }); + }); + + it('Should filter by owned by', () => { + cy.visit(WorkflowsPage.url); + + WorkflowsPage.getters.newWorkflowButtonCard().click(); + cy.createFixtureWorkflow('Test_workflow_1.json', `Workflow 1`); + cy.visit(WorkflowsPage.url); + WorkflowsPage.getters.createWorkflowButton().click(); + cy.createFixtureWorkflow('Test_workflow_3.json', `Workflow 3`); + WorkflowPages.getters.activatorSwitch().click(); + cy.visit(WorkflowsPage.url); + + WorkflowsPage.getters.workflowFilterButton().click(); + WorkflowsPage.getters.workflowOwnershipDropdown().realClick(); + WorkflowsPage.getters.workflowOwner(INSTANCE_OWNER.email).click(); + cy.get('body').click(0, 0); + + WorkflowsPage.getters.workflowCards().should('have.length', 2); + mainSidebar.actions.goToSettings(); + cy.go('back'); + + WorkflowsPage.getters.workflowResetFilters().click(); + + WorkflowsPage.getters.workflowCards().each(($el) => { + const workflowName = $el.find('[data-test-id="workflow-card-name"]').text(); + + WorkflowsPage.getters.workflowCardActions(workflowName).click(); + WorkflowsPage.getters.workflowDeleteButton().click(); + + cy.get('button').contains('delete').click(); + }); + }); +}); diff --git a/cypress/e2e/31-demo.cy.ts b/cypress/e2e/31-demo.cy.ts new file mode 100644 index 0000000000..d9397ace4e --- /dev/null +++ b/cypress/e2e/31-demo.cy.ts @@ -0,0 +1,23 @@ +import workflow from '../fixtures/Manual_wait_set.json'; +import { importWorkflow, vistDemoPage } from '../pages/demo'; +import { WorkflowPage } from '../pages/workflow'; + +const workflowPage = new WorkflowPage(); + +describe('Demo', () => { + it('can import template', () => { + vistDemoPage(); + importWorkflow(workflow); + workflowPage.getters.canvasNodes().should('have.length', 3); + }); + + it('can override theme to dark', () => { + vistDemoPage('dark'); + cy.get('body').should('have.attr', 'data-theme', 'dark'); + }); + + it('can override theme to light', () => { + vistDemoPage('light'); + cy.get('body').should('have.attr', 'data-theme', 'light'); + }); +}); diff --git a/cypress/e2e/32-node-io-filter.cy.ts b/cypress/e2e/32-node-io-filter.cy.ts new file mode 100644 index 0000000000..4dc2cc5968 --- /dev/null +++ b/cypress/e2e/32-node-io-filter.cy.ts @@ -0,0 +1,116 @@ +import { WorkflowPage as WorkflowPageClass, NDV } from '../pages'; + +const workflowPage = new WorkflowPageClass(); +const ndv = new NDV(); + +describe('Node IO Filter', () => { + beforeEach(() => { + workflowPage.actions.visit(); + cy.createFixtureWorkflow('Node_IO_filter.json', `Node IO filter`); + workflowPage.actions.saveWorkflowOnButtonClick(); + workflowPage.actions.executeWorkflow(); + }); + + it('should filter pinned data', () => { + workflowPage.getters.canvasNodes().first().dblclick(); + ndv.actions.close(); + workflowPage.getters.canvasNodes().first().dblclick(); + cy.wait(500); + ndv.getters.outputDataContainer().should('be.visible'); + cy.document().trigger('keyup', { key: '/' }); + + const searchInput = ndv.getters.searchInput(); + + searchInput.filter(':focus').should('exist'); + ndv.getters.pagination().find('li').should('have.length', 3); + cy.get('.highlight').should('not.exist'); + + searchInput.type('ar'); + ndv.getters.pagination().find('li').should('have.length', 2); + cy.get('.highlight').its('length').should('be.gt', 0); + + searchInput.type('i'); + ndv.getters.pagination().should('not.exist'); + cy.get('.highlight').its('length').should('be.gt', 0); + }); + + it.only('should filter input/output data separately', () => { + workflowPage.getters.canvasNodes().eq(1).dblclick(); + cy.wait(500); + ndv.getters.outputDataContainer().should('be.visible'); + ndv.getters.inputDataContainer().should('be.visible'); + ndv.actions.switchInputMode('Table'); + cy.document().trigger('keyup', { key: '/' }); + + ndv.getters.outputPanel().findChildByTestId('ndv-search').filter(':focus').should('not.exist'); + + let focusedInput = ndv.getters + .inputPanel() + .findChildByTestId('ndv-search') + .filter(':focus') + .should('exist'); + + const getInputPagination = () => + ndv.getters.inputPanel().findChildByTestId('ndv-data-pagination'); + const getInputCounter = () => ndv.getters.inputPanel().findChildByTestId('ndv-items-count'); + const getOuputPagination = () => + ndv.getters.outputPanel().findChildByTestId('ndv-data-pagination'); + const getOutputCounter = () => ndv.getters.outputPanel().findChildByTestId('ndv-items-count'); + + getInputPagination().find('li').should('have.length', 3); + getInputCounter().contains('21 items').should('exist'); + getOuputPagination().find('li').should('have.length', 3); + getOutputCounter().contains('21 items').should('exist'); + focusedInput.type('ar'); + + getInputPagination().find('li').should('have.length', 2); + getInputCounter().should('contain', '14 of 21 items'); + getOuputPagination().find('li').should('have.length', 3); + getOutputCounter().should('contain', '21 items'); + focusedInput.type('i'); + + getInputPagination().should('not.exist'); + getInputCounter().should('contain', '8 of 21 items'); + getOuputPagination().find('li').should('have.length', 3); + getOutputCounter().should('contain', '21 items'); + + focusedInput.clear(); + getInputPagination().find('li').should('have.length', 3); + getInputCounter().contains('21 items').should('exist'); + getOuputPagination().find('li').should('have.length', 3); + getOutputCounter().contains('21 items').should('exist'); + + ndv.getters.outputDataContainer().trigger('mouseover'); + cy.document().trigger('keyup', { key: '/' }); + ndv.getters.inputPanel().findChildByTestId('ndv-search').filter(':focus').should('not.exist'); + + focusedInput = ndv.getters + .outputPanel() + .findChildByTestId('ndv-search') + .filter(':focus') + .should('exist'); + + getInputPagination().find('li').should('have.length', 3); + getInputCounter().contains('21 items').should('exist'); + getOuputPagination().find('li').should('have.length', 3); + getOutputCounter().contains('21 items').should('exist'); + focusedInput.type('ar'); + + getInputPagination().find('li').should('have.length', 3); + getInputCounter().contains('21 items').should('exist'); + getOuputPagination().find('li').should('have.length', 2); + getOutputCounter().should('contain', '14 of 21 items'); + focusedInput.type('i'); + + getInputPagination().find('li').should('have.length', 3); + getInputCounter().contains('21 items').should('exist'); + getOuputPagination().should('not.exist'); + getOutputCounter().should('contain', '8 of 21 items'); + + focusedInput.clear(); + getInputPagination().find('li').should('have.length', 3); + getInputCounter().contains('21 items').should('exist'); + getOuputPagination().find('li').should('have.length', 3); + getOutputCounter().contains('21 items').should('exist'); + }); +}); diff --git a/cypress/e2e/32-worker-view.cy.ts b/cypress/e2e/32-worker-view.cy.ts new file mode 100644 index 0000000000..ba3edbe4c9 --- /dev/null +++ b/cypress/e2e/32-worker-view.cy.ts @@ -0,0 +1,43 @@ +import { INSTANCE_MEMBERS, INSTANCE_OWNER } from '../constants'; +import { WorkerViewPage } from '../pages'; + +const workerViewPage = new WorkerViewPage(); + +describe('Worker View (unlicensed)', () => { + beforeEach(() => { + cy.disableFeature('workerView'); + cy.disableQueueMode(); + }); + + it('should not show up in the menu sidebar', () => { + cy.signin(INSTANCE_MEMBERS[0]); + cy.visit(workerViewPage.url); + workerViewPage.getters.menuItem().should('not.exist'); + }); + + it('should show action box', () => { + cy.signin(INSTANCE_MEMBERS[0]); + cy.visit(workerViewPage.url); + workerViewPage.getters.workerViewUnlicensed().should('exist'); + }); +}); + +describe('Worker View (licensed)', () => { + beforeEach(() => { + cy.enableFeature('workerView'); + cy.enableQueueMode(); + }); + + it('should show up in the menu sidebar', () => { + cy.signin(INSTANCE_OWNER); + cy.enableQueueMode(); + cy.visit(workerViewPage.url); + workerViewPage.getters.menuItem().should('exist'); + }); + + it('should show worker list view', () => { + cy.signin(INSTANCE_MEMBERS[0]); + cy.visit(workerViewPage.url); + workerViewPage.getters.workerViewLicensed().should('exist'); + }); +}); diff --git a/cypress/e2e/33-settings-personal.cy.ts b/cypress/e2e/33-settings-personal.cy.ts new file mode 100644 index 0000000000..9257bee22d --- /dev/null +++ b/cypress/e2e/33-settings-personal.cy.ts @@ -0,0 +1,52 @@ +import { WorkflowPage } from "../pages"; + +const workflowPage = new WorkflowPage(); + +const INVALID_NAMES = [ + 'https://n8n.io', + 'http://n8n.io', + 'www.n8n.io', + 'n8n.io', + 'n8n.бг', + 'n8n.io/home', + 'n8n.io/home?send=true', + 'Jack', + '', +]; + +const VALID_NAMES = [ + ['a', 'a'], + ['alice', 'alice'], + ['Robert', 'Downey Jr.'], + ['Mia', 'Mia-Downey'], + ['Mark', "O'neil"], + ['Thomas', 'Müler'], + ['ßáçøñ', 'ßáçøñ'], + ['أحمد', 'فلسطين'], + ['Милорад', 'Филиповић'], +]; + +describe('Personal Settings', () => { + it ('should allow to change first and last name', () => { + cy.visit('/settings/personal'); + VALID_NAMES.forEach((name) => { + cy.getByTestId('personal-data-form').find('input[name="firstName"]').clear().type(name[0]); + cy.getByTestId('personal-data-form').find('input[name="lastName"]').clear().type(name[1]); + cy.getByTestId('save-settings-button').click(); + workflowPage.getters.successToast().should('contain', 'Personal details updated'); + workflowPage.getters.successToast().find('.el-notification__closeBtn').click(); + }); + }); + it('not allow malicious values for personal data', () => { + cy.visit('/settings/personal'); + INVALID_NAMES.forEach((name) => { + cy.getByTestId('personal-data-form').find('input[name="firstName"]').clear().type(name); + cy.getByTestId('personal-data-form').find('input[name="lastName"]').clear().type(name); + cy.getByTestId('save-settings-button').click(); + workflowPage.getters + .errorToast() + .should('contain', 'Malicious firstName | Malicious lastName'); + workflowPage.getters.errorToast().find('.el-notification__closeBtn').click(); + }); + }); +}); diff --git a/cypress/e2e/34-template-credentials-setup.cy.ts b/cypress/e2e/34-template-credentials-setup.cy.ts new file mode 100644 index 0000000000..863cc61c18 --- /dev/null +++ b/cypress/e2e/34-template-credentials-setup.cy.ts @@ -0,0 +1,212 @@ +import { + clickUseWorkflowButtonByTitle, + visitTemplateCollectionPage, + testData, +} from '../pages/template-collection'; +import * as templateCredentialsSetupPage from '../pages/template-credential-setup'; +import { TemplateWorkflowPage } from '../pages/template-workflow'; +import { WorkflowPage } from '../pages/workflow'; +import * as formStep from '../composables/setup-template-form-step'; +import { getSetupWorkflowCredentialsButton } from '../composables/setup-workflow-credentials-button'; +import * as setupCredsModal from '../composables/modals/workflow-credential-setup-modal'; + +const templateWorkflowPage = new TemplateWorkflowPage(); +const workflowPage = new WorkflowPage(); + +const testTemplate = templateCredentialsSetupPage.testData.simpleTemplate; + +// NodeView uses beforeunload listener that will show a browser +// native popup, which will block cypress from continuing / exiting. +// This prevent the registration of the listener. +Cypress.on('window:before:load', (win) => { + const origAddEventListener = win.addEventListener; + win.addEventListener = (eventName: string, listener: any, opts: any) => { + if (eventName === 'beforeunload') { + return; + } + + return origAddEventListener.call(win, eventName, listener, opts); + }; +}); + +describe('Template credentials setup', () => { + beforeEach(() => { + cy.intercept('GET', `https://api.n8n.io/api/templates/workflows/${testTemplate.id}`, { + fixture: testTemplate.fixture, + }); + }); + + it('can be opened from template workflow page', () => { + templateWorkflowPage.actions.visit(testTemplate.id); + templateCredentialsSetupPage.enableTemplateCredentialSetupFeatureFlag(); + templateWorkflowPage.getters.useTemplateButton().should('be.visible'); + templateCredentialsSetupPage.enableTemplateCredentialSetupFeatureFlag(); + templateWorkflowPage.actions.clickUseThisWorkflowButton(); + + templateCredentialsSetupPage.getters + .title(`Set up 'Promote new Shopify products on Twitter and Telegram' template`) + .should('be.visible'); + }); + + it('can be opened from template collection page', () => { + visitTemplateCollectionPage(testData.ecommerceStarterPack); + templateCredentialsSetupPage.enableTemplateCredentialSetupFeatureFlag(); + clickUseWorkflowButtonByTitle('Promote new Shopify products on Twitter and Telegram'); + + templateCredentialsSetupPage.getters + .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(`Set up 'Promote new Shopify products on Twitter and Telegram' template`) + .should('be.visible'); + + templateCredentialsSetupPage.getters + .infoCallout() + .should( + 'contain.text', + 'You need 1x Shopify, 1x X (Formerly Twitter) and 1x Telegram account to setup this template', + ); + + const expectedAppNames = ['1. Shopify', '2. X (Formerly Twitter)', '3. Telegram']; + const expectedAppDescriptions = [ + 'The credential you select will be used in the product created node of the workflow template.', + 'The credential you select will be used in the Twitter node of the workflow template.', + 'The credential you select will be used in the Telegram node of the workflow template.', + ]; + + formStep.getFormStep().each(($el, index) => { + formStep.getStepHeading($el).should('have.text', expectedAppNames[index]); + formStep.getStepDescription($el).should('have.text', expectedAppDescriptions[index]); + }); + }); + + it('can skip template creation', () => { + templateCredentialsSetupPage.visitTemplateCredentialSetupPage(testTemplate.id); + + templateCredentialsSetupPage.getters.skipLink().click(); + workflowPage.getters.canvasNodes().should('have.length', 3); + }); + + it('can create credentials and workflow from the template', () => { + templateCredentialsSetupPage.visitTemplateCredentialSetupPage(testTemplate.id); + + // Continue button should be disabled if no credentials are created + templateCredentialsSetupPage.getters.continueButton().should('be.disabled'); + + templateCredentialsSetupPage.fillInDummyCredentialsForApp('Shopify'); + + // Continue button should be enabled if at least one has been created + templateCredentialsSetupPage.getters.continueButton().should('be.enabled'); + + templateCredentialsSetupPage.fillInDummyCredentialsForAppWithConfirm('X (Formerly Twitter)'); + templateCredentialsSetupPage.fillInDummyCredentialsForApp('Telegram'); + + templateCredentialsSetupPage.finishCredentialSetup(); + + workflowPage.getters.canvasNodes().should('have.length', 3); + + // Focus the canvas so the copy to clipboard works + workflowPage.getters.canvasNodes().eq(0).realClick(); + workflowPage.actions.selectAll(); + workflowPage.actions.hitCopy(); + + cy.grantBrowserPermissions('clipboardReadWrite', 'clipboardSanitizedWrite'); + // Check workflow JSON by copying it to clipboard + cy.readClipboard().then((workflowJSON) => { + const workflow = JSON.parse(workflowJSON); + + expect(workflow.meta).to.haveOwnProperty('templateId', testTemplate.id.toString()); + workflow.nodes.forEach((node: any) => { + expect(Object.keys(node.credentials ?? {})).to.have.lengthOf(1); + }); + }); + }); + + it('should work with a template that has no credentials (ADO-1603)', () => { + const templateWithoutCreds = templateCredentialsSetupPage.testData.templateWithoutCredentials; + cy.intercept('GET', `https://api.n8n.io/api/templates/workflows/${templateWithoutCreds.id}`, { + fixture: templateWithoutCreds.fixture, + }); + templateCredentialsSetupPage.visitTemplateCredentialSetupPage(templateWithoutCreds.id); + + const expectedAppNames = ['1. Email (IMAP)', '2. Nextcloud']; + const expectedAppDescriptions = [ + 'The credential you select will be used in the IMAP Email node of the workflow template.', + 'The credential you select will be used in the Nextcloud node of the workflow template.', + ]; + + formStep.getFormStep().each(($el, index) => { + formStep.getStepHeading($el).should('have.text', expectedAppNames[index]); + formStep.getStepDescription($el).should('have.text', expectedAppDescriptions[index]); + }); + + templateCredentialsSetupPage.getters.continueButton().should('be.disabled'); + + templateCredentialsSetupPage.fillInDummyCredentialsForApp('Email (IMAP)'); + templateCredentialsSetupPage.fillInDummyCredentialsForApp('Nextcloud'); + + templateCredentialsSetupPage.finishCredentialSetup(); + + workflowPage.getters.canvasNodes().should('have.length', 3); + }); + + describe('Credential setup from workflow editor', () => { + beforeEach(() => { + cy.resetDatabase(); + cy.signinAsOwner(); + }); + + it('should allow credential setup from workflow editor if user skips it during template setup', () => { + templateCredentialsSetupPage.visitTemplateCredentialSetupPage(testTemplate.id); + templateCredentialsSetupPage.getters.skipLink().click(); + + getSetupWorkflowCredentialsButton().should('be.visible'); + }); + + it('should allow credential setup from workflow editor if user fills in credentials partially during template setup', () => { + templateCredentialsSetupPage.visitTemplateCredentialSetupPage(testTemplate.id); + templateCredentialsSetupPage.fillInDummyCredentialsForApp('Shopify'); + + templateCredentialsSetupPage.finishCredentialSetup(); + + getSetupWorkflowCredentialsButton().should('be.visible'); + }); + + it('should fill credentials from workflow editor', () => { + templateCredentialsSetupPage.visitTemplateCredentialSetupPage(testTemplate.id); + templateCredentialsSetupPage.getters.skipLink().click(); + + getSetupWorkflowCredentialsButton().click(); + setupCredsModal.getWorkflowCredentialsModal().should('be.visible'); + + templateCredentialsSetupPage.fillInDummyCredentialsForApp('Shopify'); + templateCredentialsSetupPage.fillInDummyCredentialsForAppWithConfirm('X (Formerly Twitter)'); + templateCredentialsSetupPage.fillInDummyCredentialsForApp('Telegram'); + + setupCredsModal.closeModalFromContinueButton(); + setupCredsModal.getWorkflowCredentialsModal().should('not.exist'); + + // Focus the canvas so the copy to clipboard works + workflowPage.getters.canvasNodes().eq(0).realClick(); + workflowPage.actions.selectAll(); + workflowPage.actions.hitCopy(); + + cy.grantBrowserPermissions('clipboardReadWrite', 'clipboardSanitizedWrite'); + // Check workflow JSON by copying it to clipboard + cy.readClipboard().then((workflowJSON) => { + const workflow = JSON.parse(workflowJSON); + + workflow.nodes.forEach((node: any) => { + expect(Object.keys(node.credentials ?? {})).to.have.lengthOf(1); + }); + }); + + getSetupWorkflowCredentialsButton().should('not.exist'); + }); + }); +}); diff --git a/cypress/e2e/35-admin-user-smoke-test.cy.ts b/cypress/e2e/35-admin-user-smoke-test.cy.ts new file mode 100644 index 0000000000..05e70aa339 --- /dev/null +++ b/cypress/e2e/35-admin-user-smoke-test.cy.ts @@ -0,0 +1,23 @@ +import { INSTANCE_ADMIN, INSTANCE_OWNER } from '../constants'; +import { SettingsPage } from '../pages/settings'; + +const settingsPage = new SettingsPage(); + +describe('Admin user', { disableAutoLogin: true }, () => { + it('should see same Settings sub menu items as instance owner', () => { + cy.signin(INSTANCE_OWNER); + cy.visit(settingsPage.url); + + let ownerMenuItems = 0; + + settingsPage.getters.menuItems().then(($el) => { + ownerMenuItems = $el.length; + }); + + cy.signout(); + cy.signin(INSTANCE_ADMIN); + cy.visit(settingsPage.url); + + settingsPage.getters.menuItems().should('have.length', ownerMenuItems); + }); +}); diff --git a/cypress/e2e/36-suggested-templates.cy.ts b/cypress/e2e/36-suggested-templates.cy.ts new file mode 100644 index 0000000000..897714a835 --- /dev/null +++ b/cypress/e2e/36-suggested-templates.cy.ts @@ -0,0 +1,143 @@ +import { WorkflowsPage as WorkflowsPageClass } from '../pages/workflows'; +import { WorkflowPage as WorkflowPageClass } from '../pages/workflow'; + +type SuggestedTemplatesStub = { + sections: SuggestedTemplatesSectionStub[]; +} + +type SuggestedTemplatesSectionStub = { + name: string; + title: string; + description: string; + workflows: Array; +}; + +const WorkflowsListPage = new WorkflowsPageClass(); +const WorkflowPage = new WorkflowPageClass(); + +let fixtureSections: SuggestedTemplatesStub = { sections: [] };; + +describe('Suggested templates - Should render', () => { + + before(() => { + cy.fixture('Suggested_Templates.json').then((data) => { + fixtureSections = data; + }); + }); + + beforeEach(() => { + localStorage.removeItem('SHOW_N8N_SUGGESTED_TEMPLATES'); + cy.intercept('GET', '/rest/settings', (req) => { + req.on('response', (res) => { + res.send({ + data: { ...res.body.data, deployment: { type: 'cloud' } }, + }); + }); + }).as('loadSettings'); + cy.intercept('GET', '/rest/cloud/proxy/templates', { + fixture: 'Suggested_Templates.json', + }); + cy.visit(WorkflowsListPage.url); + cy.wait('@loadSettings'); + }); + + it('should render suggested templates page in empty workflow list', () => { + WorkflowsListPage.getters.suggestedTemplatesPageContainer().should('exist'); + WorkflowsListPage.getters.suggestedTemplatesCards().should('have.length', fixtureSections.sections[0].workflows.length); + WorkflowsListPage.getters.suggestedTemplatesSectionDescription().should('contain', fixtureSections.sections[0].description); + }); + + it('should render suggested templates when there are workflows in the list', () => { + WorkflowsListPage.getters.suggestedTemplatesNewWorkflowButton().click(); + cy.createFixtureWorkflow('Test_workflow_1.json', 'Test Workflow'); + cy.visit(WorkflowsListPage.url); + WorkflowsListPage.getters.suggestedTemplatesSectionContainer().should('exist'); + cy.contains(`Explore ${fixtureSections.sections[0].name.toLocaleLowerCase()} workflow templates`).should('exist'); + WorkflowsListPage.getters.suggestedTemplatesCards().should('have.length', fixtureSections.sections[0].workflows.length); + }); + + it('should enable users to signup for suggested templates templates', () => { + // Test the whole flow + WorkflowsListPage.getters.suggestedTemplatesCards().first().click(); + WorkflowsListPage.getters.suggestedTemplatesPreviewModal().should('exist'); + WorkflowsListPage.getters.suggestedTemplatesUseTemplateButton().click(); + cy.url().should('include', '/workflow/new'); + WorkflowPage.getters.infoToast().should('contain', 'Template coming soon!'); + WorkflowPage.getters.infoToast().contains('Notify me when it\'s available').click(); + WorkflowPage.getters.successToast().should('contain', 'We will contact you via email once this template is released.'); + cy.visit(WorkflowsListPage.url); + // Once users have signed up for a template, suggestions should not be shown again + WorkflowsListPage.getters.suggestedTemplatesSectionContainer().should('not.exist'); + }); + +}); + +describe('Suggested templates - Should not render', () => { + beforeEach(() => { + localStorage.removeItem('SHOW_N8N_SUGGESTED_TEMPLATES'); + cy.visit(WorkflowsListPage.url); + }); + + it('should not render suggested templates templates if not in cloud deployment', () => { + cy.intercept('GET', '/rest/settings', (req) => { + req.on('response', (res) => { + res.send({ + data: { ...res.body.data, deployment: { type: 'notCloud' } }, + }); + }); + }); + WorkflowsListPage.getters.suggestedTemplatesPageContainer().should('not.exist'); + WorkflowsListPage.getters.suggestedTemplatesSectionContainer().should('not.exist'); + }); + + it('should not render suggested templates templates if endpoint throws error', () => { + cy.intercept('GET', '/rest/settings', (req) => { + req.on('response', (res) => { + res.send({ + data: { ...res.body.data, deployment: { type: 'cloud' } }, + }); + }); + }); + cy.intercept('GET', '/rest/cloud/proxy/templates', { statusCode: 500 }).as('loadTemplates'); + WorkflowsListPage.getters.suggestedTemplatesPageContainer().should('not.exist'); + WorkflowsListPage.getters.suggestedTemplatesSectionContainer().should('not.exist'); + }); + + it('should not render suggested templates templates if endpoint returns empty list', () => { + cy.intercept('GET', '/rest/settings', (req) => { + req.on('response', (res) => { + res.send({ + data: { ...res.body.data, deployment: { type: 'cloud' } }, + }); + }); + }); + cy.intercept('GET', '/rest/cloud/proxy/templates', (req) => { + req.on('response', (res) => { + res.send({ + data: { collections: [] }, + }); + }); + }); + WorkflowsListPage.getters.suggestedTemplatesPageContainer().should('not.exist'); + WorkflowsListPage.getters.suggestedTemplatesSectionContainer().should('not.exist'); + }); + + it('should not render suggested templates templates if endpoint returns invalid response', () => { + cy.intercept('GET', '/rest/settings', (req) => { + req.on('response', (res) => { + res.send({ + data: { ...res.body.data, deployment: { type: 'cloud' } }, + }); + }); + }); + cy.intercept('GET', '/rest/cloud/proxy/templates', (req) => { + req.on('response', (res) => { + res.send({ + data: { somethingElse: [] }, + }); + }); + }); + WorkflowsListPage.getters.suggestedTemplatesPageContainer().should('not.exist'); + WorkflowsListPage.getters.suggestedTemplatesSectionContainer().should('not.exist'); + }); +}); diff --git a/cypress/e2e/36-versions.cy.ts b/cypress/e2e/36-versions.cy.ts new file mode 100644 index 0000000000..2d93223ebb --- /dev/null +++ b/cypress/e2e/36-versions.cy.ts @@ -0,0 +1,66 @@ +import { INSTANCE_OWNER } from '../constants'; +import { WorkflowsPage } from '../pages/workflows'; +import { + closeVersionUpdatesPanel, + getVersionCard, + getVersionUpdatesPanelOpenButton, + openVersionUpdatesPanel, +} from '../composables/versions'; + +const workflowsPage = new WorkflowsPage(); + +describe('Versions', () => { + it('should open updates panel', () => { + cy.intercept('GET', '/rest/settings', (req) => { + req.continue((res) => { + if (res.body.hasOwnProperty('data')) { + res.body.data = { + ...res.body.data, + releaseChannel: 'stable', + versionCli: '1.0.0', + versionNotifications: { + enabled: true, + endpoint: 'https://api.n8n.io/api/versions/', + infoUrl: 'https://docs.n8n.io/getting-started/installation/updating.html', + }, + }; + } + }); + }).as('settings'); + + cy.intercept('GET', 'https://api.n8n.io/api/versions/1.0.0', [ + { + name: '1.3.1', + createdAt: '2023-08-18T11:53:12.857Z', + hasSecurityIssue: null, + hasSecurityFix: null, + securityIssueFixVersion: null, + hasBreakingChange: null, + documentationUrl: 'https://docs.n8n.io/release-notes/#n8n131', + nodes: [], + description: 'Includes bug fixes', + }, + { + name: '1.0.5', + createdAt: '2023-07-24T10:54:56.097Z', + hasSecurityIssue: false, + hasSecurityFix: null, + securityIssueFixVersion: null, + hasBreakingChange: true, + documentationUrl: 'https://docs.n8n.io/release-notes/#n8n104', + nodes: [], + description: 'Includes core functionality and bug fixes', + }, + ]); + + cy.signin(INSTANCE_OWNER); + + cy.visit(workflowsPage.url); + cy.wait('@settings'); + + getVersionUpdatesPanelOpenButton().should('contain', '2 updates'); + openVersionUpdatesPanel(); + getVersionCard().should('have.length', 2); + closeVersionUpdatesPanel(); + }); +}); diff --git a/cypress/e2e/4-node-creator.cy.ts b/cypress/e2e/4-node-creator.cy.ts index 8f9fbde4d3..90ea78c1fb 100644 --- a/cypress/e2e/4-node-creator.cy.ts +++ b/cypress/e2e/4-node-creator.cy.ts @@ -2,6 +2,7 @@ import { NodeCreator } from '../pages/features/node-creator'; import { WorkflowPage as WorkflowPageClass } from '../pages/workflow'; import { NDV } from '../pages/ndv'; import { getVisibleSelect } from '../utils'; +import { IF_NODE_NAME } from '../constants'; const nodeCreatorFeature = new NodeCreator(); const WorkflowPage = new WorkflowPageClass(); @@ -34,7 +35,7 @@ describe('Node Creator', () => { nodeCreatorFeature.actions.openNodeCreator(); nodeCreatorFeature.getters.searchBar().find('input').type('manual'); - nodeCreatorFeature.getters.creatorItem().should('have.length', 1); + nodeCreatorFeature.getters.creatorItem().should('have.length', 2); nodeCreatorFeature.getters.searchBar().find('input').clear().type('manual123'); nodeCreatorFeature.getters.creatorItem().should('have.length', 0); nodeCreatorFeature.getters @@ -101,15 +102,15 @@ describe('Node Creator', () => { nodeCreatorFeature.getters.searchBar().find('input').type('{rightarrow}'); nodeCreatorFeature.getters.activeSubcategory().should('have.text', 'FTP'); nodeCreatorFeature.getters.searchBar().find('input').clear().type('file'); - // Navigate to rename action which should be the 4th item - nodeCreatorFeature.getters.searchBar().find('input').type('{uparrow}{rightarrow}'); + // The 1st trigger is selected, up 1x to the collapsable header, up 2x to the last action (rename) + nodeCreatorFeature.getters.searchBar().find('input').type('{uparrow}{uparrow}{rightarrow}'); NDVModal.getters.parameterInput('operation').find('input').should('have.value', 'Rename'); }); it('should not show actions for single action nodes', () => { const singleActionNodes = [ 'DHL', - 'iCalendar', + 'Edit Fields', 'LingvaNex', 'Mailcheck', 'MSG91', @@ -307,7 +308,7 @@ describe('Node Creator', () => { nodeCreatorFeature.getters.getCategoryItem('Actions').click(); nodeCreatorFeature.getters.getCreatorItem('Create a credential').click(); NDVModal.actions.close(); - WorkflowPage.actions.deleteNode('When clicking "Execute Workflow"'); + WorkflowPage.actions.deleteNode('When clicking "Test Workflow"'); WorkflowPage.getters.canvasNodePlusEndpointByName('n8n').click(); nodeCreatorFeature.getters.searchBar().find('input').clear().type('n8n'); nodeCreatorFeature.getters.getCreatorItem('n8n').click(); @@ -316,7 +317,7 @@ describe('Node Creator', () => { NDVModal.actions.close(); WorkflowPage.getters.canvasNodes().should('have.length', 2); WorkflowPage.actions.zoomToFit(); - WorkflowPage.actions.addNodeBetweenNodes('n8n', 'n8n1', 'Item Lists', 'Summarize'); + WorkflowPage.actions.addNodeBetweenNodes('n8n', 'n8n1', 'Summarize'); WorkflowPage.getters.canvasNodes().should('have.length', 3); }); }); @@ -360,7 +361,7 @@ describe('Node Creator', () => { nodeCreatorFeature.getters.nodeItemName().first().should('have.text', 'Edit Fields (Set)'); nodeCreatorFeature.getters.searchBar().find('input').clear().type('i'); - nodeCreatorFeature.getters.nodeItemName().first().should('have.text', 'IF'); + nodeCreatorFeature.getters.nodeItemName().first().should('have.text', IF_NODE_NAME); nodeCreatorFeature.getters.nodeItemName().eq(1).should('have.text', 'Switch'); nodeCreatorFeature.getters.searchBar().find('input').clear().type('sw'); @@ -368,11 +369,11 @@ describe('Node Creator', () => { nodeCreatorFeature.getters.nodeItemName().first().should('have.text', 'Edit Fields (Set)'); nodeCreatorFeature.getters.searchBar().find('input').clear().type('i'); - nodeCreatorFeature.getters.nodeItemName().first().should('have.text', 'IF'); + nodeCreatorFeature.getters.nodeItemName().first().should('have.text', IF_NODE_NAME); nodeCreatorFeature.getters.nodeItemName().eq(1).should('have.text', 'Switch'); nodeCreatorFeature.getters.searchBar().find('input').clear().type('IF'); - nodeCreatorFeature.getters.nodeItemName().first().should('have.text', 'IF'); + nodeCreatorFeature.getters.nodeItemName().first().should('have.text', IF_NODE_NAME); nodeCreatorFeature.getters.nodeItemName().eq(1).should('have.text', 'Switch'); nodeCreatorFeature.getters.searchBar().find('input').clear().type('sw'); @@ -410,7 +411,7 @@ describe('Node Creator', () => { nodeCreatorFeature.getters.searchBar().find('input').clear().type('js'); nodeCreatorFeature.getters.nodeItemName().first().should('have.text', 'Code'); - nodeCreatorFeature.getters.nodeItemName().eq(1).should('have.text', 'Item Lists'); + nodeCreatorFeature.getters.nodeItemName().eq(1).should('have.text', 'Edit Fields (Set)'); nodeCreatorFeature.getters.searchBar().find('input').clear().type('fi'); nodeCreatorFeature.getters.nodeItemName().first().should('have.text', 'Filter'); @@ -478,15 +479,14 @@ describe('Node Creator', () => { nodeCreatorFeature.getters.searchBar().find('input').clear().type('wa'); nodeCreatorFeature.getters.nodeItemName().first().should('have.text', 'Wait'); - nodeCreatorFeature.getters.nodeItemName().eq(1).should('have.text', 'Merge'); nodeCreatorFeature.getters.searchBar().find('input').clear().type('wait'); nodeCreatorFeature.getters.nodeItemName().first().should('have.text', 'Wait'); - nodeCreatorFeature.getters.nodeItemName().eq(1).should('have.text', 'Merge'); nodeCreatorFeature.getters.searchBar().find('input').clear().type('spreadsheet'); - nodeCreatorFeature.getters.nodeItemName().first().should('have.text', 'Spreadsheet File'); - nodeCreatorFeature.getters.nodeItemName().eq(1).should('have.text', 'Google Sheets'); + nodeCreatorFeature.getters.nodeItemName().first().should('have.text', 'Convert to File'); + nodeCreatorFeature.getters.nodeItemName().eq(1).should('have.text', 'Extract From File'); + nodeCreatorFeature.getters.nodeItemName().eq(2).should('have.text', 'Google Sheets'); nodeCreatorFeature.getters.searchBar().find('input').clear().type('sheets'); nodeCreatorFeature.getters.nodeItemName().first().should('have.text', 'Google Sheets'); diff --git a/cypress/e2e/5-ndv.cy.ts b/cypress/e2e/5-ndv.cy.ts index 956d6470f5..4fc69f8fde 100644 --- a/cypress/e2e/5-ndv.cy.ts +++ b/cypress/e2e/5-ndv.cy.ts @@ -1,7 +1,7 @@ -import { WorkflowPage, NDV } from '../pages'; import { v4 as uuid } from 'uuid'; -import { getPopper, getVisiblePopper, getVisibleSelect } from '../utils'; -import { META_KEY } from '../constants'; +import { getVisibleSelect } from '../utils'; +import { MANUAL_TRIGGER_NODE_DISPLAY_NAME } from '../constants'; +import { NDV, WorkflowPage } from '../pages'; const workflowPage = new WorkflowPage(); const ndv = new NDV(); @@ -42,7 +42,7 @@ describe('NDV', () => { ndv.getters.outputDisplayMode().should('have.length.at.least', 1).and('be.visible'); }); - it('should change input', () => { + it('should change input and go back to canvas', () => { cy.createFixtureWorkflow('NDV-test-select-input.json', `NDV test select input ${uuid()}`); workflowPage.actions.zoomToFit(); workflowPage.getters.canvasNodes().last().dblclick(); @@ -50,6 +50,9 @@ describe('NDV', () => { ndv.getters.inputOption().last().click(); ndv.getters.inputDataContainer().find('[class*=schema_]').should('exist'); ndv.getters.inputDataContainer().should('contain', 'start'); + ndv.getters.backToCanvas().click(); + ndv.getters.container().should('not.be.visible'); + cy.shouldNotHaveConsoleErrors(); }); it('should show correct validation state for resource locator params', () => { @@ -68,10 +71,10 @@ describe('NDV', () => { workflowPage.actions.addNodeToCanvas('Manual'); workflowPage.actions.addNodeToCanvas('Airtable', true, true, 'Search records'); ndv.getters.container().should('be.visible'); - // cy.get('.has-issues').should('have.length', 0); + cy.get('.has-issues').should('have.length', 0); ndv.getters.parameterInput('table').find('input').eq(1).focus().blur(); ndv.getters.parameterInput('base').find('input').eq(1).focus().blur(); - cy.get('.has-issues').should('have.length', 0); + cy.get('.has-issues').should('have.length', 2); ndv.getters.backToCanvas().click(); workflowPage.actions.openNode('Airtable'); cy.get('.has-issues').should('have.length', 2); @@ -299,11 +302,11 @@ describe('NDV', () => { ndv.actions.setInvalidExpression({ fieldName: 'fieldId', delay: 200 }); - ndv.getters.container().click(); // remove focus from input, hide expression preview + ndv.getters.nodeParameters().click(); // remove focus from input, hide expression preview ndv.getters.parameterInput('remoteOptions').click(); - ndv.getters.parameterInputIssues('remoteOptions').realHover(); + ndv.getters.parameterInputIssues('remoteOptions').realHover({ scrollBehavior: false}); // Remote options dropdown should not be visible ndv.getters.parameterInput('remoteOptions').find('.el-select').should('not.exist'); }); @@ -317,7 +320,7 @@ describe('NDV', () => { ndv.actions.setInvalidExpression({ fieldName: 'otherField', delay: 50 }); - ndv.getters.container().click(); // remove focus from input, hide expression preview + ndv.getters.nodeParameters().click(); // remove focus from input, hide expression preview ndv.getters.parameterInput('remoteOptions').click(); getVisibleSelect().find('.el-select-dropdown__item').should('have.length', 3); @@ -357,12 +360,253 @@ describe('NDV', () => { ndv.getters.nodeExecuteButton().should('be.visible'); }); + it('should allow editing code in fullscreen in the Code node', () => { + workflowPage.actions.addInitialNodeToCanvas('Code', { keepNdvOpen: true }); + ndv.actions.openCodeEditorFullscreen(); + + ndv.getters.codeEditorFullscreen().type('{selectall}').type('{backspace}').type('foo()'); + ndv.getters.codeEditorFullscreen().should('contain.text', 'foo()'); + cy.wait(200); + ndv.getters.codeEditorDialog().find('.el-dialog__close').click(); + ndv.getters.parameterInput('jsCode').get('.cm-content').should('contain.text', 'foo()'); + }); + it('should not retrieve remote options when a parameter value changes', () => { - cy.intercept('/rest/node-parameter-options?**', cy.spy().as('fetchParameterOptions')); + cy.intercept('/rest/dynamic-node-parameters/options?**', cy.spy().as('fetchParameterOptions')); workflowPage.actions.addInitialNodeToCanvas('E2e Test', { action: 'Remote Options' }); // Type something into the field ndv.actions.typeIntoParameterInput('otherField', 'test'); // Should call the endpoint only once (on mount), not for every keystroke cy.get('@fetchParameterOptions').should('have.been.calledOnce'); }); + + describe('floating nodes', () => { + function getFloatingNodeByPosition( + position: 'inputMain' | 'outputMain' | 'outputSub' | 'inputSub', + ) { + return cy.get(`[data-node-placement=${position}]`); + } + beforeEach(() => { + cy.createFixtureWorkflow('Floating_Nodes.json', `Floating Nodes`); + workflowPage.getters.canvasNodes().first().dblclick(); + getFloatingNodeByPosition('inputMain').should('not.exist'); + getFloatingNodeByPosition('outputMain').should('exist'); + }); + + it('should traverse floating nodes with mouse', () => { + // Traverse 4 connected node forwards + Array.from(Array(4).keys()).forEach((i) => { + getFloatingNodeByPosition('outputMain').click({ force: true }); + ndv.getters.nodeNameContainer().should('contain', `Node ${i + 1}`); + getFloatingNodeByPosition('inputMain').should('exist'); + getFloatingNodeByPosition('outputMain').should('exist'); + ndv.actions.close(); + workflowPage.getters.selectedNodes().should('have.length', 1); + workflowPage.getters + .selectedNodes() + .first() + .should('contain', `Node ${i + 1}`); + workflowPage.getters.selectedNodes().first().dblclick(); + }); + + getFloatingNodeByPosition('outputMain').click({ force: true }); + ndv.getters.nodeNameContainer().should('contain', 'Chain'); + getFloatingNodeByPosition('inputSub').should('exist'); + getFloatingNodeByPosition('inputSub').click({ force: true }); + ndv.getters.nodeNameContainer().should('contain', 'Model'); + getFloatingNodeByPosition('inputSub').should('not.exist'); + getFloatingNodeByPosition('inputMain').should('not.exist'); + getFloatingNodeByPosition('outputMain').should('not.exist'); + getFloatingNodeByPosition('outputSub').should('exist'); + ndv.actions.close(); + workflowPage.getters.selectedNodes().should('have.length', 1); + workflowPage.getters.selectedNodes().first().should('contain', 'Model'); + workflowPage.getters.selectedNodes().first().dblclick(); + getFloatingNodeByPosition('outputSub').click({ force: true }); + ndv.getters.nodeNameContainer().should('contain', 'Chain'); + + // Traverse 4 connected node backwards + Array.from(Array(4).keys()).forEach((i) => { + getFloatingNodeByPosition('inputMain').click({ force: true }); + ndv.getters.nodeNameContainer().should('contain', `Node ${4 - i}`); + getFloatingNodeByPosition('outputMain').should('exist'); + getFloatingNodeByPosition('inputMain').should('exist'); + }); + getFloatingNodeByPosition('inputMain').click({ force: true }); + workflowPage.getters + .selectedNodes() + .first() + .should('contain', MANUAL_TRIGGER_NODE_DISPLAY_NAME); + getFloatingNodeByPosition('inputMain').should('not.exist'); + getFloatingNodeByPosition('inputSub').should('not.exist'); + getFloatingNodeByPosition('outputSub').should('not.exist'); + ndv.actions.close(); + workflowPage.getters.selectedNodes().should('have.length', 1); + workflowPage.getters + .selectedNodes() + .first() + .should('contain', MANUAL_TRIGGER_NODE_DISPLAY_NAME); + }); + + it('should traverse floating nodes with mouse', () => { + // Traverse 4 connected node forwards + Array.from(Array(4).keys()).forEach((i) => { + cy.realPress(['ShiftLeft', 'Meta', 'AltLeft', 'ArrowRight']); + ndv.getters.nodeNameContainer().should('contain', `Node ${i + 1}`); + getFloatingNodeByPosition('inputMain').should('exist'); + getFloatingNodeByPosition('outputMain').should('exist'); + ndv.actions.close(); + workflowPage.getters.selectedNodes().should('have.length', 1); + workflowPage.getters + .selectedNodes() + .first() + .should('contain', `Node ${i + 1}`); + workflowPage.getters.selectedNodes().first().dblclick(); + }); + + cy.realPress(['ShiftLeft', 'Meta', 'AltLeft', 'ArrowRight']); + ndv.getters.nodeNameContainer().should('contain', 'Chain'); + getFloatingNodeByPosition('inputSub').should('exist'); + cy.realPress(['ShiftLeft', 'Meta', 'AltLeft', 'ArrowDown']); + ndv.getters.nodeNameContainer().should('contain', 'Model'); + getFloatingNodeByPosition('inputSub').should('not.exist'); + getFloatingNodeByPosition('inputMain').should('not.exist'); + getFloatingNodeByPosition('outputMain').should('not.exist'); + getFloatingNodeByPosition('outputSub').should('exist'); + ndv.actions.close(); + workflowPage.getters.selectedNodes().should('have.length', 1); + workflowPage.getters.selectedNodes().first().should('contain', 'Model'); + workflowPage.getters.selectedNodes().first().dblclick(); + cy.realPress(['ShiftLeft', 'Meta', 'AltLeft', 'ArrowUp']); + ndv.getters.nodeNameContainer().should('contain', 'Chain'); + + // Traverse 4 connected node backwards + Array.from(Array(4).keys()).forEach((i) => { + cy.realPress(['ShiftLeft', 'Meta', 'AltLeft', 'ArrowLeft']); + ndv.getters.nodeNameContainer().should('contain', `Node ${4 - i}`); + getFloatingNodeByPosition('outputMain').should('exist'); + getFloatingNodeByPosition('inputMain').should('exist'); + }); + cy.realPress(['ShiftLeft', 'Meta', 'AltLeft', 'ArrowLeft']); + workflowPage.getters + .selectedNodes() + .first() + .should('contain', MANUAL_TRIGGER_NODE_DISPLAY_NAME); + getFloatingNodeByPosition('inputMain').should('not.exist'); + getFloatingNodeByPosition('inputSub').should('not.exist'); + getFloatingNodeByPosition('outputSub').should('not.exist'); + ndv.actions.close(); + workflowPage.getters.selectedNodes().should('have.length', 1); + workflowPage.getters + .selectedNodes() + .first() + .should('contain', MANUAL_TRIGGER_NODE_DISPLAY_NAME); + }); + }); + + it('should show node name and version in settings', () => { + cy.createFixtureWorkflow('Test_workflow_ndv_version.json', `NDV test version ${uuid()}`); + + workflowPage.actions.openNode('Edit Fields (old)'); + ndv.actions.openSettings(); + ndv.getters.nodeVersion().should('have.text', 'Set node version 2 (Latest version: 3.2)'); + ndv.actions.close(); + + workflowPage.actions.openNode('Edit Fields (latest)'); + ndv.actions.openSettings(); + ndv.getters.nodeVersion().should('have.text', 'Edit Fields (Set) node version 3.2 (Latest)'); + ndv.actions.close(); + + workflowPage.actions.openNode('Function'); + ndv.actions.openSettings(); + ndv.getters.nodeVersion().should('have.text', 'Function node version 1 (Deprecated)'); + ndv.actions.close(); + }); + + it('Should render xml and html tags as strings and can search', () => { + cy.createFixtureWorkflow('Test_workflow_xml_output.json', `test`); + + workflowPage.actions.executeWorkflow(); + + workflowPage.actions.openNode('Edit Fields'); + + ndv.getters.outputDisplayMode().find('[class*=active]').should('contain', 'Table'); + + ndv.getters + .outputTableRow(1) + .should('include.text', ' '); + + cy.document().trigger('keyup', { key: '/' }); + ndv.getters.searchInput().filter(':focus').type(' Introduction to XML John Doe 2020 1234567890 Data Science Basics Jane Smith 2019 0987654321 Programming in Python Bob Johnson 2021 5432109876 "}]', + ); + ndv.getters.outputDataContainer().find('mark').should('have.text', ' span') + .should('include.text', ''); + }); + + it('should properly show node execution indicator', () => { + workflowPage.actions.addInitialNodeToCanvas('Code'); + workflowPage.actions.openNode('Code'); + // Should not show run info before execution + ndv.getters.nodeRunSuccessIndicator().should('not.exist'); + ndv.getters.nodeRunErrorIndicator().should('not.exist'); + ndv.getters.nodeExecuteButton().click(); + ndv.getters.nodeRunSuccessIndicator().should('exist'); + }); + + it('should properly show node execution indicator for multiple nodes', () => { + workflowPage.actions.addInitialNodeToCanvas('Code'); + workflowPage.actions.openNode('Code'); + ndv.actions.typeIntoParameterInput('jsCode', 'testets'); + ndv.getters.backToCanvas().click(); + workflowPage.actions.executeWorkflow(); + // Manual tigger node should show success indicator + workflowPage.actions.openNode('When clicking "Test Workflow"'); + ndv.getters.nodeRunSuccessIndicator().should('exist'); + // Code node should show error + ndv.getters.backToCanvas().click(); + workflowPage.actions.openNode('Code'); + ndv.getters.nodeRunErrorIndicator().should('exist'); + }); + + it('Should handle mismatched option attributes', () => { + workflowPage.actions.addInitialNodeToCanvas('LDAP', { + keepNdvOpen: true, + action: 'Create a new entry', + }); + // Add some attributes in Create operation + cy.getByTestId('parameter-item').contains('Add Attributes').click(); + ndv.actions.changeNodeOperation('Update'); + // Attributes should be empty after operation change + cy.getByTestId('parameter-item').contains('Currently no items exist').should('exist'); + }); + + it('Should keep RLC values after operation change', () => { + const TEST_DOC_ID = '1111'; + workflowPage.actions.addInitialNodeToCanvas('Google Sheets', { + keepNdvOpen: true, + action: 'Append row in sheet', + }); + ndv.actions.setRLCValue('documentId', TEST_DOC_ID); + ndv.actions.changeNodeOperation('Update Row'); + ndv.getters.resourceLocatorInput('documentId').find('input').should('have.value', TEST_DOC_ID); + }); }); diff --git a/cypress/e2e/7-workflow-actions.cy.ts b/cypress/e2e/7-workflow-actions.cy.ts index 7a0eda770c..ef15dd97d7 100644 --- a/cypress/e2e/7-workflow-actions.cy.ts +++ b/cypress/e2e/7-workflow-actions.cy.ts @@ -4,6 +4,8 @@ import { META_KEY, SCHEDULE_TRIGGER_NODE_NAME, EDIT_FIELDS_SET_NODE_NAME, + INSTANCE_MEMBERS, + INSTANCE_OWNER, } from '../constants'; import { WorkflowPage as WorkflowPageClass } from '../pages/workflow'; import { WorkflowsPage as WorkflowsPageClass } from '../pages/workflows'; @@ -100,6 +102,7 @@ describe('Workflow Actions', () => { cy.get('body').type(META_KEY, { release: false }).type('s'); cy.get('body').type(META_KEY, { release: false }).type('s'); cy.wrap(null).then(() => expect(interceptCalledCount).to.eq(0)); + cy.waitForLoad(); WorkflowPage.actions.addNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME); cy.get('body').type(META_KEY, { release: false }).type('s'); cy.wait('@saveWorkflow'); @@ -275,3 +278,19 @@ describe('Workflow Actions', () => { WorkflowPage.getters.nodeCreatorSearchBar().should('be.visible'); }); }); + +describe('Menu entry Push To Git', () => { + it('should not show up in the menu for members', () => { + cy.signin(INSTANCE_MEMBERS[0]); + cy.visit(WorkflowPages.url); + WorkflowPage.actions.visit(); + WorkflowPage.getters.workflowMenuItemGitPush().should('not.exist'); + }); + + it('should show up for owners', () => { + cy.signin(INSTANCE_OWNER); + cy.visit(WorkflowPages.url); + WorkflowPage.actions.visit(); + WorkflowPage.getters.workflowMenuItemGitPush().should('exist'); + }); +}); diff --git a/cypress/e2e/8-http-request-node.cy.ts b/cypress/e2e/8-http-request-node.cy.ts index c7f44e3494..1567815ddf 100644 --- a/cypress/e2e/8-http-request-node.cy.ts +++ b/cypress/e2e/8-http-request-node.cy.ts @@ -1,6 +1,8 @@ import { WorkflowPage, NDV } from '../pages'; +import { NodeCreator } from '../pages/features/node-creator'; const workflowPage = new WorkflowPage(); +const nodeCreatorFeature = new NodeCreator(); const ndv = new NDV(); describe('HTTP Request node', () => { @@ -18,4 +20,40 @@ describe('HTTP Request node', () => { ndv.getters.outputPanel().contains('fact'); }); + + describe('Credential-only HTTP Request Node variants', () => { + it('should render a modified HTTP Request Node', () => { + workflowPage.actions.addInitialNodeToCanvas('Manual'); + + workflowPage.getters.nodeCreatorPlusButton().click(); + workflowPage.getters.nodeCreatorSearchBar().type('VirusTotal'); + + expect(nodeCreatorFeature.getters.nodeItemName().first().should('have.text', 'VirusTotal')); + expect( + nodeCreatorFeature.getters + .nodeItemDescription() + .first() + .should('have.text', 'HTTP request'), + ); + + nodeCreatorFeature.actions.selectNode('VirusTotal'); + expect(ndv.getters.nodeNameContainer().should('contain.text', 'VirusTotal HTTP Request')); + expect( + ndv.getters + .parameterInput('url') + .find('input') + .should('contain.value', 'https://www.virustotal.com/api/v3/'), + ); + + // These parameters exist for normal HTTP Request Node, but are hidden for credential-only variants + expect(ndv.getters.parameterInput('authentication').should('not.exist')); + expect(ndv.getters.parameterInput('nodeCredentialType').should('not.exist')); + + expect( + workflowPage.getters + .nodeCredentialsLabel() + .should('contain.text', 'Credential for VirusTotal'), + ); + }); + }); }); diff --git a/cypress/e2e/9-expression-editor-modal.cy.ts b/cypress/e2e/9-expression-editor-modal.cy.ts index 46affa0d62..3cd00d1f68 100644 --- a/cypress/e2e/9-expression-editor-modal.cy.ts +++ b/cypress/e2e/9-expression-editor-modal.cy.ts @@ -57,6 +57,6 @@ describe('Expression editor modal', () => { it('should resolve $parameter[]', () => { WorkflowPage.getters.expressionModalInput().clear(); WorkflowPage.getters.expressionModalInput().type('{{ $parameter["operation"]'); - WorkflowPage.getters.expressionModalOutput().contains(/^get$/); + WorkflowPage.getters.expressionModalOutput().should('have.text', 'getAll'); }); }); diff --git a/cypress/fixtures/Ecommerce_starter_pack_template_collection.json b/cypress/fixtures/Ecommerce_starter_pack_template_collection.json new file mode 100644 index 0000000000..1f908c587b --- /dev/null +++ b/cypress/fixtures/Ecommerce_starter_pack_template_collection.json @@ -0,0 +1 @@ +{"collection":{"id":1,"name":"eCommerce Starter Pack","description":"eCommerce operations are complex — but there are many things that you can automate to make your life easier. This collection provides a few ideas to get started.\n\nReduce manual work and the risk of human error by automating processes such as social media promotion of products, updating customer databases, and get notifications for important events.","totalViews":0,"createdAt":"2022-02-17T12:40:50.498Z","nodes":[{"id":20,"name":"n8n-nodes-base.if","defaults":{"name":"IF","color":"#408000"},"displayName":"IF","icon":"fa:map-signs","iconData":{"icon":"map-signs","type":"icon"},"typeVersion":1,"categories":[{"id":9,"name":"Core Nodes"}]},{"id":49,"name":"n8n-nodes-base.telegram","defaults":{"name":"Telegram"},"displayName":"Telegram","icon":"file:telegram.svg","iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgNjYgNjYiIGZpbGw9IiNmZmYiIGZpbGwtcnVsZT0iZXZlbm9kZCIgc3Ryb2tlPSIjMDAwIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjx1c2UgeGxpbms6aHJlZj0iI2EiIHg9Ii41IiB5PSIuNSIvPjxzeW1ib2wgaWQ9ImEiIG92ZXJmbG93PSJ2aXNpYmxlIj48ZyBzdHJva2U9Im5vbmUiIGZpbGwtcnVsZT0ibm9uemVybyI+PHBhdGggZD0iTTAgMzJjMCAxNy42NzMgMTQuMzI3IDMyIDMyIDMyczMyLTE0LjMyNyAzMi0zMlM0OS42NzMgMCAzMiAwIDAgMTQuMzI3IDAgMzIiIGZpbGw9IiMzN2FlZTIiLz48cGF0aCBkPSJNMjEuNjYxIDM0LjMzOGwzLjc5NyAxMC41MDhzLjQ3NS45ODMuOTgzLjk4MyA4LjA2OC03Ljg2NCA4LjA2OC03Ljg2NGw4LjQwNy0xNi4yMzctMjEuMTE5IDkuODk4eiIgZmlsbD0iI2M4ZGFlYSIvPjxwYXRoIGQ9Ik0yNi42OTUgMzcuMDM0bC0uNzI5IDcuNzQ2cy0uMzA1IDIuMzczIDIuMDY4IDBsNC42NDQtNC4yMDMiIGZpbGw9IiNhOWM2ZDgiLz48cGF0aCBkPSJNMjEuNzMgMzQuNzEybC03LjgwOS0yLjU0NXMtLjkzMi0uMzc4LS42MzMtMS4yMzdjLjA2Mi0uMTc3LjE4Ni0uMzI4LjU1OS0uNTg4IDEuNzMxLTEuMjA2IDMyLjAyOC0xMi4wOTYgMzIuMDI4LTEyLjA5NnMuODU2LS4yODggMS4zNjEtLjA5N2MuMjMxLjA4OC4zNzguMTg3LjUwMy41NDguMDQ1LjEzMi4wNzEuNDExLjA2OC42ODktLjAwMy4yMDEtLjAyNy4zODYtLjA0NS42NzgtLjE4NCAyLjk3OC01LjcwNiAyNS4xOTgtNS43MDYgMjUuMTk4cy0uMzMgMS4zLTEuNTE0IDEuMzQ1Yy0uNDMyLjAxNi0uOTU2LS4wNzEtMS41ODItLjYxLTIuMzIzLTEuOTk4LTEwLjM1Mi03LjM5NC0xMi4xMjYtOC41OGEuMzQuMzQgMCAwMS0uMTQ2LS4yMzljLS4wMjUtLjEyNS4xMDgtLjI4LjEwOC0uMjhzMTMuOTgtMTIuNDI3IDE0LjM1Mi0xMy43MzFjLjAyOS0uMTAxLS4wNzktLjE1MS0uMjI2LS4xMDctLjkyOS4zNDItMTcuMDI1IDEwLjUwNi0xOC44MDEgMTEuNjI5LS4xMDQuMDY2LS4zOTUuMDIzLS4zOTUuMDIzIi8+PC9nPjwvc3ltYm9sPjwvc3ZnPg=="},"typeVersion":1,"categories":[{"id":6,"name":"Communication"}]},{"id":107,"name":"n8n-nodes-base.shopifyTrigger","defaults":{"name":"Shopify Trigger"},"displayName":"Shopify Trigger","icon":"file:shopify.svg","iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgNTggNjYiIGZpbGw9IiNmZmYiIGZpbGwtcnVsZT0iZXZlbm9kZCIgc3Ryb2tlPSIjMDAwIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjx1c2UgeGxpbms6aHJlZj0iI2EiIHg9Ii41IiB5PSIuNSIvPjxzeW1ib2wgaWQ9ImEiIG92ZXJmbG93PSJ2aXNpYmxlIj48ZyBzdHJva2U9Im5vbmUiIGZpbGwtcnVsZT0ibm9uemVybyI+PHBhdGggZD0iTTQ5LjI1NSAxMi40ODRhLjYzMy42MzMgMCAwMC0uNTY0LS41MjdjLS4yMjUtLjAzNy01LjE3LS4zNzYtNS4xNy0uMzc2bC0zLjc3LTMuNzdjLS4zNC0uMzc2LTEuMDkyLS4yNjYtMS4zNzYtLjE4OC0uMDM3IDAtLjc1Mi4yMjUtMS45MjIuNjA1LTEuMTM3LTMuMy0zLjE1LTYuMzA2LTYuNjk2LTYuMzA2aC0uMzAzQzI4LjQzOC42MDUgMjcuMTk0IDAgMjYuMTQ0IDBjLTguMjU2LjAzNy0xMi4yIDEwLjMzMy0xMy40MzQgMTUuNTk0bC01Ljc3IDEuNzdjLTEuNzcuNTY0LTEuODM1LjYwNS0yLjA3MyAyLjI5M0wwIDU3LjE3NSAzNi40NjggNjRsMTkuNzYzLTQuMjZjMC0uMDM3LTYuOTQtNDYuODk3LTYuOTc2LTQ3LjI1NXpNMzQuNDMxIDguODZjLS45MTcuMzAzLTEuOTYzLjYwNS0zLjEuOTQ1di0uNjhhMTUuMDMgMTUuMDMgMCAwMC0uNzUyLTQuOTk5YzEuODQ4LjI4NCAzLjEgMi4zNTcgMy44NDMgNC43MzN6bS02LjA2OC00LjI5OGMuNjAzIDEuNzc4Ljg4MyAzLjY1LjgyNiA1LjUyN3YuMzRsLTYuMzc1IDEuOTYzYzEuMjQ4LTQuNjYgMy41NS02Ljk2MiA1LjU1LTcuODN6bS0yLjQ1LTIuMjkzYTEuOTQgMS45NCAwIDAxMS4wNTUuMzM5Yy0yLjY2IDEuMjM4LTUuNDcyIDQuMzY2LTYuNjc4IDEwLjYyN2wtNS4wNDUgMS41NDZDMTYuNjY4IDEwLjAzIDE5Ljk4OCAyLjI2IDI1LjkxIDIuMjZ6IiBmaWxsPSIjOTViZjQ3Ii8+PHBhdGggZD0iTTQ4LjY5MSAxMS45NTdjLS4yMjUtLjAzNy01LjE3LS4zNzYtNS4xNy0uMzc2bC0zLjc3LTMuNzdhLjc1My43NTMgMCAwMC0uNTI3LS4yMjVMMzYuNDcyIDY0bDE5Ljc2My00LjI2LTYuOTgtNDcuMjE4YS42OC42OCAwIDAwLS41NjQtLjU2NHoiIGZpbGw9IiM1ZThlM2UiLz48cGF0aCBkPSJNMjkuNzU4IDIyLjlsLTIuNDU0IDcuMjQyYTExLjM2IDExLjM2IDAgMDAtNC43NTItMS4xMzNjLTMuODQ4IDAtNC4wMzYgMi40MTItNC4wMzYgMy4wMTggMCAzLjI5OCA4LjYzNiA0LjU2NCA4LjYzNiAxMi4zMzMgMCA2LjEtMy44ODUgMTAuMDMtOS4xIDEwLjAzLTYuMjYgMC05LjQ2Ny0zLjg4NS05LjQ2Ny0zLjg4NWwxLjY2NS01LjU1czMuMjggMi44MyA2LjA3MyAyLjgzYTIuNDcgMi40NyAwIDAwMi41NjQtMi40OWMwLTQuMzQtNy4xLTQuNTI3LTcuMS0xMS42MTggMC01Ljk2MiA0LjI5OC0xMS43NyAxMi45MzQtMTEuNzcgMy4zOTQuMDUgNS4wMTggMSA1LjAxOCAxeiIvPjwvZz48L3N5bWJvbD48L3N2Zz4="},"typeVersion":1,"categories":[{"id":2,"name":"Sales"}]},{"id":126,"name":"n8n-nodes-base.mautic","defaults":{"name":"Mautic"},"displayName":"Mautic","icon":"file:mautic.svg","iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAxNi4wLjAsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+DQo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4Ig0KCSB3aWR0aD0iMzQ5Ljc3OXB4IiBoZWlnaHQ9IjM0OS43NzlweCIgdmlld0JveD0iMCAwIDM0OS43NzkgMzQ5Ljc3OSIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgMzQ5Ljc3OSAzNDkuNzc5Ig0KCSB4bWw6c3BhY2U9InByZXNlcnZlIj4NCjxwYXRoIGZpbGw9IiNGRkZGRkYiIGQ9Ik0xNzQuODksMzQ5Ljc3OUM3OC42MTIsMzQ5Ljc3OSwwLDI3MS40NjIsMCwxNzQuODlTNzguNjEyLDAsMTc0Ljg5LDBjMjMuMjYsMCw0NS45MzEsNC40MTcsNjcuMTI5LDEzLjU0Mw0KCWM1Ljg4OSwyLjY1LDguODMzLDkuNDIyLDYuNDc4LDE1LjYwNWMtMi42NDksNS44ODgtOS40MjEsOC44MzMtMTUuNjA0LDYuNDc3Yy0xOC41NDktNy42NTUtMzcuOTgtMTEuNDgyLTU4LjAwMi0xMS40ODINCgljLTgzLjMyMywwLTE1MS4wNDEsNjcuNzE4LTE1MS4wNDEsMTUxLjA0MVM5MS41NjcsMzI2LjIyNSwxNzQuODksMzI2LjIyNWM4My4zMjMsMCwxNTEuMDQxLTY3LjcxOCwxNTEuMDQxLTE1MS4wNDENCgljMC0xNy45Ni0yLjk0NC0zNS4zMzItOS4xMjctNTEuODE5Yy0yLjM1NS02LjE4MywwLjg4My0xMi45NTUsNy4wNjYtMTUuMzFjNi4xODMtMi4zNTUsMTIuOTU0LDAuODgzLDE1LjMxLDcuMDY2DQoJYzcuMDY2LDE5LjEzOCwxMC42LDM5LjQ1MywxMC42LDYwLjA2M0MzNDkuNzc5LDI3MS4xNjcsMjcxLjQ2MiwzNDkuNzc5LDE3NC44OSwzNDkuNzc5Ii8+DQo8Zz4NCgk8cG9seWdvbiBmaWxsPSIjRkRCOTMzIiBwb2ludHM9IjI1MS40NCwxNTYuOTMgMjI0LjM1NCwxODUuMTk0IDIzOS4zNjksMjQ4LjQ5NiAyNzMuNTIyLDI0OC40OTYgCSIvPg0KPC9nPg0KPHBvbHlnb24gZmlsbD0iI0ZEQjkzMyIgcG9pbnRzPSIyNDAuMjUzLDczLjMxMiAyNDkuNjc0LDgyLjczNCAxNzQuODksMTYxLjkzNSAxMTAuOTk5LDk2LjI3NyA3NC4xOTYsMjQ4LjQ5NiAxMDguMzUsMjQ4LjQ5NiANCgkxMjguNjY1LDE2My45OTYgMTc0Ljg5LDIxNC4zNDMgMjczLjgxNywxMDYuNTgzIDI4My4yMzksMTE2LjI5OSAyOTIuNjYsNjMuMDA3ICIvPg0KPC9zdmc+DQo="},"typeVersion":1,"categories":[{"id":1,"name":"Marketing & Content"},{"id":6,"name":"Communication"}]},{"id":235,"name":"n8n-nodes-base.wooCommerceTrigger","defaults":{"name":"WooCommerce Trigger"},"displayName":"WooCommerce Trigger","icon":"file:wooCommerce.svg","iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWlkWU1pZCIgdmVyc2lvbj0iMS4xIiB2aWV3Qm94PSIwIC01MCAyNTYgMjUzIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KPHRpdGxlPldvb0NvbW1lcmNlIExvZ288L3RpdGxlPgo8bWV0YWRhdGE+CjxyZGY6UkRGPgo8Y2M6V29yayByZGY6YWJvdXQ9IiI+CjxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0Pgo8ZGM6dHlwZSByZGY6cmVzb3VyY2U9Imh0dHA6Ly9wdXJsLm9yZy9kYy9kY21pdHlwZS9TdGlsbEltYWdlIi8+CjxkYzp0aXRsZS8+CjwvY2M6V29yaz4KPC9yZGY6UkRGPgo8L21ldGFkYXRhPgo8cGF0aCBkPSJtMjMuNzU5IDBoMjA4LjM4YzEzLjE4NyAwIDIzLjg2MyAxMC42NzUgMjMuODYzIDIzLjg2M3Y3OS41NDJjMCAxMy4xODctMTAuNjc1IDIzLjg2My0yMy44NjMgMjMuODYzaC03NC43MjdsMTAuMjU3IDI1LjExOC00NS4xMDktMjUuMTE4aC05OC42OTVjLTEzLjE4NyAwLTIzLjg2My0xMC42NzUtMjMuODYzLTIzLjg2M3YtNzkuNTQyYy0wLjEwNDY2LTEzLjA4MyAxMC41NzEtMjMuODYzIDIzLjc1OC0yMy44NjN6IiBmaWxsPSIjN2Y1NGIzIi8+CjxwYXRoIGQ9Im0xNC41NzggMjEuNzVjMS40NTY5LTEuOTc3MiAzLjY0MjMtMy4wMTc5IDYuNTU2MS0zLjIyNiA1LjMwNzMtMC40MTYyNiA4LjMyNTIgMi4wODEzIDkuMDUzNyA3LjQ5MjcgMy4yMjYgMjEuNzUgNi43NjQyIDQwLjE2OSAxMC41MTEgNTUuMjU5bDIyLjc5LTQzLjM5NWMyLjA4MTMtMy45NTQ1IDQuNjgyOS02LjAzNTggNy44MDQ5LTYuMjQzOSA0LjU3ODktMC4zMTIyIDcuMzg4NiAyLjYwMTYgOC41MzMzIDguNzQxNSAyLjYwMTYgMTMuODQxIDUuOTMxNyAyNS42IDkuODg2MiAzNS41OSAyLjcwNTctMjYuNDMzIDcuMjg0Ni00NS40NzYgMTMuNzM3LTU3LjIzNiAxLjU2MS0yLjkxMzggMy44NTA0LTQuMzcwNyA2Ljg2ODMtNC41Nzg5IDIuMzkzNS0wLjIwODEzIDQuNTc4OSAwLjUyMDMzIDYuNTU2MSAyLjA4MTMgMS45NzcyIDEuNTYxIDMuMDE3OSAzLjUzODIgMy4yMjYgNS45MzE3IDAuMTA0MDYgMS44NzMyLTAuMjA4MTMgMy40MzQxLTEuMDQwNyA0Ljk5NTEtNC4wNTg1IDcuNDkyNy03LjM4ODYgMjAuMDg1LTEwLjA5NCAzNy41NjctMi42MDE2IDE2Ljk2My0zLjUzODIgMzAuMTc5LTIuOTEzOCAzOS42NDkgMC4yMDgxMyAyLjYwMTYtMC4yMDgxMyA0Ljg5MTEtMS4yNDg4IDYuODY4My0xLjI0ODggMi4yODk0LTMuMTIyIDMuNTM4Mi01LjUxNTQgMy43NDYzLTIuNzA1NyAwLjIwODEzLTUuNTE1NC0xLjA0MDYtOC4yMjExLTMuODUwNC05LjY3OC05Ljg4NjItMTcuMzc5LTI0LjY2My0yMi45OTgtNDQuMzMyLTYuNzY0MiAxMy4zMi0xMS43NTkgMjMuMzExLTE0Ljk4NSAyOS45NzEtNi4xMzk4IDExLjc1OS0xMS4zNDMgMTcuNzk1LTE1LjcxNCAxOC4xMDctMi44MDk4IDAuMjA4MTMtNS4yMDMzLTIuMTg1NC03LjI4NDYtNy4xODA1LTUuMzA3My0xMy42MzMtMTEuMDMxLTM5Ljk2MS0xNy4xNzEtNzguOTg1LTAuNDE2MjYtMi43MDU3IDAuMjA4MTMtNS4wOTkyIDEuNjY1LTYuOTcyNHptMjIzLjY0IDE2LjMzOGMtMy43NDYzLTYuNTU2MS05LjI2MTgtMTAuNTExLTE2LjY1LTEyLjA3Mi0xLjk3NzItMC40MTYyNi0zLjg1MDQtMC42MjQzOS01LjYxOTUtMC42MjQzOS05Ljk5MDIgMC0xOC4xMDcgNS4yMDMzLTI0LjQ1NSAxNS42MS01LjQxMTQgOC44NDU1LTguMTE3MSAxOC42MjgtOC4xMTcxIDI5LjM0NiAwIDguMDEzIDEuNjY1IDE0Ljg4MSA0Ljk5NTEgMjAuNjA1IDMuNzQ2MyA2LjU1NjEgOS4yNjE4IDEwLjUxMSAxNi42NSAxMi4wNzIgMS45NzcyIDAuNDE2MjYgMy44NTA0IDAuNjI0MzkgNS42MTk1IDAuNjI0MzkgMTAuMDk0IDAgMTguMjExLTUuMjAzMyAyNC40NTUtMTUuNjEgNS40MTE0LTguOTQ5NiA4LjExNzEtMTguNzMyIDguMTE3MS0yOS40NSAwLjEwNDA2LTguMTE3MS0xLjY2NS0xNC44ODEtNC45OTUxLTIwLjUwMXptLTEzLjExMiAyOC44MjZjLTEuNDU2OSA2Ljg2ODMtNC4wNTg1IDExLjk2Ny03LjkwODkgMTUuNDAyLTMuMDE3OSAyLjcwNTctNS44Mjc2IDMuODUwNC04LjQyOTMgMy4zMzAxLTIuNDk3Ni0wLjUyMDMzLTQuNTc4OS0yLjcwNTctNi4xMzk4LTYuNzY0Mi0xLjI0ODgtMy4yMjYtMS44NzMyLTYuNDUyLTEuODczMi05LjQ2OTkgMC0yLjYwMTYgMC4yMDgxMy01LjIwMzMgMC43Mjg0Ni03LjU5NjcgMC45MzY1OS00LjI2NjcgMi43MDU3LTguNDI5MyA1LjUxNTQtMTIuMzg0IDMuNDM0MS01LjA5OTIgNy4wNzY0LTcuMTgwNSAxMC44MjMtNi40NTIgMi40OTc2IDAuNTIwMzMgNC41Nzg5IDIuNzA1NyA2LjEzOTggNi43NjQyIDEuMjQ4OCAzLjIyNiAxLjg3MzIgNi40NTIgMS44NzMyIDkuNDY5OSAwIDIuNzA1Ny0wLjIwODEzIDUuMzA3My0wLjcyODQ2IDcuNzAwOHptLTUyLjAzMy0yOC44MjZjLTMuNzQ2My02LjU1NjEtOS4zNjU5LTEwLjUxMS0xNi42NS0xMi4wNzItMS45NzcyLTAuNDE2MjYtMy44NTA0LTAuNjI0MzktNS42MTk1LTAuNjI0MzktOS45OTAyIDAtMTguMTA3IDUuMjAzMy0yNC40NTUgMTUuNjEtNS40MTE0IDguODQ1NS04LjExNzEgMTguNjI4LTguMTE3MSAyOS4zNDYgMCA4LjAxMyAxLjY2NSAxNC44ODEgNC45OTUxIDIwLjYwNSAzLjc0NjMgNi41NTYxIDkuMjYxOCAxMC41MTEgMTYuNjUgMTIuMDcyIDEuOTc3MiAwLjQxNjI2IDMuODUwNCAwLjYyNDM5IDUuNjE5NSAwLjYyNDM5IDEwLjA5NCAwIDE4LjIxMS01LjIwMzMgMjQuNDU1LTE1LjYxIDUuNDExNC04Ljk0OTYgOC4xMTcxLTE4LjczMiA4LjExNzEtMjkuNDUgMC04LjExNzEtMS42NjUtMTQuODgxLTQuOTk1MS0yMC41MDF6bS0xMy4yMTYgMjguODI2Yy0xLjQ1NjkgNi44NjgzLTQuMDU4NSAxMS45NjctNy45MDg5IDE1LjQwMi0zLjAxNzkgMi43MDU3LTUuODI3NiAzLjg1MDQtOC40MjkzIDMuMzMwMS0yLjQ5NzYtMC41MjAzMy00LjU3ODktMi43MDU3LTYuMTM5OC02Ljc2NDItMS4yNDg4LTMuMjI2LTEuODczMi02LjQ1Mi0xLjg3MzItOS40Njk5IDAtMi42MDE2IDAuMjA4MTMtNS4yMDMzIDAuNzI4NDYtNy41OTY3IDAuOTM2NTgtNC4yNjY3IDIuNzA1Ny04LjQyOTMgNS41MTU0LTEyLjM4NCAzLjQzNDEtNS4wOTkyIDcuMDc2NC03LjE4MDUgMTAuODIzLTYuNDUyIDIuNDk3NiAwLjUyMDMzIDQuNTc4OSAyLjcwNTcgNi4xMzk4IDYuNzY0MiAxLjI0ODggMy4yMjYgMS44NzMyIDYuNDUyIDEuODczMiA5LjQ2OTkgMC4xMDQwNiAyLjcwNTctMC4yMDgxMyA1LjMwNzMtMC43Mjg0NiA3LjcwMDh6IiBmaWxsPSIjZmZmIi8+Cjwvc3ZnPgo="},"typeVersion":1,"categories":[{"id":2,"name":"Sales"}]},{"id":325,"name":"n8n-nodes-base.twitter","defaults":{"name":"X"},"displayName":"X (Formerly Twitter)","icon":"file:x.svg","iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjQgMjQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTE4LjI0NCAyLjI1aDMuMzA4bC03LjIyNyA4LjI2IDguNTAyIDExLjI0SDE2LjE3bC01LjIxNC02LjgxN0w0Ljk5IDIxLjc1SDEuNjhsNy43My04LjgzNUwxLjI1NCAyLjI1SDguMDhsNC43MTMgNi4yMzF6bS0xLjE2MSAxNy41MmgxLjgzM0w3LjA4NCA0LjEyNkg1LjExN3oiPjwvcGF0aD48L3N2Zz4K"},"typeVersion":2,"categories":[{"id":1,"name":"Marketing & Content"}]}],"categories":[{"id":2,"name":"Sales"}],"workflows":[{"id":1205,"name":"Promote new Shopify products on Twitter and Telegram","views":485,"recentViews":9850,"totalViews":485,"createdAt":"2021-08-24T10:40:50.007Z","description":"This workflow automatically promotes your new Shopify products on Twitter and Telegram. This workflow is also featured in the blog post [*6 e-commerce workflows to power up your Shopify store*](https://n8n.io/blog/no-code-ecommerce-workflow-automations/#promote-your-new-products-on-social-media).\n\n## Prerequisites\n\n- A Shopify account and [credentials](https://docs.n8n.io/integrations/credentials/shopify/)\n- A Twitter account and [credentials](https://docs.n8n.io/integrations/credentials/twitter/)\n- A Telegram account and [credentials](https://docs.n8n.io/integrations/credentials/telegram/) for the channel you want to send messages to.\n\n## Nodes\n\n- [Shopify Trigger node](https://docs.n8n.io/integrations/trigger-nodes/n8n-nodes-base.shopifytrigger/) triggers the workflow when you create a new product in Shopify.\n- [Twitter node](https://docs.n8n.io/integrations/nodes/n8n-nodes-base.twitter/) posts a tweet with the text \"Hey there, my design is now on a new product! Visit my {shop name} to get this cool {product title} (and check out more {product type})\".\n- [Telegram node](https://docs.n8n.io/integrations/nodes/n8n-nodes-base.telegram/) posts a message with the same text as above in a Telegram channel.","workflow":{"nodes":[{"name":"Twitter","type":"n8n-nodes-base.twitter","position":[720,-220],"parameters":{"text":"=Hey there, my design is now on a new product ✨\nVisit my {{$json[\"vendor\"]}} shop to get this cool{{$json[\"title\"]}} (and check out more {{$json[\"product_type\"]}}) 🛍️","additionalFields":{}},"credentials":{"twitterOAuth1Api":"twitter"},"typeVersion":1},{"name":"Telegram","type":"n8n-nodes-base.telegram","position":[720,-20],"parameters":{"text":"=Hey there, my design is now on a new product!\nVisit my {{$json[\"vendor\"]}} shop to get this cool{{$json[\"title\"]}} (and check out more {{$json[\"product_type\"]}})","chatId":"123456","additionalFields":{}},"credentials":{"telegramApi":"telegram_habot"},"typeVersion":1},{"name":"product created","type":"n8n-nodes-base.shopifyTrigger","position":[540,-110],"webhookId":"2a7e0e50-8f09-4a2b-bf54-a849a6ac4fe0","parameters":{"topic":"products/create"},"credentials":{"shopifyApi":"shopify_nodeqa"},"typeVersion":1}],"connections":{"product created":{"main":[[{"node":"Twitter","type":"main","index":0},{"node":"Telegram","type":"main","index":0}]]}}},"workflowInfo":{"nodeCount":3,"nodeTypes":{"n8n-nodes-base.twitter":{"count":1},"n8n-nodes-base.telegram":{"count":1},"n8n-nodes-base.shopifyTrigger":{"count":1}}},"user":{"username":"lorenanda"},"nodes":[{"id":49,"icon":"file:telegram.svg","name":"n8n-nodes-base.telegram","defaults":{"name":"Telegram"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgNjYgNjYiIGZpbGw9IiNmZmYiIGZpbGwtcnVsZT0iZXZlbm9kZCIgc3Ryb2tlPSIjMDAwIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjx1c2UgeGxpbms6aHJlZj0iI2EiIHg9Ii41IiB5PSIuNSIvPjxzeW1ib2wgaWQ9ImEiIG92ZXJmbG93PSJ2aXNpYmxlIj48ZyBzdHJva2U9Im5vbmUiIGZpbGwtcnVsZT0ibm9uemVybyI+PHBhdGggZD0iTTAgMzJjMCAxNy42NzMgMTQuMzI3IDMyIDMyIDMyczMyLTE0LjMyNyAzMi0zMlM0OS42NzMgMCAzMiAwIDAgMTQuMzI3IDAgMzIiIGZpbGw9IiMzN2FlZTIiLz48cGF0aCBkPSJNMjEuNjYxIDM0LjMzOGwzLjc5NyAxMC41MDhzLjQ3NS45ODMuOTgzLjk4MyA4LjA2OC03Ljg2NCA4LjA2OC03Ljg2NGw4LjQwNy0xNi4yMzctMjEuMTE5IDkuODk4eiIgZmlsbD0iI2M4ZGFlYSIvPjxwYXRoIGQ9Ik0yNi42OTUgMzcuMDM0bC0uNzI5IDcuNzQ2cy0uMzA1IDIuMzczIDIuMDY4IDBsNC42NDQtNC4yMDMiIGZpbGw9IiNhOWM2ZDgiLz48cGF0aCBkPSJNMjEuNzMgMzQuNzEybC03LjgwOS0yLjU0NXMtLjkzMi0uMzc4LS42MzMtMS4yMzdjLjA2Mi0uMTc3LjE4Ni0uMzI4LjU1OS0uNTg4IDEuNzMxLTEuMjA2IDMyLjAyOC0xMi4wOTYgMzIuMDI4LTEyLjA5NnMuODU2LS4yODggMS4zNjEtLjA5N2MuMjMxLjA4OC4zNzguMTg3LjUwMy41NDguMDQ1LjEzMi4wNzEuNDExLjA2OC42ODktLjAwMy4yMDEtLjAyNy4zODYtLjA0NS42NzgtLjE4NCAyLjk3OC01LjcwNiAyNS4xOTgtNS43MDYgMjUuMTk4cy0uMzMgMS4zLTEuNTE0IDEuMzQ1Yy0uNDMyLjAxNi0uOTU2LS4wNzEtMS41ODItLjYxLTIuMzIzLTEuOTk4LTEwLjM1Mi03LjM5NC0xMi4xMjYtOC41OGEuMzQuMzQgMCAwMS0uMTQ2LS4yMzljLS4wMjUtLjEyNS4xMDgtLjI4LjEwOC0uMjhzMTMuOTgtMTIuNDI3IDE0LjM1Mi0xMy43MzFjLjAyOS0uMTAxLS4wNzktLjE1MS0uMjI2LS4xMDctLjkyOS4zNDItMTcuMDI1IDEwLjUwNi0xOC44MDEgMTEuNjI5LS4xMDQuMDY2LS4zOTUuMDIzLS4zOTUuMDIzIi8+PC9nPjwvc3ltYm9sPjwvc3ZnPg=="},"categories":[{"id":6,"name":"Communication"}],"displayName":"Telegram","typeVersion":1},{"id":107,"icon":"file:shopify.svg","name":"n8n-nodes-base.shopifyTrigger","defaults":{"name":"Shopify Trigger"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgNTggNjYiIGZpbGw9IiNmZmYiIGZpbGwtcnVsZT0iZXZlbm9kZCIgc3Ryb2tlPSIjMDAwIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjx1c2UgeGxpbms6aHJlZj0iI2EiIHg9Ii41IiB5PSIuNSIvPjxzeW1ib2wgaWQ9ImEiIG92ZXJmbG93PSJ2aXNpYmxlIj48ZyBzdHJva2U9Im5vbmUiIGZpbGwtcnVsZT0ibm9uemVybyI+PHBhdGggZD0iTTQ5LjI1NSAxMi40ODRhLjYzMy42MzMgMCAwMC0uNTY0LS41MjdjLS4yMjUtLjAzNy01LjE3LS4zNzYtNS4xNy0uMzc2bC0zLjc3LTMuNzdjLS4zNC0uMzc2LTEuMDkyLS4yNjYtMS4zNzYtLjE4OC0uMDM3IDAtLjc1Mi4yMjUtMS45MjIuNjA1LTEuMTM3LTMuMy0zLjE1LTYuMzA2LTYuNjk2LTYuMzA2aC0uMzAzQzI4LjQzOC42MDUgMjcuMTk0IDAgMjYuMTQ0IDBjLTguMjU2LjAzNy0xMi4yIDEwLjMzMy0xMy40MzQgMTUuNTk0bC01Ljc3IDEuNzdjLTEuNzcuNTY0LTEuODM1LjYwNS0yLjA3MyAyLjI5M0wwIDU3LjE3NSAzNi40NjggNjRsMTkuNzYzLTQuMjZjMC0uMDM3LTYuOTQtNDYuODk3LTYuOTc2LTQ3LjI1NXpNMzQuNDMxIDguODZjLS45MTcuMzAzLTEuOTYzLjYwNS0zLjEuOTQ1di0uNjhhMTUuMDMgMTUuMDMgMCAwMC0uNzUyLTQuOTk5YzEuODQ4LjI4NCAzLjEgMi4zNTcgMy44NDMgNC43MzN6bS02LjA2OC00LjI5OGMuNjAzIDEuNzc4Ljg4MyAzLjY1LjgyNiA1LjUyN3YuMzRsLTYuMzc1IDEuOTYzYzEuMjQ4LTQuNjYgMy41NS02Ljk2MiA1LjU1LTcuODN6bS0yLjQ1LTIuMjkzYTEuOTQgMS45NCAwIDAxMS4wNTUuMzM5Yy0yLjY2IDEuMjM4LTUuNDcyIDQuMzY2LTYuNjc4IDEwLjYyN2wtNS4wNDUgMS41NDZDMTYuNjY4IDEwLjAzIDE5Ljk4OCAyLjI2IDI1LjkxIDIuMjZ6IiBmaWxsPSIjOTViZjQ3Ii8+PHBhdGggZD0iTTQ4LjY5MSAxMS45NTdjLS4yMjUtLjAzNy01LjE3LS4zNzYtNS4xNy0uMzc2bC0zLjc3LTMuNzdhLjc1My43NTMgMCAwMC0uNTI3LS4yMjVMMzYuNDcyIDY0bDE5Ljc2My00LjI2LTYuOTgtNDcuMjE4YS42OC42OCAwIDAwLS41NjQtLjU2NHoiIGZpbGw9IiM1ZThlM2UiLz48cGF0aCBkPSJNMjkuNzU4IDIyLjlsLTIuNDU0IDcuMjQyYTExLjM2IDExLjM2IDAgMDAtNC43NTItMS4xMzNjLTMuODQ4IDAtNC4wMzYgMi40MTItNC4wMzYgMy4wMTggMCAzLjI5OCA4LjYzNiA0LjU2NCA4LjYzNiAxMi4zMzMgMCA2LjEtMy44ODUgMTAuMDMtOS4xIDEwLjAzLTYuMjYgMC05LjQ2Ny0zLjg4NS05LjQ2Ny0zLjg4NWwxLjY2NS01LjU1czMuMjggMi44MyA2LjA3MyAyLjgzYTIuNDcgMi40NyAwIDAwMi41NjQtMi40OWMwLTQuMzQtNy4xLTQuNTI3LTcuMS0xMS42MTggMC01Ljk2MiA0LjI5OC0xMS43NyAxMi45MzQtMTEuNzcgMy4zOTQuMDUgNS4wMTggMSA1LjAxOCAxeiIvPjwvZz48L3N5bWJvbD48L3N2Zz4="},"categories":[{"id":2,"name":"Sales"}],"displayName":"Shopify Trigger","typeVersion":1},{"id":325,"icon":"file:x.svg","name":"n8n-nodes-base.twitter","defaults":{"name":"X"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjQgMjQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTE4LjI0NCAyLjI1aDMuMzA4bC03LjIyNyA4LjI2IDguNTAyIDExLjI0SDE2LjE3bC01LjIxNC02LjgxN0w0Ljk5IDIxLjc1SDEuNjhsNy43My04LjgzNUwxLjI1NCAyLjI1SDguMDhsNC43MTMgNi4yMzF6bS0xLjE2MSAxNy41MmgxLjgzM0w3LjA4NCA0LjEyNkg1LjExN3oiPjwvcGF0aD48L3N2Zz4K"},"categories":[{"id":1,"name":"Marketing & Content"}],"displayName":"X (Formerly Twitter)","typeVersion":2}],"categories":[{"id":2,"name":"Sales"},{"id":19,"name":"Marketing & Growth"}],"image":[{"id":527,"url":"https://n8niostorageaccount.blob.core.windows.net/n8nio-strapi-blobs-prod/assets/89a078b208fe4c6181902608b1cd1332.png"}]},{"id":1456,"name":"Add new customers from WooCommerce to Mautic","views":333,"recentViews":9833,"totalViews":333,"createdAt":"2022-02-17T15:00:40.748Z","description":"This workflow uses a WooCommerce trigger that will run when a new customer has been added, It will then add the customer to Mautic.\n\nTo use this workflow you will need to set the credentials to use for the WooCommerce and Mautic nodes.","workflow":{"id":83,"name":"New WooCommerce Customer to Mautic","nodes":[{"name":"Check for Existing","type":"n8n-nodes-base.mautic","position":[280,480],"parameters":{"options":{"search":"={{$json[\"email\"]}}"},"operation":"getAll","authentication":"oAuth2"},"credentials":{"mauticOAuth2Api":{"id":"54","name":"Mautic account"}},"typeVersion":1,"alwaysOutputData":true},{"name":"If New","type":"n8n-nodes-base.if","position":[460,480],"parameters":{"conditions":{"string":[{"value1":"={{$json[\"id\"]}}","operation":"isEmpty"}]}},"typeVersion":1},{"name":"Create Contact","type":"n8n-nodes-base.mautic","position":[680,320],"parameters":{"email":"={{$node[\"Customer Created\"].json[\"email\"]}}","company":"={{$node[\"Customer Created\"].json[\"billing\"][\"company\"]}}","options":{},"lastName":"={{$node[\"Customer Created\"].json[\"last_name\"]}}","firstName":"={{$node[\"Customer Created\"].json[\"first_name\"]}}","authentication":"oAuth2","additionalFields":{}},"credentials":{"mauticOAuth2Api":{"id":"54","name":"Mautic account"}},"typeVersion":1},{"name":"Update Contact","type":"n8n-nodes-base.mautic","position":[680,580],"parameters":{"options":{},"contactId":"={{$json[\"id\"]}}","operation":"update","updateFields":{"lastName":"={{$node[\"Customer Created or Updated\"].json[\"last_name\"]}}","firstName":"={{$node[\"Customer Created or Updated\"].json[\"first_name\"]}}"},"authentication":"oAuth2"},"credentials":{"mauticOAuth2Api":{"id":"54","name":"Mautic account"}},"typeVersion":1},{"name":"Customer Created or Updated","type":"n8n-nodes-base.wooCommerceTrigger","position":[100,480],"webhookId":"5d89e322-a5e0-4cce-9eab-185e8375175b","parameters":{"event":"customer.updated"},"credentials":{"wooCommerceApi":{"id":"48","name":"WooCommerce account"}},"typeVersion":1}],"active":false,"settings":{"saveManualExecutions":true,"saveExecutionProgress":true,"saveDataSuccessExecution":"all"},"connections":{"If New":{"main":[[{"node":"Create Contact","type":"main","index":0}],[{"node":"Update Contact","type":"main","index":0}]]},"Check for Existing":{"main":[[{"node":"If New","type":"main","index":0}]]},"Customer Created or Updated":{"main":[[{"node":"Check for Existing","type":"main","index":0}]]}}},"workflowInfo":{"nodeCount":6,"nodeTypes":{"n8n-nodes-base.if":{"count":1},"n8n-nodes-base.mautic":{"count":3},"n8n-nodes-base.wooCommerceTrigger":{"count":1}}},"user":{"username":"jon-n8n"},"nodes":[{"id":20,"icon":"fa:map-signs","name":"n8n-nodes-base.if","defaults":{"name":"IF","color":"#408000"},"iconData":{"icon":"map-signs","type":"icon"},"categories":[{"id":9,"name":"Core Nodes"}],"displayName":"IF","typeVersion":1},{"id":42,"icon":"fa:play","name":"n8n-nodes-base.start","defaults":{"name":"Start","color":"#00e000"},"iconData":{"icon":"play","type":"icon"},"categories":[{"id":9,"name":"Core Nodes"}],"displayName":"Start","typeVersion":1},{"id":126,"icon":"file:mautic.svg","name":"n8n-nodes-base.mautic","defaults":{"name":"Mautic"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAxNi4wLjAsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+DQo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4Ig0KCSB3aWR0aD0iMzQ5Ljc3OXB4IiBoZWlnaHQ9IjM0OS43NzlweCIgdmlld0JveD0iMCAwIDM0OS43NzkgMzQ5Ljc3OSIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgMzQ5Ljc3OSAzNDkuNzc5Ig0KCSB4bWw6c3BhY2U9InByZXNlcnZlIj4NCjxwYXRoIGZpbGw9IiNGRkZGRkYiIGQ9Ik0xNzQuODksMzQ5Ljc3OUM3OC42MTIsMzQ5Ljc3OSwwLDI3MS40NjIsMCwxNzQuODlTNzguNjEyLDAsMTc0Ljg5LDBjMjMuMjYsMCw0NS45MzEsNC40MTcsNjcuMTI5LDEzLjU0Mw0KCWM1Ljg4OSwyLjY1LDguODMzLDkuNDIyLDYuNDc4LDE1LjYwNWMtMi42NDksNS44ODgtOS40MjEsOC44MzMtMTUuNjA0LDYuNDc3Yy0xOC41NDktNy42NTUtMzcuOTgtMTEuNDgyLTU4LjAwMi0xMS40ODINCgljLTgzLjMyMywwLTE1MS4wNDEsNjcuNzE4LTE1MS4wNDEsMTUxLjA0MVM5MS41NjcsMzI2LjIyNSwxNzQuODksMzI2LjIyNWM4My4zMjMsMCwxNTEuMDQxLTY3LjcxOCwxNTEuMDQxLTE1MS4wNDENCgljMC0xNy45Ni0yLjk0NC0zNS4zMzItOS4xMjctNTEuODE5Yy0yLjM1NS02LjE4MywwLjg4My0xMi45NTUsNy4wNjYtMTUuMzFjNi4xODMtMi4zNTUsMTIuOTU0LDAuODgzLDE1LjMxLDcuMDY2DQoJYzcuMDY2LDE5LjEzOCwxMC42LDM5LjQ1MywxMC42LDYwLjA2M0MzNDkuNzc5LDI3MS4xNjcsMjcxLjQ2MiwzNDkuNzc5LDE3NC44OSwzNDkuNzc5Ii8+DQo8Zz4NCgk8cG9seWdvbiBmaWxsPSIjRkRCOTMzIiBwb2ludHM9IjI1MS40NCwxNTYuOTMgMjI0LjM1NCwxODUuMTk0IDIzOS4zNjksMjQ4LjQ5NiAyNzMuNTIyLDI0OC40OTYgCSIvPg0KPC9nPg0KPHBvbHlnb24gZmlsbD0iI0ZEQjkzMyIgcG9pbnRzPSIyNDAuMjUzLDczLjMxMiAyNDkuNjc0LDgyLjczNCAxNzQuODksMTYxLjkzNSAxMTAuOTk5LDk2LjI3NyA3NC4xOTYsMjQ4LjQ5NiAxMDguMzUsMjQ4LjQ5NiANCgkxMjguNjY1LDE2My45OTYgMTc0Ljg5LDIxNC4zNDMgMjczLjgxNywxMDYuNTgzIDI4My4yMzksMTE2LjI5OSAyOTIuNjYsNjMuMDA3ICIvPg0KPC9zdmc+DQo="},"categories":[{"id":1,"name":"Marketing & Content"},{"id":6,"name":"Communication"}],"displayName":"Mautic","typeVersion":1},{"id":235,"icon":"file:wooCommerce.svg","name":"n8n-nodes-base.wooCommerceTrigger","defaults":{"name":"WooCommerce Trigger"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWlkWU1pZCIgdmVyc2lvbj0iMS4xIiB2aWV3Qm94PSIwIC01MCAyNTYgMjUzIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KPHRpdGxlPldvb0NvbW1lcmNlIExvZ288L3RpdGxlPgo8bWV0YWRhdGE+CjxyZGY6UkRGPgo8Y2M6V29yayByZGY6YWJvdXQ9IiI+CjxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0Pgo8ZGM6dHlwZSByZGY6cmVzb3VyY2U9Imh0dHA6Ly9wdXJsLm9yZy9kYy9kY21pdHlwZS9TdGlsbEltYWdlIi8+CjxkYzp0aXRsZS8+CjwvY2M6V29yaz4KPC9yZGY6UkRGPgo8L21ldGFkYXRhPgo8cGF0aCBkPSJtMjMuNzU5IDBoMjA4LjM4YzEzLjE4NyAwIDIzLjg2MyAxMC42NzUgMjMuODYzIDIzLjg2M3Y3OS41NDJjMCAxMy4xODctMTAuNjc1IDIzLjg2My0yMy44NjMgMjMuODYzaC03NC43MjdsMTAuMjU3IDI1LjExOC00NS4xMDktMjUuMTE4aC05OC42OTVjLTEzLjE4NyAwLTIzLjg2My0xMC42NzUtMjMuODYzLTIzLjg2M3YtNzkuNTQyYy0wLjEwNDY2LTEzLjA4MyAxMC41NzEtMjMuODYzIDIzLjc1OC0yMy44NjN6IiBmaWxsPSIjN2Y1NGIzIi8+CjxwYXRoIGQ9Im0xNC41NzggMjEuNzVjMS40NTY5LTEuOTc3MiAzLjY0MjMtMy4wMTc5IDYuNTU2MS0zLjIyNiA1LjMwNzMtMC40MTYyNiA4LjMyNTIgMi4wODEzIDkuMDUzNyA3LjQ5MjcgMy4yMjYgMjEuNzUgNi43NjQyIDQwLjE2OSAxMC41MTEgNTUuMjU5bDIyLjc5LTQzLjM5NWMyLjA4MTMtMy45NTQ1IDQuNjgyOS02LjAzNTggNy44MDQ5LTYuMjQzOSA0LjU3ODktMC4zMTIyIDcuMzg4NiAyLjYwMTYgOC41MzMzIDguNzQxNSAyLjYwMTYgMTMuODQxIDUuOTMxNyAyNS42IDkuODg2MiAzNS41OSAyLjcwNTctMjYuNDMzIDcuMjg0Ni00NS40NzYgMTMuNzM3LTU3LjIzNiAxLjU2MS0yLjkxMzggMy44NTA0LTQuMzcwNyA2Ljg2ODMtNC41Nzg5IDIuMzkzNS0wLjIwODEzIDQuNTc4OSAwLjUyMDMzIDYuNTU2MSAyLjA4MTMgMS45NzcyIDEuNTYxIDMuMDE3OSAzLjUzODIgMy4yMjYgNS45MzE3IDAuMTA0MDYgMS44NzMyLTAuMjA4MTMgMy40MzQxLTEuMDQwNyA0Ljk5NTEtNC4wNTg1IDcuNDkyNy03LjM4ODYgMjAuMDg1LTEwLjA5NCAzNy41NjctMi42MDE2IDE2Ljk2My0zLjUzODIgMzAuMTc5LTIuOTEzOCAzOS42NDkgMC4yMDgxMyAyLjYwMTYtMC4yMDgxMyA0Ljg5MTEtMS4yNDg4IDYuODY4My0xLjI0ODggMi4yODk0LTMuMTIyIDMuNTM4Mi01LjUxNTQgMy43NDYzLTIuNzA1NyAwLjIwODEzLTUuNTE1NC0xLjA0MDYtOC4yMjExLTMuODUwNC05LjY3OC05Ljg4NjItMTcuMzc5LTI0LjY2My0yMi45OTgtNDQuMzMyLTYuNzY0MiAxMy4zMi0xMS43NTkgMjMuMzExLTE0Ljk4NSAyOS45NzEtNi4xMzk4IDExLjc1OS0xMS4zNDMgMTcuNzk1LTE1LjcxNCAxOC4xMDctMi44MDk4IDAuMjA4MTMtNS4yMDMzLTIuMTg1NC03LjI4NDYtNy4xODA1LTUuMzA3My0xMy42MzMtMTEuMDMxLTM5Ljk2MS0xNy4xNzEtNzguOTg1LTAuNDE2MjYtMi43MDU3IDAuMjA4MTMtNS4wOTkyIDEuNjY1LTYuOTcyNHptMjIzLjY0IDE2LjMzOGMtMy43NDYzLTYuNTU2MS05LjI2MTgtMTAuNTExLTE2LjY1LTEyLjA3Mi0xLjk3NzItMC40MTYyNi0zLjg1MDQtMC42MjQzOS01LjYxOTUtMC42MjQzOS05Ljk5MDIgMC0xOC4xMDcgNS4yMDMzLTI0LjQ1NSAxNS42MS01LjQxMTQgOC44NDU1LTguMTE3MSAxOC42MjgtOC4xMTcxIDI5LjM0NiAwIDguMDEzIDEuNjY1IDE0Ljg4MSA0Ljk5NTEgMjAuNjA1IDMuNzQ2MyA2LjU1NjEgOS4yNjE4IDEwLjUxMSAxNi42NSAxMi4wNzIgMS45NzcyIDAuNDE2MjYgMy44NTA0IDAuNjI0MzkgNS42MTk1IDAuNjI0MzkgMTAuMDk0IDAgMTguMjExLTUuMjAzMyAyNC40NTUtMTUuNjEgNS40MTE0LTguOTQ5NiA4LjExNzEtMTguNzMyIDguMTE3MS0yOS40NSAwLjEwNDA2LTguMTE3MS0xLjY2NS0xNC44ODEtNC45OTUxLTIwLjUwMXptLTEzLjExMiAyOC44MjZjLTEuNDU2OSA2Ljg2ODMtNC4wNTg1IDExLjk2Ny03LjkwODkgMTUuNDAyLTMuMDE3OSAyLjcwNTctNS44Mjc2IDMuODUwNC04LjQyOTMgMy4zMzAxLTIuNDk3Ni0wLjUyMDMzLTQuNTc4OS0yLjcwNTctNi4xMzk4LTYuNzY0Mi0xLjI0ODgtMy4yMjYtMS44NzMyLTYuNDUyLTEuODczMi05LjQ2OTkgMC0yLjYwMTYgMC4yMDgxMy01LjIwMzMgMC43Mjg0Ni03LjU5NjcgMC45MzY1OS00LjI2NjcgMi43MDU3LTguNDI5MyA1LjUxNTQtMTIuMzg0IDMuNDM0MS01LjA5OTIgNy4wNzY0LTcuMTgwNSAxMC44MjMtNi40NTIgMi40OTc2IDAuNTIwMzMgNC41Nzg5IDIuNzA1NyA2LjEzOTggNi43NjQyIDEuMjQ4OCAzLjIyNiAxLjg3MzIgNi40NTIgMS44NzMyIDkuNDY5OSAwIDIuNzA1Ny0wLjIwODEzIDUuMzA3My0wLjcyODQ2IDcuNzAwOHptLTUyLjAzMy0yOC44MjZjLTMuNzQ2My02LjU1NjEtOS4zNjU5LTEwLjUxMS0xNi42NS0xMi4wNzItMS45NzcyLTAuNDE2MjYtMy44NTA0LTAuNjI0MzktNS42MTk1LTAuNjI0MzktOS45OTAyIDAtMTguMTA3IDUuMjAzMy0yNC40NTUgMTUuNjEtNS40MTE0IDguODQ1NS04LjExNzEgMTguNjI4LTguMTE3MSAyOS4zNDYgMCA4LjAxMyAxLjY2NSAxNC44ODEgNC45OTUxIDIwLjYwNSAzLjc0NjMgNi41NTYxIDkuMjYxOCAxMC41MTEgMTYuNjUgMTIuMDcyIDEuOTc3MiAwLjQxNjI2IDMuODUwNCAwLjYyNDM5IDUuNjE5NSAwLjYyNDM5IDEwLjA5NCAwIDE4LjIxMS01LjIwMzMgMjQuNDU1LTE1LjYxIDUuNDExNC04Ljk0OTYgOC4xMTcxLTE4LjczMiA4LjExNzEtMjkuNDUgMC04LjExNzEtMS42NjUtMTQuODgxLTQuOTk1MS0yMC41MDF6bS0xMy4yMTYgMjguODI2Yy0xLjQ1NjkgNi44NjgzLTQuMDU4NSAxMS45NjctNy45MDg5IDE1LjQwMi0zLjAxNzkgMi43MDU3LTUuODI3NiAzLjg1MDQtOC40MjkzIDMuMzMwMS0yLjQ5NzYtMC41MjAzMy00LjU3ODktMi43MDU3LTYuMTM5OC02Ljc2NDItMS4yNDg4LTMuMjI2LTEuODczMi02LjQ1Mi0xLjg3MzItOS40Njk5IDAtMi42MDE2IDAuMjA4MTMtNS4yMDMzIDAuNzI4NDYtNy41OTY3IDAuOTM2NTgtNC4yNjY3IDIuNzA1Ny04LjQyOTMgNS41MTU0LTEyLjM4NCAzLjQzNDEtNS4wOTkyIDcuMDc2NC03LjE4MDUgMTAuODIzLTYuNDUyIDIuNDk3NiAwLjUyMDMzIDQuNTc4OSAyLjcwNTcgNi4xMzk4IDYuNzY0MiAxLjI0ODggMy4yMjYgMS44NzMyIDYuNDUyIDEuODczMiA5LjQ2OTkgMC4xMDQwNiAyLjcwNTctMC4yMDgxMyA1LjMwNzMtMC43Mjg0NiA3LjcwMDh6IiBmaWxsPSIjZmZmIi8+Cjwvc3ZnPgo="},"categories":[{"id":2,"name":"Sales"}],"displayName":"WooCommerce Trigger","typeVersion":1}],"categories":[{"id":2,"name":"Sales"}],"image":[]},{"id":1459,"name":"Notify on Telegram and Twitter when new order is added in WooCommerce","views":620,"recentViews":9823,"totalViews":620,"createdAt":"2022-02-17T15:02:14.961Z","description":"This workflow uses a WooCommerce trigger that will run a new product has been added, It will then post the product to Telegram and Twitter.\n\nTo use this workflow you will need to set the credentials to use for the WooCommerce, Twitter and Telegram nodes.","workflow":{"id":85,"name":"New WooCommerce Product to Twitter and Telegram","nodes":[{"name":"Twitter","type":"n8n-nodes-base.twitter","position":[720,300],"parameters":{"text":"=✨ New Product Announcement ✨\nWe have just added {{$json[\"name\"]}}, Head to {{$json[\"permalink\"]}} to find out more.","additionalFields":{}},"credentials":{"twitterOAuth1Api":{"id":"37","name":"joffcom"}},"typeVersion":1},{"name":"Telegram","type":"n8n-nodes-base.telegram","position":[720,500],"parameters":{"text":"=✨ New Product Announcement ✨\nWe have just added {{$json[\"name\"]}}, Head to {{$json[\"permalink\"]}} to find out more.","chatId":"123456","additionalFields":{}},"credentials":{"telegramApi":{"id":"56","name":"Telegram account"}},"typeVersion":1},{"name":"WooCommerce Trigger","type":"n8n-nodes-base.wooCommerceTrigger","position":[540,400],"webhookId":"ab7b134b-9b2d-4e0d-b496-1aee30db0808","parameters":{"event":"product.created"},"credentials":{"wooCommerceApi":{"id":"48","name":"WooCommerce account"}},"typeVersion":1}],"active":false,"settings":{},"connections":{"WooCommerce Trigger":{"main":[[{"node":"Twitter","type":"main","index":0},{"node":"Telegram","type":"main","index":0}]]}}},"workflowInfo":{"nodeCount":4,"nodeTypes":{"n8n-nodes-base.twitter":{"count":1},"n8n-nodes-base.telegram":{"count":1},"n8n-nodes-base.wooCommerceTrigger":{"count":1}}},"user":{"username":"jon-n8n"},"nodes":[{"id":42,"icon":"fa:play","name":"n8n-nodes-base.start","defaults":{"name":"Start","color":"#00e000"},"iconData":{"icon":"play","type":"icon"},"categories":[{"id":9,"name":"Core Nodes"}],"displayName":"Start","typeVersion":1},{"id":49,"icon":"file:telegram.svg","name":"n8n-nodes-base.telegram","defaults":{"name":"Telegram"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgNjYgNjYiIGZpbGw9IiNmZmYiIGZpbGwtcnVsZT0iZXZlbm9kZCIgc3Ryb2tlPSIjMDAwIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjx1c2UgeGxpbms6aHJlZj0iI2EiIHg9Ii41IiB5PSIuNSIvPjxzeW1ib2wgaWQ9ImEiIG92ZXJmbG93PSJ2aXNpYmxlIj48ZyBzdHJva2U9Im5vbmUiIGZpbGwtcnVsZT0ibm9uemVybyI+PHBhdGggZD0iTTAgMzJjMCAxNy42NzMgMTQuMzI3IDMyIDMyIDMyczMyLTE0LjMyNyAzMi0zMlM0OS42NzMgMCAzMiAwIDAgMTQuMzI3IDAgMzIiIGZpbGw9IiMzN2FlZTIiLz48cGF0aCBkPSJNMjEuNjYxIDM0LjMzOGwzLjc5NyAxMC41MDhzLjQ3NS45ODMuOTgzLjk4MyA4LjA2OC03Ljg2NCA4LjA2OC03Ljg2NGw4LjQwNy0xNi4yMzctMjEuMTE5IDkuODk4eiIgZmlsbD0iI2M4ZGFlYSIvPjxwYXRoIGQ9Ik0yNi42OTUgMzcuMDM0bC0uNzI5IDcuNzQ2cy0uMzA1IDIuMzczIDIuMDY4IDBsNC42NDQtNC4yMDMiIGZpbGw9IiNhOWM2ZDgiLz48cGF0aCBkPSJNMjEuNzMgMzQuNzEybC03LjgwOS0yLjU0NXMtLjkzMi0uMzc4LS42MzMtMS4yMzdjLjA2Mi0uMTc3LjE4Ni0uMzI4LjU1OS0uNTg4IDEuNzMxLTEuMjA2IDMyLjAyOC0xMi4wOTYgMzIuMDI4LTEyLjA5NnMuODU2LS4yODggMS4zNjEtLjA5N2MuMjMxLjA4OC4zNzguMTg3LjUwMy41NDguMDQ1LjEzMi4wNzEuNDExLjA2OC42ODktLjAwMy4yMDEtLjAyNy4zODYtLjA0NS42NzgtLjE4NCAyLjk3OC01LjcwNiAyNS4xOTgtNS43MDYgMjUuMTk4cy0uMzMgMS4zLTEuNTE0IDEuMzQ1Yy0uNDMyLjAxNi0uOTU2LS4wNzEtMS41ODItLjYxLTIuMzIzLTEuOTk4LTEwLjM1Mi03LjM5NC0xMi4xMjYtOC41OGEuMzQuMzQgMCAwMS0uMTQ2LS4yMzljLS4wMjUtLjEyNS4xMDgtLjI4LjEwOC0uMjhzMTMuOTgtMTIuNDI3IDE0LjM1Mi0xMy43MzFjLjAyOS0uMTAxLS4wNzktLjE1MS0uMjI2LS4xMDctLjkyOS4zNDItMTcuMDI1IDEwLjUwNi0xOC44MDEgMTEuNjI5LS4xMDQuMDY2LS4zOTUuMDIzLS4zOTUuMDIzIi8+PC9nPjwvc3ltYm9sPjwvc3ZnPg=="},"categories":[{"id":6,"name":"Communication"}],"displayName":"Telegram","typeVersion":1},{"id":235,"icon":"file:wooCommerce.svg","name":"n8n-nodes-base.wooCommerceTrigger","defaults":{"name":"WooCommerce Trigger"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWlkWU1pZCIgdmVyc2lvbj0iMS4xIiB2aWV3Qm94PSIwIC01MCAyNTYgMjUzIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KPHRpdGxlPldvb0NvbW1lcmNlIExvZ288L3RpdGxlPgo8bWV0YWRhdGE+CjxyZGY6UkRGPgo8Y2M6V29yayByZGY6YWJvdXQ9IiI+CjxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0Pgo8ZGM6dHlwZSByZGY6cmVzb3VyY2U9Imh0dHA6Ly9wdXJsLm9yZy9kYy9kY21pdHlwZS9TdGlsbEltYWdlIi8+CjxkYzp0aXRsZS8+CjwvY2M6V29yaz4KPC9yZGY6UkRGPgo8L21ldGFkYXRhPgo8cGF0aCBkPSJtMjMuNzU5IDBoMjA4LjM4YzEzLjE4NyAwIDIzLjg2MyAxMC42NzUgMjMuODYzIDIzLjg2M3Y3OS41NDJjMCAxMy4xODctMTAuNjc1IDIzLjg2My0yMy44NjMgMjMuODYzaC03NC43MjdsMTAuMjU3IDI1LjExOC00NS4xMDktMjUuMTE4aC05OC42OTVjLTEzLjE4NyAwLTIzLjg2My0xMC42NzUtMjMuODYzLTIzLjg2M3YtNzkuNTQyYy0wLjEwNDY2LTEzLjA4MyAxMC41NzEtMjMuODYzIDIzLjc1OC0yMy44NjN6IiBmaWxsPSIjN2Y1NGIzIi8+CjxwYXRoIGQ9Im0xNC41NzggMjEuNzVjMS40NTY5LTEuOTc3MiAzLjY0MjMtMy4wMTc5IDYuNTU2MS0zLjIyNiA1LjMwNzMtMC40MTYyNiA4LjMyNTIgMi4wODEzIDkuMDUzNyA3LjQ5MjcgMy4yMjYgMjEuNzUgNi43NjQyIDQwLjE2OSAxMC41MTEgNTUuMjU5bDIyLjc5LTQzLjM5NWMyLjA4MTMtMy45NTQ1IDQuNjgyOS02LjAzNTggNy44MDQ5LTYuMjQzOSA0LjU3ODktMC4zMTIyIDcuMzg4NiAyLjYwMTYgOC41MzMzIDguNzQxNSAyLjYwMTYgMTMuODQxIDUuOTMxNyAyNS42IDkuODg2MiAzNS41OSAyLjcwNTctMjYuNDMzIDcuMjg0Ni00NS40NzYgMTMuNzM3LTU3LjIzNiAxLjU2MS0yLjkxMzggMy44NTA0LTQuMzcwNyA2Ljg2ODMtNC41Nzg5IDIuMzkzNS0wLjIwODEzIDQuNTc4OSAwLjUyMDMzIDYuNTU2MSAyLjA4MTMgMS45NzcyIDEuNTYxIDMuMDE3OSAzLjUzODIgMy4yMjYgNS45MzE3IDAuMTA0MDYgMS44NzMyLTAuMjA4MTMgMy40MzQxLTEuMDQwNyA0Ljk5NTEtNC4wNTg1IDcuNDkyNy03LjM4ODYgMjAuMDg1LTEwLjA5NCAzNy41NjctMi42MDE2IDE2Ljk2My0zLjUzODIgMzAuMTc5LTIuOTEzOCAzOS42NDkgMC4yMDgxMyAyLjYwMTYtMC4yMDgxMyA0Ljg5MTEtMS4yNDg4IDYuODY4My0xLjI0ODggMi4yODk0LTMuMTIyIDMuNTM4Mi01LjUxNTQgMy43NDYzLTIuNzA1NyAwLjIwODEzLTUuNTE1NC0xLjA0MDYtOC4yMjExLTMuODUwNC05LjY3OC05Ljg4NjItMTcuMzc5LTI0LjY2My0yMi45OTgtNDQuMzMyLTYuNzY0MiAxMy4zMi0xMS43NTkgMjMuMzExLTE0Ljk4NSAyOS45NzEtNi4xMzk4IDExLjc1OS0xMS4zNDMgMTcuNzk1LTE1LjcxNCAxOC4xMDctMi44MDk4IDAuMjA4MTMtNS4yMDMzLTIuMTg1NC03LjI4NDYtNy4xODA1LTUuMzA3My0xMy42MzMtMTEuMDMxLTM5Ljk2MS0xNy4xNzEtNzguOTg1LTAuNDE2MjYtMi43MDU3IDAuMjA4MTMtNS4wOTkyIDEuNjY1LTYuOTcyNHptMjIzLjY0IDE2LjMzOGMtMy43NDYzLTYuNTU2MS05LjI2MTgtMTAuNTExLTE2LjY1LTEyLjA3Mi0xLjk3NzItMC40MTYyNi0zLjg1MDQtMC42MjQzOS01LjYxOTUtMC42MjQzOS05Ljk5MDIgMC0xOC4xMDcgNS4yMDMzLTI0LjQ1NSAxNS42MS01LjQxMTQgOC44NDU1LTguMTE3MSAxOC42MjgtOC4xMTcxIDI5LjM0NiAwIDguMDEzIDEuNjY1IDE0Ljg4MSA0Ljk5NTEgMjAuNjA1IDMuNzQ2MyA2LjU1NjEgOS4yNjE4IDEwLjUxMSAxNi42NSAxMi4wNzIgMS45NzcyIDAuNDE2MjYgMy44NTA0IDAuNjI0MzkgNS42MTk1IDAuNjI0MzkgMTAuMDk0IDAgMTguMjExLTUuMjAzMyAyNC40NTUtMTUuNjEgNS40MTE0LTguOTQ5NiA4LjExNzEtMTguNzMyIDguMTE3MS0yOS40NSAwLjEwNDA2LTguMTE3MS0xLjY2NS0xNC44ODEtNC45OTUxLTIwLjUwMXptLTEzLjExMiAyOC44MjZjLTEuNDU2OSA2Ljg2ODMtNC4wNTg1IDExLjk2Ny03LjkwODkgMTUuNDAyLTMuMDE3OSAyLjcwNTctNS44Mjc2IDMuODUwNC04LjQyOTMgMy4zMzAxLTIuNDk3Ni0wLjUyMDMzLTQuNTc4OS0yLjcwNTctNi4xMzk4LTYuNzY0Mi0xLjI0ODgtMy4yMjYtMS44NzMyLTYuNDUyLTEuODczMi05LjQ2OTkgMC0yLjYwMTYgMC4yMDgxMy01LjIwMzMgMC43Mjg0Ni03LjU5NjcgMC45MzY1OS00LjI2NjcgMi43MDU3LTguNDI5MyA1LjUxNTQtMTIuMzg0IDMuNDM0MS01LjA5OTIgNy4wNzY0LTcuMTgwNSAxMC44MjMtNi40NTIgMi40OTc2IDAuNTIwMzMgNC41Nzg5IDIuNzA1NyA2LjEzOTggNi43NjQyIDEuMjQ4OCAzLjIyNiAxLjg3MzIgNi40NTIgMS44NzMyIDkuNDY5OSAwIDIuNzA1Ny0wLjIwODEzIDUuMzA3My0wLjcyODQ2IDcuNzAwOHptLTUyLjAzMy0yOC44MjZjLTMuNzQ2My02LjU1NjEtOS4zNjU5LTEwLjUxMS0xNi42NS0xMi4wNzItMS45NzcyLTAuNDE2MjYtMy44NTA0LTAuNjI0MzktNS42MTk1LTAuNjI0MzktOS45OTAyIDAtMTguMTA3IDUuMjAzMy0yNC40NTUgMTUuNjEtNS40MTE0IDguODQ1NS04LjExNzEgMTguNjI4LTguMTE3MSAyOS4zNDYgMCA4LjAxMyAxLjY2NSAxNC44ODEgNC45OTUxIDIwLjYwNSAzLjc0NjMgNi41NTYxIDkuMjYxOCAxMC41MTEgMTYuNjUgMTIuMDcyIDEuOTc3MiAwLjQxNjI2IDMuODUwNCAwLjYyNDM5IDUuNjE5NSAwLjYyNDM5IDEwLjA5NCAwIDE4LjIxMS01LjIwMzMgMjQuNDU1LTE1LjYxIDUuNDExNC04Ljk0OTYgOC4xMTcxLTE4LjczMiA4LjExNzEtMjkuNDUgMC04LjExNzEtMS42NjUtMTQuODgxLTQuOTk1MS0yMC41MDF6bS0xMy4yMTYgMjguODI2Yy0xLjQ1NjkgNi44NjgzLTQuMDU4NSAxMS45NjctNy45MDg5IDE1LjQwMi0zLjAxNzkgMi43MDU3LTUuODI3NiAzLjg1MDQtOC40MjkzIDMuMzMwMS0yLjQ5NzYtMC41MjAzMy00LjU3ODktMi43MDU3LTYuMTM5OC02Ljc2NDItMS4yNDg4LTMuMjI2LTEuODczMi02LjQ1Mi0xLjg3MzItOS40Njk5IDAtMi42MDE2IDAuMjA4MTMtNS4yMDMzIDAuNzI4NDYtNy41OTY3IDAuOTM2NTgtNC4yNjY3IDIuNzA1Ny04LjQyOTMgNS41MTU0LTEyLjM4NCAzLjQzNDEtNS4wOTkyIDcuMDc2NC03LjE4MDUgMTAuODIzLTYuNDUyIDIuNDk3NiAwLjUyMDMzIDQuNTc4OSAyLjcwNTcgNi4xMzk4IDYuNzY0MiAxLjI0ODggMy4yMjYgMS44NzMyIDYuNDUyIDEuODczMiA5LjQ2OTkgMC4xMDQwNiAyLjcwNTctMC4yMDgxMyA1LjMwNzMtMC43Mjg0NiA3LjcwMDh6IiBmaWxsPSIjZmZmIi8+Cjwvc3ZnPgo="},"categories":[{"id":2,"name":"Sales"}],"displayName":"WooCommerce Trigger","typeVersion":1},{"id":325,"icon":"file:x.svg","name":"n8n-nodes-base.twitter","defaults":{"name":"X"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjQgMjQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTE4LjI0NCAyLjI1aDMuMzA4bC03LjIyNyA4LjI2IDguNTAyIDExLjI0SDE2LjE3bC01LjIxNC02LjgxN0w0Ljk5IDIxLjc1SDEuNjhsNy43My04LjgzNUwxLjI1NCAyLjI1SDguMDhsNC43MTMgNi4yMzF6bS0xLjE2MSAxNy41MmgxLjgzM0w3LjA4NCA0LjEyNkg1LjExN3oiPjwvcGF0aD48L3N2Zz4K"},"categories":[{"id":1,"name":"Marketing & Content"}],"displayName":"X (Formerly Twitter)","typeVersion":2}],"categories":[{"id":2,"name":"Sales"},{"id":19,"name":"Marketing & Growth"}],"image":[]},{"id":1457,"name":"Notify on Slack when new order is registered in WooCommerce","views":178,"recentViews":9787,"totalViews":178,"createdAt":"2022-02-17T15:01:13.489Z","description":"This workflow uses a WooCommerce trigger that will run when an order has been placed.\n\nIf the value of this is over 100 it will post it to a Slack channel.\n\nTo use this workflow you will need to set the credentials to use for the WooCommerce and Slack nodes, You will also need to pick a channel to post the message to.","workflow":{"id":81,"name":"New WooCommerce order to Slack","nodes":[{"name":"Order Created","type":"n8n-nodes-base.wooCommerceTrigger","position":[340,500],"webhookId":"287b4bf4-67ec-4c97-85d9-c0d3e6f59e6b","parameters":{"event":"order.created"},"credentials":{"wooCommerceApi":{"id":"48","name":"WooCommerce account"}},"typeVersion":1},{"name":"Send to Slack","type":"n8n-nodes-base.slack","position":[780,480],"parameters":{"text":":sparkles: There is a new order :sparkles:","channel":"woo-commerce","blocksUi":{"blocksValues":[]},"attachments":[{"color":"#66FF00","fields":{"item":[{"short":true,"title":"Order ID","value":"={{$json[\"id\"]}}"},{"short":true,"title":"Status","value":"={{$json[\"status\"]}}"},{"short":true,"title":"Total","value":"={{$json[\"currency_symbol\"]}}{{$json[\"total\"]}}"},{"short":false,"title":"Link","value":"={{$node[\"Order Created\"].json[\"_links\"][\"self\"][0][\"href\"]}}"}]},"footer":"=*Ordered:* {{$json[\"date_created\"]}} | *Transaction ID:* {{$json[\"transaction_id\"]}}"}],"otherOptions":{}},"credentials":{"slackApi":{"id":"53","name":"Slack Access Token"}},"typeVersion":1},{"name":"Price over 100","type":"n8n-nodes-base.if","position":[540,500],"parameters":{"conditions":{"number":[{"value1":"={{$json[\"total\"]}}","value2":100,"operation":"largerEqual"}]}},"typeVersion":1}],"active":false,"settings":{"saveManualExecutions":true,"saveExecutionProgress":true,"saveDataSuccessExecution":"all"},"connections":{"Order Created":{"main":[[{"node":"Price over 100","type":"main","index":0}]]},"Price over 100":{"main":[[{"node":"Send to Slack","type":"main","index":0}],[]]}}},"workflowInfo":{"nodeCount":4,"nodeTypes":{"n8n-nodes-base.if":{"count":1},"n8n-nodes-base.slack":{"count":1},"n8n-nodes-base.wooCommerceTrigger":{"count":1}}},"user":{"username":"jon-n8n"},"nodes":[{"id":20,"icon":"fa:map-signs","name":"n8n-nodes-base.if","defaults":{"name":"IF","color":"#408000"},"iconData":{"icon":"map-signs","type":"icon"},"categories":[{"id":9,"name":"Core Nodes"}],"displayName":"IF","typeVersion":1},{"id":40,"icon":"file:slack.svg","name":"n8n-nodes-base.slack","defaults":{"name":"Slack"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgMTUwLjg1MiAxNTAuODUyIiBmaWxsPSIjZmZmIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIj48dXNlIHhsaW5rOmhyZWY9IiNhIiB4PSIuOTI2IiB5PSIuOTI2Ii8+PHN5bWJvbCBpZD0iYSIgb3ZlcmZsb3c9InZpc2libGUiPjxnIHN0cm9rZS13aWR0aD0iMS44NTIiPjxwYXRoIGQ9Ik00MC43NDEgOTMuNTVjMC04LjczNSA2LjYwNy0xNS43NzIgMTQuODE1LTE1Ljc3MnMxNC44MTUgNy4wMzcgMTQuODE1IDE1Ljc3MnYzOC44MjRjMCA4LjczNy02LjYwNyAxNS43NzQtMTQuODE1IDE1Ljc3NHMtMTQuODE1LTcuMDM3LTE0LjgxNS0xNS43NzJ6IiBmaWxsPSIjZTAxZTVhIiBzdHJva2U9IiNlMDFlNWEiLz48cGF0aCBkPSJNOTMuNTUgMTA3LjQwOGMtOC43MzUgMC0xNS43NzItNi42MDctMTUuNzcyLTE0LjgxNXM3LjAzNy0xNC44MTUgMTUuNzcyLTE0LjgxNWgzOC44MjZjOC43MzUgMCAxNS43NzIgNi42MDcgMTUuNzcyIDE0LjgxNXMtNy4wMzcgMTQuODE1LTE1Ljc3MiAxNC44MTV6IiBmaWxsPSIjZWNiMjJkIiBzdHJva2U9IiNlY2IyMmQiLz48cGF0aCBkPSJNNzcuNzc4IDE1Ljc3MkM3Ny43NzggNy4wMzcgODQuMzg1IDAgOTIuNTkzIDBzMTQuODE1IDcuMDM3IDE0LjgxNSAxNS43NzJ2MzguODI2YzAgOC43MzUtNi42MDcgMTUuNzcyLTE0LjgxNSAxNS43NzJzLTE0LjgxNS03LjAzNy0xNC44MTUtMTUuNzcyeiIgZmlsbD0iIzJmYjY3YyIgc3Ryb2tlPSIjMmZiNjdjIi8+PHBhdGggZD0iTTE1Ljc3MiA3MC4zNzFDNy4wMzcgNzAuMzcxIDAgNjMuNzYzIDAgNTUuNTU2czcuMDM3LTE0LjgxNSAxNS43NzItMTQuODE1aDM4LjgyNmM4LjczNSAwIDE1Ljc3MiA2LjYwNyAxNS43NzIgMTQuODE1cy03LjAzNyAxNC44MTUtMTUuNzcyIDE0LjgxNXoiIGZpbGw9IiMzNmM1ZjEiIHN0cm9rZT0iIzM2YzVmMSIvPjxnIHN0cm9rZS1saW5lam9pbj0ibWl0ZXIiPjxwYXRoIGQ9Ik03Ny43NzggMTMzLjMzM2MwIDguMjA4IDYuNjA3IDE0LjgxNSAxNC44MTUgMTQuODE1czE0LjgxNS02LjYwNyAxNC44MTUtMTQuODE1LTYuNjA3LTE0LjgxNS0xNC44MTUtMTQuODE1SDc3Ljc3OHoiIGZpbGw9IiNlY2IyMmQiIHN0cm9rZT0iI2VjYjIyZCIvPjxwYXRoIGQ9Ik0xMzMuMzM0IDcwLjM3MWgtMTQuODE1VjU1LjU1NmMwLTguMjA3IDYuNjA3LTE0LjgxNSAxNC44MTUtMTQuODE1czE0LjgxNSA2LjYwNyAxNC44MTUgMTQuODE1LTYuNjA3IDE0LjgxNS0xNC44MTUgMTQuODE1eiIgZmlsbD0iIzJmYjY3YyIgc3Ryb2tlPSIjMmZiNjdjIi8+PHBhdGggZD0iTTE0LjgxNSA3Ny43NzhIMjkuNjN2MTQuODE1YzAgOC4yMDctNi42MDcgMTQuODE1LTE0LjgxNSAxNC44MTVTMCAxMDAuOCAwIDkyLjU5M3M2LjYwNy0xNC44MTUgMTQuODE1LTE0LjgxNXoiIGZpbGw9IiNlMDFlNWEiIHN0cm9rZT0iI2UwMWU1YSIvPjxwYXRoIGQ9Ik03MC4zNzEgMTQuODE1VjI5LjYzSDU1LjU1NmMtOC4yMDcgMC0xNC44MTUtNi42MDctMTQuODE1LTE0LjgxNVM0Ny4zNDggMCA1NS41NTYgMHMxNC44MTUgNi42MDcgMTQuODE1IDE0LjgxNXoiIGZpbGw9IiMzNmM1ZjEiIHN0cm9rZT0iIzM2YzVmMSIvPjwvZz48L2c+PC9zeW1ib2w+PC9zdmc+"},"categories":[{"id":6,"name":"Communication"}],"displayName":"Slack","typeVersion":2},{"id":42,"icon":"fa:play","name":"n8n-nodes-base.start","defaults":{"name":"Start","color":"#00e000"},"iconData":{"icon":"play","type":"icon"},"categories":[{"id":9,"name":"Core Nodes"}],"displayName":"Start","typeVersion":1},{"id":235,"icon":"file:wooCommerce.svg","name":"n8n-nodes-base.wooCommerceTrigger","defaults":{"name":"WooCommerce Trigger"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWlkWU1pZCIgdmVyc2lvbj0iMS4xIiB2aWV3Qm94PSIwIC01MCAyNTYgMjUzIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KPHRpdGxlPldvb0NvbW1lcmNlIExvZ288L3RpdGxlPgo8bWV0YWRhdGE+CjxyZGY6UkRGPgo8Y2M6V29yayByZGY6YWJvdXQ9IiI+CjxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0Pgo8ZGM6dHlwZSByZGY6cmVzb3VyY2U9Imh0dHA6Ly9wdXJsLm9yZy9kYy9kY21pdHlwZS9TdGlsbEltYWdlIi8+CjxkYzp0aXRsZS8+CjwvY2M6V29yaz4KPC9yZGY6UkRGPgo8L21ldGFkYXRhPgo8cGF0aCBkPSJtMjMuNzU5IDBoMjA4LjM4YzEzLjE4NyAwIDIzLjg2MyAxMC42NzUgMjMuODYzIDIzLjg2M3Y3OS41NDJjMCAxMy4xODctMTAuNjc1IDIzLjg2My0yMy44NjMgMjMuODYzaC03NC43MjdsMTAuMjU3IDI1LjExOC00NS4xMDktMjUuMTE4aC05OC42OTVjLTEzLjE4NyAwLTIzLjg2My0xMC42NzUtMjMuODYzLTIzLjg2M3YtNzkuNTQyYy0wLjEwNDY2LTEzLjA4MyAxMC41NzEtMjMuODYzIDIzLjc1OC0yMy44NjN6IiBmaWxsPSIjN2Y1NGIzIi8+CjxwYXRoIGQ9Im0xNC41NzggMjEuNzVjMS40NTY5LTEuOTc3MiAzLjY0MjMtMy4wMTc5IDYuNTU2MS0zLjIyNiA1LjMwNzMtMC40MTYyNiA4LjMyNTIgMi4wODEzIDkuMDUzNyA3LjQ5MjcgMy4yMjYgMjEuNzUgNi43NjQyIDQwLjE2OSAxMC41MTEgNTUuMjU5bDIyLjc5LTQzLjM5NWMyLjA4MTMtMy45NTQ1IDQuNjgyOS02LjAzNTggNy44MDQ5LTYuMjQzOSA0LjU3ODktMC4zMTIyIDcuMzg4NiAyLjYwMTYgOC41MzMzIDguNzQxNSAyLjYwMTYgMTMuODQxIDUuOTMxNyAyNS42IDkuODg2MiAzNS41OSAyLjcwNTctMjYuNDMzIDcuMjg0Ni00NS40NzYgMTMuNzM3LTU3LjIzNiAxLjU2MS0yLjkxMzggMy44NTA0LTQuMzcwNyA2Ljg2ODMtNC41Nzg5IDIuMzkzNS0wLjIwODEzIDQuNTc4OSAwLjUyMDMzIDYuNTU2MSAyLjA4MTMgMS45NzcyIDEuNTYxIDMuMDE3OSAzLjUzODIgMy4yMjYgNS45MzE3IDAuMTA0MDYgMS44NzMyLTAuMjA4MTMgMy40MzQxLTEuMDQwNyA0Ljk5NTEtNC4wNTg1IDcuNDkyNy03LjM4ODYgMjAuMDg1LTEwLjA5NCAzNy41NjctMi42MDE2IDE2Ljk2My0zLjUzODIgMzAuMTc5LTIuOTEzOCAzOS42NDkgMC4yMDgxMyAyLjYwMTYtMC4yMDgxMyA0Ljg5MTEtMS4yNDg4IDYuODY4My0xLjI0ODggMi4yODk0LTMuMTIyIDMuNTM4Mi01LjUxNTQgMy43NDYzLTIuNzA1NyAwLjIwODEzLTUuNTE1NC0xLjA0MDYtOC4yMjExLTMuODUwNC05LjY3OC05Ljg4NjItMTcuMzc5LTI0LjY2My0yMi45OTgtNDQuMzMyLTYuNzY0MiAxMy4zMi0xMS43NTkgMjMuMzExLTE0Ljk4NSAyOS45NzEtNi4xMzk4IDExLjc1OS0xMS4zNDMgMTcuNzk1LTE1LjcxNCAxOC4xMDctMi44MDk4IDAuMjA4MTMtNS4yMDMzLTIuMTg1NC03LjI4NDYtNy4xODA1LTUuMzA3My0xMy42MzMtMTEuMDMxLTM5Ljk2MS0xNy4xNzEtNzguOTg1LTAuNDE2MjYtMi43MDU3IDAuMjA4MTMtNS4wOTkyIDEuNjY1LTYuOTcyNHptMjIzLjY0IDE2LjMzOGMtMy43NDYzLTYuNTU2MS05LjI2MTgtMTAuNTExLTE2LjY1LTEyLjA3Mi0xLjk3NzItMC40MTYyNi0zLjg1MDQtMC42MjQzOS01LjYxOTUtMC42MjQzOS05Ljk5MDIgMC0xOC4xMDcgNS4yMDMzLTI0LjQ1NSAxNS42MS01LjQxMTQgOC44NDU1LTguMTE3MSAxOC42MjgtOC4xMTcxIDI5LjM0NiAwIDguMDEzIDEuNjY1IDE0Ljg4MSA0Ljk5NTEgMjAuNjA1IDMuNzQ2MyA2LjU1NjEgOS4yNjE4IDEwLjUxMSAxNi42NSAxMi4wNzIgMS45NzcyIDAuNDE2MjYgMy44NTA0IDAuNjI0MzkgNS42MTk1IDAuNjI0MzkgMTAuMDk0IDAgMTguMjExLTUuMjAzMyAyNC40NTUtMTUuNjEgNS40MTE0LTguOTQ5NiA4LjExNzEtMTguNzMyIDguMTE3MS0yOS40NSAwLjEwNDA2LTguMTE3MS0xLjY2NS0xNC44ODEtNC45OTUxLTIwLjUwMXptLTEzLjExMiAyOC44MjZjLTEuNDU2OSA2Ljg2ODMtNC4wNTg1IDExLjk2Ny03LjkwODkgMTUuNDAyLTMuMDE3OSAyLjcwNTctNS44Mjc2IDMuODUwNC04LjQyOTMgMy4zMzAxLTIuNDk3Ni0wLjUyMDMzLTQuNTc4OS0yLjcwNTctNi4xMzk4LTYuNzY0Mi0xLjI0ODgtMy4yMjYtMS44NzMyLTYuNDUyLTEuODczMi05LjQ2OTkgMC0yLjYwMTYgMC4yMDgxMy01LjIwMzMgMC43Mjg0Ni03LjU5NjcgMC45MzY1OS00LjI2NjcgMi43MDU3LTguNDI5MyA1LjUxNTQtMTIuMzg0IDMuNDM0MS01LjA5OTIgNy4wNzY0LTcuMTgwNSAxMC44MjMtNi40NTIgMi40OTc2IDAuNTIwMzMgNC41Nzg5IDIuNzA1NyA2LjEzOTggNi43NjQyIDEuMjQ4OCAzLjIyNiAxLjg3MzIgNi40NTIgMS44NzMyIDkuNDY5OSAwIDIuNzA1Ny0wLjIwODEzIDUuMzA3My0wLjcyODQ2IDcuNzAwOHptLTUyLjAzMy0yOC44MjZjLTMuNzQ2My02LjU1NjEtOS4zNjU5LTEwLjUxMS0xNi42NS0xMi4wNzItMS45NzcyLTAuNDE2MjYtMy44NTA0LTAuNjI0MzktNS42MTk1LTAuNjI0MzktOS45OTAyIDAtMTguMTA3IDUuMjAzMy0yNC40NTUgMTUuNjEtNS40MTE0IDguODQ1NS04LjExNzEgMTguNjI4LTguMTE3MSAyOS4zNDYgMCA4LjAxMyAxLjY2NSAxNC44ODEgNC45OTUxIDIwLjYwNSAzLjc0NjMgNi41NTYxIDkuMjYxOCAxMC41MTEgMTYuNjUgMTIuMDcyIDEuOTc3MiAwLjQxNjI2IDMuODUwNCAwLjYyNDM5IDUuNjE5NSAwLjYyNDM5IDEwLjA5NCAwIDE4LjIxMS01LjIwMzMgMjQuNDU1LTE1LjYxIDUuNDExNC04Ljk0OTYgOC4xMTcxLTE4LjczMiA4LjExNzEtMjkuNDUgMC04LjExNzEtMS42NjUtMTQuODgxLTQuOTk1MS0yMC41MDF6bS0xMy4yMTYgMjguODI2Yy0xLjQ1NjkgNi44NjgzLTQuMDU4NSAxMS45NjctNy45MDg5IDE1LjQwMi0zLjAxNzkgMi43MDU3LTUuODI3NiAzLjg1MDQtOC40MjkzIDMuMzMwMS0yLjQ5NzYtMC41MjAzMy00LjU3ODktMi43MDU3LTYuMTM5OC02Ljc2NDItMS4yNDg4LTMuMjI2LTEuODczMi02LjQ1Mi0xLjg3MzItOS40Njk5IDAtMi42MDE2IDAuMjA4MTMtNS4yMDMzIDAuNzI4NDYtNy41OTY3IDAuOTM2NTgtNC4yNjY3IDIuNzA1Ny04LjQyOTMgNS41MTU0LTEyLjM4NCAzLjQzNDEtNS4wOTkyIDcuMDc2NC03LjE4MDUgMTAuODIzLTYuNDUyIDIuNDk3NiAwLjUyMDMzIDQuNTc4OSAyLjcwNTcgNi4xMzk4IDYuNzY0MiAxLjI0ODggMy4yMjYgMS44NzMyIDYuNDUyIDEuODczMiA5LjQ2OTkgMC4xMDQwNiAyLjcwNTctMC4yMDgxMyA1LjMwNzMtMC43Mjg0NiA3LjcwMDh6IiBmaWxsPSIjZmZmIi8+Cjwvc3ZnPgo="},"categories":[{"id":2,"name":"Sales"}],"displayName":"WooCommerce Trigger","typeVersion":1}],"categories":[{"id":2,"name":"Sales"}],"image":[]},{"id":1765,"name":"Get Slack notifications when new product published on WooCommerce","views":79,"recentViews":9577,"totalViews":79,"createdAt":"2022-08-12T12:36:53.409Z","description":"This workflow let's a bot in Slack notify a specific channel when a new product in WooCommerce is published and live on the site. \n\n## Prerequisites\n\n[WooCommerce](https://docs.n8n.io/integrations/builtin/trigger-nodes/n8n-nodes-base.woocommercetrigger/) account\n[Slack](https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.slack/) and a [Slack bot](https://slack.com/help/articles/115005265703-Create-a-bot-for-your-workspace)\n\n## How it works\n\n1. Listen for WooCommerce product creation\n2. If permalink starts with https://[your-url-here].com/product/\n3. Slack bot notifies channel that a new product has been added. \n\nPlease note, you must update the URL in the IF node to match your url. If your WooCommerce doesn't use the slug /product/, that will need to be updated too. \n","workflow":{"id":1016,"name":"Woocommerce to slack: notify new product created","tags":[{"id":"5","name":"FVF","createdAt":"2022-07-30T07:43:44.795Z","updatedAt":"2022-07-30T07:43:44.795Z"}],"nodes":[{"name":"If URL has /product/","type":"n8n-nodes-base.if","position":[640,300],"parameters":{"conditions":{"string":[{"value1":"={{$json[\"permalink\"]}}","value2":"https://[add-your-url-here]/product/","operation":"startsWith"}]}},"typeVersion":1},{"name":"Send message to slack","type":"n8n-nodes-base.slack","position":[920,260],"parameters":{"text":":new: A new product has been added! :new:","channel":"newproducts","blocksUi":{"blocksValues":[]},"attachments":[{"color":"#66FF00","fields":{"item":[{"short":false,"title":"Name","value":"={{$json[\"name\"]}}"},{"short":true,"title":"Price","value":"={{$json[\"regular_price\"]}}"},{"short":true,"title":"Sale Price","value":"={{$json[\"sale_price\"]}}"},{"short":false,"title":"Link","value":"={{$json[\"permalink\"]}}"}]},"footer":"=Added: {{$json[\"date_created\"]}}"}],"otherOptions":{}},"credentials":{"slackApi":{"id":"21","name":"FVF bot"}},"typeVersion":1},{"name":"On product creation","type":"n8n-nodes-base.wooCommerceTrigger","position":[460,300],"webhookId":"267c4855-6227-4d33-867e-74600097473e","parameters":{"event":"product.created"},"credentials":{"wooCommerceApi":{"id":"20","name":"WooCommerce account FVF"}},"typeVersion":1}],"active":true,"settings":{},"connections":{"On product creation":{"main":[[{"node":"If URL has /product/","type":"main","index":0}]]},"If URL has /product/":{"main":[[{"node":"Send message to slack","type":"main","index":0}]]}}},"workflowInfo":{"nodeCount":4,"nodeTypes":{"n8n-nodes-base.if":{"count":1},"n8n-nodes-base.slack":{"count":1},"n8n-nodes-base.wooCommerceTrigger":{"count":1}}},"user":{"username":"n8n-team"},"nodes":[{"id":20,"icon":"fa:map-signs","name":"n8n-nodes-base.if","defaults":{"name":"IF","color":"#408000"},"iconData":{"icon":"map-signs","type":"icon"},"categories":[{"id":9,"name":"Core Nodes"}],"displayName":"IF","typeVersion":1},{"id":40,"icon":"file:slack.svg","name":"n8n-nodes-base.slack","defaults":{"name":"Slack"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgMTUwLjg1MiAxNTAuODUyIiBmaWxsPSIjZmZmIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIj48dXNlIHhsaW5rOmhyZWY9IiNhIiB4PSIuOTI2IiB5PSIuOTI2Ii8+PHN5bWJvbCBpZD0iYSIgb3ZlcmZsb3c9InZpc2libGUiPjxnIHN0cm9rZS13aWR0aD0iMS44NTIiPjxwYXRoIGQ9Ik00MC43NDEgOTMuNTVjMC04LjczNSA2LjYwNy0xNS43NzIgMTQuODE1LTE1Ljc3MnMxNC44MTUgNy4wMzcgMTQuODE1IDE1Ljc3MnYzOC44MjRjMCA4LjczNy02LjYwNyAxNS43NzQtMTQuODE1IDE1Ljc3NHMtMTQuODE1LTcuMDM3LTE0LjgxNS0xNS43NzJ6IiBmaWxsPSIjZTAxZTVhIiBzdHJva2U9IiNlMDFlNWEiLz48cGF0aCBkPSJNOTMuNTUgMTA3LjQwOGMtOC43MzUgMC0xNS43NzItNi42MDctMTUuNzcyLTE0LjgxNXM3LjAzNy0xNC44MTUgMTUuNzcyLTE0LjgxNWgzOC44MjZjOC43MzUgMCAxNS43NzIgNi42MDcgMTUuNzcyIDE0LjgxNXMtNy4wMzcgMTQuODE1LTE1Ljc3MiAxNC44MTV6IiBmaWxsPSIjZWNiMjJkIiBzdHJva2U9IiNlY2IyMmQiLz48cGF0aCBkPSJNNzcuNzc4IDE1Ljc3MkM3Ny43NzggNy4wMzcgODQuMzg1IDAgOTIuNTkzIDBzMTQuODE1IDcuMDM3IDE0LjgxNSAxNS43NzJ2MzguODI2YzAgOC43MzUtNi42MDcgMTUuNzcyLTE0LjgxNSAxNS43NzJzLTE0LjgxNS03LjAzNy0xNC44MTUtMTUuNzcyeiIgZmlsbD0iIzJmYjY3YyIgc3Ryb2tlPSIjMmZiNjdjIi8+PHBhdGggZD0iTTE1Ljc3MiA3MC4zNzFDNy4wMzcgNzAuMzcxIDAgNjMuNzYzIDAgNTUuNTU2czcuMDM3LTE0LjgxNSAxNS43NzItMTQuODE1aDM4LjgyNmM4LjczNSAwIDE1Ljc3MiA2LjYwNyAxNS43NzIgMTQuODE1cy03LjAzNyAxNC44MTUtMTUuNzcyIDE0LjgxNXoiIGZpbGw9IiMzNmM1ZjEiIHN0cm9rZT0iIzM2YzVmMSIvPjxnIHN0cm9rZS1saW5lam9pbj0ibWl0ZXIiPjxwYXRoIGQ9Ik03Ny43NzggMTMzLjMzM2MwIDguMjA4IDYuNjA3IDE0LjgxNSAxNC44MTUgMTQuODE1czE0LjgxNS02LjYwNyAxNC44MTUtMTQuODE1LTYuNjA3LTE0LjgxNS0xNC44MTUtMTQuODE1SDc3Ljc3OHoiIGZpbGw9IiNlY2IyMmQiIHN0cm9rZT0iI2VjYjIyZCIvPjxwYXRoIGQ9Ik0xMzMuMzM0IDcwLjM3MWgtMTQuODE1VjU1LjU1NmMwLTguMjA3IDYuNjA3LTE0LjgxNSAxNC44MTUtMTQuODE1czE0LjgxNSA2LjYwNyAxNC44MTUgMTQuODE1LTYuNjA3IDE0LjgxNS0xNC44MTUgMTQuODE1eiIgZmlsbD0iIzJmYjY3YyIgc3Ryb2tlPSIjMmZiNjdjIi8+PHBhdGggZD0iTTE0LjgxNSA3Ny43NzhIMjkuNjN2MTQuODE1YzAgOC4yMDctNi42MDcgMTQuODE1LTE0LjgxNSAxNC44MTVTMCAxMDAuOCAwIDkyLjU5M3M2LjYwNy0xNC44MTUgMTQuODE1LTE0LjgxNXoiIGZpbGw9IiNlMDFlNWEiIHN0cm9rZT0iI2UwMWU1YSIvPjxwYXRoIGQ9Ik03MC4zNzEgMTQuODE1VjI5LjYzSDU1LjU1NmMtOC4yMDcgMC0xNC44MTUtNi42MDctMTQuODE1LTE0LjgxNVM0Ny4zNDggMCA1NS41NTYgMHMxNC44MTUgNi42MDcgMTQuODE1IDE0LjgxNXoiIGZpbGw9IiMzNmM1ZjEiIHN0cm9rZT0iIzM2YzVmMSIvPjwvZz48L2c+PC9zeW1ib2w+PC9zdmc+"},"categories":[{"id":6,"name":"Communication"}],"displayName":"Slack","typeVersion":2},{"id":42,"icon":"fa:play","name":"n8n-nodes-base.start","defaults":{"name":"Start","color":"#00e000"},"iconData":{"icon":"play","type":"icon"},"categories":[{"id":9,"name":"Core Nodes"}],"displayName":"Start","typeVersion":1},{"id":235,"icon":"file:wooCommerce.svg","name":"n8n-nodes-base.wooCommerceTrigger","defaults":{"name":"WooCommerce Trigger"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWlkWU1pZCIgdmVyc2lvbj0iMS4xIiB2aWV3Qm94PSIwIC01MCAyNTYgMjUzIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KPHRpdGxlPldvb0NvbW1lcmNlIExvZ288L3RpdGxlPgo8bWV0YWRhdGE+CjxyZGY6UkRGPgo8Y2M6V29yayByZGY6YWJvdXQ9IiI+CjxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0Pgo8ZGM6dHlwZSByZGY6cmVzb3VyY2U9Imh0dHA6Ly9wdXJsLm9yZy9kYy9kY21pdHlwZS9TdGlsbEltYWdlIi8+CjxkYzp0aXRsZS8+CjwvY2M6V29yaz4KPC9yZGY6UkRGPgo8L21ldGFkYXRhPgo8cGF0aCBkPSJtMjMuNzU5IDBoMjA4LjM4YzEzLjE4NyAwIDIzLjg2MyAxMC42NzUgMjMuODYzIDIzLjg2M3Y3OS41NDJjMCAxMy4xODctMTAuNjc1IDIzLjg2My0yMy44NjMgMjMuODYzaC03NC43MjdsMTAuMjU3IDI1LjExOC00NS4xMDktMjUuMTE4aC05OC42OTVjLTEzLjE4NyAwLTIzLjg2My0xMC42NzUtMjMuODYzLTIzLjg2M3YtNzkuNTQyYy0wLjEwNDY2LTEzLjA4MyAxMC41NzEtMjMuODYzIDIzLjc1OC0yMy44NjN6IiBmaWxsPSIjN2Y1NGIzIi8+CjxwYXRoIGQ9Im0xNC41NzggMjEuNzVjMS40NTY5LTEuOTc3MiAzLjY0MjMtMy4wMTc5IDYuNTU2MS0zLjIyNiA1LjMwNzMtMC40MTYyNiA4LjMyNTIgMi4wODEzIDkuMDUzNyA3LjQ5MjcgMy4yMjYgMjEuNzUgNi43NjQyIDQwLjE2OSAxMC41MTEgNTUuMjU5bDIyLjc5LTQzLjM5NWMyLjA4MTMtMy45NTQ1IDQuNjgyOS02LjAzNTggNy44MDQ5LTYuMjQzOSA0LjU3ODktMC4zMTIyIDcuMzg4NiAyLjYwMTYgOC41MzMzIDguNzQxNSAyLjYwMTYgMTMuODQxIDUuOTMxNyAyNS42IDkuODg2MiAzNS41OSAyLjcwNTctMjYuNDMzIDcuMjg0Ni00NS40NzYgMTMuNzM3LTU3LjIzNiAxLjU2MS0yLjkxMzggMy44NTA0LTQuMzcwNyA2Ljg2ODMtNC41Nzg5IDIuMzkzNS0wLjIwODEzIDQuNTc4OSAwLjUyMDMzIDYuNTU2MSAyLjA4MTMgMS45NzcyIDEuNTYxIDMuMDE3OSAzLjUzODIgMy4yMjYgNS45MzE3IDAuMTA0MDYgMS44NzMyLTAuMjA4MTMgMy40MzQxLTEuMDQwNyA0Ljk5NTEtNC4wNTg1IDcuNDkyNy03LjM4ODYgMjAuMDg1LTEwLjA5NCAzNy41NjctMi42MDE2IDE2Ljk2My0zLjUzODIgMzAuMTc5LTIuOTEzOCAzOS42NDkgMC4yMDgxMyAyLjYwMTYtMC4yMDgxMyA0Ljg5MTEtMS4yNDg4IDYuODY4My0xLjI0ODggMi4yODk0LTMuMTIyIDMuNTM4Mi01LjUxNTQgMy43NDYzLTIuNzA1NyAwLjIwODEzLTUuNTE1NC0xLjA0MDYtOC4yMjExLTMuODUwNC05LjY3OC05Ljg4NjItMTcuMzc5LTI0LjY2My0yMi45OTgtNDQuMzMyLTYuNzY0MiAxMy4zMi0xMS43NTkgMjMuMzExLTE0Ljk4NSAyOS45NzEtNi4xMzk4IDExLjc1OS0xMS4zNDMgMTcuNzk1LTE1LjcxNCAxOC4xMDctMi44MDk4IDAuMjA4MTMtNS4yMDMzLTIuMTg1NC03LjI4NDYtNy4xODA1LTUuMzA3My0xMy42MzMtMTEuMDMxLTM5Ljk2MS0xNy4xNzEtNzguOTg1LTAuNDE2MjYtMi43MDU3IDAuMjA4MTMtNS4wOTkyIDEuNjY1LTYuOTcyNHptMjIzLjY0IDE2LjMzOGMtMy43NDYzLTYuNTU2MS05LjI2MTgtMTAuNTExLTE2LjY1LTEyLjA3Mi0xLjk3NzItMC40MTYyNi0zLjg1MDQtMC42MjQzOS01LjYxOTUtMC42MjQzOS05Ljk5MDIgMC0xOC4xMDcgNS4yMDMzLTI0LjQ1NSAxNS42MS01LjQxMTQgOC44NDU1LTguMTE3MSAxOC42MjgtOC4xMTcxIDI5LjM0NiAwIDguMDEzIDEuNjY1IDE0Ljg4MSA0Ljk5NTEgMjAuNjA1IDMuNzQ2MyA2LjU1NjEgOS4yNjE4IDEwLjUxMSAxNi42NSAxMi4wNzIgMS45NzcyIDAuNDE2MjYgMy44NTA0IDAuNjI0MzkgNS42MTk1IDAuNjI0MzkgMTAuMDk0IDAgMTguMjExLTUuMjAzMyAyNC40NTUtMTUuNjEgNS40MTE0LTguOTQ5NiA4LjExNzEtMTguNzMyIDguMTE3MS0yOS40NSAwLjEwNDA2LTguMTE3MS0xLjY2NS0xNC44ODEtNC45OTUxLTIwLjUwMXptLTEzLjExMiAyOC44MjZjLTEuNDU2OSA2Ljg2ODMtNC4wNTg1IDExLjk2Ny03LjkwODkgMTUuNDAyLTMuMDE3OSAyLjcwNTctNS44Mjc2IDMuODUwNC04LjQyOTMgMy4zMzAxLTIuNDk3Ni0wLjUyMDMzLTQuNTc4OS0yLjcwNTctNi4xMzk4LTYuNzY0Mi0xLjI0ODgtMy4yMjYtMS44NzMyLTYuNDUyLTEuODczMi05LjQ2OTkgMC0yLjYwMTYgMC4yMDgxMy01LjIwMzMgMC43Mjg0Ni03LjU5NjcgMC45MzY1OS00LjI2NjcgMi43MDU3LTguNDI5MyA1LjUxNTQtMTIuMzg0IDMuNDM0MS01LjA5OTIgNy4wNzY0LTcuMTgwNSAxMC44MjMtNi40NTIgMi40OTc2IDAuNTIwMzMgNC41Nzg5IDIuNzA1NyA2LjEzOTggNi43NjQyIDEuMjQ4OCAzLjIyNiAxLjg3MzIgNi40NTIgMS44NzMyIDkuNDY5OSAwIDIuNzA1Ny0wLjIwODEzIDUuMzA3My0wLjcyODQ2IDcuNzAwOHptLTUyLjAzMy0yOC44MjZjLTMuNzQ2My02LjU1NjEtOS4zNjU5LTEwLjUxMS0xNi42NS0xMi4wNzItMS45NzcyLTAuNDE2MjYtMy44NTA0LTAuNjI0MzktNS42MTk1LTAuNjI0MzktOS45OTAyIDAtMTguMTA3IDUuMjAzMy0yNC40NTUgMTUuNjEtNS40MTE0IDguODQ1NS04LjExNzEgMTguNjI4LTguMTE3MSAyOS4zNDYgMCA4LjAxMyAxLjY2NSAxNC44ODEgNC45OTUxIDIwLjYwNSAzLjc0NjMgNi41NTYxIDkuMjYxOCAxMC41MTEgMTYuNjUgMTIuMDcyIDEuOTc3MiAwLjQxNjI2IDMuODUwNCAwLjYyNDM5IDUuNjE5NSAwLjYyNDM5IDEwLjA5NCAwIDE4LjIxMS01LjIwMzMgMjQuNDU1LTE1LjYxIDUuNDExNC04Ljk0OTYgOC4xMTcxLTE4LjczMiA4LjExNzEtMjkuNDUgMC04LjExNzEtMS42NjUtMTQuODgxLTQuOTk1MS0yMC41MDF6bS0xMy4yMTYgMjguODI2Yy0xLjQ1NjkgNi44NjgzLTQuMDU4NSAxMS45NjctNy45MDg5IDE1LjQwMi0zLjAxNzkgMi43MDU3LTUuODI3NiAzLjg1MDQtOC40MjkzIDMuMzMwMS0yLjQ5NzYtMC41MjAzMy00LjU3ODktMi43MDU3LTYuMTM5OC02Ljc2NDItMS4yNDg4LTMuMjI2LTEuODczMi02LjQ1Mi0xLjg3MzItOS40Njk5IDAtMi42MDE2IDAuMjA4MTMtNS4yMDMzIDAuNzI4NDYtNy41OTY3IDAuOTM2NTgtNC4yNjY3IDIuNzA1Ny04LjQyOTMgNS41MTU0LTEyLjM4NCAzLjQzNDEtNS4wOTkyIDcuMDc2NC03LjE4MDUgMTAuODIzLTYuNDUyIDIuNDk3NiAwLjUyMDMzIDQuNTc4OSAyLjcwNTcgNi4xMzk4IDYuNzY0MiAxLjI0ODggMy4yMjYgMS44NzMyIDYuNDUyIDEuODczMiA5LjQ2OTkgMC4xMDQwNiAyLjcwNTctMC4yMDgxMyA1LjMwNzMtMC43Mjg0NiA3LjcwMDh6IiBmaWxsPSIjZmZmIi8+Cjwvc3ZnPgo="},"categories":[{"id":2,"name":"Sales"}],"displayName":"WooCommerce Trigger","typeVersion":1}],"categories":[{"id":2,"name":"Sales"}],"image":[]},{"id":1460,"name":"Notify on Slack when refund is registered in WooCommerce","views":85,"recentViews":9541,"totalViews":85,"createdAt":"2022-02-17T15:02:58.662Z","description":"This workflow uses a WooCommerce trigger that will run when an order has been updated and the status is refunded.\n\nIf the value of this is over 100 it will post it to a Slack channel.\n\nTo use this workflow you will need to set the credentials to use for the WooCommerce and Slack nodes, You will also need to pick a channel to post the message to.","workflow":{"id":82,"name":"New WooCommerce refund to Slack","nodes":[{"name":"Order Updated","type":"n8n-nodes-base.wooCommerceTrigger","position":[320,500],"webhookId":"f7736be3-e978-4a17-b936-7ce9f8ccdb72","parameters":{"event":"order.updated"},"credentials":{"wooCommerceApi":{"id":"48","name":"WooCommerce account"}},"typeVersion":1},{"name":"If Refund and Over 100","type":"n8n-nodes-base.if","position":[540,500],"parameters":{"conditions":{"number":[{"value1":"={{$json[\"total\"]}}","value2":100,"operation":"largerEqual"}],"string":[{"value1":"={{$json[\"status\"]}}","value2":"refunded"}]}},"typeVersion":1},{"name":"Send to Slack","type":"n8n-nodes-base.slack","position":[780,480],"parameters":{"text":":x: A refund has been issued :x:","channel":"woo-commerce","blocksUi":{"blocksValues":[]},"attachments":[{"color":"#FF0000","fields":{"item":[{"short":true,"title":"Order ID","value":"={{$json[\"id\"]}}"},{"short":true,"title":"Status","value":"={{$json[\"status\"]}}"},{"short":true,"title":"Total","value":"={{$json[\"currency_symbol\"]}}{{$json[\"total\"]}}"}]},"footer":"=*Order updated:* {{$json[\"date_modified\"]}}"}],"otherOptions":{}},"credentials":{"slackApi":{"id":"53","name":"Slack Access Token"}},"typeVersion":1}],"active":false,"settings":{"saveManualExecutions":true,"saveExecutionProgress":true,"saveDataSuccessExecution":"all"},"connections":{"Order Updated":{"main":[[{"node":"If Refund and Over 100","type":"main","index":0}]]},"If Refund and Over 100":{"main":[[{"node":"Send to Slack","type":"main","index":0}],[]]}}},"workflowInfo":{"nodeCount":4,"nodeTypes":{"n8n-nodes-base.if":{"count":1},"n8n-nodes-base.slack":{"count":1},"n8n-nodes-base.wooCommerceTrigger":{"count":1}}},"user":{"username":"jon-n8n"},"nodes":[{"id":20,"icon":"fa:map-signs","name":"n8n-nodes-base.if","defaults":{"name":"IF","color":"#408000"},"iconData":{"icon":"map-signs","type":"icon"},"categories":[{"id":9,"name":"Core Nodes"}],"displayName":"IF","typeVersion":1},{"id":40,"icon":"file:slack.svg","name":"n8n-nodes-base.slack","defaults":{"name":"Slack"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgMTUwLjg1MiAxNTAuODUyIiBmaWxsPSIjZmZmIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIj48dXNlIHhsaW5rOmhyZWY9IiNhIiB4PSIuOTI2IiB5PSIuOTI2Ii8+PHN5bWJvbCBpZD0iYSIgb3ZlcmZsb3c9InZpc2libGUiPjxnIHN0cm9rZS13aWR0aD0iMS44NTIiPjxwYXRoIGQ9Ik00MC43NDEgOTMuNTVjMC04LjczNSA2LjYwNy0xNS43NzIgMTQuODE1LTE1Ljc3MnMxNC44MTUgNy4wMzcgMTQuODE1IDE1Ljc3MnYzOC44MjRjMCA4LjczNy02LjYwNyAxNS43NzQtMTQuODE1IDE1Ljc3NHMtMTQuODE1LTcuMDM3LTE0LjgxNS0xNS43NzJ6IiBmaWxsPSIjZTAxZTVhIiBzdHJva2U9IiNlMDFlNWEiLz48cGF0aCBkPSJNOTMuNTUgMTA3LjQwOGMtOC43MzUgMC0xNS43NzItNi42MDctMTUuNzcyLTE0LjgxNXM3LjAzNy0xNC44MTUgMTUuNzcyLTE0LjgxNWgzOC44MjZjOC43MzUgMCAxNS43NzIgNi42MDcgMTUuNzcyIDE0LjgxNXMtNy4wMzcgMTQuODE1LTE1Ljc3MiAxNC44MTV6IiBmaWxsPSIjZWNiMjJkIiBzdHJva2U9IiNlY2IyMmQiLz48cGF0aCBkPSJNNzcuNzc4IDE1Ljc3MkM3Ny43NzggNy4wMzcgODQuMzg1IDAgOTIuNTkzIDBzMTQuODE1IDcuMDM3IDE0LjgxNSAxNS43NzJ2MzguODI2YzAgOC43MzUtNi42MDcgMTUuNzcyLTE0LjgxNSAxNS43NzJzLTE0LjgxNS03LjAzNy0xNC44MTUtMTUuNzcyeiIgZmlsbD0iIzJmYjY3YyIgc3Ryb2tlPSIjMmZiNjdjIi8+PHBhdGggZD0iTTE1Ljc3MiA3MC4zNzFDNy4wMzcgNzAuMzcxIDAgNjMuNzYzIDAgNTUuNTU2czcuMDM3LTE0LjgxNSAxNS43NzItMTQuODE1aDM4LjgyNmM4LjczNSAwIDE1Ljc3MiA2LjYwNyAxNS43NzIgMTQuODE1cy03LjAzNyAxNC44MTUtMTUuNzcyIDE0LjgxNXoiIGZpbGw9IiMzNmM1ZjEiIHN0cm9rZT0iIzM2YzVmMSIvPjxnIHN0cm9rZS1saW5lam9pbj0ibWl0ZXIiPjxwYXRoIGQ9Ik03Ny43NzggMTMzLjMzM2MwIDguMjA4IDYuNjA3IDE0LjgxNSAxNC44MTUgMTQuODE1czE0LjgxNS02LjYwNyAxNC44MTUtMTQuODE1LTYuNjA3LTE0LjgxNS0xNC44MTUtMTQuODE1SDc3Ljc3OHoiIGZpbGw9IiNlY2IyMmQiIHN0cm9rZT0iI2VjYjIyZCIvPjxwYXRoIGQ9Ik0xMzMuMzM0IDcwLjM3MWgtMTQuODE1VjU1LjU1NmMwLTguMjA3IDYuNjA3LTE0LjgxNSAxNC44MTUtMTQuODE1czE0LjgxNSA2LjYwNyAxNC44MTUgMTQuODE1LTYuNjA3IDE0LjgxNS0xNC44MTUgMTQuODE1eiIgZmlsbD0iIzJmYjY3YyIgc3Ryb2tlPSIjMmZiNjdjIi8+PHBhdGggZD0iTTE0LjgxNSA3Ny43NzhIMjkuNjN2MTQuODE1YzAgOC4yMDctNi42MDcgMTQuODE1LTE0LjgxNSAxNC44MTVTMCAxMDAuOCAwIDkyLjU5M3M2LjYwNy0xNC44MTUgMTQuODE1LTE0LjgxNXoiIGZpbGw9IiNlMDFlNWEiIHN0cm9rZT0iI2UwMWU1YSIvPjxwYXRoIGQ9Ik03MC4zNzEgMTQuODE1VjI5LjYzSDU1LjU1NmMtOC4yMDcgMC0xNC44MTUtNi42MDctMTQuODE1LTE0LjgxNVM0Ny4zNDggMCA1NS41NTYgMHMxNC44MTUgNi42MDcgMTQuODE1IDE0LjgxNXoiIGZpbGw9IiMzNmM1ZjEiIHN0cm9rZT0iIzM2YzVmMSIvPjwvZz48L2c+PC9zeW1ib2w+PC9zdmc+"},"categories":[{"id":6,"name":"Communication"}],"displayName":"Slack","typeVersion":2},{"id":42,"icon":"fa:play","name":"n8n-nodes-base.start","defaults":{"name":"Start","color":"#00e000"},"iconData":{"icon":"play","type":"icon"},"categories":[{"id":9,"name":"Core Nodes"}],"displayName":"Start","typeVersion":1},{"id":235,"icon":"file:wooCommerce.svg","name":"n8n-nodes-base.wooCommerceTrigger","defaults":{"name":"WooCommerce Trigger"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWlkWU1pZCIgdmVyc2lvbj0iMS4xIiB2aWV3Qm94PSIwIC01MCAyNTYgMjUzIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KPHRpdGxlPldvb0NvbW1lcmNlIExvZ288L3RpdGxlPgo8bWV0YWRhdGE+CjxyZGY6UkRGPgo8Y2M6V29yayByZGY6YWJvdXQ9IiI+CjxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0Pgo8ZGM6dHlwZSByZGY6cmVzb3VyY2U9Imh0dHA6Ly9wdXJsLm9yZy9kYy9kY21pdHlwZS9TdGlsbEltYWdlIi8+CjxkYzp0aXRsZS8+CjwvY2M6V29yaz4KPC9yZGY6UkRGPgo8L21ldGFkYXRhPgo8cGF0aCBkPSJtMjMuNzU5IDBoMjA4LjM4YzEzLjE4NyAwIDIzLjg2MyAxMC42NzUgMjMuODYzIDIzLjg2M3Y3OS41NDJjMCAxMy4xODctMTAuNjc1IDIzLjg2My0yMy44NjMgMjMuODYzaC03NC43MjdsMTAuMjU3IDI1LjExOC00NS4xMDktMjUuMTE4aC05OC42OTVjLTEzLjE4NyAwLTIzLjg2My0xMC42NzUtMjMuODYzLTIzLjg2M3YtNzkuNTQyYy0wLjEwNDY2LTEzLjA4MyAxMC41NzEtMjMuODYzIDIzLjc1OC0yMy44NjN6IiBmaWxsPSIjN2Y1NGIzIi8+CjxwYXRoIGQ9Im0xNC41NzggMjEuNzVjMS40NTY5LTEuOTc3MiAzLjY0MjMtMy4wMTc5IDYuNTU2MS0zLjIyNiA1LjMwNzMtMC40MTYyNiA4LjMyNTIgMi4wODEzIDkuMDUzNyA3LjQ5MjcgMy4yMjYgMjEuNzUgNi43NjQyIDQwLjE2OSAxMC41MTEgNTUuMjU5bDIyLjc5LTQzLjM5NWMyLjA4MTMtMy45NTQ1IDQuNjgyOS02LjAzNTggNy44MDQ5LTYuMjQzOSA0LjU3ODktMC4zMTIyIDcuMzg4NiAyLjYwMTYgOC41MzMzIDguNzQxNSAyLjYwMTYgMTMuODQxIDUuOTMxNyAyNS42IDkuODg2MiAzNS41OSAyLjcwNTctMjYuNDMzIDcuMjg0Ni00NS40NzYgMTMuNzM3LTU3LjIzNiAxLjU2MS0yLjkxMzggMy44NTA0LTQuMzcwNyA2Ljg2ODMtNC41Nzg5IDIuMzkzNS0wLjIwODEzIDQuNTc4OSAwLjUyMDMzIDYuNTU2MSAyLjA4MTMgMS45NzcyIDEuNTYxIDMuMDE3OSAzLjUzODIgMy4yMjYgNS45MzE3IDAuMTA0MDYgMS44NzMyLTAuMjA4MTMgMy40MzQxLTEuMDQwNyA0Ljk5NTEtNC4wNTg1IDcuNDkyNy03LjM4ODYgMjAuMDg1LTEwLjA5NCAzNy41NjctMi42MDE2IDE2Ljk2My0zLjUzODIgMzAuMTc5LTIuOTEzOCAzOS42NDkgMC4yMDgxMyAyLjYwMTYtMC4yMDgxMyA0Ljg5MTEtMS4yNDg4IDYuODY4My0xLjI0ODggMi4yODk0LTMuMTIyIDMuNTM4Mi01LjUxNTQgMy43NDYzLTIuNzA1NyAwLjIwODEzLTUuNTE1NC0xLjA0MDYtOC4yMjExLTMuODUwNC05LjY3OC05Ljg4NjItMTcuMzc5LTI0LjY2My0yMi45OTgtNDQuMzMyLTYuNzY0MiAxMy4zMi0xMS43NTkgMjMuMzExLTE0Ljk4NSAyOS45NzEtNi4xMzk4IDExLjc1OS0xMS4zNDMgMTcuNzk1LTE1LjcxNCAxOC4xMDctMi44MDk4IDAuMjA4MTMtNS4yMDMzLTIuMTg1NC03LjI4NDYtNy4xODA1LTUuMzA3My0xMy42MzMtMTEuMDMxLTM5Ljk2MS0xNy4xNzEtNzguOTg1LTAuNDE2MjYtMi43MDU3IDAuMjA4MTMtNS4wOTkyIDEuNjY1LTYuOTcyNHptMjIzLjY0IDE2LjMzOGMtMy43NDYzLTYuNTU2MS05LjI2MTgtMTAuNTExLTE2LjY1LTEyLjA3Mi0xLjk3NzItMC40MTYyNi0zLjg1MDQtMC42MjQzOS01LjYxOTUtMC42MjQzOS05Ljk5MDIgMC0xOC4xMDcgNS4yMDMzLTI0LjQ1NSAxNS42MS01LjQxMTQgOC44NDU1LTguMTE3MSAxOC42MjgtOC4xMTcxIDI5LjM0NiAwIDguMDEzIDEuNjY1IDE0Ljg4MSA0Ljk5NTEgMjAuNjA1IDMuNzQ2MyA2LjU1NjEgOS4yNjE4IDEwLjUxMSAxNi42NSAxMi4wNzIgMS45NzcyIDAuNDE2MjYgMy44NTA0IDAuNjI0MzkgNS42MTk1IDAuNjI0MzkgMTAuMDk0IDAgMTguMjExLTUuMjAzMyAyNC40NTUtMTUuNjEgNS40MTE0LTguOTQ5NiA4LjExNzEtMTguNzMyIDguMTE3MS0yOS40NSAwLjEwNDA2LTguMTE3MS0xLjY2NS0xNC44ODEtNC45OTUxLTIwLjUwMXptLTEzLjExMiAyOC44MjZjLTEuNDU2OSA2Ljg2ODMtNC4wNTg1IDExLjk2Ny03LjkwODkgMTUuNDAyLTMuMDE3OSAyLjcwNTctNS44Mjc2IDMuODUwNC04LjQyOTMgMy4zMzAxLTIuNDk3Ni0wLjUyMDMzLTQuNTc4OS0yLjcwNTctNi4xMzk4LTYuNzY0Mi0xLjI0ODgtMy4yMjYtMS44NzMyLTYuNDUyLTEuODczMi05LjQ2OTkgMC0yLjYwMTYgMC4yMDgxMy01LjIwMzMgMC43Mjg0Ni03LjU5NjcgMC45MzY1OS00LjI2NjcgMi43MDU3LTguNDI5MyA1LjUxNTQtMTIuMzg0IDMuNDM0MS01LjA5OTIgNy4wNzY0LTcuMTgwNSAxMC44MjMtNi40NTIgMi40OTc2IDAuNTIwMzMgNC41Nzg5IDIuNzA1NyA2LjEzOTggNi43NjQyIDEuMjQ4OCAzLjIyNiAxLjg3MzIgNi40NTIgMS44NzMyIDkuNDY5OSAwIDIuNzA1Ny0wLjIwODEzIDUuMzA3My0wLjcyODQ2IDcuNzAwOHptLTUyLjAzMy0yOC44MjZjLTMuNzQ2My02LjU1NjEtOS4zNjU5LTEwLjUxMS0xNi42NS0xMi4wNzItMS45NzcyLTAuNDE2MjYtMy44NTA0LTAuNjI0MzktNS42MTk1LTAuNjI0MzktOS45OTAyIDAtMTguMTA3IDUuMjAzMy0yNC40NTUgMTUuNjEtNS40MTE0IDguODQ1NS04LjExNzEgMTguNjI4LTguMTE3MSAyOS4zNDYgMCA4LjAxMyAxLjY2NSAxNC44ODEgNC45OTUxIDIwLjYwNSAzLjc0NjMgNi41NTYxIDkuMjYxOCAxMC41MTEgMTYuNjUgMTIuMDcyIDEuOTc3MiAwLjQxNjI2IDMuODUwNCAwLjYyNDM5IDUuNjE5NSAwLjYyNDM5IDEwLjA5NCAwIDE4LjIxMS01LjIwMzMgMjQuNDU1LTE1LjYxIDUuNDExNC04Ljk0OTYgOC4xMTcxLTE4LjczMiA4LjExNzEtMjkuNDUgMC04LjExNzEtMS42NjUtMTQuODgxLTQuOTk1MS0yMC41MDF6bS0xMy4yMTYgMjguODI2Yy0xLjQ1NjkgNi44NjgzLTQuMDU4NSAxMS45NjctNy45MDg5IDE1LjQwMi0zLjAxNzkgMi43MDU3LTUuODI3NiAzLjg1MDQtOC40MjkzIDMuMzMwMS0yLjQ5NzYtMC41MjAzMy00LjU3ODktMi43MDU3LTYuMTM5OC02Ljc2NDItMS4yNDg4LTMuMjI2LTEuODczMi02LjQ1Mi0xLjg3MzItOS40Njk5IDAtMi42MDE2IDAuMjA4MTMtNS4yMDMzIDAuNzI4NDYtNy41OTY3IDAuOTM2NTgtNC4yNjY3IDIuNzA1Ny04LjQyOTMgNS41MTU0LTEyLjM4NCAzLjQzNDEtNS4wOTkyIDcuMDc2NC03LjE4MDUgMTAuODIzLTYuNDUyIDIuNDk3NiAwLjUyMDMzIDQuNTc4OSAyLjcwNTcgNi4xMzk4IDYuNzY0MiAxLjI0ODggMy4yMjYgMS44NzMyIDYuNDUyIDEuODczMiA5LjQ2OTkgMC4xMDQwNiAyLjcwNTctMC4yMDgxMyA1LjMwNzMtMC43Mjg0NiA3LjcwMDh6IiBmaWxsPSIjZmZmIi8+Cjwvc3ZnPgo="},"categories":[{"id":2,"name":"Sales"}],"displayName":"WooCommerce Trigger","typeVersion":1}],"categories":[{"id":2,"name":"Sales"},{"id":8,"name":"Finance & Accounting"}],"image":[]}],"image":[]}} diff --git a/cypress/fixtures/Floating_Nodes.json b/cypress/fixtures/Floating_Nodes.json new file mode 100644 index 0000000000..d95675d3c2 --- /dev/null +++ b/cypress/fixtures/Floating_Nodes.json @@ -0,0 +1,176 @@ +{ + "name": "Floating Nodes", + "nodes": [ + { + "parameters": {}, + "id": "d0eda550-2526-42a1-aa19-dee411c8acf9", + "name": "When clicking \"Test Workflow\"", + "type": "n8n-nodes-base.manualTrigger", + "typeVersion": 1, + "position": [ + 700, + 560 + ] + }, + { + "parameters": { + "options": {} + }, + "id": "30412165-1229-4b21-9890-05bfbd9952ab", + "name": "Node 1", + "type": "n8n-nodes-base.set", + "typeVersion": 3.2, + "position": [ + 920, + 560 + ] + }, + { + "parameters": { + "options": {} + }, + "id": "201cc8fc-3124-47a3-bc08-b3917c1ddcd9", + "name": "Node 2", + "type": "n8n-nodes-base.set", + "typeVersion": 3.2, + "position": [ + 1100, + 560 + ] + }, + { + "parameters": { + "options": {} + }, + "id": "a29802bb-a284-495d-9917-6c6e42fef01e", + "name": "Node 3", + "type": "n8n-nodes-base.set", + "typeVersion": 3.2, + "position": [ + 1280, + 560 + ] + }, + { + "parameters": { + "options": {} + }, + "id": "a95a72b3-8b39-44e2-a05b-d8d677741c80", + "name": "Node 4", + "type": "n8n-nodes-base.set", + "typeVersion": 3.2, + "position": [ + 1440, + 560 + ] + }, + { + "parameters": {}, + "id": "4674f10d-6144-4a17-bbbb-350c3974438e", + "name": "Chain", + "type": "@n8n/n8n-nodes-langchain.chainLlm", + "typeVersion": 1, + "position": [ + 1580, + 560 + ] + }, + { + "parameters": { + "options": {} + }, + "id": "58e12ea5-bd3e-4abf-abec-fcfb5c0a7955", + "name": "Model", + "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi", + "typeVersion": 1, + "position": [ + 1600, + 740 + ] + } + ], + "pinData": {}, + "connections": { + "When clicking \"Test Workflow\"": { + "main": [ + [ + { + "node": "Node 1", + "type": "main", + "index": 0 + } + ] + ] + }, + "Node 1": { + "main": [ + [ + { + "node": "Node 2", + "type": "main", + "index": 0 + } + ] + ] + }, + "Node 3": { + "main": [ + [ + { + "node": "Node 4", + "type": "main", + "index": 0 + } + ] + ] + }, + "Node 2": { + "main": [ + [ + { + "node": "Node 3", + "type": "main", + "index": 0 + } + ] + ] + }, + "Chain": { + "main": [ + [] + ] + }, + "Model": { + "ai_languageModel": [ + [ + { + "node": "Chain", + "type": "ai_languageModel", + "index": 0 + } + ] + ] + }, + "Node 4": { + "main": [ + [ + { + "node": "Chain", + "type": "main", + "index": 0 + } + ] + ] + } + }, + "active": false, + "settings": { + "executionOrder": "v1" + }, + "versionId": "2730d156-a98a-4ac8-b481-5c16361fdba2", + "id": "6bzXMGxHuxeEaqsA", + "meta": { + "instanceId": "1838be0fa0389fbaf5e2e4aaedab4ddc79abc4175b433401abb22a281001b853" + }, + "tags": [] +} diff --git a/cypress/fixtures/Lots_of_nodes.json b/cypress/fixtures/Lots_of_nodes.json new file mode 100644 index 0000000000..85a100b94e --- /dev/null +++ b/cypress/fixtures/Lots_of_nodes.json @@ -0,0 +1,1051 @@ +{ + "name": "Lots of nodes", + "nodes": [ + { + "parameters": {}, + "id": "369fe424-dd3b-4399-9de3-50bd4ce1f75b", + "name": "When clicking \"Test Workflow\"", + "type": "n8n-nodes-base.manualTrigger", + "typeVersion": 1, + "position": [ + 860, + 740 + ] + }, + { + "parameters": { + "jsCode": "// Loop over input items and add a new field called 'myNewField' to the JSON of each one\nfor (const item of $input.all()) {\n item.json.myNewField = 1;\n}\n\nreturn $input.all();" + }, + "id": "dce967a7-8c5e-43cc-ba2b-e0fb0c9cf14c", + "name": "Code", + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 1080, + 740 + ] + }, + { + "parameters": { + "options": {} + }, + "id": "df7a719e-b25a-43e3-b941-7091a7d9a1a8", + "name": "Edit Fields", + "type": "n8n-nodes-base.set", + "typeVersion": 3.2, + "position": [ + 1300, + 740 + ] + }, + { + "parameters": {}, + "id": "32968b79-6a8b-43ed-b884-eb906b597661", + "name": "IF", + "type": "n8n-nodes-base.if", + "typeVersion": 1, + "position": [ + 1520, + 740 + ] + }, + { + "parameters": { + "jsCode": "// Loop over input items and add a new field called 'myNewField' to the JSON of each one\nfor (const item of $input.all()) {\n item.json.myNewField = 1;\n}\n\nreturn $input.all();" + }, + "id": "e9a72745-6dbb-4be1-b286-aaa679b95e36", + "name": "Code1", + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 1820, + 80 + ] + }, + { + "parameters": { + "options": {} + }, + "id": "f831d21b-c3a9-4bd8-9fc3-6daef12bd43f", + "name": "Edit Fields1", + "type": "n8n-nodes-base.set", + "typeVersion": 3.2, + "position": [ + 2040, + 80 + ] + }, + { + "parameters": {}, + "id": "6e6b2a4f-9e61-4245-8502-ca01e851fcbe", + "name": "IF1", + "type": "n8n-nodes-base.if", + "typeVersion": 1, + "position": [ + 2260, + 80 + ] + }, + { + "parameters": { + "jsCode": "// Loop over input items and add a new field called 'myNewField' to the JSON of each one\nfor (const item of $input.all()) {\n item.json.myNewField = 1;\n}\n\nreturn $input.all();" + }, + "id": "535b9786-ead9-44f9-bff2-ef2e019a4cf9", + "name": "Code3", + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 2560, + -260 + ] + }, + { + "parameters": { + "options": {} + }, + "id": "6a181d75-f2f2-4ad1-be3c-81ebe077ccc8", + "name": "Edit Fields3", + "type": "n8n-nodes-base.set", + "typeVersion": 3.2, + "position": [ + 2780, + -260 + ] + }, + { + "parameters": {}, + "id": "4b45828e-4e2b-4046-b9ae-24b373a81863", + "name": "IF7", + "type": "n8n-nodes-base.if", + "typeVersion": 1, + "position": [ + 3000, + -260 + ] + }, + { + "parameters": { + "jsCode": "// Loop over input items and add a new field called 'myNewField' to the JSON of each one\nfor (const item of $input.all()) {\n item.json.myNewField = 1;\n}\n\nreturn $input.all();" + }, + "id": "059534cb-820c-4fb7-933c-eeed2ae74f1c", + "name": "Code7", + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 3260, + -400 + ] + }, + { + "parameters": { + "options": {} + }, + "id": "4f5c0d94-b69d-4ad3-aa8f-f1dd5824ec4a", + "name": "Edit Fields7", + "type": "n8n-nodes-base.set", + "typeVersion": 3.2, + "position": [ + 3480, + -400 + ] + }, + { + "parameters": {}, + "id": "cd74f840-7b0f-425d-8ecd-e247a7d8abf5", + "name": "IF8", + "type": "n8n-nodes-base.if", + "typeVersion": 1, + "position": [ + 3700, + -400 + ] + }, + { + "parameters": { + "jsCode": "// Loop over input items and add a new field called 'myNewField' to the JSON of each one\nfor (const item of $input.all()) {\n item.json.myNewField = 1;\n}\n\nreturn $input.all();" + }, + "id": "3c97fd14-9c23-45e2-a1ac-934d743e9a01", + "name": "Code8", + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 3260, + -80 + ] + }, + { + "parameters": { + "options": {} + }, + "id": "9e7bd7e9-5142-4751-b132-735d27007d82", + "name": "Edit Fields8", + "type": "n8n-nodes-base.set", + "typeVersion": 3.2, + "position": [ + 3480, + -80 + ] + }, + { + "parameters": {}, + "id": "8d3968b6-16d4-4e03-9026-eeaf70b17805", + "name": "IF9", + "type": "n8n-nodes-base.if", + "typeVersion": 1, + "position": [ + 3700, + -80 + ] + }, + { + "parameters": { + "jsCode": "// Loop over input items and add a new field called 'myNewField' to the JSON of each one\nfor (const item of $input.all()) {\n item.json.myNewField = 1;\n}\n\nreturn $input.all();" + }, + "id": "141edef3-ea0f-4e90-9b6a-09f5d5551195", + "name": "Code4", + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 2560, + 440 + ] + }, + { + "parameters": { + "options": {} + }, + "id": "b5b93cd7-9448-4290-91b7-c3c8429925fd", + "name": "Edit Fields4", + "type": "n8n-nodes-base.set", + "typeVersion": 3.2, + "position": [ + 2780, + 440 + ] + }, + { + "parameters": {}, + "id": "79d2c11c-0378-4ff5-b166-ae1bf773f53a", + "name": "IF14", + "type": "n8n-nodes-base.if", + "typeVersion": 1, + "position": [ + 3000, + 440 + ] + }, + { + "parameters": { + "jsCode": "// Loop over input items and add a new field called 'myNewField' to the JSON of each one\nfor (const item of $input.all()) {\n item.json.myNewField = 1;\n}\n\nreturn $input.all();" + }, + "id": "8483e962-24e7-4495-9c8e-481481ebe897", + "name": "Code13", + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 3260, + 300 + ] + }, + { + "parameters": { + "options": {} + }, + "id": "74dfb8f9-6d14-493e-97d5-729e1f44856b", + "name": "Edit Fields13", + "type": "n8n-nodes-base.set", + "typeVersion": 3.2, + "position": [ + 3480, + 300 + ] + }, + { + "parameters": {}, + "id": "0c2e8e54-958d-4932-91b5-b23979460c97", + "name": "IF15", + "type": "n8n-nodes-base.if", + "typeVersion": 1, + "position": [ + 3700, + 300 + ] + }, + { + "parameters": { + "jsCode": "// Loop over input items and add a new field called 'myNewField' to the JSON of each one\nfor (const item of $input.all()) {\n item.json.myNewField = 1;\n}\n\nreturn $input.all();" + }, + "id": "bfed29c6-c453-4850-8acf-7aa11b1d0d8e", + "name": "Code14", + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 3260, + 620 + ] + }, + { + "parameters": { + "options": {} + }, + "id": "d8415057-c597-40a9-95f6-bafbe3fafac0", + "name": "Edit Fields14", + "type": "n8n-nodes-base.set", + "typeVersion": 3.2, + "position": [ + 3480, + 620 + ] + }, + { + "parameters": {}, + "id": "51ed9040-bb6c-4f77-9740-74b54ac56a00", + "name": "IF16", + "type": "n8n-nodes-base.if", + "typeVersion": 1, + "position": [ + 3700, + 620 + ] + }, + { + "parameters": { + "jsCode": "// Loop over input items and add a new field called 'myNewField' to the JSON of each one\nfor (const item of $input.all()) {\n item.json.myNewField = 1;\n}\n\nreturn $input.all();" + }, + "id": "5864e701-eb16-4412-ae8b-be1f2a1f16a5", + "name": "Code2", + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 1820, + 1480 + ] + }, + { + "parameters": { + "options": {} + }, + "id": "4b7de291-f1c7-4ae8-a545-81aaa2ebd1fb", + "name": "Edit Fields2", + "type": "n8n-nodes-base.set", + "typeVersion": 3.2, + "position": [ + 2040, + 1480 + ] + }, + { + "parameters": {}, + "id": "328aa16f-82ed-465e-b548-9436f21eb519", + "name": "IF2", + "type": "n8n-nodes-base.if", + "typeVersion": 1, + "position": [ + 2260, + 1480 + ] + }, + { + "parameters": { + "jsCode": "// Loop over input items and add a new field called 'myNewField' to the JSON of each one\nfor (const item of $input.all()) {\n item.json.myNewField = 1;\n}\n\nreturn $input.all();" + }, + "id": "90aaf0b0-57b6-4a08-b000-abb2956ba640", + "name": "Code5", + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 2560, + 1140 + ] + }, + { + "parameters": { + "options": {} + }, + "id": "7d327c87-da3b-4f4b-9f9a-51c9c622990d", + "name": "Edit Fields5", + "type": "n8n-nodes-base.set", + "typeVersion": 3.2, + "position": [ + 2780, + 1140 + ] + }, + { + "parameters": {}, + "id": "fa2a3b1b-53de-454e-a16d-e2bf62cb05ec", + "name": "IF21", + "type": "n8n-nodes-base.if", + "typeVersion": 1, + "position": [ + 3000, + 1140 + ] + }, + { + "parameters": { + "jsCode": "// Loop over input items and add a new field called 'myNewField' to the JSON of each one\nfor (const item of $input.all()) {\n item.json.myNewField = 1;\n}\n\nreturn $input.all();" + }, + "id": "8efaa5a3-982e-41b4-af6e-28e35c64093d", + "name": "Code19", + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 3260, + 1000 + ] + }, + { + "parameters": { + "options": {} + }, + "id": "987e27fd-778a-4562-85a9-369b1ec232de", + "name": "Edit Fields19", + "type": "n8n-nodes-base.set", + "typeVersion": 3.2, + "position": [ + 3480, + 1000 + ] + }, + { + "parameters": {}, + "id": "b3f4e9b3-9995-4019-9b0f-dadd64e036b4", + "name": "IF22", + "type": "n8n-nodes-base.if", + "typeVersion": 1, + "position": [ + 3700, + 1000 + ] + }, + { + "parameters": { + "jsCode": "// Loop over input items and add a new field called 'myNewField' to the JSON of each one\nfor (const item of $input.all()) {\n item.json.myNewField = 1;\n}\n\nreturn $input.all();" + }, + "id": "681c1b30-063d-4c1e-b550-942a9dd3eb9a", + "name": "Code20", + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 3260, + 1320 + ] + }, + { + "parameters": { + "options": {} + }, + "id": "024770b6-7bf4-44f6-9675-d4f7dc73d6ac", + "name": "Edit Fields20", + "type": "n8n-nodes-base.set", + "typeVersion": 3.2, + "position": [ + 3480, + 1320 + ] + }, + { + "parameters": {}, + "id": "24699015-3ccf-4ffa-b52f-8ba4c4853963", + "name": "IF23", + "type": "n8n-nodes-base.if", + "typeVersion": 1, + "position": [ + 3700, + 1320 + ] + }, + { + "parameters": { + "jsCode": "// Loop over input items and add a new field called 'myNewField' to the JSON of each one\nfor (const item of $input.all()) {\n item.json.myNewField = 1;\n}\n\nreturn $input.all();" + }, + "id": "f4b2d116-2fda-4a3a-9509-0e8c64e7796e", + "name": "Code6", + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 2560, + 1840 + ] + }, + { + "parameters": { + "options": {} + }, + "id": "535e5b12-6743-4c01-9fc5-e27b10421423", + "name": "Edit Fields6", + "type": "n8n-nodes-base.set", + "typeVersion": 3.2, + "position": [ + 2780, + 1840 + ] + }, + { + "parameters": {}, + "id": "3dcbecdf-686b-445f-9c77-2902d0dc1f56", + "name": "IF28", + "type": "n8n-nodes-base.if", + "typeVersion": 1, + "position": [ + 3000, + 1840 + ] + }, + { + "parameters": { + "jsCode": "// Loop over input items and add a new field called 'myNewField' to the JSON of each one\nfor (const item of $input.all()) {\n item.json.myNewField = 1;\n}\n\nreturn $input.all();" + }, + "id": "7223c6ef-664b-426a-8d08-eca1b34e6b23", + "name": "Code25", + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 3260, + 1700 + ] + }, + { + "parameters": { + "options": {} + }, + "id": "496414a6-384a-4f94-97ec-d2e5ad646f82", + "name": "Edit Fields25", + "type": "n8n-nodes-base.set", + "typeVersion": 3.2, + "position": [ + 3480, + 1700 + ] + }, + { + "parameters": {}, + "id": "82f9562d-e4a8-49f3-924d-983effb4b6c6", + "name": "IF29", + "type": "n8n-nodes-base.if", + "typeVersion": 1, + "position": [ + 3700, + 1700 + ] + }, + { + "parameters": { + "jsCode": "// Loop over input items and add a new field called 'myNewField' to the JSON of each one\nfor (const item of $input.all()) {\n item.json.myNewField = 1;\n}\n\nreturn $input.all();" + }, + "id": "c91d4bc5-3c60-4c22-aa31-44e84e0816ec", + "name": "Code26", + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 3260, + 2020 + ] + }, + { + "parameters": { + "options": {} + }, + "id": "49b61f23-bf3f-474d-8bba-a3b7de6f6441", + "name": "Edit Fields26", + "type": "n8n-nodes-base.set", + "typeVersion": 3.2, + "position": [ + 3480, + 2020 + ] + }, + { + "parameters": {}, + "id": "1cad6ae3-1064-4f30-a9ec-502891868332", + "name": "IF30", + "type": "n8n-nodes-base.if", + "typeVersion": 1, + "position": [ + 3700, + 2020 + ] + } + ], + "pinData": {}, + "connections": { + "When clicking \"Test Workflow\"": { + "main": [ + [ + { + "node": "Code", + "type": "main", + "index": 0 + } + ] + ] + }, + "Code": { + "main": [ + [ + { + "node": "Edit Fields", + "type": "main", + "index": 0 + } + ] + ] + }, + "Edit Fields": { + "main": [ + [ + { + "node": "IF", + "type": "main", + "index": 0 + } + ] + ] + }, + "IF": { + "main": [ + [ + { + "node": "Code1", + "type": "main", + "index": 0 + } + ], + [ + { + "node": "Code2", + "type": "main", + "index": 0 + } + ] + ] + }, + "Code1": { + "main": [ + [ + { + "node": "Edit Fields1", + "type": "main", + "index": 0 + } + ] + ] + }, + "Edit Fields1": { + "main": [ + [ + { + "node": "IF1", + "type": "main", + "index": 0 + } + ] + ] + }, + "Code3": { + "main": [ + [ + { + "node": "Edit Fields3", + "type": "main", + "index": 0 + } + ] + ] + }, + "Edit Fields3": { + "main": [ + [ + { + "node": "IF7", + "type": "main", + "index": 0 + } + ] + ] + }, + "IF1": { + "main": [ + [ + { + "node": "Code3", + "type": "main", + "index": 0 + } + ], + [ + { + "node": "Code4", + "type": "main", + "index": 0 + } + ] + ] + }, + "IF7": { + "main": [ + [ + { + "node": "Code7", + "type": "main", + "index": 0 + } + ], + [ + { + "node": "Code8", + "type": "main", + "index": 0 + } + ] + ] + }, + "Code7": { + "main": [ + [ + { + "node": "Edit Fields7", + "type": "main", + "index": 0 + } + ] + ] + }, + "Edit Fields7": { + "main": [ + [ + { + "node": "IF8", + "type": "main", + "index": 0 + } + ] + ] + }, + "Code8": { + "main": [ + [ + { + "node": "Edit Fields8", + "type": "main", + "index": 0 + } + ] + ] + }, + "Edit Fields8": { + "main": [ + [ + { + "node": "IF9", + "type": "main", + "index": 0 + } + ] + ] + }, + "Code4": { + "main": [ + [ + { + "node": "Edit Fields4", + "type": "main", + "index": 0 + } + ] + ] + }, + "Edit Fields4": { + "main": [ + [ + { + "node": "IF14", + "type": "main", + "index": 0 + } + ] + ] + }, + "IF14": { + "main": [ + [ + { + "node": "Code13", + "type": "main", + "index": 0 + } + ], + [ + { + "node": "Code14", + "type": "main", + "index": 0 + } + ] + ] + }, + "Code13": { + "main": [ + [ + { + "node": "Edit Fields13", + "type": "main", + "index": 0 + } + ] + ] + }, + "Edit Fields13": { + "main": [ + [ + { + "node": "IF15", + "type": "main", + "index": 0 + } + ] + ] + }, + "Code14": { + "main": [ + [ + { + "node": "Edit Fields14", + "type": "main", + "index": 0 + } + ] + ] + }, + "Edit Fields14": { + "main": [ + [ + { + "node": "IF16", + "type": "main", + "index": 0 + } + ] + ] + }, + "Code2": { + "main": [ + [ + { + "node": "Edit Fields2", + "type": "main", + "index": 0 + } + ] + ] + }, + "Edit Fields2": { + "main": [ + [ + { + "node": "IF2", + "type": "main", + "index": 0 + } + ] + ] + }, + "IF2": { + "main": [ + [ + { + "node": "Code5", + "type": "main", + "index": 0 + } + ], + [ + { + "node": "Code6", + "type": "main", + "index": 0 + } + ] + ] + }, + "Code5": { + "main": [ + [ + { + "node": "Edit Fields5", + "type": "main", + "index": 0 + } + ] + ] + }, + "Edit Fields5": { + "main": [ + [ + { + "node": "IF21", + "type": "main", + "index": 0 + } + ] + ] + }, + "IF21": { + "main": [ + [ + { + "node": "Code19", + "type": "main", + "index": 0 + } + ], + [ + { + "node": "Code20", + "type": "main", + "index": 0 + } + ] + ] + }, + "Code19": { + "main": [ + [ + { + "node": "Edit Fields19", + "type": "main", + "index": 0 + } + ] + ] + }, + "Edit Fields19": { + "main": [ + [ + { + "node": "IF22", + "type": "main", + "index": 0 + } + ] + ] + }, + "Code20": { + "main": [ + [ + { + "node": "Edit Fields20", + "type": "main", + "index": 0 + } + ] + ] + }, + "Edit Fields20": { + "main": [ + [ + { + "node": "IF23", + "type": "main", + "index": 0 + } + ] + ] + }, + "Code6": { + "main": [ + [ + { + "node": "Edit Fields6", + "type": "main", + "index": 0 + } + ] + ] + }, + "Edit Fields6": { + "main": [ + [ + { + "node": "IF28", + "type": "main", + "index": 0 + } + ] + ] + }, + "IF28": { + "main": [ + [ + { + "node": "Code25", + "type": "main", + "index": 0 + } + ], + [ + { + "node": "Code26", + "type": "main", + "index": 0 + } + ] + ] + }, + "Code25": { + "main": [ + [ + { + "node": "Edit Fields25", + "type": "main", + "index": 0 + } + ] + ] + }, + "Edit Fields25": { + "main": [ + [ + { + "node": "IF29", + "type": "main", + "index": 0 + } + ] + ] + }, + "Code26": { + "main": [ + [ + { + "node": "Edit Fields26", + "type": "main", + "index": 0 + } + ] + ] + }, + "Edit Fields26": { + "main": [ + [ + { + "node": "IF30", + "type": "main", + "index": 0 + } + ] + ] + } + }, + "active": false, + "settings": { + "executionOrder": "v1" + }, + "versionId": "d38289e0-49d3-4e1d-8e4b-46e4eb85a2c9", + "id": "iKlx4AGIjCNJSu9M", + "meta": { + "instanceId": "8a47b83b4479b11330fdf21ccc96d4a8117035a968612e452b4c87bfd09c16c7" + }, + "tags": [] +} \ No newline at end of file diff --git a/cypress/fixtures/Node_IO_filter.json b/cypress/fixtures/Node_IO_filter.json new file mode 100644 index 0000000000..9bb9ff994f --- /dev/null +++ b/cypress/fixtures/Node_IO_filter.json @@ -0,0 +1,653 @@ +{ + "name": "Node IO filter", + "nodes": [ + { + "parameters": {}, + "id": "46770685-44d1-4aad-9107-1d790cf26b50", + "name": "When clicking \"Test Workflow\"", + "type": "n8n-nodes-base.manualTrigger", + "typeVersion": 1, + "position": [ + 840, + 180 + ] + }, + { + "parameters": { + "options": {} + }, + "id": "480e3832-2ce4-4118-9f7b-a8aed6017174", + "name": "Edit Fields", + "type": "n8n-nodes-base.set", + "typeVersion": 3.2, + "position": [ + 1080, + 180 + ] + }, + { + "parameters": { + "conditions": { + "string": [ + { + "value1": "={{ $json.profile.name }}", + "operation": "contains", + "value2": "an" + } + ] + } + }, + "id": "4773d460-6ed9-49e1-a688-7e480f0fbacf", + "name": "IF", + "type": "n8n-nodes-base.if", + "typeVersion": 1, + "position": [ + 1300, + 180 + ] + }, + { + "parameters": { + "options": {} + }, + "id": "d17dffe6-e29c-4c1a-8b4c-9e374dcd70ea", + "name": "True", + "type": "n8n-nodes-base.set", + "typeVersion": 3.2, + "position": [ + 1560, + 60 + ] + }, + { + "parameters": { + "options": {} + }, + "id": "893d6e79-feb4-4752-a6f8-e2e5f5163787", + "name": "False", + "type": "n8n-nodes-base.set", + "typeVersion": 3.2, + "position": [ + 1560, + 240 + ] + } + ], + "pinData": { + "When clicking \"Test Workflow\"": [ + { + "json": { + "id": "654cfa05fa51480dcb543b1a", + "email": "reese_hahn@kidgrease.coach", + "username": "reese94", + "profile": { + "name": "Reese Hahn", + "company": "Kidgrease", + "dob": "1994-06-18", + "address": "3 Richmond Street, Norfolk, Delaware", + "location": { + "lat": 22.507436, + "long": -50.812775 + }, + "about": "Cupidatat voluptate reprehenderit commodo mollit tempor sint id. Id exercitation id eiusmod dolore non non anim voluptate anim eu consectetur." + }, + "apiKey": "a18592bf-1147-4b61-a70f-2ab90b60bb6e", + "roles": [ + "guest" + ], + "createdAt": "2010-10-04T09:57:59.240Z", + "updatedAt": "2010-10-05T09:57:59.240Z" + } + }, + { + "json": { + "id": "654cfa055bea471bc4853158", + "email": "jeanne_boyd@hatology.gratis", + "username": "jeanne91", + "profile": { + "name": "Jeanne Boyd", + "company": "Hatology", + "dob": "1991-02-21", + "address": "81 Kingsway Place, Blairstown, Vermont", + "location": { + "lat": -57.665234, + "long": -41.301893 + }, + "about": "Proident pariatur non consequat cupidatat Lorem nisi est consequat dolor id eiusmod id. Amet culpa ex Lorem nostrud labore laboris culpa mollit dolor culpa ut." + }, + "apiKey": "8a6056a6-0197-4920-858d-cb26f8c8a1e2", + "roles": [ + "owner", + "admin" + ], + "createdAt": "2011-11-06T09:05:41.945Z", + "updatedAt": "2011-11-07T09:05:41.945Z" + } + }, + { + "json": { + "id": "654cfa05b012921c060dc5a5", + "email": "roslyn_underwood@portico.melbourne", + "username": "roslyn88", + "profile": { + "name": "Roslyn Underwood", + "company": "Portico", + "dob": "1988-04-30", + "address": "24 Schenck Street, Drytown, New Jersey", + "location": { + "lat": 11.797141, + "long": 10.751804 + }, + "about": "Duis excepteur minim consequat exercitation. Laboris occaecat cupidatat aliqua consequat occaecat." + }, + "apiKey": "72d629f3-d613-4fd0-bbfe-3f67c8ad7af2", + "roles": [ + "member", + "owner" + ], + "createdAt": "2012-11-17T22:09:10.911Z", + "updatedAt": "2012-11-18T22:09:10.911Z" + } + }, + { + "json": { + "id": "654cfa05df7b35968507efe6", + "email": "combs_hardy@acrodance.domains", + "username": "combs91", + "profile": { + "name": "Combs Hardy", + "company": "Acrodance", + "dob": "1991-04-30", + "address": "58 Pineapple Street, Falconaire, New Mexico", + "location": { + "lat": -62.922443, + "long": -159.493799 + }, + "about": "Magna qui minim velit magna est eiusmod aliquip elit aliquip excepteur. Laborum labore do ut et ut in incididunt do elit nostrud." + }, + "apiKey": "d9807b9e-aee9-486d-9826-4e6c166bfbe4", + "roles": [ + "owner", + "member" + ], + "createdAt": "2014-04-13T13:02:09.319Z", + "updatedAt": "2014-04-14T13:02:09.319Z" + } + }, + { + "json": { + "id": "654cfa05f2d4a0508a7c59c4", + "email": "terrell_peters@vantage.international", + "username": "terrell94", + "profile": { + "name": "Terrell Peters", + "company": "Vantage", + "dob": "1994-01-31", + "address": "10 Lafayette Walk, Vincent, Virginia", + "location": { + "lat": -62.267913, + "long": 29.682121 + }, + "about": "Eiusmod fugiat nulla ea tempor incididunt nulla nulla consectetur officia incididunt proident sint. Sunt duis non excepteur non." + }, + "apiKey": "20b96df1-d882-4dea-a505-84d7ff296a6e", + "roles": [ + "admin", + "guest" + ], + "createdAt": "2010-12-09T08:24:56.517Z", + "updatedAt": "2010-12-10T08:24:56.517Z" + } + }, + { + "json": { + "id": "654cfa0599fbabf3a05c7b14", + "email": "shari_winters@powernet.supply", + "username": "shari93", + "profile": { + "name": "Shari Winters", + "company": "Powernet", + "dob": "1993-03-10", + "address": "89 Aviation Road, Leyner, Indiana", + "location": { + "lat": 40.404704, + "long": -141.216235 + }, + "about": "Occaecat sit laboris elit laboris do anim culpa dolore exercitation enim. Non veniam sint exercitation irure." + }, + "apiKey": "2b869ce9-3431-4edb-944d-9d9336b1eb4a", + "roles": [ + "guest", + "admin" + ], + "createdAt": "2014-10-15T15:56:55.873Z", + "updatedAt": "2014-10-16T15:56:55.873Z" + } + }, + { + "json": { + "id": "654cfa050df18b4798ec95be", + "email": "rena_beasley@bitrex.ma", + "username": "rena90", + "profile": { + "name": "Rena Beasley", + "company": "Bitrex", + "dob": "1990-01-09", + "address": "78 Forbell Street, Homeland, Maine", + "location": { + "lat": 46.047548, + "long": 4.128049 + }, + "about": "Lorem aliqua veniam duis ut cillum ad sunt mollit incididunt elit. Ipsum incididunt et magna incididunt quis duis amet duis occaecat laborum nulla et commodo nisi." + }, + "apiKey": "17e350f8-1020-4344-bbd7-ceb62cd44edb", + "roles": [ + "member", + "owner" + ], + "createdAt": "2010-04-22T13:35:24.838Z", + "updatedAt": "2010-04-23T13:35:24.838Z" + } + }, + { + "json": { + "id": "654cfa0595243d2b7b1ea22a", + "email": "sally_gentry@eventex.maif", + "username": "sally93", + "profile": { + "name": "Sally Gentry", + "company": "Eventex", + "dob": "1993-04-03", + "address": "54 Plaza Street, Greenbackville, North Carolina", + "location": { + "lat": -20.529121, + "long": 73.533118 + }, + "about": "Laborum sit exercitation sint laborum. Fugiat sit ipsum ullamco sint do dolore in sunt incididunt adipisicing magna ullamco aute." + }, + "apiKey": "746b6ab3-c63f-44df-bb99-9de48f8e43c4", + "roles": [ + "owner", + "guest" + ], + "createdAt": "2011-09-18T13:18:49.655Z", + "updatedAt": "2011-09-19T13:18:49.655Z" + } + }, + { + "json": { + "id": "654cfa05cdea66c87bb01439", + "email": "battle_duran@jasper.property", + "username": "battle88", + "profile": { + "name": "Battle Duran", + "company": "Jasper", + "dob": "1988-11-04", + "address": "34 Amherst Street, Corriganville, Nevada", + "location": { + "lat": 74.391489, + "long": -98.421464 + }, + "about": "Nostrud occaecat laborum aliquip sint est minim id aliquip adipisicing dolor. Aute velit amet officia anim sint anim aliquip." + }, + "apiKey": "b22a3ddd-d540-4df0-9ce5-e837bc6a6a10", + "roles": [ + "member" + ], + "createdAt": "2012-08-31T19:14:37.463Z", + "updatedAt": "2012-09-01T19:14:37.463Z" + } + }, + { + "json": { + "id": "654cfa05e9c13e25d41d4135", + "email": "petty_moore@neurocell.shriram", + "username": "petty91", + "profile": { + "name": "Petty Moore", + "company": "Neurocell", + "dob": "1991-03-10", + "address": "78 Interborough Parkway, Grill, Texas", + "location": { + "lat": -79.817761, + "long": -36.728201 + }, + "about": "Dolor occaecat anim est Lorem culpa fugiat id aliqua sint. Sit nisi do exercitation do voluptate exercitation in." + }, + "apiKey": "4b341cfb-a83c-4f2a-9f4d-11cd747b8783", + "roles": [ + "admin" + ], + "createdAt": "2012-01-02T21:28:22.431Z", + "updatedAt": "2012-01-03T21:28:22.431Z" + } + }, + { + "json": { + "id": "654cfa052890c7b4d510d3d4", + "email": "matilda_kelley@senmei.in", + "username": "matilda93", + "profile": { + "name": "Matilda Kelley", + "company": "Senmei", + "dob": "1993-02-04", + "address": "29 Stuart Street, Henrietta, New York", + "location": { + "lat": 40.788206, + "long": -135.821558 + }, + "about": "Dolor veniam ex ullamco deserunt reprehenderit nostrud sunt culpa cupidatat qui labore deserunt. In ad anim laboris amet labore duis consequat nostrud eiusmod." + }, + "apiKey": "dcf40383-a00a-43ef-8bd0-4af7e70413bd", + "roles": [ + "owner", + "guest" + ], + "createdAt": "2014-03-28T22:07:39.636Z", + "updatedAt": "2014-03-29T22:07:39.636Z" + } + }, + { + "json": { + "id": "654cfa05af129db469473bf1", + "email": "savannah_hardin@exoblue.kn", + "username": "savannah89", + "profile": { + "name": "Savannah Hardin", + "company": "Exoblue", + "dob": "1989-07-01", + "address": "44 Navy Walk, Fresno, Kentucky", + "location": { + "lat": 75.679679, + "long": -58.534947 + }, + "about": "Id eiusmod eu elit consequat quis anim veniam officia anim ipsum. Sunt ex sit ipsum id est eu." + }, + "apiKey": "98d6abb7-e4aa-4b3b-8958-ff3c4d672f1d", + "roles": [ + "guest", + "member" + ], + "createdAt": "2011-04-15T00:55:02.325Z", + "updatedAt": "2011-04-16T00:55:02.325Z" + } + }, + { + "json": { + "id": "654cfa055dfa731b01573a67", + "email": "abbott_gallegos@katakana.dad", + "username": "abbott91", + "profile": { + "name": "Abbott Gallegos", + "company": "Katakana", + "dob": "1991-03-04", + "address": "85 Indiana Place, Forestburg, Michigan", + "location": { + "lat": -5.417414, + "long": -4.557904 + }, + "about": "Adipisicing amet ullamco aliquip velit nostrud qui non pariatur Lorem. Culpa ut deserunt esse quis magna." + }, + "apiKey": "3cf92c24-6193-4cc9-85fc-78e4ad9d6e13", + "roles": [ + "guest", + "owner" + ], + "createdAt": "2011-06-01T16:38:39.316Z", + "updatedAt": "2011-06-02T16:38:39.316Z" + } + }, + { + "json": { + "id": "654cfa05386de2e6d75c1694", + "email": "short_brennan@hyplex.tc", + "username": "short92", + "profile": { + "name": "Short Brennan", + "company": "Hyplex", + "dob": "1992-04-19", + "address": "21 Irving Place, Hinsdale, Northern Mariana Islands", + "location": { + "lat": 57.340225, + "long": -7.021582 + }, + "about": "Mollit dolor dolore deserunt anim minim adipisicing eiusmod velit tempor id veniam cupidatat. Magna veniam consequat incididunt ut quis culpa excepteur tempor eiusmod consectetur excepteur." + }, + "apiKey": "07bf533d-4a31-4e78-9d6e-d46160479069", + "roles": [ + "admin", + "member" + ], + "createdAt": "2014-03-10T19:25:02.217Z", + "updatedAt": "2014-03-11T19:25:02.217Z" + } + }, + { + "json": { + "id": "654cfa05fd2a878d43bb45cd", + "email": "bowers_cooke@iplax.ci", + "username": "bowers92", + "profile": { + "name": "Bowers Cooke", + "company": "Iplax", + "dob": "1992-07-05", + "address": "83 Greenpoint Avenue, Marion, Georgia", + "location": { + "lat": 64.261022, + "long": -58.493714 + }, + "about": "Deserunt ipsum fugiat tempor sunt eu ea laboris ad magna ex laborum laboris. Ullamco nostrud qui exercitation aute consectetur irure." + }, + "apiKey": "a3ecc58b-f292-4de1-b6e5-014345a76a7a", + "roles": [ + "member", + "owner" + ], + "createdAt": "2010-06-20T16:34:56.467Z", + "updatedAt": "2010-06-21T16:34:56.467Z" + } + }, + { + "json": { + "id": "654cfa05a6de547367990f9c", + "email": "tara_rutledge@escenta.lc", + "username": "tara90", + "profile": { + "name": "Tara Rutledge", + "company": "Escenta", + "dob": "1990-08-11", + "address": "25 Butler Place, Frierson, Missouri", + "location": { + "lat": -32.176783, + "long": 67.345415 + }, + "about": "Aute sunt laborum anim ex non pariatur nisi minim tempor adipisicing. Excepteur irure non amet eiusmod et excepteur." + }, + "apiKey": "22da9647-a7b7-4815-91bb-d5101fc90e55", + "roles": [ + "member" + ], + "createdAt": "2013-09-06T21:41:53.287Z", + "updatedAt": "2013-09-07T21:41:53.287Z" + } + }, + { + "json": { + "id": "654cfa053778601ad57f22cd", + "email": "elva_chapman@bytrex.gg", + "username": "elva90", + "profile": { + "name": "Elva Chapman", + "company": "Bytrex", + "dob": "1990-05-31", + "address": "4 Royce Place, Advance, New Hampshire", + "location": { + "lat": -28.393464, + "long": -28.622091 + }, + "about": "Est sit deserunt Lorem amet voluptate elit reprehenderit occaecat est eiusmod eu reprehenderit laborum. Pariatur magna occaecat et excepteur est excepteur consectetur ad nulla." + }, + "apiKey": "4d242fa4-ac69-42f1-8f12-ec19d9c6d632", + "roles": [ + "owner", + "admin" + ], + "createdAt": "2011-04-05T04:04:31.524Z", + "updatedAt": "2011-04-06T04:04:31.524Z" + } + }, + { + "json": { + "id": "654cfa054c6abbc57efcb100", + "email": "pitts_meyer@unisure.tui", + "username": "pitts93", + "profile": { + "name": "Pitts Meyer", + "company": "Unisure", + "dob": "1993-06-12", + "address": "47 Columbus Place, Cade, Alaska", + "location": { + "lat": 56.723675, + "long": 158.093389 + }, + "about": "Non ea pariatur excepteur nostrud elit quis qui. Dolore aute velit ipsum officia ea pariatur incididunt non elit tempor duis consequat." + }, + "apiKey": "82a88344-d289-447c-81b5-1ae10cd1994b", + "roles": [ + "guest", + "admin" + ], + "createdAt": "2014-05-15T06:38:59.269Z", + "updatedAt": "2014-05-16T06:38:59.269Z" + } + }, + { + "json": { + "id": "654cfa0527e7ce14e421d9cd", + "email": "delia_figueroa@overplex.um", + "username": "delia89", + "profile": { + "name": "Delia Figueroa", + "company": "Overplex", + "dob": "1989-04-22", + "address": "12 Nova Court, Taft, Ohio", + "location": { + "lat": -32.990583, + "long": -4.598863 + }, + "about": "Cupidatat fugiat veniam eu proident excepteur deserunt ad esse fugiat deserunt. Non velit cillum velit veniam ex minim eiusmod tempor excepteur voluptate adipisicing nostrud." + }, + "apiKey": "b3a7747b-24a0-4039-8a21-56e83441a660", + "roles": [ + "admin", + "guest" + ], + "createdAt": "2014-09-20T03:40:10.190Z", + "updatedAt": "2014-09-21T03:40:10.190Z" + } + }, + { + "json": { + "id": "654cfa05cf60000cbca6dca4", + "email": "kristina_fulton@portaline.engineer", + "username": "kristina88", + "profile": { + "name": "Kristina Fulton", + "company": "Portaline", + "dob": "1988-07-25", + "address": "50 Laurel Avenue, Greenwich, Palau", + "location": { + "lat": 44.118984, + "long": 41.518949 + }, + "about": "Id incididunt officia exercitation ipsum id cillum consectetur. Veniam enim voluptate ut proident ex." + }, + "apiKey": "c106dbf0-bfc0-461d-b1d7-1840fe8e1cbc", + "roles": [ + "admin", + "member" + ], + "createdAt": "2010-04-10T08:06:27.028Z", + "updatedAt": "2010-04-11T08:06:27.028Z" + } + }, + { + "json": { + "id": "654cfa0501fe5691d620f570", + "email": "gould_noel@gonkle.gmx", + "username": "gould91", + "profile": { + "name": "Gould Noel", + "company": "Gonkle", + "dob": "1991-10-08", + "address": "33 Crooke Avenue, Idamay, Oklahoma", + "location": { + "lat": -11.398731, + "long": 34.706948 + }, + "about": "Veniam esse tempor aute quis mollit consequat Lorem. Nostrud ea dolore laboris Lorem elit est do nisi Lorem minim reprehenderit culpa." + }, + "apiKey": "1089783d-32ae-4102-8ac5-1e7f6cebe3c1", + "roles": [ + "guest", + "admin" + ], + "createdAt": "2011-12-30T20:24:19.620Z", + "updatedAt": "2011-12-31T20:24:19.620Z" + } + } + ] + }, + "connections": { + "When clicking \"Test Workflow\"": { + "main": [ + [ + { + "node": "Edit Fields", + "type": "main", + "index": 0 + } + ] + ] + }, + "Edit Fields": { + "main": [ + [ + { + "node": "IF", + "type": "main", + "index": 0 + } + ] + ] + }, + "IF": { + "main": [ + [ + { + "node": "True", + "type": "main", + "index": 0 + } + ], + [ + { + "node": "False", + "type": "main", + "index": 0 + } + ] + ] + } + }, + "active": false, + "settings": { + "executionOrder": "v1" + }, + "versionId": "9812dda2-cc1b-4458-97d8-21ccb18c90d1", + "id": "WNq486x7DpV1MPRH", + "meta": { + "instanceId": "8a47b83b4479b11330fdf21ccc96d4a8117035a968612e452b4c87bfd09c16c7" + }, + "tags": [] +} diff --git a/cypress/fixtures/Plan_data_opt_in_trial.json b/cypress/fixtures/Plan_data_opt_in_trial.json index 504805de32..7a805708c6 100644 --- a/cypress/fixtures/Plan_data_opt_in_trial.json +++ b/cypress/fixtures/Plan_data_opt_in_trial.json @@ -13,8 +13,7 @@ "feat:advancedExecutionFilters": true, "quota:users": -1, "quota:maxVariables": -1, - "feat:variables": true, - "feat:apiDisabled": true + "feat:variables": true }, "metadata": { "version": "v1", diff --git a/cypress/fixtures/Schedule_pinned.json b/cypress/fixtures/Schedule_pinned.json new file mode 100644 index 0000000000..e27623b6fa --- /dev/null +++ b/cypress/fixtures/Schedule_pinned.json @@ -0,0 +1,313 @@ +{ + "name": "Schedule + pinned", + "nodes": [ + { + "parameters": { + "rule": { + "interval": [ + {} + ] + } + }, + "id": "66358c29-b263-43dd-be25-3b068b0a88eb", + "name": "Schedule Trigger", + "type": "n8n-nodes-base.scheduleTrigger", + "typeVersion": 1.1, + "position": [ + 660, + 340 + ] + }, + { + "parameters": { + "options": {} + }, + "id": "6d903354-4e59-4032-81fe-426a5d6ec33c", + "name": "Edit Fields", + "type": "n8n-nodes-base.set", + "typeVersion": 3.2, + "position": [ + 860, + 240 + ] + }, + { + "parameters": { + "options": {} + }, + "id": "d8a1e9cf-81d3-400f-97d4-ad6167e7b236", + "name": "Edit Fields1", + "type": "n8n-nodes-base.set", + "typeVersion": 3.2, + "position": [ + 860, + 440 + ] + }, + { + "parameters": { + "options": {} + }, + "id": "bdc41148-067e-4649-8f21-5707b128d877", + "name": "Edit Fields2", + "type": "n8n-nodes-base.set", + "typeVersion": 3.2, + "position": [ + 1080, + 440 + ] + }, + { + "parameters": { + "options": {} + }, + "id": "d5a4337f-a6b3-4b51-9b02-e668593d9ae8", + "name": "Edit Fields3", + "type": "n8n-nodes-base.set", + "typeVersion": 3.2, + "position": [ + 1300, + 440 + ] + }, + { + "parameters": { + "options": {} + }, + "id": "fbc23f60-e7f6-4423-9329-33b0e4809a9a", + "name": "Edit Fields4", + "type": "n8n-nodes-base.set", + "typeVersion": 3.2, + "position": [ + 1500, + 440 + ] + }, + { + "parameters": { + "options": {} + }, + "id": "eaee47b0-94ec-4137-bfeb-a6c1a2c63f81", + "name": "Edit Fields5", + "type": "n8n-nodes-base.set", + "typeVersion": 3.2, + "position": [ + 1080, + 240 + ] + }, + { + "parameters": { + "options": {} + }, + "id": "eabb6308-21e9-4e59-8f74-9220a03c3186", + "name": "Edit Fields6", + "type": "n8n-nodes-base.set", + "typeVersion": 3.2, + "position": [ + 1300, + 240 + ] + }, + { + "parameters": { + "options": {} + }, + "id": "8812a45b-5545-4080-aad8-8e9f7b17ecd7", + "name": "Edit Fields7", + "type": "n8n-nodes-base.set", + "typeVersion": 3.2, + "position": [ + 1500, + 240 + ] + }, + { + "parameters": { + "options": {} + }, + "id": "d5ea3c5b-0b3e-4514-93e1-9c88563bab5c", + "name": "Edit Fields9", + "type": "n8n-nodes-base.set", + "typeVersion": 3.2, + "position": [ + 1700, + 240 + ] + }, + { + "parameters": { + "options": {} + }, + "id": "7af34474-5cd0-40b1-abea-850858e3b495", + "name": "Edit Fields10", + "type": "n8n-nodes-base.set", + "typeVersion": 3.2, + "position": [ + 1700, + 440 + ] + } + ], + "pinData": { + "Schedule Trigger": [ + { + "json": { + "name": "First item", + "code": 1 + } + }, + { + "json": { + "name": "Second item", + "code": 2 + } + } + ], + "Edit Fields7": [ + { + "json": { + "name": "First item", + "code": 1 + } + }, + { + "json": { + "name": "Second item", + "code": 2 + } + } + ], + "Edit Fields2": [ + { + "json": { + "name": "First item", + "code": 1 + } + }, + { + "json": { + "name": "Second item", + "code": 2 + } + } + ] + }, + "connections": { + "Schedule Trigger": { + "main": [ + [ + { + "node": "Edit Fields", + "type": "main", + "index": 0 + }, + { + "node": "Edit Fields1", + "type": "main", + "index": 0 + } + ] + ] + }, + "Edit Fields1": { + "main": [ + [ + { + "node": "Edit Fields2", + "type": "main", + "index": 0 + } + ] + ] + }, + "Edit Fields2": { + "main": [ + [ + { + "node": "Edit Fields3", + "type": "main", + "index": 0 + } + ] + ] + }, + "Edit Fields3": { + "main": [ + [ + { + "node": "Edit Fields4", + "type": "main", + "index": 0 + } + ] + ] + }, + "Edit Fields5": { + "main": [ + [ + { + "node": "Edit Fields6", + "type": "main", + "index": 0 + } + ] + ] + }, + "Edit Fields6": { + "main": [ + [ + { + "node": "Edit Fields7", + "type": "main", + "index": 0 + } + ] + ] + }, + "Edit Fields": { + "main": [ + [ + { + "node": "Edit Fields5", + "type": "main", + "index": 0 + } + ] + ] + }, + "Edit Fields7": { + "main": [ + [ + { + "node": "Edit Fields9", + "type": "main", + "index": 0 + } + ] + ] + }, + "Edit Fields4": { + "main": [ + [ + { + "node": "Edit Fields10", + "type": "main", + "index": 0 + } + ] + ] + } + }, + "active": false, + "settings": { + "executionOrder": "v1" + }, + "versionId": "9b6c68c0-f94f-45bc-a604-bf97d17a47ac", + "meta": { + "templateCredsSetupCompleted": true, + "instanceId": "8a47b83b4479b11330fdf21ccc96d4a8117035a968612e452b4c87bfd09c16c7" + }, + "id": "nWzcnYUb3AVaZpHG", + "tags": [] +} diff --git a/cypress/fixtures/Suggested_Templates.json b/cypress/fixtures/Suggested_Templates.json new file mode 100644 index 0000000000..982b75296d --- /dev/null +++ b/cypress/fixtures/Suggested_Templates.json @@ -0,0 +1,655 @@ +{ + "sections": [ + { + "name": "Lead enrichment", + "description": "Explore curated lead enrichment workflows or start fresh with a blank canvas", + "workflows": [ + { + "title": "Score new leads with AI from Facebook Lead Ads with AI and get notifications for high scores on Slack", + "description": "This workflow will help you save tons of time and will notify you fully automatically about the most important incoming leads from Facebook Lead Ads. The workflow will automatically fire for every submission. It will then take the name, company, and email information, enrich the submitter via AI, and score it based on metrics that you can easily set.", + "preview": { + "nodes": [ + { + "parameters": { + "operation": "create", + "base": { + "__rl": true, + "mode": "list", + "value": "" + }, + "table": { + "__rl": true, + "mode": "list", + "value": "" + }, + "columns": { + "mappingMode": "defineBelow", + "value": {}, + "matchingColumns": [], + "schema": [] + }, + "options": {} + }, + "id": "b09d4f4d-19fa-43de-8148-2d430a04956f", + "name": "Airtable", + "type": "n8n-nodes-base.airtable", + "typeVersion": 2, + "position": [ + 1800, + 740 + ] + }, + { + "parameters": {}, + "id": "551313bb-1e01-4133-9956-e6f09968f2ce", + "name": "When clicking \"Test Workflow\"", + "type": "n8n-nodes-base.manualTrigger", + "typeVersion": 1, + "position": [ + 920, + 740 + ] + }, + { + "parameters": { + "options": {} + }, + "id": "b4c089ee-2adb-435e-8d48-47012c981a11", + "name": "Get image", + "type": "n8n-nodes-base.httpRequest", + "typeVersion": 4.1, + "position": [ + 1140, + 740 + ] + }, + { + "parameters": { + "operation": "extractHtmlContent", + "options": {} + }, + "id": "04ca2f61-b930-4fbc-b467-3470c0d93d64", + "name": "Extract Information", + "type": "n8n-nodes-base.html", + "typeVersion": 1, + "position": [ + 1360, + 740 + ] + }, + { + "parameters": { + "options": {} + }, + "id": "d1a77493-c579-4ac4-b6a7-708eea2bf8ce", + "name": "Set Information", + "type": "n8n-nodes-base.set", + "typeVersion": 3.2, + "position": [ + 1580, + 740 + ] + } + ], + "connections": { + "When clicking \"Test Workflow\"": { + "main": [ + [ + { + "node": "Get image", + "type": "main", + "index": 0 + } + ] + ] + }, + "Get image": { + "main": [ + [ + { + "node": "Extract Information", + "type": "main", + "index": 0 + } + ] + ] + }, + "Extract Information": { + "main": [ + [ + { + "node": "Set Information", + "type": "main", + "index": 0 + } + ] + ] + }, + "Set Information": { + "main": [ + [ + { + "node": "Airtable", + "type": "main", + "index": 0 + } + ] + ] + } + } + }, + "nodes": [ + { + "id": 24, + "icon": "fa:code-branch", + "defaults": { + "color": "#00bbcc" + }, + "iconData": { + "icon": "code-branch", + "type": "icon" + }, + "displayName": "Merge" + } + ] + }, + { + "title": "Verify the email address every time a contact is created in HubSpot", + "description": "This workflow will help you save tons of time and will notify you fully automatically about the most important incoming leads from Facebook Lead Ads. The workflow will automatically fire for every submission. It will then take the name, company, and email information, enrich the submitter via AI, and score it based on metrics that you can easily set.", + "preview": { + "nodes": [ + { + "parameters": { + "operation": "create", + "base": { + "__rl": true, + "mode": "list", + "value": "" + }, + "table": { + "__rl": true, + "mode": "list", + "value": "" + }, + "columns": { + "mappingMode": "defineBelow", + "value": {}, + "matchingColumns": [], + "schema": [] + }, + "options": {} + }, + "id": "b09d4f4d-19fa-43de-8148-2d430a04956f", + "name": "Airtable", + "type": "n8n-nodes-base.airtable", + "typeVersion": 2, + "position": [ + 1800, + 740 + ] + }, + { + "parameters": {}, + "id": "551313bb-1e01-4133-9956-e6f09968f2ce", + "name": "When clicking \"Test Workflow\"", + "type": "n8n-nodes-base.manualTrigger", + "typeVersion": 1, + "position": [ + 920, + 740 + ] + }, + { + "parameters": { + "options": {} + }, + "id": "b4c089ee-2adb-435e-8d48-47012c981a11", + "name": "Get image", + "type": "n8n-nodes-base.httpRequest", + "typeVersion": 4.1, + "position": [ + 1140, + 740 + ] + }, + { + "parameters": { + "operation": "extractHtmlContent", + "options": {} + }, + "id": "04ca2f61-b930-4fbc-b467-3470c0d93d64", + "name": "Extract Information", + "type": "n8n-nodes-base.html", + "typeVersion": 1, + "position": [ + 1360, + 740 + ] + }, + { + "parameters": { + "options": {} + }, + "id": "d1a77493-c579-4ac4-b6a7-708eea2bf8ce", + "name": "Set Information", + "type": "n8n-nodes-base.set", + "typeVersion": 3.2, + "position": [ + 1580, + 740 + ] + } + ], + "connections": { + "When clicking \"Test Workflow\"": { + "main": [ + [ + { + "node": "Get image", + "type": "main", + "index": 0 + } + ] + ] + }, + "Get image": { + "main": [ + [ + { + "node": "Extract Information", + "type": "main", + "index": 0 + } + ] + ] + }, + "Extract Information": { + "main": [ + [ + { + "node": "Set Information", + "type": "main", + "index": 0 + } + ] + ] + }, + "Set Information": { + "main": [ + [ + { + "node": "Airtable", + "type": "main", + "index": 0 + } + ] + ] + } + } + }, + "nodes": [ + { + "id": 14, + "icon": "fa:code", + "name": "n8n-nodes-base.function", + "defaults": { + "name": "Function", + "color": "#FF9922" + }, + "iconData": { + "icon": "code", + "type": "icon" + }, + "categories": [ + { + "id": 5, + "name": "Development" + }, + { + "id": 9, + "name": "Core Nodes" + } + ], + "displayName": "Function", + "typeVersion": 1 + }, + { + "id": 24, + "icon": "fa:code-branch", + "name": "n8n-nodes-base.merge", + "defaults": { + "name": "Merge", + "color": "#00bbcc" + }, + "iconData": { + "icon": "code-branch", + "type": "icon" + }, + "categories": [ + { + "id": 9, + "name": "Core Nodes" + } + ], + "displayName": "Merge", + "typeVersion": 2 + } + ] + }, + { + "title": "Enrich leads from HubSpot with company information via OpenAi", + "description": "This workflow will help you save tons of time and will notify you fully automatically about the most important incoming leads from Facebook Lead Ads. The workflow will automatically fire for every submission. It will then take the name, company, and email information, enrich the submitter via AI, and score it based on metrics that you can easily set.", + "preview": { + "nodes": [ + { + "parameters": { + "operation": "create", + "base": { + "__rl": true, + "mode": "list", + "value": "" + }, + "table": { + "__rl": true, + "mode": "list", + "value": "" + }, + "columns": { + "mappingMode": "defineBelow", + "value": {}, + "matchingColumns": [], + "schema": [] + }, + "options": {} + }, + "id": "b09d4f4d-19fa-43de-8148-2d430a04956f", + "name": "Airtable", + "type": "n8n-nodes-base.airtable", + "typeVersion": 2, + "position": [ + 1800, + 740 + ] + }, + { + "parameters": {}, + "id": "551313bb-1e01-4133-9956-e6f09968f2ce", + "name": "When clicking \"Test Workflow\"", + "type": "n8n-nodes-base.manualTrigger", + "typeVersion": 1, + "position": [ + 920, + 740 + ] + }, + { + "parameters": { + "options": {} + }, + "id": "b4c089ee-2adb-435e-8d48-47012c981a11", + "name": "Get image", + "type": "n8n-nodes-base.httpRequest", + "typeVersion": 4.1, + "position": [ + 1140, + 740 + ] + }, + { + "parameters": { + "operation": "extractHtmlContent", + "options": {} + }, + "id": "04ca2f61-b930-4fbc-b467-3470c0d93d64", + "name": "Extract Information", + "type": "n8n-nodes-base.html", + "typeVersion": 1, + "position": [ + 1360, + 740 + ] + }, + { + "parameters": { + "options": {} + }, + "id": "d1a77493-c579-4ac4-b6a7-708eea2bf8ce", + "name": "Set Information", + "type": "n8n-nodes-base.set", + "typeVersion": 3.2, + "position": [ + 1580, + 740 + ] + } + ], + "connections": { + "When clicking \"Test Workflow\"": { + "main": [ + [ + { + "node": "Get image", + "type": "main", + "index": 0 + } + ] + ] + }, + "Get image": { + "main": [ + [ + { + "node": "Extract Information", + "type": "main", + "index": 0 + } + ] + ] + }, + "Extract Information": { + "main": [ + [ + { + "node": "Set Information", + "type": "main", + "index": 0 + } + ] + ] + }, + "Set Information": { + "main": [ + [ + { + "node": "Airtable", + "type": "main", + "index": 0 + } + ] + ] + } + } + }, + "nodes": [ + { + "id": 14, + "icon": "fa:code", + "defaults": { + "name": "Function", + "color": "#FF9922" + }, + "iconData": { + "icon": "code", + "type": "icon" + }, + "displayName": "Function" + } + ] + }, + { + "title": "Score new lead submissions from Facebook Lead Ads with AI and notify me on Slack when it is a high score lead", + "description": "This workflow will help you save tons of time and will notify you fully automatically about the most important incoming leads from Facebook Lead Ads. The workflow will automatically fire for every submission. It will then take the name, company, and email information, enrich the submitter via AI, and score it based on metrics that you can easily set.", + "preview": { + "nodes": [ + { + "parameters": { + "operation": "create", + "base": { + "__rl": true, + "mode": "list", + "value": "" + }, + "table": { + "__rl": true, + "mode": "list", + "value": "" + }, + "columns": { + "mappingMode": "defineBelow", + "value": {}, + "matchingColumns": [], + "schema": [] + }, + "options": {} + }, + "id": "b09d4f4d-19fa-43de-8148-2d430a04956f", + "name": "Airtable", + "type": "n8n-nodes-base.airtable", + "typeVersion": 2, + "position": [ + 1800, + 740 + ] + }, + { + "parameters": {}, + "id": "551313bb-1e01-4133-9956-e6f09968f2ce", + "name": "When clicking \"Test Workflow\"", + "type": "n8n-nodes-base.manualTrigger", + "typeVersion": 1, + "position": [ + 920, + 740 + ] + }, + { + "parameters": { + "options": {} + }, + "id": "b4c089ee-2adb-435e-8d48-47012c981a11", + "name": "Get image", + "type": "n8n-nodes-base.httpRequest", + "typeVersion": 4.1, + "position": [ + 1140, + 740 + ] + }, + { + "parameters": { + "operation": "extractHtmlContent", + "options": {} + }, + "id": "04ca2f61-b930-4fbc-b467-3470c0d93d64", + "name": "Extract Information", + "type": "n8n-nodes-base.html", + "typeVersion": 1, + "position": [ + 1360, + 740 + ] + }, + { + "parameters": { + "options": {} + }, + "id": "d1a77493-c579-4ac4-b6a7-708eea2bf8ce", + "name": "Set Information", + "type": "n8n-nodes-base.set", + "typeVersion": 3.2, + "position": [ + 1580, + 740 + ] + } + ], + "connections": { + "When clicking \"Test Workflow\"": { + "main": [ + [ + { + "node": "Get image", + "type": "main", + "index": 0 + } + ] + ] + }, + "Get image": { + "main": [ + [ + { + "node": "Extract Information", + "type": "main", + "index": 0 + } + ] + ] + }, + "Extract Information": { + "main": [ + [ + { + "node": "Set Information", + "type": "main", + "index": 0 + } + ] + ] + }, + "Set Information": { + "main": [ + [ + { + "node": "Airtable", + "type": "main", + "index": 0 + } + ] + ] + } + } + }, + "nodes": [ + { + "id": 14, + "icon": "fa:code", + "defaults": { + "name": "Function", + "color": "#FF9922" + }, + "iconData": { + "icon": "code", + "type": "icon" + }, + "displayName": "Function" + }, + { + "id": 24, + "icon": "fa:code-branch", + "defaults": { + "name": "Merge", + "color": "#00bbcc" + }, + "iconData": { + "icon": "code-branch", + "type": "icon" + }, + "displayName": "Merge" + } + ] + } + ] + } + ] +} diff --git a/cypress/fixtures/Test_Template_1.json b/cypress/fixtures/Test_Template_1.json new file mode 100644 index 0000000000..f15970677e --- /dev/null +++ b/cypress/fixtures/Test_Template_1.json @@ -0,0 +1,177 @@ +{ + "workflow": { + "id": 1205, + "name": "Promote new Shopify products on Twitter and Telegram", + "views": 478, + "recentViews": 9880, + "totalViews": 478, + "createdAt": "2021-08-24T10:40:50.007Z", + "description": "This workflow automatically promotes your new Shopify products on Twitter and Telegram. This workflow is also featured in the blog post [*6 e-commerce workflows to power up your Shopify store*](https://n8n.io/blog/no-code-ecommerce-workflow-automations/#promote-your-new-products-on-social-media).\n\n## Prerequisites\n\n- A Shopify account and [credentials](https://docs.n8n.io/integrations/credentials/shopify/)\n- A Twitter account and [credentials](https://docs.n8n.io/integrations/credentials/twitter/)\n- A Telegram account and [credentials](https://docs.n8n.io/integrations/credentials/telegram/) for the channel you want to send messages to.\n\n## Nodes\n\n- [Shopify Trigger node](https://docs.n8n.io/integrations/trigger-nodes/n8n-nodes-base.shopifytrigger/) triggers the workflow when you create a new product in Shopify.\n- [Twitter node](https://docs.n8n.io/integrations/nodes/n8n-nodes-base.twitter/) posts a tweet with the text \"Hey there, my design is now on a new product! Visit my {shop name} to get this cool {product title} (and check out more {product type})\".\n- [Telegram node](https://docs.n8n.io/integrations/nodes/n8n-nodes-base.telegram/) posts a message with the same text as above in a Telegram channel.", + "workflow": { + "nodes": [ + { + "name": "Twitter", + "type": "n8n-nodes-base.twitter", + "position": [ + 720, + -220 + ], + "parameters": { + "text": "=Hey there, my design is now on a new product ✨\nVisit my {{$json[\"vendor\"]}} shop to get this cool{{$json[\"title\"]}} (and check out more {{$json[\"product_type\"]}}) 🛍️", + "additionalFields": {} + }, + "credentials": { + "twitterOAuth1Api": "twitter" + }, + "typeVersion": 1 + }, + { + "name": "Telegram", + "type": "n8n-nodes-base.telegram", + "position": [ + 720, + -20 + ], + "parameters": { + "text": "=Hey there, my design is now on a new product!\nVisit my {{$json[\"vendor\"]}} shop to get this cool{{$json[\"title\"]}} (and check out more {{$json[\"product_type\"]}})", + "chatId": "123456", + "additionalFields": {} + }, + "credentials": { + "telegramApi": "telegram_habot" + }, + "typeVersion": 1 + }, + { + "name": "product created", + "type": "n8n-nodes-base.shopifyTrigger", + "position": [ + 540, + -110 + ], + "webhookId": "2a7e0e50-8f09-4a2b-bf54-a849a6ac4fe0", + "parameters": { + "topic": "products/create" + }, + "credentials": { + "shopifyApi": "shopify_nodeqa" + }, + "typeVersion": 1 + } + ], + "connections": { + "product created": { + "main": [ + [ + { + "node": "Twitter", + "type": "main", + "index": 0 + }, + { + "node": "Telegram", + "type": "main", + "index": 0 + } + ] + ] + } + } + }, + "workflowInfo": { + "nodeCount": 3, + "nodeTypes": { + "n8n-nodes-base.twitter": { + "count": 1 + }, + "n8n-nodes-base.telegram": { + "count": 1 + }, + "n8n-nodes-base.shopifyTrigger": { + "count": 1 + } + } + }, + "user": { + "username": "lorenanda" + }, + "nodes": [ + { + "id": 49, + "icon": "file:telegram.svg", + "name": "n8n-nodes-base.telegram", + "defaults": { + "name": "Telegram" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgNjYgNjYiIGZpbGw9IiNmZmYiIGZpbGwtcnVsZT0iZXZlbm9kZCIgc3Ryb2tlPSIjMDAwIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjx1c2UgeGxpbms6aHJlZj0iI2EiIHg9Ii41IiB5PSIuNSIvPjxzeW1ib2wgaWQ9ImEiIG92ZXJmbG93PSJ2aXNpYmxlIj48ZyBzdHJva2U9Im5vbmUiIGZpbGwtcnVsZT0ibm9uemVybyI+PHBhdGggZD0iTTAgMzJjMCAxNy42NzMgMTQuMzI3IDMyIDMyIDMyczMyLTE0LjMyNyAzMi0zMlM0OS42NzMgMCAzMiAwIDAgMTQuMzI3IDAgMzIiIGZpbGw9IiMzN2FlZTIiLz48cGF0aCBkPSJNMjEuNjYxIDM0LjMzOGwzLjc5NyAxMC41MDhzLjQ3NS45ODMuOTgzLjk4MyA4LjA2OC03Ljg2NCA4LjA2OC03Ljg2NGw4LjQwNy0xNi4yMzctMjEuMTE5IDkuODk4eiIgZmlsbD0iI2M4ZGFlYSIvPjxwYXRoIGQ9Ik0yNi42OTUgMzcuMDM0bC0uNzI5IDcuNzQ2cy0uMzA1IDIuMzczIDIuMDY4IDBsNC42NDQtNC4yMDMiIGZpbGw9IiNhOWM2ZDgiLz48cGF0aCBkPSJNMjEuNzMgMzQuNzEybC03LjgwOS0yLjU0NXMtLjkzMi0uMzc4LS42MzMtMS4yMzdjLjA2Mi0uMTc3LjE4Ni0uMzI4LjU1OS0uNTg4IDEuNzMxLTEuMjA2IDMyLjAyOC0xMi4wOTYgMzIuMDI4LTEyLjA5NnMuODU2LS4yODggMS4zNjEtLjA5N2MuMjMxLjA4OC4zNzguMTg3LjUwMy41NDguMDQ1LjEzMi4wNzEuNDExLjA2OC42ODktLjAwMy4yMDEtLjAyNy4zODYtLjA0NS42NzgtLjE4NCAyLjk3OC01LjcwNiAyNS4xOTgtNS43MDYgMjUuMTk4cy0uMzMgMS4zLTEuNTE0IDEuMzQ1Yy0uNDMyLjAxNi0uOTU2LS4wNzEtMS41ODItLjYxLTIuMzIzLTEuOTk4LTEwLjM1Mi03LjM5NC0xMi4xMjYtOC41OGEuMzQuMzQgMCAwMS0uMTQ2LS4yMzljLS4wMjUtLjEyNS4xMDgtLjI4LjEwOC0uMjhzMTMuOTgtMTIuNDI3IDE0LjM1Mi0xMy43MzFjLjAyOS0uMTAxLS4wNzktLjE1MS0uMjI2LS4xMDctLjkyOS4zNDItMTcuMDI1IDEwLjUwNi0xOC44MDEgMTEuNjI5LS4xMDQuMDY2LS4zOTUuMDIzLS4zOTUuMDIzIi8+PC9nPjwvc3ltYm9sPjwvc3ZnPg==" + }, + "categories": [ + { + "id": 6, + "name": "Communication" + } + ], + "displayName": "Telegram", + "typeVersion": 1 + }, + { + "id": 107, + "icon": "file:shopify.svg", + "name": "n8n-nodes-base.shopifyTrigger", + "defaults": { + "name": "Shopify Trigger" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgNTggNjYiIGZpbGw9IiNmZmYiIGZpbGwtcnVsZT0iZXZlbm9kZCIgc3Ryb2tlPSIjMDAwIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjx1c2UgeGxpbms6aHJlZj0iI2EiIHg9Ii41IiB5PSIuNSIvPjxzeW1ib2wgaWQ9ImEiIG92ZXJmbG93PSJ2aXNpYmxlIj48ZyBzdHJva2U9Im5vbmUiIGZpbGwtcnVsZT0ibm9uemVybyI+PHBhdGggZD0iTTQ5LjI1NSAxMi40ODRhLjYzMy42MzMgMCAwMC0uNTY0LS41MjdjLS4yMjUtLjAzNy01LjE3LS4zNzYtNS4xNy0uMzc2bC0zLjc3LTMuNzdjLS4zNC0uMzc2LTEuMDkyLS4yNjYtMS4zNzYtLjE4OC0uMDM3IDAtLjc1Mi4yMjUtMS45MjIuNjA1LTEuMTM3LTMuMy0zLjE1LTYuMzA2LTYuNjk2LTYuMzA2aC0uMzAzQzI4LjQzOC42MDUgMjcuMTk0IDAgMjYuMTQ0IDBjLTguMjU2LjAzNy0xMi4yIDEwLjMzMy0xMy40MzQgMTUuNTk0bC01Ljc3IDEuNzdjLTEuNzcuNTY0LTEuODM1LjYwNS0yLjA3MyAyLjI5M0wwIDU3LjE3NSAzNi40NjggNjRsMTkuNzYzLTQuMjZjMC0uMDM3LTYuOTQtNDYuODk3LTYuOTc2LTQ3LjI1NXpNMzQuNDMxIDguODZjLS45MTcuMzAzLTEuOTYzLjYwNS0zLjEuOTQ1di0uNjhhMTUuMDMgMTUuMDMgMCAwMC0uNzUyLTQuOTk5YzEuODQ4LjI4NCAzLjEgMi4zNTcgMy44NDMgNC43MzN6bS02LjA2OC00LjI5OGMuNjAzIDEuNzc4Ljg4MyAzLjY1LjgyNiA1LjUyN3YuMzRsLTYuMzc1IDEuOTYzYzEuMjQ4LTQuNjYgMy41NS02Ljk2MiA1LjU1LTcuODN6bS0yLjQ1LTIuMjkzYTEuOTQgMS45NCAwIDAxMS4wNTUuMzM5Yy0yLjY2IDEuMjM4LTUuNDcyIDQuMzY2LTYuNjc4IDEwLjYyN2wtNS4wNDUgMS41NDZDMTYuNjY4IDEwLjAzIDE5Ljk4OCAyLjI2IDI1LjkxIDIuMjZ6IiBmaWxsPSIjOTViZjQ3Ii8+PHBhdGggZD0iTTQ4LjY5MSAxMS45NTdjLS4yMjUtLjAzNy01LjE3LS4zNzYtNS4xNy0uMzc2bC0zLjc3LTMuNzdhLjc1My43NTMgMCAwMC0uNTI3LS4yMjVMMzYuNDcyIDY0bDE5Ljc2My00LjI2LTYuOTgtNDcuMjE4YS42OC42OCAwIDAwLS41NjQtLjU2NHoiIGZpbGw9IiM1ZThlM2UiLz48cGF0aCBkPSJNMjkuNzU4IDIyLjlsLTIuNDU0IDcuMjQyYTExLjM2IDExLjM2IDAgMDAtNC43NTItMS4xMzNjLTMuODQ4IDAtNC4wMzYgMi40MTItNC4wMzYgMy4wMTggMCAzLjI5OCA4LjYzNiA0LjU2NCA4LjYzNiAxMi4zMzMgMCA2LjEtMy44ODUgMTAuMDMtOS4xIDEwLjAzLTYuMjYgMC05LjQ2Ny0zLjg4NS05LjQ2Ny0zLjg4NWwxLjY2NS01LjU1czMuMjggMi44MyA2LjA3MyAyLjgzYTIuNDcgMi40NyAwIDAwMi41NjQtMi40OWMwLTQuMzQtNy4xLTQuNTI3LTcuMS0xMS42MTggMC01Ljk2MiA0LjI5OC0xMS43NyAxMi45MzQtMTEuNzcgMy4zOTQuMDUgNS4wMTggMSA1LjAxOCAxeiIvPjwvZz48L3N5bWJvbD48L3N2Zz4=" + }, + "categories": [ + { + "id": 2, + "name": "Sales" + } + ], + "displayName": "Shopify Trigger", + "typeVersion": 1 + }, + { + "id": 325, + "icon": "file:x.svg", + "name": "n8n-nodes-base.twitter", + "defaults": { + "name": "X" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjQgMjQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTE4LjI0NCAyLjI1aDMuMzA4bC03LjIyNyA4LjI2IDguNTAyIDExLjI0SDE2LjE3bC01LjIxNC02LjgxN0w0Ljk5IDIxLjc1SDEuNjhsNy43My04LjgzNUwxLjI1NCAyLjI1SDguMDhsNC43MTMgNi4yMzF6bS0xLjE2MSAxNy41MmgxLjgzM0w3LjA4NCA0LjEyNkg1LjExN3oiPjwvcGF0aD48L3N2Zz4K" + }, + "categories": [ + { + "id": 1, + "name": "Marketing & Content" + } + ], + "displayName": "X (Formerly Twitter)", + "typeVersion": 2 + } + ], + "categories": [ + { + "id": 2, + "name": "Sales" + }, + { + "id": 19, + "name": "Marketing & Growth" + } + ], + "image": [ + { + "id": 527, + "url": "https://n8niostorageaccount.blob.core.windows.net/n8nio-strapi-blobs-prod/assets/89a078b208fe4c6181902608b1cd1332.png" + } + ] + } +} diff --git a/cypress/fixtures/Test_Template_2.json b/cypress/fixtures/Test_Template_2.json new file mode 100644 index 0000000000..29001a78e3 --- /dev/null +++ b/cypress/fixtures/Test_Template_2.json @@ -0,0 +1,182 @@ +{ + "workflow": { + "id": 1344, + "name": "Save email attachments to Nextcloud", + "views": 650, + "recentViews": 9887, + "totalViews": 650, + "createdAt": "2021-11-29T13:59:16.771Z", + "description": "This workflow will take all emails you put into a certain folder, upload any attachements to Nextcloud, and mark the emails as read (configurable).\n\nAttachements will be saved with automatically generated filenames:\n`2021-01-01_From-Sender-Name_Filename-of-attachement.pdf`\n\nInstructions:\n1. **Allow lodash to be used in n8n** (or rewrite the code...)\n `NODE_FUNCTION_ALLOW_EXTERNAL=lodash` (environment variable)\n2. Import workflow\n3. Set credentials for Email & Nextcloud nodes\n4. Configure to use correct folder / custom filters\n5. Activate\n\nCustom filter examples:\n- Only unread emails:\n `Custom Email Config` = `[\"UNSEEN\"]`\n- Filter emails by 'to' address:\n `Custom Email Config` = `[[\"TO\", \"example+invoices@posteo.de\"]]`", + "workflow": { + "nodes": [ + { + "name": "IMAP Email", + "type": "n8n-nodes-base.emailReadImap", + "position": [ + 240, + 420 + ], + "parameters": { + "format": "resolved", + "mailbox": "Invoices", + "options": { + "customEmailConfig": "[\"ALL\"]" + } + }, + "typeVersion": 1 + }, + { + "name": "Nextcloud", + "type": "n8n-nodes-base.nextCloud", + "position": [ + 940, + 420 + ], + "parameters": { + "path": "=Documents/Invoices/{{$json[\"date\"]}}_{{$json[\"from\"]}}_{{$binary.file.fileName}}", + "binaryDataUpload": true, + "binaryPropertyName": "file" + }, + "typeVersion": 1 + }, + { + "name": "Map each attachment", + "type": "n8n-nodes-base.function", + "position": [ + 620, + 420 + ], + "parameters": { + "functionCode": "const _ = require('lodash')\n\nconst sanitize = str => _.chain(str)\n .replace(/[^A-Za-z0-9&.-]/g, '-') // sanitise via whitelist of characters\n .replace(/-(?=-)/g, '') // remove repeated dashes - https://regexr.com/6ag8h\n .trim('-') // trim any leading/trailing dashes\n .truncate({\n length: 60,\n omission: '-' // when the string ends with '-', you'll know it was truncated\n })\n .value()\n\nconst result = _.flatMap(items.map(item => {\n //console.log({item})\n\n // Maps each attachment to a separate item\n return _.values(item.binary).map(file => {\n console.log(\"Saving attachement:\", file.fileName, 'from:', ...item.json.from.value)\n \n // sanitize filename but exclude extension\n const filename_parts = file.fileName.split('.')\n const ext = _.slice(filename_parts, filename_parts.length-1)\n const filename_main = _.join(_.dropRight(filename_parts), '.')\n file.fileName = sanitize(filename_main) + '.' + ext\n \n return {\n json: {\n from: sanitize(item.json.from.value[0].name),\n date: sanitize(new Date(item.json.date).toISOString().split(\"T\")[0]) // get date part \"2020-01-01\"\n }, \n binary: { file }\n }\n })\n}))\n\n//console.log(result)\nreturn result" + }, + "typeVersion": 1 + } + ], + "connections": { + "IMAP Email": { + "main": [ + [ + { + "node": "Map each attachment", + "type": "main", + "index": 0 + } + ] + ] + }, + "Map each attachment": { + "main": [ + [ + { + "node": "Nextcloud", + "type": "main", + "index": 0 + } + ] + ] + } + } + }, + "lastUpdatedBy": 11, + "workflowInfo": { + "nodeCount": 3, + "nodeTypes": { + "n8n-nodes-base.function": { + "count": 1 + }, + "n8n-nodes-base.nextCloud": { + "count": 1 + }, + "n8n-nodes-base.emailReadImap": { + "count": 1 + } + } + }, + "user": { + "username": "tennox" + }, + "nodes": [ + { + "id": 10, + "icon": "fa:inbox", + "name": "n8n-nodes-base.emailReadImap", + "defaults": { + "name": "Email Trigger (IMAP)", + "color": "#44AA22" + }, + "iconData": { + "icon": "inbox", + "type": "icon" + }, + "categories": [ + { + "id": 6, + "name": "Communication" + }, + { + "id": 9, + "name": "Core Nodes" + } + ], + "displayName": "Email Trigger (IMAP)", + "typeVersion": 2 + }, + { + "id": 14, + "icon": "fa:code", + "name": "n8n-nodes-base.function", + "defaults": { + "name": "Function", + "color": "#FF9922" + }, + "iconData": { + "icon": "code", + "type": "icon" + }, + "categories": [ + { + "id": 5, + "name": "Development" + }, + { + "id": 9, + "name": "Core Nodes" + } + ], + "displayName": "Function", + "typeVersion": 1 + }, + { + "id": 25, + "icon": "file:nextcloud.svg", + "name": "n8n-nodes-base.nextCloud", + "defaults": { + "name": "Nextcloud" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgNzYgNTEiIGZpbGw9IiNmZmYiIGZpbGwtcnVsZT0iZXZlbm9kZCIgc3Ryb2tlPSIjMDAwIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjx1c2UgeGxpbms6aHJlZj0iI0EiIHg9Ii41IiB5PSIuNSIvPjxzeW1ib2wgaWQ9IkEiIG92ZXJmbG93PSJ2aXNpYmxlIj48cGF0aCBkPSJNMzcuNTMzIDBjLTcuNzcgMC0xNC4zNTUgNS4yNjgtMTYuMzk2IDEyLjM3OS0xLjc3OC0zLjgxOS01LjU5Ny02LjQ1My0xMC4wNzUtNi40NTNDNS4wMDQgNS45MjYgMCAxMC45MzEgMCAxNy4wNTRhMTEuMTYgMTEuMTYgMCAwIDAgMTEuMTI4IDExLjEyOGM0LjQxMiAwIDguMjk3LTIuNjM0IDEwLjA3NS02LjQ1M2ExNi45OSAxNi45OSAwIDAgMCAxNi4zMyAxMi4zNzljNy43MDQgMCAxNC4yODktNS4yMDIgMTYuMzk2LTEyLjI0OCAxLjc3OCAzLjY4NyA1LjU5NyA2LjI1NiA5Ljk0MyA2LjI1NkExMS4xNiAxMS4xNiAwIDAgMCA3NSAxNi45ODljMC02LjEyNC01LjAwNC0xMS4wNjItMTEuMTI4LTExLjA2Mi00LjM0NiAwLTguMTY1IDIuNTY4LTkuOTQzIDYuMjU2QzUxLjgyMiA1LjIwMiA0NS4zMDMgMCAzNy41MzMgMHptMCA2LjUxOWExMC40OCAxMC40OCAwIDAgMSAxMC41MzUgMTAuNTM2QTEwLjQ4IDEwLjQ4IDAgMCAxIDM3LjUzMyAyNy41OWExMC40OCAxMC40OCAwIDAgMS0xMC41MzYtMTAuNTM1QTEwLjQ4IDEwLjQ4IDAgMCAxIDM3LjUzMyA2LjUxOXptLTI2LjQwNSA1LjkyNmE0LjU4IDQuNTggMCAwIDEgNC42MDkgNC42MDkgNC41OCA0LjU4IDAgMCAxLTQuNjA5IDQuNjA5IDQuNTggNC41OCAwIDAgMS00LjYwOS00LjYwOSA0LjU4IDQuNTggMCAwIDEgNC42MDktNC42MDl6bTUyLjc0NCAwYTQuNTggNC41OCAwIDAgMSA0LjYwOSA0LjYwOSA0LjYwOSA0LjYwOSAwIDEgMS05LjIxOCAwYy4wNjYtMi41NjggMi4wNDEtNC42MDkgNC42MDktNC42MDl6TTE5LjE3NiA0MS45NTdjMS44MjcgMCAyLjg1IDEuMzAxIDIuODUgMy4yNTIgMCAuMTg2LS4xNTUuMzQxLS4zNDEuMzQxSDE2Ljc2Yy4wMzEgMS43MzQgMS4yMzkgMi43MjYgMi42MzMgMi43MjZhMi44OSAyLjg5IDAgMCAwIDEuNzk2LS42MTljLjE4Ni0uMTI0LjM0MS0uMDkzLjQzNC4wOTNsLjA5My4xNTVjLjA5My4xNTUuMDYyLjMxLS4wOTMuNDM0YTMuODQgMy44NCAwIDAgMS0yLjI2MS43NDNjLTIuMDEzIDAtMy41NjItMS40NTYtMy41NjItMy41NjIuMDMxLTIuMjMgMS41MTgtMy41NjIgMy4zNzYtMy41NjJ6bTEuODg5IDIuOTExYy0uMDYyLTEuNDI1LS45MjktMi4xMzctMS45Mi0yLjEzNy0xLjE0NiAwLTIuMTM3Ljc0My0yLjM1NCAyLjEzN2g0LjI3NHptMTAuMjUzLTEuOTJ2LS43NzQtMS42MTFjMC0uMjE3LjEyNC0uMzQxLjM0MS0uMzQxaC4yNDhjLjIxNyAwIC4zMS4xMjQuMzEuMzQxdjEuNjExaDEuMzk0Yy4yMTcgMCAuMzQxLjEyNC4zNDEuMzQxdi4wOTNjMCAuMjE3LS4xMjQuMzEtLjM0MS4zMWgtMS4zOTR2My40MDdjMCAxLjU4Ljk2IDEuNzY2IDEuNDg3IDEuNzk2LjI3OS4wMzEuMzcyLjA5My4zNzIuMzQxdi4xODZjMCAuMjE3LS4wOTMuMzEtLjM3Mi4zMS0xLjQ4NyAwLTIuMzg1LS44OTgtMi4zODUtMi41MDl2LTMuNXptNy4wOTMtLjk5MWMxLjE3NyAwIDEuOTIuNDk2IDIuMjYxLjc3NC4xNTUuMTI0LjE4Ni4yNzkuMDMxLjQ2NWwtLjA5My4xNTVjLS4xMjQuMTg2LS4yNzkuMTg2LS40NjUuMDYyLS4zMS0uMjE3LS44OTgtLjYxOS0xLjcwMy0uNjE5LTEuNDg3IDAtMi42NjQgMS4xMTUtMi42NjQgMi43NTcgMCAxLjYxMSAxLjE3NyAyLjcyNiAyLjY2NCAyLjcyNi45NiAwIDEuNjExLS40MzQgMS45Mi0uNzEyLjE4Ni0uMTI0LjMxLS4wOTMuNDM0LjA5M2wuMDkzLjEyNGMuMDkzLjE4Ni4wNjIuMzEtLjA5My40NjVhMy44MSAzLjgxIDAgMCAxLTIuNDE2Ljg2N2MtMi4wMTMgMC0zLjU2Mi0xLjQ1Ni0zLjU2Mi0zLjU2Mi4wMzEtMi4xMDYgMS41OC0zLjU5MyAzLjU5My0zLjU5M3ptNC4xMTktMi4xOTljMC0uMjE3LS4xMjQtLjM0MS4wOTMtLjM0MWguMjQ4Yy4yMTcgMCAuNTU4LjEyNC41NTguMzQxdjcuNDAzYzAgLjg2Ny40MDMuOTYuNzEyLjk5MS4xNTUgMCAuMjc5LjA5My4yNzkuMzF2LjIxN2MwIC4yMTctLjA5My4zNDEtLjM0MS4zNDEtLjU1NyAwLTEuNTQ5LS4xODYtMS41NDktMS42NzN2LTcuNTg5em02LjM1IDIuMTk5YzEuOTgyIDAgMy41OTMgMS41MTggMy41OTMgMy41MzEgMCAyLjA0NC0xLjYxMSAzLjU5My0zLjU5MyAzLjU5M3MtMy41OTMtMS41NDktMy41OTMtMy41OTNjMC0yLjAxMyAxLjYxMS0zLjUzMSAzLjU5My0zLjUzMXptMCA2LjMxOWMxLjQ1NiAwIDIuNjMzLTEuMTc3IDIuNjMzLTIuNzg4IDAtMS41NDktMS4xNzctMi42OTUtMi42MzMtMi42OTVhMi42NyAyLjY3IDAgMCAwLTIuNjY0IDIuNjk1Yy4wMzEgMS41OCAxLjIwOCAyLjc4OCAyLjY2NCAyLjc4OHptMTUuNDU2LTYuMzE5YTIuNDUgMi40NSAwIDAgMSAyLjIzIDEuMzYzaC4wMzFzLS4wMzEtLjIxNy0uMDMxLS41MjZ2LTMuMDY2YzAtLjIxNy0uMDkzLS4zNDEuMTI0LS4zNDFoLjI0OGMuMjE3IDAgLjU1OC4xMjQuNTU4LjM0MXY4LjgyN2MwIC4yMTctLjA5My4zNDEtLjMxLjM0MWgtLjIxN2MtLjIxNyAwLS4zNDEtLjA5My0uMzQxLS4zMXYtLjUyN2MwLS4yNDguMDYyLS40MzQuMDYyLS40MzRoLS4wMzFzLS41ODkgMS40MjUtMi4zNTQgMS40MjVjLTEuODI3IDAtMi45NzMtMS40NTYtMi45NzMtMy41NjItLjA2Mi0yLjEwNiAxLjIwOC0zLjUzMSAzLjAwNC0zLjUzMWgwem0uMDMxIDYuMzE5YzEuMTQ2IDAgMi4xOTktLjgwNSAyLjE5OS0yLjc1NyAwLTEuMzk0LS43MTItMi43MjYtMi4xNjgtMi43MjYtMS4yMDggMC0yLjE5OS45OTEtMi4xOTkgMi43MjYuMDMxIDEuNjczLjg5OCAyLjc1NyAyLjE2OCAyLjc1N3ptLTU2LjU1OC42NWguMjQ4Yy4yMTcgMCAuMzQxLS4xMjQuMzQxLS4zNDF2LTYuNjI4YzAtMS4wNTMgMS4xNDYtMS43OTYgMi40NDctMS43OTZzMi40NDcuNzQzIDIuNDQ3IDEuNzk2djYuNjU5YzAgLjIxNy4xMjQuMzQxLjM0MS4zNDFoLjI0OGMuMjE3IDAgLjMxLS4xMjQuMzEtLjM0MXYtNi43MjFjMC0xLjc2Ni0xLjc2NS0yLjYzMy0zLjM3Ni0yLjYzM2gwIDAgMCAwYy0xLjU0OSAwLTMuMzE0Ljg2Ny0zLjMxNCAyLjYzM3Y2LjY5YzAgLjIxNy4wOTMuMzQxLjMxLjM0MXptNTEuNjk1LTYuODE0aC0uMjQ4Yy0uMjE3IDAtLjM0MS4xMjQtLjM0MS4zNDF2My43NDhjMCAxLjA1My0uNjgxIDIuMDEzLTIuMDEzIDIuMDEzLTEuMzAxIDAtMi4wMTMtLjk2LTIuMDEzLTIuMDEzdi0zLjc0OGMwLS4yMTctLjEyNC0uMzQxLS4zNDEtLjM0MUg1NC4zYy0uMjE3IDAtLjMxLjEyNC0uMzEuMzQxdjMuOTk2YzAgMS43NjUgMS4zMDEgMi42MzMgMi45MTIgMi42MzNoMCAwIDAgMGMxLjYxMSAwIDIuOTExLS44NjcgMi45MTEtMi42MzN2LTMuOTk2Yy4wMzEtLjIxNy0uMDkzLS4zNDEtLjMxLS4zNDFoMHptLTMwLjY2NC0uMDMxYy0uMDYyIDAtLjE1NS4wNjItLjIxNy4xNTVsLTEuMjM5IDEuNDg3LS45MjkgMS4xMTUtMS40MjUtMS43MDQtLjc3NC0uOTI5Yy0uMDYyLS4wOTMtLjE1NS0uMTI0LS4yMTctLjEyNHMtLjE1NS4wMzEtLjI0OC4wOTNsLS4xODYuMTU1Yy0uMTU1LjEyNC0uMTU1LjI3OS0uMDMxLjQ2NWwxLjIzOSAxLjQ4NyAxLjA1MyAxLjIzOS0xLjUxOCAxLjgyN2gwbC0uNzc0LjkyOWMtLjEyNC4xNTUtLjEyNC4zNDEuMDMxLjQ5NmwuMTg2LjE1NWMuMTU1LjEyNC4zMS4wOTMuNDY1LS4wNjJsMS4yMzktMS40ODcuOTI5LTEuMTE1IDEuNDI1IDEuNzA0aDBsLjc3NC45MjljLjEyNC4xNTUuMzEuMTg2LjQ2NS4wMzFsLjE4Ni0uMTU1Yy4xNTUtLjEyNC4xNTUtLjI3OS4wMzEtLjQ2NWwtMS4yMzktMS40ODctMS4wNTMtMS4yMzkgMS41MTgtMS44MjdoMGwuNzc0LS45MjljLjEyNC0uMTU1LjEyNC0uMzQxLS4wMzEtLjQ5NWwtLjE4Ni0uMTg2Yy0uMDkzLS4wNjItLjE1NS0uMDkzLS4yNDgtLjA2MmgweiIgZmlsbD0iIzAwODJjOSIgZmlsbC1ydWxlPSJub256ZXJvIiBzdHJva2U9Im5vbmUiLz48L3N5bWJvbD48L3N2Zz4=" + }, + "categories": [ + { + "id": 3, + "name": "Data & Storage" + } + ], + "displayName": "Nextcloud", + "typeVersion": 1 + } + ], + "categories": [ + { + "id": 2, + "name": "Sales" + }, + { + "id": 8, + "name": "Finance & Accounting" + } + ], + "image": [] + } +} diff --git a/cypress/fixtures/Test_workflow_5.json b/cypress/fixtures/Test_workflow_5.json index 6b87fc33b7..b94fcd283a 100644 --- a/cypress/fixtures/Test_workflow_5.json +++ b/cypress/fixtures/Test_workflow_5.json @@ -40,7 +40,7 @@ { "parameters": {}, "id": "ef63cdc5-50bc-4525-9873-7e7f7589a60e", - "name": "When clicking \"Execute Workflow\"", + "name": "When clicking \"Test Workflow\"", "type": "n8n-nodes-base.manualTrigger", "typeVersion": 1, "position": [ @@ -50,7 +50,6 @@ }, { "parameters": { - "operation": "sort", "sortFieldsUi": { "sortField": [ { @@ -61,9 +60,9 @@ "options": {} }, "id": "555a150c-d735-4331-b628-c1f1cfed2da1", - "name": "Item Lists", - "type": "n8n-nodes-base.itemLists", - "typeVersion": 2, + "name": "Sort", + "type": "n8n-nodes-base.sort", + "typeVersion": 1, "position": [ -280, 580 @@ -182,7 +181,7 @@ "main": [ [ { - "node": "Item Lists", + "node": "Sort", "type": "main", "index": 0 } @@ -200,7 +199,7 @@ ] ] }, - "When clicking \"Execute Workflow\"": { + "When clicking \"Test Workflow\"": { "main": [ [ { @@ -216,7 +215,7 @@ ] ] }, - "Item Lists": { + "Sort": { "main": [ [ { @@ -289,4 +288,4 @@ ] } } -} \ No newline at end of file +} diff --git a/cypress/fixtures/Test_workflow_filter.json b/cypress/fixtures/Test_workflow_filter.json new file mode 100644 index 0000000000..e30a3504ca --- /dev/null +++ b/cypress/fixtures/Test_workflow_filter.json @@ -0,0 +1,153 @@ +{ + "name": "Filter test", + "nodes": [ + { + "parameters": {}, + "id": "f332a7d1-31b4-4e78-b31e-9e8db945bf3f", + "name": "When clicking \"Test Workflow\"", + "type": "n8n-nodes-base.manualTrigger", + "typeVersion": 1, + "position": [ + -60, + 480 + ] + }, + { + "parameters": { + "jsCode": "return [\n {\n \"label\": \"Apple\",\n tags: [],\n meta: {foo: 'bar'}\n },\n {\n \"label\": \"Banana\",\n tags: ['exotic'],\n meta: {}\n },\n {\n \"label\": \"Pear\",\n tags: ['other'],\n meta: {}\n },\n {\n \"label\": \"Orange\",\n meta: {}\n }\n]" + }, + "id": "60697c7f-3948-4790-97ba-8aba03d02ac2", + "name": "Code", + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 160, + 480 + ] + }, + { + "parameters": { + "conditions": { + "options": { + "caseSensitive": true, + "leftValue": "" + }, + "conditions": [ + { + "leftValue": "={{ $json.tags }}", + "rightValue": "exotic", + "operator": { + "type": "array", + "operation": "contains", + "rightType": "any" + } + }, + { + "leftValue": "={{ $json.meta }}", + "rightValue": "", + "operator": { + "type": "object", + "operation": "notEmpty", + "singleValue": true + } + }, + { + "leftValue": "={{ $json.label }}", + "rightValue": "Pea", + "operator": { + "type": "string", + "operation": "startsWith", + "rightType": "string" + } + } + ], + "combinator": "or" + }, + "options": {} + }, + "id": "7531191b-5ac3-45dc-8afb-27ae83d8f33a", + "name": "If", + "type": "n8n-nodes-base.if", + "typeVersion": 2, + "position": [ + 380, + 480 + ] + }, + { + "parameters": {}, + "id": "d8c614ea-0bbf-4b12-ad7d-c9ebe09ce583", + "name": "Then", + "type": "n8n-nodes-base.noOp", + "typeVersion": 1, + "position": [ + 600, + 400 + ] + }, + { + "parameters": {}, + "id": "69364770-60d2-4ef4-9f29-9570718a9a10", + "name": "Else", + "type": "n8n-nodes-base.noOp", + "typeVersion": 1, + "position": [ + 600, + 580 + ] + } + ], + "pinData": {}, + "connections": { + "When clicking \"Test Workflow\"": { + "main": [ + [ + { + "node": "Code", + "type": "main", + "index": 0 + } + ] + ] + }, + "Code": { + "main": [ + [ + { + "node": "If", + "type": "main", + "index": 0 + } + ] + ] + }, + "If": { + "main": [ + [ + { + "node": "Then", + "type": "main", + "index": 0 + } + ], + [ + { + "node": "Else", + "type": "main", + "index": 0 + } + ] + ] + } + }, + "active": false, + "settings": { + "executionOrder": "v1" + }, + "versionId": "a6249f48-d88f-4b80-9ed9-79555e522d48", + "id": "BWUTRs5RHxVgQ4uT", + "meta": { + "instanceId": "78577815012af39cf16dad7a787b0898c42fb7514b8a7f99b2136862c2af502c" + }, + "tags": [] +} diff --git a/cypress/fixtures/Test_workflow_form_switch.json b/cypress/fixtures/Test_workflow_form_switch.json new file mode 100644 index 0000000000..78349c3ae5 --- /dev/null +++ b/cypress/fixtures/Test_workflow_form_switch.json @@ -0,0 +1,78 @@ +{ + "name": "My workflow 8", + "nodes": [ + { + "parameters": { + "path": "d1cba915-ca18-4425-bcfb-133205fc815a", + "formTitle": "test", + "formFields": { + "values": [ + { + "fieldLabel": "test" + } + ] + }, + "options": {} + }, + "id": "9e685367-fb94-4376-a9a4-7f311d9f7e2d", + "name": "n8n Form Trigger", + "type": "n8n-nodes-base.formTrigger", + "typeVersion": 2, + "position": [ + 620, + 580 + ], + "webhookId": "d1cba915-ca18-4425-bcfb-133205fc815a" + }, + { + "parameters": {}, + "id": "0f4dfe66-51c0-4378-9eab-680f8140a572", + "name": "Switch", + "type": "n8n-nodes-base.switch", + "typeVersion": 2, + "position": [ + 800, + 580 + ] + } + ], + "pinData": { + "n8n Form Trigger": [ + { + "json": { + "name": "First item", + "code": 1 + } + }, + { + "json": { + "name": "Second item", + "code": 2 + } + } + ] + }, + "connections": { + "n8n Form Trigger": { + "main": [ + [ + { + "node": "Switch", + "type": "main", + "index": 0 + } + ] + ] + } + }, + "active": false, + "settings": { + "executionOrder": "v1" + }, + "versionId": "d6c14bc8-a69f-47bb-b5ba-fe6e9db0a3a4", + "id": "UQSimcMQJGbTeTLG", + "meta": { + "instanceId": "a786b722078489c1fa382391a9f3476c2784761624deb2dfb4634827256d51a0" + }, + "tags": [] +} \ No newline at end of file diff --git a/cypress/fixtures/Test_workflow_ndv_version.json b/cypress/fixtures/Test_workflow_ndv_version.json new file mode 100644 index 0000000000..871a526e3a --- /dev/null +++ b/cypress/fixtures/Test_workflow_ndv_version.json @@ -0,0 +1,49 @@ +{ + "name": "Node versions", + "nodes": [ + { + "parameters": {}, + "id": "aadaed66-84ed-4cf8-bf21-082e9a65db76", + "name": "When clicking \"Test Workflow\"", + "type": "n8n-nodes-base.manualTrigger", + "typeVersion": 1, + "position": [ + 1540, + 780 + ] + }, + { + "parameters": {}, + "id": "93d73a85-82f0-4380-a032-713d5dc82b32", + "name": "Function", + "type": "n8n-nodes-base.function", + "typeVersion": 1, + "position": [ + 2040, + 780 + ] + }, + { + "id": "50f322d9-c622-4dd0-8d38-e851502739dd", + "name": "Edit Fields (old)", + "type": "n8n-nodes-base.set", + "typeVersion": 2, + "position": [ + 1880, + 780 + ] + }, + { + "id": "93aaadac-55fe-4618-b1eb-f63e61d1446a", + "name": "Edit Fields (latest)", + "type": "n8n-nodes-base.set", + "typeVersion": 3.2, + "position": [ + 1720, + 780 + ] + } + ], + "pinData": {}, + "connections": {} + } diff --git a/cypress/fixtures/Test_workflow_schema_test.json b/cypress/fixtures/Test_workflow_schema_test.json index 0db43a5ea4..f615316c73 100644 --- a/cypress/fixtures/Test_workflow_schema_test.json +++ b/cypress/fixtures/Test_workflow_schema_test.json @@ -47,7 +47,7 @@ { "parameters": {}, "id": "58512a93-dabf-4584-817f-27c608c1bdd5", - "name": "When clicking \"Execute Workflow\"", + "name": "When clicking \"Test Workflow\"", "type": "n8n-nodes-base.manualTrigger", "typeVersion": 1, "position": [ @@ -69,7 +69,7 @@ ] ] }, - "When clicking \"Execute Workflow\"": { + "When clicking \"Test Workflow\"": { "main": [ [ { diff --git a/cypress/fixtures/Test_workflow_schema_test_pinned_data.json b/cypress/fixtures/Test_workflow_schema_test_pinned_data.json index 5233a17848..867563ddfd 100644 --- a/cypress/fixtures/Test_workflow_schema_test_pinned_data.json +++ b/cypress/fixtures/Test_workflow_schema_test_pinned_data.json @@ -47,7 +47,7 @@ { "parameters": {}, "id": "3dc7cf26-ff25-4437-b9fd-0e8b127ebec9", - "name": "When clicking \"Execute Workflow\"", + "name": "When clicking \"Test Workflow\"", "type": "n8n-nodes-base.manualTrigger", "typeVersion": 1, "position": [ @@ -552,7 +552,7 @@ ] ] }, - "When clicking \"Execute Workflow\"": { + "When clicking \"Test Workflow\"": { "main": [ [ { diff --git a/cypress/fixtures/Test_workflow_webhook_with_pin_data.json b/cypress/fixtures/Test_workflow_webhook_with_pin_data.json new file mode 100644 index 0000000000..d1bfb17133 --- /dev/null +++ b/cypress/fixtures/Test_workflow_webhook_with_pin_data.json @@ -0,0 +1,151 @@ +{ + "name": "PinData Test", + "nodes": [ + { + "parameters": {}, + "id": "0a60e507-7f34-41c0-a0f9-697d852033b6", + "name": "When clicking \"Test Workflow\"", + "type": "n8n-nodes-base.manualTrigger", + "typeVersion": 1, + "position": [ + 780, + 320 + ] + }, + { + "parameters": { + "path": "b0d79ddb-df2d-49b1-8555-9fa2b482608f", + "responseMode": "lastNode", + "options": {} + }, + "id": "66425ce3-450d-4aa6-a53b-a701ab89c2de", + "name": "Webhook", + "type": "n8n-nodes-base.webhook", + "typeVersion": 1.1, + "position": [ + 780, + 540 + ], + "webhookId": "b0d79ddb-df2d-49b1-8555-9fa2b482608f" + }, + { + "parameters": { + "fields": { + "values": [ + { + "name": "nodeData", + "stringValue": "init" + } + ] + }, + "include": "none", + "options": {} + }, + "id": "3211b3c5-49e9-4694-8f86-7a5783bc653a", + "name": "Init Data", + "type": "n8n-nodes-base.set", + "typeVersion": 3.2, + "position": [ + 1000, + 320 + ] + }, + { + "parameters": { + "fields": { + "values": [ + { + "name": "nodeData", + "stringValue": "pin" + } + ] + }, + "options": {} + }, + "id": "97b31120-4720-4632-9d35-356f345119f7", + "name": "Pin Data", + "type": "n8n-nodes-base.set", + "typeVersion": 3.2, + "position": [ + 1240, + 320 + ] + }, + { + "parameters": {}, + "id": "1ee7be4f-7006-43bf-bb0c-29db3058a399", + "name": "End", + "type": "n8n-nodes-base.noOp", + "typeVersion": 1, + "position": [ + 1460, + 320 + ] + } + ], + "pinData": { + "Pin Data": [ + { + "json": { + "nodeData": "pin-overwritten" + } + } + ] + }, + "connections": { + "When clicking \"Test Workflow\"": { + "main": [ + [ + { + "node": "Init Data", + "type": "main", + "index": 0 + } + ] + ] + }, + "Webhook": { + "main": [ + [ + { + "node": "Init Data", + "type": "main", + "index": 0 + } + ] + ] + }, + "Init Data": { + "main": [ + [ + { + "node": "Pin Data", + "type": "main", + "index": 0 + } + ] + ] + }, + "Pin Data": { + "main": [ + [ + { + "node": "End", + "type": "main", + "index": 0 + } + ] + ] + } + }, + "active": false, + "settings": { + "executionOrder": "v1" + }, + "versionId": "ded8577a-3ed2-4611-842c-a7922ec58b98", + "id": "weofVLZo0ssmPDrV", + "meta": { + "instanceId": "021d3c82ba2d3bc090cbf4fc81c9312668bcc34297e022bb3438c5c88a43a5ff" + }, + "tags": [] + } diff --git a/cypress/fixtures/Test_workflow_xml_output.json b/cypress/fixtures/Test_workflow_xml_output.json new file mode 100644 index 0000000000..03d09a1735 --- /dev/null +++ b/cypress/fixtures/Test_workflow_xml_output.json @@ -0,0 +1,53 @@ +{ + "meta": { + "instanceId": "2d1cf27f75b18bb9e146336f791c37884f4fc7ddb97c2def27c0444d106778bf" + }, + "nodes": [ + { + "parameters": {}, + "id": "8108d313-8b03-4aa4-963d-cd1c0fe8f85c", + "name": "When clicking \"Test Workflow\"", + "type": "n8n-nodes-base.manualTrigger", + "typeVersion": 1, + "position": [ + 420, + 220 + ] + }, + { + "parameters": { + "fields": { + "values": [ + { + "name": "body", + "stringValue": " Introduction to XML John Doe 2020 1234567890 Data Science Basics Jane Smith 2019 0987654321 Programming in Python Bob Johnson 2021 5432109876 " + } + ] + }, + "options": {} + }, + "id": "45888152-7c5f-4d88-9039-660c594da084", + "name": "Edit Fields", + "type": "n8n-nodes-base.set", + "typeVersion": 3.2, + "position": [ + 640, + 220 + ] + } + ], + "connections": { + "When clicking \"Test Workflow\"": { + "main": [ + [ + { + "node": "Edit Fields", + "type": "main", + "index": 0 + } + ] + ] + } + }, + "pinData": {} + } diff --git a/cypress/fixtures/Workflow_template_write_http_query.json b/cypress/fixtures/Workflow_template_write_http_query.json new file mode 100644 index 0000000000..a0a3eba649 --- /dev/null +++ b/cypress/fixtures/Workflow_template_write_http_query.json @@ -0,0 +1,188 @@ +{ + "workflow": { + "id": 3, + "name": "Write HTTP query string on image", + "views": 116, + "recentViews": 9766, + "totalViews": 116, + "createdAt": "2019-08-31T00:13:41.893Z", + "description": "1. Receives data from an incoming HTTP Request\n1. Reads file from internet\n ![workflow-screenshot](fileId:14) \n1. Writes data on image\n1. Returns the data\n\nThe URL to call will look like this:\nhttp://localhost:5678/webhook-test/webhook/test?name=Jim\n\nOnce called it will return an image like this:\n\n![reponseimage.jpeg](fileId:4)", + "workflow": { + "nodes": [ + { + "name": "Webhook", + "type": "n8n-nodes-base.webhook", + "position": [ + 700, + 350 + ], + "parameters": { + "path": "test", + "responseData": "firstEntryBinary", + "responseMode": "lastNode" + }, + "typeVersion": 1 + }, + { + "name": "Edit Image", + "type": "n8n-nodes-base.editImage", + "position": [ + 1100, + 350 + ], + "parameters": { + "text": "=They found the killer it was {{$node[\"Webhook\"].data[\"query\"][\"name\"]}}!", + "fontSize": "=25", + "operation": "text", + "positionX": 150, + "positionY": 180, + "lineLength": 18 + }, + "typeVersion": 1 + }, + { + "name": "Read File URL", + "type": "n8n-nodes-base.httpRequest", + "position": [ + 900, + 350 + ], + "parameters": { + "url": "https://www.needpix.com/file_download.php?url=//storage.needpix.com/thumbs/newspaper-412809_1280.jpg", + "responseFormat": "file" + }, + "typeVersion": 1 + } + ], + "connections": { + "Webhook": { + "main": [ + [ + { + "node": "Read File URL", + "type": "main", + "index": 0 + } + ] + ] + }, + "Read File URL": { + "main": [ + [ + { + "node": "Edit Image", + "type": "main", + "index": 0 + } + ] + ] + } + } + }, + "lastUpdatedBy": 11, + "workflowInfo": { + "nodeCount": 3, + "nodeTypes": { + "n8n-nodes-base.webhook": { + "count": 1 + }, + "n8n-nodes-base.editImage": { + "count": 1 + }, + "n8n-nodes-base.httpRequest": { + "count": 1 + } + } + }, + "user": { + "username": "jan" + }, + "nodes": [ + { + "id": 9, + "icon": "fa:image", + "name": "n8n-nodes-base.editImage", + "defaults": { + "name": "Edit Image", + "color": "#553399" + }, + "iconData": { + "icon": "image", + "type": "icon" + }, + "categories": [ + { + "id": 9, + "name": "Core Nodes" + }, + { + "id": 27, + "name": "Marketing" + } + ], + "displayName": "Edit Image", + "typeVersion": 1 + }, + { + "id": 19, + "icon": "file:httprequest.svg", + "name": "n8n-nodes-base.httpRequest", + "defaults": { + "name": "HTTP Request", + "color": "#0004F5" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgNDAgNDAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CiAgICA8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTQwIDIwQzQwIDguOTUzMTQgMzEuMDQ2OSAwIDIwIDBDOC45NTMxNCAwIDAgOC45NTMxNCAwIDIwQzAgMzEuMDQ2OSA4Ljk1MzE0IDQwIDIwIDQwQzMxLjA0NjkgNDAgNDAgMzEuMDQ2OSA0MCAyMFpNMjAgMzYuOTQ1OEMxOC44ODUyIDM2Ljk0NTggMTcuMTM3OCAzNS45NjcgMTUuNDk5OCAzMi42OTg1QzE0Ljc5NjQgMzEuMjkxOCAxNC4xOTYxIDI5LjU0MzEgMTMuNzUyNiAyNy42ODQ3SDI2LjE4OThDMjUuODA0NSAyOS41NDAzIDI1LjIwNDQgMzEuMjkwMSAyNC41MDAyIDMyLjY5ODVDMjIuODYyMiAzNS45NjcgMjEuMTE0OCAzNi45NDU4IDIwIDM2Ljk0NThaTTEyLjkwNjQgMjBDMTIuOTA2NCAyMS42MDk3IDEzLjAwODcgMjMuMTY0IDEzLjIwMDMgMjQuNjMwNUgyNi43OTk3QzI2Ljk5MTMgMjMuMTY0IDI3LjA5MzYgMjEuNjA5NyAyNy4wOTM2IDIwQzI3LjA5MzYgMTguMzkwMyAyNi45OTEzIDE2LjgzNiAyNi43OTk3IDE1LjM2OTVIMTMuMjAwM0MxMy4wMDg3IDE2LjgzNiAxMi45MDY0IDE4LjM5MDMgMTIuOTA2NCAyMFpNMjAgMy4wNTQxOUMyMS4xMTQ5IDMuMDU0MTkgMjIuODYyMiA0LjAzMDc4IDI0LjUwMDEgNy4zMDAzOUMyNS4yMDY2IDguNzE0MDggMjUuODA3MiAxMC40MDY3IDI2LjE5MiAxMi4zMTUzSDEzLjc1MDFDMTQuMTkzMyAxMC40MDQ3IDE0Ljc5NDIgOC43MTI1NCAxNS40OTk4IDcuMzAwNjRDMTcuMTM3NyA0LjAzMDgzIDE4Ljg4NTEgMy4wNTQxOSAyMCAzLjA1NDE5Wk0zMC4xNDc4IDIwQzMwLjE0NzggMTguNDA5OSAzMC4wNTQzIDE2Ljg2MTcgMjkuODIyNyAxNS4zNjk1SDM2LjMwNDJDMzYuNzI1MiAxNi44NDIgMzYuOTQ1OCAxOC4zOTY0IDM2Ljk0NTggMjBDMzYuOTQ1OCAyMS42MDM2IDM2LjcyNTIgMjMuMTU4IDM2LjMwNDIgMjQuNjMwNUgyOS44MjI3QzMwLjA1NDMgMjMuMTM4MyAzMC4xNDc4IDIxLjU5MDEgMzAuMTQ3OCAyMFpNMjYuMjc2NyA0LjI1NTExQzI3LjYzNjUgNi4zNjAxOSAyOC43MTEgOS4xMzIgMjkuMzc3NCAxMi4zMTUzSDM1LjEwNDZDMzMuMjUxMSA4LjY2OCAzMC4xMDcgNS43ODM0NiAyNi4yNzY3IDQuMjU1MTFaTTEwLjYyMjYgMTIuMzE1M0g0Ljg5MjkzQzYuNzUxNDcgOC42Njc4NCA5Ljg5MzUxIDUuNzgzNDEgMTMuNzIzMiA0LjI1NTEzQzEyLjM2MzUgNi4zNjAyMSAxMS4yODkgOS4xMzIwMSAxMC42MjI2IDEyLjMxNTNaTTMuMDU0MTkgMjBDMy4wNTQxOSAyMS42MDMgMy4yNzc0MyAyMy4xNTc1IDMuNjk0ODQgMjQuNjMwNUgxMC4xMjE3QzkuOTQ2MTkgMjMuMTQyIDkuODUyMjIgMjEuNTk0MyA5Ljg1MjIyIDIwQzkuODUyMjIgMTguNDA1NyA5Ljk0NjE5IDE2Ljg1OCAxMC4xMjE3IDE1LjM2OTVIMy42OTQ4NEMzLjI3NzQzIDE2Ljg0MjUgMy4wNTQxOSAxOC4zOTcgMy4wNTQxOSAyMFpNMjYuMjc2NiAzNS43NDI3QzI3LjYzNjUgMzMuNjM5MyAyOC43MTEgMzAuODY4IDI5LjM3NzQgMjcuNjg0N0gzNS4xMDQ2QzMzLjI1MSAzMS4zMzIyIDMwLjEwNjggMzQuMjE3OSAyNi4yNzY2IDM1Ljc0MjdaTTEzLjcyMzQgMzUuNzQyN0M5Ljg5MzY5IDM0LjIxNzkgNi43NTE1NSAzMS4zMzI0IDQuODkyOTMgMjcuNjg0N0gxMC42MjI2QzExLjI4OSAzMC44NjggMTIuMzYzNSAzMy42MzkzIDEzLjcyMzQgMzUuNzQyN1oiIGZpbGw9IiMwMDA0RjUiLz4KPC9zdmc+Cg==" + }, + "categories": [ + { + "id": 5, + "name": "Development" + }, + { + "id": 9, + "name": "Core Nodes" + } + ], + "displayName": "HTTP Request", + "typeVersion": 3 + }, + { + "id": 47, + "icon": "file:webhook.svg", + "name": "n8n-nodes-base.webhook", + "defaults": { + "name": "Webhook" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciICB2aWV3Qm94PSIwIDAgNDggNDgiIHdpZHRoPSI0OHB4IiBoZWlnaHQ9IjQ4cHgiPjxwYXRoIGZpbGw9IiMzNzQ3NGYiIGQ9Ik0zNSwzN2MtMi4yLDAtNC0xLjgtNC00czEuOC00LDQtNHM0LDEuOCw0LDRTMzcuMiwzNywzNSwzN3oiLz48cGF0aCBmaWxsPSIjMzc0NzRmIiBkPSJNMzUsNDNjLTMsMC01LjktMS40LTcuOC0zLjdsMy4xLTIuNWMxLjEsMS40LDIuOSwyLjMsNC43LDIuM2MzLjMsMCw2LTIuNyw2LTZzLTIuNy02LTYtNiBjLTEsMC0yLDAuMy0yLjksMC43bC0xLjcsMUwyMy4zLDE2bDMuNS0xLjlsNS4zLDkuNGMxLTAuMywyLTAuNSwzLTAuNWM1LjUsMCwxMCw0LjUsMTAsMTBTNDAuNSw0MywzNSw0M3oiLz48cGF0aCBmaWxsPSIjMzc0NzRmIiBkPSJNMTQsNDNDOC41LDQzLDQsMzguNSw0LDMzYzAtNC42LDMuMS04LjUsNy41LTkuN2wxLDMuOUM5LjksMjcuOSw4LDMwLjMsOCwzM2MwLDMuMywyLjcsNiw2LDYgczYtMi43LDYtNnYtMmgxNXY0SDIzLjhDMjIuOSwzOS42LDE4LjgsNDMsMTQsNDN6Ii8+PHBhdGggZmlsbD0iI2U5MWU2MyIgZD0iTTE0LDM3Yy0yLjIsMC00LTEuOC00LTRzMS44LTQsNC00czQsMS44LDQsNFMxNi4yLDM3LDE0LDM3eiIvPjxwYXRoIGZpbGw9IiMzNzQ3NGYiIGQ9Ik0yNSwxOWMtMi4yLDAtNC0xLjgtNC00czEuOC00LDQtNHM0LDEuOCw0LDRTMjcuMiwxOSwyNSwxOXoiLz48cGF0aCBmaWxsPSIjZTkxZTYzIiBkPSJNMTUuNywzNEwxMi4zLDMybDUuOS05LjdjLTItMS45LTMuMi00LjUtMy4yLTcuM2MwLTUuNSw0LjUtMTAsMTAtMTBjNS41LDAsMTAsNC41LDEwLDEwIGMwLDAuOS0wLjEsMS43LTAuMywyLjVsLTMuOS0xYzAuMS0wLjUsMC4yLTEsMC4yLTEuNWMwLTMuMy0yLjctNi02LTZzLTYsMi43LTYsNmMwLDIuMSwxLjEsNCwyLjksNS4xbDEuNywxTDE1LjcsMzR6Ii8+PC9zdmc+Cg==" + }, + "categories": [ + { + "id": 5, + "name": "Development" + }, + { + "id": 9, + "name": "Core Nodes" + } + ], + "displayName": "Webhook", + "typeVersion": 1 + } + ], + "categories": [], + "image": [ + { + "id": 4, + "url": "data:image/jpeg;base64,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" + }, + { + "id": 14, + "url": "data:image/png;base64,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" + } + ] + } +} \ No newline at end of file diff --git a/cypress/fixtures/expression_with_paired_item_in_multi_input_node.json b/cypress/fixtures/expression_with_paired_item_in_multi_input_node.json index 569ac1fc13..8fb17e15b4 100644 --- a/cypress/fixtures/expression_with_paired_item_in_multi_input_node.json +++ b/cypress/fixtures/expression_with_paired_item_in_multi_input_node.json @@ -6,7 +6,7 @@ { "parameters": {}, "id": "bcb6abdf-d34b-4ea7-a8ed-58155b708c43", - "name": "When clicking \"Execute Workflow\"", + "name": "When clicking \"Test Workflow\"", "type": "n8n-nodes-base.manualTrigger", "typeVersion": 1, "position": [ @@ -90,7 +90,7 @@ } ], "connections": { - "When clicking \"Execute Workflow\"": { + "When clicking \"Test Workflow\"": { "main": [ [ { diff --git a/cypress/fixtures/templates_search/all_templates_search_response.json b/cypress/fixtures/templates_search/all_templates_search_response.json new file mode 100644 index 0000000000..5a0a1eb5ad --- /dev/null +++ b/cypress/fixtures/templates_search/all_templates_search_response.json @@ -0,0 +1,1071 @@ +{ + "totalWorkflows": 506, + "workflows": [ + { + "id": 60, + "name": "test1 test1", + "totalViews": 120000000, + "recentViews": 0, + "user": { + "username": "admin" + }, + "createdAt": "2019-08-30T16:39:31.362Z", + "nodes": [ + { + "id": 11, + "icon": "file:amqp.png", + "name": "n8n-nodes-base.amqpTrigger", + "defaults": { + "name": "AMQP Trigger" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAA8CAYAAAA6/NlyAAAB7UlEQVRoge2W4W3CMBCFj26QjkBHSEdIR4AR6Ah0BBgBRqAjhBFgBBghHaEVlV29PN0lDr+o9D7JEjhn+975bJ8JIYQQQgghhBBCCCGEEA9CY2bf0NaBW2uyu7UN2XSOzTyY60J2BzNbObbsH7eTmS2mhHJHE1wmCD7A93ngEAquHaHc2omCcysSXQW74g32BHfwfTEiuCoQm9vuDsEndPYpELxKjjBj0foCEXX6XdM3by3c7aOZPZvZzMzeaBzbIh9pzIuZXaG/RqNIMAq7Ur8XCHQ2kx3LC56DMQ39X4LI23zbAd88ruRHD09wTVF5p+/eBZI5g7O8w5FgXOvsZAI7PxRwS4HGIPbm8wRjBL/Sgp/QNyQYHWySmOxgJBgFeGnPfZHgDVyufET+YMEVCdo7gziCTBbGmRKlGQpCMXOnj+1L6B0JFsxndO3cjjZyjo6OnZeqGb5gqhTQS3qKeK1SwbesfB3IrF/awqu+g8Dgs5SLE37SciHiPUv8rLVp7k2wdl63tDDqgTs8lqpINWGXbSTKe9rlJgXME7C9I6V7oGAWsEzv2gzeN2TstkbCZyIJWBYKWUwtF4foKGU9TpRGdZDSdVDpDNXSVVBLt5TeucS9K6X/E3USX3rshBBCCCGEEEIIIYQQ4tExsx8PuuPnwhCIbgAAAABJRU5ErkJggg==" + }, + "categories": [ + { + "id": 5, + "name": "Development" + }, + { + "id": 6, + "name": "Communication" + } + ], + "displayName": "AMQP Trigger", + "typeVersion": 1 + }, + { + "id": 18, + "icon": "file:autopilot.svg", + "name": "n8n-nodes-base.autopilot", + "defaults": { + "name": "Autopilot" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjM4IDI2IDM1IDM1Ij48Y2lyY2xlIGN4PSI1MCIgY3k9IjUwIiByPSI0MCIgc3Ryb2tlPSIjMThkNGIyIiBzdHJva2Utd2lkdGg9IjMiIGZpbGw9IiMxOGQ0YjIiLz48cGF0aCBmaWxsPSIjZmZmIiBkPSJNNDUuNCA0Mi42aDE5LjlsMy40LTQuOEg0MmwzLjQgNC44em0zLjEgOC4zaDEzLjFsMy40LTQuOEg0NS40bDMuMSA0Ljh6bTU0LS43Ii8+PC9zdmc+" + }, + "categories": [ + { + "id": 1, + "name": "Marketing" + } + ], + "displayName": "Autopilot", + "typeVersion": 1 + }, + { + "id": 20, + "icon": "file:lambda.svg", + "name": "n8n-nodes-base.awsLambda", + "defaults": { + "name": "AWS Lambda" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjI1MDAiIHZpZXdCb3g9Ii0zLjAyMyAtMC4yMiA0MjAuOTIzIDQzMy41NCIgd2lkdGg9IjI0NDMiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTIwOC40NSAyMjcuODljLTEuNTkgMi4yNi0yLjkzIDQuMTItNC4yMiA2cS0zMC44NiA0NS40Mi02MS43IDkwLjgzLTI4LjY5IDQyLjI0LTU3LjQ0IDg0LjQzYTMuODggMy44OCAwIDAxLTIuNzMgMS41OXEtNDAuNTktLjM1LTgxLjE2LS44OGMtLjMgMC0uNjEtLjA5LTEuMi0uMThhMTQuNDQgMTQuNDQgMCAwMS43Ni0xLjY1cTI4LjMxLTQzLjg5IDU2LjYyLTg3Ljc2IDI1LjExLTM4Ljg4IDUwLjI1LTc3Ljc0IDI3Ljg2LTQzLjE4IDU1LjY5LTg2LjQyYzIuNzQtNC4yNSA1LjU5LTguNDIgOC4xOS0xMi43NWE1LjI2IDUuMjYgMCAwMC41Ni0zLjgzYy01LTE1Ljk0LTEwLjEtMzEuODQtMTUuMTktNDcuNzQtMi4xOC02LjgxLTQuNDYtMTMuNTgtNi41LTIwLjQzLS42Ni0yLjItMS43NS0yLjg3LTQtMi44Ni0xNyAuMDctMzMuOS4wNS01MC44NS4wNS0zLjIyIDAtMy4yMyAwLTMuMjMtMy4xOCAwLTIwLjg0IDAtNDEuNjgtLjA2LTYyLjUyIDAtMi4zMi43Ni0yLjg0IDIuOTQtMi44NHE1MS4xOS4wOSAxMDIuNCAwYTMuMjkgMy4yOSAwIDAxMy42IDIuNDNxMjcgNjcuOTEgNTQgMTM1Ljc3IDMxLjUgNzkuMTQgNjMgMTU4LjNjNi41MiAxNi4zOCAxMy4wOSAzMi43NSAxOS41NCA0OS4xNy43NyAyIDEuNTcgMi4zOCAzLjU5IDEuNzYgMTcuODktNS41MyAzNS44Mi0xMC45MSA1My43LTE2LjQ1IDIuMjUtLjcgMy4wNy0uMjMgMy43NyAyIDYuMSAxOS4xNyAxMi4zMiAzOC4zIDE4LjUgNTcuNDUuMjEuNjYuMzcgMS4zMy42MiAyLjI1LTEuMjguNDctMi40OCAxLTMuNzEgMS4zNHEtNjEgMTkuMzMtMTIxLjkzIDM4LjY4Yy0xLjk0LjYxLTIuNTItLjA1LTMuMTctMS42OHEtMTguNjEtNDcuMTYtMzcuMzEtOTQuMjgtMTguMjktNDYuMTQtMzYuNi05Mi4yOGMtMS44My00LjYyLTMuNjMtOS4yNi01LjQ2LTEzLjg4LS4yOS0uNzktLjY5LTEuNDgtMS4yNy0yLjd6IiBmaWxsPSIjZmE3ZTE0Ii8+PC9zdmc+" + }, + "categories": [ + { + "id": 5, + "name": "Development" + } + ], + "displayName": "AWS Lambda", + "typeVersion": 1 + }, + { + "id": 40, + "icon": "file:clearbit.svg", + "name": "n8n-nodes-base.clearbit", + "defaults": { + "name": "Clearbit" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3MiIgaGVpZ2h0PSI3MiI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJhIiB4MT0iNTAlIiB4Mj0iMTAwJSIgeTE9IjAlIiB5Mj0iMTAwJSI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI0RFRjJGRSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI0RCRjFGRSIvPjwvbGluZWFyR3JhZGllbnQ+PGxpbmVhckdyYWRpZW50IGlkPSJiIiB4MT0iMCUiIHgyPSI1MCUiIHkxPSIwJSIgeTI9IjEwMCUiPjxzdG9wIG9mZnNldD0iMCUiIHN0b3AtY29sb3I9IiM1N0JDRkQiLz48c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiM1MUI1RkQiLz48L2xpbmVhckdyYWRpZW50PjxsaW5lYXJHcmFkaWVudCBpZD0iYyIgeDE9IjM3LjUlIiB4Mj0iNjIuNSUiIHkxPSIwJSIgeTI9IjEwMCUiPjxzdG9wIG9mZnNldD0iMCUiIHN0b3AtY29sb3I9IiMxQ0E3RkQiLz48c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiMxNDhDRkMiLz48L2xpbmVhckdyYWRpZW50PjwvZGVmcz48ZyBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxwYXRoIGZpbGw9InVybCgjYSkiIGQ9Ik03MiAzNnYxNi43N2wtLjAwNC44NjhjLS4wNiA2LjAzNS0uNzUgOC4zNTMtMiAxMC42ODhhMTMuNjMgMTMuNjMgMCAwMS01LjY3IDUuNjdsLS4zMjYuMTcxQzYxLjY1OCA3MS4zNjQgNTkuMTYgNzIgNTIuNzcgNzJIMzZWMzZoMzZ6Ii8+PHBhdGggZmlsbD0idXJsKCNiKSIgZD0iTTY0LjMyNiAyLjAwM2ExMy42MyAxMy42MyAwIDAxNS42NyA1LjY3bC4xNzEuMzI3QzcxLjM2NCAxMC4zNDIgNzIgMTIuODQgNzIgMTkuMjNWMzZIMzZWMGgxNi43N2M2LjY4NyAwIDkuMTEyLjY5NiAxMS41NTYgMi4wMDN6Ii8+PHBhdGggZmlsbD0idXJsKCNjKSIgZD0iTTM2IDB2NzJIMTkuMjNsLS44NjgtLjAwNGMtNi4wMzUtLjA2LTguMzUzLS43NS0xMC42ODgtMmExMy42MyAxMy42MyAwIDAxLTUuNjctNS42N0wxLjgzMiA2NEMuNjM2IDYxLjY1OCAwIDU5LjE2IDAgNTIuNzdWMTkuMjNjMC02LjY4Ny42OTYtOS4xMTIgMi4wMDMtMTEuNTU2YTEzLjYzIDEzLjYzIDAgMDE1LjY3LTUuNjdMOCAxLjgzMkMxMC4zNDIuNjM2IDEyLjg0IDAgMTkuMjMgMEgzNnoiLz48L2c+PC9zdmc+" + }, + "categories": [ + { + "id": 2, + "name": "Sales" + } + ], + "displayName": "Clearbit", + "typeVersion": 1 + }, + { + "id": 51, + "icon": "file:convertKit.svg", + "name": "n8n-nodes-base.convertKitTrigger", + "defaults": { + "name": "ConvertKit Trigger" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTcyIiBoZWlnaHQ9IjE2MCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJNODIuNzIgMTI2LjMxNmMyOS43NyAwIDUyLjc4LTIyLjYyMiA1Mi43OC01MC41MjYgMC0yNi4xNDMtMjEuNjE3LTQyLjEwNi0zNS45MzUtNDIuMTA2LTE5Ljk0NSAwLTM1LjkzIDE0LjA4NC0zOC4xOTggMzQuOTg4LS40MTggMy44NTYtMy40NzYgNy4wOS03LjM1NSA3LjA2MS02LjQyMy0uMDQ2LTE1Ljc0Ni0uMS0yMS42NTgtLjA4LTIuNTU1LjAwOC00LjY2OS0yLjA2NS00LjU0My00LjYxOC44OS0xOC4xMjMgNi45MTQtMzUuMDcgMTguNDAyLTQ4LjA4N0M1OC45NzYgOC40ODggNzcuNTYxIDAgOTkuNTY1IDBjMzYuOTY5IDAgNzEuODY5IDMzLjc4NiA3MS44NjkgNzUuNzkgMCA0Ni41MDgtMzguMzEyIDg0LjIxLTg3LjkyNyA4NC4yMS0zNS4zODQgMC03MS4wMjEtMjMuMjU4LTgzLjQ2NC01NS43NzVhLjcwMi43MDIgMCAwMS0uMDMtLjM3N2MuMTY1LS45NjIuNDk0LTEuODQxLjgxOC0yLjcwNy40NzEtMS4yNTguOTMxLTIuNDg4Ljg2NC0zLjkwNmwtLjIxNS00LjUyOWE1LjUyMyA1LjUyMyAwIDAxMy4xOC01LjI2M2wxLjc5OC0uODQyYTYuOTgyIDYuOTgyIDAgMDAzLjkxMi01LjA3NSA2Ljk5MyA2Ljk5MyAwIDAxNi44ODctNS43MzZjNS4yODIgMCA5Ljg3NSAzLjUxNSAxMS41OSA4LjUxMiA4LjMwNyAyNC4yMTIgMjEuNTExIDQyLjAxNCA1My44NzMgNDIuMDE0eiIgZmlsbD0iI0ZCNjk3MCIvPjwvc3ZnPg==" + }, + "categories": [ + { + "id": 1, + "name": "Marketing" + }, + { + "id": 2, + "name": "Sales" + } + ], + "displayName": "ConvertKit Trigger", + "typeVersion": 1 + } + ] + }, + { + "id": 25, + "name": "test1 test1", + "totalViews": 120000000, + "recentViews": 0, + "user": { + "username": "admin" + }, + "createdAt": "2019-08-18T18:17:58.358Z", + "nodes": [ + { + "id": 15, + "icon": "file:affinity.png", + "name": "n8n-nodes-base.affinity", + "defaults": { + "name": "Affinity" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAA8CAMAAAANIilAAAAC/VBMVEUAAAAtTfMvVekpSN8rR+g2POglUecfWuY0PukdXeUoTucrS+c2POkfW+VGJusyQeghWOcjVecmUecNceQgWeY4OegiV+YeXOYxQ+gXZeUuR+gmUuc9MuoZYeZHJuszP+g4OulFKOsTauQpS+c7Ou02O+kvRegiVuZBLuoMdOQoTeoQbuQ9NekVaOVHJesQbeQhWOctR+gTauUyQuk9L+wqS+cjVeY0PugyQeg7NekXZeVDK+pHJesgWeYqTOglUugcYeg0PukNceRDK+tGJ+pFKOsyQeg4OekrSec2O+kyQOgkVOYOceQjVechWOY6NuglUecQbuQtSOchV+cxQek9MelAMOkcX+ZDKetEK+otR+dCL+oPZ+IcX+YSbOQ5OOgyQegKdeQUaeU3O+gcX+U2O+kdXeYgWuYmUeYZY+ZFJ+onT+gjVOcSbORFKesKdeMcXeYPbuMYZOQNbOYlUuYyQegcXuU4OehEKeopTechV+dHJusnUOdHJus0PukOceRGJuoPbeQyQegjVeY3O+kpTudGJ+svR+c6N+kcXuUVZ+VGJuoLdOMzQOgLdOQvRegKdeQPceQdXeQqTOZBLegQb+UiVuYeXOQlU+dBLelBLukPcOQTauQdXOUsSuYLd+MvR+hEK+otR+dDLOpHJusPceUiVeczQOkiVuU1Pek2POkgWOYeW+UfWucgY/c2RPcjXfQ3OugkVOc7PfU6NulMKfw4Q/kzQOlLKPgqTektSOclW/ZJKPM1Qe4iWu0YZegsUfUPePIkWvI2QfIeYe85O+1HJ+0tSeowRepAL+kiV+cySvgjXvYRdvQxSfMsTvAxRe09Ne0VbewMc+RILvsvTvk1Rvk7P/lDM/knWPcqVPYWb/Q6PfInVfAnU+xHJuseXuojV+obYOYZavQ3QfQMffMTcvNELvNAMvALe+8Odu8aZu0Mduo9M+oQcOkoUOgLdeU/OflIK/chYfY8OPIgXu8wQ+g8P/wsVPoeZfU6Ou8vSu5BL+0hWugSbea5VKRRAAAAqXRSTlMAAgQFBvkQ/fv+/f37+fv7+/v++/ng2b79/fz7/f35vfz7+0UV/fz8+/sj+Qv+/f38ek0uF/jtqIx1cGZfUU8eF/39/PTj4tvOy8Ozp5OIhnl4b2JQT0tGRDc1HA358O7p5NjU0sW6q6V5eG9rZlRDPDIuE/f26+ba2tHLx7+9urWuoaCamJeXlZGLin5kYllXKe/u6unf3dXTz8/Ix8XBurmdnJqVfnNZVQrS8AAABtNJREFUSMfllndQE0EUxveSCwES0rBEAggoRQRBwYqKvffee++99957712pwUgkVAGlSUdQsYJSLAj2ruPbC2HvxPFvZ9xMkne7+7tv79379g79K42ieHw+j6L+OMDjUX9DeYaITOMeUpV5gtIe/bZv7+cBM/gUQeHrOai5V/NBQ5kj0thTPDZ2SFSr1QU+HZs4I2Sg+QjNON0uQqfVRbRb3pwiNJulNyYkZyiVUqlxSOQdcU+9Bv4dfCT7bbytrURS77FOu2gQqpQSPnJenBNm4eDgIAXaWOD7qbMRdFIw0etZsZNTW1ugrSUS6wjdlN9pCnnvjW4YZhGWlpx2+3ZBa+PWgjtVayMKPk3vJjklOX3Pzn6r1UZIHksk2q2I4rK8xdENQ0XpCafq79xZv0vkdWMfwZWqnsgFNX1jaZmUdHffWa8ZXhvaax/Xq2etbY74nAvu/hnY6BMe+uPBY9UCH8GDhWOrds58ZWn56llPF6afnvI4XlIv3noo0YbIWaEwNY2eBCEfqgRWW/OqwEcc9+Dr++Bgy8zM/pAUPACntY4H6XMgR7LVI1elergMR3oFhGqqjX3sbS7bBAdnZg5CNKVXodGMCEh6O0+DNP4/9FCleDQEWNLVBa7b9/Lly8HBAxDN0lmlk0h0cNVk1Y4aECYXgrWb3Db2wXTcSBfErrYBOom1dgMEho6BUY7y3B4QEAWApSFA28ctdOHcF8928da65RAY4BZBjvLwaSyYAlgtDQHaPm7kUMRnwS7tI6wjlkJEYHdHBiYeqZ+m/BgCteYjfgDVwmfD8RimKuBZH4RVUrpDQNhkh4/KELX6OtBXME3yYxtvq1vFynYrsx+yqGps3RyLMIeM+Y3XdMbVQmj488q2tdVuZa/l+AehLOgSog1stCjMIjGxFhxBtRAa6x0ubtv27QDEI6ltFmgmjGrUCiLM9ooWiUQJCbUQTXNpyGPPu05ti9vTiCLKwxbkCYUfqhkhmgfsPVORKBSzkA9CwxgfPAYWy56C+Gxj9A00868SBDRCvXJVpqahoZiFRjG0L+MxBKxlklPxIh4jTLQnYjplSa+1Ex8qFKoyPcuhO25at+k9eCzp+2BQ48CoOtCyqJTwWLlG8agMs2RzWwG0OO7OmzfB2J/9DYsmM4AW+gtljnKNRqMoZ4m2Wuxrb2ODPUZYDj3hhom/n9/Nm/LYvhUs8ZgYTII91p+McbZAExN/TJMyJx5bd5UxqM37jiRXHOlWw5+Y+Af4+cnCL3BhOGqsZgxq87Ujjw2Tmpz8EqRBWxbUArS4q1qjZgxKPMZpwD61emICLUAYtQT2bA7bJ0MZEmLc2t7XhniMy9awsnpyIy9PGCBLqYZpsqY+yR+VypDIyEixL+Mx6rd1Tb9fo0aNa8PHLdhzQxiAa41sNMDCoyRR2WFUB8i54MpYqGyO7uwsV2CvDURGwyYEmulpvmHNOdhjGf0oI5qptU+N2TBFoWP37VyBZeSqExqveSr2Z8K3fszUFWqBr80VliMhalBk5+qa1QDRMJvQjI+mYo8lfGuJaAqPjS8Qix+M5+ynq4vs7O5vARYam0aYBY+ltzSMec73sbeP82DRw/aXuv0cMRd6uPTarpMeKRSExbXW86pYfKcp4pN0lbrVLeqGKTYtiwqPjZUrFGWPKlg4/ZBIePxCysglv3Crm9obQ4R+JwwQyuRyjVxDWAwbjYoUFIxnbb27YszNU6dDwKbhfoPFwKC4WAlMd4g0vt4ZIqIMMCizfdQj0AxbzD2/Ea4JtnKG9HoXlvLs123MY1Zzn1Wbv5hhgwJsxIGHZCil6sbsbI8uMb9VtxWrhwZ4OGPvfLwjs5+eyVLp7Yuki4e6FdYxjzlPcgo7+UvGY0I/9yhcLRWst0WiUpkI95nAM5/XudXmxQ6gKEBharOnVthjN7DH9LVGQYHRiNcpzcIhrSanuNFJoG+V9DZ0bMH+vGY1btxB7LHYA0PK+507pVuEWaTVYqUHwt2vX4N2zNHpu+fOndNsDOPPly2g0qu/w9WSP2mgt5F3rfWm6aKwhjlnAOD4eVch0OYvUt1GjPhZlGXnCmwDRDH7eRU/95vhsfMazXt4L9RU1DCnE8XxM9be8bykTh1z8zalpW520LJcG5T7aEKgUObu6KjJ16hU8ML1uZMLYUnSRheWAF0XmlvW/TFz9HcD6B557344QqXKVaqye+nrEWFZK6e3jX5eGBMTk5qaNaYZ0rPMzDkTq6SkBMWG5+aqus6qzJZr0DO3dVu5sltvmMF5QUHefbt3rd61e19n8tZfWZyEnC2OJJfiolx1Pg8av9LZKdzHvJX+7+0X6aNkPIGC7jsAAAAASUVORK5CYII=" + }, + "categories": [ + { + "id": 2, + "name": "Sales" + } + ], + "displayName": "Affinity", + "typeVersion": 1 + }, + { + "id": 21, + "icon": "file:comprehend.svg", + "name": "n8n-nodes-base.awsComprehend", + "defaults": { + "name": "AWS Comprehend" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA3NSA3NSI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJhIiB4MT0iNjE3LjQ2IiB5MT0iLTY3NC41MyIgeDI9IjcyMy41MyIgeTI9Ii01NjguNDYiIGdyYWRpZW50VHJhbnNmb3JtPSJyb3RhdGUoLTkwIDY4My41IDI0LjUpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSIjMDU1ZjRlIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjNTZjMGE3Ii8+PC9saW5lYXJHcmFkaWVudD48L2RlZnM+PHBhdGggZGF0YS1uYW1lPSJUdXJxdW9pc2UgR3JhZGllbnQiIGQ9Ik0wIDBoNzV2NzVIMHoiIGZpbGw9InVybCgjYSkiLz48cGF0aCBkPSJNNDQuNSAzNC4ydi05LjdhMSAxIDAgMDAtLjI5LS43MWwtMTEtMTFhMSAxIDAgMDAtLjcxLS4yOWgtMTlhMSAxIDAgMDAtMSAxdjQzYTEgMSAwIDAwMSAxaDMwYTEgMSAwIDAwMS0xdi00LjQ0YTExLjggMTEuOCAwIDAxLTItMi4zdjUuNzRoLTI4di00MWgxN3YxMGExIDEgMCAwMDEgMWgxMHYxMWExMS41NiAxMS41NiAwIDAxMi0yLjN6bS0xMS0xMC43di03LjU5bDcuNTkgNy41OXptLTEwIDhoLTZ2LTJoNnptMTYgMGgtMTR2LTJoMTR6bTAgNmgtMjJ2LTJoMjJ6bTE1LjQ0IDI1aC00Ljg4YTEgMSAwIDAxLS45My0uNjJsLTEuMjEtM2ExIDEgMCAwMS4wOS0uOTQgMSAxIDAgMDEuODMtLjQ0aDcuMzJhMSAxIDAgMDEuODMuNDQgMSAxIDAgMDEuMDkuOTRsLTEuMjEgM2ExIDEgMCAwMS0uOTMuNjJ6bS00LjIxLTJoMy41NGwuNC0xaC00LjM0em0xMS42NC0xOWExMCAxMCAwIDAwLTE5Ljg3IDEuNjIgMTAgMTAgMCAwMDQuMjggOC4yIDMuODggMy44OCAwIDAxLjcyLjU5djMuNTlhMSAxIDAgMDAxIDFoOGExIDEgMCAwMDEtMXYtMy42YTQuMzMgNC4zMyAwIDAxLjcxLS41NyA5LjkyIDkuOTIgMCAwMDQuMjktOC4yIDEwLjE5IDEwLjE5IDAgMDAtLjEzLTEuNjV6bS01LjMyIDguMmMtLjU4LjQtMS41NSAxLjA3LTEuNTUgMi4xdjIuN2gtMnYtN2gydi0yaC02djJoMnY3aC0ydi0yLjY4YzAtMS0xLTEuNzMtMS41OC0yLjE0QTggOCAwIDExNTggMzcuMzJhNy44OSA3Ljg5IDAgMDEyLjM5IDQuNDcgOCA4IDAgMDEtMy4zNCA3Ljkxek0yOC41IDI1LjVoLTExdi0yaDExem0xIDE4aC0xMnYtMmgxMnptMTAgMGgtOHYtMmg4em0tOSA2aC0xM3YtMmgxM3oiIGRhdGEtbmFtZT0iSWNvbiBUZXN0IiBmaWxsPSIjZmZmIi8+PC9zdmc+" + }, + "categories": [ + { + "id": 5, + "name": "Development" + } + ], + "displayName": "AWS Comprehend", + "typeVersion": 1 + }, + { + "id": 38, + "icon": "file:chargebee.png", + "name": "n8n-nodes-base.chargebeeTrigger", + "defaults": { + "name": "Chargebee Trigger" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAA8CAMAAAANIilAAAAAk1BMVEUAAAD9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azXS0qjqAAAAMHRSTlMAAta1CPr3HAyb8mNPLBLq5MmGZ1wi28+soEQX7pZ9dkk/MijDurCMims54b6lb1YyU8hoAAACDklEQVRIx+2V2bKiMBBAmyTs+w6CIi64O/n/rxtKJwIjJPA4Nfc8acOpTnfSAX7493G2QZN6Zeml+GwVS0xiNSXtYeBIm6lKEaJf3C5kjrvz6SioFruVTKcIJEG1D8oBa1wXUy6+w2lVxhFFuU+0j4KOjwdOjUHQnHJ/DVZ4iaXXatTo2OuhPuEWXm9fLOhxPQjldeeuyV/NqGT+su1ucZuRkt5PMRElDmCEbdK2MNcmKnaZi2EUrd7GMEHEXGMPi8mYfFruqjd2NLrE+/P9oF9nyCuW+P4JhcorcBbL4dc2WSxyEcoBe9Vi5yJlEc8RyTp7ldVo0w8rkZyzN3ddExjR4sw7TmZhzSRhEddZ3m2TRar5+3z8hDZ/xlMSnzCPnbDu9NcPhLItzKAZG0hJnHVYYhnDYtSS2RksJ+fcQi0qAbDtKXknU84oWPQJtizvxanpmcCQJ3VtiA1lUlbdzk7rfs/bzwFSVR/bsaDhjHukvvfJsbBXm8S+UboXTwfDQFjXG6S0/dde18oGrN20TI4DOzHaA3drzAJWPqVy2Fa+5qTWDn05AKJJbS+eafsHtUlRUgAHB/dkJT+ddJzI7U+3kopjVgiGUwroF7J/IWAac+7RFeo0D6X3daQCiRBNZs2XVrmDr4e2zQ1aVhrMwwl9+Z1Zzw42ptQPHZiPFJv5IXENWXGvdRhL8MN/y2+uRpQ1fWz5HAAAAABJRU5ErkJggg==" + }, + "categories": [ + { + "id": 8, + "name": "Finance & Accounting" + } + ], + "displayName": "Chargebee Trigger", + "typeVersion": 1 + }, + { + "id": 42, + "icon": "file:clickup.svg", + "name": "n8n-nodes-base.clickUpTrigger", + "defaults": { + "name": "ClickUp Trigger" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSItMTAgMCAxNTUgMTU1IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxkZWZzPjxsaW5lYXJHcmFkaWVudCB4MT0iMCUiIHkxPSI2OC4wMSUiIHkyPSI2OC4wMSUiIGlkPSJhIj48c3RvcCBzdG9wLWNvbG9yPSIjODkzMEZEIiBvZmZzZXQ9IjAlIi8+PHN0b3Agc3RvcC1jb2xvcj0iIzQ5Q0NGOSIgb2Zmc2V0PSIxMDAlIi8+PC9saW5lYXJHcmFkaWVudD48bGluZWFyR3JhZGllbnQgeDE9IjAlIiB5MT0iNjguMDElIiB5Mj0iNjguMDElIiBpZD0iYiI+PHN0b3Agc3RvcC1jb2xvcj0iI0ZGMDJGMCIgb2Zmc2V0PSIwJSIvPjxzdG9wIHN0b3AtY29sb3I9IiNGRkM4MDAiIG9mZnNldD0iMTAwJSIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxnIGZpbGw9Im5vbmUiPjxwYXRoIGQ9Ik0uNCAxMTkuMTJsMjMuODEtMTguMjRDMzYuODYgMTE3LjM5IDUwLjMgMTI1IDY1LjI2IDEyNWMxNC44OCAwIDI3Ljk0LTcuNTIgNDAuMDItMjMuOWwyNC4xNSAxNy44QzExMiAxNDIuNTIgOTAuMzQgMTU1IDY1LjI2IDE1NWMtMjUgMC00Ni44Ny0xMi40LTY0Ljg2LTM1Ljg4eiIgZmlsbD0idXJsKCNhKSIvPjxwYXRoIGZpbGw9InVybCgjYikiIGQ9Ik02NS4xOCAzOS44NEwyMi44IDc2LjM2IDMuMjEgNTMuNjQgNjUuMjcuMTZsNjEuNTcgNTMuNTItMTkuNjggMjIuNjR6Ii8+PC9nPjwvc3ZnPg==" + }, + "categories": [ + { + "id": 4, + "name": "Productivity" + }, + { + "id": 6, + "name": "Communication" + } + ], + "displayName": "ClickUp Trigger", + "typeVersion": 1 + } + ] + }, + { + "id": 16, + "name": "Create dynamic Twitter Profile Banner", + "totalViews": 120000, + "recentViews": 0, + "user": { + "username": "admin" + }, + "createdAt": "2022-01-10T14:15:25.921Z", + "nodes": [] + }, + { + "id": 1073, + "name": "Scrape and Store Data from Multiple Pages Websites", + "totalViews": 2476, + "recentViews": 0, + "user": { + "username": "admin" + }, + "createdAt": "2021-05-07T14:48:56.297Z", + "nodes": [ + { + "id": 13, + "icon": "file:asana.svg", + "name": "n8n-nodes-base.asanaTrigger", + "defaults": { + "name": "Asana Trigger" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNjAiIGhlaWdodD0iNjAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PHJhZGlhbEdyYWRpZW50IGN4PSI1MCUiIGN5PSI1NSUiIGZ4PSI1MCUiIGZ5PSI1NSUiIHI9IjcyLjUwNyUiIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoLjkyNDA0IDAgMCAxIC4wMzggMCkiIGlkPSJhIj48c3RvcCBzdG9wLWNvbG9yPSIjRkZCOTAwIiBvZmZzZXQ9IjAlIi8+PHN0b3Agc3RvcC1jb2xvcj0iI0Y5NUQ4RiIgb2Zmc2V0PSI2MCUiLz48c3RvcCBzdG9wLWNvbG9yPSIjRjk1MzUzIiBvZmZzZXQ9Ijk5LjkxJSIvPjwvcmFkaWFsR3JhZGllbnQ+PC9kZWZzPjxwYXRoIGQ9Ik00NS41OTQgMjguNWMtNi45OTQuMDAzLTEyLjY2NCA1LjY3My0xMi42NjcgMTIuNjY3LjAwMyA2Ljk5NSA1LjY3MyAxMi42NjQgMTIuNjY3IDEyLjY2OCA2Ljk5NS0uMDA0IDEyLjY2NC01LjY3MyAxMi42NjctMTIuNjY4LS4wMDMtNi45OTQtNS42NzItMTIuNjY0LTEyLjY2Ny0xMi42Njd6bS0zMi45MjcuMDAxQzUuNjczIDI4LjUwNS4wMDMgMzQuMTc0IDAgNDEuMTdjLjAwMyA2Ljk5NCA1LjY3MyAxMi42NjQgMTIuNjY3IDEyLjY2NyA2Ljk5NS0uMDAzIDEyLjY2NC01LjY3MyAxMi42NjgtMTIuNjY3LS4wMDQtNi45OTUtNS42NzMtMTIuNjY0LTEyLjY2OC0xMi42Njh6TTQxLjc5IDEyLjY2N2MtLjAwMiA2Ljk5NS01LjY3MSAxMi42NjUtMTIuNjY2IDEyLjY3LTYuOTk1LS4wMDQtMTIuNjY0LTUuNjc0LTEyLjY2Ny0xMi42N0MxNi40NiA1LjY3MyAyMi4xMy4wMDMgMjkuMTIzIDBjNi45OTQuMDA0IDEyLjY2MyA1LjY3MyAxMi42NjYgMTIuNjY3eiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLjczMiAyLjczMikiIGZpbGw9InVybCgjYSkiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvc3ZnPg==" + }, + "categories": [ + { + "id": 4, + "name": "Productivity" + } + ], + "displayName": "Asana Trigger", + "typeVersion": 1 + }, + { + "id": 14, + "icon": "file:apiTemplateIo.svg", + "name": "n8n-nodes-base.apiTemplateIo", + "defaults": { + "name": "APITemplate.io" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4MCIgaGVpZ2h0PSI4MCIgdmlld0JveD0iMCAwIDIzLjE2NyAyMS4xNjciPjxwYXRoIGZpbGw9IiM5MWJjZTUiIGQ9Ik0xOC42MTEuNTA0TDMuNDczIDExLjI0bDQuNTk3IDIuMjEzIDEuMDY2IDMuODgzIDMuMTcxLTIuMjI2IDIuODAyLjk1MXoiLz48cGF0aCBmaWxsPSIjMWYyMTJiIiBkPSJNOS4xNzIgMTcuNTk3YS4yNjQuMjY0IDAgMDEtLjI5LS4xOTJsLTEuMDM1LTMuNzY3LTQuNDg4LTIuMTZhLjI2NS4yNjUgMCAwMS0uMDM5LS40NTRMMTguNDU4LjI4OGEuMjY0LjI2NCAwIDAxLjQxLjI3NGwtMy41IDE1LjU1NmEuMjY1LjI2NSAwIDAxLS4zNDQuMTkzbC0yLjY3NC0uOTA4LTMuMDYxIDIuMTVhLjI3My4yNzMgMCAwMS0uMTE3LjA0NHptLTUuMTgtNi40MDFsNC4xOTMgMi4wMTdjLjA2OS4wMzQuMTIuMDk1LjE0LjE2OWwuOTY3IDMuNTIgMi44NjMtMi4wMWEuMjY1LjI2NSAwIDAxLjIzNy0uMDMzbDIuNTI0Ljg1NkwxOC4yIDEuMTE5eiIvPjxwYXRoIGZpbGw9IiMzYTg0YzEiIGQ9Ik05LjMzOSAxNy4wNzlsLjk0Ni0yLjkxM0wxOC42NDIuNzI4IDguMjI2IDEzLjIzNXoiLz48cGF0aCBmaWxsPSIjMWYyMTJiIiBkPSJNOS4zNTYgMTcuMjFhLjEzMi4xMzIgMCAwMS0uMTQzLS4xNzJsLjk0Ni0yLjkxM2EuMTYuMTYgMCAwMS4wMTQtLjAzbDcuMzkzLTExLjg4OC05LjQxOSAxMS4xNTZhLjEzMy4xMzMgMCAwMS0uMjAyLS4xNzFMMTguNTQuNjQyYS4xMzIuMTMyIDAgMDEuMjE0LjE1NmwtOC4zNDggMTMuNDI0LS45NDEgMi44OThhLjEzMi4xMzIgMCAwMS0uMTA5LjA5em0tMy4yMTUgMS4wNDVjLTEuMTY4LjAyOS0yLjMwMy0uMzczLTMuMDY3LTEuMjg3LS40ODUtLjU4LS42ODMtMS41NzQuMjc1LTEuNzA1Ljg1OS0uMTE5IDEuMjI5LjQ4NSAxLjQ4IDEuMTk2LjgwNCAyLjAyNi4xNiAyLjkzOC0xLjY4IDQuMjcyLS4xNDQuMDkuMDE1LjI5Ni4xNTkuMjA2IDEuMjE4LTEgMS44MjktMS40NjYgMi4xLTIuNDQ4LjEzNC0xLjM2LS40MDItMy4wOTEtMS4zODQtMy40MzYtMS4xMi0uMzgyLTIuMDYzLjUxNi0xLjQ5OSAxLjU4NS42OTkgMS4zMjQgMi4yMyAxLjkxNCAzLjY1MiAxLjg3OC4xNy0uMDA0LjEzMy0uMjY2LS4wMzYtLjI2MXoiLz48cGF0aCBmaWxsPSIjMWYyMTJiIiBkPSJNNy4yMzUgMTguMTRhMTQuOTUgMTQuOTUgMCAwMS0uNzU5LjA5Yy0uMTcuMDE3LS4xMzMuMjc5LjAzNi4yNjIuMjUzLS4wMjUuNTA3LS4wNTIuNzU4LS4wOS4xNjktLjAyNi4xMzMtLjI4OC0uMDM1LS4yNjJ6bTEuMTk0LS41MDRhOC40NiA4LjQ2IDAgMDEtLjg2NS4zNDRjLS4xNjIuMDU0LS4wNTcuMjk3LjEwNC4yNDNhOC4zNyA4LjM3IDAgMDAuODY1LS4zNDRjLjE1NC0uMDcxLjA1LS4zMTUtLjEwNC0uMjQzeiIvPjxwYXRoIGQ9Ik0xMi41OTkgMTUuMjJhLjEzMS4xMzEgMCAwMS0uMDctLjAxbC0yLjE2OC0uOTI0YS4xMzMuMTMzIDAgMDEuMTAzLS4yNDNsMi4xNjkuOTIzYS4xMzMuMTMzIDAgMDEtLjAzNC4yNTN6Ii8+PC9zdmc+" + }, + "categories": [ + { + "id": 1, + "name": "Marketing" + } + ], + "displayName": "APITemplate.io", + "typeVersion": 1 + }, + { + "id": 15, + "icon": "file:affinity.png", + "name": "n8n-nodes-base.affinity", + "defaults": { + "name": "Affinity" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/png;base64,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" + }, + "categories": [ + { + "id": 2, + "name": "Sales" + } + ], + "displayName": "Affinity", + "typeVersion": 1 + }, + { + "id": 19, + "icon": "file:autopilot.svg", + "name": "n8n-nodes-base.autopilotTrigger", + "defaults": { + "name": "Autopilot Trigger" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjM4IDI2IDM1IDM1Ij48Y2lyY2xlIGN4PSI1MCIgY3k9IjUwIiByPSI0MCIgc3Ryb2tlPSIjMThkNGIyIiBzdHJva2Utd2lkdGg9IjMiIGZpbGw9IiMxOGQ0YjIiLz48cGF0aCBmaWxsPSIjZmZmIiBkPSJNNDUuNCA0Mi42aDE5LjlsMy40LTQuOEg0MmwzLjQgNC44em0zLjEgOC4zaDEzLjFsMy40LTQuOEg0NS40bDMuMSA0Ljh6bTU0LS43Ii8+PC9zdmc+" + }, + "categories": [ + { + "id": 1, + "name": "Marketing" + } + ], + "displayName": "Autopilot Trigger", + "typeVersion": 1 + }, + { + "id": 20, + "icon": "file:lambda.svg", + "name": "n8n-nodes-base.awsLambda", + "defaults": { + "name": "AWS Lambda" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjI1MDAiIHZpZXdCb3g9Ii0zLjAyMyAtMC4yMiA0MjAuOTIzIDQzMy41NCIgd2lkdGg9IjI0NDMiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTIwOC40NSAyMjcuODljLTEuNTkgMi4yNi0yLjkzIDQuMTItNC4yMiA2cS0zMC44NiA0NS40Mi02MS43IDkwLjgzLTI4LjY5IDQyLjI0LTU3LjQ0IDg0LjQzYTMuODggMy44OCAwIDAxLTIuNzMgMS41OXEtNDAuNTktLjM1LTgxLjE2LS44OGMtLjMgMC0uNjEtLjA5LTEuMi0uMThhMTQuNDQgMTQuNDQgMCAwMS43Ni0xLjY1cTI4LjMxLTQzLjg5IDU2LjYyLTg3Ljc2IDI1LjExLTM4Ljg4IDUwLjI1LTc3Ljc0IDI3Ljg2LTQzLjE4IDU1LjY5LTg2LjQyYzIuNzQtNC4yNSA1LjU5LTguNDIgOC4xOS0xMi43NWE1LjI2IDUuMjYgMCAwMC41Ni0zLjgzYy01LTE1Ljk0LTEwLjEtMzEuODQtMTUuMTktNDcuNzQtMi4xOC02LjgxLTQuNDYtMTMuNTgtNi41LTIwLjQzLS42Ni0yLjItMS43NS0yLjg3LTQtMi44Ni0xNyAuMDctMzMuOS4wNS01MC44NS4wNS0zLjIyIDAtMy4yMyAwLTMuMjMtMy4xOCAwLTIwLjg0IDAtNDEuNjgtLjA2LTYyLjUyIDAtMi4zMi43Ni0yLjg0IDIuOTQtMi44NHE1MS4xOS4wOSAxMDIuNCAwYTMuMjkgMy4yOSAwIDAxMy42IDIuNDNxMjcgNjcuOTEgNTQgMTM1Ljc3IDMxLjUgNzkuMTQgNjMgMTU4LjNjNi41MiAxNi4zOCAxMy4wOSAzMi43NSAxOS41NCA0OS4xNy43NyAyIDEuNTcgMi4zOCAzLjU5IDEuNzYgMTcuODktNS41MyAzNS44Mi0xMC45MSA1My43LTE2LjQ1IDIuMjUtLjcgMy4wNy0uMjMgMy43NyAyIDYuMSAxOS4xNyAxMi4zMiAzOC4zIDE4LjUgNTcuNDUuMjEuNjYuMzcgMS4zMy42MiAyLjI1LTEuMjguNDctMi40OCAxLTMuNzEgMS4zNHEtNjEgMTkuMzMtMTIxLjkzIDM4LjY4Yy0xLjk0LjYxLTIuNTItLjA1LTMuMTctMS42OHEtMTguNjEtNDcuMTYtMzcuMzEtOTQuMjgtMTguMjktNDYuMTQtMzYuNi05Mi4yOGMtMS44My00LjYyLTMuNjMtOS4yNi01LjQ2LTEzLjg4LS4yOS0uNzktLjY5LTEuNDgtMS4yNy0yLjd6IiBmaWxsPSIjZmE3ZTE0Ii8+PC9zdmc+" + }, + "categories": [ + { + "id": 5, + "name": "Development" + } + ], + "displayName": "AWS Lambda", + "typeVersion": 1 + }, + { + "id": 31, + "icon": "file:bitwarden.svg", + "name": "n8n-nodes-base.bitwarden", + "defaults": { + "name": "Bitwarden" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgNTUgNjYiIGZpbGw9IiNmZmYiIGZpbGwtcnVsZT0iZXZlbm9kZCIgc3Ryb2tlPSIjMDAwIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjx1c2UgeGxpbms6aHJlZj0iI2EiIHg9Ii41IiB5PSIuNSIvPjxzeW1ib2wgaWQ9ImEiIG92ZXJmbG93PSJ2aXNpYmxlIj48cGF0aCBkPSJNNTMuMzMzIDIuNjY3djMyYzAgMi4zODgtLjQ2NSA0Ljc1Ni0xLjM5NiA3LjEwM3MtMi4wODQgNC40My0zLjQ1OCA2LjI1LTMuMDE1IDMuNTktNC45MTcgNS4zMTItMy42NiAzLjE1My01LjI3MiA0LjI5MmwtNS4wNCAzLjIzLTMuNzMgMi4wNjItMS43Ny44MzRjLS4zMzMuMTY2LS42OTUuMjUtMS4wODMuMjVhMi40IDIuNCAwIDAxLTEuMDgzLS4yNWwtMS43Ny0uODM0LTMuNzMtMi4wNjItNS4wNDItMy4yM2MtMS42MS0xLjE0LTMuMzY4LTIuNTctNS4yNy00LjI5MnMtMy41NC0zLjQ5Mi00LjkxNi01LjMxMi0yLjUyOC0zLjkwMy0zLjQ2LTYuMjVTMCAzNy4wNTUgMCAzNC42Njd2LTMyQTIuNTYgMi41NiAwIDAxLjc5MS43OTIgMi41NiAyLjU2IDAgMDEyLjY2NiAwaDQ4Yy43MiAwIDEuMzQ2LjI2NCAxLjg3NC43OTJhMi41NiAyLjU2IDAgMDEuNzkyIDEuODc1bS04IDMyVjhIMjYuNjY2djQ3LjM3NWMzLjMwNS0xLjc1IDYuMjY0LTMuNjUzIDguODc1LTUuNzA4IDYuNTI3LTUuMTEgOS43OS0xMC4xMSA5Ljc5LTE1IiBzdHJva2U9Im5vbmUiIGZpbGw9IiMzYzhkYmMiIGZpbGwtcnVsZT0ibm9uemVybyIvPjwvc3ltYm9sPjwvc3ZnPg==" + }, + "categories": [ + { + "id": 3, + "name": "Data & Storage" + } + ], + "displayName": "Bitwarden", + "typeVersion": 1 + }, + { + "id": 38, + "icon": "file:chargebee.png", + "name": "n8n-nodes-base.chargebeeTrigger", + "defaults": { + "name": "Chargebee Trigger" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAA8CAMAAAANIilAAAAAk1BMVEUAAAD9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azXS0qjqAAAAMHRSTlMAAta1CPr3HAyb8mNPLBLq5MmGZ1wi28+soEQX7pZ9dkk/MijDurCMims54b6lb1YyU8hoAAACDklEQVRIx+2V2bKiMBBAmyTs+w6CIi64O/n/rxtKJwIjJPA4Nfc8acOpTnfSAX7493G2QZN6Zeml+GwVS0xiNSXtYeBIm6lKEaJf3C5kjrvz6SioFruVTKcIJEG1D8oBa1wXUy6+w2lVxhFFuU+0j4KOjwdOjUHQnHJ/DVZ4iaXXatTo2OuhPuEWXm9fLOhxPQjldeeuyV/NqGT+su1ucZuRkt5PMRElDmCEbdK2MNcmKnaZi2EUrd7GMEHEXGMPi8mYfFruqjd2NLrE+/P9oF9nyCuW+P4JhcorcBbL4dc2WSxyEcoBe9Vi5yJlEc8RyTp7ldVo0w8rkZyzN3ddExjR4sw7TmZhzSRhEddZ3m2TRar5+3z8hDZ/xlMSnzCPnbDu9NcPhLItzKAZG0hJnHVYYhnDYtSS2RksJ+fcQi0qAbDtKXknU84oWPQJtizvxanpmcCQJ3VtiA1lUlbdzk7rfs/bzwFSVR/bsaDhjHukvvfJsbBXm8S+UboXTwfDQFjXG6S0/dde18oGrN20TI4DOzHaA3drzAJWPqVy2Fa+5qTWDn05AKJJbS+eafsHtUlRUgAHB/dkJT+ddJzI7U+3kopjVgiGUwroF7J/IWAac+7RFeo0D6X3daQCiRBNZs2XVrmDr4e2zQ1aVhrMwwl9+Z1Zzw42ptQPHZiPFJv5IXENWXGvdRhL8MN/y2+uRpQ1fWz5HAAAAABJRU5ErkJggg==" + }, + "categories": [ + { + "id": 8, + "name": "Finance & Accounting" + } + ], + "displayName": "Chargebee Trigger", + "typeVersion": 1 + }, + { + "id": 39, + "icon": "file:circleCi.png", + "name": "n8n-nodes-base.circleCi", + "defaults": { + "name": "CircleCI" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEYAAABGCAMAAABG8BK2AAAAmVBMVEUAAAAVFBUEAwQVExUAAAAAAAAVFBUWExYVFBUUFBQcHBwVFBUUFBQVExUVERURDxEVExUUExQVFBUUFBQVFBUVFBUUEhQUFBQVExUUFBQVFBUVExUUExQVExUWFhYZGRkVFRUVFBUVFBUVFBUVFBUVFBUUFBQWFBYWFhYTExMUFBQTExMUFBQVFBUVExUVFBUWFRYVFBUVFBX33+L1AAAAMnRSTlMA/QX5AwL7adxiCHqYux8QqUHn4cuOKZ9t79fEr4UiFAy19eyUgHJGLhpLNTPPWlI54+oyz24AAAM7SURBVFjDlJWJduIwDEUly3ZD2MtaylrWlnbaef//cYNLIIntAPNyTuDYyZWebCsUlVHurubdWn3QGDUG9Vq3pX+HDD0s7d44jKeJBZhxvknSGQ8dSD8Omde/4MRyEcOpX289CFJEPx3rwgszcjGLddzOkUg9ADmsABQQZdTpPh3eAylKNy4kI65sbpDe4hii/bOLeFsC7nXJmGpDM2cH9+QCbauMKVJNZsYjYuapIRWlLCZBUW6V6GtJKkbpQfC4BEk74GhaJh7lPmeSkiZP/YByn/MaJNNhwf9KeFSypeipisJnVXFqBY6mIcAxhFicZSVGYsg8L4+h11hhLON0rZNJsob7Z2PlaZK5WqpFLDkjyeZ72Fak2sPvTeKGIpw/V1vLZ3A4j9W+tAofTUBCWz11SaYeJMOWJ103p0zWVN3D+wSWgyrvSFUkw4yGY1BRSpMeBcYYf9NssSH+FMYUPTC0CziCbNH7YC8XN2PiH4wnn2OzvfyJgF8nVdmTZhA/9QNpohmLR1kVT5zRSpc6ZNPjCLuovifG23uOycqckzS1xH/h9QRvMYtYJ/m93hy8UNTFsfvZpuLQliWIS2niFbiX5gboZ/oCYN05Fly1X/x0Pojo/amsFuWLNABwPt+zQpUbkHJxto7uy1x/R8zC587LDdIXTLe86ILOeaOXlJ/Ycd7iOW8thtLytrfoU6h4DRjPaZanpiZs+XimN77DNUi0JSgasJQwL4sbmJGHaVwxOw8jrWpTXurWbe5ongye/6vF2nEQhmFoTRxPsCGBuEAlxJT7H45QF6UiH6d9wUvVJamT+v3+vQzeVHFPsY64/8Kn+oXjvx+FqzUM4iiWOQz5aN6PjOYooFDYSpXD1qsHtgwQZdaHCaI2pDN7E9L3EwyVCSZ2JQW6m6p0FzK684PItyUFuFcKNIRJeMwL27FX5vws+awJk6ZMusx+h0zCRVtTQtKPhJSahLQEbdgAhS1ocXmdvud2SOwrPOPWY7QRwm0ZbhJByyrJssIGmmshx2rnyepH7TweLsQDQKIOsaMOLf8NXsgJEW2HQlwevNgxEOn2WrK8aQzEvXnSaQ2lHBBKZRHZuSMiewPdyr8Dzj/WNwAAAABJRU5ErkJggg==" + }, + "categories": [ + { + "id": 5, + "name": "Development" + } + ], + "displayName": "CircleCI", + "typeVersion": 1 + }, + { + "id": 42, + "icon": "file:clickup.svg", + "name": "n8n-nodes-base.clickUpTrigger", + "defaults": { + "name": "ClickUp Trigger" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSItMTAgMCAxNTUgMTU1IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxkZWZzPjxsaW5lYXJHcmFkaWVudCB4MT0iMCUiIHkxPSI2OC4wMSUiIHkyPSI2OC4wMSUiIGlkPSJhIj48c3RvcCBzdG9wLWNvbG9yPSIjODkzMEZEIiBvZmZzZXQ9IjAlIi8+PHN0b3Agc3RvcC1jb2xvcj0iIzQ5Q0NGOSIgb2Zmc2V0PSIxMDAlIi8+PC9saW5lYXJHcmFkaWVudD48bGluZWFyR3JhZGllbnQgeDE9IjAlIiB5MT0iNjguMDElIiB5Mj0iNjguMDElIiBpZD0iYiI+PHN0b3Agc3RvcC1jb2xvcj0iI0ZGMDJGMCIgb2Zmc2V0PSIwJSIvPjxzdG9wIHN0b3AtY29sb3I9IiNGRkM4MDAiIG9mZnNldD0iMTAwJSIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxnIGZpbGw9Im5vbmUiPjxwYXRoIGQ9Ik0uNCAxMTkuMTJsMjMuODEtMTguMjRDMzYuODYgMTE3LjM5IDUwLjMgMTI1IDY1LjI2IDEyNWMxNC44OCAwIDI3Ljk0LTcuNTIgNDAuMDItMjMuOWwyNC4xNSAxNy44QzExMiAxNDIuNTIgOTAuMzQgMTU1IDY1LjI2IDE1NWMtMjUgMC00Ni44Ny0xMi40LTY0Ljg2LTM1Ljg4eiIgZmlsbD0idXJsKCNhKSIvPjxwYXRoIGZpbGw9InVybCgjYikiIGQ9Ik02NS4xOCAzOS44NEwyMi44IDc2LjM2IDMuMjEgNTMuNjQgNjUuMjcuMTZsNjEuNTcgNTMuNTItMTkuNjggMjIuNjR6Ii8+PC9nPjwvc3ZnPg==" + }, + "categories": [ + { + "id": 4, + "name": "Productivity" + }, + { + "id": 6, + "name": "Communication" + } + ], + "displayName": "ClickUp Trigger", + "typeVersion": 1 + }, + { + "id": 46, + "icon": "fa:file-archive", + "name": "n8n-nodes-base.compression", + "defaults": { + "name": "Compression", + "color": "#408000" + }, + "iconData": { + "icon": "file-archive", + "type": "icon" + }, + "categories": [ + { + "id": 3, + "name": "Data & Storage" + }, + { + "id": 9, + "name": "Core Nodes" + } + ], + "displayName": "Compression", + "typeVersion": 1 + }, + { + "id": 59, + "icon": "fa:clock", + "name": "n8n-nodes-base.dateTime", + "defaults": { + "name": "Date & Time", + "color": "#408000" + }, + "iconData": { + "icon": "clock", + "type": "icon" + }, + "categories": [ + { + "id": 9, + "name": "Core Nodes" + } + ], + "displayName": "Date & Time", + "typeVersion": 2 + }, + { + "id": 114, + "icon": "file:helpScout.svg", + "name": "n8n-nodes-base.helpScoutTrigger", + "defaults": { + "name": "HelpScout Trigger" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjU2IiBoZWlnaHQ9IjMxMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWlkWU1pZCI+PHBhdGggZD0iTTE4LjQzMiAxODAuOTY5bDkwLjQ4NC05MC40ODVhNjMuNzE3IDYzLjcxNyAwIDAwMTguOTktNDUuNDI4QTY0LjI0NSA2NC4yNDUgMCAwMDEwOS40NzYgMEwxOC45OSA5MC40ODRBNjMuNzE3IDYzLjcxNyAwIDAwMCAxMzUuOTEzYzAgMTcuNjg3IDcuMDc1IDMzLjUxMiAxOC40MzIgNDUuMDU2em0yMTkuMTM2LTUyLjg3NmwtOTAuNDg0IDkwLjQ4NGE2My43MTcgNjMuNzE3IDAgMDAtMTguOTkgNDUuNDI5IDY0LjI0NSA2NC4yNDUgMCAwMDE4LjQzMSA0NS4wNTZsOTAuNDg0LTkwLjQ4NUE2My43MTcgNjMuNzE3IDAgMDAyNTYgMTczLjE1YzAtMTcuNjg3LTcuMDc1LTMzLjUxMy0xOC40MzItNDUuMDU2em0tLjU1OS0zNy40MjJBNjMuNzE3IDYzLjcxNyAwIDAwMjU2IDQ1LjI0MiA2NC4yNDUgNjQuMjQ1IDAgMDAyMzcuNTY4LjE4NkwxOC45OTEgMjE4LjU3N0M3LjI2IDIzMC4zMDcgMCAyNDYuMzIgMCAyNjQuMTkyYTY0LjI0NSA2NC4yNDUgMCAwMDE4LjQzMiA0NS4wNTZMMjM3LjAwOSA5MC42NzF6IiBmaWxsPSIjMTI5MkVFIi8+PC9zdmc+" + }, + "categories": [ + { + "id": 6, + "name": "Communication" + } + ], + "displayName": "HelpScout Trigger", + "typeVersion": 1 + } + ] + }, + { + "id": 226, + "name": "Receive Google Sheet data via REST API", + "totalViews": 2438, + "recentViews": 0, + "user": { + "username": "admin" + }, + "createdAt": "2020-01-01T19:27:20.732Z", + "nodes": [ + { + "id": 18, + "icon": "file:autopilot.svg", + "name": "n8n-nodes-base.autopilot", + "defaults": { + "name": "Autopilot" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjM4IDI2IDM1IDM1Ij48Y2lyY2xlIGN4PSI1MCIgY3k9IjUwIiByPSI0MCIgc3Ryb2tlPSIjMThkNGIyIiBzdHJva2Utd2lkdGg9IjMiIGZpbGw9IiMxOGQ0YjIiLz48cGF0aCBmaWxsPSIjZmZmIiBkPSJNNDUuNCA0Mi42aDE5LjlsMy40LTQuOEg0MmwzLjQgNC44em0zLjEgOC4zaDEzLjFsMy40LTQuOEg0NS40bDMuMSA0Ljh6bTU0LS43Ii8+PC9zdmc+" + }, + "categories": [ + { + "id": 1, + "name": "Marketing" + } + ], + "displayName": "Autopilot", + "typeVersion": 1 + }, + { + "id": 47, + "icon": "file:coda.svg", + "name": "n8n-nodes-base.coda", + "defaults": { + "name": "Coda" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI0LjIuMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCA2MCA2MCIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgNjAgNjA7IiB4bWw6c3BhY2U9InByZXNlcnZlIj4KPHN0eWxlIHR5cGU9InRleHQvY3NzIj4KCS5zdDB7ZmlsbDojRUU1QTI5O30KPC9zdHlsZT4KPHBhdGggY2xhc3M9InN0MCIgZD0iTTQ1LjIsMTYuMmMzLjMsMCw2LjUsMS4zLDguOCwzLjNjMS41LDEuMywzLjgsMC4yLDMuOC0xLjhWNC43YzAtMi41LTIuMS00LjctNC43LTQuN0g2LjMKCUMzLjcsMCwxLjYsMi4xLDEuNiw0Ljd2NTAuN2MwLDIuNSwyLjEsNC42LDQuNyw0LjZoNDYuOGMyLjUsMCw0LjctMi4xLDQuNy00LjdWNDIuM2MwLTItMi4zLTMuMS0zLjgtMS44Yy0yLjQsMi4xLTUuNCwzLjMtOC44LDMuMwoJYy03LjYsMC0xMy43LTYuMi0xMy43LTEzLjhDMzEuNiwyMi40LDM3LjcsMTYuMiw0NS4yLDE2LjJ6Ii8+Cjwvc3ZnPgo=" + }, + "categories": [ + { + "id": 4, + "name": "Productivity" + } + ], + "displayName": "Coda", + "typeVersion": 1 + } + ] + }, + { + "id": 156, + "name": "Get Execute Command Data and Transfer to JSON", + "totalViews": 1855, + "recentViews": 0, + "user": { + "username": "admin" + }, + "createdAt": "2019-11-02T12:18:37.963Z", + "nodes": [ + { + "id": 13, + "icon": "file:asana.svg", + "name": "n8n-nodes-base.asanaTrigger", + "defaults": { + "name": "Asana Trigger" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNjAiIGhlaWdodD0iNjAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PHJhZGlhbEdyYWRpZW50IGN4PSI1MCUiIGN5PSI1NSUiIGZ4PSI1MCUiIGZ5PSI1NSUiIHI9IjcyLjUwNyUiIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoLjkyNDA0IDAgMCAxIC4wMzggMCkiIGlkPSJhIj48c3RvcCBzdG9wLWNvbG9yPSIjRkZCOTAwIiBvZmZzZXQ9IjAlIi8+PHN0b3Agc3RvcC1jb2xvcj0iI0Y5NUQ4RiIgb2Zmc2V0PSI2MCUiLz48c3RvcCBzdG9wLWNvbG9yPSIjRjk1MzUzIiBvZmZzZXQ9Ijk5LjkxJSIvPjwvcmFkaWFsR3JhZGllbnQ+PC9kZWZzPjxwYXRoIGQ9Ik00NS41OTQgMjguNWMtNi45OTQuMDAzLTEyLjY2NCA1LjY3My0xMi42NjcgMTIuNjY3LjAwMyA2Ljk5NSA1LjY3MyAxMi42NjQgMTIuNjY3IDEyLjY2OCA2Ljk5NS0uMDA0IDEyLjY2NC01LjY3MyAxMi42NjctMTIuNjY4LS4wMDMtNi45OTQtNS42NzItMTIuNjY0LTEyLjY2Ny0xMi42Njd6bS0zMi45MjcuMDAxQzUuNjczIDI4LjUwNS4wMDMgMzQuMTc0IDAgNDEuMTdjLjAwMyA2Ljk5NCA1LjY3MyAxMi42NjQgMTIuNjY3IDEyLjY2NyA2Ljk5NS0uMDAzIDEyLjY2NC01LjY3MyAxMi42NjgtMTIuNjY3LS4wMDQtNi45OTUtNS42NzMtMTIuNjY0LTEyLjY2OC0xMi42Njh6TTQxLjc5IDEyLjY2N2MtLjAwMiA2Ljk5NS01LjY3MSAxMi42NjUtMTIuNjY2IDEyLjY3LTYuOTk1LS4wMDQtMTIuNjY0LTUuNjc0LTEyLjY2Ny0xMi42N0MxNi40NiA1LjY3MyAyMi4xMy4wMDMgMjkuMTIzIDBjNi45OTQuMDA0IDEyLjY2MyA1LjY3MyAxMi42NjYgMTIuNjY3eiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLjczMiAyLjczMikiIGZpbGw9InVybCgjYSkiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvc3ZnPg==" + }, + "categories": [ + { + "id": 4, + "name": "Productivity" + } + ], + "displayName": "Asana Trigger", + "typeVersion": 1 + }, + { + "id": 15, + "icon": "file:affinity.png", + "name": "n8n-nodes-base.affinity", + "defaults": { + "name": "Affinity" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/png;base64,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" + }, + "categories": [ + { + "id": 2, + "name": "Sales" + } + ], + "displayName": "Affinity", + "typeVersion": 1 + }, + { + "id": 20, + "icon": "file:lambda.svg", + "name": "n8n-nodes-base.awsLambda", + "defaults": { + "name": "AWS Lambda" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjI1MDAiIHZpZXdCb3g9Ii0zLjAyMyAtMC4yMiA0MjAuOTIzIDQzMy41NCIgd2lkdGg9IjI0NDMiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTIwOC40NSAyMjcuODljLTEuNTkgMi4yNi0yLjkzIDQuMTItNC4yMiA2cS0zMC44NiA0NS40Mi02MS43IDkwLjgzLTI4LjY5IDQyLjI0LTU3LjQ0IDg0LjQzYTMuODggMy44OCAwIDAxLTIuNzMgMS41OXEtNDAuNTktLjM1LTgxLjE2LS44OGMtLjMgMC0uNjEtLjA5LTEuMi0uMThhMTQuNDQgMTQuNDQgMCAwMS43Ni0xLjY1cTI4LjMxLTQzLjg5IDU2LjYyLTg3Ljc2IDI1LjExLTM4Ljg4IDUwLjI1LTc3Ljc0IDI3Ljg2LTQzLjE4IDU1LjY5LTg2LjQyYzIuNzQtNC4yNSA1LjU5LTguNDIgOC4xOS0xMi43NWE1LjI2IDUuMjYgMCAwMC41Ni0zLjgzYy01LTE1Ljk0LTEwLjEtMzEuODQtMTUuMTktNDcuNzQtMi4xOC02LjgxLTQuNDYtMTMuNTgtNi41LTIwLjQzLS42Ni0yLjItMS43NS0yLjg3LTQtMi44Ni0xNyAuMDctMzMuOS4wNS01MC44NS4wNS0zLjIyIDAtMy4yMyAwLTMuMjMtMy4xOCAwLTIwLjg0IDAtNDEuNjgtLjA2LTYyLjUyIDAtMi4zMi43Ni0yLjg0IDIuOTQtMi44NHE1MS4xOS4wOSAxMDIuNCAwYTMuMjkgMy4yOSAwIDAxMy42IDIuNDNxMjcgNjcuOTEgNTQgMTM1Ljc3IDMxLjUgNzkuMTQgNjMgMTU4LjNjNi41MiAxNi4zOCAxMy4wOSAzMi43NSAxOS41NCA0OS4xNy43NyAyIDEuNTcgMi4zOCAzLjU5IDEuNzYgMTcuODktNS41MyAzNS44Mi0xMC45MSA1My43LTE2LjQ1IDIuMjUtLjcgMy4wNy0uMjMgMy43NyAyIDYuMSAxOS4xNyAxMi4zMiAzOC4zIDE4LjUgNTcuNDUuMjEuNjYuMzcgMS4zMy42MiAyLjI1LTEuMjguNDctMi40OCAxLTMuNzEgMS4zNHEtNjEgMTkuMzMtMTIxLjkzIDM4LjY4Yy0xLjk0LjYxLTIuNTItLjA1LTMuMTctMS42OHEtMTguNjEtNDcuMTYtMzcuMzEtOTQuMjgtMTguMjktNDYuMTQtMzYuNi05Mi4yOGMtMS44My00LjYyLTMuNjMtOS4yNi01LjQ2LTEzLjg4LS4yOS0uNzktLjY5LTEuNDgtMS4yNy0yLjd6IiBmaWxsPSIjZmE3ZTE0Ii8+PC9zdmc+" + }, + "categories": [ + { + "id": 5, + "name": "Development" + } + ], + "displayName": "AWS Lambda", + "typeVersion": 1 + } + ] + }, + { + "id": 1, + "name": "Excel to Postgres", + "totalViews": 1757, + "recentViews": 0, + "user": { + "username": "admin" + }, + "createdAt": "2019-08-31T00:05:02.587Z", + "nodes": [ + { + "id": 30, + "icon": "file:bitly.svg", + "name": "n8n-nodes-base.bitly", + "defaults": { + "name": "Bitly" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMTI4IDEyOCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZyBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGZpbGwtcnVsZT0iZXZlbm9kZCI+PHBhdGggZmlsbD0ibm9uZSIgZD0iTTAgMGgxMjh2MTI4SDB6Ii8+PHBhdGggZD0iTTYzLjcwOCAwQzI4LjQ0MyAwIDAgMjguMzA2IDAgNjUuMzIyYzAgMTkuMTkzIDEwLjI2NiAzNy43MzYgMjQuNzAzIDQ4Ljg3NyAyLjc4IDIuMTQ1IDYuMDkxIDEuOTQ5IDcuOTg5LjEwOSAxLjU5OS0xLjU1IDEuNDY3LTUuMjgyLTEuNTA3LTcuODk3LTExLjU0MS0xMC4xNDktMTkuNTItMjUuNTE0LTE5LjUyLTQwLjg2MSAwLTI3LjU2NCAyNC40NzQtNTAuNzU2IDUyLjA0NC01MC43NTYgMzMuNTU4IDAgNTEuNTAzIDI3LjI1MiA1MS41MDMgNTAuMzUxIDAgMTQuMTE0LTYuOTAyIDMxLjE1NS0xOS4zNjEgNDIuMDI1LjAxOS0uMDQ5IDIuNTg4LTUuMTAxIDIuNTg4LTE0Ljk0IDAtMTYuNzQ4LTEwLjYxMi0yNS44MjEtMjIuOTI2LTI1LjgyMS04LjkxNCAwLTE0LjI1MSAzLjE4Ny0xNy44ODMgNi4xNTggMC02LjgyMi4yMjgtMTkuNTYzLjIyOC0xOS41NjMgMC04LjQwOS0yLjk0Ni0xNS4xNC0xMy4yMTMtMTUuMjg3LTUuOTQzLS4wODQtMTAuMzUzIDIuNjQxLTEzLjEwMyA4LjgwMy0uOTkxIDIuMzExLS42MjYgNC44MjIgMS4zMzMgNS45NiAxLjYyMS45NDEgNC4yODcuMjQzIDUuNjA2LTEuNTE3Ljg4MS0xLjEgMS4zNzUtMS4zMzUgMi4xNDEtMS4yNTIgMS4yNjEuMTM2IDEuMzA5IDIuMTY4IDEuMzU5IDMuNDY4LjAzOC45OTggMS4wMDggMTUuNDk0LjQ3NyA1Mi43NDYgMCAxMC4yNzUgOC4wNTkgMjIuMDc3IDI3LjM1NSAyMi4wNzcgOC4zMDcgMCAxNC42ODYtMi4zMjIgMjMuOTUzLTcuNTg3QzEwNy44NzEgMTEyLjM5OCAxMjggOTQuNjIgMTI4IDY0LjU1IDEyOCAyNi45NzcgOTcuNTcyIDAgNjMuNzA4IDB6bTYuMDUyIDExMy42MDJjLTcuMTgzLjEyNi0xMi40MjItMi4xODMtMTIuNzQzLTguNDYxLS4xMi0yLjM1Ni0uMDgyLTQuODY1LjAzMy02LjM3OC42OTUtOS4xNzQgNy4xMS0xNS43NzQgMTMuNDQzLTE3LjA0MSA3Ljg3Ni0xLjU3NiAxMy4xMjMgMi4wMjYgMTMuMTIzIDEyLjMzNy0uMDAxIDYuOTY4LTEuOTM1IDE5LjMzNC0xMy44NTYgMTkuNTQzeiIgZmlsbD0iI0RENUEyQiIvPjwvZz48L3N2Zz4=" + }, + "categories": [ + { + "id": 7, + "name": "Utility" + } + ], + "displayName": "Bitly", + "typeVersion": 1 + }, + { + "id": 31, + "icon": "file:bitwarden.svg", + "name": "n8n-nodes-base.bitwarden", + "defaults": { + "name": "Bitwarden" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgNTUgNjYiIGZpbGw9IiNmZmYiIGZpbGwtcnVsZT0iZXZlbm9kZCIgc3Ryb2tlPSIjMDAwIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjx1c2UgeGxpbms6aHJlZj0iI2EiIHg9Ii41IiB5PSIuNSIvPjxzeW1ib2wgaWQ9ImEiIG92ZXJmbG93PSJ2aXNpYmxlIj48cGF0aCBkPSJNNTMuMzMzIDIuNjY3djMyYzAgMi4zODgtLjQ2NSA0Ljc1Ni0xLjM5NiA3LjEwM3MtMi4wODQgNC40My0zLjQ1OCA2LjI1LTMuMDE1IDMuNTktNC45MTcgNS4zMTItMy42NiAzLjE1My01LjI3MiA0LjI5MmwtNS4wNCAzLjIzLTMuNzMgMi4wNjItMS43Ny44MzRjLS4zMzMuMTY2LS42OTUuMjUtMS4wODMuMjVhMi40IDIuNCAwIDAxLTEuMDgzLS4yNWwtMS43Ny0uODM0LTMuNzMtMi4wNjItNS4wNDItMy4yM2MtMS42MS0xLjE0LTMuMzY4LTIuNTctNS4yNy00LjI5MnMtMy41NC0zLjQ5Mi00LjkxNi01LjMxMi0yLjUyOC0zLjkwMy0zLjQ2LTYuMjVTMCAzNy4wNTUgMCAzNC42Njd2LTMyQTIuNTYgMi41NiAwIDAxLjc5MS43OTIgMi41NiAyLjU2IDAgMDEyLjY2NiAwaDQ4Yy43MiAwIDEuMzQ2LjI2NCAxLjg3NC43OTJhMi41NiAyLjU2IDAgMDEuNzkyIDEuODc1bS04IDMyVjhIMjYuNjY2djQ3LjM3NWMzLjMwNS0xLjc1IDYuMjY0LTMuNjUzIDguODc1LTUuNzA4IDYuNTI3LTUuMTEgOS43OS0xMC4xMSA5Ljc5LTE1IiBzdHJva2U9Im5vbmUiIGZpbGw9IiMzYzhkYmMiIGZpbGwtcnVsZT0ibm9uemVybyIvPjwvc3ltYm9sPjwvc3ZnPg==" + }, + "categories": [ + { + "id": 3, + "name": "Data & Storage" + } + ], + "displayName": "Bitwarden", + "typeVersion": 1 + }, + { + "id": 41, + "icon": "file:clickup.svg", + "name": "n8n-nodes-base.clickUp", + "defaults": { + "name": "ClickUp" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSItMTAgMCAxNTUgMTU1IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxkZWZzPjxsaW5lYXJHcmFkaWVudCB4MT0iMCUiIHkxPSI2OC4wMSUiIHkyPSI2OC4wMSUiIGlkPSJhIj48c3RvcCBzdG9wLWNvbG9yPSIjODkzMEZEIiBvZmZzZXQ9IjAlIi8+PHN0b3Agc3RvcC1jb2xvcj0iIzQ5Q0NGOSIgb2Zmc2V0PSIxMDAlIi8+PC9saW5lYXJHcmFkaWVudD48bGluZWFyR3JhZGllbnQgeDE9IjAlIiB5MT0iNjguMDElIiB5Mj0iNjguMDElIiBpZD0iYiI+PHN0b3Agc3RvcC1jb2xvcj0iI0ZGMDJGMCIgb2Zmc2V0PSIwJSIvPjxzdG9wIHN0b3AtY29sb3I9IiNGRkM4MDAiIG9mZnNldD0iMTAwJSIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxnIGZpbGw9Im5vbmUiPjxwYXRoIGQ9Ik0uNCAxMTkuMTJsMjMuODEtMTguMjRDMzYuODYgMTE3LjM5IDUwLjMgMTI1IDY1LjI2IDEyNWMxNC44OCAwIDI3Ljk0LTcuNTIgNDAuMDItMjMuOWwyNC4xNSAxNy44QzExMiAxNDIuNTIgOTAuMzQgMTU1IDY1LjI2IDE1NWMtMjUgMC00Ni44Ny0xMi40LTY0Ljg2LTM1Ljg4eiIgZmlsbD0idXJsKCNhKSIvPjxwYXRoIGZpbGw9InVybCgjYikiIGQ9Ik02NS4xOCAzOS44NEwyMi44IDc2LjM2IDMuMjEgNTMuNjQgNjUuMjcuMTZsNjEuNTcgNTMuNTItMTkuNjggMjIuNjR6Ii8+PC9nPjwvc3ZnPg==" + }, + "categories": [ + { + "id": 4, + "name": "Productivity" + }, + { + "id": 6, + "name": "Communication" + } + ], + "displayName": "ClickUp", + "typeVersion": 1 + }, + { + "id": 42, + "icon": "file:clickup.svg", + "name": "n8n-nodes-base.clickUpTrigger", + "defaults": { + "name": "ClickUp Trigger" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSItMTAgMCAxNTUgMTU1IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxkZWZzPjxsaW5lYXJHcmFkaWVudCB4MT0iMCUiIHkxPSI2OC4wMSUiIHkyPSI2OC4wMSUiIGlkPSJhIj48c3RvcCBzdG9wLWNvbG9yPSIjODkzMEZEIiBvZmZzZXQ9IjAlIi8+PHN0b3Agc3RvcC1jb2xvcj0iIzQ5Q0NGOSIgb2Zmc2V0PSIxMDAlIi8+PC9saW5lYXJHcmFkaWVudD48bGluZWFyR3JhZGllbnQgeDE9IjAlIiB5MT0iNjguMDElIiB5Mj0iNjguMDElIiBpZD0iYiI+PHN0b3Agc3RvcC1jb2xvcj0iI0ZGMDJGMCIgb2Zmc2V0PSIwJSIvPjxzdG9wIHN0b3AtY29sb3I9IiNGRkM4MDAiIG9mZnNldD0iMTAwJSIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxnIGZpbGw9Im5vbmUiPjxwYXRoIGQ9Ik0uNCAxMTkuMTJsMjMuODEtMTguMjRDMzYuODYgMTE3LjM5IDUwLjMgMTI1IDY1LjI2IDEyNWMxNC44OCAwIDI3Ljk0LTcuNTIgNDAuMDItMjMuOWwyNC4xNSAxNy44QzExMiAxNDIuNTIgOTAuMzQgMTU1IDY1LjI2IDE1NWMtMjUgMC00Ni44Ny0xMi40LTY0Ljg2LTM1Ljg4eiIgZmlsbD0idXJsKCNhKSIvPjxwYXRoIGZpbGw9InVybCgjYikiIGQ9Ik02NS4xOCAzOS44NEwyMi44IDc2LjM2IDMuMjEgNTMuNjQgNjUuMjcuMTZsNjEuNTcgNTMuNTItMTkuNjggMjIuNjR6Ii8+PC9nPjwvc3ZnPg==" + }, + "categories": [ + { + "id": 4, + "name": "Productivity" + }, + { + "id": 6, + "name": "Communication" + } + ], + "displayName": "ClickUp Trigger", + "typeVersion": 1 + } + ] + }, + { + "id": 980, + "name": "Loading Data Into Spreadsheet or Database", + "totalViews": 1553, + "recentViews": 0, + "user": { + "username": "admin" + }, + "createdAt": "2021-03-11T11:20:25.787Z", + "nodes": [ + { + "id": 14, + "icon": "file:apiTemplateIo.svg", + "name": "n8n-nodes-base.apiTemplateIo", + "defaults": { + "name": "APITemplate.io" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4MCIgaGVpZ2h0PSI4MCIgdmlld0JveD0iMCAwIDIzLjE2NyAyMS4xNjciPjxwYXRoIGZpbGw9IiM5MWJjZTUiIGQ9Ik0xOC42MTEuNTA0TDMuNDczIDExLjI0bDQuNTk3IDIuMjEzIDEuMDY2IDMuODgzIDMuMTcxLTIuMjI2IDIuODAyLjk1MXoiLz48cGF0aCBmaWxsPSIjMWYyMTJiIiBkPSJNOS4xNzIgMTcuNTk3YS4yNjQuMjY0IDAgMDEtLjI5LS4xOTJsLTEuMDM1LTMuNzY3LTQuNDg4LTIuMTZhLjI2NS4yNjUgMCAwMS0uMDM5LS40NTRMMTguNDU4LjI4OGEuMjY0LjI2NCAwIDAxLjQxLjI3NGwtMy41IDE1LjU1NmEuMjY1LjI2NSAwIDAxLS4zNDQuMTkzbC0yLjY3NC0uOTA4LTMuMDYxIDIuMTVhLjI3My4yNzMgMCAwMS0uMTE3LjA0NHptLTUuMTgtNi40MDFsNC4xOTMgMi4wMTdjLjA2OS4wMzQuMTIuMDk1LjE0LjE2OWwuOTY3IDMuNTIgMi44NjMtMi4wMWEuMjY1LjI2NSAwIDAxLjIzNy0uMDMzbDIuNTI0Ljg1NkwxOC4yIDEuMTE5eiIvPjxwYXRoIGZpbGw9IiMzYTg0YzEiIGQ9Ik05LjMzOSAxNy4wNzlsLjk0Ni0yLjkxM0wxOC42NDIuNzI4IDguMjI2IDEzLjIzNXoiLz48cGF0aCBmaWxsPSIjMWYyMTJiIiBkPSJNOS4zNTYgMTcuMjFhLjEzMi4xMzIgMCAwMS0uMTQzLS4xNzJsLjk0Ni0yLjkxM2EuMTYuMTYgMCAwMS4wMTQtLjAzbDcuMzkzLTExLjg4OC05LjQxOSAxMS4xNTZhLjEzMy4xMzMgMCAwMS0uMjAyLS4xNzFMMTguNTQuNjQyYS4xMzIuMTMyIDAgMDEuMjE0LjE1NmwtOC4zNDggMTMuNDI0LS45NDEgMi44OThhLjEzMi4xMzIgMCAwMS0uMTA5LjA5em0tMy4yMTUgMS4wNDVjLTEuMTY4LjAyOS0yLjMwMy0uMzczLTMuMDY3LTEuMjg3LS40ODUtLjU4LS42ODMtMS41NzQuMjc1LTEuNzA1Ljg1OS0uMTE5IDEuMjI5LjQ4NSAxLjQ4IDEuMTk2LjgwNCAyLjAyNi4xNiAyLjkzOC0xLjY4IDQuMjcyLS4xNDQuMDkuMDE1LjI5Ni4xNTkuMjA2IDEuMjE4LTEgMS44MjktMS40NjYgMi4xLTIuNDQ4LjEzNC0xLjM2LS40MDItMy4wOTEtMS4zODQtMy40MzYtMS4xMi0uMzgyLTIuMDYzLjUxNi0xLjQ5OSAxLjU4NS42OTkgMS4zMjQgMi4yMyAxLjkxNCAzLjY1MiAxLjg3OC4xNy0uMDA0LjEzMy0uMjY2LS4wMzYtLjI2MXoiLz48cGF0aCBmaWxsPSIjMWYyMTJiIiBkPSJNNy4yMzUgMTguMTRhMTQuOTUgMTQuOTUgMCAwMS0uNzU5LjA5Yy0uMTcuMDE3LS4xMzMuMjc5LjAzNi4yNjIuMjUzLS4wMjUuNTA3LS4wNTIuNzU4LS4wOS4xNjktLjAyNi4xMzMtLjI4OC0uMDM1LS4yNjJ6bTEuMTk0LS41MDRhOC40NiA4LjQ2IDAgMDEtLjg2NS4zNDRjLS4xNjIuMDU0LS4wNTcuMjk3LjEwNC4yNDNhOC4zNyA4LjM3IDAgMDAuODY1LS4zNDRjLjE1NC0uMDcxLjA1LS4zMTUtLjEwNC0uMjQzeiIvPjxwYXRoIGQ9Ik0xMi41OTkgMTUuMjJhLjEzMS4xMzEgMCAwMS0uMDctLjAxbC0yLjE2OC0uOTI0YS4xMzMuMTMzIDAgMDEuMTAzLS4yNDNsMi4xNjkuOTIzYS4xMzMuMTMzIDAgMDEtLjAzNC4yNTN6Ii8+PC9zdmc+" + }, + "categories": [ + { + "id": 1, + "name": "Marketing" + } + ], + "displayName": "APITemplate.io", + "typeVersion": 1 + }, + { + "id": 26, + "icon": "file:sns.svg", + "name": "n8n-nodes-base.awsSnsTrigger", + "defaults": { + "name": "AWS SNS Trigger" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQ5MCIgaGVpZ2h0PSIyNTAwIiB2aWV3Qm94PSIwIDAgMjU2IDI1NyIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWlkWU1pZCI+PHBhdGggZD0iTTk4Ljg3NSAyMzIuMDMzbC0yNi40MzMtNy40MDgtMjUuMDAxLTI4LjUwOCAzMS4yNzItLjg2MyAyMC4xNjIgMzYuNzc5bS02MS4xMjUtMTguOGwtMTQuODc1LTQuMTY2LTE0LjA1OC0xNi4wMzQgMTcuMDgyLTIuODA5IDExLjg1MSAyMy4wMDkiIGZpbGw9IiM5OTVCODAiLz48cGF0aCBkPSJNMCAxOTEuMDE3bDE1LjIwNCAzLjA5MSAyLjIwNy0zLjg4MlY1OC41MDNsLTIuMjA3LTIuNTYxTDAgNjQuNnYxMjYuNDE3IiBmaWxsPSIjN0IzRjY1Ii8+PHBhdGggZD0iTTczLjkzMyA2OS43MDhMMTUuMjA4IDU1Ljk0MnYxMzguMTY2bDguNzk4LS44MTggMTMuNzQ0IDE5Ljk0MyAxMC42LTIyLjIwNSAyNS41ODMtMi4zNzhWNjkuNzA4IiBmaWxsPSIjQzE3QjlEIi8+PHBhdGggZD0iTTMzLjk1OCAxOTguMTMzbDI2LjA2MyA1LjI1IDEuNzE2LTQuMDQ1VjM3LjQ0bC0xLjcxNi0zLjY2NS0yNi4wNjMgMTMuMjA4djE1MS4xNSIgZmlsbD0iIzdCM0Y2NSIvPjxwYXRoIGQ9Ik0yMDguNzM0IDgxLjUxNkw2MC4wMjEgMzMuNzc1djE2OS42MTJsMTcuMjIxLTIuMjE2IDIxLjYzMyAzMC44NjIgMTcuMTI2LTM1Ljg1IDkyLjczMy0xMS45MzNWODEuNTE2IiBmaWxsPSIjQzE3QjlEIi8+PHBhdGggZD0iTTE4MS44MzMgMjU2LjQ5MmwtMzcuNTY2LTEwLjUyNS0zNS41MDktNDAuNSA0Ni4wMzMtLjQ2OCAyNy4wNDIgNTEuNDkzIiBmaWxsPSIjOTk1QjgwIi8+PHBhdGggZD0iTTg5LjU5MSAyMDguOTVsMzguMzMgNy40MTcgMi45NzctMi41NjZWNC4xMTdMMTI3LjkyMSAwbC0zOC4zMyAxOS4xNThWMjA4Ljk1IiBmaWxsPSIjN0IzRjY1Ii8+PHBhdGggZD0iTTI1NiA2NC4wMzNMMTI3LjkyNSAwdjIxNi4zNjdsMjIuNTk3LTQuNTI4IDMxLjMxMSA0NC42NTMgMjYuOTAxLTU2LjMwOS0uMDE3LS4wMDJMMjU2IDE5MC43MDhWNjQuMDMzIiBmaWxsPSIjQzE3QjlEIi8+PC9zdmc+" + }, + "categories": [ + { + "id": 5, + "name": "Development" + }, + { + "id": 6, + "name": "Communication" + } + ], + "displayName": "AWS SNS Trigger", + "typeVersion": 1 + }, + { + "id": 38, + "icon": "file:chargebee.png", + "name": "n8n-nodes-base.chargebeeTrigger", + "defaults": { + "name": "Chargebee Trigger" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAA8CAMAAAANIilAAAAAk1BMVEUAAAD9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azXS0qjqAAAAMHRSTlMAAta1CPr3HAyb8mNPLBLq5MmGZ1wi28+soEQX7pZ9dkk/MijDurCMims54b6lb1YyU8hoAAACDklEQVRIx+2V2bKiMBBAmyTs+w6CIi64O/n/rxtKJwIjJPA4Nfc8acOpTnfSAX7493G2QZN6Zeml+GwVS0xiNSXtYeBIm6lKEaJf3C5kjrvz6SioFruVTKcIJEG1D8oBa1wXUy6+w2lVxhFFuU+0j4KOjwdOjUHQnHJ/DVZ4iaXXatTo2OuhPuEWXm9fLOhxPQjldeeuyV/NqGT+su1ucZuRkt5PMRElDmCEbdK2MNcmKnaZi2EUrd7GMEHEXGMPi8mYfFruqjd2NLrE+/P9oF9nyCuW+P4JhcorcBbL4dc2WSxyEcoBe9Vi5yJlEc8RyTp7ldVo0w8rkZyzN3ddExjR4sw7TmZhzSRhEddZ3m2TRar5+3z8hDZ/xlMSnzCPnbDu9NcPhLItzKAZG0hJnHVYYhnDYtSS2RksJ+fcQi0qAbDtKXknU84oWPQJtizvxanpmcCQJ3VtiA1lUlbdzk7rfs/bzwFSVR/bsaDhjHukvvfJsbBXm8S+UboXTwfDQFjXG6S0/dde18oGrN20TI4DOzHaA3drzAJWPqVy2Fa+5qTWDn05AKJJbS+eafsHtUlRUgAHB/dkJT+ddJzI7U+3kopjVgiGUwroF7J/IWAac+7RFeo0D6X3daQCiRBNZs2XVrmDr4e2zQ1aVhrMwwl9+Z1Zzw42ptQPHZiPFJv5IXENWXGvdRhL8MN/y2+uRpQ1fWz5HAAAAABJRU5ErkJggg==" + }, + "categories": [ + { + "id": 8, + "name": "Finance & Accounting" + } + ], + "displayName": "Chargebee Trigger", + "typeVersion": 1 + }, + { + "id": 42, + "icon": "file:clickup.svg", + "name": "n8n-nodes-base.clickUpTrigger", + "defaults": { + "name": "ClickUp Trigger" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSItMTAgMCAxNTUgMTU1IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxkZWZzPjxsaW5lYXJHcmFkaWVudCB4MT0iMCUiIHkxPSI2OC4wMSUiIHkyPSI2OC4wMSUiIGlkPSJhIj48c3RvcCBzdG9wLWNvbG9yPSIjODkzMEZEIiBvZmZzZXQ9IjAlIi8+PHN0b3Agc3RvcC1jb2xvcj0iIzQ5Q0NGOSIgb2Zmc2V0PSIxMDAlIi8+PC9saW5lYXJHcmFkaWVudD48bGluZWFyR3JhZGllbnQgeDE9IjAlIiB5MT0iNjguMDElIiB5Mj0iNjguMDElIiBpZD0iYiI+PHN0b3Agc3RvcC1jb2xvcj0iI0ZGMDJGMCIgb2Zmc2V0PSIwJSIvPjxzdG9wIHN0b3AtY29sb3I9IiNGRkM4MDAiIG9mZnNldD0iMTAwJSIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxnIGZpbGw9Im5vbmUiPjxwYXRoIGQ9Ik0uNCAxMTkuMTJsMjMuODEtMTguMjRDMzYuODYgMTE3LjM5IDUwLjMgMTI1IDY1LjI2IDEyNWMxNC44OCAwIDI3Ljk0LTcuNTIgNDAuMDItMjMuOWwyNC4xNSAxNy44QzExMiAxNDIuNTIgOTAuMzQgMTU1IDY1LjI2IDE1NWMtMjUgMC00Ni44Ny0xMi40LTY0Ljg2LTM1Ljg4eiIgZmlsbD0idXJsKCNhKSIvPjxwYXRoIGZpbGw9InVybCgjYikiIGQ9Ik02NS4xOCAzOS44NEwyMi44IDc2LjM2IDMuMjEgNTMuNjQgNjUuMjcuMTZsNjEuNTcgNTMuNTItMTkuNjggMjIuNjR6Ii8+PC9nPjwvc3ZnPg==" + }, + "categories": [ + { + "id": 4, + "name": "Productivity" + }, + { + "id": 6, + "name": "Communication" + } + ], + "displayName": "ClickUp Trigger", + "typeVersion": 1 + } + ] + }, + { + "id": 225, + "name": "Trending \"Show HN\" to email", + "totalViews": 1459, + "recentViews": 0, + "user": { + "username": "admin" + }, + "createdAt": "2020-01-01T18:56:31.672Z", + "nodes": [ + { + "id": 7, + "icon": "file:airtable.svg", + "name": "n8n-nodes-base.airtable", + "defaults": { + "name": "Airtable" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyMDAgMTcwIj48cGF0aCBkPSJNODkgNC44TDE2LjIgMzQuOWMtNC4xIDEuNy00IDcuNC4xIDkuMWw3My4yIDI5YzYuNCAyLjYgMTMuNiAyLjYgMjAgMGw3My4yLTI5YzQuMS0xLjYgNC4xLTcuNC4xLTkuMWwtNzMtMzAuMUMxMDMuMiAyIDk1LjcgMiA4OSA0LjgiIGZpbGw9IiNmY2I0MDAiLz48cGF0aCBkPSJNMTA1LjkgODguOXY3Mi41YzAgMy40IDMuNSA1LjggNi43IDQuNWw4MS42LTMxLjdjMS45LS43IDMuMS0yLjUgMy4xLTQuNVY1Ny4yYzAtMy40LTMuNS01LjgtNi43LTQuNUwxMDkgODQuM2MtMS45LjgtMy4xIDIuNi0zLjEgNC42IiBmaWxsPSIjMThiZmZmIi8+PHBhdGggZD0iTTg2LjkgOTIuNmwtMjQuMiAxMS43LTIuNSAxLjJMOS4xIDEzMGMtMy4yIDEuNi03LjQtLjgtNy40LTQuNFY1Ny41YzAtMS4zLjctMi40IDEuNi0zLjMuNC0uNC44LS43IDEuMi0uOSAxLjItLjcgMy0uOSA0LjQtLjNsNzcuNSAzMC43YzQgMS41IDQuMyA3LjEuNSA4LjkiIGZpbGw9IiNmODJiNjAiLz48cGF0aCBkPSJNODYuOSA5Mi42bC0yNC4yIDExLjctNTkuNC01MGMuNC0uNC44LS43IDEuMi0uOSAxLjItLjcgMy0uOSA0LjQtLjNsNzcuNSAzMC43YzQgMS40IDQuMyA3IC41IDguOCIgZmlsbD0iI2JhMWU0NSIvPjwvc3ZnPg==" + }, + "categories": [ + { + "id": 3, + "name": "Data & Storage" + } + ], + "displayName": "Airtable", + "typeVersion": 2 + }, + { + "id": 11, + "icon": "file:amqp.png", + "name": "n8n-nodes-base.amqpTrigger", + "defaults": { + "name": "AMQP Trigger" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAA8CAYAAAA6/NlyAAAB7UlEQVRoge2W4W3CMBCFj26QjkBHSEdIR4AR6Ah0BBgBRqAjhBFgBBghHaEVlV29PN0lDr+o9D7JEjhn+975bJ8JIYQQQgghhBBCCCGEEA9CY2bf0NaBW2uyu7UN2XSOzTyY60J2BzNbObbsH7eTmS2mhHJHE1wmCD7A93ngEAquHaHc2omCcysSXQW74g32BHfwfTEiuCoQm9vuDsEndPYpELxKjjBj0foCEXX6XdM3by3c7aOZPZvZzMzeaBzbIh9pzIuZXaG/RqNIMAq7Ur8XCHQ2kx3LC56DMQ39X4LI23zbAd88ruRHD09wTVF5p+/eBZI5g7O8w5FgXOvsZAI7PxRwS4HGIPbm8wRjBL/Sgp/QNyQYHWySmOxgJBgFeGnPfZHgDVyufET+YMEVCdo7gziCTBbGmRKlGQpCMXOnj+1L6B0JFsxndO3cjjZyjo6OnZeqGb5gqhTQS3qKeK1SwbesfB3IrF/awqu+g8Dgs5SLE37SciHiPUv8rLVp7k2wdl63tDDqgTs8lqpINWGXbSTKe9rlJgXME7C9I6V7oGAWsEzv2gzeN2TstkbCZyIJWBYKWUwtF4foKGU9TpRGdZDSdVDpDNXSVVBLt5TeucS9K6X/E3USX3rshBBCCCGEEEIIIYQQ4tExsx8PuuPnwhCIbgAAAABJRU5ErkJggg==" + }, + "categories": [ + { + "id": 5, + "name": "Development" + }, + { + "id": 6, + "name": "Communication" + } + ], + "displayName": "AMQP Trigger", + "typeVersion": 1 + }, + { + "id": 14, + "icon": "file:apiTemplateIo.svg", + "name": "n8n-nodes-base.apiTemplateIo", + "defaults": { + "name": "APITemplate.io" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4MCIgaGVpZ2h0PSI4MCIgdmlld0JveD0iMCAwIDIzLjE2NyAyMS4xNjciPjxwYXRoIGZpbGw9IiM5MWJjZTUiIGQ9Ik0xOC42MTEuNTA0TDMuNDczIDExLjI0bDQuNTk3IDIuMjEzIDEuMDY2IDMuODgzIDMuMTcxLTIuMjI2IDIuODAyLjk1MXoiLz48cGF0aCBmaWxsPSIjMWYyMTJiIiBkPSJNOS4xNzIgMTcuNTk3YS4yNjQuMjY0IDAgMDEtLjI5LS4xOTJsLTEuMDM1LTMuNzY3LTQuNDg4LTIuMTZhLjI2NS4yNjUgMCAwMS0uMDM5LS40NTRMMTguNDU4LjI4OGEuMjY0LjI2NCAwIDAxLjQxLjI3NGwtMy41IDE1LjU1NmEuMjY1LjI2NSAwIDAxLS4zNDQuMTkzbC0yLjY3NC0uOTA4LTMuMDYxIDIuMTVhLjI3My4yNzMgMCAwMS0uMTE3LjA0NHptLTUuMTgtNi40MDFsNC4xOTMgMi4wMTdjLjA2OS4wMzQuMTIuMDk1LjE0LjE2OWwuOTY3IDMuNTIgMi44NjMtMi4wMWEuMjY1LjI2NSAwIDAxLjIzNy0uMDMzbDIuNTI0Ljg1NkwxOC4yIDEuMTE5eiIvPjxwYXRoIGZpbGw9IiMzYTg0YzEiIGQ9Ik05LjMzOSAxNy4wNzlsLjk0Ni0yLjkxM0wxOC42NDIuNzI4IDguMjI2IDEzLjIzNXoiLz48cGF0aCBmaWxsPSIjMWYyMTJiIiBkPSJNOS4zNTYgMTcuMjFhLjEzMi4xMzIgMCAwMS0uMTQzLS4xNzJsLjk0Ni0yLjkxM2EuMTYuMTYgMCAwMS4wMTQtLjAzbDcuMzkzLTExLjg4OC05LjQxOSAxMS4xNTZhLjEzMy4xMzMgMCAwMS0uMjAyLS4xNzFMMTguNTQuNjQyYS4xMzIuMTMyIDAgMDEuMjE0LjE1NmwtOC4zNDggMTMuNDI0LS45NDEgMi44OThhLjEzMi4xMzIgMCAwMS0uMTA5LjA5em0tMy4yMTUgMS4wNDVjLTEuMTY4LjAyOS0yLjMwMy0uMzczLTMuMDY3LTEuMjg3LS40ODUtLjU4LS42ODMtMS41NzQuMjc1LTEuNzA1Ljg1OS0uMTE5IDEuMjI5LjQ4NSAxLjQ4IDEuMTk2LjgwNCAyLjAyNi4xNiAyLjkzOC0xLjY4IDQuMjcyLS4xNDQuMDkuMDE1LjI5Ni4xNTkuMjA2IDEuMjE4LTEgMS44MjktMS40NjYgMi4xLTIuNDQ4LjEzNC0xLjM2LS40MDItMy4wOTEtMS4zODQtMy40MzYtMS4xMi0uMzgyLTIuMDYzLjUxNi0xLjQ5OSAxLjU4NS42OTkgMS4zMjQgMi4yMyAxLjkxNCAzLjY1MiAxLjg3OC4xNy0uMDA0LjEzMy0uMjY2LS4wMzYtLjI2MXoiLz48cGF0aCBmaWxsPSIjMWYyMTJiIiBkPSJNNy4yMzUgMTguMTRhMTQuOTUgMTQuOTUgMCAwMS0uNzU5LjA5Yy0uMTcuMDE3LS4xMzMuMjc5LjAzNi4yNjIuMjUzLS4wMjUuNTA3LS4wNTIuNzU4LS4wOS4xNjktLjAyNi4xMzMtLjI4OC0uMDM1LS4yNjJ6bTEuMTk0LS41MDRhOC40NiA4LjQ2IDAgMDEtLjg2NS4zNDRjLS4xNjIuMDU0LS4wNTcuMjk3LjEwNC4yNDNhOC4zNyA4LjM3IDAgMDAuODY1LS4zNDRjLjE1NC0uMDcxLjA1LS4zMTUtLjEwNC0uMjQzeiIvPjxwYXRoIGQ9Ik0xMi41OTkgMTUuMjJhLjEzMS4xMzEgMCAwMS0uMDctLjAxbC0yLjE2OC0uOTI0YS4xMzMuMTMzIDAgMDEuMTAzLS4yNDNsMi4xNjkuOTIzYS4xMzMuMTMzIDAgMDEtLjAzNC4yNTN6Ii8+PC9zdmc+" + }, + "categories": [ + { + "id": 1, + "name": "Marketing" + } + ], + "displayName": "APITemplate.io", + "typeVersion": 1 + }, + { + "id": 19, + "icon": "file:autopilot.svg", + "name": "n8n-nodes-base.autopilotTrigger", + "defaults": { + "name": "Autopilot Trigger" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjM4IDI2IDM1IDM1Ij48Y2lyY2xlIGN4PSI1MCIgY3k9IjUwIiByPSI0MCIgc3Ryb2tlPSIjMThkNGIyIiBzdHJva2Utd2lkdGg9IjMiIGZpbGw9IiMxOGQ0YjIiLz48cGF0aCBmaWxsPSIjZmZmIiBkPSJNNDUuNCA0Mi42aDE5LjlsMy40LTQuOEg0MmwzLjQgNC44em0zLjEgOC4zaDEzLjFsMy40LTQuOEg0NS40bDMuMSA0Ljh6bTU0LS43Ii8+PC9zdmc+" + }, + "categories": [ + { + "id": 1, + "name": "Marketing" + } + ], + "displayName": "Autopilot Trigger", + "typeVersion": 1 + }, + { + "id": 20, + "icon": "file:lambda.svg", + "name": "n8n-nodes-base.awsLambda", + "defaults": { + "name": "AWS Lambda" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjI1MDAiIHZpZXdCb3g9Ii0zLjAyMyAtMC4yMiA0MjAuOTIzIDQzMy41NCIgd2lkdGg9IjI0NDMiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTIwOC40NSAyMjcuODljLTEuNTkgMi4yNi0yLjkzIDQuMTItNC4yMiA2cS0zMC44NiA0NS40Mi02MS43IDkwLjgzLTI4LjY5IDQyLjI0LTU3LjQ0IDg0LjQzYTMuODggMy44OCAwIDAxLTIuNzMgMS41OXEtNDAuNTktLjM1LTgxLjE2LS44OGMtLjMgMC0uNjEtLjA5LTEuMi0uMThhMTQuNDQgMTQuNDQgMCAwMS43Ni0xLjY1cTI4LjMxLTQzLjg5IDU2LjYyLTg3Ljc2IDI1LjExLTM4Ljg4IDUwLjI1LTc3Ljc0IDI3Ljg2LTQzLjE4IDU1LjY5LTg2LjQyYzIuNzQtNC4yNSA1LjU5LTguNDIgOC4xOS0xMi43NWE1LjI2IDUuMjYgMCAwMC41Ni0zLjgzYy01LTE1Ljk0LTEwLjEtMzEuODQtMTUuMTktNDcuNzQtMi4xOC02LjgxLTQuNDYtMTMuNTgtNi41LTIwLjQzLS42Ni0yLjItMS43NS0yLjg3LTQtMi44Ni0xNyAuMDctMzMuOS4wNS01MC44NS4wNS0zLjIyIDAtMy4yMyAwLTMuMjMtMy4xOCAwLTIwLjg0IDAtNDEuNjgtLjA2LTYyLjUyIDAtMi4zMi43Ni0yLjg0IDIuOTQtMi44NHE1MS4xOS4wOSAxMDIuNCAwYTMuMjkgMy4yOSAwIDAxMy42IDIuNDNxMjcgNjcuOTEgNTQgMTM1Ljc3IDMxLjUgNzkuMTQgNjMgMTU4LjNjNi41MiAxNi4zOCAxMy4wOSAzMi43NSAxOS41NCA0OS4xNy43NyAyIDEuNTcgMi4zOCAzLjU5IDEuNzYgMTcuODktNS41MyAzNS44Mi0xMC45MSA1My43LTE2LjQ1IDIuMjUtLjcgMy4wNy0uMjMgMy43NyAyIDYuMSAxOS4xNyAxMi4zMiAzOC4zIDE4LjUgNTcuNDUuMjEuNjYuMzcgMS4zMy42MiAyLjI1LTEuMjguNDctMi40OCAxLTMuNzEgMS4zNHEtNjEgMTkuMzMtMTIxLjkzIDM4LjY4Yy0xLjk0LjYxLTIuNTItLjA1LTMuMTctMS42OHEtMTguNjEtNDcuMTYtMzcuMzEtOTQuMjgtMTguMjktNDYuMTQtMzYuNi05Mi4yOGMtMS44My00LjYyLTMuNjMtOS4yNi01LjQ2LTEzLjg4LS4yOS0uNzktLjY5LTEuNDgtMS4yNy0yLjd6IiBmaWxsPSIjZmE3ZTE0Ii8+PC9zdmc+" + }, + "categories": [ + { + "id": 5, + "name": "Development" + } + ], + "displayName": "AWS Lambda", + "typeVersion": 1 + }, + { + "id": 114, + "icon": "file:helpScout.svg", + "name": "n8n-nodes-base.helpScoutTrigger", + "defaults": { + "name": "HelpScout Trigger" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjU2IiBoZWlnaHQ9IjMxMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWlkWU1pZCI+PHBhdGggZD0iTTE4LjQzMiAxODAuOTY5bDkwLjQ4NC05MC40ODVhNjMuNzE3IDYzLjcxNyAwIDAwMTguOTktNDUuNDI4QTY0LjI0NSA2NC4yNDUgMCAwMDEwOS40NzYgMEwxOC45OSA5MC40ODRBNjMuNzE3IDYzLjcxNyAwIDAwMCAxMzUuOTEzYzAgMTcuNjg3IDcuMDc1IDMzLjUxMiAxOC40MzIgNDUuMDU2em0yMTkuMTM2LTUyLjg3NmwtOTAuNDg0IDkwLjQ4NGE2My43MTcgNjMuNzE3IDAgMDAtMTguOTkgNDUuNDI5IDY0LjI0NSA2NC4yNDUgMCAwMDE4LjQzMSA0NS4wNTZsOTAuNDg0LTkwLjQ4NUE2My43MTcgNjMuNzE3IDAgMDAyNTYgMTczLjE1YzAtMTcuNjg3LTcuMDc1LTMzLjUxMy0xOC40MzItNDUuMDU2em0tLjU1OS0zNy40MjJBNjMuNzE3IDYzLjcxNyAwIDAwMjU2IDQ1LjI0MiA2NC4yNDUgNjQuMjQ1IDAgMDAyMzcuNTY4LjE4NkwxOC45OTEgMjE4LjU3N0M3LjI2IDIzMC4zMDcgMCAyNDYuMzIgMCAyNjQuMTkyYTY0LjI0NSA2NC4yNDUgMCAwMDE4LjQzMiA0NS4wNTZMMjM3LjAwOSA5MC42NzF6IiBmaWxsPSIjMTI5MkVFIi8+PC9zdmc+" + }, + "categories": [ + { + "id": 6, + "name": "Communication" + } + ], + "displayName": "HelpScout Trigger", + "typeVersion": 1 + } + ] + }, + { + "id": 524, + "name": "Get today's date and day using the Function node", + "totalViews": 1354, + "recentViews": 0, + "user": { + "username": "admin" + }, + "createdAt": "2020-07-16T09:26:32.454Z", + "nodes": [ + { + "id": 14, + "icon": "file:apiTemplateIo.svg", + "name": "n8n-nodes-base.apiTemplateIo", + "defaults": { + "name": "APITemplate.io" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4MCIgaGVpZ2h0PSI4MCIgdmlld0JveD0iMCAwIDIzLjE2NyAyMS4xNjciPjxwYXRoIGZpbGw9IiM5MWJjZTUiIGQ9Ik0xOC42MTEuNTA0TDMuNDczIDExLjI0bDQuNTk3IDIuMjEzIDEuMDY2IDMuODgzIDMuMTcxLTIuMjI2IDIuODAyLjk1MXoiLz48cGF0aCBmaWxsPSIjMWYyMTJiIiBkPSJNOS4xNzIgMTcuNTk3YS4yNjQuMjY0IDAgMDEtLjI5LS4xOTJsLTEuMDM1LTMuNzY3LTQuNDg4LTIuMTZhLjI2NS4yNjUgMCAwMS0uMDM5LS40NTRMMTguNDU4LjI4OGEuMjY0LjI2NCAwIDAxLjQxLjI3NGwtMy41IDE1LjU1NmEuMjY1LjI2NSAwIDAxLS4zNDQuMTkzbC0yLjY3NC0uOTA4LTMuMDYxIDIuMTVhLjI3My4yNzMgMCAwMS0uMTE3LjA0NHptLTUuMTgtNi40MDFsNC4xOTMgMi4wMTdjLjA2OS4wMzQuMTIuMDk1LjE0LjE2OWwuOTY3IDMuNTIgMi44NjMtMi4wMWEuMjY1LjI2NSAwIDAxLjIzNy0uMDMzbDIuNTI0Ljg1NkwxOC4yIDEuMTE5eiIvPjxwYXRoIGZpbGw9IiMzYTg0YzEiIGQ9Ik05LjMzOSAxNy4wNzlsLjk0Ni0yLjkxM0wxOC42NDIuNzI4IDguMjI2IDEzLjIzNXoiLz48cGF0aCBmaWxsPSIjMWYyMTJiIiBkPSJNOS4zNTYgMTcuMjFhLjEzMi4xMzIgMCAwMS0uMTQzLS4xNzJsLjk0Ni0yLjkxM2EuMTYuMTYgMCAwMS4wMTQtLjAzbDcuMzkzLTExLjg4OC05LjQxOSAxMS4xNTZhLjEzMy4xMzMgMCAwMS0uMjAyLS4xNzFMMTguNTQuNjQyYS4xMzIuMTMyIDAgMDEuMjE0LjE1NmwtOC4zNDggMTMuNDI0LS45NDEgMi44OThhLjEzMi4xMzIgMCAwMS0uMTA5LjA5em0tMy4yMTUgMS4wNDVjLTEuMTY4LjAyOS0yLjMwMy0uMzczLTMuMDY3LTEuMjg3LS40ODUtLjU4LS42ODMtMS41NzQuMjc1LTEuNzA1Ljg1OS0uMTE5IDEuMjI5LjQ4NSAxLjQ4IDEuMTk2LjgwNCAyLjAyNi4xNiAyLjkzOC0xLjY4IDQuMjcyLS4xNDQuMDkuMDE1LjI5Ni4xNTkuMjA2IDEuMjE4LTEgMS44MjktMS40NjYgMi4xLTIuNDQ4LjEzNC0xLjM2LS40MDItMy4wOTEtMS4zODQtMy40MzYtMS4xMi0uMzgyLTIuMDYzLjUxNi0xLjQ5OSAxLjU4NS42OTkgMS4zMjQgMi4yMyAxLjkxNCAzLjY1MiAxLjg3OC4xNy0uMDA0LjEzMy0uMjY2LS4wMzYtLjI2MXoiLz48cGF0aCBmaWxsPSIjMWYyMTJiIiBkPSJNNy4yMzUgMTguMTRhMTQuOTUgMTQuOTUgMCAwMS0uNzU5LjA5Yy0uMTcuMDE3LS4xMzMuMjc5LjAzNi4yNjIuMjUzLS4wMjUuNTA3LS4wNTIuNzU4LS4wOS4xNjktLjAyNi4xMzMtLjI4OC0uMDM1LS4yNjJ6bTEuMTk0LS41MDRhOC40NiA4LjQ2IDAgMDEtLjg2NS4zNDRjLS4xNjIuMDU0LS4wNTcuMjk3LjEwNC4yNDNhOC4zNyA4LjM3IDAgMDAuODY1LS4zNDRjLjE1NC0uMDcxLjA1LS4zMTUtLjEwNC0uMjQzeiIvPjxwYXRoIGQ9Ik0xMi41OTkgMTUuMjJhLjEzMS4xMzEgMCAwMS0uMDctLjAxbC0yLjE2OC0uOTI0YS4xMzMuMTMzIDAgMDEuMTAzLS4yNDNsMi4xNjkuOTIzYS4xMzMuMTMzIDAgMDEtLjAzNC4yNTN6Ii8+PC9zdmc+" + }, + "categories": [ + { + "id": 1, + "name": "Marketing" + } + ], + "displayName": "APITemplate.io", + "typeVersion": 1 + }, + { + "id": 42, + "icon": "file:clickup.svg", + "name": "n8n-nodes-base.clickUpTrigger", + "defaults": { + "name": "ClickUp Trigger" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSItMTAgMCAxNTUgMTU1IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxkZWZzPjxsaW5lYXJHcmFkaWVudCB4MT0iMCUiIHkxPSI2OC4wMSUiIHkyPSI2OC4wMSUiIGlkPSJhIj48c3RvcCBzdG9wLWNvbG9yPSIjODkzMEZEIiBvZmZzZXQ9IjAlIi8+PHN0b3Agc3RvcC1jb2xvcj0iIzQ5Q0NGOSIgb2Zmc2V0PSIxMDAlIi8+PC9saW5lYXJHcmFkaWVudD48bGluZWFyR3JhZGllbnQgeDE9IjAlIiB5MT0iNjguMDElIiB5Mj0iNjguMDElIiBpZD0iYiI+PHN0b3Agc3RvcC1jb2xvcj0iI0ZGMDJGMCIgb2Zmc2V0PSIwJSIvPjxzdG9wIHN0b3AtY29sb3I9IiNGRkM4MDAiIG9mZnNldD0iMTAwJSIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxnIGZpbGw9Im5vbmUiPjxwYXRoIGQ9Ik0uNCAxMTkuMTJsMjMuODEtMTguMjRDMzYuODYgMTE3LjM5IDUwLjMgMTI1IDY1LjI2IDEyNWMxNC44OCAwIDI3Ljk0LTcuNTIgNDAuMDItMjMuOWwyNC4xNSAxNy44QzExMiAxNDIuNTIgOTAuMzQgMTU1IDY1LjI2IDE1NWMtMjUgMC00Ni44Ny0xMi40LTY0Ljg2LTM1Ljg4eiIgZmlsbD0idXJsKCNhKSIvPjxwYXRoIGZpbGw9InVybCgjYikiIGQ9Ik02NS4xOCAzOS44NEwyMi44IDc2LjM2IDMuMjEgNTMuNjQgNjUuMjcuMTZsNjEuNTcgNTMuNTItMTkuNjggMjIuNjR6Ii8+PC9nPjwvc3ZnPg==" + }, + "categories": [ + { + "id": 4, + "name": "Productivity" + }, + { + "id": 6, + "name": "Communication" + } + ], + "displayName": "ClickUp Trigger", + "typeVersion": 1 + } + ] + } + ], + "filters": [ + { + "counts": [ + { + "count": 66, + "highlighted": "Building Blocks", + "value": "Building Blocks" + }, + { + "count": 24, + "highlighted": "Sales", + "value": "Sales" + }, + { + "count": 24, + "highlighted": "Marketing & Growth", + "value": "Marketing & Growth" + }, + { + "count": 16, + "highlighted": "DevOps & IT", + "value": "DevOps & IT" + }, + { + "count": 12, + "highlighted": "HR & People Ops", + "value": "HR & People Ops" + }, + { + "count": 9, + "highlighted": "Development", + "value": "Development" + }, + { + "count": 9, + "highlighted": "Managed Service Providers", + "value": "Managed Service Providers" + }, + { + "count": 7, + "highlighted": "Finance & Accounting", + "value": "Finance & Accounting" + }, + { + "count": 6, + "highlighted": "Product & Project Management", + "value": "Product & Project Management" + }, + { + "count": 3, + "highlighted": "Customer Service", + "value": "Customer Service" + } + ], + "field_name": "categories", + "sampled": false, + "stats": { + "total_values": 11 + } + } + ] +} diff --git a/cypress/fixtures/templates_search/sales_templates_search_response.json b/cypress/fixtures/templates_search/sales_templates_search_response.json new file mode 100644 index 0000000000..4efbb3585b --- /dev/null +++ b/cypress/fixtures/templates_search/sales_templates_search_response.json @@ -0,0 +1,1316 @@ +{ + "totalWorkflows": 24, + "workflows": [ + { + "id": 837, + "name": "Automating Products Price Changes Tracking", + "totalViews": 1343, + "recentViews": 0, + "user": { + "username": "admin" + }, + "createdAt": "2020-12-19T10:26:27.570Z", + "nodes": [ + { + "id": 7, + "icon": "file:airtable.svg", + "name": "n8n-nodes-base.airtable", + "defaults": { + "name": "Airtable" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyMDAgMTcwIj48cGF0aCBkPSJNODkgNC44TDE2LjIgMzQuOWMtNC4xIDEuNy00IDcuNC4xIDkuMWw3My4yIDI5YzYuNCAyLjYgMTMuNiAyLjYgMjAgMGw3My4yLTI5YzQuMS0xLjYgNC4xLTcuNC4xLTkuMWwtNzMtMzAuMUMxMDMuMiAyIDk1LjcgMiA4OSA0LjgiIGZpbGw9IiNmY2I0MDAiLz48cGF0aCBkPSJNMTA1LjkgODguOXY3Mi41YzAgMy40IDMuNSA1LjggNi43IDQuNWw4MS42LTMxLjdjMS45LS43IDMuMS0yLjUgMy4xLTQuNVY1Ny4yYzAtMy40LTMuNS01LjgtNi43LTQuNUwxMDkgODQuM2MtMS45LjgtMy4xIDIuNi0zLjEgNC42IiBmaWxsPSIjMThiZmZmIi8+PHBhdGggZD0iTTg2LjkgOTIuNmwtMjQuMiAxMS43LTIuNSAxLjJMOS4xIDEzMGMtMy4yIDEuNi03LjQtLjgtNy40LTQuNFY1Ny41YzAtMS4zLjctMi40IDEuNi0zLjMuNC0uNC44LS43IDEuMi0uOSAxLjItLjcgMy0uOSA0LjQtLjNsNzcuNSAzMC43YzQgMS41IDQuMyA3LjEuNSA4LjkiIGZpbGw9IiNmODJiNjAiLz48cGF0aCBkPSJNODYuOSA5Mi42bC0yNC4yIDExLjctNTkuNC01MGMuNC0uNC44LS43IDEuMi0uOSAxLjItLjcgMy0uOSA0LjQtLjNsNzcuNSAzMC43YzQgMS40IDQuMyA3IC41IDguOCIgZmlsbD0iI2JhMWU0NSIvPjwvc3ZnPg==" + }, + "categories": [ + { + "id": 3, + "name": "Data & Storage" + } + ], + "displayName": "Airtable", + "typeVersion": 2 + }, + { + "id": 11, + "icon": "file:amqp.png", + "name": "n8n-nodes-base.amqpTrigger", + "defaults": { + "name": "AMQP Trigger" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAA8CAYAAAA6/NlyAAAB7UlEQVRoge2W4W3CMBCFj26QjkBHSEdIR4AR6Ah0BBgBRqAjhBFgBBghHaEVlV29PN0lDr+o9D7JEjhn+975bJ8JIYQQQgghhBBCCCGEEA9CY2bf0NaBW2uyu7UN2XSOzTyY60J2BzNbObbsH7eTmS2mhHJHE1wmCD7A93ngEAquHaHc2omCcysSXQW74g32BHfwfTEiuCoQm9vuDsEndPYpELxKjjBj0foCEXX6XdM3by3c7aOZPZvZzMzeaBzbIh9pzIuZXaG/RqNIMAq7Ur8XCHQ2kx3LC56DMQ39X4LI23zbAd88ruRHD09wTVF5p+/eBZI5g7O8w5FgXOvsZAI7PxRwS4HGIPbm8wRjBL/Sgp/QNyQYHWySmOxgJBgFeGnPfZHgDVyufET+YMEVCdo7gziCTBbGmRKlGQpCMXOnj+1L6B0JFsxndO3cjjZyjo6OnZeqGb5gqhTQS3qKeK1SwbesfB3IrF/awqu+g8Dgs5SLE37SciHiPUv8rLVp7k2wdl63tDDqgTs8lqpINWGXbSTKe9rlJgXME7C9I6V7oGAWsEzv2gzeN2TstkbCZyIJWBYKWUwtF4foKGU9TpRGdZDSdVDpDNXSVVBLt5TeucS9K6X/E3USX3rshBBCCCGEEEIIIYQQ4tExsx8PuuPnwhCIbgAAAABJRU5ErkJggg==" + }, + "categories": [ + { + "id": 5, + "name": "Development" + }, + { + "id": 6, + "name": "Communication" + } + ], + "displayName": "AMQP Trigger", + "typeVersion": 1 + }, + { + "id": 13, + "icon": "file:asana.svg", + "name": "n8n-nodes-base.asanaTrigger", + "defaults": { + "name": "Asana Trigger" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNjAiIGhlaWdodD0iNjAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PHJhZGlhbEdyYWRpZW50IGN4PSI1MCUiIGN5PSI1NSUiIGZ4PSI1MCUiIGZ5PSI1NSUiIHI9IjcyLjUwNyUiIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoLjkyNDA0IDAgMCAxIC4wMzggMCkiIGlkPSJhIj48c3RvcCBzdG9wLWNvbG9yPSIjRkZCOTAwIiBvZmZzZXQ9IjAlIi8+PHN0b3Agc3RvcC1jb2xvcj0iI0Y5NUQ4RiIgb2Zmc2V0PSI2MCUiLz48c3RvcCBzdG9wLWNvbG9yPSIjRjk1MzUzIiBvZmZzZXQ9Ijk5LjkxJSIvPjwvcmFkaWFsR3JhZGllbnQ+PC9kZWZzPjxwYXRoIGQ9Ik00NS41OTQgMjguNWMtNi45OTQuMDAzLTEyLjY2NCA1LjY3My0xMi42NjcgMTIuNjY3LjAwMyA2Ljk5NSA1LjY3MyAxMi42NjQgMTIuNjY3IDEyLjY2OCA2Ljk5NS0uMDA0IDEyLjY2NC01LjY3MyAxMi42NjctMTIuNjY4LS4wMDMtNi45OTQtNS42NzItMTIuNjY0LTEyLjY2Ny0xMi42Njd6bS0zMi45MjcuMDAxQzUuNjczIDI4LjUwNS4wMDMgMzQuMTc0IDAgNDEuMTdjLjAwMyA2Ljk5NCA1LjY3MyAxMi42NjQgMTIuNjY3IDEyLjY2NyA2Ljk5NS0uMDAzIDEyLjY2NC01LjY3MyAxMi42NjgtMTIuNjY3LS4wMDQtNi45OTUtNS42NzMtMTIuNjY0LTEyLjY2OC0xMi42Njh6TTQxLjc5IDEyLjY2N2MtLjAwMiA2Ljk5NS01LjY3MSAxMi42NjUtMTIuNjY2IDEyLjY3LTYuOTk1LS4wMDQtMTIuNjY0LTUuNjc0LTEyLjY2Ny0xMi42N0MxNi40NiA1LjY3MyAyMi4xMy4wMDMgMjkuMTIzIDBjNi45OTQuMDA0IDEyLjY2MyA1LjY3MyAxMi42NjYgMTIuNjY3eiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLjczMiAyLjczMikiIGZpbGw9InVybCgjYSkiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvc3ZnPg==" + }, + "categories": [ + { + "id": 4, + "name": "Productivity" + } + ], + "displayName": "Asana Trigger", + "typeVersion": 1 + }, + { + "id": 15, + "icon": "file:affinity.png", + "name": "n8n-nodes-base.affinity", + "defaults": { + "name": "Affinity" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/png;base64,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" + }, + "categories": [ + { + "id": 2, + "name": "Sales" + } + ], + "displayName": "Affinity", + "typeVersion": 1 + }, + { + "id": 19, + "icon": "file:autopilot.svg", + "name": "n8n-nodes-base.autopilotTrigger", + "defaults": { + "name": "Autopilot Trigger" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjM4IDI2IDM1IDM1Ij48Y2lyY2xlIGN4PSI1MCIgY3k9IjUwIiByPSI0MCIgc3Ryb2tlPSIjMThkNGIyIiBzdHJva2Utd2lkdGg9IjMiIGZpbGw9IiMxOGQ0YjIiLz48cGF0aCBmaWxsPSIjZmZmIiBkPSJNNDUuNCA0Mi42aDE5LjlsMy40LTQuOEg0MmwzLjQgNC44em0zLjEgOC4zaDEzLjFsMy40LTQuOEg0NS40bDMuMSA0Ljh6bTU0LS43Ii8+PC9zdmc+" + }, + "categories": [ + { + "id": 1, + "name": "Marketing" + } + ], + "displayName": "Autopilot Trigger", + "typeVersion": 1 + }, + { + "id": 20, + "icon": "file:lambda.svg", + "name": "n8n-nodes-base.awsLambda", + "defaults": { + "name": "AWS Lambda" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjI1MDAiIHZpZXdCb3g9Ii0zLjAyMyAtMC4yMiA0MjAuOTIzIDQzMy41NCIgd2lkdGg9IjI0NDMiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTIwOC40NSAyMjcuODljLTEuNTkgMi4yNi0yLjkzIDQuMTItNC4yMiA2cS0zMC44NiA0NS40Mi02MS43IDkwLjgzLTI4LjY5IDQyLjI0LTU3LjQ0IDg0LjQzYTMuODggMy44OCAwIDAxLTIuNzMgMS41OXEtNDAuNTktLjM1LTgxLjE2LS44OGMtLjMgMC0uNjEtLjA5LTEuMi0uMThhMTQuNDQgMTQuNDQgMCAwMS43Ni0xLjY1cTI4LjMxLTQzLjg5IDU2LjYyLTg3Ljc2IDI1LjExLTM4Ljg4IDUwLjI1LTc3Ljc0IDI3Ljg2LTQzLjE4IDU1LjY5LTg2LjQyYzIuNzQtNC4yNSA1LjU5LTguNDIgOC4xOS0xMi43NWE1LjI2IDUuMjYgMCAwMC41Ni0zLjgzYy01LTE1Ljk0LTEwLjEtMzEuODQtMTUuMTktNDcuNzQtMi4xOC02LjgxLTQuNDYtMTMuNTgtNi41LTIwLjQzLS42Ni0yLjItMS43NS0yLjg3LTQtMi44Ni0xNyAuMDctMzMuOS4wNS01MC44NS4wNS0zLjIyIDAtMy4yMyAwLTMuMjMtMy4xOCAwLTIwLjg0IDAtNDEuNjgtLjA2LTYyLjUyIDAtMi4zMi43Ni0yLjg0IDIuOTQtMi44NHE1MS4xOS4wOSAxMDIuNCAwYTMuMjkgMy4yOSAwIDAxMy42IDIuNDNxMjcgNjcuOTEgNTQgMTM1Ljc3IDMxLjUgNzkuMTQgNjMgMTU4LjNjNi41MiAxNi4zOCAxMy4wOSAzMi43NSAxOS41NCA0OS4xNy43NyAyIDEuNTcgMi4zOCAzLjU5IDEuNzYgMTcuODktNS41MyAzNS44Mi0xMC45MSA1My43LTE2LjQ1IDIuMjUtLjcgMy4wNy0uMjMgMy43NyAyIDYuMSAxOS4xNyAxMi4zMiAzOC4zIDE4LjUgNTcuNDUuMjEuNjYuMzcgMS4zMy42MiAyLjI1LTEuMjguNDctMi40OCAxLTMuNzEgMS4zNHEtNjEgMTkuMzMtMTIxLjkzIDM4LjY4Yy0xLjk0LjYxLTIuNTItLjA1LTMuMTctMS42OHEtMTguNjEtNDcuMTYtMzcuMzEtOTQuMjgtMTguMjktNDYuMTQtMzYuNi05Mi4yOGMtMS44My00LjYyLTMuNjMtOS4yNi01LjQ2LTEzLjg4LS4yOS0uNzktLjY5LTEuNDgtMS4yNy0yLjd6IiBmaWxsPSIjZmE3ZTE0Ii8+PC9zdmc+" + }, + "categories": [ + { + "id": 5, + "name": "Development" + } + ], + "displayName": "AWS Lambda", + "typeVersion": 1 + }, + { + "id": 31, + "icon": "file:bitwarden.svg", + "name": "n8n-nodes-base.bitwarden", + "defaults": { + "name": "Bitwarden" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgNTUgNjYiIGZpbGw9IiNmZmYiIGZpbGwtcnVsZT0iZXZlbm9kZCIgc3Ryb2tlPSIjMDAwIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjx1c2UgeGxpbms6aHJlZj0iI2EiIHg9Ii41IiB5PSIuNSIvPjxzeW1ib2wgaWQ9ImEiIG92ZXJmbG93PSJ2aXNpYmxlIj48cGF0aCBkPSJNNTMuMzMzIDIuNjY3djMyYzAgMi4zODgtLjQ2NSA0Ljc1Ni0xLjM5NiA3LjEwM3MtMi4wODQgNC40My0zLjQ1OCA2LjI1LTMuMDE1IDMuNTktNC45MTcgNS4zMTItMy42NiAzLjE1My01LjI3MiA0LjI5MmwtNS4wNCAzLjIzLTMuNzMgMi4wNjItMS43Ny44MzRjLS4zMzMuMTY2LS42OTUuMjUtMS4wODMuMjVhMi40IDIuNCAwIDAxLTEuMDgzLS4yNWwtMS43Ny0uODM0LTMuNzMtMi4wNjItNS4wNDItMy4yM2MtMS42MS0xLjE0LTMuMzY4LTIuNTctNS4yNy00LjI5MnMtMy41NC0zLjQ5Mi00LjkxNi01LjMxMi0yLjUyOC0zLjkwMy0zLjQ2LTYuMjVTMCAzNy4wNTUgMCAzNC42Njd2LTMyQTIuNTYgMi41NiAwIDAxLjc5MS43OTIgMi41NiAyLjU2IDAgMDEyLjY2NiAwaDQ4Yy43MiAwIDEuMzQ2LjI2NCAxLjg3NC43OTJhMi41NiAyLjU2IDAgMDEuNzkyIDEuODc1bS04IDMyVjhIMjYuNjY2djQ3LjM3NWMzLjMwNS0xLjc1IDYuMjY0LTMuNjUzIDguODc1LTUuNzA4IDYuNTI3LTUuMTEgOS43OS0xMC4xMSA5Ljc5LTE1IiBzdHJva2U9Im5vbmUiIGZpbGw9IiMzYzhkYmMiIGZpbGwtcnVsZT0ibm9uemVybyIvPjwvc3ltYm9sPjwvc3ZnPg==" + }, + "categories": [ + { + "id": 3, + "name": "Data & Storage" + } + ], + "displayName": "Bitwarden", + "typeVersion": 1 + }, + { + "id": 42, + "icon": "file:clickup.svg", + "name": "n8n-nodes-base.clickUpTrigger", + "defaults": { + "name": "ClickUp Trigger" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSItMTAgMCAxNTUgMTU1IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxkZWZzPjxsaW5lYXJHcmFkaWVudCB4MT0iMCUiIHkxPSI2OC4wMSUiIHkyPSI2OC4wMSUiIGlkPSJhIj48c3RvcCBzdG9wLWNvbG9yPSIjODkzMEZEIiBvZmZzZXQ9IjAlIi8+PHN0b3Agc3RvcC1jb2xvcj0iIzQ5Q0NGOSIgb2Zmc2V0PSIxMDAlIi8+PC9saW5lYXJHcmFkaWVudD48bGluZWFyR3JhZGllbnQgeDE9IjAlIiB5MT0iNjguMDElIiB5Mj0iNjguMDElIiBpZD0iYiI+PHN0b3Agc3RvcC1jb2xvcj0iI0ZGMDJGMCIgb2Zmc2V0PSIwJSIvPjxzdG9wIHN0b3AtY29sb3I9IiNGRkM4MDAiIG9mZnNldD0iMTAwJSIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxnIGZpbGw9Im5vbmUiPjxwYXRoIGQ9Ik0uNCAxMTkuMTJsMjMuODEtMTguMjRDMzYuODYgMTE3LjM5IDUwLjMgMTI1IDY1LjI2IDEyNWMxNC44OCAwIDI3Ljk0LTcuNTIgNDAuMDItMjMuOWwyNC4xNSAxNy44QzExMiAxNDIuNTIgOTAuMzQgMTU1IDY1LjI2IDE1NWMtMjUgMC00Ni44Ny0xMi40LTY0Ljg2LTM1Ljg4eiIgZmlsbD0idXJsKCNhKSIvPjxwYXRoIGZpbGw9InVybCgjYikiIGQ9Ik02NS4xOCAzOS44NEwyMi44IDc2LjM2IDMuMjEgNTMuNjQgNjUuMjcuMTZsNjEuNTcgNTMuNTItMTkuNjggMjIuNjR6Ii8+PC9nPjwvc3ZnPg==" + }, + "categories": [ + { + "id": 4, + "name": "Productivity" + }, + { + "id": 6, + "name": "Communication" + } + ], + "displayName": "ClickUp Trigger", + "typeVersion": 1 + }, + { + "id": 46, + "icon": "fa:file-archive", + "name": "n8n-nodes-base.compression", + "defaults": { + "name": "Compression", + "color": "#408000" + }, + "iconData": { + "icon": "file-archive", + "type": "icon" + }, + "categories": [ + { + "id": 3, + "name": "Data & Storage" + }, + { + "id": 9, + "name": "Core Nodes" + } + ], + "displayName": "Compression", + "typeVersion": 1 + }, + { + "id": 62, + "icon": "file:discord.svg", + "name": "n8n-nodes-base.discord", + "defaults": { + "name": "Discord" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iMjU2cHgiIGhlaWdodD0iMTk5cHgiIHZpZXdCb3g9IjAgMCAyNTYgMTk5IiB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHByZXNlcnZlQXNwZWN0UmF0aW89InhNaWRZTWlkIj4KICAgIDxnPgogICAgICAgIDxwYXRoIGQ9Ik0yMTYuODU2MzM5LDE2LjU5NjYwMzEgQzIwMC4yODUwMDIsOC44NDMyODY2NSAxODIuNTY2MTQ0LDMuMjA4NDk4OCAxNjQuMDQxNTY0LDAgQzE2MS43NjY1MjMsNC4xMTMxODEwNiAxNTkuMTA4NjI0LDkuNjQ1NDk5MDggMTU3LjI3NjA5OSwxNC4wNDY0Mzc5IEMxMzcuNTgzOTk1LDExLjA4NDk4OTYgMTE4LjA3Mjk2NywxMS4wODQ5ODk2IDk4Ljc0MzAxNjMsMTQuMDQ2NDM3OSBDOTYuOTEwODQxNyw5LjY0NTQ5OTA4IDk0LjE5MjU4MzgsNC4xMTMxODEwNiA5MS44OTcxODk1LDAgQzczLjM1MjYwNjgsMy4yMDg0OTg4IDU1LjYxMzM5NDksOC44NjM5OTExNyAzOS4wNDIwNTgzLDE2LjYzNzY2MTIgQzUuNjE3NTIyOTMsNjcuMTQ2NTE0IC0zLjQ0MzMxOTEsMTE2LjQwMDgxMyAxLjA4NzExMDY5LDE2NC45NTU3MjEgQzIzLjI1NjAxOTYsMTgxLjUxMDkxNSA0NC43NDAzNjM0LDE5MS41Njc2OTcgNjUuODYyMTMyNSwxOTguMTQ4NTc2IEM3MS4wNzcyMTUxLDE5MC45NzExMjYgNzUuNzI4MzYyOCwxODMuMzQxMzM1IDc5LjczNTIxMzksMTc1LjMwMDI2MSBDNzIuMTA0MDE5LDE3Mi40MDA1NzUgNjQuNzk0OTcyNCwxNjguODIyMjAyIDU3Ljg4ODc4NjYsMTY0LjY2Nzk2MyBDNTkuNzIwOTYxMiwxNjMuMzEwNTg5IDYxLjUxMzEzMDQsMTYxLjg5MTQ1MiA2My4yNDQ1ODk4LDE2MC40MzEyNTcgQzEwNS4zNjc0MSwxODAuMTMzMTg3IDE1MS4xMzQ5MjgsMTgwLjEzMzE4NyAxOTIuNzU0NTIzLDE2MC40MzEyNTcgQzE5NC41MDYzMzYsMTYxLjg5MTQ1MiAxOTYuMjk4MTU0LDE2My4zMTA1ODkgMTk4LjExMDMyNiwxNjQuNjY3OTYzIEMxOTEuMTgzNzg3LDE2OC44NDI1NTYgMTgzLjg1NDczNywxNzIuNDIwOTI5IDE3Ni4yMjM1NDIsMTc1LjMyMDk2NSBDMTgwLjIzMDM5MywxODMuMzQxMzM1IDE4NC44NjE1MzgsMTkwLjk5MTgzMSAxOTAuMDk2NjI0LDE5OC4xNjg5MyBDMjExLjIzODc0NiwxOTEuNTg4MDUxIDIzMi43NDMwMjMsMTgxLjUzMTYxOSAyNTQuOTExOTQ5LDE2NC45NTU3MjEgQzI2MC4yMjc3NDcsMTA4LjY2ODIwMSAyNDUuODMxMDg3LDU5Ljg2NjI0MzIgMjE2Ljg1NjMzOSwxNi41OTY2MDMxIFogTTg1LjQ3Mzg3NTIsMTM1LjA5NDg5IEM3Mi44MjkwMjgxLDEzNS4wOTQ4OSA2Mi40NTkyMjE3LDEyMy4yOTAxNTUgNjIuNDU5MjIxNywxMDguOTE0OTAxIEM2Mi40NTkyMjE3LDk0LjUzOTY0NzIgNzIuNjA3NTk1LDgyLjcxNDU1ODcgODUuNDczODc1Miw4Mi43MTQ1NTg3IEM5OC4zNDA1MDY0LDgyLjcxNDU1ODcgMTA4LjcwOTk2Miw5NC41MTg5NDI3IDEwOC40ODg1MjksMTA4LjkxNDkwMSBDMTA4LjUwODUzMSwxMjMuMjkwMTU1IDk4LjM0MDUwNjQsMTM1LjA5NDg5IDg1LjQ3Mzg3NTIsMTM1LjA5NDg5IFogTTE3MC41MjUyMzcsMTM1LjA5NDg5IEMxNTcuODgwMzksMTM1LjA5NDg5IDE0Ny41MTA1ODQsMTIzLjI5MDE1NSAxNDcuNTEwNTg0LDEwOC45MTQ5MDEgQzE0Ny41MTA1ODQsOTQuNTM5NjQ3MiAxNTcuNjU4NjA2LDgyLjcxNDU1ODcgMTcwLjUyNTIzNyw4Mi43MTQ1NTg3IEMxODMuMzkxNTE4LDgyLjcxNDU1ODcgMTkzLjc2MTMyNCw5NC41MTg5NDI3IDE5My41Mzk4OTEsMTA4LjkxNDkwMSBDMTkzLjUzOTg5MSwxMjMuMjkwMTU1IDE4My4zOTE1MTgsMTM1LjA5NDg5IDE3MC41MjUyMzcsMTM1LjA5NDg5IFoiIGZpbGw9IiM1ODY1RjIiIGZpbGwtcnVsZT0ibm9uemVybyI+PC9wYXRoPgogICAgPC9nPgo8L3N2Zz4=" + }, + "categories": [ + { + "id": 6, + "name": "Communication" + } + ], + "displayName": "Discord", + "typeVersion": 2 + }, + { + "id": 114, + "icon": "file:helpScout.svg", + "name": "n8n-nodes-base.helpScoutTrigger", + "defaults": { + "name": "HelpScout Trigger" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjU2IiBoZWlnaHQ9IjMxMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWlkWU1pZCI+PHBhdGggZD0iTTE4LjQzMiAxODAuOTY5bDkwLjQ4NC05MC40ODVhNjMuNzE3IDYzLjcxNyAwIDAwMTguOTktNDUuNDI4QTY0LjI0NSA2NC4yNDUgMCAwMDEwOS40NzYgMEwxOC45OSA5MC40ODRBNjMuNzE3IDYzLjcxNyAwIDAwMCAxMzUuOTEzYzAgMTcuNjg3IDcuMDc1IDMzLjUxMiAxOC40MzIgNDUuMDU2em0yMTkuMTM2LTUyLjg3NmwtOTAuNDg0IDkwLjQ4NGE2My43MTcgNjMuNzE3IDAgMDAtMTguOTkgNDUuNDI5IDY0LjI0NSA2NC4yNDUgMCAwMDE4LjQzMSA0NS4wNTZsOTAuNDg0LTkwLjQ4NUE2My43MTcgNjMuNzE3IDAgMDAyNTYgMTczLjE1YzAtMTcuNjg3LTcuMDc1LTMzLjUxMy0xOC40MzItNDUuMDU2em0tLjU1OS0zNy40MjJBNjMuNzE3IDYzLjcxNyAwIDAwMjU2IDQ1LjI0MiA2NC4yNDUgNjQuMjQ1IDAgMDAyMzcuNTY4LjE4NkwxOC45OTEgMjE4LjU3N0M3LjI2IDIzMC4zMDcgMCAyNDYuMzIgMCAyNjQuMTkyYTY0LjI0NSA2NC4yNDUgMCAwMDE4LjQzMiA0NS4wNTZMMjM3LjAwOSA5MC42NzF6IiBmaWxsPSIjMTI5MkVFIi8+PC9zdmc+" + }, + "categories": [ + { + "id": 6, + "name": "Communication" + } + ], + "displayName": "HelpScout Trigger", + "typeVersion": 1 + } + ] + }, + { + "id": 1323, + "name": "Create Email Campaign From LinkedIn Post Interactions", + "totalViews": 942, + "recentViews": 0, + "user": { + "username": "admin" + }, + "createdAt": "2021-11-15T14:48:51.258Z", + "nodes": [ + { + "id": 7, + "icon": "file:airtable.svg", + "name": "n8n-nodes-base.airtable", + "defaults": { + "name": "Airtable" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyMDAgMTcwIj48cGF0aCBkPSJNODkgNC44TDE2LjIgMzQuOWMtNC4xIDEuNy00IDcuNC4xIDkuMWw3My4yIDI5YzYuNCAyLjYgMTMuNiAyLjYgMjAgMGw3My4yLTI5YzQuMS0xLjYgNC4xLTcuNC4xLTkuMWwtNzMtMzAuMUMxMDMuMiAyIDk1LjcgMiA4OSA0LjgiIGZpbGw9IiNmY2I0MDAiLz48cGF0aCBkPSJNMTA1LjkgODguOXY3Mi41YzAgMy40IDMuNSA1LjggNi43IDQuNWw4MS42LTMxLjdjMS45LS43IDMuMS0yLjUgMy4xLTQuNVY1Ny4yYzAtMy40LTMuNS01LjgtNi43LTQuNUwxMDkgODQuM2MtMS45LjgtMy4xIDIuNi0zLjEgNC42IiBmaWxsPSIjMThiZmZmIi8+PHBhdGggZD0iTTg2LjkgOTIuNmwtMjQuMiAxMS43LTIuNSAxLjJMOS4xIDEzMGMtMy4yIDEuNi03LjQtLjgtNy40LTQuNFY1Ny41YzAtMS4zLjctMi40IDEuNi0zLjMuNC0uNC44LS43IDEuMi0uOSAxLjItLjcgMy0uOSA0LjQtLjNsNzcuNSAzMC43YzQgMS41IDQuMyA3LjEuNSA4LjkiIGZpbGw9IiNmODJiNjAiLz48cGF0aCBkPSJNODYuOSA5Mi42bC0yNC4yIDExLjctNTkuNC01MGMuNC0uNC44LS43IDEuMi0uOSAxLjItLjcgMy0uOSA0LjQtLjNsNzcuNSAzMC43YzQgMS40IDQuMyA3IC41IDguOCIgZmlsbD0iI2JhMWU0NSIvPjwvc3ZnPg==" + }, + "categories": [ + { + "id": 3, + "name": "Data & Storage" + } + ], + "displayName": "Airtable", + "typeVersion": 2 + }, + { + "id": 14, + "icon": "file:apiTemplateIo.svg", + "name": "n8n-nodes-base.apiTemplateIo", + "defaults": { + "name": "APITemplate.io" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4MCIgaGVpZ2h0PSI4MCIgdmlld0JveD0iMCAwIDIzLjE2NyAyMS4xNjciPjxwYXRoIGZpbGw9IiM5MWJjZTUiIGQ9Ik0xOC42MTEuNTA0TDMuNDczIDExLjI0bDQuNTk3IDIuMjEzIDEuMDY2IDMuODgzIDMuMTcxLTIuMjI2IDIuODAyLjk1MXoiLz48cGF0aCBmaWxsPSIjMWYyMTJiIiBkPSJNOS4xNzIgMTcuNTk3YS4yNjQuMjY0IDAgMDEtLjI5LS4xOTJsLTEuMDM1LTMuNzY3LTQuNDg4LTIuMTZhLjI2NS4yNjUgMCAwMS0uMDM5LS40NTRMMTguNDU4LjI4OGEuMjY0LjI2NCAwIDAxLjQxLjI3NGwtMy41IDE1LjU1NmEuMjY1LjI2NSAwIDAxLS4zNDQuMTkzbC0yLjY3NC0uOTA4LTMuMDYxIDIuMTVhLjI3My4yNzMgMCAwMS0uMTE3LjA0NHptLTUuMTgtNi40MDFsNC4xOTMgMi4wMTdjLjA2OS4wMzQuMTIuMDk1LjE0LjE2OWwuOTY3IDMuNTIgMi44NjMtMi4wMWEuMjY1LjI2NSAwIDAxLjIzNy0uMDMzbDIuNTI0Ljg1NkwxOC4yIDEuMTE5eiIvPjxwYXRoIGZpbGw9IiMzYTg0YzEiIGQ9Ik05LjMzOSAxNy4wNzlsLjk0Ni0yLjkxM0wxOC42NDIuNzI4IDguMjI2IDEzLjIzNXoiLz48cGF0aCBmaWxsPSIjMWYyMTJiIiBkPSJNOS4zNTYgMTcuMjFhLjEzMi4xMzIgMCAwMS0uMTQzLS4xNzJsLjk0Ni0yLjkxM2EuMTYuMTYgMCAwMS4wMTQtLjAzbDcuMzkzLTExLjg4OC05LjQxOSAxMS4xNTZhLjEzMy4xMzMgMCAwMS0uMjAyLS4xNzFMMTguNTQuNjQyYS4xMzIuMTMyIDAgMDEuMjE0LjE1NmwtOC4zNDggMTMuNDI0LS45NDEgMi44OThhLjEzMi4xMzIgMCAwMS0uMTA5LjA5em0tMy4yMTUgMS4wNDVjLTEuMTY4LjAyOS0yLjMwMy0uMzczLTMuMDY3LTEuMjg3LS40ODUtLjU4LS42ODMtMS41NzQuMjc1LTEuNzA1Ljg1OS0uMTE5IDEuMjI5LjQ4NSAxLjQ4IDEuMTk2LjgwNCAyLjAyNi4xNiAyLjkzOC0xLjY4IDQuMjcyLS4xNDQuMDkuMDE1LjI5Ni4xNTkuMjA2IDEuMjE4LTEgMS44MjktMS40NjYgMi4xLTIuNDQ4LjEzNC0xLjM2LS40MDItMy4wOTEtMS4zODQtMy40MzYtMS4xMi0uMzgyLTIuMDYzLjUxNi0xLjQ5OSAxLjU4NS42OTkgMS4zMjQgMi4yMyAxLjkxNCAzLjY1MiAxLjg3OC4xNy0uMDA0LjEzMy0uMjY2LS4wMzYtLjI2MXoiLz48cGF0aCBmaWxsPSIjMWYyMTJiIiBkPSJNNy4yMzUgMTguMTRhMTQuOTUgMTQuOTUgMCAwMS0uNzU5LjA5Yy0uMTcuMDE3LS4xMzMuMjc5LjAzNi4yNjIuMjUzLS4wMjUuNTA3LS4wNTIuNzU4LS4wOS4xNjktLjAyNi4xMzMtLjI4OC0uMDM1LS4yNjJ6bTEuMTk0LS41MDRhOC40NiA4LjQ2IDAgMDEtLjg2NS4zNDRjLS4xNjIuMDU0LS4wNTcuMjk3LjEwNC4yNDNhOC4zNyA4LjM3IDAgMDAuODY1LS4zNDRjLjE1NC0uMDcxLjA1LS4zMTUtLjEwNC0uMjQzeiIvPjxwYXRoIGQ9Ik0xMi41OTkgMTUuMjJhLjEzMS4xMzEgMCAwMS0uMDctLjAxbC0yLjE2OC0uOTI0YS4xMzMuMTMzIDAgMDEuMTAzLS4yNDNsMi4xNjkuOTIzYS4xMzMuMTMzIDAgMDEtLjAzNC4yNTN6Ii8+PC9zdmc+" + }, + "categories": [ + { + "id": 1, + "name": "Marketing" + } + ], + "displayName": "APITemplate.io", + "typeVersion": 1 + }, + { + "id": 20, + "icon": "file:lambda.svg", + "name": "n8n-nodes-base.awsLambda", + "defaults": { + "name": "AWS Lambda" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjI1MDAiIHZpZXdCb3g9Ii0zLjAyMyAtMC4yMiA0MjAuOTIzIDQzMy41NCIgd2lkdGg9IjI0NDMiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTIwOC40NSAyMjcuODljLTEuNTkgMi4yNi0yLjkzIDQuMTItNC4yMiA2cS0zMC44NiA0NS40Mi02MS43IDkwLjgzLTI4LjY5IDQyLjI0LTU3LjQ0IDg0LjQzYTMuODggMy44OCAwIDAxLTIuNzMgMS41OXEtNDAuNTktLjM1LTgxLjE2LS44OGMtLjMgMC0uNjEtLjA5LTEuMi0uMThhMTQuNDQgMTQuNDQgMCAwMS43Ni0xLjY1cTI4LjMxLTQzLjg5IDU2LjYyLTg3Ljc2IDI1LjExLTM4Ljg4IDUwLjI1LTc3Ljc0IDI3Ljg2LTQzLjE4IDU1LjY5LTg2LjQyYzIuNzQtNC4yNSA1LjU5LTguNDIgOC4xOS0xMi43NWE1LjI2IDUuMjYgMCAwMC41Ni0zLjgzYy01LTE1Ljk0LTEwLjEtMzEuODQtMTUuMTktNDcuNzQtMi4xOC02LjgxLTQuNDYtMTMuNTgtNi41LTIwLjQzLS42Ni0yLjItMS43NS0yLjg3LTQtMi44Ni0xNyAuMDctMzMuOS4wNS01MC44NS4wNS0zLjIyIDAtMy4yMyAwLTMuMjMtMy4xOCAwLTIwLjg0IDAtNDEuNjgtLjA2LTYyLjUyIDAtMi4zMi43Ni0yLjg0IDIuOTQtMi44NHE1MS4xOS4wOSAxMDIuNCAwYTMuMjkgMy4yOSAwIDAxMy42IDIuNDNxMjcgNjcuOTEgNTQgMTM1Ljc3IDMxLjUgNzkuMTQgNjMgMTU4LjNjNi41MiAxNi4zOCAxMy4wOSAzMi43NSAxOS41NCA0OS4xNy43NyAyIDEuNTcgMi4zOCAzLjU5IDEuNzYgMTcuODktNS41MyAzNS44Mi0xMC45MSA1My43LTE2LjQ1IDIuMjUtLjcgMy4wNy0uMjMgMy43NyAyIDYuMSAxOS4xNyAxMi4zMiAzOC4zIDE4LjUgNTcuNDUuMjEuNjYuMzcgMS4zMy42MiAyLjI1LTEuMjguNDctMi40OCAxLTMuNzEgMS4zNHEtNjEgMTkuMzMtMTIxLjkzIDM4LjY4Yy0xLjk0LjYxLTIuNTItLjA1LTMuMTctMS42OHEtMTguNjEtNDcuMTYtMzcuMzEtOTQuMjgtMTguMjktNDYuMTQtMzYuNi05Mi4yOGMtMS44My00LjYyLTMuNjMtOS4yNi01LjQ2LTEzLjg4LS4yOS0uNzktLjY5LTEuNDgtMS4yNy0yLjd6IiBmaWxsPSIjZmE3ZTE0Ii8+PC9zdmc+" + }, + "categories": [ + { + "id": 5, + "name": "Development" + } + ], + "displayName": "AWS Lambda", + "typeVersion": 1 + }, + { + "id": 38, + "icon": "file:chargebee.png", + "name": "n8n-nodes-base.chargebeeTrigger", + "defaults": { + "name": "Chargebee Trigger" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAA8CAMAAAANIilAAAAAk1BMVEUAAAD9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azXS0qjqAAAAMHRSTlMAAta1CPr3HAyb8mNPLBLq5MmGZ1wi28+soEQX7pZ9dkk/MijDurCMims54b6lb1YyU8hoAAACDklEQVRIx+2V2bKiMBBAmyTs+w6CIi64O/n/rxtKJwIjJPA4Nfc8acOpTnfSAX7493G2QZN6Zeml+GwVS0xiNSXtYeBIm6lKEaJf3C5kjrvz6SioFruVTKcIJEG1D8oBa1wXUy6+w2lVxhFFuU+0j4KOjwdOjUHQnHJ/DVZ4iaXXatTo2OuhPuEWXm9fLOhxPQjldeeuyV/NqGT+su1ucZuRkt5PMRElDmCEbdK2MNcmKnaZi2EUrd7GMEHEXGMPi8mYfFruqjd2NLrE+/P9oF9nyCuW+P4JhcorcBbL4dc2WSxyEcoBe9Vi5yJlEc8RyTp7ldVo0w8rkZyzN3ddExjR4sw7TmZhzSRhEddZ3m2TRar5+3z8hDZ/xlMSnzCPnbDu9NcPhLItzKAZG0hJnHVYYhnDYtSS2RksJ+fcQi0qAbDtKXknU84oWPQJtizvxanpmcCQJ3VtiA1lUlbdzk7rfs/bzwFSVR/bsaDhjHukvvfJsbBXm8S+UboXTwfDQFjXG6S0/dde18oGrN20TI4DOzHaA3drzAJWPqVy2Fa+5qTWDn05AKJJbS+eafsHtUlRUgAHB/dkJT+ddJzI7U+3kopjVgiGUwroF7J/IWAac+7RFeo0D6X3daQCiRBNZs2XVrmDr4e2zQ1aVhrMwwl9+Z1Zzw42ptQPHZiPFJv5IXENWXGvdRhL8MN/y2+uRpQ1fWz5HAAAAABJRU5ErkJggg==" + }, + "categories": [ + { + "id": 8, + "name": "Finance & Accounting" + } + ], + "displayName": "Chargebee Trigger", + "typeVersion": 1 + }, + { + "id": 76, + "icon": "fa:sign-in-alt", + "name": "n8n-nodes-base.executeWorkflow", + "defaults": { + "name": "Execute Workflow", + "color": "#ff6d5a" + }, + "iconData": { + "icon": "sign-in-alt", + "type": "icon" + }, + "categories": [ + { + "id": 9, + "name": "Core Nodes" + } + ], + "displayName": "Execute Workflow", + "typeVersion": 1 + } + ] + }, + { + "id": 467, + "name": "Funnel Users and Sales Data From Webhook to a Marketing Platform (We’d might like to generalize the workflow as it’s very specific to Teachable and Muatic integration)", + "totalViews": 658, + "recentViews": 0, + "user": { + "username": "admin" + }, + "createdAt": "2020-07-09T20:34:27.156Z", + "nodes": [ + { + "id": 14, + "icon": "file:apiTemplateIo.svg", + "name": "n8n-nodes-base.apiTemplateIo", + "defaults": { + "name": "APITemplate.io" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4MCIgaGVpZ2h0PSI4MCIgdmlld0JveD0iMCAwIDIzLjE2NyAyMS4xNjciPjxwYXRoIGZpbGw9IiM5MWJjZTUiIGQ9Ik0xOC42MTEuNTA0TDMuNDczIDExLjI0bDQuNTk3IDIuMjEzIDEuMDY2IDMuODgzIDMuMTcxLTIuMjI2IDIuODAyLjk1MXoiLz48cGF0aCBmaWxsPSIjMWYyMTJiIiBkPSJNOS4xNzIgMTcuNTk3YS4yNjQuMjY0IDAgMDEtLjI5LS4xOTJsLTEuMDM1LTMuNzY3LTQuNDg4LTIuMTZhLjI2NS4yNjUgMCAwMS0uMDM5LS40NTRMMTguNDU4LjI4OGEuMjY0LjI2NCAwIDAxLjQxLjI3NGwtMy41IDE1LjU1NmEuMjY1LjI2NSAwIDAxLS4zNDQuMTkzbC0yLjY3NC0uOTA4LTMuMDYxIDIuMTVhLjI3My4yNzMgMCAwMS0uMTE3LjA0NHptLTUuMTgtNi40MDFsNC4xOTMgMi4wMTdjLjA2OS4wMzQuMTIuMDk1LjE0LjE2OWwuOTY3IDMuNTIgMi44NjMtMi4wMWEuMjY1LjI2NSAwIDAxLjIzNy0uMDMzbDIuNTI0Ljg1NkwxOC4yIDEuMTE5eiIvPjxwYXRoIGZpbGw9IiMzYTg0YzEiIGQ9Ik05LjMzOSAxNy4wNzlsLjk0Ni0yLjkxM0wxOC42NDIuNzI4IDguMjI2IDEzLjIzNXoiLz48cGF0aCBmaWxsPSIjMWYyMTJiIiBkPSJNOS4zNTYgMTcuMjFhLjEzMi4xMzIgMCAwMS0uMTQzLS4xNzJsLjk0Ni0yLjkxM2EuMTYuMTYgMCAwMS4wMTQtLjAzbDcuMzkzLTExLjg4OC05LjQxOSAxMS4xNTZhLjEzMy4xMzMgMCAwMS0uMjAyLS4xNzFMMTguNTQuNjQyYS4xMzIuMTMyIDAgMDEuMjE0LjE1NmwtOC4zNDggMTMuNDI0LS45NDEgMi44OThhLjEzMi4xMzIgMCAwMS0uMTA5LjA5em0tMy4yMTUgMS4wNDVjLTEuMTY4LjAyOS0yLjMwMy0uMzczLTMuMDY3LTEuMjg3LS40ODUtLjU4LS42ODMtMS41NzQuMjc1LTEuNzA1Ljg1OS0uMTE5IDEuMjI5LjQ4NSAxLjQ4IDEuMTk2LjgwNCAyLjAyNi4xNiAyLjkzOC0xLjY4IDQuMjcyLS4xNDQuMDkuMDE1LjI5Ni4xNTkuMjA2IDEuMjE4LTEgMS44MjktMS40NjYgMi4xLTIuNDQ4LjEzNC0xLjM2LS40MDItMy4wOTEtMS4zODQtMy40MzYtMS4xMi0uMzgyLTIuMDYzLjUxNi0xLjQ5OSAxLjU4NS42OTkgMS4zMjQgMi4yMyAxLjkxNCAzLjY1MiAxLjg3OC4xNy0uMDA0LjEzMy0uMjY2LS4wMzYtLjI2MXoiLz48cGF0aCBmaWxsPSIjMWYyMTJiIiBkPSJNNy4yMzUgMTguMTRhMTQuOTUgMTQuOTUgMCAwMS0uNzU5LjA5Yy0uMTcuMDE3LS4xMzMuMjc5LjAzNi4yNjIuMjUzLS4wMjUuNTA3LS4wNTIuNzU4LS4wOS4xNjktLjAyNi4xMzMtLjI4OC0uMDM1LS4yNjJ6bTEuMTk0LS41MDRhOC40NiA4LjQ2IDAgMDEtLjg2NS4zNDRjLS4xNjIuMDU0LS4wNTcuMjk3LjEwNC4yNDNhOC4zNyA4LjM3IDAgMDAuODY1LS4zNDRjLjE1NC0uMDcxLjA1LS4zMTUtLjEwNC0uMjQzeiIvPjxwYXRoIGQ9Ik0xMi41OTkgMTUuMjJhLjEzMS4xMzEgMCAwMS0uMDctLjAxbC0yLjE2OC0uOTI0YS4xMzMuMTMzIDAgMDEuMTAzLS4yNDNsMi4xNjkuOTIzYS4xMzMuMTMzIDAgMDEtLjAzNC4yNTN6Ii8+PC9zdmc+" + }, + "categories": [ + { + "id": 1, + "name": "Marketing" + } + ], + "displayName": "APITemplate.io", + "typeVersion": 1 + }, + { + "id": 20, + "icon": "file:lambda.svg", + "name": "n8n-nodes-base.awsLambda", + "defaults": { + "name": "AWS Lambda" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjI1MDAiIHZpZXdCb3g9Ii0zLjAyMyAtMC4yMiA0MjAuOTIzIDQzMy41NCIgd2lkdGg9IjI0NDMiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTIwOC40NSAyMjcuODljLTEuNTkgMi4yNi0yLjkzIDQuMTItNC4yMiA2cS0zMC44NiA0NS40Mi02MS43IDkwLjgzLTI4LjY5IDQyLjI0LTU3LjQ0IDg0LjQzYTMuODggMy44OCAwIDAxLTIuNzMgMS41OXEtNDAuNTktLjM1LTgxLjE2LS44OGMtLjMgMC0uNjEtLjA5LTEuMi0uMThhMTQuNDQgMTQuNDQgMCAwMS43Ni0xLjY1cTI4LjMxLTQzLjg5IDU2LjYyLTg3Ljc2IDI1LjExLTM4Ljg4IDUwLjI1LTc3Ljc0IDI3Ljg2LTQzLjE4IDU1LjY5LTg2LjQyYzIuNzQtNC4yNSA1LjU5LTguNDIgOC4xOS0xMi43NWE1LjI2IDUuMjYgMCAwMC41Ni0zLjgzYy01LTE1Ljk0LTEwLjEtMzEuODQtMTUuMTktNDcuNzQtMi4xOC02LjgxLTQuNDYtMTMuNTgtNi41LTIwLjQzLS42Ni0yLjItMS43NS0yLjg3LTQtMi44Ni0xNyAuMDctMzMuOS4wNS01MC44NS4wNS0zLjIyIDAtMy4yMyAwLTMuMjMtMy4xOCAwLTIwLjg0IDAtNDEuNjgtLjA2LTYyLjUyIDAtMi4zMi43Ni0yLjg0IDIuOTQtMi44NHE1MS4xOS4wOSAxMDIuNCAwYTMuMjkgMy4yOSAwIDAxMy42IDIuNDNxMjcgNjcuOTEgNTQgMTM1Ljc3IDMxLjUgNzkuMTQgNjMgMTU4LjNjNi41MiAxNi4zOCAxMy4wOSAzMi43NSAxOS41NCA0OS4xNy43NyAyIDEuNTcgMi4zOCAzLjU5IDEuNzYgMTcuODktNS41MyAzNS44Mi0xMC45MSA1My43LTE2LjQ1IDIuMjUtLjcgMy4wNy0uMjMgMy43NyAyIDYuMSAxOS4xNyAxMi4zMiAzOC4zIDE4LjUgNTcuNDUuMjEuNjYuMzcgMS4zMy42MiAyLjI1LTEuMjguNDctMi40OCAxLTMuNzEgMS4zNHEtNjEgMTkuMzMtMTIxLjkzIDM4LjY4Yy0xLjk0LjYxLTIuNTItLjA1LTMuMTctMS42OHEtMTguNjEtNDcuMTYtMzcuMzEtOTQuMjgtMTguMjktNDYuMTQtMzYuNi05Mi4yOGMtMS44My00LjYyLTMuNjMtOS4yNi01LjQ2LTEzLjg4LS4yOS0uNzktLjY5LTEuNDgtMS4yNy0yLjd6IiBmaWxsPSIjZmE3ZTE0Ii8+PC9zdmc+" + }, + "categories": [ + { + "id": 5, + "name": "Development" + } + ], + "displayName": "AWS Lambda", + "typeVersion": 1 + }, + { + "id": 24, + "icon": "file:ses.svg", + "name": "n8n-nodes-base.awsSes", + "defaults": { + "name": "AWS SES" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgNzQuMzc1IDg1IiBmaWxsPSIjZmZmIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIj48dXNlIHhsaW5rOmhyZWY9IiNhIiB4PSIyLjE4OCIgeT0iMi41Ii8+PHN5bWJvbCBpZD0iYSIgb3ZlcmZsb3c9InZpc2libGUiPjxnIHN0cm9rZT0ibm9uZSI+PHBhdGggZD0iTTE2LjU1OCAxMi43NUwwIDM4LjU5MWwxNi41NTggMjUuODQxIDEzLjIyNy0zLjMyNC42NTQtNDQuODY5LTEzLjg4MS0zLjQ4OXoiIGZpbGw9IiM4NzY5MjkiLz48cGF0aCBkPSJNMzUuMDQ5IDU5Ljc4NmwtMTguNDkxIDQuNjQ1VjEyLjc1bDE4LjQ5MSA0LjY0NXY0Mi4zOTF6IiBmaWxsPSIjZDlhNzQxIi8+PGcgZmlsbD0iIzg3NjkyOSI+PHBhdGggZD0iTTMyLjg0OSAyMS42MTRMMzUuMDUgODAgNzAgNjIuODY3bC0uMDEtNDMuNjE1LTguOTE0IDEuNDQ4LTI4LjIyOC45MTN6Ii8+PHBhdGggZD0iTTQ2LjE4NCAzMy4xNDlsMTAuOTA2LS42MzIgMTAuNzc4LTE5LjE2NEw0MC42MTIgMCAzMC40MzkgNC4zNjRsMTUuNzQ1IDI4Ljc4NXoiLz48L2c+PHBhdGggZD0iTTQwLjYxMiAwbDI3LjI1NiAxMy4zNTNMNTcuMDkgMzIuNTE3IDQwLjYxMiAweiIgZmlsbD0iI2Q5YTc0MSIvPjxwYXRoIGQ9Ik0zNS4wNDkgNS41MzlMNTcuMDkgNDQuNzQybDMuNzg4IDIyLjU5NUwzNS4wNDkgODBsLTEwLjQ2LTUuMTMxVjkuNjRsMTAuNDYtNC4xMDF6IiBmaWxsPSIjODc2OTI5Ii8+PHBhdGggZD0iTTY5Ljk5MSAxOS4yNTFMNzAgNjIuODY3IDM1LjA1IDgwVjUuNTM5bDIyLjA0MSAzOS4yMDNMNjkuOTkgMTkuMjUxeiIgZmlsbD0iI2Q5YTc0MSIvPjwvZz48L3N5bWJvbD48L3N2Zz4=" + }, + "categories": [ + { + "id": 5, + "name": "Development" + }, + { + "id": 6, + "name": "Communication" + } + ], + "displayName": "AWS SES", + "typeVersion": 1 + }, + { + "id": 38, + "icon": "file:chargebee.png", + "name": "n8n-nodes-base.chargebeeTrigger", + "defaults": { + "name": "Chargebee Trigger" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAA8CAMAAAANIilAAAAAk1BMVEUAAAD9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azXS0qjqAAAAMHRSTlMAAta1CPr3HAyb8mNPLBLq5MmGZ1wi28+soEQX7pZ9dkk/MijDurCMims54b6lb1YyU8hoAAACDklEQVRIx+2V2bKiMBBAmyTs+w6CIi64O/n/rxtKJwIjJPA4Nfc8acOpTnfSAX7493G2QZN6Zeml+GwVS0xiNSXtYeBIm6lKEaJf3C5kjrvz6SioFruVTKcIJEG1D8oBa1wXUy6+w2lVxhFFuU+0j4KOjwdOjUHQnHJ/DVZ4iaXXatTo2OuhPuEWXm9fLOhxPQjldeeuyV/NqGT+su1ucZuRkt5PMRElDmCEbdK2MNcmKnaZi2EUrd7GMEHEXGMPi8mYfFruqjd2NLrE+/P9oF9nyCuW+P4JhcorcBbL4dc2WSxyEcoBe9Vi5yJlEc8RyTp7ldVo0w8rkZyzN3ddExjR4sw7TmZhzSRhEddZ3m2TRar5+3z8hDZ/xlMSnzCPnbDu9NcPhLItzKAZG0hJnHVYYhnDYtSS2RksJ+fcQi0qAbDtKXknU84oWPQJtizvxanpmcCQJ3VtiA1lUlbdzk7rfs/bzwFSVR/bsaDhjHukvvfJsbBXm8S+UboXTwfDQFjXG6S0/dde18oGrN20TI4DOzHaA3drzAJWPqVy2Fa+5qTWDn05AKJJbS+eafsHtUlRUgAHB/dkJT+ddJzI7U+3kopjVgiGUwroF7J/IWAac+7RFeo0D6X3daQCiRBNZs2XVrmDr4e2zQ1aVhrMwwl9+Z1Zzw42ptQPHZiPFJv5IXENWXGvdRhL8MN/y2+uRpQ1fWz5HAAAAABJRU5ErkJggg==" + }, + "categories": [ + { + "id": 8, + "name": "Finance & Accounting" + } + ], + "displayName": "Chargebee Trigger", + "typeVersion": 1 + }, + { + "id": 42, + "icon": "file:clickup.svg", + "name": "n8n-nodes-base.clickUpTrigger", + "defaults": { + "name": "ClickUp Trigger" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSItMTAgMCAxNTUgMTU1IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxkZWZzPjxsaW5lYXJHcmFkaWVudCB4MT0iMCUiIHkxPSI2OC4wMSUiIHkyPSI2OC4wMSUiIGlkPSJhIj48c3RvcCBzdG9wLWNvbG9yPSIjODkzMEZEIiBvZmZzZXQ9IjAlIi8+PHN0b3Agc3RvcC1jb2xvcj0iIzQ5Q0NGOSIgb2Zmc2V0PSIxMDAlIi8+PC9saW5lYXJHcmFkaWVudD48bGluZWFyR3JhZGllbnQgeDE9IjAlIiB5MT0iNjguMDElIiB5Mj0iNjguMDElIiBpZD0iYiI+PHN0b3Agc3RvcC1jb2xvcj0iI0ZGMDJGMCIgb2Zmc2V0PSIwJSIvPjxzdG9wIHN0b3AtY29sb3I9IiNGRkM4MDAiIG9mZnNldD0iMTAwJSIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxnIGZpbGw9Im5vbmUiPjxwYXRoIGQ9Ik0uNCAxMTkuMTJsMjMuODEtMTguMjRDMzYuODYgMTE3LjM5IDUwLjMgMTI1IDY1LjI2IDEyNWMxNC44OCAwIDI3Ljk0LTcuNTIgNDAuMDItMjMuOWwyNC4xNSAxNy44QzExMiAxNDIuNTIgOTAuMzQgMTU1IDY1LjI2IDE1NWMtMjUgMC00Ni44Ny0xMi40LTY0Ljg2LTM1Ljg4eiIgZmlsbD0idXJsKCNhKSIvPjxwYXRoIGZpbGw9InVybCgjYikiIGQ9Ik02NS4xOCAzOS44NEwyMi44IDc2LjM2IDMuMjEgNTMuNjQgNjUuMjcuMTZsNjEuNTcgNTMuNTItMTkuNjggMjIuNjR6Ii8+PC9nPjwvc3ZnPg==" + }, + "categories": [ + { + "id": 4, + "name": "Productivity" + }, + { + "id": 6, + "name": "Communication" + } + ], + "displayName": "ClickUp Trigger", + "typeVersion": 1 + }, + { + "id": 47, + "icon": "file:coda.svg", + "name": "n8n-nodes-base.coda", + "defaults": { + "name": "Coda" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI0LjIuMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCA2MCA2MCIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgNjAgNjA7IiB4bWw6c3BhY2U9InByZXNlcnZlIj4KPHN0eWxlIHR5cGU9InRleHQvY3NzIj4KCS5zdDB7ZmlsbDojRUU1QTI5O30KPC9zdHlsZT4KPHBhdGggY2xhc3M9InN0MCIgZD0iTTQ1LjIsMTYuMmMzLjMsMCw2LjUsMS4zLDguOCwzLjNjMS41LDEuMywzLjgsMC4yLDMuOC0xLjhWNC43YzAtMi41LTIuMS00LjctNC43LTQuN0g2LjMKCUMzLjcsMCwxLjYsMi4xLDEuNiw0Ljd2NTAuN2MwLDIuNSwyLjEsNC42LDQuNyw0LjZoNDYuOGMyLjUsMCw0LjctMi4xLDQuNy00LjdWNDIuM2MwLTItMi4zLTMuMS0zLjgtMS44Yy0yLjQsMi4xLTUuNCwzLjMtOC44LDMuMwoJYy03LjYsMC0xMy43LTYuMi0xMy43LTEzLjhDMzEuNiwyMi40LDM3LjcsMTYuMiw0NS4yLDE2LjJ6Ii8+Cjwvc3ZnPgo=" + }, + "categories": [ + { + "id": 4, + "name": "Productivity" + } + ], + "displayName": "Coda", + "typeVersion": 1 + }, + { + "id": 112, + "icon": "file:harvest.png", + "name": "n8n-nodes-base.harvest", + "defaults": { + "name": "Harvest" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/png;base64,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" + }, + "categories": [ + { + "id": 4, + "name": "Productivity" + } + ], + "displayName": "Harvest", + "typeVersion": 1 + }, + { + "id": 126, + "icon": "file:invoiceNinja.svg", + "name": "n8n-nodes-base.invoiceNinjaTrigger", + "defaults": { + "name": "Invoice Ninja Trigger" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTE2LjI0NyAxMC4zMjZhMS4xNjQgMS4xNjQgMCAxMS0yLjMyOCAwIDEuMTY0IDEuMTY0IDAgMDEyLjMyOCAwem0tNi4yODggMGExLjE2NCAxLjE2NCAwIDExLTIuMzI5IDAgMS4xNjQgMS4xNjQgMCAwMTIuMzI5IDB6bS0uMTQgMTMuNTJjLTQuNzEyLS45OC04LjIyNy00LjI1Ny05LjQ4Mi04Ljg0Mi0uNDIxLTEuNTM3LS40MjEtNC40OSAwLTYuMDI3QzEuNTA2IDQuNzA5IDQuNzMgMS40ODUgOC45OTcuMzE2YzEuNTM4LS40MjEgNC40OS0uNDIxIDYuMDI4IDAgNC4yNjcgMS4xNjkgNy40OTIgNC4zOTMgOC42NiA4LjY2LjI0Ljg3NC4yOTQgMS40My4yOTQgMy4wMTQgMCAxLjU4NC0uMDU0IDIuMTQtLjI5MyAzLjAxNC0xLjE3IDQuMjcxLTQuNDM5IDcuNTM2LTguNjYxIDguNjUtMS4zOTEuMzY3LTMuOTE2LjQ2LTUuMjA2LjE5MnptNi42NC05LjMxNWMtMy4wNDctMS4zNDgtNC4wNTQtMS43MzctNC41LTEuNzM3LS40NDYgMC0xLjQzMy4zOC00LjM4IDEuNjg0LTIuMDkxLjkyNi0zLjgyOCAxLjc2LTMuODYgMS43OWgxNi42NjN6bS05Ljg3My0uMzYxYzEuNjIxLS43MjkgMy4wNi0xLjM4NyAzLjE5Ni0xLjQ2NC4yNTgtLjE0NS4zMzctLjA5LTUuMjg1LTMuNjgyLS41Ni0uMzU4LTEuMDIzLS42OTgtMS4wMjUtLjY1djcuMTlhNzkwLjEgNzkwLjEgMCAwMDMuMTE0LTEuMzk0em0xNC4wNzgtMi4xOTRWOC40MTdjMC0uMTEtMS42NzYuOTkzLTMuNDk2IDIuMTItMyAxLjg1NC0zLjI4MSAyLjA2LTMuMDA0IDIuMTg1IDEuMzQ1LjYxMSA2LjQyIDIuODYyIDYuNSAyLjg3MnptLTguMTY5LjExYy41NDUuMTI1LjY0My4xMDQgMS4yMjYtLjI2My4zNDktLjIyLjY1NS0uNDE5LjY4MS0uNDQyLjAyNi0uMDI0LS4wNS0uMTgxLS4xNjctLjM1LS4xMTgtLjE2OC0uMjE1LS41LS4yMTUtLjczOVY5Ljg2bC0uNTY5LjIxYy0uNzI2LjI2Ny0yLjI4LjI3LTMgLjAwNWwtLjU1Ni0uMjA1LjAxMy40NTJjLjAwNy4yNi0uMDg4LjU2My0uMjI1LjcxNS0uMjMyLjI1Ni0uMjIuMjc2LjQ1LjcyNi42NC40MzIuNzI1LjQ1NSAxLjIzLjMyN2EyLjM0OSAyLjM0OSAwIDAxMS4xMzItLjAwMnptLTQuMjMtMi42NWMtLjEwNS0uMTEzLTIuOTctLjk1NC0zLjAzMy0uODkxLS4wMy4wMy41MDQuNDE0IDEuMTg2Ljg1NGwxLjI0LjguMzQtLjM0NGMuMTg2LS4xODguMzA3LS4zNzcuMjY4LS40MnptOS43Ni0uMzczYy40NzMtLjMwNi44LS41NTUuNzI4LS41NTUtLjE1NSAwLTIuODc3LjgwNC0zLjAyNy44OTQtLjA1Ny4wMzQuMDMzLjIyOS4yLjQzM2wuMzA0LjM3LjQ3LS4yOTNjLjI1Ny0uMTYyLjg1NC0uNTQ0IDEuMzI2LS44NXptLTEuNjM2LS41NTVjMi4xMS0uNTkgMy44NjctMS4xMDIgMy45MDQtMS4xMzlIMy41OWMuMTg3LjE4NyA3Ljc3OSAyLjE5NSA4LjMyMyAyLjIwMi40MS4wMDUgMi4wMTQtLjM3NiA0LjQ3Ni0xLjA2M3oiLz48L3N2Zz4=" + }, + "categories": [ + { + "id": 8, + "name": "Finance & Accounting" + } + ], + "displayName": "Invoice Ninja Trigger", + "typeVersion": 1 + } + ] + }, + { + "id": 1206, + "name": "Process Shopify New Orders with CRM and Marketing Platforms", + "totalViews": 471, + "recentViews": 0, + "user": { + "username": "admin" + }, + "createdAt": "2021-08-24T11:23:23.518Z", + "nodes": [ + { + "id": 20, + "icon": "file:lambda.svg", + "name": "n8n-nodes-base.awsLambda", + "defaults": { + "name": "AWS Lambda" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjI1MDAiIHZpZXdCb3g9Ii0zLjAyMyAtMC4yMiA0MjAuOTIzIDQzMy41NCIgd2lkdGg9IjI0NDMiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTIwOC40NSAyMjcuODljLTEuNTkgMi4yNi0yLjkzIDQuMTItNC4yMiA2cS0zMC44NiA0NS40Mi02MS43IDkwLjgzLTI4LjY5IDQyLjI0LTU3LjQ0IDg0LjQzYTMuODggMy44OCAwIDAxLTIuNzMgMS41OXEtNDAuNTktLjM1LTgxLjE2LS44OGMtLjMgMC0uNjEtLjA5LTEuMi0uMThhMTQuNDQgMTQuNDQgMCAwMS43Ni0xLjY1cTI4LjMxLTQzLjg5IDU2LjYyLTg3Ljc2IDI1LjExLTM4Ljg4IDUwLjI1LTc3Ljc0IDI3Ljg2LTQzLjE4IDU1LjY5LTg2LjQyYzIuNzQtNC4yNSA1LjU5LTguNDIgOC4xOS0xMi43NWE1LjI2IDUuMjYgMCAwMC41Ni0zLjgzYy01LTE1Ljk0LTEwLjEtMzEuODQtMTUuMTktNDcuNzQtMi4xOC02LjgxLTQuNDYtMTMuNTgtNi41LTIwLjQzLS42Ni0yLjItMS43NS0yLjg3LTQtMi44Ni0xNyAuMDctMzMuOS4wNS01MC44NS4wNS0zLjIyIDAtMy4yMyAwLTMuMjMtMy4xOCAwLTIwLjg0IDAtNDEuNjgtLjA2LTYyLjUyIDAtMi4zMi43Ni0yLjg0IDIuOTQtMi44NHE1MS4xOS4wOSAxMDIuNCAwYTMuMjkgMy4yOSAwIDAxMy42IDIuNDNxMjcgNjcuOTEgNTQgMTM1Ljc3IDMxLjUgNzkuMTQgNjMgMTU4LjNjNi41MiAxNi4zOCAxMy4wOSAzMi43NSAxOS41NCA0OS4xNy43NyAyIDEuNTcgMi4zOCAzLjU5IDEuNzYgMTcuODktNS41MyAzNS44Mi0xMC45MSA1My43LTE2LjQ1IDIuMjUtLjcgMy4wNy0uMjMgMy43NyAyIDYuMSAxOS4xNyAxMi4zMiAzOC4zIDE4LjUgNTcuNDUuMjEuNjYuMzcgMS4zMy42MiAyLjI1LTEuMjguNDctMi40OCAxLTMuNzEgMS4zNHEtNjEgMTkuMzMtMTIxLjkzIDM4LjY4Yy0xLjk0LjYxLTIuNTItLjA1LTMuMTctMS42OHEtMTguNjEtNDcuMTYtMzcuMzEtOTQuMjgtMTguMjktNDYuMTQtMzYuNi05Mi4yOGMtMS44My00LjYyLTMuNjMtOS4yNi01LjQ2LTEzLjg4LS4yOS0uNzktLjY5LTEuNDgtMS4yNy0yLjd6IiBmaWxsPSIjZmE3ZTE0Ii8+PC9zdmc+" + }, + "categories": [ + { + "id": 5, + "name": "Development" + } + ], + "displayName": "AWS Lambda", + "typeVersion": 1 + }, + { + "id": 38, + "icon": "file:chargebee.png", + "name": "n8n-nodes-base.chargebeeTrigger", + "defaults": { + "name": "Chargebee Trigger" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAA8CAMAAAANIilAAAAAk1BMVEUAAAD9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azXS0qjqAAAAMHRSTlMAAta1CPr3HAyb8mNPLBLq5MmGZ1wi28+soEQX7pZ9dkk/MijDurCMims54b6lb1YyU8hoAAACDklEQVRIx+2V2bKiMBBAmyTs+w6CIi64O/n/rxtKJwIjJPA4Nfc8acOpTnfSAX7493G2QZN6Zeml+GwVS0xiNSXtYeBIm6lKEaJf3C5kjrvz6SioFruVTKcIJEG1D8oBa1wXUy6+w2lVxhFFuU+0j4KOjwdOjUHQnHJ/DVZ4iaXXatTo2OuhPuEWXm9fLOhxPQjldeeuyV/NqGT+su1ucZuRkt5PMRElDmCEbdK2MNcmKnaZi2EUrd7GMEHEXGMPi8mYfFruqjd2NLrE+/P9oF9nyCuW+P4JhcorcBbL4dc2WSxyEcoBe9Vi5yJlEc8RyTp7ldVo0w8rkZyzN3ddExjR4sw7TmZhzSRhEddZ3m2TRar5+3z8hDZ/xlMSnzCPnbDu9NcPhLItzKAZG0hJnHVYYhnDYtSS2RksJ+fcQi0qAbDtKXknU84oWPQJtizvxanpmcCQJ3VtiA1lUlbdzk7rfs/bzwFSVR/bsaDhjHukvvfJsbBXm8S+UboXTwfDQFjXG6S0/dde18oGrN20TI4DOzHaA3drzAJWPqVy2Fa+5qTWDn05AKJJbS+eafsHtUlRUgAHB/dkJT+ddJzI7U+3kopjVgiGUwroF7J/IWAac+7RFeo0D6X3daQCiRBNZs2XVrmDr4e2zQ1aVhrMwwl9+Z1Zzw42ptQPHZiPFJv5IXENWXGvdRhL8MN/y2+uRpQ1fWz5HAAAAABJRU5ErkJggg==" + }, + "categories": [ + { + "id": 8, + "name": "Finance & Accounting" + } + ], + "displayName": "Chargebee Trigger", + "typeVersion": 1 + }, + { + "id": 43, + "icon": "file:clockify.svg", + "name": "n8n-nodes-base.clockifyTrigger", + "defaults": { + "name": "Clockify Trigger" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjU2IiBoZWlnaHQ9IjI1NiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBmaWxsPSJub25lIiBkPSJNMTQgMGgyMjd2MjU2SDE0eiIvPjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMTY1LjEyNCA4Ny45OWw1OC43NzctNTguODE0IDE2LjgyNSAxNi44MjMtNTguNzc4IDU4LjgxNS0xNi44MjQtMTYuODIzem0tMjMuODE3IDYwLjEwNWMtMTEuNTAzIDAtMjAuODIyLTkuMzYtMjAuODIyLTIwLjkxOCAwLTExLjU0NiA5LjMxOS0yMC45MTggMjAuODIyLTIwLjkxOCAxMS41MDMgMCAyMC44MjIgOS4zNzIgMjAuODIyIDIwLjkxOCAwIDExLjU1OC05LjMxOSAyMC45MTgtMjAuODIyIDIwLjkxOHpNMjQxIDIwOC44NDVsLTE2LjgyNCAxNi44MzUtNTguNzc4LTU4LjgxNiAxNi44MjUtMTYuODM1TDI0MSAyMDguODQ1eiIgZmlsbD0iIzIyMiIvPjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMTQxLjkxMyAyMTcuNjM3YzExLjg0OCAwIDIzLjEzNi0yLjM2NCAzMy40ODItNi41NjdsMjguNzA4IDI4Ljc0QzE4NS42OTIgMjUwLjA4OSAxNjQuNSAyNTYgMTQxLjkxMyAyNTYgNzEuMjc0IDI1NiAxNCAxOTguNjg5IDE0IDEyOC4wMDYgMTQgNTcuMzExIDcxLjI3NSAwIDE0MS45MTMgMGMyMi4zNjEgMCA0My4zNjIgNS43NjcgNjEuNjQxIDE1Ljg1NmwtMjguMjMxIDI4LjI2MWMtMTAuMzMzLTQuMTc5LTIxLjU4NS02LjU0My0zMy40MS02LjU0My00OS40NyAwLTg5LjU3NSA0MC4zMDktODkuNTc1IDkwLjAzOCAwIDQ5LjcxNiA0MC4xMDQgOTAuMDI1IDg5LjU3NSA5MC4wMjV6IiBmaWxsPSIjMDNBOUY0Ii8+PC9zdmc+" + }, + "categories": [ + { + "id": 4, + "name": "Productivity" + }, + { + "id": 7, + "name": "Utility" + } + ], + "displayName": "Clockify Trigger", + "typeVersion": 1 + }, + { + "id": 71, + "icon": "file:emelia.svg", + "name": "n8n-nodes-base.emelia", + "defaults": { + "name": "Emelia" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSItMiAyIDQ1IDQ1IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik0yOS43MTQgNi41MzlIOS4wNzhhMi4xMDkgMi4xMDkgMCAwMC0yLjAwNyAyLjc2Mmw1LjQ4NCAxNi44NjMgNC4yMTktMS44MTdhMi42OSAyLjY5IDAgMDEtLjAyNC0uMzQgMi42NDEgMi42NDEgMCAxMTEuMDIzIDIuMDgxIDI0MTYuNzEgMjQxNi43MSAwIDAxLTQuNDE1IDIuNTQ1bDUuMTU2IDE1Ljg1MmEuODY1Ljg2NSAwIDAwMS42NDUgMGwxMS40OC0zNS4yOTNhMi4wMjYgMi4wMjYgMCAwMC0xLjkyNS0yLjY1M3oiIG9wYWNpdHk9Ii45IiBmaWxsPSIjZjRiNDU0Ii8+PHBhdGggZD0iTTM3LjE1NCAyMS4yNjFMMS4wNzEgMTIuNTc3YS44NjUuODY1IDAgMDAtLjg2MSAxLjRsMTAuOTA5IDEyLjggNS42NTYtMi40MzZhMi42OTQgMi42OTQgMCAwMS0uMDI0LS4zNCAyLjY0MSAyLjY0MSAwIDExMS4wMjMgMi4wODEgMjE3OC4xNSAyMTc4LjE1IDAgMDEtNC44NiAyLjhsMTEuMjkxIDEzLjI1NWEyLjExIDIuMTEgMCAwMDMuNC0uMjY0bDEwLjgtMTcuNTg2YTIuMDI2IDIuMDI2IDAgMDAtMS4yNTEtMy4wMjZ6IiBmaWxsPSIjZWY2ZDRhIiBvcGFjaXR5PSIuOSIvPjxwYXRoIGQ9Ik0zNy42MzYgMTIuNTc3TDEuNTUzIDIxLjI2MUEyLjAyNSAyLjAyNSAwIDAwLjMwMSAyNC4yOWwzLjQ3MiA1LjY1NiAxMy01LjZhMi42ODkgMi42ODkgMCAwMS0uMDI0LS4zNCAyLjY0MSAyLjY0MSAwIDExMS4wMjMgMi4wODFjLTMuMSAxLjc5MS05LjA3MyA1LjIzNC0xMi4xMDggNi45NGw1LjQzMiA4Ljg0OGEyLjExIDIuMTEgMCAwMDMuNC4yNjRsMjMuOTk1LTI4LjE2MWEuODY1Ljg2NSAwIDAwLS44NTUtMS40MDF6IiBmaWxsPSIjMjAzNTRjIiBvcGFjaXR5PSIuOSIvPjwvc3ZnPg==" + }, + "categories": [ + { + "id": 1, + "name": "Marketing" + }, + { + "id": 6, + "name": "Communication" + } + ], + "displayName": "Emelia", + "typeVersion": 1 + }, + { + "id": 107, + "icon": "file:gotify.png", + "name": "n8n-nodes-base.gotify", + "defaults": { + "name": "Gotify" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/png;base64,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" + }, + "categories": [ + { + "id": 6, + "name": "Communication" + } + ], + "displayName": "Gotify", + "typeVersion": 1 + }, + { + "id": 225, + "icon": "file:strapi.svg", + "name": "n8n-nodes-base.strapi", + "defaults": { + "name": "Strapi" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAyNSIgaGVpZ2h0PSIxMDMyIiBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik0zNDEuMzI4IDB2MzQ0LjA2MWgzNDEuMzMzdjM0NC4wNmgzNDEuMzI5VjBIMzQxLjMyOHoiIGZpbGw9InVybCgjYSkiLz48cGF0aCBkPSJNNjgzIDM0My43MjVIMzQzdjM0NC43MzJoMzQwVjM0My43MjV6IiBmaWxsPSJ1cmwoI2IpIi8+PHBhdGggZD0iTTM0MS4zMzMgMzQ0LjA2MUgwTDM0MS4zMzMgMHYzNDQuMDYxeiIgZmlsbD0idXJsKCNjKSIvPjxwYXRoIGQ9Ik02ODIuMzY3IDEwMzEuMThWNjg3LjQ1N2gzNDIuMDAzTDY4Mi4zNjcgMTAzMS4xOHoiIGZpbGw9InVybCgjZCkiLz48Y2lyY2xlIHI9IjEuNzk2IiB0cmFuc2Zvcm09Im1hdHJpeCgxIDAgMCAtMSAzNjUuMTYyIDMxNC42NjMpIiBmaWxsPSIjOTU2RkZGIi8+PGNpcmNsZSByPSIxLjc5NiIgdHJhbnNmb3JtPSJtYXRyaXgoMSAwIDAgLTEgMzc0LjUwMiAzMTQuNjYzKSIgZmlsbD0iIzk1NkZGRiIvPjxjaXJjbGUgcj0iMS43OTYiIHRyYW5zZm9ybT0ibWF0cml4KDEgMCAwIC0xIDM4My44NDMgMzE0LjY2MykiIGZpbGw9IiM5NTZGRkYiLz48Y2lyY2xlIHI9IjEuNzk2IiB0cmFuc2Zvcm09Im1hdHJpeCgxIDAgMCAtMSAzOTMuMTg0IDMxNC42NjMpIiBmaWxsPSIjOTU2RkZGIi8+PGNpcmNsZSByPSIxLjc5NiIgdHJhbnNmb3JtPSJtYXRyaXgoMSAwIDAgLTEgNDAyLjUyNSAzMTQuNjYzKSIgZmlsbD0iIzk1NkZGRiIvPjxjaXJjbGUgcj0iMS43OTYiIHRyYW5zZm9ybT0ibWF0cml4KDEgMCAwIC0xIDQxMS44NjYgMzE0LjY2MykiIGZpbGw9IiM5NTZGRkYiLz48Y2lyY2xlIHI9IjEuNzk2IiB0cmFuc2Zvcm09Im1hdHJpeCgxIDAgMCAtMSA0MjEuMjA2IDMxNC42NjMpIiBmaWxsPSIjOTU2RkZGIi8+PGNpcmNsZSByPSIxLjc5NiIgdHJhbnNmb3JtPSJtYXRyaXgoMSAwIDAgLTEgNDMwLjU0NiAzMTQuNjYzKSIgZmlsbD0iIzk1NkZGRiIvPjxjaXJjbGUgcj0iMS43OTYiIHRyYW5zZm9ybT0ibWF0cml4KDEgMCAwIC0xIDQzOS44ODcgMzE0LjY2MykiIGZpbGw9IiM5NTZGRkYiLz48Y2lyY2xlIHI9IjEuNzk2IiB0cmFuc2Zvcm09Im1hdHJpeCgxIDAgMCAtMSA0NDkuMjI4IDMxNC42NjMpIiBmaWxsPSIjOTU2RkZGIi8+PGNpcmNsZSByPSIxLjc5NiIgdHJhbnNmb3JtPSJtYXRyaXgoMSAwIDAgLTEgNDU4LjU2OSAzMTQuNjYzKSIgZmlsbD0iIzk1NkZGRiIvPjxjaXJjbGUgcj0iMS43OTYiIHRyYW5zZm9ybT0ibWF0cml4KDEgMCAwIC0xIDM2NS4xNjIgMzA2LjA0MSkiIGZpbGw9IiM5NTZGRkYiLz48Y2lyY2xlIHI9IjEuNzk2IiB0cmFuc2Zvcm09Im1hdHJpeCgxIDAgMCAtMSAzNzQuNTAyIDMwNi4wNDEpIiBmaWxsPSIjOTU2RkZGIi8+PGNpcmNsZSByPSIxLjc5NiIgdHJhbnNmb3JtPSJtYXRyaXgoMSAwIDAgLTEgMzgzLjg0MyAzMDYuMDQxKSIgZmlsbD0iIzk1NkZGRiIvPjxjaXJjbGUgcj0iMS43OTYiIHRyYW5zZm9ybT0ibWF0cml4KDEgMCAwIC0xIDM5My4xODQgMzA2LjA0MSkiIGZpbGw9IiM5NTZGRkYiLz48Y2lyY2xlIHI9IjEuNzk2IiB0cmFuc2Zvcm09Im1hdHJpeCgxIDAgMCAtMSA0MDIuNTI1IDMwNi4wNDEpIiBmaWxsPSIjOTU2RkZGIi8+PGNpcmNsZSByPSIxLjc5NiIgdHJhbnNmb3JtPSJtYXRyaXgoMSAwIDAgLTEgNDExLjg2NiAzMDYuMDQxKSIgZmlsbD0iIzk1NkZGRiIvPjxjaXJjbGUgcj0iMS43OTYiIHRyYW5zZm9ybT0ibWF0cml4KDEgMCAwIC0xIDQyMS4yMDYgMzA2LjA0MSkiIGZpbGw9IiM5NTZGRkYiLz48Y2lyY2xlIHI9IjEuNzk2IiB0cmFuc2Zvcm09Im1hdHJpeCgxIDAgMCAtMSA0MzAuNTQ2IDMwNi4wNDEpIiBmaWxsPSIjOTU2RkZGIi8+PGNpcmNsZSByPSIxLjc5NiIgdHJhbnNmb3JtPSJtYXRyaXgoMSAwIDAgLTEgMzY1LjE2MiAyOTcuNDE5KSIgZmlsbD0iIzk1NkZGRiIvPjxjaXJjbGUgcj0iMS43OTYiIHRyYW5zZm9ybT0ibWF0cml4KDEgMCAwIC0xIDM3NC41MDIgMjk3LjQxOSkiIGZpbGw9IiM5NTZGRkYiLz48Y2lyY2xlIHI9IjEuNzk2IiB0cmFuc2Zvcm09Im1hdHJpeCgxIDAgMCAtMSAzODMuODQzIDI5Ny40MTkpIiBmaWxsPSIjOTU2RkZGIi8+PGNpcmNsZSByPSIxLjc5NiIgdHJhbnNmb3JtPSJtYXRyaXgoMSAwIDAgLTEgMzkzLjE4NCAyOTcuNDE5KSIgZmlsbD0iIzk1NkZGRiIvPjxjaXJjbGUgcj0iMS43OTYiIHRyYW5zZm9ybT0ibWF0cml4KDEgMCAwIC0xIDQwMi41MjUgMjk3LjQxOSkiIGZpbGw9IiM5NTZGRkYiLz48Y2lyY2xlIHI9IjEuNzk2IiB0cmFuc2Zvcm09Im1hdHJpeCgxIDAgMCAtMSAzNjUuMTYyIDI4OC43OTYpIiBmaWxsPSIjOTU2RkZGIi8+PGNpcmNsZSByPSIxLjc5NiIgdHJhbnNmb3JtPSJtYXRyaXgoMSAwIDAgLTEgMzc0LjUwMiAyODguNzk3KSIgZmlsbD0iIzk1NkZGRiIvPjxjaXJjbGUgcj0iMS43OTYiIHRyYW5zZm9ybT0ibWF0cml4KDEgMCAwIC0xIDM4My44NDMgMjg4Ljc5NykiIGZpbGw9IiM5NTZGRkYiLz48Y2lyY2xlIGN4PSI3MTYuMTYyIiBjeT0iNjM4Ljc5NiIgcj0iMS43OTYiIGZpbGw9IiM5NTZGRkYiLz48Y2lyY2xlIGN4PSI3MjUuNTAyIiBjeT0iNjM4Ljc5NiIgcj0iMS43OTYiIGZpbGw9IiM5NTZGRkYiLz48Y2lyY2xlIGN4PSI3MzQuODQzIiBjeT0iNjM4Ljc5NiIgcj0iMS43OTYiIGZpbGw9IiM5NTZGRkYiLz48Y2lyY2xlIGN4PSI3NDQuMTg0IiBjeT0iNjM4Ljc5NiIgcj0iMS43OTYiIGZpbGw9IiM5NTZGRkYiLz48Y2lyY2xlIGN4PSI3NTMuNTI1IiBjeT0iNjM4Ljc5NiIgcj0iMS43OTYiIGZpbGw9IiM5NTZGRkYiLz48Y2lyY2xlIGN4PSI3NjIuODY2IiBjeT0iNjM4Ljc5NiIgcj0iMS43OTYiIGZpbGw9IiM5NTZGRkYiLz48Y2lyY2xlIGN4PSI3NzIuMjA2IiBjeT0iNjM4Ljc5NiIgcj0iMS43OTYiIGZpbGw9IiM5NTZGRkYiLz48Y2lyY2xlIGN4PSI3MTYuMTYyIiBjeT0iNjQ3LjQxOSIgcj0iMS43OTYiIGZpbGw9IiM5NTZGRkYiLz48Y2lyY2xlIGN4PSI3MjUuNTAyIiBjeT0iNjQ3LjQxOSIgcj0iMS43OTYiIGZpbGw9IiM5NTZGRkYiLz48Y2lyY2xlIGN4PSI3MzQuODQzIiBjeT0iNjQ3LjQxOSIgcj0iMS43OTYiIGZpbGw9IiM5NTZGRkYiLz48Y2lyY2xlIGN4PSI3NDQuMTg0IiBjeT0iNjQ3LjQxOSIgcj0iMS43OTYiIGZpbGw9IiM5NTZGRkYiLz48Y2lyY2xlIGN4PSI3NTMuNTI1IiBjeT0iNjQ3LjQxOSIgcj0iMS43OTYiIGZpbGw9IiM5NTZGRkYiLz48Y2lyY2xlIGN4PSI3NjIuODY2IiBjeT0iNjQ3LjQxOSIgcj0iMS43OTYiIGZpbGw9IiM5NTZGRkYiLz48Y2lyY2xlIGN4PSI3MTYuMTYyIiBjeT0iNjU2LjA0MSIgcj0iMS43OTYiIGZpbGw9IiM5NTZGRkYiLz48Y2lyY2xlIGN4PSI3MjUuNTAyIiBjeT0iNjU2LjA0IiByPSIxLjc5NiIgZmlsbD0iIzk1NkZGRiIvPjxjaXJjbGUgY3g9IjczNC44NDMiIGN5PSI2NTYuMDQiIHI9IjEuNzk2IiBmaWxsPSIjOTU2RkZGIi8+PGNpcmNsZSBjeD0iNzQ0LjE4NCIgY3k9IjY1Ni4wNCIgcj0iMS43OTYiIGZpbGw9IiM5NTZGRkYiLz48Y2lyY2xlIGN4PSI3NTMuNTI1IiBjeT0iNjU2LjA0IiByPSIxLjc5NiIgZmlsbD0iIzk1NkZGRiIvPjxjaXJjbGUgY3g9IjcxNi4xNjIiIGN5PSI2NjQuNjY0IiByPSIxLjc5NiIgZmlsbD0iIzk1NkZGRiIvPjxjaXJjbGUgY3g9IjcyNS41MDIiIGN5PSI2NjQuNjYzIiByPSIxLjc5NiIgZmlsbD0iIzk1NkZGRiIvPjxjaXJjbGUgY3g9IjczNC44NDMiIGN5PSI2NjQuNjYzIiByPSIxLjc5NiIgZmlsbD0iIzk1NkZGRiIvPjxkZWZzPjxsaW5lYXJHcmFkaWVudCBpZD0iYSIgeDE9IjEwNzIuMzYiIHkxPSItNTEuNDA3IiB4Mj0iNTUzLjAzMSIgeTI9IjQ0Ni45NDUiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj48c3RvcCBvZmZzZXQ9Ii4wMjEiIHN0b3AtY29sb3I9IiM5NTZGRkYiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiMxQzFCN0UiLz48L2xpbmVhckdyYWRpZW50PjxsaW5lYXJHcmFkaWVudCBpZD0iYiIgeDE9IjYzNC4zNjUiIHkxPSIzOTYuNSIgeDI9IjI3Ny4xNzciIHkyPSI3MTIuOTAxIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agc3RvcC1jb2xvcj0iIzk1NkZGRiIvPjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iIzFBMjY3MCIvPjwvbGluZWFyR3JhZGllbnQ+PGxpbmVhckdyYWRpZW50IGlkPSJjIiB4MT0iMTcwLjY2NyIgeTE9IjAiIHgyPSIxNzAuNjY3IiB5Mj0iMzQ0LjA2MSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPjxzdG9wIG9mZnNldD0iLjAyMSIgc3RvcC1jb2xvcj0iIzk1NkZGRiIvPjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iIzFDMUI3RSIvPjwvbGluZWFyR3JhZGllbnQ+PGxpbmVhckdyYWRpZW50IGlkPSJkIiB4MT0iMTA4NS4xOCIgeTE9IjU1Ni4yMiIgeDI9IjY5NS42NTQiIHkyPSI5NjcuMDQyIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agc3RvcC1jb2xvcj0iIzk1NkZGRiIvPjxzdG9wIG9mZnNldD0iLjgzOSIgc3RvcC1jb2xvcj0iIzFDMUI3RSIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjwvc3ZnPg==" + }, + "categories": [ + { + "id": 1, + "name": "Marketing" + }, + { + "id": 3, + "name": "Data & Storage" + } + ], + "displayName": "Strapi", + "typeVersion": 1 + } + ] + }, + { + "id": 1207, + "name": "Run Weekly Inventories on Shopify Sales ", + "totalViews": 424, + "recentViews": 0, + "user": { + "username": "admin" + }, + "createdAt": "2021-08-24T15:08:34.063Z", + "nodes": [ + { + "id": 7, + "icon": "file:airtable.svg", + "name": "n8n-nodes-base.airtable", + "defaults": { + "name": "Airtable" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyMDAgMTcwIj48cGF0aCBkPSJNODkgNC44TDE2LjIgMzQuOWMtNC4xIDEuNy00IDcuNC4xIDkuMWw3My4yIDI5YzYuNCAyLjYgMTMuNiAyLjYgMjAgMGw3My4yLTI5YzQuMS0xLjYgNC4xLTcuNC4xLTkuMWwtNzMtMzAuMUMxMDMuMiAyIDk1LjcgMiA4OSA0LjgiIGZpbGw9IiNmY2I0MDAiLz48cGF0aCBkPSJNMTA1LjkgODguOXY3Mi41YzAgMy40IDMuNSA1LjggNi43IDQuNWw4MS42LTMxLjdjMS45LS43IDMuMS0yLjUgMy4xLTQuNVY1Ny4yYzAtMy40LTMuNS01LjgtNi43LTQuNUwxMDkgODQuM2MtMS45LjgtMy4xIDIuNi0zLjEgNC42IiBmaWxsPSIjMThiZmZmIi8+PHBhdGggZD0iTTg2LjkgOTIuNmwtMjQuMiAxMS43LTIuNSAxLjJMOS4xIDEzMGMtMy4yIDEuNi03LjQtLjgtNy40LTQuNFY1Ny41YzAtMS4zLjctMi40IDEuNi0zLjMuNC0uNC44LS43IDEuMi0uOSAxLjItLjcgMy0uOSA0LjQtLjNsNzcuNSAzMC43YzQgMS41IDQuMyA3LjEuNSA4LjkiIGZpbGw9IiNmODJiNjAiLz48cGF0aCBkPSJNODYuOSA5Mi42bC0yNC4yIDExLjctNTkuNC01MGMuNC0uNC44LS43IDEuMi0uOSAxLjItLjcgMy0uOSA0LjQtLjNsNzcuNSAzMC43YzQgMS40IDQuMyA3IC41IDguOCIgZmlsbD0iI2JhMWU0NSIvPjwvc3ZnPg==" + }, + "categories": [ + { + "id": 3, + "name": "Data & Storage" + } + ], + "displayName": "Airtable", + "typeVersion": 2 + }, + { + "id": 14, + "icon": "file:apiTemplateIo.svg", + "name": "n8n-nodes-base.apiTemplateIo", + "defaults": { + "name": "APITemplate.io" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4MCIgaGVpZ2h0PSI4MCIgdmlld0JveD0iMCAwIDIzLjE2NyAyMS4xNjciPjxwYXRoIGZpbGw9IiM5MWJjZTUiIGQ9Ik0xOC42MTEuNTA0TDMuNDczIDExLjI0bDQuNTk3IDIuMjEzIDEuMDY2IDMuODgzIDMuMTcxLTIuMjI2IDIuODAyLjk1MXoiLz48cGF0aCBmaWxsPSIjMWYyMTJiIiBkPSJNOS4xNzIgMTcuNTk3YS4yNjQuMjY0IDAgMDEtLjI5LS4xOTJsLTEuMDM1LTMuNzY3LTQuNDg4LTIuMTZhLjI2NS4yNjUgMCAwMS0uMDM5LS40NTRMMTguNDU4LjI4OGEuMjY0LjI2NCAwIDAxLjQxLjI3NGwtMy41IDE1LjU1NmEuMjY1LjI2NSAwIDAxLS4zNDQuMTkzbC0yLjY3NC0uOTA4LTMuMDYxIDIuMTVhLjI3My4yNzMgMCAwMS0uMTE3LjA0NHptLTUuMTgtNi40MDFsNC4xOTMgMi4wMTdjLjA2OS4wMzQuMTIuMDk1LjE0LjE2OWwuOTY3IDMuNTIgMi44NjMtMi4wMWEuMjY1LjI2NSAwIDAxLjIzNy0uMDMzbDIuNTI0Ljg1NkwxOC4yIDEuMTE5eiIvPjxwYXRoIGZpbGw9IiMzYTg0YzEiIGQ9Ik05LjMzOSAxNy4wNzlsLjk0Ni0yLjkxM0wxOC42NDIuNzI4IDguMjI2IDEzLjIzNXoiLz48cGF0aCBmaWxsPSIjMWYyMTJiIiBkPSJNOS4zNTYgMTcuMjFhLjEzMi4xMzIgMCAwMS0uMTQzLS4xNzJsLjk0Ni0yLjkxM2EuMTYuMTYgMCAwMS4wMTQtLjAzbDcuMzkzLTExLjg4OC05LjQxOSAxMS4xNTZhLjEzMy4xMzMgMCAwMS0uMjAyLS4xNzFMMTguNTQuNjQyYS4xMzIuMTMyIDAgMDEuMjE0LjE1NmwtOC4zNDggMTMuNDI0LS45NDEgMi44OThhLjEzMi4xMzIgMCAwMS0uMTA5LjA5em0tMy4yMTUgMS4wNDVjLTEuMTY4LjAyOS0yLjMwMy0uMzczLTMuMDY3LTEuMjg3LS40ODUtLjU4LS42ODMtMS41NzQuMjc1LTEuNzA1Ljg1OS0uMTE5IDEuMjI5LjQ4NSAxLjQ4IDEuMTk2LjgwNCAyLjAyNi4xNiAyLjkzOC0xLjY4IDQuMjcyLS4xNDQuMDkuMDE1LjI5Ni4xNTkuMjA2IDEuMjE4LTEgMS44MjktMS40NjYgMi4xLTIuNDQ4LjEzNC0xLjM2LS40MDItMy4wOTEtMS4zODQtMy40MzYtMS4xMi0uMzgyLTIuMDYzLjUxNi0xLjQ5OSAxLjU4NS42OTkgMS4zMjQgMi4yMyAxLjkxNCAzLjY1MiAxLjg3OC4xNy0uMDA0LjEzMy0uMjY2LS4wMzYtLjI2MXoiLz48cGF0aCBmaWxsPSIjMWYyMTJiIiBkPSJNNy4yMzUgMTguMTRhMTQuOTUgMTQuOTUgMCAwMS0uNzU5LjA5Yy0uMTcuMDE3LS4xMzMuMjc5LjAzNi4yNjIuMjUzLS4wMjUuNTA3LS4wNTIuNzU4LS4wOS4xNjktLjAyNi4xMzMtLjI4OC0uMDM1LS4yNjJ6bTEuMTk0LS41MDRhOC40NiA4LjQ2IDAgMDEtLjg2NS4zNDRjLS4xNjIuMDU0LS4wNTcuMjk3LjEwNC4yNDNhOC4zNyA4LjM3IDAgMDAuODY1LS4zNDRjLjE1NC0uMDcxLjA1LS4zMTUtLjEwNC0uMjQzeiIvPjxwYXRoIGQ9Ik0xMi41OTkgMTUuMjJhLjEzMS4xMzEgMCAwMS0uMDctLjAxbC0yLjE2OC0uOTI0YS4xMzMuMTMzIDAgMDEuMTAzLS4yNDNsMi4xNjkuOTIzYS4xMzMuMTMzIDAgMDEtLjAzNC4yNTN6Ii8+PC9zdmc+" + }, + "categories": [ + { + "id": 1, + "name": "Marketing" + } + ], + "displayName": "APITemplate.io", + "typeVersion": 1 + }, + { + "id": 18, + "icon": "file:autopilot.svg", + "name": "n8n-nodes-base.autopilot", + "defaults": { + "name": "Autopilot" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjM4IDI2IDM1IDM1Ij48Y2lyY2xlIGN4PSI1MCIgY3k9IjUwIiByPSI0MCIgc3Ryb2tlPSIjMThkNGIyIiBzdHJva2Utd2lkdGg9IjMiIGZpbGw9IiMxOGQ0YjIiLz48cGF0aCBmaWxsPSIjZmZmIiBkPSJNNDUuNCA0Mi42aDE5LjlsMy40LTQuOEg0MmwzLjQgNC44em0zLjEgOC4zaDEzLjFsMy40LTQuOEg0NS40bDMuMSA0Ljh6bTU0LS43Ii8+PC9zdmc+" + }, + "categories": [ + { + "id": 1, + "name": "Marketing" + } + ], + "displayName": "Autopilot", + "typeVersion": 1 + }, + { + "id": 20, + "icon": "file:lambda.svg", + "name": "n8n-nodes-base.awsLambda", + "defaults": { + "name": "AWS Lambda" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjI1MDAiIHZpZXdCb3g9Ii0zLjAyMyAtMC4yMiA0MjAuOTIzIDQzMy41NCIgd2lkdGg9IjI0NDMiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTIwOC40NSAyMjcuODljLTEuNTkgMi4yNi0yLjkzIDQuMTItNC4yMiA2cS0zMC44NiA0NS40Mi02MS43IDkwLjgzLTI4LjY5IDQyLjI0LTU3LjQ0IDg0LjQzYTMuODggMy44OCAwIDAxLTIuNzMgMS41OXEtNDAuNTktLjM1LTgxLjE2LS44OGMtLjMgMC0uNjEtLjA5LTEuMi0uMThhMTQuNDQgMTQuNDQgMCAwMS43Ni0xLjY1cTI4LjMxLTQzLjg5IDU2LjYyLTg3Ljc2IDI1LjExLTM4Ljg4IDUwLjI1LTc3Ljc0IDI3Ljg2LTQzLjE4IDU1LjY5LTg2LjQyYzIuNzQtNC4yNSA1LjU5LTguNDIgOC4xOS0xMi43NWE1LjI2IDUuMjYgMCAwMC41Ni0zLjgzYy01LTE1Ljk0LTEwLjEtMzEuODQtMTUuMTktNDcuNzQtMi4xOC02LjgxLTQuNDYtMTMuNTgtNi41LTIwLjQzLS42Ni0yLjItMS43NS0yLjg3LTQtMi44Ni0xNyAuMDctMzMuOS4wNS01MC44NS4wNS0zLjIyIDAtMy4yMyAwLTMuMjMtMy4xOCAwLTIwLjg0IDAtNDEuNjgtLjA2LTYyLjUyIDAtMi4zMi43Ni0yLjg0IDIuOTQtMi44NHE1MS4xOS4wOSAxMDIuNCAwYTMuMjkgMy4yOSAwIDAxMy42IDIuNDNxMjcgNjcuOTEgNTQgMTM1Ljc3IDMxLjUgNzkuMTQgNjMgMTU4LjNjNi41MiAxNi4zOCAxMy4wOSAzMi43NSAxOS41NCA0OS4xNy43NyAyIDEuNTcgMi4zOCAzLjU5IDEuNzYgMTcuODktNS41MyAzNS44Mi0xMC45MSA1My43LTE2LjQ1IDIuMjUtLjcgMy4wNy0uMjMgMy43NyAyIDYuMSAxOS4xNyAxMi4zMiAzOC4zIDE4LjUgNTcuNDUuMjEuNjYuMzcgMS4zMy42MiAyLjI1LTEuMjguNDctMi40OCAxLTMuNzEgMS4zNHEtNjEgMTkuMzMtMTIxLjkzIDM4LjY4Yy0xLjk0LjYxLTIuNTItLjA1LTMuMTctMS42OHEtMTguNjEtNDcuMTYtMzcuMzEtOTQuMjgtMTguMjktNDYuMTQtMzYuNi05Mi4yOGMtMS44My00LjYyLTMuNjMtOS4yNi01LjQ2LTEzLjg4LS4yOS0uNzktLjY5LTEuNDgtMS4yNy0yLjd6IiBmaWxsPSIjZmE3ZTE0Ii8+PC9zdmc+" + }, + "categories": [ + { + "id": 5, + "name": "Development" + } + ], + "displayName": "AWS Lambda", + "typeVersion": 1 + }, + { + "id": 26, + "icon": "file:sns.svg", + "name": "n8n-nodes-base.awsSnsTrigger", + "defaults": { + "name": "AWS SNS Trigger" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQ5MCIgaGVpZ2h0PSIyNTAwIiB2aWV3Qm94PSIwIDAgMjU2IDI1NyIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWlkWU1pZCI+PHBhdGggZD0iTTk4Ljg3NSAyMzIuMDMzbC0yNi40MzMtNy40MDgtMjUuMDAxLTI4LjUwOCAzMS4yNzItLjg2MyAyMC4xNjIgMzYuNzc5bS02MS4xMjUtMTguOGwtMTQuODc1LTQuMTY2LTE0LjA1OC0xNi4wMzQgMTcuMDgyLTIuODA5IDExLjg1MSAyMy4wMDkiIGZpbGw9IiM5OTVCODAiLz48cGF0aCBkPSJNMCAxOTEuMDE3bDE1LjIwNCAzLjA5MSAyLjIwNy0zLjg4MlY1OC41MDNsLTIuMjA3LTIuNTYxTDAgNjQuNnYxMjYuNDE3IiBmaWxsPSIjN0IzRjY1Ii8+PHBhdGggZD0iTTczLjkzMyA2OS43MDhMMTUuMjA4IDU1Ljk0MnYxMzguMTY2bDguNzk4LS44MTggMTMuNzQ0IDE5Ljk0MyAxMC42LTIyLjIwNSAyNS41ODMtMi4zNzhWNjkuNzA4IiBmaWxsPSIjQzE3QjlEIi8+PHBhdGggZD0iTTMzLjk1OCAxOTguMTMzbDI2LjA2MyA1LjI1IDEuNzE2LTQuMDQ1VjM3LjQ0bC0xLjcxNi0zLjY2NS0yNi4wNjMgMTMuMjA4djE1MS4xNSIgZmlsbD0iIzdCM0Y2NSIvPjxwYXRoIGQ9Ik0yMDguNzM0IDgxLjUxNkw2MC4wMjEgMzMuNzc1djE2OS42MTJsMTcuMjIxLTIuMjE2IDIxLjYzMyAzMC44NjIgMTcuMTI2LTM1Ljg1IDkyLjczMy0xMS45MzNWODEuNTE2IiBmaWxsPSIjQzE3QjlEIi8+PHBhdGggZD0iTTE4MS44MzMgMjU2LjQ5MmwtMzcuNTY2LTEwLjUyNS0zNS41MDktNDAuNSA0Ni4wMzMtLjQ2OCAyNy4wNDIgNTEuNDkzIiBmaWxsPSIjOTk1QjgwIi8+PHBhdGggZD0iTTg5LjU5MSAyMDguOTVsMzguMzMgNy40MTcgMi45NzctMi41NjZWNC4xMTdMMTI3LjkyMSAwbC0zOC4zMyAxOS4xNThWMjA4Ljk1IiBmaWxsPSIjN0IzRjY1Ii8+PHBhdGggZD0iTTI1NiA2NC4wMzNMMTI3LjkyNSAwdjIxNi4zNjdsMjIuNTk3LTQuNTI4IDMxLjMxMSA0NC42NTMgMjYuOTAxLTU2LjMwOS0uMDE3LS4wMDJMMjU2IDE5MC43MDhWNjQuMDMzIiBmaWxsPSIjQzE3QjlEIi8+PC9zdmc+" + }, + "categories": [ + { + "id": 5, + "name": "Development" + }, + { + "id": 6, + "name": "Communication" + } + ], + "displayName": "AWS SNS Trigger", + "typeVersion": 1 + }, + { + "id": 38, + "icon": "file:chargebee.png", + "name": "n8n-nodes-base.chargebeeTrigger", + "defaults": { + "name": "Chargebee Trigger" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAA8CAMAAAANIilAAAAAk1BMVEUAAAD9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azXS0qjqAAAAMHRSTlMAAta1CPr3HAyb8mNPLBLq5MmGZ1wi28+soEQX7pZ9dkk/MijDurCMims54b6lb1YyU8hoAAACDklEQVRIx+2V2bKiMBBAmyTs+w6CIi64O/n/rxtKJwIjJPA4Nfc8acOpTnfSAX7493G2QZN6Zeml+GwVS0xiNSXtYeBIm6lKEaJf3C5kjrvz6SioFruVTKcIJEG1D8oBa1wXUy6+w2lVxhFFuU+0j4KOjwdOjUHQnHJ/DVZ4iaXXatTo2OuhPuEWXm9fLOhxPQjldeeuyV/NqGT+su1ucZuRkt5PMRElDmCEbdK2MNcmKnaZi2EUrd7GMEHEXGMPi8mYfFruqjd2NLrE+/P9oF9nyCuW+P4JhcorcBbL4dc2WSxyEcoBe9Vi5yJlEc8RyTp7ldVo0w8rkZyzN3ddExjR4sw7TmZhzSRhEddZ3m2TRar5+3z8hDZ/xlMSnzCPnbDu9NcPhLItzKAZG0hJnHVYYhnDYtSS2RksJ+fcQi0qAbDtKXknU84oWPQJtizvxanpmcCQJ3VtiA1lUlbdzk7rfs/bzwFSVR/bsaDhjHukvvfJsbBXm8S+UboXTwfDQFjXG6S0/dde18oGrN20TI4DOzHaA3drzAJWPqVy2Fa+5qTWDn05AKJJbS+eafsHtUlRUgAHB/dkJT+ddJzI7U+3kopjVgiGUwroF7J/IWAac+7RFeo0D6X3daQCiRBNZs2XVrmDr4e2zQ1aVhrMwwl9+Z1Zzw42ptQPHZiPFJv5IXENWXGvdRhL8MN/y2+uRpQ1fWz5HAAAAABJRU5ErkJggg==" + }, + "categories": [ + { + "id": 8, + "name": "Finance & Accounting" + } + ], + "displayName": "Chargebee Trigger", + "typeVersion": 1 + }, + { + "id": 40, + "icon": "file:clearbit.svg", + "name": "n8n-nodes-base.clearbit", + "defaults": { + "name": "Clearbit" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3MiIgaGVpZ2h0PSI3MiI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJhIiB4MT0iNTAlIiB4Mj0iMTAwJSIgeTE9IjAlIiB5Mj0iMTAwJSI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI0RFRjJGRSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI0RCRjFGRSIvPjwvbGluZWFyR3JhZGllbnQ+PGxpbmVhckdyYWRpZW50IGlkPSJiIiB4MT0iMCUiIHgyPSI1MCUiIHkxPSIwJSIgeTI9IjEwMCUiPjxzdG9wIG9mZnNldD0iMCUiIHN0b3AtY29sb3I9IiM1N0JDRkQiLz48c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiM1MUI1RkQiLz48L2xpbmVhckdyYWRpZW50PjxsaW5lYXJHcmFkaWVudCBpZD0iYyIgeDE9IjM3LjUlIiB4Mj0iNjIuNSUiIHkxPSIwJSIgeTI9IjEwMCUiPjxzdG9wIG9mZnNldD0iMCUiIHN0b3AtY29sb3I9IiMxQ0E3RkQiLz48c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiMxNDhDRkMiLz48L2xpbmVhckdyYWRpZW50PjwvZGVmcz48ZyBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxwYXRoIGZpbGw9InVybCgjYSkiIGQ9Ik03MiAzNnYxNi43N2wtLjAwNC44NjhjLS4wNiA2LjAzNS0uNzUgOC4zNTMtMiAxMC42ODhhMTMuNjMgMTMuNjMgMCAwMS01LjY3IDUuNjdsLS4zMjYuMTcxQzYxLjY1OCA3MS4zNjQgNTkuMTYgNzIgNTIuNzcgNzJIMzZWMzZoMzZ6Ii8+PHBhdGggZmlsbD0idXJsKCNiKSIgZD0iTTY0LjMyNiAyLjAwM2ExMy42MyAxMy42MyAwIDAxNS42NyA1LjY3bC4xNzEuMzI3QzcxLjM2NCAxMC4zNDIgNzIgMTIuODQgNzIgMTkuMjNWMzZIMzZWMGgxNi43N2M2LjY4NyAwIDkuMTEyLjY5NiAxMS41NTYgMi4wMDN6Ii8+PHBhdGggZmlsbD0idXJsKCNjKSIgZD0iTTM2IDB2NzJIMTkuMjNsLS44NjgtLjAwNGMtNi4wMzUtLjA2LTguMzUzLS43NS0xMC42ODgtMmExMy42MyAxMy42MyAwIDAxLTUuNjctNS42N0wxLjgzMiA2NEMuNjM2IDYxLjY1OCAwIDU5LjE2IDAgNTIuNzdWMTkuMjNjMC02LjY4Ny42OTYtOS4xMTIgMi4wMDMtMTEuNTU2YTEzLjYzIDEzLjYzIDAgMDE1LjY3LTUuNjdMOCAxLjgzMkMxMC4zNDIuNjM2IDEyLjg0IDAgMTkuMjMgMEgzNnoiLz48L2c+PC9zdmc+" + }, + "categories": [ + { + "id": 2, + "name": "Sales" + } + ], + "displayName": "Clearbit", + "typeVersion": 1 + }, + { + "id": 221, + "icon": "file:stackby.png", + "name": "n8n-nodes-base.stackby", + "defaults": { + "name": "Stackby" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/png;base64,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" + }, + "categories": [ + { + "id": 3, + "name": "Data & Storage" + } + ], + "displayName": "Stackby", + "typeVersion": 1 + }, + { + "id": 312, + "icon": "file:perspective.svg", + "name": "n8n-nodes-base.googlePerspective", + "defaults": { + "name": "Google Perspective" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgdmlld0JveD0iNiAxMCAxMjUgMTI1IgogICB2ZXJzaW9uPSIxLjEiCiAgIGlkPSJzdmc0IgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogIDxkZWZzCiAgICAgaWQ9ImRlZnM4Ij4KICAgIDxjbGlwUGF0aAogICAgICAgY2xpcFBhdGhVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBpZD0iY2xpcFBhdGg5MzciPgogICAgICA8cmVjdAogICAgICAgICBzdHlsZT0iZmlsbDojMDAwMGZmO2ZpbGwtcnVsZTpldmVub2RkIgogICAgICAgICBpZD0icmVjdDkzOSIKICAgICAgICAgd2lkdGg9IjEyMy40OTQ3IgogICAgICAgICBoZWlnaHQ9IjExOS4wMjc3MyIKICAgICAgICAgeD0iMTIuMjUiCiAgICAgICAgIHk9IjEyLjQ4MjI3MSIgLz4KICAgIDwvY2xpcFBhdGg+CiAgPC9kZWZzPgogIDxnCiAgICAgaWQ9Imc1MzIiCiAgICAgY2xpcC1wYXRoPSJ1cmwoI2NsaXBQYXRoOTM3KSI+CiAgICA8cGF0aAogICAgICAgZD0iTTEzMS41MyA3MmE1OS43OSA1OS43OSAwIDAgMS01My44MSA1OS40OXYtMTE5QTU5Ljc5IDU5Ljc5IDAgMCAxIDEzMS41MyA3MnpNMTIuMjUgNzh2NTMuNTFoNTMuNTJBNTkuODEgNTkuODEgMCAwIDAgMTIuMjUgNzh6bTAtMTJoNTMuNTJWMTIuNTFBNTkuODEgNTkuODEgMCAwIDAgMTIuMjUgNjZ6bTE3Ni44My0zNy4wNkgyMjNjMjEuNzcgMCAzNS44IDExLjMyIDM1LjggMjkuNjVzLTE0IDI5LjY0LTM1LjggMjkuNjRoLTE1LjIydjI2LjgyaC0xOC43em0zMy4zNCA0NGMxMS44MSAwIDE3LjM0LTQuNTUgMTcuMzQtMTQuMzlzLTUuNTMtMTQuNC0xNy4zNC0xNC40aC0xNC42NFY3M3ptMzcuMTQgMTEuNzJ2LS42MWMwLTE5LjkzIDEyLjkxLTMzIDMyLTMzIDE4IDAgMzEgMTIuNjcgMzEgMzMuNDZ2NC4xOGgtNDQuOWMxIDkuNzIgNiAxNC4yNyAxNC4xNCAxNC4yNyA1LjkxIDAgMTAuMDktMi41OCAxMS45NC03aDE4LjJjLTIuNyAxMi40Mi0xNC42NCAyMS0zMC4yNiAyMS0xOS44Mi4wNC0zMi4xMi0xMS45Ni0zMi4xMi0zMi4zem0xOC41Ny03Ljg3aDI2LjJjLTEuNzItOC4xMi02LjI3LTEyLjE4LTEyLjkxLTEyLjE4cy0xMS42OSAzLjk0LTEzLjI5IDEyLjE4em01NC43My0yMy43NGgxNy41OXY5LjQ3QzM1NC4yNiA1NS44OCAzNjEgNTIuMzEgMzcwIDUyLjMxaDMuNTZ2MTdoLTE1LjVjLTQuNjcgMC03LjEzIDIuNDYtNy4xMyA3djM4Ljc1aC0xOC4wN3pNMzc4LjEyIDk2SDM5NWMuNjIgNC45MiA0LjU1IDcuNSAxMy4yOSA3LjUgNy4zOCAwIDEwLjk0LTIuMzMgMTAuOTQtNS42NSAwLTIuNzEtMS43Mi0zLjk0LTYuMTUtNC44bC0xNC43OS0yLjg1Yy0xMy42NS0yLjQ2LTE5LjA2LTguMTItMTkuMDYtMTguMDggMC0xMi4zIDEwLTIxIDI3LjkyLTIxIDE2LjczIDAgMjguMTcgOC4zNyAyOSAyMC41NGgtMTYuODJjLS43NC00LjQyLTQuOC03LTEyLjMtNy03IDAtMTAuNDYgMi4yMi0xMC40NiA1LjU0IDAgMi41OCAxLjcyIDMuODEgNiA0LjY3bDE0Ljg4IDIuODNjMTMuNTMgMi40NiAxOC45NCA4LjEyIDE4Ljk0IDE4LjIxIDAgMTIuOTEtMTAuNDUgMjEuMTUtMjguMjkgMjEuMTUtMTguNzktLjA2LTI5LjM3LTguNzgtMjkuOTgtMjEuMDZ6bTY4LjEzLTQyLjk1aDE3LjU5djkuODRjMy43LTcuMzggMTAuNzEtMTEuNTYgMTkuNTYtMTEuNTYgMTQuODkgMCAyNi4zMyAxMS42OCAyNi4zMyAzMnYuNjJjMCAyMC4xNy0xMi4wNiAzMi44NC0yNi45NCAzMi44NC04LjEyIDAtMTQuNzYtMy4zMi0xOC40NS05LjQ3djMwLjYzaC0xOC4wOXpNNDkxIDg0LjQydi0xLjZjMC0xMS4zMi00LjkyLTE3LjM1LTEzLjE2LTE3LjM1LTguNjEgMC0xMy41MyA1LjkxLTEzLjUzIDE3LjIzdjIuN2MwIDEyLjE4IDUgMTcuMjIgMTMuMTYgMTcuMjIgNy45IDAgMTMuNTMtNi42MiAxMy41My0xOC4yem0yNi4xLjI0di0uNjFjMC0xOS45MyAxMi45Mi0zMyAzMi0zMyAxOCAwIDMxIDEyLjY3IDMxIDMzLjQ2djQuMThoLTQ0LjljMSA5LjcyIDYgMTQuMjcgMTQuMTUgMTQuMjcgNS45IDAgMTAuMDgtMi41OCAxMS45My03aDE4LjJjLTIuNyAxMi40Mi0xNC42MyAyMS0zMC4yNSAyMS0xOS44My4wNC0zMi4xMy0xMS45Ni0zMi4xMy0zMi4zem0xOC41Ny03Ljg3aDI2LjIxYy0xLjczLTguMTItNi4yOC0xMi4xOC0xMi45Mi0xMi4xOHMtMTEuNjkgMy45NC0xMy4yOSAxMi4xOHptNTEuNTMgNy43NXYtLjc0YzAtMTkuMzEgMTMuMTYtMzIuNzIgMzEuMzctMzIuNzIgMTYuMjQgMCAyNy44IDkuMzUgMjkuNCAyNC42aC0xNy43MWMtLjc0LTYuNjQtNS40Mi0xMC40NS0xMS40NC0xMC40NS03Ljg4IDAtMTMgNi41Mi0xMyAxOC4ydjEuNDhjMCAxMS44MSA1LjE2IDE4IDEyLjc5IDE4IDYuNTIgMCAxMC45NS00LjA2IDExLjgxLTExLjJoMTcuNzFjLTEuNiAxNS43NS0xMyAyNS4zNC0yOS43NyAyNS4zNEM2MDAgMTE3IDU4Ny4yIDEwNC4zNCA1ODcuMiA4NC41NHptNzYuMDEgOS4zNVY2Ny4zMmgtMTAuN1Y1My4wNWgxMC43di0xOGgxOHYxOGgxOC4yMXYxNC4yN2gtMTguMjV2MzAuNzVjMCAxLjcyLjc0IDIuNDYgMi40NiAyLjQ2aDE1Ljc1djE0LjUySDY4NS42Yy0xNC41MiAwLTIyLjM5LTcuNTEtMjIuMzktMjEuMTZ6bTQ2LTU4LjMxYTEwLjA5IDEwLjA5IDAgMCAxIDIwLjE3IDAgMTAuMDkgMTAuMDkgMCAxIDEtMjAuMTcgMHptMSAxNy40N2gxOC4wOHY2MmgtMTguMXptMjUuMTkgMEg3NTRsMTQuMzkgNDUuMjcgMTQuMjctNDUuMjdoMTcuNzFsLTIwLjkxIDYyaC0yMy4xNXptNjguODggMzEuNjF2LS42MWMwLTE5LjkzIDEyLjkyLTMzIDMyLTMzIDE4IDAgMzEgMTIuNjcgMzEgMzMuNDZ2NC4xOGgtNDQuOWMxIDkuNzIgNiAxNC4yNyAxNC4xNSAxNC4yNyA1LjkgMCAxMC4wOS0yLjU4IDExLjkzLTdoMTguMjFjLTIuNzEgMTIuNDItMTQuNjQgMjEtMzAuMjYgMjEtMTkuODMuMDQtMzIuMTMtMTEuOTYtMzIuMTMtMzIuM3ptMTguNTctNy44N2gyNi4yMWMtMS43My04LjEyLTYuMjgtMTIuMTgtMTIuOTItMTIuMThzLTExLjY5IDMuOTQtMTMuMjkgMTIuMTh6IgogICAgICAgZmlsbD0iIzQ1MTczNSIKICAgICAgIGlkPSJwYXRoMiIgLz4KICA8L2c+Cjwvc3ZnPgo=" + }, + "categories": [ + { + "id": 7, + "name": "Utility" + }, + { + "id": 10, + "name": "Analytics" + } + ], + "displayName": "Google Perspective", + "typeVersion": 1 + } + ] + }, + { + "id": 628, + "name": "Receive updates from HubSpot when a new contact is created", + "totalViews": 376, + "recentViews": 0, + "user": { + "username": "admin" + }, + "createdAt": "2020-08-31T00:27:22.044Z", + "nodes": [ + { + "id": 303, + "icon": "file:notion.svg", + "name": "n8n-nodes-base.notionTrigger", + "defaults": { + "name": "Notion Trigger" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjI1MDAiIHdpZHRoPSIyNTAwIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjEyIDAuMTkgNDg3LjYxOSA1MTAuOTQxIj48cGF0aCBkPSJNOTYuMDg1IDkxLjExOGMxNS44MSAxMi44NDUgMjEuNzQxIDExLjg2NSA1MS40MyA5Ljg4NGwyNzkuODg4LTE2LjgwNmM1LjkzNiAwIDEtNS45MjItLjk4LTYuOTA2TDM3OS45NCA0My42ODZjLTguOTA3LTYuOTE1LTIwLjc3My0xNC44MzQtNDMuNTE2LTEyLjg1M0w2NS40MDggNTAuNmMtOS44ODQuOTgtMTEuODU4IDUuOTIyLTcuOTIyIDkuODgzem0xNi44MDQgNjUuMjI4djI5NC40OTFjMCAxNS44MjcgNy45MDkgMjEuNzQ4IDI1LjcxIDIwLjc2OWwzMDcuNTk3LTE3Ljc5OWMxNy44MS0uOTc5IDE5Ljc5NC0xMS44NjUgMTkuNzk0LTI0LjcyMlYxMzYuNTdjMC0xMi44MzYtNC45MzgtMTkuNzU4LTE1Ljg0LTE4Ljc3bC0zMjEuNDQyIDE4Ljc3Yy0xMS44NjMuOTk3LTE1LjgyIDYuOTMxLTE1LjgyIDE5Ljc3NnptMzAzLjY1OSAxNS43OTdjMS45NzIgOC45MDMgMCAxNy43OTgtOC45MiAxOC43OTlsLTE0LjgyIDIuOTUzdjIxNy40MTJjLTEyLjg2OCA2LjkxNi0yNC43MzQgMTAuODctMzQuNjIyIDEwLjg3LTE1LjgzMSAwLTE5Ljc5Ni00Ljk0NS0zMS42NTQtMTkuNzZsLTk2Ljk0NC0xNTIuMTl2MTQ3LjI0OGwzMC42NzcgNi45MjJzMCAxNy43OC0yNC43NSAxNy43OGwtNjguMjMgMy45NThjLTEuOTgyLTMuOTU4IDAtMTMuODMyIDYuOTIxLTE1LjgxbDE3LjgwNS00LjkzNVYyMTAuN2wtMjQuNzIxLTEuOTgxYy0xLjk4My04LjkwMyAyLjk1NS0yMS43NCAxNi44MTItMjIuNzM2bDczLjE5NS00LjkzNEwzNTguMTg2IDMzNS4yMlYxOTguODM2bC0yNS43MjMtMi45NTJjLTEuOTc0LTEwLjg4NCA1LjkyNy0xOC43ODcgMTUuODE5LTE5Ljc2N3pNNDIuNjUzIDIzLjkxOWwyODEuOS0yMC43NmMzNC42MTgtMi45NjkgNDMuNTI1LS45OCA2NS4yODMgMTQuODI1bDg5Ljk4NiA2My4yNDdjMTQuODQ4IDEwLjg3NiAxOS43OTcgMTMuODM3IDE5Ljc5NyAyNS42OTN2MzQ2Ljg4M2MwIDIxLjc0LTcuOTIgMzQuNTk3LTM1LjYwOCAzNi41NjRMMTM2LjY0IDUxMC4xNGMtMjAuNzg1Ljk5MS0zMC42NzctMS45NzEtNDEuNTYyLTE1LjgxNWwtNjYuMjY3LTg1Ljk3OEMxNi45MzggMzkyLjUyIDEyIDM4MC42OCAxMiAzNjYuODI4VjU4LjQ5NWMwLTE3Ljc3OCA3LjkyMi0zMi42MDggMzAuNjUzLTM0LjU3NnoiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvc3ZnPg==" + }, + "categories": [ + { + "id": 4, + "name": "Productivity" + } + ], + "displayName": "Notion Trigger", + "typeVersion": 1 + } + ] + }, + { + "id": 1344, + "name": "Save Email Attachments to Cloud Storage (Nextcloud)", + "totalViews": 362, + "recentViews": 0, + "user": { + "username": "admin" + }, + "createdAt": "2021-11-29T13:59:16.771Z", + "nodes": [ + { + "id": 10, + "icon": "file:amqp.png", + "name": "n8n-nodes-base.amqp", + "defaults": { + "name": "AMQP Sender" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAA8CAYAAAA6/NlyAAAB7UlEQVRoge2W4W3CMBCFj26QjkBHSEdIR4AR6Ah0BBgBRqAjhBFgBBghHaEVlV29PN0lDr+o9D7JEjhn+975bJ8JIYQQQgghhBBCCCGEEA9CY2bf0NaBW2uyu7UN2XSOzTyY60J2BzNbObbsH7eTmS2mhHJHE1wmCD7A93ngEAquHaHc2omCcysSXQW74g32BHfwfTEiuCoQm9vuDsEndPYpELxKjjBj0foCEXX6XdM3by3c7aOZPZvZzMzeaBzbIh9pzIuZXaG/RqNIMAq7Ur8XCHQ2kx3LC56DMQ39X4LI23zbAd88ruRHD09wTVF5p+/eBZI5g7O8w5FgXOvsZAI7PxRwS4HGIPbm8wRjBL/Sgp/QNyQYHWySmOxgJBgFeGnPfZHgDVyufET+YMEVCdo7gziCTBbGmRKlGQpCMXOnj+1L6B0JFsxndO3cjjZyjo6OnZeqGb5gqhTQS3qKeK1SwbesfB3IrF/awqu+g8Dgs5SLE37SciHiPUv8rLVp7k2wdl63tDDqgTs8lqpINWGXbSTKe9rlJgXME7C9I6V7oGAWsEzv2gzeN2TstkbCZyIJWBYKWUwtF4foKGU9TpRGdZDSdVDpDNXSVVBLt5TeucS9K6X/E3USX3rshBBCCCGEEEIIIYQQ4tExsx8PuuPnwhCIbgAAAABJRU5ErkJggg==" + }, + "categories": [ + { + "id": 5, + "name": "Development" + }, + { + "id": 6, + "name": "Communication" + } + ], + "displayName": "AMQP Sender", + "typeVersion": 1 + }, + { + "id": 14, + "icon": "file:apiTemplateIo.svg", + "name": "n8n-nodes-base.apiTemplateIo", + "defaults": { + "name": "APITemplate.io" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4MCIgaGVpZ2h0PSI4MCIgdmlld0JveD0iMCAwIDIzLjE2NyAyMS4xNjciPjxwYXRoIGZpbGw9IiM5MWJjZTUiIGQ9Ik0xOC42MTEuNTA0TDMuNDczIDExLjI0bDQuNTk3IDIuMjEzIDEuMDY2IDMuODgzIDMuMTcxLTIuMjI2IDIuODAyLjk1MXoiLz48cGF0aCBmaWxsPSIjMWYyMTJiIiBkPSJNOS4xNzIgMTcuNTk3YS4yNjQuMjY0IDAgMDEtLjI5LS4xOTJsLTEuMDM1LTMuNzY3LTQuNDg4LTIuMTZhLjI2NS4yNjUgMCAwMS0uMDM5LS40NTRMMTguNDU4LjI4OGEuMjY0LjI2NCAwIDAxLjQxLjI3NGwtMy41IDE1LjU1NmEuMjY1LjI2NSAwIDAxLS4zNDQuMTkzbC0yLjY3NC0uOTA4LTMuMDYxIDIuMTVhLjI3My4yNzMgMCAwMS0uMTE3LjA0NHptLTUuMTgtNi40MDFsNC4xOTMgMi4wMTdjLjA2OS4wMzQuMTIuMDk1LjE0LjE2OWwuOTY3IDMuNTIgMi44NjMtMi4wMWEuMjY1LjI2NSAwIDAxLjIzNy0uMDMzbDIuNTI0Ljg1NkwxOC4yIDEuMTE5eiIvPjxwYXRoIGZpbGw9IiMzYTg0YzEiIGQ9Ik05LjMzOSAxNy4wNzlsLjk0Ni0yLjkxM0wxOC42NDIuNzI4IDguMjI2IDEzLjIzNXoiLz48cGF0aCBmaWxsPSIjMWYyMTJiIiBkPSJNOS4zNTYgMTcuMjFhLjEzMi4xMzIgMCAwMS0uMTQzLS4xNzJsLjk0Ni0yLjkxM2EuMTYuMTYgMCAwMS4wMTQtLjAzbDcuMzkzLTExLjg4OC05LjQxOSAxMS4xNTZhLjEzMy4xMzMgMCAwMS0uMjAyLS4xNzFMMTguNTQuNjQyYS4xMzIuMTMyIDAgMDEuMjE0LjE1NmwtOC4zNDggMTMuNDI0LS45NDEgMi44OThhLjEzMi4xMzIgMCAwMS0uMTA5LjA5em0tMy4yMTUgMS4wNDVjLTEuMTY4LjAyOS0yLjMwMy0uMzczLTMuMDY3LTEuMjg3LS40ODUtLjU4LS42ODMtMS41NzQuMjc1LTEuNzA1Ljg1OS0uMTE5IDEuMjI5LjQ4NSAxLjQ4IDEuMTk2LjgwNCAyLjAyNi4xNiAyLjkzOC0xLjY4IDQuMjcyLS4xNDQuMDkuMDE1LjI5Ni4xNTkuMjA2IDEuMjE4LTEgMS44MjktMS40NjYgMi4xLTIuNDQ4LjEzNC0xLjM2LS40MDItMy4wOTEtMS4zODQtMy40MzYtMS4xMi0uMzgyLTIuMDYzLjUxNi0xLjQ5OSAxLjU4NS42OTkgMS4zMjQgMi4yMyAxLjkxNCAzLjY1MiAxLjg3OC4xNy0uMDA0LjEzMy0uMjY2LS4wMzYtLjI2MXoiLz48cGF0aCBmaWxsPSIjMWYyMTJiIiBkPSJNNy4yMzUgMTguMTRhMTQuOTUgMTQuOTUgMCAwMS0uNzU5LjA5Yy0uMTcuMDE3LS4xMzMuMjc5LjAzNi4yNjIuMjUzLS4wMjUuNTA3LS4wNTIuNzU4LS4wOS4xNjktLjAyNi4xMzMtLjI4OC0uMDM1LS4yNjJ6bTEuMTk0LS41MDRhOC40NiA4LjQ2IDAgMDEtLjg2NS4zNDRjLS4xNjIuMDU0LS4wNTcuMjk3LjEwNC4yNDNhOC4zNyA4LjM3IDAgMDAuODY1LS4zNDRjLjE1NC0uMDcxLjA1LS4zMTUtLjEwNC0uMjQzeiIvPjxwYXRoIGQ9Ik0xMi41OTkgMTUuMjJhLjEzMS4xMzEgMCAwMS0uMDctLjAxbC0yLjE2OC0uOTI0YS4xMzMuMTMzIDAgMDEuMTAzLS4yNDNsMi4xNjkuOTIzYS4xMzMuMTMzIDAgMDEtLjAzNC4yNTN6Ii8+PC9zdmc+" + }, + "categories": [ + { + "id": 1, + "name": "Marketing" + } + ], + "displayName": "APITemplate.io", + "typeVersion": 1 + }, + { + "id": 25, + "icon": "file:sns.svg", + "name": "n8n-nodes-base.awsSns", + "defaults": { + "name": "AWS SNS" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQ5MCIgaGVpZ2h0PSIyNTAwIiB2aWV3Qm94PSIwIDAgMjU2IDI1NyIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWlkWU1pZCI+PHBhdGggZD0iTTk4Ljg3NSAyMzIuMDMzbC0yNi40MzMtNy40MDgtMjUuMDAxLTI4LjUwOCAzMS4yNzItLjg2MyAyMC4xNjIgMzYuNzc5bS02MS4xMjUtMTguOGwtMTQuODc1LTQuMTY2LTE0LjA1OC0xNi4wMzQgMTcuMDgyLTIuODA5IDExLjg1MSAyMy4wMDkiIGZpbGw9IiM5OTVCODAiLz48cGF0aCBkPSJNMCAxOTEuMDE3bDE1LjIwNCAzLjA5MSAyLjIwNy0zLjg4MlY1OC41MDNsLTIuMjA3LTIuNTYxTDAgNjQuNnYxMjYuNDE3IiBmaWxsPSIjN0IzRjY1Ii8+PHBhdGggZD0iTTczLjkzMyA2OS43MDhMMTUuMjA4IDU1Ljk0MnYxMzguMTY2bDguNzk4LS44MTggMTMuNzQ0IDE5Ljk0MyAxMC42LTIyLjIwNSAyNS41ODMtMi4zNzhWNjkuNzA4IiBmaWxsPSIjQzE3QjlEIi8+PHBhdGggZD0iTTMzLjk1OCAxOTguMTMzbDI2LjA2MyA1LjI1IDEuNzE2LTQuMDQ1VjM3LjQ0bC0xLjcxNi0zLjY2NS0yNi4wNjMgMTMuMjA4djE1MS4xNSIgZmlsbD0iIzdCM0Y2NSIvPjxwYXRoIGQ9Ik0yMDguNzM0IDgxLjUxNkw2MC4wMjEgMzMuNzc1djE2OS42MTJsMTcuMjIxLTIuMjE2IDIxLjYzMyAzMC44NjIgMTcuMTI2LTM1Ljg1IDkyLjczMy0xMS45MzNWODEuNTE2IiBmaWxsPSIjQzE3QjlEIi8+PHBhdGggZD0iTTE4MS44MzMgMjU2LjQ5MmwtMzcuNTY2LTEwLjUyNS0zNS41MDktNDAuNSA0Ni4wMzMtLjQ2OCAyNy4wNDIgNTEuNDkzIiBmaWxsPSIjOTk1QjgwIi8+PHBhdGggZD0iTTg5LjU5MSAyMDguOTVsMzguMzMgNy40MTcgMi45NzctMi41NjZWNC4xMTdMMTI3LjkyMSAwbC0zOC4zMyAxOS4xNThWMjA4Ljk1IiBmaWxsPSIjN0IzRjY1Ii8+PHBhdGggZD0iTTI1NiA2NC4wMzNMMTI3LjkyNSAwdjIxNi4zNjdsMjIuNTk3LTQuNTI4IDMxLjMxMSA0NC42NTMgMjYuOTAxLTU2LjMwOS0uMDE3LS4wMDJMMjU2IDE5MC43MDhWNjQuMDMzIiBmaWxsPSIjQzE3QjlEIi8+PC9zdmc+" + }, + "categories": [ + { + "id": 5, + "name": "Development" + }, + { + "id": 6, + "name": "Communication" + } + ], + "displayName": "AWS SNS", + "typeVersion": 1 + } + ] + }, + { + "id": 1225, + "name": "Export New Deals from CRM to Internal Messaging, Email and Database", + "totalViews": 309, + "recentViews": 0, + "user": { + "username": "admin" + }, + "createdAt": "2021-09-10T14:28:58.405Z", + "nodes": [ + { + "id": 20, + "icon": "file:lambda.svg", + "name": "n8n-nodes-base.awsLambda", + "defaults": { + "name": "AWS Lambda" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjI1MDAiIHZpZXdCb3g9Ii0zLjAyMyAtMC4yMiA0MjAuOTIzIDQzMy41NCIgd2lkdGg9IjI0NDMiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTIwOC40NSAyMjcuODljLTEuNTkgMi4yNi0yLjkzIDQuMTItNC4yMiA2cS0zMC44NiA0NS40Mi02MS43IDkwLjgzLTI4LjY5IDQyLjI0LTU3LjQ0IDg0LjQzYTMuODggMy44OCAwIDAxLTIuNzMgMS41OXEtNDAuNTktLjM1LTgxLjE2LS44OGMtLjMgMC0uNjEtLjA5LTEuMi0uMThhMTQuNDQgMTQuNDQgMCAwMS43Ni0xLjY1cTI4LjMxLTQzLjg5IDU2LjYyLTg3Ljc2IDI1LjExLTM4Ljg4IDUwLjI1LTc3Ljc0IDI3Ljg2LTQzLjE4IDU1LjY5LTg2LjQyYzIuNzQtNC4yNSA1LjU5LTguNDIgOC4xOS0xMi43NWE1LjI2IDUuMjYgMCAwMC41Ni0zLjgzYy01LTE1Ljk0LTEwLjEtMzEuODQtMTUuMTktNDcuNzQtMi4xOC02LjgxLTQuNDYtMTMuNTgtNi41LTIwLjQzLS42Ni0yLjItMS43NS0yLjg3LTQtMi44Ni0xNyAuMDctMzMuOS4wNS01MC44NS4wNS0zLjIyIDAtMy4yMyAwLTMuMjMtMy4xOCAwLTIwLjg0IDAtNDEuNjgtLjA2LTYyLjUyIDAtMi4zMi43Ni0yLjg0IDIuOTQtMi44NHE1MS4xOS4wOSAxMDIuNCAwYTMuMjkgMy4yOSAwIDAxMy42IDIuNDNxMjcgNjcuOTEgNTQgMTM1Ljc3IDMxLjUgNzkuMTQgNjMgMTU4LjNjNi41MiAxNi4zOCAxMy4wOSAzMi43NSAxOS41NCA0OS4xNy43NyAyIDEuNTcgMi4zOCAzLjU5IDEuNzYgMTcuODktNS41MyAzNS44Mi0xMC45MSA1My43LTE2LjQ1IDIuMjUtLjcgMy4wNy0uMjMgMy43NyAyIDYuMSAxOS4xNyAxMi4zMiAzOC4zIDE4LjUgNTcuNDUuMjEuNjYuMzcgMS4zMy42MiAyLjI1LTEuMjguNDctMi40OCAxLTMuNzEgMS4zNHEtNjEgMTkuMzMtMTIxLjkzIDM4LjY4Yy0xLjk0LjYxLTIuNTItLjA1LTMuMTctMS42OHEtMTguNjEtNDcuMTYtMzcuMzEtOTQuMjgtMTguMjktNDYuMTQtMzYuNi05Mi4yOGMtMS44My00LjYyLTMuNjMtOS4yNi01LjQ2LTEzLjg4LS4yOS0uNzktLjY5LTEuNDgtMS4yNy0yLjd6IiBmaWxsPSIjZmE3ZTE0Ii8+PC9zdmc+" + }, + "categories": [ + { + "id": 5, + "name": "Development" + } + ], + "displayName": "AWS Lambda", + "typeVersion": 1 + }, + { + "id": 38, + "icon": "file:chargebee.png", + "name": "n8n-nodes-base.chargebeeTrigger", + "defaults": { + "name": "Chargebee Trigger" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAA8CAMAAAANIilAAAAAk1BMVEUAAAD9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azX9azXS0qjqAAAAMHRSTlMAAta1CPr3HAyb8mNPLBLq5MmGZ1wi28+soEQX7pZ9dkk/MijDurCMims54b6lb1YyU8hoAAACDklEQVRIx+2V2bKiMBBAmyTs+w6CIi64O/n/rxtKJwIjJPA4Nfc8acOpTnfSAX7493G2QZN6Zeml+GwVS0xiNSXtYeBIm6lKEaJf3C5kjrvz6SioFruVTKcIJEG1D8oBa1wXUy6+w2lVxhFFuU+0j4KOjwdOjUHQnHJ/DVZ4iaXXatTo2OuhPuEWXm9fLOhxPQjldeeuyV/NqGT+su1ucZuRkt5PMRElDmCEbdK2MNcmKnaZi2EUrd7GMEHEXGMPi8mYfFruqjd2NLrE+/P9oF9nyCuW+P4JhcorcBbL4dc2WSxyEcoBe9Vi5yJlEc8RyTp7ldVo0w8rkZyzN3ddExjR4sw7TmZhzSRhEddZ3m2TRar5+3z8hDZ/xlMSnzCPnbDu9NcPhLItzKAZG0hJnHVYYhnDYtSS2RksJ+fcQi0qAbDtKXknU84oWPQJtizvxanpmcCQJ3VtiA1lUlbdzk7rfs/bzwFSVR/bsaDhjHukvvfJsbBXm8S+UboXTwfDQFjXG6S0/dde18oGrN20TI4DOzHaA3drzAJWPqVy2Fa+5qTWDn05AKJJbS+eafsHtUlRUgAHB/dkJT+ddJzI7U+3kopjVgiGUwroF7J/IWAac+7RFeo0D6X3daQCiRBNZs2XVrmDr4e2zQ1aVhrMwwl9+Z1Zzw42ptQPHZiPFJv5IXENWXGvdRhL8MN/y2+uRpQ1fWz5HAAAAABJRU5ErkJggg==" + }, + "categories": [ + { + "id": 8, + "name": "Finance & Accounting" + } + ], + "displayName": "Chargebee Trigger", + "typeVersion": 1 + }, + { + "id": 40, + "icon": "file:clearbit.svg", + "name": "n8n-nodes-base.clearbit", + "defaults": { + "name": "Clearbit" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3MiIgaGVpZ2h0PSI3MiI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJhIiB4MT0iNTAlIiB4Mj0iMTAwJSIgeTE9IjAlIiB5Mj0iMTAwJSI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI0RFRjJGRSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI0RCRjFGRSIvPjwvbGluZWFyR3JhZGllbnQ+PGxpbmVhckdyYWRpZW50IGlkPSJiIiB4MT0iMCUiIHgyPSI1MCUiIHkxPSIwJSIgeTI9IjEwMCUiPjxzdG9wIG9mZnNldD0iMCUiIHN0b3AtY29sb3I9IiM1N0JDRkQiLz48c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiM1MUI1RkQiLz48L2xpbmVhckdyYWRpZW50PjxsaW5lYXJHcmFkaWVudCBpZD0iYyIgeDE9IjM3LjUlIiB4Mj0iNjIuNSUiIHkxPSIwJSIgeTI9IjEwMCUiPjxzdG9wIG9mZnNldD0iMCUiIHN0b3AtY29sb3I9IiMxQ0E3RkQiLz48c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiMxNDhDRkMiLz48L2xpbmVhckdyYWRpZW50PjwvZGVmcz48ZyBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxwYXRoIGZpbGw9InVybCgjYSkiIGQ9Ik03MiAzNnYxNi43N2wtLjAwNC44NjhjLS4wNiA2LjAzNS0uNzUgOC4zNTMtMiAxMC42ODhhMTMuNjMgMTMuNjMgMCAwMS01LjY3IDUuNjdsLS4zMjYuMTcxQzYxLjY1OCA3MS4zNjQgNTkuMTYgNzIgNTIuNzcgNzJIMzZWMzZoMzZ6Ii8+PHBhdGggZmlsbD0idXJsKCNiKSIgZD0iTTY0LjMyNiAyLjAwM2ExMy42MyAxMy42MyAwIDAxNS42NyA1LjY3bC4xNzEuMzI3QzcxLjM2NCAxMC4zNDIgNzIgMTIuODQgNzIgMTkuMjNWMzZIMzZWMGgxNi43N2M2LjY4NyAwIDkuMTEyLjY5NiAxMS41NTYgMi4wMDN6Ii8+PHBhdGggZmlsbD0idXJsKCNjKSIgZD0iTTM2IDB2NzJIMTkuMjNsLS44NjgtLjAwNGMtNi4wMzUtLjA2LTguMzUzLS43NS0xMC42ODgtMmExMy42MyAxMy42MyAwIDAxLTUuNjctNS42N0wxLjgzMiA2NEMuNjM2IDYxLjY1OCAwIDU5LjE2IDAgNTIuNzdWMTkuMjNjMC02LjY4Ny42OTYtOS4xMTIgMi4wMDMtMTEuNTU2YTEzLjYzIDEzLjYzIDAgMDE1LjY3LTUuNjdMOCAxLjgzMkMxMC4zNDIuNjM2IDEyLjg0IDAgMTkuMjMgMEgzNnoiLz48L2c+PC9zdmc+" + }, + "categories": [ + { + "id": 2, + "name": "Sales" + } + ], + "displayName": "Clearbit", + "typeVersion": 1 + }, + { + "id": 76, + "icon": "fa:sign-in-alt", + "name": "n8n-nodes-base.executeWorkflow", + "defaults": { + "name": "Execute Workflow", + "color": "#ff6d5a" + }, + "iconData": { + "icon": "sign-in-alt", + "type": "icon" + }, + "categories": [ + { + "id": 9, + "name": "Core Nodes" + } + ], + "displayName": "Execute Workflow", + "typeVersion": 1 + }, + { + "id": 112, + "icon": "file:harvest.png", + "name": "n8n-nodes-base.harvest", + "defaults": { + "name": "Harvest" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/png;base64,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" + }, + "categories": [ + { + "id": 4, + "name": "Productivity" + } + ], + "displayName": "Harvest", + "typeVersion": 1 + }, + { + "id": 303, + "icon": "file:notion.svg", + "name": "n8n-nodes-base.notionTrigger", + "defaults": { + "name": "Notion Trigger" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjI1MDAiIHdpZHRoPSIyNTAwIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjEyIDAuMTkgNDg3LjYxOSA1MTAuOTQxIj48cGF0aCBkPSJNOTYuMDg1IDkxLjExOGMxNS44MSAxMi44NDUgMjEuNzQxIDExLjg2NSA1MS40MyA5Ljg4NGwyNzkuODg4LTE2LjgwNmM1LjkzNiAwIDEtNS45MjItLjk4LTYuOTA2TDM3OS45NCA0My42ODZjLTguOTA3LTYuOTE1LTIwLjc3My0xNC44MzQtNDMuNTE2LTEyLjg1M0w2NS40MDggNTAuNmMtOS44ODQuOTgtMTEuODU4IDUuOTIyLTcuOTIyIDkuODgzem0xNi44MDQgNjUuMjI4djI5NC40OTFjMCAxNS44MjcgNy45MDkgMjEuNzQ4IDI1LjcxIDIwLjc2OWwzMDcuNTk3LTE3Ljc5OWMxNy44MS0uOTc5IDE5Ljc5NC0xMS44NjUgMTkuNzk0LTI0LjcyMlYxMzYuNTdjMC0xMi44MzYtNC45MzgtMTkuNzU4LTE1Ljg0LTE4Ljc3bC0zMjEuNDQyIDE4Ljc3Yy0xMS44NjMuOTk3LTE1LjgyIDYuOTMxLTE1LjgyIDE5Ljc3NnptMzAzLjY1OSAxNS43OTdjMS45NzIgOC45MDMgMCAxNy43OTgtOC45MiAxOC43OTlsLTE0LjgyIDIuOTUzdjIxNy40MTJjLTEyLjg2OCA2LjkxNi0yNC43MzQgMTAuODctMzQuNjIyIDEwLjg3LTE1LjgzMSAwLTE5Ljc5Ni00Ljk0NS0zMS42NTQtMTkuNzZsLTk2Ljk0NC0xNTIuMTl2MTQ3LjI0OGwzMC42NzcgNi45MjJzMCAxNy43OC0yNC43NSAxNy43OGwtNjguMjMgMy45NThjLTEuOTgyLTMuOTU4IDAtMTMuODMyIDYuOTIxLTE1LjgxbDE3LjgwNS00LjkzNVYyMTAuN2wtMjQuNzIxLTEuOTgxYy0xLjk4My04LjkwMyAyLjk1NS0yMS43NCAxNi44MTItMjIuNzM2bDczLjE5NS00LjkzNEwzNTguMTg2IDMzNS4yMlYxOTguODM2bC0yNS43MjMtMi45NTJjLTEuOTc0LTEwLjg4NCA1LjkyNy0xOC43ODcgMTUuODE5LTE5Ljc2N3pNNDIuNjUzIDIzLjkxOWwyODEuOS0yMC43NmMzNC42MTgtMi45NjkgNDMuNTI1LS45OCA2NS4yODMgMTQuODI1bDg5Ljk4NiA2My4yNDdjMTQuODQ4IDEwLjg3NiAxOS43OTcgMTMuODM3IDE5Ljc5NyAyNS42OTN2MzQ2Ljg4M2MwIDIxLjc0LTcuOTIgMzQuNTk3LTM1LjYwOCAzNi41NjRMMTM2LjY0IDUxMC4xNGMtMjAuNzg1Ljk5MS0zMC42NzctMS45NzEtNDEuNTYyLTE1LjgxNWwtNjYuMjY3LTg1Ljk3OEMxNi45MzggMzkyLjUyIDEyIDM4MC42OCAxMiAzNjYuODI4VjU4LjQ5NWMwLTE3Ljc3OCA3LjkyMi0zMi42MDggMzAuNjUzLTM0LjU3NnoiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvc3ZnPg==" + }, + "categories": [ + { + "id": 4, + "name": "Productivity" + } + ], + "displayName": "Notion Trigger", + "typeVersion": 1 + } + ] + }, + { + "id": 1221, + "name": "Send Reminders After Meetings", + "totalViews": 281, + "recentViews": 0, + "user": { + "username": "admin" + }, + "createdAt": "2021-09-08T08:45:12.497Z", + "nodes": [ + { + "id": 28, + "icon": "file:beeminder.png", + "name": "n8n-nodes-base.beeminder", + "defaults": { + "name": "Beeminder" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAA8CAMAAAANIilAAAABelBMVEUAAAA6Ojw6Ojw6Ojw6Ojw6Ojw6Ojw7Ozw7Oz46Ojw6Ojw6Ojw6Ojw6Ojw6Ojw6Ojw6Ojw6Ojw6Ojw6Ojw6Ojw6Ojw7Oz07Oz06Ojw6Ojw6Oj07Oz06Ojw6Ojw6Ojw6Ojw6Ojw7Ozw5OTw7Oz06Ojw6Oj06Ojw6Ojw6Ojw6Ojz/ywf/zgYyND03ODw1NjwvMj3/0wRCQUD2xxL/1Af/zwT/0QRPSz8vMj//0Qo9PUAyNUE0NTz/zwotMEAxM0BYUT5wZDp3ajn/0ggxND7vwxP/1QRGREA3OEBUTz5kWz2iiS6skCvtwBb4yQ7uvgw5OUI0NkFKRj9rYDx8bTiXgTG/oCPWsB3btBzhuBn0xBD6yw/9ywz/2QFSTT1cVDs6OjpsYDmCcTaZgzCTfTC6myfFoiTfthrovhlbUz1gWDxoXjqKdzSPejPCoSmylSm2mCjDoCHPqyD9zQz/0wv/1wR0ZjmpjizKpiPTrR/iuhjquw1UUEaPfD6tkjF8XVfuAAAAKXRSTlMA59K3oPqzMQkF9smWj0AsJ+7g2HpcGhGag2JSTPPMxq2piXRuNta/ZZKUy0EAAAOpSURBVEjH7ZVnW9pQGIbD0jqq1tm915NxEggBwpSNWFoHG7TuvbW7/e89SUA+kLbRr/X+ALmS685zzsl73sPc8D/zYhRwOUa0y2GHCxh9ad29DR3bSB8zbIPOc6vuEIBkkFDbPkBdEk0CeGVRHgSy66UYDx0+VloXgNsWZQdCO+fNskp4kSdErTQDsRB6LMrPILi3lPTa3G58eimSl5WKW4DTonwfEBfOuNoWGwjXNmve+gwPTFiUbw0AkdkE52VZllO4xOcI8OTWv72+KWPciM4qbIvAtAg80B72/S1x4qndZu+533sH/F6YbaN8yQKDI+MO++s390YYc3ofw6Af8G80L2VOpnPu74fOo3vm7h1AoB9HAIVMV6WW6w0cZYx7okgA3DUbsx3g1YXFPbcoABDdR2Gv5krb8ajmhKJL8XguKQD3u+W3Wi2Vw4nwt/1sSMvxx7dl1pvYmBdB8ec2PIFEIZ8FRrsX/gkEtZLWhil/yUX08PljObUaDGmxwdWUxNHpX+yLwMMu+RHERZnVkVL7ukKC8ZgeK/pKCc5YgC1VMClzwD8rXa7voU+3kgTarlp8J7EtGss8HCZy8ERh20jvVlUYCL7jgJdtE57m8dhEntvsyGy6PhOCztyvZsdlA4si7GbJJalTF1+XSTsZBx6lk7zCY8Bszvm2rNTXPvCa50/qvzunMtd6ayFHTLY2reeVQCu2PBMxCiUf9xMA/Idi3QhXvn4A7pm0D2QPLziOk87yqh4bmamkw5/don69W057OU45/8QDQ13yQ4C4j1KFVGnPr8WG3PmwxHmb1RW92nl1veIpbC0mgYE+xiyaROdz80E9Vvxx6Em9o9S2f7bmry7l1CS04G4m7aB6iECf5NpCxm2QyRRXIoKmE6L9mff/qbsuGJDYqfxp3tcic3JeVAUY2B8wf2B43Ol02sAveyS24LmkwV1szAEOp/P5w3+0sh4qN+gH60Cr9SQKDFk6qAS1qhVkx2XldT9cIxbkB639Vfv+Xuc7fU1jh8dYnwV5ktbaboPzVn3kIyV0EGalzaDVw8oJRI/TrHI6TVk4KHDc2Qxv9ZjsdYFkqhJtmxq0haTX/ECP9SOWj2132srsnABXr0V5aozavrJkuOFiUABeMFYZtlHbvSlrHa++GhG0Tm+d3lGAZOlBqaQWIgAGmaswOaB13qL8Te+948zVmOoBhGh8ibr9E8yVGQTAE8A2xFyDcWiMDTPXYmIM/U8nmRtuuD6/AQzW+dqtokXXAAAAAElFTkSuQmCC" + }, + "categories": [ + { + "id": 4, + "name": "Productivity" + } + ], + "displayName": "Beeminder", + "typeVersion": 1 + }, + { + "id": 40, + "icon": "file:clearbit.svg", + "name": "n8n-nodes-base.clearbit", + "defaults": { + "name": "Clearbit" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3MiIgaGVpZ2h0PSI3MiI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJhIiB4MT0iNTAlIiB4Mj0iMTAwJSIgeTE9IjAlIiB5Mj0iMTAwJSI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI0RFRjJGRSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI0RCRjFGRSIvPjwvbGluZWFyR3JhZGllbnQ+PGxpbmVhckdyYWRpZW50IGlkPSJiIiB4MT0iMCUiIHgyPSI1MCUiIHkxPSIwJSIgeTI9IjEwMCUiPjxzdG9wIG9mZnNldD0iMCUiIHN0b3AtY29sb3I9IiM1N0JDRkQiLz48c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiM1MUI1RkQiLz48L2xpbmVhckdyYWRpZW50PjxsaW5lYXJHcmFkaWVudCBpZD0iYyIgeDE9IjM3LjUlIiB4Mj0iNjIuNSUiIHkxPSIwJSIgeTI9IjEwMCUiPjxzdG9wIG9mZnNldD0iMCUiIHN0b3AtY29sb3I9IiMxQ0E3RkQiLz48c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiMxNDhDRkMiLz48L2xpbmVhckdyYWRpZW50PjwvZGVmcz48ZyBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxwYXRoIGZpbGw9InVybCgjYSkiIGQ9Ik03MiAzNnYxNi43N2wtLjAwNC44NjhjLS4wNiA2LjAzNS0uNzUgOC4zNTMtMiAxMC42ODhhMTMuNjMgMTMuNjMgMCAwMS01LjY3IDUuNjdsLS4zMjYuMTcxQzYxLjY1OCA3MS4zNjQgNTkuMTYgNzIgNTIuNzcgNzJIMzZWMzZoMzZ6Ii8+PHBhdGggZmlsbD0idXJsKCNiKSIgZD0iTTY0LjMyNiAyLjAwM2ExMy42MyAxMy42MyAwIDAxNS42NyA1LjY3bC4xNzEuMzI3QzcxLjM2NCAxMC4zNDIgNzIgMTIuODQgNzIgMTkuMjNWMzZIMzZWMGgxNi43N2M2LjY4NyAwIDkuMTEyLjY5NiAxMS41NTYgMi4wMDN6Ii8+PHBhdGggZmlsbD0idXJsKCNjKSIgZD0iTTM2IDB2NzJIMTkuMjNsLS44NjgtLjAwNGMtNi4wMzUtLjA2LTguMzUzLS43NS0xMC42ODgtMmExMy42MyAxMy42MyAwIDAxLTUuNjctNS42N0wxLjgzMiA2NEMuNjM2IDYxLjY1OCAwIDU5LjE2IDAgNTIuNzdWMTkuMjNjMC02LjY4Ny42OTYtOS4xMTIgMi4wMDMtMTEuNTU2YTEzLjYzIDEzLjYzIDAgMDE1LjY3LTUuNjdMOCAxLjgzMkMxMC4zNDIuNjM2IDEyLjg0IDAgMTkuMjMgMEgzNnoiLz48L2c+PC9zdmc+" + }, + "categories": [ + { + "id": 2, + "name": "Sales" + } + ], + "displayName": "Clearbit", + "typeVersion": 1 + }, + { + "id": 221, + "icon": "file:stackby.png", + "name": "n8n-nodes-base.stackby", + "defaults": { + "name": "Stackby" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/png;base64,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" + }, + "categories": [ + { + "id": 3, + "name": "Data & Storage" + } + ], + "displayName": "Stackby", + "typeVersion": 1 + } + ] + }, + { + "id": 1205, + "name": "Promote New Shopify Products on Social Media (Twitter and Telegram)", + "totalViews": 219, + "recentViews": 0, + "user": { + "username": "admin" + }, + "createdAt": "2021-08-24T10:40:50.007Z", + "nodes": [ + { + "id": 49, + "icon": "file:contentful.png", + "name": "n8n-nodes-base.contentful", + "defaults": { + "name": "Contentful" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAA8CAMAAAANIilAAAACzVBMVEUAAAD//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////v3wV1FOteFQteH/11/8/f79/v///f3xWlX///4tsN//12BYuOPwXljORzn/2GXwYFry+Pz/1lrwVk/++/lTtuHvPzXvLyD//fdhueNbuOP/12L/1En/1EXvSkPq8/rO5fT/+e5LtOE9suD/9doAd7wIgbYAgbb/2nD/113MSjz+9PTU5/T98O+72++22e+GxedHs+AzseAArN73v74Ae7IAbq7/5KT0m5m6so//22H/2lnwTUb/1DzvRDvOTjrMPzfKRTbvOS7wNCnvKhjIAQD2+v3i7/jc6/fF4PL87Oz86umWy+mSyuh2wOX/9eH74eA/suA4sd8mr98crt763Nz/8tf61tb/8NL50tGiu8/6zs04ncz4yskbjcAAiMD/6r8AfLcohLYdgrYIgrYvhbU0hbT2s7L0pKLzlJHzjYvyf3v/23npzHjxcW3/3l7/2VXuVlDwUUvZT0TUSj7JLTjuJQ3+9fX//Oun0eqbzeqZzOl/wub/+eVtveSsyN8Prd4ArN2Ludnd2ss4ksL2uLcRgrb2t7Xy0LQAcbHorar+4qbczaPiqaDgpJ3nl5T0yY7Juou1rYbyhYLxeHX/33P/32/4yWTyZmHwZmH4yVvtsVPpp1PeWFD/2k/PVk3xUkvMVEnppEjILUTIIDvnoTj/1SvmmRDWLA7KHADUAAA0tfvvAAAAQ3RSTlMA/vz6AgQIBQOJf3UV+fjCbF1aVxEH9+7r5dzXyoRnUEpFKSUc8ejhtq6fm3FvMCwiCryxr6WWjnphQD42DdLNxZg46i9pNgAABRtJREFUSMelloV700AYxpNUJ8wZG3PB3V2vtq50LbSTlm0wxsYYDHfXbbi7u7u7u7u729/Ad+nCJaE0PPDuedbker973+/ukivlVgqlUk7u1EqlgvpLyZUq/BES3zwmODimRUIrfKeC0SQFNvAvMbBGZGU/xMovvG3jYF9olMYBLR9YtRISqULDGPZLT1KpqXpNqyAsmieEFRGgplQKj7bR4RzImZLbisEezMtTdf1dfZFIXGMD3z/QcjUVFIqIp1s8LBYs3LAU5YUQQ1B3OINQTUold8NWxyzyKEx7Uarf4eoYlRSmxclVkPk31pjkklFE+1BKIRsIrIjkXxsFdCyfVlPxDDSK0W59pw8Y0H/6NDtcJ/HpMF9B3ZFIxmeTgBxwSr/AjJWfOSs3CRmNvFmrSmAFVQtYga39uN6cn6fTg3S6QYPNc3KJOc4YRIL7VuKHpjug3KzBeXq+5i+cbf9Fw16oqKLkZcZeqBxhOyB0du2aVTqw5IRxc+ZUQsNe4axDKghna8XwjT2HrckDKMNisWS4+Ix8HdDEWgHW2DiAXzGNLg7feKXnpmGrLFkZ+eaFgwdZLC7aPBAZiTVXdSTPWIN6PCjYXAjwJd35+XNmnxyYucjMuusHZdmB5qzxhIN5HbgmBac/GVWwpbCw54a1lv7dcMwp02Yt0lnyAM+086wrJVIK+KsJqYnxui+71xdu2bxh+AU74tRXvyAzK2vICWBJ7kDIraYa8Evu3Ct1zP31BQXXVyCuK3x0OzPk3JCBdgFcAxetqoIYYrxsXCfDmN2jRt3qgDsgDp+S279vEmFxoZF4uuvKEK/kEV07GQxj9r05DQN5ECBhITDXzfmLnL5rX7YhJbvLCGAlYFkdgIOQjBh3T01JMRiyuyyRhlEcwAF8eG4XSG3ondJdCoa0wQD78OEeuOSU1F6d4VrKudn/wrX+NXYQwIH/OmGxAMfxl6rzrtRsyN31pgjWJCcna4QwUxvgeFq8SXqP3rv3mIBOdo0ggCv4AlwvXLQ9e49+8erzPUwQNn350KUa3EK2Z2v2qKgvejBGv5x4+PD328BoyhKjGY+KJzt3HCXekLURHBwKylv4SI57OnHihKKi0rt9WBL3X2obbzWZjmwDawJHs49kexybvAwef/g0oWPHjkXfng2dgcn0xSMdB0xarfbA8z6cNcChCa53d4TwNfT1I4YnHXxXnLZz5MjtexwOE2a1abZ+YM2V7A+ZKS43CXSt9C0Lm0xpOQ7H+LGAYpnGP0Qaza/UAewLUC54ovG2vFP6vmjSof0YAGEQbK1jc+aBMQdXDoGCWevqfGsY/MaPQ6+BJUoz2SZbCQupvbAxa53AwC2fXqktNtk4T63Waht7ZAcULNghCu6ga4LPG0LTqN+2HGeOyWqzWm02a5qjZM9qxGNpwfmu5F6CxHzm1Z05zhKn01ky2bp9ZTpuI0dsG8hLztg4JKMFtAacZi4funXr5dWL+wj3NQPPBF5jQkchP4TEOCcWJaHZZRKompgGhJPo11Q78BX9DPNHoUhatAxVw91FtMIf+dFSLMNgVi1k2YaGSMZ4ZmVwRBFWmDwKoXIezBnYDLVIZjEdFw5F0X+sFkW0hIVxL/iiXhOE3IZnAC3nLSfz7I6m4hsxuCvD0GRxZHi40MaJXLke8ISoiq6YZUJYEU0T8ZcSkquhU4so/zASuXJ975Z4XDxTkjhrEFI7JtrH29snOrZOK4qHSksNXQXDua/1J0BR0xDIlyn3AAAAAElFTkSuQmCC" + }, + "categories": [ + { + "id": 1, + "name": "Marketing" + }, + { + "id": 5, + "name": "Development" + } + ], + "displayName": "Contentful", + "typeVersion": 1 + }, + { + "id": 107, + "icon": "file:gotify.png", + "name": "n8n-nodes-base.gotify", + "defaults": { + "name": "Gotify" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/png;base64,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" + }, + "categories": [ + { + "id": 6, + "name": "Communication" + } + ], + "displayName": "Gotify", + "typeVersion": 1 + }, + { + "id": 325, + "icon": "file:dropcontact.svg", + "name": "n8n-nodes-base.dropcontact", + "defaults": { + "name": "Dropcontact" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgNTAgNTAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMjkuNTY5MiAxNi4yMTlDMjguMTQ2OSAxNS41MzcxIDI2LjU1MzUgMTUuMTU1MiAyNC44NzA5IDE1LjE1NTJDMTguODYxNSAxNS4xNTUyIDEzLjk4OTkgMjAuMDI2OCAxMy45ODk5IDI2LjAzNjJDMTMuOTg5OSAzMi4wNDU2IDE4Ljg2MTUgMzYuOTE3MiAyNC44NzA5IDM2LjkxNzJDMjguOTYyMSAzNi45MTcyIDMyLjUyNiAzNC42NTkyIDM0LjM4NDMgMzEuMzIxNUwzMS43NjE0IDI5Ljg2NDNDMzAuNDE1NCAzMi4yODE4IDI3LjgzNDEgMzMuOTE3MiAyNC44NzA5IDMzLjkxNzJDMjAuNTE4MyAzMy45MTcyIDE2Ljk4OTkgMzAuMzg4OCAxNi45ODk5IDI2LjAzNjJDMTYuOTg5OSAyMS42ODM2IDIwLjUxODMgMTguMTU1MiAyNC44NzA5IDE4LjE1NTJDMjYuMDg5NiAxOC4xNTUyIDI3LjI0MzcgMTguNDMxOCAyOC4yNzM4IDE4LjkyNTdMMjkuNTY5MiAxNi4yMTlaTTM0Ljc3NzkgMS45ODQ0NFYyNy43NDYxSDMxLjY2OVYwLjg5OTM2M0MyOS41NDU5IDAuMzEzMTcxIDI3LjMwOTUgMCAyNSAwQzExLjE5MjkgMCAwIDExLjE5MjkgMCAyNUMwIDM4LjgwNzEgMTEuMTkyOSA1MCAyNSA1MEMzOC44MDcxIDUwIDUwIDM4LjgwNzEgNTAgMjVDNTAgMTQuNjYyNiA0My43MjU5IDUuNzkwNyAzNC43Nzc5IDEuOTg0NDRaIiBmaWxsPSIjMEFCQTlGIi8+Cjwvc3ZnPgo=" + }, + "categories": [ + { + "id": 2, + "name": "Sales" + } + ], + "displayName": "Dropcontact", + "typeVersion": 1 + } + ] + } + ], + "filters": [ + { + "counts": [ + { + "count": 24, + "highlighted": "Sales", + "value": "Sales" + }, + { + "count": 12, + "highlighted": "Marketing & Growth", + "value": "Marketing & Growth" + }, + { + "count": 6, + "highlighted": "Building Blocks", + "value": "Building Blocks" + }, + { + "count": 3, + "highlighted": "Finance & Accounting", + "value": "Finance & Accounting" + }, + { + "count": 2, + "highlighted": "HR & People Ops", + "value": "HR & People Ops" + } + ], + "field_name": "categories", + "sampled": false, + "stats": { + "total_values": 5 + } + } + ] +} diff --git a/cypress/fixtures/templates_search/test_template_import.json b/cypress/fixtures/templates_search/test_template_import.json new file mode 100644 index 0000000000..c77be3db9c --- /dev/null +++ b/cypress/fixtures/templates_search/test_template_import.json @@ -0,0 +1,19 @@ +{ + "id": 60, + "name": "test1 test1", + "workflow": { + "nodes": [ + { + "name": "Start", + "type": "n8n-nodes-base.start", + "position": [ + 250, + 300 + ], + "parameters": {}, + "typeVersion": 1 + } + ], + "connections": {} + } +} diff --git a/cypress/fixtures/templates_search/test_template_preview.json b/cypress/fixtures/templates_search/test_template_preview.json new file mode 100644 index 0000000000..4d3ca1e548 --- /dev/null +++ b/cypress/fixtures/templates_search/test_template_preview.json @@ -0,0 +1,150 @@ +{ + "workflow": { + "id": 60, + "name": "test1 test1", + "views": 120000000, + "recentViews": 0, + "totalViews": 120000000, + "createdAt": "2019-08-30T16:39:31.362Z", + "description": "here is a description. here is a description. here is a description. \n\n![Screenshot from 20190806 091433.png](fileId:88)", + "workflow": { + "nodes": [ + { + "name": "Start", + "type": "n8n-nodes-base.start", + "position": [ + 250, + 300 + ], + "parameters": {}, + "typeVersion": 1 + } + ], + "connections": {} + }, + "lastUpdatedBy": null, + "workflowInfo": { + "nodeCount": 1, + "nodeTypes": { + "n8n-nodes-base.start": { + "count": 1 + } + } + }, + "user": { + "username": "admin" + }, + "nodes": [ + { + "id": 11, + "icon": "file:amqp.png", + "name": "n8n-nodes-base.amqpTrigger", + "defaults": { + "name": "AMQP Trigger" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAA8CAYAAAA6/NlyAAAB7UlEQVRoge2W4W3CMBCFj26QjkBHSEdIR4AR6Ah0BBgBRqAjhBFgBBghHaEVlV29PN0lDr+o9D7JEjhn+975bJ8JIYQQQgghhBBCCCGEEA9CY2bf0NaBW2uyu7UN2XSOzTyY60J2BzNbObbsH7eTmS2mhHJHE1wmCD7A93ngEAquHaHc2omCcysSXQW74g32BHfwfTEiuCoQm9vuDsEndPYpELxKjjBj0foCEXX6XdM3by3c7aOZPZvZzMzeaBzbIh9pzIuZXaG/RqNIMAq7Ur8XCHQ2kx3LC56DMQ39X4LI23zbAd88ruRHD09wTVF5p+/eBZI5g7O8w5FgXOvsZAI7PxRwS4HGIPbm8wRjBL/Sgp/QNyQYHWySmOxgJBgFeGnPfZHgDVyufET+YMEVCdo7gziCTBbGmRKlGQpCMXOnj+1L6B0JFsxndO3cjjZyjo6OnZeqGb5gqhTQS3qKeK1SwbesfB3IrF/awqu+g8Dgs5SLE37SciHiPUv8rLVp7k2wdl63tDDqgTs8lqpINWGXbSTKe9rlJgXME7C9I6V7oGAWsEzv2gzeN2TstkbCZyIJWBYKWUwtF4foKGU9TpRGdZDSdVDpDNXSVVBLt5TeucS9K6X/E3USX3rshBBCCCGEEEIIIYQQ4tExsx8PuuPnwhCIbgAAAABJRU5ErkJggg==" + }, + "categories": [ + { + "id": 5, + "name": "Development" + }, + { + "id": 6, + "name": "Communication" + } + ], + "displayName": "AMQP Trigger", + "typeVersion": 1 + }, + { + "id": 18, + "icon": "file:autopilot.svg", + "name": "n8n-nodes-base.autopilot", + "defaults": { + "name": "Autopilot" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjM4IDI2IDM1IDM1Ij48Y2lyY2xlIGN4PSI1MCIgY3k9IjUwIiByPSI0MCIgc3Ryb2tlPSIjMThkNGIyIiBzdHJva2Utd2lkdGg9IjMiIGZpbGw9IiMxOGQ0YjIiLz48cGF0aCBmaWxsPSIjZmZmIiBkPSJNNDUuNCA0Mi42aDE5LjlsMy40LTQuOEg0MmwzLjQgNC44em0zLjEgOC4zaDEzLjFsMy40LTQuOEg0NS40bDMuMSA0Ljh6bTU0LS43Ii8+PC9zdmc+" + }, + "categories": [ + { + "id": 1, + "name": "Marketing" + } + ], + "displayName": "Autopilot", + "typeVersion": 1 + }, + { + "id": 20, + "icon": "file:lambda.svg", + "name": "n8n-nodes-base.awsLambda", + "defaults": { + "name": "AWS Lambda" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjI1MDAiIHZpZXdCb3g9Ii0zLjAyMyAtMC4yMiA0MjAuOTIzIDQzMy41NCIgd2lkdGg9IjI0NDMiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTIwOC40NSAyMjcuODljLTEuNTkgMi4yNi0yLjkzIDQuMTItNC4yMiA2cS0zMC44NiA0NS40Mi02MS43IDkwLjgzLTI4LjY5IDQyLjI0LTU3LjQ0IDg0LjQzYTMuODggMy44OCAwIDAxLTIuNzMgMS41OXEtNDAuNTktLjM1LTgxLjE2LS44OGMtLjMgMC0uNjEtLjA5LTEuMi0uMThhMTQuNDQgMTQuNDQgMCAwMS43Ni0xLjY1cTI4LjMxLTQzLjg5IDU2LjYyLTg3Ljc2IDI1LjExLTM4Ljg4IDUwLjI1LTc3Ljc0IDI3Ljg2LTQzLjE4IDU1LjY5LTg2LjQyYzIuNzQtNC4yNSA1LjU5LTguNDIgOC4xOS0xMi43NWE1LjI2IDUuMjYgMCAwMC41Ni0zLjgzYy01LTE1Ljk0LTEwLjEtMzEuODQtMTUuMTktNDcuNzQtMi4xOC02LjgxLTQuNDYtMTMuNTgtNi41LTIwLjQzLS42Ni0yLjItMS43NS0yLjg3LTQtMi44Ni0xNyAuMDctMzMuOS4wNS01MC44NS4wNS0zLjIyIDAtMy4yMyAwLTMuMjMtMy4xOCAwLTIwLjg0IDAtNDEuNjgtLjA2LTYyLjUyIDAtMi4zMi43Ni0yLjg0IDIuOTQtMi44NHE1MS4xOS4wOSAxMDIuNCAwYTMuMjkgMy4yOSAwIDAxMy42IDIuNDNxMjcgNjcuOTEgNTQgMTM1Ljc3IDMxLjUgNzkuMTQgNjMgMTU4LjNjNi41MiAxNi4zOCAxMy4wOSAzMi43NSAxOS41NCA0OS4xNy43NyAyIDEuNTcgMi4zOCAzLjU5IDEuNzYgMTcuODktNS41MyAzNS44Mi0xMC45MSA1My43LTE2LjQ1IDIuMjUtLjcgMy4wNy0uMjMgMy43NyAyIDYuMSAxOS4xNyAxMi4zMiAzOC4zIDE4LjUgNTcuNDUuMjEuNjYuMzcgMS4zMy42MiAyLjI1LTEuMjguNDctMi40OCAxLTMuNzEgMS4zNHEtNjEgMTkuMzMtMTIxLjkzIDM4LjY4Yy0xLjk0LjYxLTIuNTItLjA1LTMuMTctMS42OHEtMTguNjEtNDcuMTYtMzcuMzEtOTQuMjgtMTguMjktNDYuMTQtMzYuNi05Mi4yOGMtMS44My00LjYyLTMuNjMtOS4yNi01LjQ2LTEzLjg4LS4yOS0uNzktLjY5LTEuNDgtMS4yNy0yLjd6IiBmaWxsPSIjZmE3ZTE0Ii8+PC9zdmc+" + }, + "categories": [ + { + "id": 5, + "name": "Development" + } + ], + "displayName": "AWS Lambda", + "typeVersion": 1 + }, + { + "id": 40, + "icon": "file:clearbit.svg", + "name": "n8n-nodes-base.clearbit", + "defaults": { + "name": "Clearbit" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3MiIgaGVpZ2h0PSI3MiI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJhIiB4MT0iNTAlIiB4Mj0iMTAwJSIgeTE9IjAlIiB5Mj0iMTAwJSI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI0RFRjJGRSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI0RCRjFGRSIvPjwvbGluZWFyR3JhZGllbnQ+PGxpbmVhckdyYWRpZW50IGlkPSJiIiB4MT0iMCUiIHgyPSI1MCUiIHkxPSIwJSIgeTI9IjEwMCUiPjxzdG9wIG9mZnNldD0iMCUiIHN0b3AtY29sb3I9IiM1N0JDRkQiLz48c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiM1MUI1RkQiLz48L2xpbmVhckdyYWRpZW50PjxsaW5lYXJHcmFkaWVudCBpZD0iYyIgeDE9IjM3LjUlIiB4Mj0iNjIuNSUiIHkxPSIwJSIgeTI9IjEwMCUiPjxzdG9wIG9mZnNldD0iMCUiIHN0b3AtY29sb3I9IiMxQ0E3RkQiLz48c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiMxNDhDRkMiLz48L2xpbmVhckdyYWRpZW50PjwvZGVmcz48ZyBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxwYXRoIGZpbGw9InVybCgjYSkiIGQ9Ik03MiAzNnYxNi43N2wtLjAwNC44NjhjLS4wNiA2LjAzNS0uNzUgOC4zNTMtMiAxMC42ODhhMTMuNjMgMTMuNjMgMCAwMS01LjY3IDUuNjdsLS4zMjYuMTcxQzYxLjY1OCA3MS4zNjQgNTkuMTYgNzIgNTIuNzcgNzJIMzZWMzZoMzZ6Ii8+PHBhdGggZmlsbD0idXJsKCNiKSIgZD0iTTY0LjMyNiAyLjAwM2ExMy42MyAxMy42MyAwIDAxNS42NyA1LjY3bC4xNzEuMzI3QzcxLjM2NCAxMC4zNDIgNzIgMTIuODQgNzIgMTkuMjNWMzZIMzZWMGgxNi43N2M2LjY4NyAwIDkuMTEyLjY5NiAxMS41NTYgMi4wMDN6Ii8+PHBhdGggZmlsbD0idXJsKCNjKSIgZD0iTTM2IDB2NzJIMTkuMjNsLS44NjgtLjAwNGMtNi4wMzUtLjA2LTguMzUzLS43NS0xMC42ODgtMmExMy42MyAxMy42MyAwIDAxLTUuNjctNS42N0wxLjgzMiA2NEMuNjM2IDYxLjY1OCAwIDU5LjE2IDAgNTIuNzdWMTkuMjNjMC02LjY4Ny42OTYtOS4xMTIgMi4wMDMtMTEuNTU2YTEzLjYzIDEzLjYzIDAgMDE1LjY3LTUuNjdMOCAxLjgzMkMxMC4zNDIuNjM2IDEyLjg0IDAgMTkuMjMgMEgzNnoiLz48L2c+PC9zdmc+" + }, + "categories": [ + { + "id": 2, + "name": "Sales" + } + ], + "displayName": "Clearbit", + "typeVersion": 1 + }, + { + "id": 51, + "icon": "file:convertKit.svg", + "name": "n8n-nodes-base.convertKitTrigger", + "defaults": { + "name": "ConvertKit Trigger" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTcyIiBoZWlnaHQ9IjE2MCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJNODIuNzIgMTI2LjMxNmMyOS43NyAwIDUyLjc4LTIyLjYyMiA1Mi43OC01MC41MjYgMC0yNi4xNDMtMjEuNjE3LTQyLjEwNi0zNS45MzUtNDIuMTA2LTE5Ljk0NSAwLTM1LjkzIDE0LjA4NC0zOC4xOTggMzQuOTg4LS40MTggMy44NTYtMy40NzYgNy4wOS03LjM1NSA3LjA2MS02LjQyMy0uMDQ2LTE1Ljc0Ni0uMS0yMS42NTgtLjA4LTIuNTU1LjAwOC00LjY2OS0yLjA2NS00LjU0My00LjYxOC44OS0xOC4xMjMgNi45MTQtMzUuMDcgMTguNDAyLTQ4LjA4N0M1OC45NzYgOC40ODggNzcuNTYxIDAgOTkuNTY1IDBjMzYuOTY5IDAgNzEuODY5IDMzLjc4NiA3MS44NjkgNzUuNzkgMCA0Ni41MDgtMzguMzEyIDg0LjIxLTg3LjkyNyA4NC4yMS0zNS4zODQgMC03MS4wMjEtMjMuMjU4LTgzLjQ2NC01NS43NzVhLjcwMi43MDIgMCAwMS0uMDMtLjM3N2MuMTY1LS45NjIuNDk0LTEuODQxLjgxOC0yLjcwNy40NzEtMS4yNTguOTMxLTIuNDg4Ljg2NC0zLjkwNmwtLjIxNS00LjUyOWE1LjUyMyA1LjUyMyAwIDAxMy4xOC01LjI2M2wxLjc5OC0uODQyYTYuOTgyIDYuOTgyIDAgMDAzLjkxMi01LjA3NSA2Ljk5MyA2Ljk5MyAwIDAxNi44ODctNS43MzZjNS4yODIgMCA5Ljg3NSAzLjUxNSAxMS41OSA4LjUxMiA4LjMwNyAyNC4yMTIgMjEuNTExIDQyLjAxNCA1My44NzMgNDIuMDE0eiIgZmlsbD0iI0ZCNjk3MCIvPjwvc3ZnPg==" + }, + "categories": [ + { + "id": 1, + "name": "Marketing" + }, + { + "id": 2, + "name": "Sales" + } + ], + "displayName": "ConvertKit Trigger", + "typeVersion": 1 + } + ], + "categories": [], + "image": [] + } +} diff --git a/cypress/fixtures/workflow-with-unknown-credentials.json b/cypress/fixtures/workflow-with-unknown-credentials.json new file mode 100644 index 0000000000..17d355f92e --- /dev/null +++ b/cypress/fixtures/workflow-with-unknown-credentials.json @@ -0,0 +1,52 @@ +{ + "meta": { + "instanceId": "123" + }, + "nodes": [ + { + "parameters": { + "resource": "credential", + "name": "123", + "credentialTypeName": "123" + }, + "id": "a01f79f6-e8c3-44c5-be5e-4bc482e23172", + "name": "n8n", + "type": "n8n-nodes-base.n8n", + "typeVersion": 1, + "position": [ + 540, + 240 + ], + "credentials": { + "n8nApi": { + "id": "10", + "name": "n8n account" + } + } + }, + { + "parameters": {}, + "id": "acdd1bdc-c642-4ea6-ad67-f4201b640cfa", + "name": "When clicking \"Test Workflow\"", + "type": "n8n-nodes-base.manualTrigger", + "typeVersion": 1, + "position": [ + 300, + 240 + ] + } + ], + "connections": { + "When clicking \"Test Workflow\"": { + "main": [ + [ + { + "node": "n8n", + "type": "main", + "index": 0 + } + ] + ] + } + } +} diff --git a/cypress/fixtures/workflow-with-unknown-nodes.json b/cypress/fixtures/workflow-with-unknown-nodes.json new file mode 100644 index 0000000000..c5b5f165b3 --- /dev/null +++ b/cypress/fixtures/workflow-with-unknown-nodes.json @@ -0,0 +1,90 @@ +{ + "meta": { + "instanceId": "15bbf37b6a515ccc2f534cabcd8bd171ca33583ff7744b1e9420e5ce68e615bb" + }, + "nodes": [ + { + "parameters": {}, + "id": "40720511-19b6-4421-bdb0-3fb6efef4bc5", + "name": "When clicking \"Test Workflow\"", + "type": "n8n-nodes-base.manualTrigger", + "typeVersion": 1, + "position": [ + 280, + 320 + ] + }, + { + "parameters": {}, + "id": "acdd1bdc-c642-4ea6-ad67-f4201b640cfa", + "name": "Unknown node 1", + "type": "n8n-nodes-base.thisNodeDoesntExist", + "typeVersion": 1, + "position": [ + 400, + 500 + ] + }, + { + "parameters": {}, + "id": "acdd1bdc-c642-4ea6-ad67-f4201b640ffa", + "name": "Unknown node 2", + "type": "n8n-nodes-base.thisNodeDoesntExistEither", + "typeVersion": 1, + "position": [ + 600, + 500 + ] + }, + { + "parameters": { + "options": {} + }, + "id": "fbe5163b-7474-4741-980a-e4956789be0a", + "name": "Edit Fields", + "type": "n8n-nodes-base.set", + "typeVersion": 3.2, + "position": [ + 500, + 320 + ] + }, + { + "parameters": { + "options": {} + }, + "id": "163313b9-64ff-4ffc-b00f-09b267d8132c", + "name": "Edit Fields1", + "type": "n8n-nodes-base.set", + "typeVersion": 3.2, + "position": [ + 720, + 320 + ] + } + ], + "connections": { + "When clicking \"Test Workflow\"": { + "main": [ + [ + { + "node": "Edit Fields", + "type": "main", + "index": 0 + } + ] + ] + }, + "Edit Fields": { + "main": [ + [ + { + "node": "Edit Fields1", + "type": "main", + "index": 0 + } + ] + ] + } + } +} diff --git a/cypress/pages/demo.ts b/cypress/pages/demo.ts new file mode 100644 index 0000000000..0590fb8def --- /dev/null +++ b/cypress/pages/demo.ts @@ -0,0 +1,21 @@ +/** + * Actions + */ + +export function vistDemoPage(theme?: 'dark' | 'light') { + const query = theme ? `?theme=${theme}` : ''; + cy.visit('/workflows/demo' + query); + cy.waitForLoad(); + cy.window().then((win) => { + // @ts-ignore + win.preventNodeViewBeforeUnload = true; + }); +} + +export function importWorkflow(workflow: object) { + const OPEN_WORKFLOW = {command: 'openWorkflow', workflow}; + cy.window().then($window => { + const message = JSON.stringify(OPEN_WORKFLOW); + $window.postMessage(message, '*') + }); +} diff --git a/cypress/pages/features/node-creator.ts b/cypress/pages/features/node-creator.ts index 6686de25ff..3e6a819443 100644 --- a/cypress/pages/features/node-creator.ts +++ b/cypress/pages/features/node-creator.ts @@ -20,6 +20,7 @@ export class NodeCreator extends BasePage { communityNodeTooltip: () => cy.getByTestId('node-item-community-tooltip'), noResults: () => cy.getByTestId('node-creator-no-results'), nodeItemName: () => cy.getByTestId('node-creator-item-name'), + nodeItemDescription: () => cy.getByTestId('node-creator-item-description'), activeSubcategory: () => cy.getByTestId('nodes-list-header'), expandedCategories: () => this.getters.creatorItem().find('>div').filter('.active').invoke('text'), diff --git a/cypress/pages/index.ts b/cypress/pages/index.ts index 18e3649e1a..39c9be3b56 100644 --- a/cypress/pages/index.ts +++ b/cypress/pages/index.ts @@ -10,3 +10,6 @@ export * from './ndv'; export * from './bannerStack'; export * from './workflow-executions-tab'; export * from './signin'; +export * from './workflow-history'; +export * from './workerView'; +export * from './settings-public-api'; diff --git a/cypress/pages/modals/credentials-modal.ts b/cypress/pages/modals/credentials-modal.ts index 312e9edbf5..08a258a057 100644 --- a/cypress/pages/modals/credentials-modal.ts +++ b/cypress/pages/modals/credentials-modal.ts @@ -1,4 +1,5 @@ import { BasePage } from '../base'; +import { getVisibleSelect } from '../../utils'; export class CredentialsModal extends BasePage { getters = { @@ -30,11 +31,7 @@ export class CredentialsModal extends BasePage { actions = { addUser: (email: string) => { this.getters.usersSelect().click(); - this.getters - .usersSelect() - .get('.el-select-dropdown__item') - .contains(email.toLowerCase()) - .click(); + getVisibleSelect().contains(email.toLowerCase()).click(); }, setName: (name: string) => { this.getters.name().click(); @@ -48,6 +45,12 @@ export class CredentialsModal extends BasePage { if (test) cy.wait('@testCredential'); this.getters.saveButton().should('contain.text', 'Saved'); }, + saveSharing: (test = false) => { + cy.intercept('PUT', '/rest/credentials/*/share').as('shareCredential'); + this.getters.saveButton().click({ force: true }); + cy.wait('@shareCredential'); + this.getters.saveButton().should('contain.text', 'Saved'); + }, close: () => { this.getters.closeButton().click(); }, diff --git a/cypress/pages/ndv.ts b/cypress/pages/ndv.ts index 611a351ff5..449ad75eb4 100644 --- a/cypress/pages/ndv.ts +++ b/cypress/pages/ndv.ts @@ -24,6 +24,7 @@ export class NDV extends BasePage { editPinnedDataButton: () => cy.getByTestId('ndv-edit-pinned-data'), pinnedDataEditor: () => this.getters.outputPanel().find('.cm-editor .cm-scroller'), runDataPaneHeader: () => cy.getByTestId('run-data-pane-header'), + nodeOutputHint: () => cy.getByTestId('ndv-output-run-node-hint'), savePinnedDataButton: () => this.getters.runDataPaneHeader().find('button').filter(':visible').contains('Save'), outputTableRows: () => this.getters.outputDataContainer().find('table tr'), @@ -48,9 +49,7 @@ export class NDV extends BasePage { parameterExpressionPreview: (parameterName: string) => this.getters .nodeParameters() - .find( - `[data-test-id="parameter-input-${parameterName}"] + [data-test-id="parameter-expression-preview"]`, - ), + .find(`[data-test-id="parameter-expression-preview-${parameterName}"]`), nodeNameContainer: () => cy.getByTestId('node-title-container'), nodeRenameInput: () => cy.getByTestId('node-rename-input'), executePrevious: () => cy.getByTestId('execute-previous-node'), @@ -72,15 +71,43 @@ export class NDV extends BasePage { this.getters.resourceLocator(paramName).find('[data-test-id="rlc-mode-selector"]'), resourceMapperFieldsContainer: () => cy.getByTestId('mapping-fields-container'), resourceMapperSelectColumn: () => cy.getByTestId('matching-column-select'), - resourceMapperRemoveFieldButton: (fieldName: string) => cy.getByTestId(`remove-field-button-${fieldName}`), - resourceMapperColumnsOptionsButton: () => cy.getByTestId('columns-parameter-input-options-container'), + resourceMapperRemoveFieldButton: (fieldName: string) => + cy.getByTestId(`remove-field-button-${fieldName}`), + resourceMapperColumnsOptionsButton: () => + cy.getByTestId('columns-parameter-input-options-container'), resourceMapperRemoveAllFieldsOption: () => cy.getByTestId('action-removeAllFields'), sqlEditorContainer: () => cy.getByTestId('sql-editor-container'), + filterComponent: (paramName: string) => cy.getByTestId(`filter-${paramName}`), + filterCombinator: (paramName: string, index = 0) => + this.getters.filterComponent(paramName).getByTestId('filter-combinator-select').eq(index), + filterConditions: (paramName: string) => + this.getters.filterComponent(paramName).getByTestId('filter-condition'), + filterCondition: (paramName: string, index = 0) => + this.getters.filterComponent(paramName).getByTestId('filter-condition').eq(index), + filterConditionLeft: (paramName: string, index = 0) => + this.getters.filterComponent(paramName).getByTestId('filter-condition-left').eq(index), + filterConditionRight: (paramName: string, index = 0) => + this.getters.filterComponent(paramName).getByTestId('filter-condition-right').eq(index), + filterConditionOperator: (paramName: string, index = 0) => + this.getters.filterComponent(paramName).getByTestId('filter-operator-select').eq(index), + filterConditionRemove: (paramName: string, index = 0) => + this.getters.filterComponent(paramName).getByTestId('filter-remove-condition').eq(index), + filterConditionAdd: (paramName: string) => + this.getters.filterComponent(paramName).getByTestId('filter-add-condition'), + searchInput: () => cy.getByTestId('ndv-search'), + pagination: () => cy.getByTestId('ndv-data-pagination'), + nodeVersion: () => cy.getByTestId('node-version'), + nodeSettingsTab: () => cy.getByTestId('tab-settings'), + codeEditorFullscreenButton: () => cy.getByTestId('code-editor-fullscreen-button'), + codeEditorDialog: () => cy.getByTestId('code-editor-fullscreen'), + codeEditorFullscreen: () => this.getters.codeEditorDialog().find('.cm-content'), + nodeRunSuccessIndicator: () => cy.getByTestId('node-run-info-success'), + nodeRunErrorIndicator: () => cy.getByTestId('node-run-info-danger'), }; actions = { pinData: () => { - this.getters.pinDataButton().click(); + this.getters.pinDataButton().click({ force: true }); }, editPinnedData: () => { this.getters.editPinnedDataButton().click(); @@ -104,7 +131,12 @@ export class NDV extends BasePage { this.getters.pinnedDataEditor().click(); this.getters .pinnedDataEditor() - .type(`{selectall}{backspace}${JSON.stringify(data).replace(new RegExp('{', 'g'), '{{}')}`); + .type( + `{selectall}{backspace}${JSON.stringify(data).replace(new RegExp('{', 'g'), '{{}')}`, + { + delay: 0, + }, + ); this.actions.savePinnedData(); }, @@ -114,7 +146,7 @@ export class NDV extends BasePage { typeIntoParameterInput: ( parameterName: string, content: string, - opts?: { parseSpecialCharSequences: boolean, delay?: number }, + opts?: { parseSpecialCharSequences: boolean; delay?: number }, ) => { this.getters.parameterInput(parameterName).type(content, opts); }, @@ -187,7 +219,6 @@ export class NDV extends BasePage { .find('span') .should('include.html', asEncodedHTML(value)); }, - refreshResourceMapperColumns: () => { this.getters.resourceMapperSelectColumn().realHover(); this.getters @@ -198,8 +229,21 @@ export class NDV extends BasePage { getVisiblePopper().find('li').last().click(); }, - - setInvalidExpression: ({ fieldName, invalidExpression, delay }: { fieldName: string, invalidExpression?: string, delay?: number }) => { + addFilterCondition: (paramName: string) => { + this.getters.filterConditionAdd(paramName).click(); + }, + removeFilterCondition: (paramName: string, index: number) => { + this.getters.filterConditionRemove(paramName, index).click(); + }, + setInvalidExpression: ({ + fieldName, + invalidExpression, + delay, + }: { + fieldName: string; + invalidExpression?: string; + delay?: number; + }) => { this.actions.typeIntoParameterInput(fieldName, '='); this.actions.typeIntoParameterInput(fieldName, invalidExpression ?? "{{ $('unknown')", { parseSpecialCharSequences: false, @@ -207,6 +251,20 @@ export class NDV extends BasePage { }); this.actions.validateExpressionPreview(fieldName, `node doesn't exist`); }, + openSettings: () => { + this.getters.nodeSettingsTab().click(); + }, + + openCodeEditorFullscreen: () => { + this.getters.codeEditorFullscreenButton().click({ force: true }); + }, + changeNodeOperation: (operation: string) => { + this.getters.parameterInput('operation').click(); + cy.get('.el-select-dropdown__item') + .contains(new RegExp(`^${operation}$`)) + .click({ force: true }); + this.getters.parameterInput('operation').find('input').should('have.value', operation); + }, }; } diff --git a/cypress/pages/settings-personal.ts b/cypress/pages/settings-personal.ts index 79cf58c752..716625beb5 100644 --- a/cypress/pages/settings-personal.ts +++ b/cypress/pages/settings-personal.ts @@ -20,8 +20,15 @@ export class PersonalSettingsPage extends BasePage { saveSettingsButton: () => cy.getByTestId('save-settings-button'), enableMfaButton: () => cy.getByTestId('enable-mfa-button'), disableMfaButton: () => cy.getByTestId('disable-mfa-button'), + themeSelector: () => cy.getByTestId('theme-select'), + selectOptionsVisible: () => cy.get('.el-select-dropdown:visible .el-select-dropdown__item'), }; actions = { + changeTheme: (theme: 'System default' | 'Dark' | 'Light') => { + this.getters.themeSelector().click(); + this.getters.selectOptionsVisible().should('have.length', 3); + this.getters.selectOptionsVisible().contains(theme).click(); + }, loginAndVisit: (email: string, password: string) => { cy.signin({ email, password }); cy.visit(this.url); diff --git a/cypress/pages/settings-public-api.ts b/cypress/pages/settings-public-api.ts new file mode 100644 index 0000000000..1a7d668136 --- /dev/null +++ b/cypress/pages/settings-public-api.ts @@ -0,0 +1,5 @@ +export const getPublicApiUpgradeCTA = () => cy.getByTestId('public-api-upgrade-cta'); + +export const visitPublicApiPage = () => { + cy.visit('/settings/api'); +}; diff --git a/cypress/pages/settings-users.ts b/cypress/pages/settings-users.ts index d9a2e32df9..e3c80e5bcc 100644 --- a/cypress/pages/settings-users.ts +++ b/cypress/pages/settings-users.ts @@ -20,6 +20,8 @@ export class SettingsUsersPage extends BasePage { userItem: (email: string) => cy.getByTestId(`user-list-item-${email.toLowerCase()}`), userActionsToggle: (email: string) => this.getters.userItem(email).find('[data-test-id="action-toggle"]'), + userRoleSelect: (email: string) => + this.getters.userItem(email).find('[data-test-id="user-role-select"]'), deleteUserAction: () => cy.getByTestId('action-toggle-dropdown').find('li:contains("Delete"):visible'), confirmDeleteModal: () => cy.getByTestId('deleteUser-modal').last(), diff --git a/cypress/pages/settings.ts b/cypress/pages/settings.ts new file mode 100644 index 0000000000..264b525dee --- /dev/null +++ b/cypress/pages/settings.ts @@ -0,0 +1,9 @@ +import { BasePage } from './base'; + +export class SettingsPage extends BasePage { + url = '/settings'; + getters = { + menuItems: () => cy.getByTestId('menu-item'), + }; + actions = {}; +} diff --git a/cypress/pages/sidebar/main-sidebar.ts b/cypress/pages/sidebar/main-sidebar.ts index 1559d3da65..5379b1f889 100644 --- a/cypress/pages/sidebar/main-sidebar.ts +++ b/cypress/pages/sidebar/main-sidebar.ts @@ -12,7 +12,9 @@ export class MainSidebar extends BasePage { workflows: () => this.getters.menuItem('Workflows'), credentials: () => this.getters.menuItem('Credentials'), executions: () => this.getters.menuItem('Executions'), + adminPanel: () => this.getters.menuItem('Admin Panel'), userMenu: () => cy.get('div[class="action-dropdown-container"]'), + logo: () => cy.getByTestId('n8n-logo'), }; actions = { goToSettings: () => { diff --git a/cypress/pages/template-collection.ts b/cypress/pages/template-collection.ts new file mode 100644 index 0000000000..9f3457eae9 --- /dev/null +++ b/cypress/pages/template-collection.ts @@ -0,0 +1,33 @@ +export function visitTemplateCollectionPage(withFixture: Fixture) { + cy.intercept( + 'GET', + `https://api.n8n.io/api/templates/collections/${testData.ecommerceStarterPack.id}`, + { + fixture: withFixture.fixture, + }, + ).as('getTemplateCollection'); + + cy.visit(`/collections/${withFixture.id}`); + + cy.wait('@getTemplateCollection'); +} + +export function clickUseWorkflowButtonByTitle(workflowTitle: string) { + cy.getByTestId('template-card') + .contains('[data-test-id=template-card]', workflowTitle) + .realHover({ position: 'center' }) + .findChildByTestId('use-workflow-button') + .click({ force: true }); +} + +export type Fixture = { + id: number; + fixture: string; +}; + +export const testData = { + ecommerceStarterPack: { + id: 1, + fixture: 'Ecommerce_starter_pack_template_collection.json', + }, +}; diff --git a/cypress/pages/template-credential-setup.ts b/cypress/pages/template-credential-setup.ts new file mode 100644 index 0000000000..d673261fdf --- /dev/null +++ b/cypress/pages/template-credential-setup.ts @@ -0,0 +1,70 @@ +import { CredentialsModal, MessageBox } from './modals'; +import * as formStep from '../composables/setup-template-form-step'; +import { overrideFeatureFlag } from '../composables/featureFlags'; + +export type TemplateTestData = { + id: number; + fixture: string; +}; + +export const testData = { + simpleTemplate: { + id: 1205, + fixture: 'Test_Template_1.json', + }, + templateWithoutCredentials: { + id: 1344, + fixture: 'Test_Template_2.json', + }, +}; + +const credentialsModal = new CredentialsModal(); +const messageBox = new MessageBox(); + +export const getters = { + continueButton: () => cy.getByTestId('continue-button'), + skipLink: () => cy.get('a:contains("Skip")'), + title: (title: string) => cy.get(`h1:contains(${title})`), + infoCallout: () => cy.getByTestId('info-callout'), +}; + +export const enableTemplateCredentialSetupFeatureFlag = () => { + overrideFeatureFlag('017_template_credential_setup_v2', true); +}; + +export const visitTemplateCredentialSetupPage = (templateId: number) => { + cy.visit(`templates/${templateId}/setup`); + enableTemplateCredentialSetupFeatureFlag(); + + formStep.getFormStep().eq(0).should('be.visible'); +}; + +/** + * Fills in dummy credentials for the given app name. + */ +export const fillInDummyCredentialsForApp = (appName: string) => { + formStep.getCreateAppCredentialsButton(appName).click(); + credentialsModal.getters.editCredentialModal().find('input:first()').type('test'); + credentialsModal.actions.save(false); + credentialsModal.actions.close(); +}; + +/** + * Fills in dummy credentials for the given app name. Assumes + * that a confirmation message box will be shown, which will be + * handled. + */ +export const fillInDummyCredentialsForAppWithConfirm = (appName: string) => { + fillInDummyCredentialsForApp(appName); + messageBox.actions.cancel(); +}; + +/** + * Finishes the credential setup by clicking the continue button. + */ +export const finishCredentialSetup = () => { + cy.intercept('POST', '/rest/workflows').as('createWorkflow'); + getters.continueButton().should('be.enabled'); + getters.continueButton().click(); + cy.wait('@createWorkflow'); +}; diff --git a/cypress/pages/template-workflow.ts b/cypress/pages/template-workflow.ts new file mode 100644 index 0000000000..ff54e1e3d4 --- /dev/null +++ b/cypress/pages/template-workflow.ts @@ -0,0 +1,38 @@ +import { BasePage } from './base'; + +export class TemplateWorkflowPage extends BasePage { + url = '/templates'; + + getters = { + useTemplateButton: () => cy.get('[data-test-id="use-template-button"]'), + description: () => cy.get('[data-test-id="template-description"]'), + }; + + actions = { + visit: (templateId: number) => { + cy.visit(`${this.url}/${templateId}`); + }, + + clickUseThisWorkflowButton: () => { + this.getters.useTemplateButton().click(); + }, + + openTemplate: (template: { + workflow: { + id: number; + name: string; + description: string; + user: { username: string }; + image: { id: number; url: string }[]; + }; + }) => { + cy.intercept('GET', `https://api.n8n.io/api/templates/workflows/${template.workflow.id}`, { + statusCode: 200, + body: template, + }).as('getTemplate'); + + this.actions.visit(template.workflow.id); + cy.wait('@getTemplate'); + }, + }; +} diff --git a/cypress/pages/templates.ts b/cypress/pages/templates.ts index d49c086a79..e72c450312 100644 --- a/cypress/pages/templates.ts +++ b/cypress/pages/templates.ts @@ -1,15 +1,29 @@ import { BasePage } from './base'; -import { WorkflowPage } from './workflow'; -const workflowPage = new WorkflowPage(); export class TemplatesPage extends BasePage { url = '/templates'; getters = { + useTemplateButton: () => cy.getByTestId('use-template-button'), + templateCards: () => cy.getByTestId('template-card'), + firstTemplateCard: () => this.getters.templateCards().first(), + allCategoriesFilter: () => cy.getByTestId('template-filter-all-categories'), + searchInput: () => cy.getByTestId('template-search-input'), + categoryFilters: () => cy.get('[data-test-id^=template-filter]'), + categoryFilter: (category: string) => cy.getByTestId(`template-filter-${category}`), + collectionCountLabel: () => cy.getByTestId('collection-count-label'), + templateCountLabel: () => cy.getByTestId('template-count-label'), + templatesLoadingContainer: () => cy.getByTestId('templates-loading-container'), + expandCategoriesButton: () => cy.getByTestId('expand-categories-button'), }; actions = { - openOnboardingFlow: (id: number, name: string , workflow: object) => { + openSingleTemplateView: (templateId: number) => { + cy.visit(`${this.url}/${templateId}`); + cy.waitForLoad(); + }, + + openOnboardingFlow: (id: number, name: string, workflow: object) => { const apiResponse = { id, name, @@ -43,8 +57,7 @@ export class TemplatesPage extends BasePage { cy.visit(`/workflows/templates/${id}`); cy.wait('@getTemplate'); - cy.wait( '@getWorkflow'); - } - } + cy.wait('@getWorkflow'); + }, + }; } - diff --git a/cypress/pages/workerView.ts b/cypress/pages/workerView.ts new file mode 100644 index 0000000000..e14bfd36a2 --- /dev/null +++ b/cypress/pages/workerView.ts @@ -0,0 +1,15 @@ +import { BasePage } from './base'; + +export class WorkerViewPage extends BasePage { + url = '/settings/workers'; + getters = { + workerCards: () => cy.getByTestId('worker-card'), + workerCard: (workerId: string) => this.getters.workerCards().contains(workerId), + workerViewLicensed: () => cy.getByTestId('worker-view-licensed'), + workerViewUnlicensed: () => cy.getByTestId('worker-view-unlicensed'), + menuItems: () => cy.get('.el-menu-item'), + menuItem: () => this.getters.menuItems().get('#settings-workersview'), + }; + + actions = {}; +} diff --git a/cypress/pages/workflow-executions-tab.ts b/cypress/pages/workflow-executions-tab.ts index eff3fedd30..eb855f026f 100644 --- a/cypress/pages/workflow-executions-tab.ts +++ b/cypress/pages/workflow-executions-tab.ts @@ -14,6 +14,7 @@ export class WorkflowExecutionsTab extends BasePage { failedExecutionListItems: () => cy.get('[data-test-execution-status="error"]'), executionCard: (executionId: string) => cy.getByTestId(`execution-details-${executionId}`), executionPreviewDetails: () => cy.get('[data-test-id^="execution-preview-details-"]'), + executionPreviewDeleteButton: () => cy.get('[data-test-id="execution-preview-delete-button"]'), executionPreviewDetailsById: (executionId: string) => cy.getByTestId(`execution-preview-details-${executionId}`), executionPreviewTime: () => @@ -38,9 +39,15 @@ export class WorkflowExecutionsTab extends BasePage { }, switchToExecutionsTab: () => { this.getters.executionsTabButton().click(); + cy.url().should('include', '/executions'); }, switchToEditorTab: () => { workflowPage.getters.editorTabButton().click(); + cy.url().should('match', /\/workflow\/[^\/]+$/); + }, + deleteExecutionInPreview: () => { + this.getters.executionPreviewDeleteButton().click(); + cy.get('button.btn--confirm').click(); }, }; } diff --git a/cypress/pages/workflow-history.ts b/cypress/pages/workflow-history.ts new file mode 100644 index 0000000000..18cd6ed999 --- /dev/null +++ b/cypress/pages/workflow-history.ts @@ -0,0 +1,7 @@ +import { BasePage } from "./base"; + +export class WorkflowHistoryPage extends BasePage { + getters = { + workflowHistoryCloseButton: () => cy.getByTestId('workflow-history-close-button'), + } +} diff --git a/cypress/pages/workflow.ts b/cypress/pages/workflow.ts index 4bcccc7418..f3994b4b42 100644 --- a/cypress/pages/workflow.ts +++ b/cypress/pages/workflow.ts @@ -46,8 +46,10 @@ export class WorkflowPage extends BasePage { canvasNodePlusEndpointByName: (nodeName: string, index = 0) => { return cy.get(this.getters.getEndpointSelector('plus', nodeName, index)); }, - successToast: () => cy.get('.el-notification .el-notification--success').parent(), - errorToast: () => cy.get('.el-notification .el-notification--error'), + successToast: () => cy.get('.el-notification:has(.el-notification--success)'), + warningToast: () => cy.get('.el-notification:has(.el-notification--warning)'), + errorToast: () => cy.get('.el-notification:has(.el-notification--error)'), + infoToast: () => cy.get('.el-notification:has(.el-notification--info)'), activatorSwitch: () => cy.getByTestId('workflow-activate-switch'), workflowMenu: () => cy.getByTestId('workflow-menu'), firstStepButton: () => cy.getByTestId('canvas-add-button'), @@ -70,6 +72,7 @@ export class WorkflowPage extends BasePage { workflowMenuItemImportFromFile: () => cy.getByTestId('workflow-menu-item-import-from-file'), workflowMenuItemSettings: () => cy.getByTestId('workflow-menu-item-settings'), workflowMenuItemDelete: () => cy.getByTestId('workflow-menu-item-delete'), + workflowMenuItemGitPush: () => cy.getByTestId('workflow-menu-item-push'), // Workflow settings dialog elements workflowSettingsModal: () => cy.getByTestId('workflow-settings-dialog'), workflowSettingsErrorWorkflowSelect: () => cy.getByTestId('workflow-settings-error-workflow'), @@ -124,6 +127,9 @@ export class WorkflowPage extends BasePage { addStickyButton: () => cy.getByTestId('add-sticky-button'), stickies: () => cy.getByTestId('sticky'), editorTabButton: () => cy.getByTestId('radio-button-workflow'), + workflowHistoryButton: () => cy.getByTestId('workflow-history-button'), + colors: () => cy.getByTestId('color'), + contextMenuAction: (action: string) => cy.getByTestId(`context-menu-item-${action}`), }; actions = { visit: (preventNodeViewUnload = true) => { @@ -168,23 +174,86 @@ export class WorkflowPage extends BasePage { this.getters.nodeCreatorSearchBar().type(nodeDisplayName); this.getters.nodeCreatorSearchBar().type('{enter}'); - cy.wait(500); cy.get('body').then((body) => { if (body.find('[data-test-id=node-creator]').length > 0) { if (action) { cy.contains(action).click(); } else { // Select the first action - cy.get('[data-keyboard-nav-type="action"]').eq(0).click(); + if (body.find('[data-keyboard-nav-type="action"]').length > 0) { + cy.get('[data-keyboard-nav-type="action"]').eq(0).click(); + } } } }); if (!preventNdvClose) cy.get('body').type('{esc}'); }, + openContextMenu: ( + nodeTypeName?: string, + method: 'right-click' | 'overflow-button' = 'right-click', + ) => { + const target = nodeTypeName + ? this.getters.canvasNodeByName(nodeTypeName) + : this.getters.nodeViewBackground(); + + if (method === 'right-click') { + target.rightclick(nodeTypeName ? 'center' : 'topLeft', { force: true }); + } else { + target.realHover(); + target.find('[data-test-id="overflow-node-button"]').click({ force: true }); + } + }, openNode: (nodeTypeName: string) => { this.getters.canvasNodeByName(nodeTypeName).first().dblclick(); }, + duplicateNode: (nodeTypeName: string) => { + this.actions.openContextMenu(nodeTypeName); + this.actions.contextMenuAction('duplicate'); + }, + deleteNodeFromContextMenu: (nodeTypeName: string) => { + this.actions.openContextMenu(nodeTypeName); + this.actions.contextMenuAction('delete'); + }, + executeNode: (nodeTypeName: string) => { + this.actions.openContextMenu(nodeTypeName); + this.actions.contextMenuAction('execute'); + }, + addStickyFromContextMenu: () => { + this.actions.openContextMenu(); + this.actions.contextMenuAction('add_sticky'); + }, + renameNode: (nodeTypeName: string) => { + this.actions.openContextMenu(nodeTypeName); + this.actions.contextMenuAction('rename'); + }, + copyNode: (nodeTypeName: string) => { + this.actions.openContextMenu(nodeTypeName); + this.actions.contextMenuAction('copy'); + }, + contextMenuAction: (action: string) => { + this.getters.contextMenuAction(action).click(); + }, + disableNode: (nodeTypeName: string) => { + this.actions.openContextMenu(nodeTypeName); + this.actions.contextMenuAction('toggle_activation'); + }, + pinNode: (nodeTypeName: string) => { + this.actions.openContextMenu(nodeTypeName); + this.actions.contextMenuAction('toggle_pin'); + }, + openNodeFromContextMenu: (nodeTypeName: string) => { + this.actions.openContextMenu(nodeTypeName, 'overflow-button'); + this.actions.contextMenuAction('open'); + }, + selectAllFromContextMenu: () => { + this.actions.openContextMenu(); + this.actions.contextMenuAction('select_all'); + }, + deselectAll: () => { + this.actions.openContextMenu(); + this.actions.contextMenuAction('deselect_all'); + }, openExpressionEditorModal: () => { cy.contains('Expression').invoke('show').click(); cy.getByTestId('expander').invoke('show').click(); @@ -209,6 +278,7 @@ export class WorkflowPage extends BasePage { this.getters.saveButton().should('contain', 'Save'); this.getters.saveButton().click(); this.getters.saveButton().should('contain', 'Saved'); + cy.url().should('not.have.string', '/new'); }, saveWorkflowUsingKeyboardShortcut: () => { cy.intercept('POST', '/rest/workflows').as('createWorkflow'); @@ -260,7 +330,8 @@ export class WorkflowPage extends BasePage { ctrlKey: true, pageX: cy.window().innerWidth / 2, pageY: cy.window().innerHeight / 2, - deltaY: mode === 'zoomOut' ? 16 * steps : -16 * steps, + deltaMode: 1, + deltaY: mode === 'zoomOut' ? steps : -steps, }); }, hitUndo: () => { @@ -276,13 +347,22 @@ export class WorkflowPage extends BasePage { cy.get('body').type(META_KEY, { delay: 500, release: false }).type('a'); }, hitDisableNodeShortcut: () => { - cy.get('body').type(META_KEY, { delay: 500, release: false }).type('d'); + cy.get('body').type('d'); }, hitCopy: () => { cy.get('body').type(META_KEY, { delay: 500, release: false }).type('c'); }, - hitPaste: () => { - cy.get('body').type(META_KEY, { delay: 500, release: false }).type('P'); + hitPinNodeShortcut: () => { + cy.get('body').type('p'); + }, + hitExecuteWorkflowShortcut: () => { + cy.get('body').type(META_KEY, { delay: 500, release: false }).type('{enter}'); + }, + hitDuplicateNodeShortcut: () => { + cy.get('body').type(META_KEY, { delay: 500, release: false }).type('d'); + }, + hitAddStickyShortcut: () => { + cy.get('body').type('{shift}', { delay: 500, release: false }).type('S'); }, executeWorkflow: () => { this.getters.executeWorkflowButton().click(); @@ -321,6 +401,17 @@ export class WorkflowPage extends BasePage { deleteSticky: () => { this.getters.stickies().eq(0).realHover().find('[data-test-id="delete-sticky"]').click(); }, + toggleColorPalette: () => { + this.getters + .stickies() + .eq(0) + .realHover() + .find('[data-test-id="change-sticky-color"]') + .click({ force: true }); + }, + pickColor: (index: number) => { + this.getters.colors().eq(1).click(); + }, editSticky: (content: string) => { this.getters.stickies().dblclick().find('textarea').clear().type(content).type('{esc}'); }, @@ -334,5 +425,11 @@ export class WorkflowPage extends BasePage { cy.getByTestId('node-view-wrapper').trigger('mouseup', to[0], to[1], { force: true }); cy.get('#select-box').should('not.be.visible'); }, + getNodePosition: (node: Cypress.Chainable>) => { + return node.then(($el) => ({ + left: +$el[0].style.left.replace('px', ''), + top: +$el[0].style.top.replace('px', ''), + })); + }, }; } diff --git a/cypress/pages/workflows.ts b/cypress/pages/workflows.ts index 416528e85c..56a3c44923 100644 --- a/cypress/pages/workflows.ts +++ b/cypress/pages/workflows.ts @@ -23,9 +23,24 @@ export class WorkflowsPage extends BasePage { this.getters.workflowCard(workflowName).findChildByTestId('workflow-card-actions'), workflowDeleteButton: () => cy.getByTestId('action-toggle-dropdown').filter(':visible').contains('Delete'), + workflowFilterButton: () => cy.getByTestId('resources-list-filters-trigger').filter(':visible'), + workflowTagsDropdown: () => cy.getByTestId('tags-dropdown'), + workflowTagItem: (tag: string) => cy.getByTestId('tag').contains(tag), + workflowStatusDropdown: () => cy.getByTestId('status-dropdown'), + workflowStatusItem: (status: string) => cy.getByTestId('status').contains(status), + workflowOwnershipDropdown: () => cy.getByTestId('user-select-trigger'), + workflowOwner: (email: string) => cy.getByTestId('user-email').contains(email), + workflowResetFilters: () => cy.getByTestId('workflows-filter-reset'), // Not yet implemented // myWorkflows: () => cy.getByTestId('my-workflows'), // allWorkflows: () => cy.getByTestId('all-workflows'), + suggestedTemplatesPageContainer: () => cy.getByTestId('suggested-templates-page-container'), + suggestedTemplatesCards: () => cy.get('.agile__slides--regular [data-test-id=templates-info-card]'), + suggestedTemplatesNewWorkflowButton: () => cy.getByTestId('suggested-templates-new-workflow-button'), + suggestedTemplatesSectionContainer: () => cy.getByTestId('suggested-templates-section-container'), + suggestedTemplatesPreviewModal: () => cy.getByTestId('suggested-templates-preview-modal'), + suggestedTemplatesUseTemplateButton: () => cy.getByTestId('use-template-button'), + suggestedTemplatesSectionDescription: () => cy.getByTestId('suggested-template-section-description'), }; actions = { diff --git a/cypress/support/commands.ts b/cypress/support/commands.ts index c86db382a3..238ea0d2a2 100644 --- a/cypress/support/commands.ts +++ b/cypress/support/commands.ts @@ -1,6 +1,12 @@ import 'cypress-real-events'; import { WorkflowPage } from '../pages'; -import { BACKEND_BASE_URL, N8N_AUTH_COOKIE } from '../constants'; +import { + BACKEND_BASE_URL, + INSTANCE_ADMIN, + INSTANCE_MEMBERS, + INSTANCE_OWNER, + N8N_AUTH_COOKIE, +} from '../constants'; Cypress.Commands.add('getByTestId', (selector, ...args) => { return cy.get(`[data-test-id="${selector}"]`, ...args); @@ -16,8 +22,8 @@ Cypress.Commands.add('createFixtureWorkflow', (fixtureKey, workflowName) => { cy.waitForLoad(false); workflowPage.actions.setWorkflowName(workflowName); - workflowPage.getters.saveButton().should('contain', 'Saved'); + workflowPage.actions.zoomToFit(); }); Cypress.Commands.add( @@ -33,7 +39,7 @@ Cypress.Commands.add('waitForLoad', (waitForIntercepts = true) => { // we can't set them up here because at this point it would be too late // and the requests would already have been made if (waitForIntercepts) { - cy.wait(['@loadSettings']); + cy.wait(['@loadSettings', '@loadNodeTypes']); } cy.getByTestId('node-view-loader', { timeout: 20000 }).should('not.exist'); cy.get('.el-loading-mask', { timeout: 20000 }).should('not.exist'); @@ -51,6 +57,10 @@ Cypress.Commands.add('signin', ({ email, password }) => { ); }); +Cypress.Commands.add('signinAsOwner', () => { + cy.signin({ email: INSTANCE_OWNER.email, password: INSTANCE_OWNER.password }); +}); + Cypress.Commands.add('signout', () => { cy.request('POST', `${BACKEND_BASE_URL}/rest/logout`); cy.getCookie(N8N_AUTH_COOKIE).should('not.exist'); @@ -66,8 +76,15 @@ const setFeature = (feature: string, enabled: boolean) => enabled, }); +const setQueueMode = (enabled: boolean) => + cy.request('PATCH', `${BACKEND_BASE_URL}/rest/e2e/queue-mode`, { + enabled, + }); + Cypress.Commands.add('enableFeature', (feature: string) => setFeature(feature, true)); -Cypress.Commands.add('disableFeature', (feature): string => setFeature(feature, false)); +Cypress.Commands.add('disableFeature', (feature: string) => setFeature(feature, false)); +Cypress.Commands.add('enableQueueMode', () => setQueueMode(true)); +Cypress.Commands.add('disableQueueMode', () => setQueueMode(false)); Cypress.Commands.add('grantBrowserPermissions', (...permissions: string[]) => { if (Cypress.isBrowser('chrome')) { @@ -153,6 +170,7 @@ Cypress.Commands.add('draganddrop', (draggableSelector, droppableSelector) => { cy.get(draggableSelector).trigger('mousedown'); } // We don't chain these commands to make sure cy.get is re-trying correctly + cy.get(droppableSelector).realMouseMove(0, 0); cy.get(droppableSelector).realMouseMove(pageX, pageY); cy.get(droppableSelector).realHover(); cy.get(droppableSelector).realMouseUp(); @@ -161,3 +179,25 @@ Cypress.Commands.add('draganddrop', (draggableSelector, droppableSelector) => { } }); }); + +Cypress.Commands.add('push', (type, data) => { + cy.request('POST', `${BACKEND_BASE_URL}/rest/e2e/push`, { + type, + data, + }); +}); + +Cypress.Commands.add('shouldNotHaveConsoleErrors', () => { + cy.window().then((win) => { + const spy = cy.spy(win.console, 'error'); + cy.wrap(spy).should('not.have.been.called'); + }); +}); + +Cypress.Commands.add('resetDatabase', () => { + cy.request('POST', `${BACKEND_BASE_URL}/rest/e2e/reset`, { + owner: INSTANCE_OWNER, + members: INSTANCE_MEMBERS, + admin: INSTANCE_ADMIN, + }); +}); diff --git a/cypress/support/e2e.ts b/cypress/support/e2e.ts index a750918c6d..1a209d66b9 100644 --- a/cypress/support/e2e.ts +++ b/cypress/support/e2e.ts @@ -1,11 +1,8 @@ -import { BACKEND_BASE_URL, INSTANCE_MEMBERS, INSTANCE_OWNER } from '../constants'; +import { INSTANCE_OWNER } from '../constants'; import './commands'; before(() => { - cy.request('POST', `${BACKEND_BASE_URL}/rest/e2e/reset`, { - owner: INSTANCE_OWNER, - members: INSTANCE_MEMBERS, - }); + cy.resetDatabase(); Cypress.on('uncaught:exception', (err) => { return !err.message.includes('ResizeObserver'); @@ -18,6 +15,7 @@ beforeEach(() => { } cy.intercept('GET', '/rest/settings').as('loadSettings'); + cy.intercept('GET', '/types/nodes.json').as('loadNodeTypes'); // Always intercept the request to test credentials and return a success cy.intercept('POST', '/rest/credentials/test', { diff --git a/cypress/support/index.ts b/cypress/support/index.ts index 37140351ff..f31e50c578 100644 --- a/cypress/support/index.ts +++ b/cypress/support/index.ts @@ -23,10 +23,13 @@ declare global { findChildByTestId(childTestId: string): Chainable>; createFixtureWorkflow(fixtureKey: string, workflowName: string): void; signin(payload: SigninPayload): void; + signinAsOwner(): void; signout(): void; interceptREST(method: string, url: string): Chainable; enableFeature(feature: string): void; disableFeature(feature: string): void; + enableQueueMode(): void; + disableQueueMode(): void; waitForLoad(waitForIntercepts?: boolean): void; grantBrowserPermissions(...permissions: string[]): void; readClipboard(): Chainable; @@ -34,9 +37,19 @@ declare global { drag( selector: string | Cypress.Chainable>, target: [number, number], - options?: { abs?: boolean; index?: number; realMouse?: boolean }, + options?: { abs?: boolean; index?: number; realMouse?: boolean; clickToFinish?: boolean }, ): void; draganddrop(draggableSelector: string, droppableSelector: string): void; + push(type: string, data: unknown): void; + shouldNotHaveConsoleErrors(): void; + window(): Chainable< + AUTWindow & { + featureFlags: { + override: (feature: string, value: any) => void; + }; + } + >; + resetDatabase(): void; } } } diff --git a/cypress/tsconfig.json b/cypress/tsconfig.json index 61b0e504a3..26a5da716b 100644 --- a/cypress/tsconfig.json +++ b/cypress/tsconfig.json @@ -1,7 +1,11 @@ { "extends": "../tsconfig.json", "compilerOptions": { + "sourceMap": false, "declaration": false, - "sourceMap": false - } + "lib": ["esnext", "dom"], + "types": ["cypress", "node"] + }, + "include": ["**/*.ts"], + "exclude": ["**/dist/**/*", "**/node_modules/**/*"] } diff --git a/cypress/utils/executions.ts b/cypress/utils/executions.ts new file mode 100644 index 0000000000..81748af505 --- /dev/null +++ b/cypress/utils/executions.ts @@ -0,0 +1,135 @@ +import { ITaskData } from '../../packages/workflow/src'; +import { IPinData } from '../../packages/workflow'; +import { clickExecuteWorkflowButton } from '../composables/workflow'; + +export function createMockNodeExecutionData( + name: string, + { + data, + inputOverride, + executionStatus = 'success', + jsonData, + ...rest + }: Partial & { jsonData?: Record }, +): Record { + return { + [name]: { + startTime: new Date().getTime(), + executionTime: 0, + executionStatus, + data: jsonData + ? Object.keys(jsonData).reduce((acc, key) => { + acc[key] = [ + [ + { + json: jsonData[key], + pairedItem: { item: 0 }, + }, + ], + ]; + + return acc; + }, {}) + : data, + source: [null], + ...rest, + }, + }; +} + +export function createMockWorkflowExecutionData({ + executionId, + runData, + pinData = {}, + lastNodeExecuted, +}: { + executionId: string; + runData: Record; + pinData?: IPinData; + lastNodeExecuted: string; +}) { + return { + executionId, + data: { + data: { + startData: {}, + resultData: { + runData, + pinData, + lastNodeExecuted, + }, + executionData: { + contextData: {}, + nodeExecutionStack: [], + metadata: {}, + waitingExecution: {}, + waitingExecutionSource: {}, + }, + }, + mode: 'manual', + startedAt: new Date().toISOString(), + stoppedAt: new Date().toISOString(), + status: 'success', + finished: true, + }, + }; +} + +export function runMockWorkflowExcution({ + trigger, + lastNodeExecuted, + runData, + workflowExecutionData, +}: { + trigger?: () => void; + lastNodeExecuted: string; + runData: Array>; + workflowExecutionData?: ReturnType; +}) { + const executionId = Math.random().toString(36).substring(4); + + cy.intercept('POST', '/rest/workflows/run', { + statusCode: 201, + body: { + data: { + executionId, + }, + }, + }).as('runWorkflow'); + + if (trigger) { + trigger(); + } else { + clickExecuteWorkflowButton(); + } + + cy.wait('@runWorkflow'); + + const resolvedRunData = {}; + runData.forEach((nodeExecution) => { + const nodeName = Object.keys(nodeExecution)[0]; + const nodeRunData = nodeExecution[nodeName]; + + cy.push('nodeExecuteBefore', { + executionId, + nodeName, + }); + cy.push('nodeExecuteAfter', { + executionId, + nodeName, + data: nodeRunData, + }); + + resolvedRunData[nodeName] = nodeExecution[nodeName]; + }); + + cy.push( + 'executionFinished', + createMockWorkflowExecutionData({ + executionId, + lastNodeExecuted, + runData: resolvedRunData, + ...workflowExecutionData, + }), + ); +} diff --git a/cypress/utils/index.ts b/cypress/utils/index.ts index 1929454b18..3cfa5a7449 100644 --- a/cypress/utils/index.ts +++ b/cypress/utils/index.ts @@ -1 +1,3 @@ +export * from './executions'; +export * from './modal'; export * from './popper'; diff --git a/docker/images/n8n/Dockerfile b/docker/images/n8n/Dockerfile index a699563633..14398820a1 100644 --- a/docker/images/n8n/Dockerfile +++ b/docker/images/n8n/Dockerfile @@ -16,9 +16,10 @@ RUN set -eux; \ case "$apkArch" in \ 'armv7') apk del build-dependencies;; \ esac && \ + rm -rf /usr/local/lib/node_modules/n8n/node_modules/@n8n/chat && \ rm -rf /usr/local/lib/node_modules/n8n/node_modules/n8n-design-system && \ rm -rf /usr/local/lib/node_modules/n8n/node_modules/n8n-editor-ui/node_modules && \ - find /usr/local/lib/node_modules/n8n -type f -name "*.ts" -o -name "*.js.map" -o -name "*.vue" | xargs rm && \ + find /usr/local/lib/node_modules/n8n -type f -name "*.ts" -o -name "*.js.map" -o -name "*.vue" | xargs rm -f && \ rm -rf /root/.npm COPY docker-entrypoint.sh / diff --git a/docker/images/n8n/README.md b/docker/images/n8n/README.md index 8497d1ed77..848c21c7cd 100644 --- a/docker/images/n8n/README.md +++ b/docker/images/n8n/README.md @@ -2,7 +2,7 @@ # n8n - Workflow automation tool -n8n is an extendable workflow automation tool. With a [fair-code](http://faircode.io) distribution model, n8n will always have visible source code, be available to self-host, and allow you to add your own custom functions, logic and apps. n8n's node-based approach makes it highly versatile, enabling you to connect anything to everything. +n8n is an extendable workflow automation tool. With a [fair-code](https://faircode.io) distribution model, n8n will always have visible source code, be available to self-host, and allow you to add your own custom functions, logic and apps. n8n's node-based approach makes it highly versatile, enabling you to connect anything to everything. n8n.io - Screenshot @@ -63,7 +63,7 @@ You can then access n8n by opening: To be able to use webhooks which all triggers of external services like Github rely on n8n has to be reachable from the web. To make that easy n8n has a -special tunnel service (uses this code: [https://github.com/localtunnel/localtunnel](https://github.com/localtunnel/localtunnel)) which redirects requests from our servers to your local +special tunnel service (uses this code: [https://github.com/n8n-io/localtunnel](https://github.com/n8n-io/localtunnel)) which redirects requests from our servers to your local n8n instance. To use it simply start n8n with `--tunnel` @@ -258,6 +258,6 @@ Before you upgrade to the latest version make sure to check here if there are an ## License -n8n is [fair-code](http://faircode.io) distributed under the [**Sustainable Use License**](https://github.com/n8n-io/n8n/blob/master/packages/cli/LICENSE.md). +n8n is [fair-code](https://faircode.io) distributed under the [**Sustainable Use License**](https://github.com/n8n-io/n8n/blob/master/packages/cli/LICENSE.md). Additional information about the license can be found in the [docs](https://docs.n8n.io/reference/license/). diff --git a/jest.config.js b/jest.config.js index a4c416f152..f3f7824c14 100644 --- a/jest.config.js +++ b/jest.config.js @@ -30,8 +30,8 @@ const config = { return acc; }, {}), setupFilesAfterEnv: ['jest-expect-message'], - collectCoverage: true, - coverageReporters: [process.env.COVERAGE_REPORT === 'true' ? 'text' : 'text-summary'], + collectCoverage: process.env.COVERAGE_ENABLED === 'true', + coverageReporters: ['text-summary'], collectCoverageFrom: ['src/**/*.ts'], }; diff --git a/package.json b/package.json index 2ce4e34681..2e794cfbf2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "n8n", - "version": "1.14.0", + "version": "1.24.0", "private": true, "homepage": "https://n8n.io", "engines": { @@ -11,8 +11,11 @@ "scripts": { "preinstall": "node scripts/block-npm-install.js", "build": "turbo run build", + "build:backend": "pnpm --filter=!@n8n/chat --filter=!n8n-design-system --filter=!n8n-editor-ui build", + "build:frontend": "pnpm --filter=@n8n/chat --filter=n8n-design-system --filter=n8n-editor-ui build", "typecheck": "turbo run typecheck", - "dev": "turbo run dev --parallel", + "dev": "turbo run dev --parallel --filter=!n8n-design-system --filter=!@n8n/chat", + "dev:ai": "turbo run dev --parallel --filter=@n8n/nodes-langchain --filter=n8n --filter=n8n-core", "clean": "turbo run clean --parallel", "format": "turbo run format && node scripts/format.mjs", "lint": "turbo run lint", @@ -23,14 +26,17 @@ "start:tunnel": "./packages/cli/bin/n8n start --tunnel", "start:windows": "cd packages/cli/bin && n8n", "test": "turbo run test", + "test:backend": "pnpm --filter=!@n8n/chat --filter=!n8n-design-system --filter=!n8n-editor-ui --filter=!n8n-nodes-base test", + "test:nodes": "pnpm --filter=n8n-nodes-base test", + "test:frontend": "pnpm --filter=@n8n/chat --filter=n8n-design-system --filter=n8n-editor-ui test", "watch": "turbo run watch", "webhook": "./packages/cli/bin/n8n webhook", "worker": "./packages/cli/bin/n8n worker", "cypress:install": "cypress install", "cypress:open": "CYPRESS_BASE_URL=http://localhost:8080 cypress open", - "test:e2e:ui": "cross-env E2E_TESTS=true NODE_OPTIONS=--dns-result-order=ipv4first start-server-and-test start http://localhost:5678/favicon.ico 'cypress open'", - "test:e2e:dev": "cross-env E2E_TESTS=true NODE_OPTIONS=--dns-result-order=ipv4first CYPRESS_BASE_URL=http://localhost:8080 start-server-and-test dev http://localhost:8080/favicon.ico 'cypress open'", - "test:e2e:all": "cross-env E2E_TESTS=true NODE_OPTIONS=--dns-result-order=ipv4first start-server-and-test start http://localhost:5678/favicon.ico 'cypress run --headless'" + "test:e2e:ui": "scripts/run-e2e.js ui", + "test:e2e:dev": "scripts/run-e2e.js dev", + "test:e2e:all": "scripts/run-e2e.js all" }, "dependencies": { "n8n": "workspace:*" @@ -40,11 +46,11 @@ "@ngneat/falso": "^6.4.0", "@types/jest": "^29.5.3", "@types/supertest": "^2.0.12", - "@vitest/coverage-v8": "^0.33.0", + "@vitest/coverage-v8": "^1.1.0", "cross-env": "^7.0.3", + "cypress": "^13.6.2", "cypress-otp": "^1.0.3", - "cypress": "^12.17.2", - "cypress-real-events": "^1.9.1", + "cypress-real-events": "^1.11.0", "jest": "^29.6.2", "jest-environment-jsdom": "^29.6.2", "jest-expect-message": "^1.1.3", @@ -55,16 +61,16 @@ "p-limit": "^3.1.0", "rimraf": "^5.0.1", "run-script-os": "^1.0.7", - "start-server-and-test": "^2.0.0", + "start-server-and-test": "^2.0.3", "supertest": "^6.3.3", "ts-jest": "^29.1.1", "tsc-alias": "^1.8.7", "tsc-watch": "^6.0.4", "turbo": "1.10.12", "typescript": "*", - "vite": "^4.4.7", - "vitest": "^0.33.0", - "vue-tsc": "^1.8.8" + "vite": "^5.0.10", + "vitest": "^1.1.0", + "vue-tsc": "^1.8.25" }, "pnpm": { "onlyBuiltDependencies": [ @@ -74,16 +80,17 @@ "@types/node": "^18.16.16", "chokidar": "3.5.2", "jsonwebtoken": "9.0.0", - "prettier": "^3.0.3", + "prettier": "^3.1.0", "semver": "^7.5.4", "tough-cookie": "^4.1.3", "tslib": "^2.6.1", "tsconfig-paths": "^4.2.0", "ts-node": "^10.9.1", - "typescript": "^5.2.2", + "typescript": "^5.3.0", "xml2js": "^0.5.0", "cpy@8>globby": "^11.1.0", - "qqjs>globby": "^11.1.0" + "qqjs>globby": "^11.1.0", + "@langchain/core": "^0.1.8" }, "patchedDependencies": { "typedi@0.10.0": "patches/typedi@0.10.0.patch", diff --git a/packages/@n8n/chat/.eslintignore b/packages/@n8n/chat/.eslintignore new file mode 100644 index 0000000000..40a7b4122b --- /dev/null +++ b/packages/@n8n/chat/.eslintignore @@ -0,0 +1,2 @@ +.eslintrc.cjs +vitest.config.ts diff --git a/packages/@n8n/chat/.eslintrc.cjs b/packages/@n8n/chat/.eslintrc.cjs new file mode 100644 index 0000000000..a1ad467ea3 --- /dev/null +++ b/packages/@n8n/chat/.eslintrc.cjs @@ -0,0 +1,10 @@ +const sharedOptions = require('@n8n_io/eslint-config/shared'); + +/** + * @type {import('@types/eslint').ESLint.ConfigData} + */ +module.exports = { + extends: ['@n8n_io/eslint-config/frontend'], + + ...sharedOptions(__dirname, 'frontend'), +}; diff --git a/packages/@n8n/chat/.gitignore b/packages/@n8n/chat/.gitignore new file mode 100644 index 0000000000..38adffa64e --- /dev/null +++ b/packages/@n8n/chat/.gitignore @@ -0,0 +1,28 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +.DS_Store +dist +dist-ssr +coverage +*.local + +/cypress/videos/ +/cypress/screenshots/ + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/packages/@n8n/chat/.np-config.json b/packages/@n8n/chat/.np-config.json new file mode 100644 index 0000000000..5b1cb0a96a --- /dev/null +++ b/packages/@n8n/chat/.np-config.json @@ -0,0 +1,5 @@ +{ + "yarn": false, + "tests": false, + "contents": "./dist" +} diff --git a/packages/@n8n/chat/.storybook/main.ts b/packages/@n8n/chat/.storybook/main.ts new file mode 100644 index 0000000000..c826aea088 --- /dev/null +++ b/packages/@n8n/chat/.storybook/main.ts @@ -0,0 +1,27 @@ +import type { StorybookConfig } from '@storybook/vue3-vite'; + +import { join, dirname } from 'path'; + +/** + * This function is used to resolve the absolute path of a package. + * It is needed in projects that use Yarn PnP or are set up within a monorepo. + */ +function getAbsolutePath(value: string): any { + return dirname(require.resolve(join(value, 'package.json'))); +} +const config: StorybookConfig = { + stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'], + addons: [ + getAbsolutePath('@storybook/addon-links'), + getAbsolutePath('@storybook/addon-essentials'), + getAbsolutePath('@storybook/addon-interactions'), + ], + framework: { + name: getAbsolutePath('@storybook/vue3-vite'), + options: {}, + }, + docs: { + autodocs: 'tag', + }, +}; +export default config; diff --git a/packages/@n8n/chat/.storybook/preview.scss b/packages/@n8n/chat/.storybook/preview.scss new file mode 100644 index 0000000000..abaf406f8d --- /dev/null +++ b/packages/@n8n/chat/.storybook/preview.scss @@ -0,0 +1,4 @@ +html, body, #storybook-root, #n8n-chat { + width: 100%; + height: 100%; +} diff --git a/packages/@n8n/chat/.storybook/preview.ts b/packages/@n8n/chat/.storybook/preview.ts new file mode 100644 index 0000000000..0a92b65cb2 --- /dev/null +++ b/packages/@n8n/chat/.storybook/preview.ts @@ -0,0 +1,16 @@ +import type { Preview } from '@storybook/vue3'; +import './preview.scss'; + +const preview: Preview = { + parameters: { + actions: { argTypesRegex: '^on[A-Z].*' }, + controls: { + matchers: { + color: /(background|color)$/i, + date: /Date$/, + }, + }, + }, +}; + +export default preview; diff --git a/packages/@n8n/chat/.vscode/extensions.json b/packages/@n8n/chat/.vscode/extensions.json new file mode 100644 index 0000000000..c0a6e5a481 --- /dev/null +++ b/packages/@n8n/chat/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"] +} diff --git a/packages/@n8n/chat/LICENSE.md b/packages/@n8n/chat/LICENSE.md new file mode 100644 index 0000000000..c1d7423975 --- /dev/null +++ b/packages/@n8n/chat/LICENSE.md @@ -0,0 +1,85 @@ +# License + +Portions of this software are licensed as follows: + +- Content of branches other than the main branch (i.e. "master") are not licensed. +- All source code files that contain ".ee." in their filename are licensed under the + "n8n Enterprise License" defined in "LICENSE_EE.md". +- All third party components incorporated into the n8n Software are licensed under the original license + provided by the owner of the applicable component. +- Content outside of the above mentioned files or restrictions is available under the "Sustainable Use + License" as defined below. + +## Sustainable Use License + +Version 1.0 + +### Acceptance + +By using the software, you agree to all of the terms and conditions below. + +### Copyright License + +The licensor grants you a non-exclusive, royalty-free, worldwide, non-sublicensable, non-transferable license +to use, copy, distribute, make available, and prepare derivative works of the software, in each case subject +to the limitations below. + +### Limitations + +You may use or modify the software only for your own internal business purposes or for non-commercial or +personal use. You may distribute the software or provide it to others only if you do so free of charge for +non-commercial purposes. You may not alter, remove, or obscure any licensing, copyright, or other notices of +the licensor in the software. Any use of the licensor’s trademarks is subject to applicable law. + +### Patents + +The licensor grants you a license, under any patent claims the licensor can license, or becomes able to +license, to make, have made, use, sell, offer for sale, import and have imported the software, in each case +subject to the limitations and conditions in this license. This license does not cover any patent claims that +you cause to be infringed by modifications or additions to the software. If you or your company make any +written claim that the software infringes or contributes to infringement of any patent, your patent license +for the software granted under these terms ends immediately. If your company makes such a claim, your patent +license ends immediately for work on behalf of your company. + +### Notices + +You must ensure that anyone who gets a copy of any part of the software from you also gets a copy of these +terms. If you modify the software, you must include in any modified copies of the software a prominent notice +stating that you have modified the software. + +### No Other Rights + +These terms do not imply any licenses other than those expressly granted in these terms. + +### Termination + +If you use the software in violation of these terms, such use is not licensed, and your license will +automatically terminate. If the licensor provides you with a notice of your violation, and you cease all +violation of this license no later than 30 days after you receive that notice, your license will be reinstated +retroactively. However, if you violate these terms after such reinstatement, any additional violation of these +terms will cause your license to terminate automatically and permanently. + +### No Liability + +As far as the law allows, the software comes as is, without any warranty or condition, and the licensor will +not be liable to you for any damages arising out of these terms or the use or nature of the software, under +any kind of legal claim. + +### Definitions + +The “licensor” is the entity offering these terms. + +The “software” is the software the licensor makes available under these terms, including any portion of it. + +“You” refers to the individual or entity agreeing to these terms. + +“Your company” is any legal entity, sole proprietorship, or other kind of organization that you work for, plus +all organizations that have control over, are under the control of, or are under common control with that +organization. Control means ownership of substantially all the assets of an entity, or the power to direct its +management and policies by vote, contract, or otherwise. Control can be direct or indirect. + +“Your license” is the license granted to you for the software under these terms. + +“Use” means anything you do with the software requiring your license. + +“Trademark” means trademarks, service marks, and similar rights. diff --git a/packages/@n8n/chat/README.md b/packages/@n8n/chat/README.md new file mode 100644 index 0000000000..2cb9babbf1 --- /dev/null +++ b/packages/@n8n/chat/README.md @@ -0,0 +1,250 @@ +# n8n Chat +This is an embeddable Chat widget for n8n. It allows the execution of AI-Powered Workflows through a Chat window. + +**Windowed Example** +![n8n Chat Windowed](https://raw.githubusercontent.com/n8n-io/n8n/master/packages/%40n8n/chat/resources/images/windowed.png) + +**Fullscreen Example** +![n8n Chat Fullscreen](https://raw.githubusercontent.com/n8n-io/n8n/master/packages/%40n8n/chat/resources/images/fullscreen.png) + +## Prerequisites +Create a n8n workflow which you want to execute via chat. The workflow has to be triggered using a **Chat Trigger** node. + +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) + +> Make sure the workflow is **Active.** + +### How it works +Each Chat request is sent to the n8n Webhook endpoint, which then sends back a response. + +Each request is accompanied by an `action` query parameter, where `action` can be one of: +- `loadPreviousSession` - When the user opens the Chatbot again and the previous chat session should be loaded +- `sendMessage` - When the user sends a message + +## 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. + +### a. CDN Embed +Add the following code to your HTML page. + +```html + + +``` + +### b. Import Embed +Install and save n8n Chat as a production dependency. + +```sh +npm install @n8n/chat +``` + +Import the CSS and use the `createChat` function to initialize your Chat window. + +```ts +import '@n8n/chat/style.css'; +import { createChat } from '@n8n/chat'; + +createChat({ + webhookUrl: 'YOUR_PRODUCTION_WEBHOOK_URL' +}); +``` + +##### Vue.js + +```html + + +``` + +##### React + +```tsx +// App.tsx +import { useEffect } from 'react'; +import '@n8n/chat/style.css'; +import { createChat } from '@n8n/chat'; + +export const App = () => { + useEffect(() => { + createChat({ + webhookUrl: 'YOUR_PRODUCTION_WEBHOOK_URL' + }); + }, []); + + return (
); +}; +``` + +## Options +The default options are: + +```ts +createChat({ + webhookUrl: '', + webhookConfig: { + method: 'POST', + headers: {} + }, + target: '#n8n-chat', + mode: 'window', + chatInputKey: 'chatInput', + chatSessionKey: 'sessionId', + metadata: {}, + showWelcomeScreen: false, + defaultLanguage: 'en', + initialMessages: [ + 'Hi there! 👋', + 'My name is Nathan. How can I assist you today?' + ], + i18n: { + en: { + title: 'Hi there! 👋', + subtitle: "Start a chat. We're here to help you 24/7.", + footer: '', + getStarted: 'New Conversation', + inputPlaceholder: 'Type your question..', + }, + }, +}); +``` + +### `webhookUrl` +- **Type**: `string` +- **Required**: `true` +- **Examples**: + - `https://yourname.app.n8n.cloud/webhook/513107b3-6f3a-4a1e-af21-659f0ed14183` + - `http://localhost:5678/webhook/513107b3-6f3a-4a1e-af21-659f0ed14183` +- **Description**: The URL of the n8n Webhook endpoint. Should be the production URL. + +### `webhookConfig` +- **Type**: `{ method: string, headers: Record }` +- **Default**: `{ method: 'POST', headers: {} }` +- **Description**: The configuration for the Webhook request. + +### `target` +- **Type**: `string` +- **Default**: `'#n8n-chat'` +- **Description**: The CSS selector of the element where the Chat window should be embedded. + +### `mode` +- **Type**: `'window' | 'fullscreen'` +- **Default**: `'window'` +- **Description**: The render mode of the Chat window. + - 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'` +- **Description**: The default language of the Chat window. Currently only `en` is supported. + +### `i18n` +- **Type**: `{ [key: string]: Record }` +- **Description**: The i18n configuration for the Chat window. Currently only `en` is supported. + +### `initialMessages` +- **Type**: `string[]` +- **Description**: The initial messages to be displayed in the Chat window. + +## Customization +The Chat window is entirely customizable using CSS variables. + +```css +:root { + --chat--color-primary: #e74266; + --chat--color-primary-shade-50: #db4061; + --chat--color-primary-shade-100: #cf3c5c; + --chat--color-secondary: #20b69e; + --chat--color-secondary-shade-50: #1ca08a; + --chat--color-white: #ffffff; + --chat--color-light: #f2f4f8; + --chat--color-light-shade-50: #e6e9f1; + --chat--color-light-shade-100: #c2c5cc; + --chat--color-medium: #d2d4d9; + --chat--color-dark: #101330; + --chat--color-disabled: #777980; + --chat--color-typing: #404040; + + --chat--spacing: 1rem; + --chat--border-radius: 0.25rem; + --chat--transition-duration: 0.15s; + + --chat--window--width: 400px; + --chat--window--height: 600px; + + --chat--textarea--height: 50px; + + --chat--message--bot--background: var(--chat--color-white); + --chat--message--bot--color: var(--chat--color-dark); + --chat--message--user--background: var(--chat--color-secondary); + --chat--message--user--color: var(--chat--color-white); + --chat--message--pre--background: rgba(0, 0, 0, 0.05); + + --chat--toggle--background: var(--chat--color-primary); + --chat--toggle--hover--background: var(--chat--color-primary-shade-50); + --chat--toggle--active--background: var(--chat--color-primary-shade-100); + --chat--toggle--color: var(--chat--color-white); + --chat--toggle--size: 64px; +} +``` + +## Caveats + +### Fullscreen mode +In fullscreen mode, the Chat window will take up the entire width and height of its target container. Make sure that the container has a set width and height. + +```css +html, +body, +#n8n-chat { + width: 100%; + height: 100%; +} +``` + +## License +n8n Chat is [fair-code](https://faircode.io) distributed under the +[**Sustainable Use License**](https://github.com/n8n-io/n8n/blob/master/packages/cli/LICENSE.md). + +Proprietary licenses are available for enterprise customers. [Get in touch](mailto:license@n8n.io) + +Additional information about the license model can be found in the +[docs](https://docs.n8n.io/reference/license/). diff --git a/packages/@n8n/chat/build.config.js b/packages/@n8n/chat/build.config.js new file mode 100644 index 0000000000..9d5910dbe7 --- /dev/null +++ b/packages/@n8n/chat/build.config.js @@ -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, +}); diff --git a/packages/@n8n/chat/env.d.ts b/packages/@n8n/chat/env.d.ts new file mode 100644 index 0000000000..11f02fe2a0 --- /dev/null +++ b/packages/@n8n/chat/env.d.ts @@ -0,0 +1 @@ +/// diff --git a/packages/@n8n/chat/index.html b/packages/@n8n/chat/index.html new file mode 100644 index 0000000000..a888544898 --- /dev/null +++ b/packages/@n8n/chat/index.html @@ -0,0 +1,13 @@ + + + + + + + Vite App + + +
+ + + diff --git a/packages/@n8n/chat/package.json b/packages/@n8n/chat/package.json new file mode 100644 index 0000000000..ec913f0fa4 --- /dev/null +++ b/packages/@n8n/chat/package.json @@ -0,0 +1,60 @@ +{ + "name": "@n8n/chat", + "version": "0.7.0", + "scripts": { + "dev": "pnpm run storybook", + "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 .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:full && cd dist && pnpm publish" + }, + "main": "./chat.umd.js", + "module": "./chat.es.js", + "types": "./types/index.d.ts", + "exports": { + ".": { + "import": "./index.mjs", + "require": "./index.js" + }, + "./style.css": { + "import": "./style.css", + "require": "./style.css" + }, + "./*": { + "import": "./*", + "require": "./*" + } + }, + "dependencies": { + "highlight.js": "^11.8.0", + "uuid": "^8.3.2", + "vue": "^3.3.4", + "vue-markdown-render": "^2.0.1" + }, + "devDependencies": { + "@iconify-json/mdi": "^1.1.54", + "shelljs": "^0.8.5", + "unbuild": "^2.0.0", + "unplugin-icons": "^0.17.0", + "vite-plugin-dts": "^3.6.4" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/n8n-io/n8n.git" + }, + "license": "SEE LICENSE IN LICENSE.md", + "homepage": "https://n8n.io" +} diff --git a/packages/@n8n/chat/public/favicon.ico b/packages/@n8n/chat/public/favicon.ico new file mode 100644 index 0000000000..df36fcfb72 Binary files /dev/null and b/packages/@n8n/chat/public/favicon.ico differ diff --git a/packages/@n8n/chat/resources/images/fullscreen.png b/packages/@n8n/chat/resources/images/fullscreen.png new file mode 100644 index 0000000000..4c3f5aed4e Binary files /dev/null and b/packages/@n8n/chat/resources/images/fullscreen.png differ diff --git a/packages/@n8n/chat/resources/images/windowed.png b/packages/@n8n/chat/resources/images/windowed.png new file mode 100644 index 0000000000..1a598c823e Binary files /dev/null and b/packages/@n8n/chat/resources/images/windowed.png differ diff --git a/packages/@n8n/chat/resources/workflow-manual.json b/packages/@n8n/chat/resources/workflow-manual.json new file mode 100644 index 0000000000..a21028b9fb --- /dev/null +++ b/packages/@n8n/chat/resources/workflow-manual.json @@ -0,0 +1,238 @@ +{ + "name": "Hosted n8n AI Chat Manual", + "nodes": [ + { + "parameters": { + "options": {} + }, + "id": "e6043748-44fc-4019-9301-5690fe26c614", + "name": "OpenAI Chat Model", + "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi", + "typeVersion": 1, + "position": [ + 860, + 540 + ], + "credentials": { + "openAiApi": { + "id": "cIIkOhl7tUX1KsL6", + "name": "OpenAi account" + } + } + }, + { + "parameters": { + "sessionKey": "={{ $json.sessionId }}" + }, + "id": "0a68a59a-8ab6-4fa5-a1ea-b7f99a93109b", + "name": "Window Buffer Memory", + "type": "@n8n/n8n-nodes-langchain.memoryBufferWindow", + "typeVersion": 1, + "position": [ + 640, + 540 + ] + }, + { + "parameters": { + "text": "={{ $json.chatInput }}", + "options": {} + }, + "id": "3d4e0fbf-d761-4569-b02e-f5c1eeb830c8", + "name": "AI Agent", + "type": "@n8n/n8n-nodes-langchain.agent", + "typeVersion": 1.1, + "position": [ + 840, + 300 + ] + }, + { + "parameters": { + "dataType": "string", + "value1": "={{ $json.action }}", + "rules": { + "rules": [ + { + "value2": "loadPreviousSession", + "outputKey": "loadPreviousSession" + }, + { + "value2": "sendMessage", + "outputKey": "sendMessage" + } + ] + } + }, + "id": "84213c7b-abc7-4f40-9567-cd3484a4ae6b", + "name": "Switch", + "type": "n8n-nodes-base.switch", + "typeVersion": 2, + "position": [ + 300, + 280 + ] + }, + { + "parameters": { + "simplifyOutput": false + }, + "id": "3be7f076-98ed-472a-80b6-bf8d9538ac87", + "name": "Chat Messages Retriever", + "type": "@n8n/n8n-nodes-langchain.memoryChatRetriever", + "typeVersion": 1, + "position": [ + 620, + 140 + ] + }, + { + "parameters": { + "options": {} + }, + "id": "3417c644-8a91-4524-974a-45b4a46d0e2e", + "name": "Respond to Webhook", + "type": "n8n-nodes-base.respondToWebhook", + "typeVersion": 1, + "position": [ + 1240, + 140 + ] + }, + { + "parameters": { + "public": true, + "authentication": "n8nUserAuth", + "options": { + "loadPreviousSession": "manually", + "responseMode": "responseNode" + } + }, + "id": "1b30c239-a819-45b4-b0ae-bdd5b92a5424", + "name": "Chat Trigger", + "type": "@n8n/n8n-nodes-langchain.chatTrigger", + "typeVersion": 1, + "position": [ + 80, + 280 + ], + "webhookId": "ed3dea26-7d68-42b3-9032-98fe967d441d" + }, + { + "parameters": { + "aggregate": "aggregateAllItemData", + "options": {} + }, + "id": "79672cf0-686b-41eb-90ae-fd31b6da837d", + "name": "Aggregate", + "type": "n8n-nodes-base.aggregate", + "typeVersion": 1, + "position": [ + 1000, + 140 + ] + } + ], + "pinData": {}, + "connections": { + "OpenAI Chat Model": { + "ai_languageModel": [ + [ + { + "node": "AI Agent", + "type": "ai_languageModel", + "index": 0 + } + ] + ] + }, + "Window Buffer Memory": { + "ai_memory": [ + [ + { + "node": "AI Agent", + "type": "ai_memory", + "index": 0 + }, + { + "node": "Chat Messages Retriever", + "type": "ai_memory", + "index": 0 + } + ] + ] + }, + "Switch": { + "main": [ + [ + { + "node": "Chat Messages Retriever", + "type": "main", + "index": 0 + } + ], + [ + { + "node": "AI Agent", + "type": "main", + "index": 0 + } + ] + ] + }, + "Chat Messages Retriever": { + "main": [ + [ + { + "node": "Aggregate", + "type": "main", + "index": 0 + } + ] + ] + }, + "AI Agent": { + "main": [ + [ + { + "node": "Respond to Webhook", + "type": "main", + "index": 0 + } + ] + ] + }, + "Chat Trigger": { + "main": [ + [ + { + "node": "Switch", + "type": "main", + "index": 0 + } + ] + ] + }, + "Aggregate": { + "main": [ + [ + { + "node": "Respond to Webhook", + "type": "main", + "index": 0 + } + ] + ] + } + }, + "active": true, + "settings": { + "executionOrder": "v1" + }, + "versionId": "425c0efe-3aa0-4e0e-8c06-abe12234b1fd", + "id": "1569HF92Y02EUtsU", + "meta": { + "instanceId": "374b43d8b8d6299cc777811a4ad220fc688ee2d54a308cfb0de4450a5233ca9e" + }, + "tags": [] +} \ No newline at end of file diff --git a/packages/@n8n/chat/resources/workflow.json b/packages/@n8n/chat/resources/workflow.json new file mode 100644 index 0000000000..1bf4be681b --- /dev/null +++ b/packages/@n8n/chat/resources/workflow.json @@ -0,0 +1,119 @@ +{ + "name": "Hosted n8n AI Chat", + "nodes": [ + { + "parameters": { + "options": {} + }, + "id": "4c109d13-62a2-4e23-9979-e50201db743d", + "name": "OpenAI Chat Model", + "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi", + "typeVersion": 1, + "position": [ + 640, + 540 + ], + "credentials": { + "openAiApi": { + "id": "cIIkOhl7tUX1KsL6", + "name": "OpenAi account" + } + } + }, + { + "parameters": { + "sessionKey": "={{ $json.sessionId }}" + }, + "id": "b416df7b-4802-462f-8f74-f0a71dc4c0be", + "name": "Window Buffer Memory", + "type": "@n8n/n8n-nodes-langchain.memoryBufferWindow", + "typeVersion": 1, + "position": [ + 340, + 540 + ] + }, + { + "parameters": { + "text": "={{ $json.chatInput }}", + "options": {} + }, + "id": "4de25807-a2ef-4453-900e-e00e0021ecdc", + "name": "AI Agent", + "type": "@n8n/n8n-nodes-langchain.agent", + "typeVersion": 1.1, + "position": [ + 620, + 300 + ] + }, + { + "parameters": { + "public": true, + "options": { + "loadPreviousSession": "memory" + } + }, + "id": "5a9612ae-51c1-4be2-bd8b-8556872d1149", + "name": "Chat Trigger", + "type": "@n8n/n8n-nodes-langchain.chatTrigger", + "typeVersion": 1, + "position": [ + 340, + 300 + ], + "webhookId": "f406671e-c954-4691-b39a-66c90aa2f103" + } + ], + "pinData": {}, + "connections": { + "OpenAI Chat Model": { + "ai_languageModel": [ + [ + { + "node": "AI Agent", + "type": "ai_languageModel", + "index": 0 + } + ] + ] + }, + "Window Buffer Memory": { + "ai_memory": [ + [ + { + "node": "AI Agent", + "type": "ai_memory", + "index": 0 + }, + { + "node": "Chat Trigger", + "type": "ai_memory", + "index": 0 + } + ] + ] + }, + "Chat Trigger": { + "main": [ + [ + { + "node": "AI Agent", + "type": "main", + "index": 0 + } + ] + ] + } + }, + "active": true, + "settings": { + "executionOrder": "v1" + }, + "versionId": "6076136f-fdb4-48d9-b483-d1c24c95ef9e", + "id": "zaBHnDtj22BzEQ6K", + "meta": { + "instanceId": "374b43d8b8d6299cc777811a4ad220fc688ee2d54a308cfb0de4450a5233ca9e" + }, + "tags": [] +} diff --git a/packages/@n8n/chat/scripts/pack.js b/packages/@n8n/chat/scripts/pack.js new file mode 100644 index 0000000000..0dffb892e4 --- /dev/null +++ b/packages/@n8n/chat/scripts/pack.js @@ -0,0 +1,11 @@ +const path = require('path'); +const shelljs = require('shelljs'); + +const rootDirPath = path.resolve(__dirname, '..'); +const distDirPath = path.resolve(rootDirPath, 'dist'); + +shelljs.cd(rootDirPath); +shelljs.exec('npm run build'); + +shelljs.cd(distDirPath); +shelljs.exec('npm pack'); diff --git a/packages/@n8n/chat/scripts/postbuild.js b/packages/@n8n/chat/scripts/postbuild.js new file mode 100644 index 0000000000..2ce6a4e8c2 --- /dev/null +++ b/packages/@n8n/chat/scripts/postbuild.js @@ -0,0 +1,36 @@ +const path = require('path'); +const shelljs = require('shelljs'); +const glob = require('fast-glob'); + +const rootDirPath = path.resolve(__dirname, '..'); +const n8nRootDirPath = path.resolve(rootDirPath, '..', '..', '..'); +const distDirPath = path.resolve(rootDirPath, 'dist'); +const srcDirPath = path.resolve(rootDirPath, 'src'); +const libDirPath = path.resolve(rootDirPath, 'tmp', 'lib'); +const cjsDirPath = path.resolve(rootDirPath, 'tmp', 'cjs'); + +const packageJsonFilePath = path.resolve(rootDirPath, 'package.json'); +const readmeFilePath = path.resolve(rootDirPath, 'README.md'); +const licenseFilePath = path.resolve(n8nRootDirPath, 'LICENSE.md'); + +shelljs.cp(packageJsonFilePath, distDirPath); +shelljs.cp(readmeFilePath, distDirPath); +shelljs.cp(licenseFilePath, distDirPath); + +shelljs.mv(path.resolve(distDirPath, 'src'), path.resolve(distDirPath, 'types')); + +function moveFiles(files, from, to) { + files.forEach((file) => { + const toFile = file.replace(from, to); + shelljs.mkdir('-p', path.dirname(toFile)); + shelljs.mv(file, toFile); + }); +} + +const cjsFiles = glob.sync(path.resolve(cjsDirPath, '**', '*')); +moveFiles(cjsFiles, 'tmp/cjs', 'dist'); +shelljs.rm('-rf', cjsDirPath); + +const libFiles = glob.sync(path.resolve(libDirPath, '**/*')); +moveFiles(libFiles, 'tmp/lib', 'dist'); +shelljs.rm('-rf', libDirPath); diff --git a/packages/@n8n/chat/src/App.vue b/packages/@n8n/chat/src/App.vue new file mode 100644 index 0000000000..ec90bef425 --- /dev/null +++ b/packages/@n8n/chat/src/App.vue @@ -0,0 +1,23 @@ + + diff --git a/packages/@n8n/chat/src/__stories__/App.stories.ts b/packages/@n8n/chat/src/__stories__/App.stories.ts new file mode 100644 index 0000000000..ca93cdb240 --- /dev/null +++ b/packages/@n8n/chat/src/__stories__/App.stories.ts @@ -0,0 +1,43 @@ +/* eslint-disable @typescript-eslint/naming-convention */ +import type { StoryObj } from '@storybook/vue3'; +import { onMounted } from 'vue'; +import type { ChatOptions } from '@n8n/chat/types'; +import { createChat } from '@n8n/chat/index'; + +const webhookUrl = 'http://localhost:5678/webhook/f406671e-c954-4691-b39a-66c90aa2f103/chat'; + +const meta = { + title: 'Chat', + render: (args: Partial) => ({ + setup() { + onMounted(() => { + createChat(args); + }); + + return {}; + }, + template: '
', + }), + parameters: { + layout: 'fullscreen', + }, + tags: ['autodocs'], +}; + +// eslint-disable-next-line import/no-default-export +export default meta; +type Story = StoryObj; + +export const Fullscreen: Story = { + args: { + webhookUrl, + mode: 'fullscreen', + } satisfies Partial, +}; + +export const Windowed: Story = { + args: { + webhookUrl, + mode: 'window', + } satisfies Partial, +}; diff --git a/packages/@n8n/chat/src/__tests__/index.spec.ts b/packages/@n8n/chat/src/__tests__/index.spec.ts new file mode 100644 index 0000000000..5307880d67 --- /dev/null +++ b/packages/@n8n/chat/src/__tests__/index.spec.ts @@ -0,0 +1,218 @@ +import { fireEvent, waitFor } from '@testing-library/vue'; +import { + createFetchResponse, + createGetLatestMessagesResponse, + createSendMessageResponse, + getChatInputSendButton, + getChatInputTextarea, + getChatMessage, + getChatMessageByText, + getChatMessages, + getChatMessageTyping, + getChatWindowToggle, + getChatWindowWrapper, + getChatWrapper, + getGetStartedButton, + getMountingTarget, +} from '@n8n/chat/__tests__/utils'; +import { createChat } from '@n8n/chat/index'; + +describe('createChat()', () => { + let app: ReturnType; + + afterEach(() => { + vi.clearAllMocks(); + + app.unmount(); + }); + + describe('mode', () => { + it('should create fullscreen chat app with default options', () => { + const fetchSpy = vi.spyOn(window, 'fetch'); + fetchSpy.mockImplementationOnce(createFetchResponse(createGetLatestMessagesResponse())); + + app = createChat({ + mode: 'fullscreen', + }); + + expect(getMountingTarget()).toBeVisible(); + expect(getChatWrapper()).toBeVisible(); + expect(getChatWindowWrapper()).not.toBeInTheDocument(); + }); + + it('should create window chat app with default options', () => { + const fetchSpy = vi.spyOn(window, 'fetch'); + fetchSpy.mockImplementationOnce(createFetchResponse(createGetLatestMessagesResponse())); + + app = createChat({ + mode: 'window', + }); + + expect(getMountingTarget()).toBeDefined(); + expect(getChatWindowWrapper()).toBeVisible(); + expect(getChatWrapper()).not.toBeVisible(); + }); + + it('should open window chat app using toggle button', async () => { + const fetchSpy = vi.spyOn(window, 'fetch'); + fetchSpy.mockImplementationOnce(createFetchResponse(createGetLatestMessagesResponse())); + + app = createChat(); + + expect(getMountingTarget()).toBeVisible(); + expect(getChatWindowWrapper()).toBeVisible(); + + const trigger = getChatWindowToggle(); + await fireEvent.click(trigger as HTMLElement); + + expect(getChatWrapper()).toBeVisible(); + }); + }); + + describe('loadPreviousMessages', () => { + it('should load previous messages on mount', async () => { + const fetchSpy = vi.spyOn(global, 'fetch'); + fetchSpy.mockImplementation(createFetchResponse(createGetLatestMessagesResponse())); + + app = createChat({ + mode: 'fullscreen', + showWelcomeScreen: true, + }); + + const getStartedButton = getGetStartedButton(); + await fireEvent.click(getStartedButton as HTMLElement); + + expect(fetchSpy.mock.calls[0][1]).toEqual( + expect.objectContaining({ + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: expect.stringContaining('"action":"loadPreviousSession"') as unknown, + mode: 'cors', + cache: 'no-cache', + }), + ); + }); + }); + + describe('initialMessages', () => { + it.each(['fullscreen', 'window'] as Array<'fullscreen' | 'window'>)( + 'should show initial default messages in %s mode', + async (mode) => { + const fetchSpy = vi.spyOn(window, 'fetch'); + fetchSpy.mockImplementationOnce(createFetchResponse(createGetLatestMessagesResponse())); + + const initialMessages = ['Hello tester!', 'How are you?']; + app = createChat({ + mode, + initialMessages, + }); + + if (mode === 'window') { + const trigger = getChatWindowToggle(); + await fireEvent.click(trigger as HTMLElement); + } + + expect(getChatMessages().length).toBe(initialMessages.length); + expect(getChatMessageByText(initialMessages[0])).toBeInTheDocument(); + expect(getChatMessageByText(initialMessages[1])).toBeInTheDocument(); + }, + ); + }); + + describe('sendMessage', () => { + it.each(['window', 'fullscreen'] as Array<'fullscreen' | 'window'>)( + 'should send a message and render a text message in %s mode', + async (mode) => { + const input = 'Hello User World!'; + const output = 'Hello Bot World!'; + + const fetchSpy = vi.spyOn(window, 'fetch'); + fetchSpy + .mockImplementationOnce(createFetchResponse(createGetLatestMessagesResponse)) + .mockImplementationOnce(createFetchResponse(createSendMessageResponse(output))); + + app = createChat({ + mode, + }); + + if (mode === 'window') { + const trigger = getChatWindowToggle(); + await fireEvent.click(trigger as HTMLElement); + } + + expect(getChatMessageTyping()).not.toBeInTheDocument(); + expect(getChatMessages().length).toBe(2); + + await waitFor(() => expect(getChatInputTextarea()).toBeInTheDocument()); + + const textarea = getChatInputTextarea(); + const sendButton = getChatInputSendButton(); + await fireEvent.update(textarea as HTMLElement, input); + expect(sendButton).not.toBeDisabled(); + await fireEvent.click(sendButton as HTMLElement); + + expect(fetchSpy.mock.calls[1][1]).toEqual( + expect.objectContaining({ + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: expect.stringMatching(/"action":"sendMessage"/) as unknown, + mode: 'cors', + cache: 'no-cache', + }), + ); + expect(fetchSpy.mock.calls[1][1]?.body).toContain(`"${input}"`); + + expect(getChatMessages().length).toBe(3); + expect(getChatMessageByText(input)).toBeInTheDocument(); + expect(getChatMessageTyping()).toBeVisible(); + + await waitFor(() => expect(getChatMessageTyping()).not.toBeInTheDocument()); + expect(getChatMessageByText(output)).toBeInTheDocument(); + }, + ); + + it.each(['fullscreen', 'window'] as Array<'fullscreen' | 'window'>)( + 'should send a message and render a code markdown message in %s mode', + async (mode) => { + const input = 'Teach me javascript!'; + const output = '# Code\n```js\nconsole.log("Hello World!");\n```'; + + const fetchSpy = vi.spyOn(window, 'fetch'); + fetchSpy + .mockImplementationOnce(createFetchResponse(createGetLatestMessagesResponse)) + .mockImplementationOnce(createFetchResponse(createSendMessageResponse(output))); + + app = createChat({ + mode, + }); + + if (mode === 'window') { + const trigger = getChatWindowToggle(); + await fireEvent.click(trigger as HTMLElement); + } + + await waitFor(() => expect(getChatInputTextarea()).toBeInTheDocument()); + + const textarea = getChatInputTextarea(); + const sendButton = getChatInputSendButton(); + await fireEvent.update(textarea as HTMLElement, input); + await fireEvent.click(sendButton as HTMLElement); + + expect(getChatMessageByText(input)).toBeInTheDocument(); + expect(getChatMessages().length).toBe(3); + + await waitFor(() => expect(getChatMessageTyping()).not.toBeInTheDocument()); + + const lastMessage = getChatMessage(-1); + expect(lastMessage).toBeInTheDocument(); + + expect(lastMessage.querySelector('h1')).toHaveTextContent('Code'); + expect(lastMessage.querySelector('code')).toHaveTextContent('console.log("Hello World!");'); + }, + ); + }); +}); diff --git a/packages/@n8n/chat/src/__tests__/setup.ts b/packages/@n8n/chat/src/__tests__/setup.ts new file mode 100644 index 0000000000..7b0828bfa8 --- /dev/null +++ b/packages/@n8n/chat/src/__tests__/setup.ts @@ -0,0 +1 @@ +import '@testing-library/jest-dom'; diff --git a/packages/@n8n/chat/src/__tests__/utils/create.ts b/packages/@n8n/chat/src/__tests__/utils/create.ts new file mode 100644 index 0000000000..db34921acd --- /dev/null +++ b/packages/@n8n/chat/src/__tests__/utils/create.ts @@ -0,0 +1,16 @@ +import { createChat } from '@n8n/chat/index'; + +export function createTestChat(options: Parameters[0] = {}): { + unmount: () => void; + container: Element; +} { + const app = createChat(options); + + const container = app._container as Element; + const unmount = () => app.unmount(); + + return { + unmount, + container, + }; +} diff --git a/packages/@n8n/chat/src/__tests__/utils/fetch.ts b/packages/@n8n/chat/src/__tests__/utils/fetch.ts new file mode 100644 index 0000000000..51d539d773 --- /dev/null +++ b/packages/@n8n/chat/src/__tests__/utils/fetch.ts @@ -0,0 +1,18 @@ +import type { LoadPreviousSessionResponse, SendMessageResponse } from '@n8n/chat/types'; + +export function createFetchResponse(data: T) { + return async () => + ({ + json: async () => new Promise((resolve) => resolve(data)), + }) as Response; +} + +export const createGetLatestMessagesResponse = ( + data: LoadPreviousSessionResponse['data'] = [], +): LoadPreviousSessionResponse => ({ data }); + +export const createSendMessageResponse = ( + output: SendMessageResponse['output'], +): SendMessageResponse => ({ + output, +}); diff --git a/packages/@n8n/chat/src/__tests__/utils/index.ts b/packages/@n8n/chat/src/__tests__/utils/index.ts new file mode 100644 index 0000000000..6dc234f1d0 --- /dev/null +++ b/packages/@n8n/chat/src/__tests__/utils/index.ts @@ -0,0 +1,3 @@ +export * from './create'; +export * from './fetch'; +export * from './selectors'; diff --git a/packages/@n8n/chat/src/__tests__/utils/selectors.ts b/packages/@n8n/chat/src/__tests__/utils/selectors.ts new file mode 100644 index 0000000000..a510652b0d --- /dev/null +++ b/packages/@n8n/chat/src/__tests__/utils/selectors.ts @@ -0,0 +1,53 @@ +import { screen } from '@testing-library/vue'; +import { defaultMountingTarget } from '@n8n/chat/constants'; + +export function getMountingTarget(target = defaultMountingTarget) { + return document.querySelector(target); +} + +export function getChatWindowWrapper() { + return document.querySelector('.chat-window-wrapper'); +} + +export function getChatWindowToggle() { + return document.querySelector('.chat-window-toggle'); +} + +export function getChatWrapper() { + return document.querySelector('.chat-wrapper'); +} + +export function getChatMessages() { + return document.querySelectorAll('.chat-message:not(.chat-message-typing)'); +} + +export function getChatMessage(index: number) { + const messages = getChatMessages(); + return index < 0 ? messages[messages.length + index] : messages[index]; +} + +export function getChatMessageByText(text: string) { + return screen.queryByText(text, { + selector: '.chat-message:not(.chat-message-typing) .chat-message-markdown p', + }); +} + +export function getChatMessageTyping() { + return document.querySelector('.chat-message-typing'); +} + +export function getGetStartedButton() { + return document.querySelector('.chat-get-started .chat-button'); +} + +export function getChatInput() { + return document.querySelector('.chat-input'); +} + +export function getChatInputTextarea() { + return document.querySelector('.chat-input textarea'); +} + +export function getChatInputSendButton() { + return document.querySelector('.chat-input .chat-input-send-button'); +} diff --git a/packages/@n8n/chat/src/api/generic.ts b/packages/@n8n/chat/src/api/generic.ts new file mode 100644 index 0000000000..98385b90c4 --- /dev/null +++ b/packages/@n8n/chat/src/api/generic.ts @@ -0,0 +1,63 @@ +async function getAccessToken() { + return ''; +} + +export async function authenticatedFetch(...args: Parameters): Promise { + const accessToken = await getAccessToken(); + + const response = await fetch(args[0], { + ...args[1], + mode: 'cors', + cache: 'no-cache', + headers: { + 'Content-Type': 'application/json', + ...(accessToken ? { authorization: `Bearer ${accessToken}` } : {}), + ...args[1]?.headers, + }, + }); + + return (await response.json()) as Promise; +} + +export async function get(url: string, query: object = {}, options: RequestInit = {}) { + let resolvedUrl = url; + if (Object.keys(query).length > 0) { + resolvedUrl = `${resolvedUrl}?${new URLSearchParams( + query as Record, + ).toString()}`; + } + + return authenticatedFetch(resolvedUrl, { ...options, method: 'GET' }); +} + +export async function post(url: string, body: object = {}, options: RequestInit = {}) { + return authenticatedFetch(url, { + ...options, + method: 'POST', + body: JSON.stringify(body), + }); +} + +export async function put(url: string, body: object = {}, options: RequestInit = {}) { + return authenticatedFetch(url, { + ...options, + method: 'PUT', + body: JSON.stringify(body), + }); +} + +export async function patch(url: string, body: object = {}, options: RequestInit = {}) { + return authenticatedFetch(url, { + ...options, + method: 'PATCH', + body: JSON.stringify(body), + }); +} + +export async function del(url: string, body: object = {}, options: RequestInit = {}) { + return authenticatedFetch(url, { + ...options, + method: 'DELETE', + body: JSON.stringify(body), + }); +} diff --git a/packages/@n8n/chat/src/api/index.ts b/packages/@n8n/chat/src/api/index.ts new file mode 100644 index 0000000000..a78afb92f2 --- /dev/null +++ b/packages/@n8n/chat/src/api/index.ts @@ -0,0 +1,2 @@ +export * from './generic'; +export * from './message'; diff --git a/packages/@n8n/chat/src/api/message.ts b/packages/@n8n/chat/src/api/message.ts new file mode 100644 index 0000000000..ff629bc917 --- /dev/null +++ b/packages/@n8n/chat/src/api/message.ts @@ -0,0 +1,37 @@ +import { get, post } from '@n8n/chat/api/generic'; +import type { + ChatOptions, + LoadPreviousSessionResponse, + SendMessageResponse, +} from '@n8n/chat/types'; + +export async function loadPreviousSession(sessionId: string, options: ChatOptions) { + const method = options.webhookConfig?.method === 'POST' ? post : get; + return method( + `${options.webhookUrl}`, + { + action: 'loadPreviousSession', + [options.chatSessionKey as string]: sessionId, + ...(options.metadata ? { metadata: options.metadata } : {}), + }, + { + headers: options.webhookConfig?.headers, + }, + ); +} + +export async function sendMessage(message: string, sessionId: string, options: ChatOptions) { + const method = options.webhookConfig?.method === 'POST' ? post : get; + return method( + `${options.webhookUrl}`, + { + action: 'sendMessage', + [options.chatSessionKey as string]: sessionId, + [options.chatInputKey as string]: message, + ...(options.metadata ? { metadata: options.metadata } : {}), + }, + { + headers: options.webhookConfig?.headers, + }, + ); +} diff --git a/packages/@n8n/chat/src/components/Button.vue b/packages/@n8n/chat/src/components/Button.vue new file mode 100644 index 0000000000..ca35153fc6 --- /dev/null +++ b/packages/@n8n/chat/src/components/Button.vue @@ -0,0 +1,41 @@ + + diff --git a/packages/@n8n/chat/src/components/Chat.vue b/packages/@n8n/chat/src/components/Chat.vue new file mode 100644 index 0000000000..e9a4476a98 --- /dev/null +++ b/packages/@n8n/chat/src/components/Chat.vue @@ -0,0 +1,52 @@ + + + diff --git a/packages/@n8n/chat/src/components/ChatWindow.vue b/packages/@n8n/chat/src/components/ChatWindow.vue new file mode 100644 index 0000000000..1d1f7ae3c2 --- /dev/null +++ b/packages/@n8n/chat/src/components/ChatWindow.vue @@ -0,0 +1,125 @@ + + + + + diff --git a/packages/@n8n/chat/src/components/GetStarted.vue b/packages/@n8n/chat/src/components/GetStarted.vue new file mode 100644 index 0000000000..7c1a6aea2b --- /dev/null +++ b/packages/@n8n/chat/src/components/GetStarted.vue @@ -0,0 +1,24 @@ + + + + diff --git a/packages/@n8n/chat/src/components/GetStartedFooter.vue b/packages/@n8n/chat/src/components/GetStartedFooter.vue new file mode 100644 index 0000000000..dca1d16bc6 --- /dev/null +++ b/packages/@n8n/chat/src/components/GetStartedFooter.vue @@ -0,0 +1,20 @@ + + + + diff --git a/packages/@n8n/chat/src/components/Input.vue b/packages/@n8n/chat/src/components/Input.vue new file mode 100644 index 0000000000..68b9deeb24 --- /dev/null +++ b/packages/@n8n/chat/src/components/Input.vue @@ -0,0 +1,93 @@ + + +