Skip to content

Commit

Permalink
Create node-param-operation-option-description-wrong-for-get-many (i…
Browse files Browse the repository at this point in the history
…vov#119)

* Create `node-param-operation-option-description-wrong-for-get-many`

* Restore version
  • Loading branch information
ivov authored Sep 8, 2022
1 parent 945e87f commit ffe6e39
Show file tree
Hide file tree
Showing 9 changed files with 279 additions and 148 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ In the `community` ruleset, the five `*-still-default` rules allow you to define
| [node-param-multi-options-type-unsorted-items](docs/rules/node-param-multi-options-type-unsorted-items.md) | Items in a multi-options-type node parameter must be alphabetized by `name` if five or more than five. | Yes |
| [node-param-operation-option-action-miscased](docs/rules/node-param-operation-option-action-miscased.md) | The property `action` in an option in an Operation node parameter must be sentence-cased. | Yes |
| [node-param-operation-option-action-wrong-for-get-many](docs/rules/node-param-operation-option-action-wrong-for-get-many.md) | The property `action` in a Get Many option in an Operation node parameter must start with `Get many`. | Yes |
| [node-param-operation-option-description-wrong-for-get-many](docs/rules/node-param-operation-option-description-wrong-for-get-many.md) | The property `description` in a Get Many option in an Operation node parameter must mention `many` instead of `all`. | Yes |
| [node-param-operation-option-without-action](docs/rules/node-param-operation-option-without-action.md) | An option in an Operation node parameter must have an `action` property. The `action` property may or may not be identical to the `description` property. | Yes |
| [node-param-operation-without-no-data-expression](docs/rules/node-param-operation-without-no-data-expression.md) | `noDataExpression` in an Operation node parameter must be present and enabled. | Yes |
| [node-param-option-description-identical-to-name](docs/rules/node-param-option-description-identical-to-name.md) | `description` in option in options-type node parameter must not be identical to `name`. | Yes |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,30 +26,9 @@ const test = {
default: "getAll",
options: [
{
name: "Get All",
value: "getAll",
description: "Retrieve all entities",
action: "Get all entities",
},
],
};

const test = {
displayName: "Action",
name: "action",
type: "options",
noDataExpression: true,
displayOptions: {
show: {
resource: ["entity"],
},
},
default: "getAll",
options: [
{
name: "Get All",
name: "Get Many",
value: "getAll",
description: "Retrieve all entities",
description: "Retrieve many entities",
action: "Get all entities",
},
],
Expand All @@ -70,27 +49,6 @@ const test = {
},
},
default: "getAll",
options: [
{
name: "Get Many",
value: "getAll",
description: "Retrieve all entities",
action: "Get many entities",
},
],
};

const test = {
displayName: "Action",
name: "action",
type: "options",
noDataExpression: true,
displayOptions: {
show: {
resource: ["entity"],
},
},
default: "getAll",
options: [
{
name: "Get Many",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
[//]: # "File generated from a template. Do not edit this file directly."

# node-param-operation-option-description-wrong-for-get-many

The property `description` in a Get Many option in an Operation node parameter must mention `many` instead of `all`.

📋 This rule is part of the `plugin:n8n-nodes-base/nodes` config.

🔧 Run ESLint with `--fix` option to autofix the issue flagged by this rule.

## Examples

❌ Example of **incorrect** code:

```js
const test = {
displayName: "Operation",
name: "operation",
type: "options",
noDataExpression: true,
displayOptions: {
show: {
resource: ["entity"],
},
},
default: "getAll",
options: [
{
name: "Get Many",
value: "getAll",
description: "Retrieve all entities",
action: "Get many entities",
},
],
};
```

✅ Example of **correct** code:

```js
const test = {
displayName: "Operation",
name: "operation",
type: "options",
noDataExpression: true,
displayOptions: {
show: {
resource: ["entity"],
},
},
default: "getAll",
options: [
{
name: "Get Many",
value: "getAll",
description: "Retrieve many entities",
action: "Get many entities",
},
],
};
```

## Links

- [Rule source](../../lib/rules/node-param-operation-option-description-wrong-for-get-many.ts)
- [Test source](../../tests/node-param-operation-option-description-wrong-for-get-many.test.ts)
12 changes: 12 additions & 0 deletions lib/ast/getters/nodeParameter.getters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,18 @@ export function getNumberProperty(
};
}

export function getGetAllOption(nodeParam: TSESTree.ObjectExpression) {
const found = nodeParam.properties.find(id.nodeParam.isGetAllOptionProperty);

if (!found) return null;

return {
ast: found,
value: '', // TODO
};
}


export function getTypeOptions(nodeParam: TSESTree.ObjectExpression) {
const found = nodeParam.properties.find(id.nodeParam.isTypeOptions);

Expand Down
14 changes: 14 additions & 0 deletions lib/ast/identifiers/nodeParameter.identifiers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -339,3 +339,17 @@ export function isShowSetting(
): property is ArrayProperty {
return isArrayPropertyNamed(showSettingKey, property);
}

export function isGetAllOptionProperty(
property: TSESTree.ObjectLiteralElement
) {
return (
property.type === AST_NODE_TYPES.Property &&
property.computed === false &&
property.key.type === AST_NODE_TYPES.Identifier &&
property.key.name === "value" &&
property.value.type === AST_NODE_TYPES.Literal &&
typeof property.value.value === "string" &&
property.value.value === "getAll"
);
}
38 changes: 14 additions & 24 deletions lib/rules/node-param-operation-option-action-wrong-for-get-many.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@ export default utils.createRule({
create(context) {
return {
ObjectExpression(node) {
if (!id.isNodeParameter(node)) return;

if (!id.nodeParam.isOperation(node) && !id.nodeParam.isAction(node)) {
return;
}
Expand All @@ -35,41 +33,33 @@ export default utils.createRule({
// skip `options: [...].sort()`, see EditImage.node.ts
if (!Array.isArray(options.ast.value.elements)) return;

const getAllOption = options.ast.value.elements.find((option) => {
return option.properties.find((property) => {
return (
property.type === AST_NODE_TYPES.Property &&
property.computed === false &&
property.key.type === AST_NODE_TYPES.Identifier &&
property.key.name === "value" &&
property.value.type === AST_NODE_TYPES.Literal &&
typeof property.value.value === "string" &&
property.value.value === "getAll"
);
});
});
const getAllOption = options.ast.value.elements.find(
getters.nodeParam.getGetAllOption
);

if (!getAllOption) return;

const action = getAllOption.properties.find(isActionProperty);
const actionNode = getAllOption.properties.find(isActionProperty);

if (!actionNode) return;

if (!action) return;
const { value: action } = actionNode.value;

const actionSentence = action.value.value;
const DEPRECATED_START_OF_ACTION = "Get all ";

if (actionSentence.startsWith("Get all")) {
const [_, resourceName] = actionSentence.split("Get all");
if (action.startsWith(DEPRECATED_START_OF_ACTION)) {
const [_, resourceName] = action.split(DEPRECATED_START_OF_ACTION);

const fixed = utils.keyValue(
"action",
actionSentence.replace("Get all", "Get many")
action.replace(DEPRECATED_START_OF_ACTION, "Get many ")
);

context.report({
messageId: "changeToGetMany",
node: action,
fix: (fixer) => fixer.replaceText(action, fixed),
data: { resourceName: resourceName.trim() }
node: actionNode,
fix: (fixer) => fixer.replaceText(actionNode, fixed),
data: { resourceName },
});
}
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { utils } from "../ast/utils";
import { id } from "../ast/identifiers";
import { getters } from "../ast/getters";
import { AST_NODE_TYPES, TSESTree } from "@typescript-eslint/utils";

export default utils.createRule({
name: utils.getRuleName(module),
meta: {
type: "problem",
docs: {
description:
"The property `description` in a Get Many option in an Operation node parameter must mention `many` instead of `all`.",
recommended: "error",
},
fixable: "code",
schema: [],
messages: {
changeToGetMany: "Change to '{{ newDescription }}' [autofixable]",
},
},
defaultOptions: [],
create(context) {
return {
ObjectExpression(node) {
if (!id.nodeParam.isOperation(node)) return;

const options = getters.nodeParam.getOptions(node);

if (!options) return;

// skip `options: [...].sort()`, see EditImage.node.ts
if (!Array.isArray(options.ast.value.elements)) return;

const getAllOption = options.ast.value.elements.find(
getters.nodeParam.getGetAllOption
);

if (!getAllOption) return;

const descriptionNode =
getAllOption.properties.find(isOptionDescription);

if (!descriptionNode) return;

const { value: description } = descriptionNode.value;

if (description.includes(" all ")) {
const [start, end] = description.split(" all ");

const newDescription = [start, "many", end].join(" ");

const fixed = utils.keyValue("description", newDescription);

context.report({
messageId: "changeToGetMany",
node: descriptionNode,
fix: (fixer) => fixer.replaceText(descriptionNode, fixed),
data: { newDescription },
});
}
},
};
},
});

function isOptionDescription(
property: TSESTree.ObjectLiteralElement
): property is TSESTree.Property & { value: { value: string } } {
return (
property.type === AST_NODE_TYPES.Property &&
property.computed === false &&
property.key.type === AST_NODE_TYPES.Identifier &&
property.key.name === "description" &&
property.value.type === AST_NODE_TYPES.Literal &&
typeof property.value.value === "string"
);
}
Loading

0 comments on commit ffe6e39

Please sign in to comment.