diff --git a/CHANGELOG.md b/CHANGELOG.md index ccd92e5..9af5d96 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- [#18](https://github.com/green-code-initiative/ecoCode-javascript/pull/18) add rule `@ecocode/limit-db-query-results` - [#14](https://github.com/green-code-initiative/ecoCode-javascript/pull/14) Create SonarQube plugin - [#12](https://github.com/green-code-initiative/ecoCode-javascript/issues/12) Pack ESLint plugin into SonarQube plugin - [#16](https://github.com/green-code-initiative/ecoCode-javascript/pull/16) Use centralized rules specifications diff --git a/eslint-plugin/README.md b/eslint-plugin/README.md index c6188f2..c8dd434 100644 --- a/eslint-plugin/README.md +++ b/eslint-plugin/README.md @@ -59,6 +59,7 @@ Add `@ecocode` to the `plugins` section of your `.eslintrc`, followed by rules c | Name | Description | ⚠️ | | :------------------------------------------------------------------------------------- | :--------------------------------------------------------- | :- | | [avoid-high-accuracy-geolocation](docs/rules/avoid-high-accuracy-geolocation.md) | Avoid using high accuracy geolocation in web applications. | ✅ | +| [limit-db-query-results](docs/rules/limit-db-query-results.md) | Should limit the number of returns for a SQL query | ✅ | | [no-import-all-from-library](docs/rules/no-import-all-from-library.md) | Should not import all from library | ✅ | | [no-multiple-access-dom-element](docs/rules/no-multiple-access-dom-element.md) | Disallow multiple access of same DOM element. | ✅ | | [no-multiple-style-changes](docs/rules/no-multiple-style-changes.md) | Disallow multiple style changes at once. | ✅ | diff --git a/eslint-plugin/docs/rules/limit-db-query-results.md b/eslint-plugin/docs/rules/limit-db-query-results.md new file mode 100644 index 0000000..4626876 --- /dev/null +++ b/eslint-plugin/docs/rules/limit-db-query-results.md @@ -0,0 +1,23 @@ +# Should limit the number of returns for a SQL query (`@ecocode/limit-db-query-results`) + +⚠️ This rule _warns_ in the ✅ `recommended` config. + + + +## Rule Details + +This rule aims at reducing CPU consumption by limiting the number of returns for a single SQL query. + +## Examples + +Examples of **non compliant** code for this rule: + +```js +const query = "SELECT * FROM clients"; +``` + +Examples of **compliant** code for this rule: + +```js +const query = "SELECT columns FROM table_name FETCH FIRST number ROWS ONLY"; +``` diff --git a/eslint-plugin/lib/rules/limit-db-query-results.js b/eslint-plugin/lib/rules/limit-db-query-results.js new file mode 100644 index 0000000..9a7b2e7 --- /dev/null +++ b/eslint-plugin/lib/rules/limit-db-query-results.js @@ -0,0 +1,55 @@ +/** + * Copyright (C) 2023 Green Code Initiative + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +"use strict"; + +/** @type {import('eslint').Rule.RuleModule} */ +module.exports = { + meta: { + type: "suggestion", + docs: { + description: "Should limit the number of returns for a SQL query", + category: "eco-design", + recommended: "warn", + }, + messages: { + LimitTheNumberOfReturns: + "Try and limit the number of data returned for a single query (by using the LIMIT keyword for example)", + }, + schema: [], + }, + create(context) { + //list of limiting clauses to check against + const limitingClauses = ["LIMIT", "TOP", "ROW_NUMBER", "FETCH FIRST"]; + return { + Literal: function (node) { + if (typeof node.value == "string") { + const query = node.value.toUpperCase(); + if ( + query.includes("SELECT") && + query.includes("FROM") && + !limitingClauses.some((clause) => query.includes(clause)) + ) { + context.report({ + node: node, + messageId: "LimitTheNumberOfReturns", + }); + } + } + }, + }; + }, +}; diff --git a/eslint-plugin/tests/lib/rules/limit-db-query-results.js b/eslint-plugin/tests/lib/rules/limit-db-query-results.js new file mode 100644 index 0000000..6658c42 --- /dev/null +++ b/eslint-plugin/tests/lib/rules/limit-db-query-results.js @@ -0,0 +1,62 @@ +/** + * Copyright (C) 2023 Green Code Initiative + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +"use strict"; +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +const rule = require("../../../lib/rules/limit-db-query-results"), + RuleTester = require("eslint").RuleTester; + +//------------------------------------------------------------------------------ +// Tests +//------------------------------------------------------------------------------ + +const ruleTester = new RuleTester({ + parserOptions: { + ecmaVersion: 6, + sourceType: "module", + }, +}); +const expectedError = { + messageId: "LimitTheNumberOfReturns", + type: "Literal", +}; + +ruleTester.run("limit-db-query-results", rule, { + valid: [ + ` + const query = "SELECT * FROM customers LIMIT 10;"; + `, + ` + const query = "SELECT TOP 5 * FROM products;"; + `, + ` + const query = "SELECT * FROM orders FETCH FIRST 20 ROWS ONLY;"; + `, + ` + const query = "WITH numbered_customers AS (SELECT *, ROW_NUMBER() OVER (ORDER BY customer_id) AS row_num FROM customers) SELECT * FROM numbered_customers WHERE row_num <= 50;"; + `, + ], + + invalid: [ + { + code: `const query = "SELECT * FROM bikes;";`, + errors: [expectedError], + }, + ], +});