Skip to content

Commit

Permalink
Merge branch 'master' into feature/resource-mapping-component
Browse files Browse the repository at this point in the history
* master: (30 commits)
  feat(core): Add versionControl feature flag (#6000)
  fix: Small changes to user activation modal (no-changelog) (#5962)
  fix(editor): Cleanup demo/video experiment (#5974)
  feat: Add variables feature (#5602)
  ci: Improve test for wait node (#5997)
  fix(editor): Fix moving canvas on middle click preventing lasso selection (#5996)
  fix(HTTP Request Node): Fix itemIndex in HTTP Request errors (#5991)
  refactor(editor): Refactor history and debounce mixins to composables (no-changelog) (#5930)
  feat(GitLab Node): Add Additional parameters for File List (#5621)
  build: Update license SDK to v2.1.0 (no-changelog) (#5987)
  fix(OpenAI Node): Update OpenAI Text Moderate input placeholder text (#5823)
  fix(Code Node): Handle user code returning `null` and `undefined` (#5989)
  fix(Google Sheets Trigger Node): Return actual error message
  fix(core): Make `getExecutionId` available on all nodes types (#5990)
  ci: Add checklist item hinting at not adding new dependencies for nodes (no-changelog) (#5985)
  fix(core): Improve SAML connection test result views (#5981)
  fix(IF Node): Fix typo in combine description (no-changelog) (#5964)
  ci: Add a workflow to push n8n releases to release channels (no-changelog) (#5839)
  fix(editor): Make sure to redirect to blank canvas after personalisation modal (#5980)
  fix(core): Fix paired item returning wrong data (#5898)
  ...

# Conflicts:
#	packages/editor-ui/src/Interface.ts
  • Loading branch information
MiloradFilipovic committed Apr 18, 2023
2 parents 8576dcc + 33299ca commit 6246bcb
Show file tree
Hide file tree
Showing 156 changed files with 4,585 additions and 675 deletions.
22 changes: 6 additions & 16 deletions .github/workflows/e2e-tests-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,10 @@ on:
- 'master'

jobs:
# We disable this for now because cancelling runs makes the Cypress Cloud tests to hang.
# cancel-previous-runs:
# runs-on: ubuntu-latest
# name: 'Cancel previous e2e test runs'
# strategy:
# matrix:
# node-version: [16.x]

# steps:
# - name: 'Cancel previous runs'
# uses: styfle/[email protected]
# with:
# access_token: ${{ github.token }}

run-e2e-tests:
name: E2E [Electron/Node 16]
uses: ./.github/workflows/e2e-reusable.yml
if: ${{ github.event.review.state == 'approved' }}
if: ${{ github.event.review.state == 'approved' && !contains(github.event.pull_request.labels.*.name, 'community') }}
with:
branch: ${{ github.event.pull_request.head.ref }}
user: ${{ github.event.pull_request.user.login || 'PR User' }}
Expand All @@ -40,7 +26,7 @@ jobs:
if: always()
steps:
- name: E2E success comment
if: needs.run-e2e-tests.result == 'success'
if: ${{!contains(github.event.pull_request.labels.*.name, 'community') || needs.run-e2e-tests.result == 'success' }}
uses: peter-evans/create-or-update-comment@v3
with:
issue-number: ${{ github.event.pull_request.number }}
Expand All @@ -57,6 +43,10 @@ jobs:
:warning: Some Cypress E2E specs are failing, please fix them before merging
token: ${{ secrets.GITHUB_TOKEN }}

- name: Success job if community PR
if: ${{ contains(github.event.pull_request.labels.*.name, 'community') }}
run: exit 0

- name: Fail job if run-e2e-tests failed
if: needs.run-e2e-tests.result == 'failure'
run: exit 1
42 changes: 42 additions & 0 deletions .github/workflows/release-push-to-channel.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: 'Release: Push to Channel'

on:
workflow_dispatch:
inputs:
version:
description: 'n8n Release version to push to a channel'
required: true

release-channel:
description: 'Release channel'
required: true
type: choice
default: 'next'
options:
- next
- latest

jobs:
release-to-npm:
name: Release to NPM
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/setup-node@v3
with:
node-version: 16.x
- run: |
echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}" > ~/.npmrc
npm dist-tag add n8n@${{ github.event.inputs.version }} ${{ github.event.inputs.release-channel }}
release-to-docker-hub:
name: Release to DockerHub
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: docker/login-action@v2
with:
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 }}
36 changes: 19 additions & 17 deletions CHECKLIST.yml
Original file line number Diff line number Diff line change
@@ -1,48 +1,50 @@
paths:
"packages/**":
'packages/**':
- If fixing bug, added test to cover scenario.
- If addressing forum or Github issue, added link to description.
"packages/**/*.ts":
'packages/**/*.ts':
- Added unit tests to cover new or updated functionality.
"**/*.vue":
'**/*.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.
'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/**":
'n8n/packages/cli/src/api/**':
- Added integration tests for new endpoints.

# editor ui
"packages/editor-ui/**/*.vue":
'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":
'packages/editor-ui/src/mixins/restApi.ts':
- Avoided adding new methods. Only deleted from here.
"packages/editor-ui/src/mixins/**":
'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/**":
'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/**":
'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":
'packages/design-system/**/*.vue':
- Used design system tokens (colors, spacings...) where possible.
- Updated Storybook with new component or updated functionality.

# e2e
"cypress/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.
- Ensured each spec does not depend on any another spec to pass.
39 changes: 38 additions & 1 deletion cypress/e2e/13-pinning.cy.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
import {
HTTP_REQUEST_NODE_NAME,
MANUAL_TRIGGER_NODE_DISPLAY_NAME,
PIPEDRIVE_NODE_NAME,
SET_NODE_NAME,
} from '../constants';
import { WorkflowPage, NDV } from '../pages';

const workflowPage = new WorkflowPage();
Expand Down Expand Up @@ -44,7 +50,7 @@ describe('Data pinning', () => {
});
});

it('Should be be able to set pinned data', () => {
it('Should be able to set pinned data', () => {
workflowPage.actions.addInitialNodeToCanvas('Schedule Trigger', { keepNdvOpen: true });
ndv.getters.container().should('be.visible');
ndv.getters.pinDataButton().should('not.exist');
Expand All @@ -67,4 +73,35 @@ describe('Data pinning', () => {
ndv.getters.outputTableHeaders().first().should('include.text', 'test');
ndv.getters.outputTbodyCell(1, 0).should('include.text', 1);
});

it('Should be able to reference paired items in a node located before pinned data', () => {
workflowPage.actions.addInitialNodeToCanvas(MANUAL_TRIGGER_NODE_DISPLAY_NAME);
workflowPage.actions.addNodeToCanvas(HTTP_REQUEST_NODE_NAME, true, true);
ndv.actions.setPinnedData([{ http: 123 }]);
ndv.actions.close();

workflowPage.actions.addNodeToCanvas(PIPEDRIVE_NODE_NAME, true, true);
ndv.actions.setPinnedData(Array(3).fill({ pipedrive: 123 }));
ndv.actions.close();

workflowPage.actions.addNodeToCanvas(SET_NODE_NAME, true, true);
setExpressionOnStringValueInSet(`{{ $('${HTTP_REQUEST_NODE_NAME}').item`);

const output = '[Object: {"json": {"http": 123}, "pairedItem": {"item": 0}}]';

cy.get('div').contains(output).should('be.visible');
});
});

function setExpressionOnStringValueInSet(expression: string) {
cy.get('button').contains('Execute node').click();
cy.get('input[placeholder="Add Value"]').click();
cy.get('span').contains('String').click();

ndv.getters.nthParam(3).contains('Expression').invoke('show').click();

ndv.getters
.inlineExpressionEditorInput()
.clear()
.type(expression, { parseSpecialCharSequences: false });
}
7 changes: 7 additions & 0 deletions cypress/e2e/22-user-activation-modal.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,20 @@ describe('User activation survey', () => {

workflowPage.actions.activateWorkflow();

cy.intercept('GET', '/rest/workflows').as('getWorkflows');
cy.intercept('GET', '/rest/credentials').as('getCredentials');
cy.intercept('GET', '/rest/active').as('getActive');

cy.request(method, `${BASE_WEBHOOK_URL}/${path}`).then((response) => {
expect(response.status).to.eq(200);
cy.visit('/');
cy.reload();

cy.wait(['@getWorkflows', '@getCredentials', '@getActive']);
userActivationSurveyModal.getters.modalContainer().should('be.visible');
userActivationSurveyModal.getters.feedbackInput().should('be.visible');
userActivationSurveyModal.getters.feedbackInput().type('testing');
userActivationSurveyModal.getters.feedbackInput().should('have.value', 'testing');
userActivationSurveyModal.getters.sendFeedbackButton().click();
});
});
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@
"tsconfig-paths": "^4.1.2"
},
"dependencies": {
"@n8n_io/license-sdk": "~2.0.0",
"@n8n_io/license-sdk": "~2.1.0",
"@oclif/command": "^1.8.16",
"@oclif/core": "^1.16.4",
"@oclif/errors": "^1.3.6",
Expand Down
2 changes: 2 additions & 0 deletions packages/cli/src/Db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import {
SharedWorkflowRepository,
TagRepository,
UserRepository,
VariablesRepository,
WebhookRepository,
WorkflowRepository,
WorkflowStatisticsRepository,
Expand Down Expand Up @@ -178,6 +179,7 @@ export async function init(
collections.SharedWorkflow = Container.get(SharedWorkflowRepository);
collections.Tag = Container.get(TagRepository);
collections.User = Container.get(UserRepository);
collections.Variables = Container.get(VariablesRepository);
collections.Webhook = Container.get(WebhookRepository);
collections.Workflow = Container.get(WorkflowRepository);
collections.WorkflowStatistics = Container.get(WorkflowStatisticsRepository);
Expand Down
8 changes: 8 additions & 0 deletions packages/cli/src/Interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ import type {
SharedWorkflowRepository,
TagRepository,
UserRepository,
VariablesRepository,
WebhookRepository,
WorkflowRepository,
WorkflowStatisticsRepository,
Expand Down Expand Up @@ -99,6 +100,7 @@ export interface IDatabaseCollections {
SharedWorkflow: SharedWorkflowRepository;
Tag: TagRepository;
User: UserRepository;
Variables: VariablesRepository;
Webhook: WebhookRepository;
Workflow: WorkflowRepository;
WorkflowStatistics: WorkflowStatisticsRepository;
Expand Down Expand Up @@ -458,6 +460,7 @@ export interface IInternalHooksClass {
}): Promise<void>;
onApiKeyCreated(apiKeyDeletedData: { user: User; public_api: boolean }): Promise<void>;
onApiKeyDeleted(apiKeyDeletedData: { user: User; public_api: boolean }): Promise<void>;
onVariableCreated(createData: { variable_type: string }): Promise<void>;
}

export interface IVersionNotificationSettings {
Expand Down Expand Up @@ -538,11 +541,16 @@ export interface IN8nUISettings {
saml: boolean;
logStreaming: boolean;
advancedExecutionFilters: boolean;
variables: boolean;
versionControl: boolean;
};
hideUsagePage: boolean;
license: {
environment: 'production' | 'staging';
};
variables: {
limit: number;
};
}

export interface IPersonalizationSurveyAnswers {
Expand Down
4 changes: 4 additions & 0 deletions packages/cli/src/InternalHooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -981,4 +981,8 @@ export class InternalHooks implements IInternalHooksClass {
async onAuditGeneratedViaCli() {
return this.telemetry.track('Instance generated security audit via CLI command');
}

async onVariableCreated(createData: { variable_type: string }): Promise<void> {
return this.telemetry.track('User created variable', createData);
}
}
21 changes: 19 additions & 2 deletions packages/cli/src/License.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@ import type { ILogger } from 'n8n-workflow';
import { getLogger } from './Logger';
import config from '@/config';
import * as Db from '@/Db';
import { LICENSE_FEATURES, N8N_VERSION, SETTINGS_LICENSE_CERT_KEY } from './constants';
import {
LICENSE_FEATURES,
LICENSE_QUOTAS,
N8N_VERSION,
SETTINGS_LICENSE_CERT_KEY,
} from './constants';
import { Service } from 'typedi';

async function loadCertStr(): Promise<TLicenseBlock> {
Expand Down Expand Up @@ -119,6 +124,14 @@ export class License {
return this.isFeatureEnabled(LICENSE_FEATURES.ADVANCED_EXECUTION_FILTERS);
}

isVariablesEnabled() {
return this.isFeatureEnabled(LICENSE_FEATURES.VARIABLES);
}

isVersionControlEnabled() {
return this.isFeatureEnabled(LICENSE_FEATURES.VERSION_CONTROL);
}

getCurrentEntitlements() {
return this.manager?.getCurrentEntitlements() ?? [];
}
Expand Down Expand Up @@ -162,7 +175,11 @@ export class License {

// Helper functions for computed data
getTriggerLimit(): number {
return (this.getFeatureValue('quota:activeWorkflows') ?? -1) as number;
return (this.getFeatureValue(LICENSE_QUOTAS.TRIGGER_LIMIT) ?? -1) as number;
}

getVariablesLimit(): number {
return (this.getFeatureValue(LICENSE_QUOTAS.VARIABLES_LIMIT) ?? -1) as number;
}

getPlanName(): string {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ export = {

// user does not have workflows hence no executions
// or the execution he is trying to access belongs to a workflow he does not own
if (!sharedWorkflowsIds.length) {
if (!sharedWorkflowsIds.length || (workflowId && !sharedWorkflowsIds.includes(workflowId))) {
return res.status(200).json({ data: [], nextCursor: null });
}

Expand All @@ -105,7 +105,7 @@ export = {
limit,
lastId,
includeData,
...(workflowId && { workflowIds: [workflowId] }),
workflowIds: workflowId ? [workflowId] : sharedWorkflowsIds,
excludedExecutionsIds: runningExecutionsIds,
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,12 @@ function insertIf(condition: boolean, elements: string[]): string[] {
}

export async function getSharedWorkflowIds(user: User): Promise<string[]> {
const where = user.globalRole.name === 'owner' ? {} : { userId: user.id };
const sharedWorkflows = await Db.collections.SharedWorkflow.find({
where: { userId: user.id },
where,
select: ['workflowId'],
});
return sharedWorkflows.map(({ workflowId }) => workflowId);

return sharedWorkflows.map(({ workflowId }) => workflowId);
}
Expand Down
Loading

0 comments on commit 6246bcb

Please sign in to comment.