mirror of
https://github.com/n8n-io/n8n.git
synced 2026-10-17 13:39:44 +02:00
fix: Report resource-operation violations as errors (#40830)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
This commit is contained in:
parent
6b77613ee8
commit
a1800105ef
|
|
@ -83,7 +83,7 @@ export default [
|
|||
| [require-node-api-error](docs/rules/require-node-api-error.md) | Require NodeApiError or NodeOperationError for error wrapping in catch blocks. Raw errors lose HTTP context in the n8n UI. | ✅ ☑️ | | | | |
|
||||
| [require-node-description-fields](docs/rules/require-node-description-fields.md) | Node class description must define all required fields: icon, subtitle | ✅ ☑️ | | | | |
|
||||
| [require-version](docs/rules/require-version.md) | Require a valid "version" field in community node package.json | ✅ ☑️ | | | | |
|
||||
| [resource-operation-pattern](docs/rules/resource-operation-pattern.md) | Enforce proper resource/operation pattern for better UX in n8n nodes | | ✅ ☑️ | | | |
|
||||
| [resource-operation-pattern](docs/rules/resource-operation-pattern.md) | Enforce proper resource/operation pattern for better UX in n8n nodes | ✅ ☑️ | | | | |
|
||||
| [trigger-node-conventions](docs/rules/trigger-node-conventions.md) | Trigger nodes (class name ends with `Trigger`) must label themselves consistently as triggers | ✅ ☑️ | | | | |
|
||||
| [valid-author](docs/rules/valid-author.md) | Require a non-empty author name and email in package.json | ✅ ☑️ | | | | |
|
||||
| [valid-credential-references](docs/rules/valid-credential-references.md) | Ensure credentials referenced in node descriptions exist as credential classes in the package | ✅ ☑️ | | | 💡 | |
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
# Enforce proper resource/operation pattern for better UX in n8n nodes (`@n8n/community-nodes/resource-operation-pattern`)
|
||||
|
||||
⚠️ This rule _warns_ in the following configs: ✅ `recommended`, ☑️ `recommendedWithoutN8nCloudSupport`.
|
||||
💼 This rule is enabled in the following configs: ✅ `recommended`, ☑️ `recommendedWithoutN8nCloudSupport`.
|
||||
|
||||
<!-- end auto-generated rule header -->
|
||||
|
||||
## Rule Details
|
||||
|
||||
Warns when a node has more than 5 operations without organizing them into resources. The resource/operation pattern improves user experience by grouping related operations together, making complex nodes easier to navigate.
|
||||
Reports an error when a node has more than 5 operations without resources. Use resources to group related operations. This pattern makes complex nodes easier to navigate.
|
||||
|
||||
When you have many operations, users benefit from having them organized into logical resource groups (e.g., "User", "Project", "File") rather than seeing a long flat list of operations.
|
||||
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ const configs = {
|
|||
'@n8n/community-nodes/no-unsafe-connection-type-cast': 'error',
|
||||
'@n8n/community-nodes/icon-validation': 'error',
|
||||
'@n8n/community-nodes/icon-prefer-themed-variants': 'warn',
|
||||
'@n8n/community-nodes/resource-operation-pattern': 'warn',
|
||||
'@n8n/community-nodes/resource-operation-pattern': 'error',
|
||||
'@n8n/community-nodes/trigger-node-conventions': 'error',
|
||||
'@n8n/community-nodes/credential-documentation-url': 'error',
|
||||
'@n8n/community-nodes/cred-class-field-icon-missing': 'error',
|
||||
|
|
@ -95,7 +95,7 @@ const configs = {
|
|||
'@n8n/community-nodes/icon-validation': 'error',
|
||||
'@n8n/community-nodes/icon-prefer-themed-variants': 'warn',
|
||||
'@n8n/community-nodes/credential-documentation-url': 'error',
|
||||
'@n8n/community-nodes/resource-operation-pattern': 'warn',
|
||||
'@n8n/community-nodes/resource-operation-pattern': 'error',
|
||||
'@n8n/community-nodes/trigger-node-conventions': 'error',
|
||||
'@n8n/community-nodes/cred-class-field-icon-missing': 'error',
|
||||
'@n8n/community-nodes/cred-class-oauth2-naming': 'error',
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
import { RuleTester } from '@typescript-eslint/rule-tester';
|
||||
import { Linter } from 'eslint';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { configs } from '../plugin.js';
|
||||
import { ResourceOperationPatternRule } from './resource-operation-pattern.js';
|
||||
|
||||
const ruleTester = new RuleTester();
|
||||
|
|
@ -215,3 +218,61 @@ ruleTester.run('resource-operation-pattern', ResourceOperationPatternRule, {
|
|||
},
|
||||
],
|
||||
});
|
||||
|
||||
const resourceOperationRuleId = '@n8n/community-nodes/resource-operation-pattern';
|
||||
|
||||
function nodeWithSixOperations(hasResource: boolean): string {
|
||||
return `
|
||||
class TestNode extends Node {
|
||||
description = {
|
||||
properties: [
|
||||
${hasResource ? "{ name: 'resource', type: 'options', options: [{ name: 'User', value: 'user' }] }," : ''}
|
||||
{
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
options: [
|
||||
{ name: 'Get', value: 'get' },
|
||||
{ name: 'Create', value: 'create' },
|
||||
{ name: 'Update', value: 'update' },
|
||||
{ name: 'Delete', value: 'delete' },
|
||||
{ name: 'List', value: 'list' },
|
||||
{ name: 'Search', value: 'search' },
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
describe.each([
|
||||
['recommended', configs.recommended],
|
||||
['recommendedWithoutN8nCloudSupport', configs.recommendedWithoutN8nCloudSupport],
|
||||
] as const)('%s config', (_name, config) => {
|
||||
const linter = new Linter();
|
||||
const lint = (code: string) =>
|
||||
linter.verify(
|
||||
code,
|
||||
{
|
||||
files: ['**/*.node.ts'],
|
||||
plugins: config.plugins,
|
||||
rules: { [resourceOperationRuleId]: config.rules[resourceOperationRuleId] },
|
||||
},
|
||||
{ filename: 'TestNode.node.ts' },
|
||||
);
|
||||
|
||||
// CE-3284: The resource finding must fail lint even when it is the only finding.
|
||||
it('reports missing resources as the only error', () => {
|
||||
expect(lint(nodeWithSixOperations(false))).toEqual([
|
||||
expect.objectContaining({
|
||||
ruleId: resourceOperationRuleId,
|
||||
messageId: 'tooManyOperationsWithoutResources',
|
||||
severity: 2,
|
||||
}),
|
||||
]);
|
||||
});
|
||||
|
||||
it('accepts operations organized by resource', () => {
|
||||
expect(lint(nodeWithSixOperations(true))).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user