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: (22 commits)
  fix: Remove workflow execution credential error message when instance owner (#6116)
  refactor(editor): Drop vue-typed-mixins (no-changelog) (#6257)
  feat(core): Return OAuth2 error body if available (#5794)
  fix: Add itemSize config to workflows view recycle scroller (no-changelog) (#6238)
  fix(editor): Fix close and cancel operations on useMessage (no-changelog) (#6260)
  feat(editor): Add cloud ExecutionsUsage and API blocking using licenses (#6159)
  refactor(editor): Turn showMessage mixin to composable (#6081) (#6244)
  feat(core): Add experimental proxy support to License-SDK (no-changelog) (#6253)
  feat(core): Reduce the number of events sent to Sentry (#6235)
  feat(editor): Drop support for legacy browsers that do not have native ESM support (#6239)
  refactor(editor): Add missing interface IUserListAction (no-changelog) (#6241)
  ci: Debug e2e pipeline (no-changelog) (#6240)
  Revert "refactor(editor): Turn showMessage mixin to composable" (#6243)
  feat(editor): Updating node reference pattern in expression editor (#6228)
  refactor(editor): Remove unused dependencies (no-changelog) (#6223)
  refactor(editor): Turn showMessage mixin to composable (#6081)
  feat(editor): Version Control settings update (WIP) (#6233)
  ci: Fix linting issue on master (no-changelog) (#6232)
  fix: Prevent type error messages for manual executions (no-changelog) (#6229)
  fix(Code Node): Restore help text (#6231)
  ...
  • Loading branch information
MiloradFilipovic committed May 16, 2023
2 parents cc65525 + e81a964 commit 723bffc
Show file tree
Hide file tree
Showing 191 changed files with 2,789 additions and 1,890 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/e2e-tests-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,5 +48,5 @@ jobs:
run: exit 0

- name: Fail job if run-e2e-tests failed
if: needs.run-e2e-tests.result == 'failure'
if: ${{ github.event.review.state != 'approved' || needs.run-e2e-tests.result == 'failure' }}
run: exit 1
8 changes: 4 additions & 4 deletions cypress/e2e/14-mapping.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ describe('Data mapping', () => {
ndv.actions.mapToParameter('value');
ndv.getters
.inlineExpressionEditorInput()
.should('have.text', `{{ $node['${SCHEDULE_TRIGGER_NODE_NAME}'].json.input[0].count }}`);
.should('have.text', `{{ $('${SCHEDULE_TRIGGER_NODE_NAME}').item.json.input[0].count }}`);
ndv.getters.parameterExpressionPreview('value').should('not.exist');

ndv.actions.switchInputMode('Table');
Expand All @@ -203,7 +203,7 @@ describe('Data mapping', () => {
.inlineExpressionEditorInput()
.should(
'have.text',
`{{ $node['${SCHEDULE_TRIGGER_NODE_NAME}'].json.input[0].count }} {{ $node['${SCHEDULE_TRIGGER_NODE_NAME}'].json.input }}`,
`{{ $('${SCHEDULE_TRIGGER_NODE_NAME}').item.json.input[0].count }} {{ $('${SCHEDULE_TRIGGER_NODE_NAME}').item.json.input }}`,
);
ndv.actions.validateExpressionPreview('value', ' ');

Expand Down Expand Up @@ -311,12 +311,12 @@ describe('Data mapping', () => {
ndv.getters.parameterInput('keepOnlySet').find('input[type="text"]')
.should('exist')
.invoke('css', 'border')
.then((border) => expect(border).to.include('1.5px dashed rgb(90, 76, 194)'));
.then((border) => expect(border).to.include('dashed rgb(90, 76, 194)'));

ndv.getters.parameterInput('value').find('input[type="text"]')
.should('exist')
.invoke('css', 'border')
.then((border) => expect(border).to.include('1.5px dashed rgb(90, 76, 194)'));
.then((border) => expect(border).to.include('dashed rgb(90, 76, 194)'));
});

});
2 changes: 1 addition & 1 deletion cypress/e2e/17-sharing.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ describe('Sharing', () => {

cy.waitForLoad();
cy.visit(workflowsPage.url);
workflowsPage.getters.workflowCard('Workflow W2').click();
workflowsPage.getters.workflowCard('Workflow W2').click('top');
workflowPage.actions.executeWorkflow();
});

Expand Down
9 changes: 8 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@
"@ngneat/falso": "^6.1.0",
"@types/jest": "^29.5.0",
"@types/supertest": "^2.0.12",
"@vitejs/plugin-vue2": "^2.2.0",
"@vitest/coverage-c8": "^0.28.5",
"c8": "^7.12.0",
"cross-env": "^7.0.3",
"cypress": "^12.8.1",
"cypress-real-events": "^1.7.6",
Expand All @@ -60,7 +63,11 @@
"ts-jest": "^29.1.0",
"tsc-watch": "^6.0.0",
"turbo": "1.8.8",
"typescript": "*"
"typescript": "*",
"vite": "^4.0.4",
"vitest": "^0.28.5",
"vue-template-compiler": "^2.7.14",
"vue-tsc": "^1.0.24"
},
"pnpm": {
"onlyBuiltDependencies": [
Expand Down
6 changes: 6 additions & 0 deletions packages/@n8n_io/eslint-config/frontend.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,11 @@ module.exports = {
'vue/no-deprecated-slot-scope-attribute': 'error',
'vue/no-multiple-template-root': 'error',
'vue/v-slot-style': 'error',

// TODO: remove these
'vue/no-unused-components': 'warn',
'vue/return-in-computed-property': 'warn',
'vue/no-mutating-props': 'warn',
'@typescript-eslint/no-floating-promises': 'warn',
},
};
2 changes: 1 addition & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@
"tsconfig-paths": "^4.1.2"
},
"dependencies": {
"@n8n_io/license-sdk": "~2.3.0",
"@n8n_io/license-sdk": "~2.4.0",
"@oclif/command": "^1.8.16",
"@oclif/core": "^1.16.4",
"@oclif/errors": "^1.3.6",
Expand Down
16 changes: 14 additions & 2 deletions packages/cli/src/ErrorReporting.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { createHash } from 'crypto';
import config from '@/config';
import { ErrorReporterProxy } from 'n8n-workflow';
import { ErrorReporterProxy, NodeError } from 'n8n-workflow';

let initialized = false;

Expand All @@ -17,7 +18,7 @@ export const initErrorHandling = async () => {
const dsn = config.getEnv('diagnostics.config.sentry.dsn');
const { N8N_VERSION: release, ENVIRONMENT: environment } = process.env;

const { init, captureException } = await import('@sentry/node');
const { init, captureException, addGlobalEventProcessor } = await import('@sentry/node');
// eslint-disable-next-line @typescript-eslint/naming-convention
const { RewriteFrames } = await import('@sentry/integrations');

Expand All @@ -32,6 +33,17 @@ export const initErrorHandling = async () => {
},
});

const seenErrors = new Set<string>();
addGlobalEventProcessor((event, { originalException }) => {
if (originalException instanceof NodeError && originalException.severity === 'warning')
return null;
if (!event.exception) return null;
const eventHash = createHash('sha1').update(JSON.stringify(event.exception)).digest('base64');
if (seenErrors.has(eventHash)) return null;
seenErrors.add(eventHash);
return event;
});

process.on('uncaughtException', (error) => {
ErrorReporterProxy.error(error);
if (error.constructor?.name !== 'AxiosError') throw error;
Expand Down
4 changes: 4 additions & 0 deletions packages/cli/src/License.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,10 @@ export class License {
return this.isFeatureEnabled(LICENSE_FEATURES.VERSION_CONTROL);
}

isAPIDisabled() {
return this.isFeatureEnabled(LICENSE_FEATURES.API_DISABLED);
}

getCurrentEntitlements() {
return this.manager?.getCurrentEntitlements() ?? [];
}
Expand Down
4 changes: 3 additions & 1 deletion packages/cli/src/NodeTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ import { LoadNodesAndCredentials } from './LoadNodesAndCredentials';

@Service()
export class NodeTypes implements INodeTypes {
constructor(private nodesAndCredentials: LoadNodesAndCredentials) {
constructor(private nodesAndCredentials: LoadNodesAndCredentials) {}

init() {
// Some nodeTypes need to get special parameters applied like the
// polling nodes the polling times
this.applySpecialNodeParameters();
Expand Down
10 changes: 10 additions & 0 deletions packages/cli/src/PublicApi/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import * as Db from '@/Db';
import { getInstanceBaseUrl } from '@/UserManagement/UserManagementHelper';
import { Container } from 'typedi';
import { InternalHooks } from '@/InternalHooks';
import { License } from '@/License';

async function createApiRouter(
version: string,
Expand Down Expand Up @@ -151,3 +152,12 @@ export const loadPublicApiVersions = async (
apiLatestVersion: Number(versions.pop()?.charAt(1)) ?? 1,
};
};

function isApiEnabledByLicense(): boolean {
const license = Container.get(License);
return !license.isAPIDisabled();
}

export function isApiEnabled(): boolean {
return !config.get('publicApi.disabled') && isApiEnabledByLicense();
}
8 changes: 4 additions & 4 deletions packages/cli/src/Server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ import {

import { executionsController } from '@/executions/executions.controller';
import { workflowStatsController } from '@/api/workflowStats.api';
import { loadPublicApiVersions } from '@/PublicApi';
import { isApiEnabled, loadPublicApiVersions } from '@/PublicApi';
import {
getInstanceBaseUrl,
isEmailSetUp,
Expand Down Expand Up @@ -280,7 +280,7 @@ export class Server extends AbstractServer {
},
},
publicApi: {
enabled: !config.getEnv('publicApi.disabled'),
enabled: isApiEnabled(),
latestVersion: 1,
path: config.getEnv('publicApi.path'),
swaggerUi: {
Expand Down Expand Up @@ -541,7 +541,7 @@ export class Server extends AbstractServer {
this.endpointWebhook,
this.endpointWebhookTest,
this.endpointPresetCredentials,
config.getEnv('publicApi.disabled') ? publicApiEndpoint : '',
isApiEnabled() ? '' : publicApiEndpoint,
...excludeEndpoints.split(':'),
].filter((u) => !!u);

Expand All @@ -567,7 +567,7 @@ export class Server extends AbstractServer {
// Public API
// ----------------------------------------

if (!config.getEnv('publicApi.disabled')) {
if (isApiEnabled()) {
const { apiRouters, apiLatestVersion } = await loadPublicApiVersions(publicApiEndpoint);
this.app.use(...apiRouters);
this.frontendSettings.publicApi.latestVersion = apiLatestVersion;
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/UserManagement/PermissionChecker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ export class PermissionChecker {

throw new NodeOperationError(nodeToFlag, 'Node has no access to credential', {
description: 'Please recreate the credential or ask its owner to share it with you.',
severity: 'warning',
});
}

Expand Down
3 changes: 2 additions & 1 deletion packages/cli/src/audit/risks/instance.risk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
import { getN8nPackageJson, inDevelopment } from '@/constants';
import type { WorkflowEntity } from '@db/entities/WorkflowEntity';
import type { Risk, n8n } from '@/audit/types';
import { isApiEnabled } from '@/PublicApi';

function getSecuritySettings() {
if (config.getEnv('deployment.type') === 'cloud') return null;
Expand All @@ -34,7 +35,7 @@ function getSecuritySettings() {
communityPackagesEnabled: config.getEnv('nodes.communityPackages.enabled'),
versionNotificationsEnabled: config.getEnv('versionNotifications.enabled'),
templatesEnabled: config.getEnv('templates.enabled'),
publicApiEnabled: !config.getEnv('publicApi.disabled'),
publicApiEnabled: isApiEnabled(),
userManagementEnabled,
};

Expand Down
4 changes: 2 additions & 2 deletions packages/cli/src/commands/BaseCommand.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { Command } from '@oclif/command';
import { ExitError } from '@oclif/errors';
import { Container } from 'typedi';
import type { INodeTypes } from 'n8n-workflow';
import { LoggerProxy, ErrorReporterProxy as ErrorReporter, sleep } from 'n8n-workflow';
import type { IUserSettings } from 'n8n-core';
import { BinaryDataManager, UserSettings } from 'n8n-core';
Expand Down Expand Up @@ -31,7 +30,7 @@ export abstract class BaseCommand extends Command {

protected loadNodesAndCredentials: LoadNodesAndCredentials;

protected nodeTypes: INodeTypes;
protected nodeTypes: NodeTypes;

protected userSettings: IUserSettings;

Expand All @@ -51,6 +50,7 @@ export abstract class BaseCommand extends Command {
this.loadNodesAndCredentials = Container.get(LoadNodesAndCredentials);
await this.loadNodesAndCredentials.init();
this.nodeTypes = Container.get(NodeTypes);
this.nodeTypes.init();
const credentialTypes = Container.get(CredentialTypes);
CredentialsOverwrites(credentialTypes);

Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ export const enum LICENSE_FEATURES {
ADVANCED_EXECUTION_FILTERS = 'feat:advancedExecutionFilters',
VARIABLES = 'feat:variables',
VERSION_CONTROL = 'feat:versionControl',
API_DISABLED = 'feat:apiDisabled',
}

export const enum LICENSE_QUOTAS {
Expand Down
18 changes: 11 additions & 7 deletions packages/cli/src/credentials/oauth2Credential.api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import type {
INodeCredentialsDetails,
ICredentialsEncrypted,
} from 'n8n-workflow';
import { LoggerProxy } from 'n8n-workflow';
import { LoggerProxy, jsonStringify } from 'n8n-workflow';
import { resolve as pathResolve } from 'path';

import * as Db from '@/Db';
Expand Down Expand Up @@ -173,8 +173,8 @@ oauth2CredentialController.get(
}),
);

const renderCallbackError = (res: express.Response, errorMessage: string) =>
res.render('oauth-error-callback', { error: { message: errorMessage } });
const renderCallbackError = (res: express.Response, message: string, reason?: string) =>
res.render('oauth-error-callback', { error: { message, reason } });

/**
* GET /oauth2-credential/callback
Expand All @@ -192,9 +192,8 @@ oauth2CredentialController.get(
if (!code || !stateEncoded) {
return renderCallbackError(
res,
`Insufficient parameters for OAuth2 callback. Received following query parameters: ${JSON.stringify(
req.query,
)}`,
'Insufficient parameters for OAuth2 callback.',
`Received following query parameters: ${JSON.stringify(req.query)}`,
);
}

Expand Down Expand Up @@ -326,7 +325,12 @@ oauth2CredentialController.get(

return res.sendFile(pathResolve(TEMPLATES_DIR, 'oauth-callback.html'));
} catch (error) {
return renderCallbackError(res, (error as Error).message);
return renderCallbackError(
res,
(error as Error).message,
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
'body' in error ? jsonStringify(error.body) : undefined,
);
}
},
);
14 changes: 9 additions & 5 deletions packages/cli/templates/oauth-error-callback.handlebars
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,21 @@
<title>n8n - OAuth Callback</title>
<style>
body { font-family: 'Open Sans', sans-serif; padding: 10px;}
pre.error { background: #f7f7f7; border: 1px solid #ddd; border-radius: 3px; padding: 10px; overflow: auto; overflow-wrap: break-word; white-space: pre-wrap; }
details.error { margin-bottom: 20px; }
pre.reason { background: #f7f7f7; border: 1px solid #ddd; border-radius: 3px; padding: 10px; overflow: auto; overflow-wrap: break-word; white-space: pre-wrap;}
</style>
</head>
<body>
{{#if error}}
<h4>Error:</h4>
<pre class='error'>{{error.message}}</pre>
<h4>Error: {{error.message}}</h4>
<details class='error'>
<summary>More details</summary>
{{#if error.reason}}<pre class="reason">{{error.reason}}</pre>{{/if}}
</details>
{{/if}}
Failed to connect. The window can be closed now.
Failed to connect. The window can be closed now.
<script>
(function messageParent() { window.opener.postMessage('error', '*'); })();
(function messageParent() { window.opener?.postMessage('error', '*'); })();
</script>
</body>
</html>
14 changes: 12 additions & 2 deletions packages/core/src/NodeExecuteFunctions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1395,6 +1395,7 @@ export async function httpRequestWithAuthentication(
throw new NodeOperationError(
node,
`Node "${node.name}" does not have any credentials of type "${credentialsType}" set!`,
{ severity: 'warning' },
);
}

Expand Down Expand Up @@ -1590,6 +1591,7 @@ export async function requestWithAuthentication(
throw new NodeOperationError(
node,
`Node "${node.name}" does not have any credentials of type "${credentialsType}" set!`,
{ severity: 'warning' },
);
}

Expand Down Expand Up @@ -1748,6 +1750,7 @@ export async function getCredentials(
throw new NodeOperationError(
node,
`Node type "${node.type}" does not have any credentials defined!`,
{ severity: 'warning' },
);
}

Expand All @@ -1758,6 +1761,7 @@ export async function getCredentials(
throw new NodeOperationError(
node,
`Node type "${node.type}" does not have any credentials of type "${type}" defined!`,
{ severity: 'warning' },
);
}

Expand All @@ -1781,10 +1785,16 @@ export async function getCredentials(
if (nodeCredentialDescription?.required === true) {
// Credentials are required so error
if (!node.credentials) {
throw new NodeOperationError(node, 'Node does not have any credentials set!');
throw new NodeOperationError(node, 'Node does not have any credentials set!', {
severity: 'warning',
});
}
if (!node.credentials[type]) {
throw new NodeOperationError(node, `Node does not have any credentials set for "${type}"!`);
throw new NodeOperationError(
node,
`Node does not have any credentials set for "${type}"!`,
{ severity: 'warning' },
);
}
} else {
// Credentials are not required
Expand Down
Loading

0 comments on commit 723bffc

Please sign in to comment.