diff --git a/CHANGELOG.md b/CHANGELOG.md
index d34f6869..081a0b9b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,15 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [1.3.0] - 2026-01-05
+
+### Changed
+ - OAR104 - ResourcesByPostVerbCheck
+
+### Fixed
+ - OAR019 - SelectParameterCheck
+ - OAR020 - ExpandParameterCheck
+
## [1.2.5] - 2025-12-31
### Fixed
diff --git a/pom.xml b/pom.xml
index e078c8c0..a89ab7ec 100644
--- a/pom.xml
+++ b/pom.xml
@@ -3,7 +3,7 @@
4.0.0
org.apiaddicts.apitools.dosonarapi
sonaropenapi-rules-community
- 1.2.5
+ 1.3.0
sonar-plugin
SonarQube OpenAPI Community Rules
diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/operations/OAR104ResourcesByPostVerbCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/operations/OAR104ResourcesByPostVerbCheck.java
index 449f2f7a..8c48753a 100644
--- a/src/main/java/apiaddicts/sonar/openapi/checks/operations/OAR104ResourcesByPostVerbCheck.java
+++ b/src/main/java/apiaddicts/sonar/openapi/checks/operations/OAR104ResourcesByPostVerbCheck.java
@@ -20,7 +20,7 @@ public class OAR104ResourcesByPostVerbCheck extends BaseCheck {
public static final String KEY = "OAR104";
private static final String MESSAGE = "OAR104.error";
- private static final String RESERVED_WORDS = "me";
+ private static final String RESERVED_WORDS = "me,search";
@RuleProperty(
key = "words-to-exclude",
@@ -74,22 +74,22 @@ private boolean isCorrect(String path) {
.filter(p -> !p.trim().isEmpty())
.toArray(String[]::new);
if (parts.length == 0) return true;
-
+
for (int i = 0; i < parts.length - 1; i++) {
if (!isVariable(parts[i]) && !isSpecialVariable(parts[i]) && !isVariable(parts[i + 1]) && !isSpecialVariable(parts[i + 1])) {
return false;
}
}
-
+
return true;
}
-
+
private boolean isVariable(String part) {
return part.startsWith("{") && part.endsWith("}");
}
private boolean isSpecialVariable(String part) {
- return "me".equalsIgnoreCase(part);
+ return reservedWords.contains(part.toLowerCase());
}
private String formatMessage(String path) {
diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/parameters/AbstractQueryParameterCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/parameters/AbstractQueryParameterCheck.java
new file mode 100644
index 00000000..92fdda27
--- /dev/null
+++ b/src/main/java/apiaddicts/sonar/openapi/checks/parameters/AbstractQueryParameterCheck.java
@@ -0,0 +1,185 @@
+package apiaddicts.sonar.openapi.checks.parameters;
+
+import apiaddicts.sonar.openapi.checks.BaseCheck;
+import com.google.common.collect.ImmutableSet;
+import com.sonar.sslr.api.AstNode;
+import com.sonar.sslr.api.AstNodeType;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.stream.Collectors;
+import org.apiaddicts.apitools.dosonarapi.api.v2.OpenApi2Grammar;
+import org.apiaddicts.apitools.dosonarapi.api.v3.OpenApi3Grammar;
+import org.apiaddicts.apitools.dosonarapi.api.v31.OpenApi31Grammar;
+import org.apiaddicts.apitools.dosonarapi.sslr.yaml.grammar.JsonNode;
+import org.sonar.check.RuleProperty;
+
+public abstract class AbstractQueryParameterCheck extends BaseCheck {
+
+ protected static final String DEFAULT_PATH = "/examples";
+ protected static final String PATH_STRATEGY = "/include";
+
+ protected final String ruleKey;
+ protected final String messageKey;
+ protected final String parameterName;
+ protected final boolean applyToParameterizedPaths;
+
+ protected Set paths;
+ protected JsonNode rootNode;
+
+ @RuleProperty(
+ key = "paths",
+ description = "List of explicit paths to include/exclude from this rule separated by comma",
+ defaultValue = DEFAULT_PATH
+ )
+ protected String pathsStr = DEFAULT_PATH;
+
+ @RuleProperty(
+ key = "pathValidationStrategy",
+ description = "Path validation strategy (include/exclude)",
+ defaultValue = PATH_STRATEGY
+ )
+ protected String pathCheckStrategy = PATH_STRATEGY;
+
+ protected AbstractQueryParameterCheck(
+ String ruleKey,
+ String messageKey,
+ String parameterName,
+ boolean applyToParameterizedPaths
+ ) {
+ this.ruleKey = ruleKey;
+ this.messageKey = messageKey;
+ this.parameterName = parameterName;
+ this.applyToParameterizedPaths = applyToParameterizedPaths;
+ }
+
+ @Override
+ public Set subscribedKinds() {
+ return ImmutableSet.of(OpenApi2Grammar.OPERATION, OpenApi3Grammar.OPERATION, OpenApi31Grammar.OPERATION);
+ }
+
+ @Override
+ protected void visitFile(JsonNode root) {
+ this.rootNode = root;
+ paths = parsePaths(pathsStr);
+ super.visitFile(root);
+ }
+
+ @Override
+ public void visitNode(JsonNode node) {
+ if (!"get".equals(node.key().getTokenValue())) {
+ return;
+ }
+
+ String path = getPath(node);
+
+ if (!applyToParameterizedPaths && endsWithPathParam(path)) {
+ return;
+ }
+
+ boolean hasParameter = hasParameterInNode(node);
+
+ if (shouldIncludePath(path) && !hasParameter) {
+ addIssue(
+ ruleKey,
+ translate(messageKey, parameterName),
+ node.key()
+ );
+ }
+ }
+
+ protected boolean hasParameterInNode(JsonNode node) {
+ JsonNode parametersNode = node.get("parameters");
+ if (parametersNode != null) {
+
+ for (JsonNode parameterNode : parametersNode.elements()) {
+ if (isRefParameter(parameterNode) && hasNamedRefParameter(parameterNode)) {
+ return true;
+ }
+ if (hasDirectParameter(parameterNode)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ protected boolean isRefParameter(JsonNode parameterNode) {
+ return parameterNode.get("$ref") != null;
+ }
+
+ protected boolean hasNamedRefParameter(JsonNode parameterNode) {
+ String refValue = parameterNode.get("$ref").getTokenValue();
+ JsonNode refParameterNode = resolveReference(refValue, rootNode);
+ if (refParameterNode != null) {
+ JsonNode nameNode = refParameterNode.get("name");
+ JsonNode inNode = refParameterNode.get("in");
+ return inNode != null && "query".equals(inNode.getTokenValue()) && nameNode != null && parameterName.equals(nameNode.getTokenValue());
+ }
+ return false;
+ }
+
+ protected boolean hasDirectParameter(JsonNode parameterNode) {
+ JsonNode nameNode = parameterNode.get("name");
+ JsonNode inNode = parameterNode.get("in");
+ return inNode != null && "query".equals(inNode.getTokenValue()) && nameNode != null && parameterName.equals(nameNode.getTokenValue());
+ }
+
+ protected String getPath(JsonNode node) {
+ StringBuilder pathBuilder = new StringBuilder();
+ AstNode pathNode = node.getFirstAncestor(OpenApi2Grammar.PATH, OpenApi3Grammar.PATH, OpenApi31Grammar.PATH);
+ if (pathNode != null) {
+ while (pathNode.getType() != OpenApi2Grammar.PATH && pathNode.getType() != OpenApi3Grammar.PATH && pathNode.getType() != OpenApi31Grammar.PATH) {
+ pathNode = pathNode.getParent();
+ }
+ pathBuilder.append(((JsonNode) pathNode).key().getTokenValue());
+ }
+ return pathBuilder.toString();
+ }
+
+ protected boolean shouldIncludePath(String path) {
+ if (pathCheckStrategy.equals("/exclude")) {
+ return !paths.contains(path);
+ } else if (pathCheckStrategy.equals(PATH_STRATEGY)) {
+ return paths.contains(path);
+ }
+ return false;
+ }
+
+ protected boolean endsWithPathParam(String path) {
+ String[] segments = path.split("/");
+ if (segments.length == 0) return false;
+
+ String last = segments[segments.length - 1].trim();
+ return last.matches("^\\{[^}]+\\}$");
+ }
+
+ protected Set parsePaths(String pathsStr) {
+ if (!pathsStr.trim().isEmpty()) {
+ return Arrays.stream(pathsStr.split(","))
+ .map(String::trim)
+ .collect(Collectors.toSet());
+ } else {
+ return new HashSet<>();
+ }
+ }
+
+ protected JsonNode resolveReference(String refValue, JsonNode root) {
+ if (refValue == null || !refValue.startsWith("#/")) {
+ return null;
+ }
+
+ String pathToReference = refValue.substring(2);
+ String[] pathParts = pathToReference.split("/");
+
+ JsonNode currentNode = root;
+ for (String part : pathParts) {
+ if (currentNode == null) {
+ return null;
+ }
+ currentNode = currentNode.get(part);
+ }
+
+ return currentNode;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR019SelectParameterCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR019SelectParameterCheck.java
index 47cacecc..262bc099 100644
--- a/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR019SelectParameterCheck.java
+++ b/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR019SelectParameterCheck.java
@@ -1,172 +1,21 @@
package apiaddicts.sonar.openapi.checks.parameters;
-import org.apiaddicts.apitools.dosonarapi.api.v2.OpenApi2Grammar;
-import org.apiaddicts.apitools.dosonarapi.api.v3.OpenApi3Grammar;
-import org.apiaddicts.apitools.dosonarapi.api.v31.OpenApi31Grammar;
-import org.apiaddicts.apitools.dosonarapi.sslr.yaml.grammar.JsonNode;
import org.sonar.check.Rule;
-import org.sonar.check.RuleProperty;
-import apiaddicts.sonar.openapi.checks.BaseCheck;
-
-import com.google.common.collect.ImmutableSet;
-import com.sonar.sslr.api.AstNode;
-import com.sonar.sslr.api.AstNodeType;
-
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.Set;
-import java.util.stream.Collectors;
@Rule(key = OAR019SelectParameterCheck.KEY)
-public class OAR019SelectParameterCheck extends BaseCheck {
+public class OAR019SelectParameterCheck extends AbstractQueryParameterCheck {
public static final String KEY = "OAR019";
private static final String MESSAGE = "OAR019.error";
- private static final String DEFAULT_PATH = "/examples";
- private static final String PATH_STRATEGY = "/include";
private static final String PARAM_NAME = "$select";
- @RuleProperty(
- key = "paths",
- description = "List of explicit paths to include/exclude from this rule separated by comma",
- defaultValue = DEFAULT_PATH
- )
- private String pathsStr = DEFAULT_PATH;
-
- @RuleProperty(
- key = "pathValidationStrategy",
- description = "Path validation strategy (include/exclude)",
- defaultValue = PATH_STRATEGY
- )
- private String pathCheckStrategy = PATH_STRATEGY;
-
- @RuleProperty(
- key = "parameterName",
- description = "Name of the parameter to be checked",
- defaultValue = PARAM_NAME
- )
- private String parameterName = PARAM_NAME;
-
- private Set paths;
- private JsonNode rootNode;
-
- @Override
- public Set subscribedKinds() {
- return ImmutableSet.of(OpenApi2Grammar.OPERATION, OpenApi3Grammar.OPERATION, OpenApi31Grammar.OPERATION);
- }
-
- @Override
- protected void visitFile(JsonNode root) {
- this.rootNode = root;
- paths = parsePaths(pathsStr);
- super.visitFile(root);
+ public OAR019SelectParameterCheck() {
+ super(
+ KEY,
+ MESSAGE,
+ PARAM_NAME,
+ false
+ );
}
- @Override
- public void visitNode(JsonNode node) {
- if ("get".equals(node.key().getTokenValue())) {
-
- String path = getPath(node);
-
- boolean hasParameter = hasParameterInNode(node);
-
- if (shouldIncludePath(path) && !hasParameter) {
- addIssue(KEY, translate(MESSAGE, PARAM_NAME), node.key());
- }
- }
- }
-
- private boolean hasParameterInNode(JsonNode node) {
- JsonNode parametersNode = node.get("parameters");
- if (parametersNode != null) {
-
- for (JsonNode parameterNode : parametersNode.elements()) {
- if (isRefParameter(parameterNode) && hasNamedRefParameter(parameterNode)) {
- return true;
- } else if (hasDirectParameter(parameterNode)) {
- return true;
- }
- }
- }
- return false;
- }
-
- private boolean isRefParameter(JsonNode parameterNode) {
- JsonNode refNode = parameterNode.get("$ref");
- if (refNode != null) {
- return true;
- }
- return false;
- }
-
- private boolean hasNamedRefParameter(JsonNode parameterNode) {
- String refValue = parameterNode.get("$ref").getTokenValue();
- JsonNode refParameterNode = resolveReference(refValue, rootNode);
- if (refParameterNode != null) {
- JsonNode nameNode = refParameterNode.get("name");
- JsonNode inNode = refParameterNode.get("in");
- return inNode != null && "query".equals(inNode.getTokenValue()) && nameNode != null && parameterName.equals(nameNode.getTokenValue());
- }
- return false;
- }
-
- private boolean hasDirectParameter(JsonNode parameterNode) {
- JsonNode nameNode = parameterNode.get("name");
- JsonNode inNode = parameterNode.get("in");
- return inNode != null && "query".equals(inNode.getTokenValue()) && nameNode != null && parameterName.equals(nameNode.getTokenValue());
- }
-
- private String getPath(JsonNode node) {
- StringBuilder pathBuilder = new StringBuilder();
- AstNode pathNode = node.getFirstAncestor(OpenApi2Grammar.PATH, OpenApi3Grammar.PATH, OpenApi31Grammar.PATH);
- if (pathNode != null) {
- while (pathNode.getType() != OpenApi2Grammar.PATH && pathNode.getType() != OpenApi3Grammar.PATH && pathNode.getType() != OpenApi31Grammar.PATH) {
- pathNode = pathNode.getParent();
- }
- pathBuilder.append(((JsonNode) pathNode).key().getTokenValue());
- }
- return pathBuilder.toString();
- }
-
- private boolean shouldIncludePath(String path) {
- if (pathCheckStrategy.equals("/exclude")) {
- return !paths.contains(path);
- } else if (pathCheckStrategy.equals("/include")) {
- return paths.contains(path);
- }
- return false;
- }
-
- private Set parsePaths(String pathsStr) {
- if (!pathsStr.trim().isEmpty()) {
- return Arrays.stream(pathsStr.split(","))
- .map(String::trim)
- .collect(Collectors.toSet());
- } else {
- return new HashSet<>();
- }
- }
-
- private JsonNode resolveReference(String refValue, JsonNode root) {
- if (refValue == null || !refValue.startsWith("#/")) {
- return null;
- }
-
- String pathToReference = refValue.substring(2);
- String[] pathParts = pathToReference.split("/");
-
- JsonNode currentNode = root;
- for (String part : pathParts) {
- if (currentNode == null) {
- return null;
- }
- currentNode = currentNode.get(part);
- }
-
- if (currentNode == null) {
- } else {
- }
-
- return currentNode;
- }
}
\ No newline at end of file
diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR020ExpandParameterCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR020ExpandParameterCheck.java
index 08cafe2f..355ff4d4 100644
--- a/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR020ExpandParameterCheck.java
+++ b/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR020ExpandParameterCheck.java
@@ -1,173 +1,21 @@
package apiaddicts.sonar.openapi.checks.parameters;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-import org.apiaddicts.apitools.dosonarapi.api.v2.OpenApi2Grammar;
-import org.apiaddicts.apitools.dosonarapi.api.v3.OpenApi3Grammar;
-import org.apiaddicts.apitools.dosonarapi.api.v31.OpenApi31Grammar;
-import org.apiaddicts.apitools.dosonarapi.sslr.yaml.grammar.JsonNode;
import org.sonar.check.Rule;
-import org.sonar.check.RuleProperty;
-import apiaddicts.sonar.openapi.checks.BaseCheck;
-
-import com.google.common.collect.ImmutableSet;
-import com.sonar.sslr.api.AstNode;
-import com.sonar.sslr.api.AstNodeType;
-
-import java.util.Arrays;
-import java.util.HashSet;
@Rule(key = OAR020ExpandParameterCheck.KEY)
-public class OAR020ExpandParameterCheck extends BaseCheck {
+public class OAR020ExpandParameterCheck extends AbstractQueryParameterCheck {
public static final String KEY = "OAR020";
private static final String MESSAGE = "OAR020.error";
- private static final String DEFAULT_PATH = "/examples";
- private static final String PATH_STRATEGY = "/include";
private static final String PARAM_NAME = "$expand";
- @RuleProperty(
- key = "paths",
- description = "List of explicit paths to include/exclude from this rule separated by comma",
- defaultValue = DEFAULT_PATH
- )
- private String pathsStr = DEFAULT_PATH;
-
- @RuleProperty(
- key = "pathValidationStrategy",
- description = "Path validation strategy (include/exclude)",
- defaultValue = PATH_STRATEGY
- )
- private String pathCheckStrategy = PATH_STRATEGY;
-
- @RuleProperty(
- key = "parameterName",
- description = "Name of the parameter to be checked",
- defaultValue = PARAM_NAME
- )
- private String parameterName = PARAM_NAME;
-
- private Set paths;
- private JsonNode rootNode;
-
- @Override
- public Set subscribedKinds() {
- return ImmutableSet.of(OpenApi2Grammar.OPERATION, OpenApi3Grammar.OPERATION, OpenApi31Grammar.OPERATION);
- }
-
- @Override
- protected void visitFile(JsonNode root) {
- this.rootNode = root;
- paths = parsePaths(pathsStr);
- super.visitFile(root);
- }
-
- @Override
- public void visitNode(JsonNode node) {
- if ("get".equals(node.key().getTokenValue())) {
-
- String path = getPath(node);
-
- boolean hasParameter = hasParameterInNode(node);
-
- if (shouldIncludePath(path) && !hasParameter) {
- addIssue(KEY, translate(MESSAGE, PARAM_NAME), node.key());
- }
- }
- }
-
- private boolean hasParameterInNode(JsonNode node) {
- JsonNode parametersNode = node.get("parameters");
- if (parametersNode != null) {
-
- for (JsonNode parameterNode : parametersNode.elements()) {
- if (isRefParameter(parameterNode) && hasNamedRefParameter(parameterNode)) {
- return true;
- } else if (hasDirectParameter(parameterNode)) {
- return true;
- }
- }
- }
- return false;
- }
-
- private boolean isRefParameter(JsonNode parameterNode) {
- JsonNode refNode = parameterNode.get("$ref");
- if (refNode != null) {
- return true;
- }
- return false;
- }
-
- private boolean hasNamedRefParameter(JsonNode parameterNode) {
- String refValue = parameterNode.get("$ref").getTokenValue();
- JsonNode refParameterNode = resolveReference(refValue, rootNode);
- if (refParameterNode != null) {
- JsonNode nameNode = refParameterNode.get("name");
- JsonNode inNode = refParameterNode.get("in");
- return inNode != null && "query".equals(inNode.getTokenValue()) && nameNode != null && parameterName.equals(nameNode.getTokenValue());
- }
- return false;
- }
-
- private boolean hasDirectParameter(JsonNode parameterNode) {
- JsonNode nameNode = parameterNode.get("name");
- JsonNode inNode = parameterNode.get("in");
- return inNode != null && "query".equals(inNode.getTokenValue()) && nameNode != null && parameterName.equals(nameNode.getTokenValue());
+ public OAR020ExpandParameterCheck() {
+ super(
+ KEY,
+ MESSAGE,
+ PARAM_NAME,
+ false
+ );
}
- private String getPath(JsonNode node) {
- StringBuilder pathBuilder = new StringBuilder();
- AstNode pathNode = node.getFirstAncestor(OpenApi2Grammar.PATH, OpenApi3Grammar.PATH, OpenApi31Grammar.PATH);
- if (pathNode != null) {
- while (pathNode.getType() != OpenApi2Grammar.PATH && pathNode.getType() != OpenApi3Grammar.PATH && pathNode.getType() != OpenApi31Grammar.PATH) {
- pathNode = pathNode.getParent();
- }
- pathBuilder.append(((JsonNode) pathNode).key().getTokenValue());
- }
- return pathBuilder.toString();
- }
-
- private boolean shouldIncludePath(String path) {
- if (pathCheckStrategy.equals("/exclude")) {
- return !paths.contains(path);
- } else if (pathCheckStrategy.equals("/include")) {
- return paths.contains(path);
- }
- return false;
- }
-
- private Set parsePaths(String pathsStr) {
- if (!pathsStr.trim().isEmpty()) {
- return Arrays.stream(pathsStr.split(","))
- .map(String::trim)
- .collect(Collectors.toSet());
- } else {
- return new HashSet<>();
- }
- }
-
- private JsonNode resolveReference(String refValue, JsonNode root) {
- if (refValue == null || !refValue.startsWith("#/")) {
- return null;
- }
-
- String pathToReference = refValue.substring(2);
- String[] pathParts = pathToReference.split("/");
-
- JsonNode currentNode = root;
- for (String part : pathParts) {
- if (currentNode == null) {
- return null;
- }
- currentNode = currentNode.get(part);
- }
-
- if (currentNode == null) {
- } else {
- }
-
- return currentNode;
- }
}
\ No newline at end of file
diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR021ExcludeParameterCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR021ExcludeParameterCheck.java
index e6057e7c..65d031db 100644
--- a/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR021ExcludeParameterCheck.java
+++ b/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR021ExcludeParameterCheck.java
@@ -1,186 +1,20 @@
package apiaddicts.sonar.openapi.checks.parameters;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-import org.apiaddicts.apitools.dosonarapi.api.v2.OpenApi2Grammar;
-import org.apiaddicts.apitools.dosonarapi.api.v3.OpenApi3Grammar;
-import org.apiaddicts.apitools.dosonarapi.api.v31.OpenApi31Grammar;
-import org.apiaddicts.apitools.dosonarapi.sslr.yaml.grammar.JsonNode;
import org.sonar.check.Rule;
-import org.sonar.check.RuleProperty;
-import apiaddicts.sonar.openapi.checks.BaseCheck;
-
-import com.google.common.collect.ImmutableSet;
-import com.sonar.sslr.api.AstNode;
-import com.sonar.sslr.api.AstNodeType;
-
-import java.util.Arrays;
-import java.util.HashSet;
@Rule(key = OAR021ExcludeParameterCheck.KEY)
-public class OAR021ExcludeParameterCheck extends BaseCheck {
+public class OAR021ExcludeParameterCheck extends AbstractQueryParameterCheck {
public static final String KEY = "OAR021";
private static final String MESSAGE = "OAR021.error";
- private static final String DEFAULT_PATH = "/examples";
- private static final String PATH_STRATEGY = "/include";
private static final String PARAM_NAME = "$exclude";
- @RuleProperty(
- key = "paths",
- description = "List of explicit paths to include/exclude from this rule separated by comma",
- defaultValue = DEFAULT_PATH
- )
- private String pathsStr = DEFAULT_PATH;
-
- @RuleProperty(
- key = "pathValidationStrategy",
- description = "Path validation strategy (include/exclude)",
- defaultValue = PATH_STRATEGY
- )
- private String pathCheckStrategy = PATH_STRATEGY;
-
- @RuleProperty(
- key = "parameterName",
- description = "Name of the parameter to be checked",
- defaultValue = PARAM_NAME
- )
- private String parameterName = PARAM_NAME;
-
- private Set paths;
- private JsonNode rootNode;
-
- @Override
- public Set subscribedKinds() {
- return ImmutableSet.of(OpenApi2Grammar.OPERATION, OpenApi3Grammar.OPERATION, OpenApi31Grammar.OPERATION);
- }
-
- @Override
- protected void visitFile(JsonNode root) {
- this.rootNode = root;
- paths = parsePaths(pathsStr);
-
- super.visitFile(root);
- }
-
- @Override
- public void visitNode(JsonNode node) {
- if ("get".equals(node.key().getTokenValue())) {
-
- String path = getPath(node);
-
- if (endsWithPathParam(path)) {
- return;
- }
-
- boolean hasParameter = hasParameterInNode(node);
-
- if (shouldIncludePath(path) && !hasParameter) {
- addIssue(KEY, translate(MESSAGE, PARAM_NAME), node.key());
- }
- }
- }
-
- private boolean endsWithPathParam(String path) {
- String[] segments = path.split("/");
- if (segments.length == 0) return false;
-
- String last = segments[segments.length - 1].trim();
- return last.matches("^\\{[^}]+\\}$");
- }
-
- private boolean hasParameterInNode(JsonNode node) {
- JsonNode parametersNode = node.get("parameters");
- if (parametersNode != null) {
-
- for (JsonNode parameterNode : parametersNode.elements()) {
- if (isRefParameter(parameterNode) && hasNamedRefParameter(parameterNode)) {
- return true;
- } else if (hasDirectParameter(parameterNode)) {
- return true;
- }
- }
- }
- return false;
- }
-
- private boolean isRefParameter(JsonNode parameterNode) {
- JsonNode refNode = parameterNode.get("$ref");
- if (refNode != null) {
- return true;
- }
- return false;
- }
-
- private boolean hasNamedRefParameter(JsonNode parameterNode) {
- String refValue = parameterNode.get("$ref").getTokenValue();
- JsonNode refParameterNode = resolveReference(refValue, rootNode);
- if (refParameterNode != null) {
- JsonNode nameNode = refParameterNode.get("name");
- JsonNode inNode = refParameterNode.get("in");
- return inNode != null && "query".equals(inNode.getTokenValue()) && nameNode != null && parameterName.equals(nameNode.getTokenValue());
- }
- return false;
- }
-
- private boolean hasDirectParameter(JsonNode parameterNode) {
- JsonNode nameNode = parameterNode.get("name");
- JsonNode inNode = parameterNode.get("in");
- return inNode != null && "query".equals(inNode.getTokenValue()) && nameNode != null && parameterName.equals(nameNode.getTokenValue());
- }
-
- private String getPath(JsonNode node) {
- StringBuilder pathBuilder = new StringBuilder();
- AstNode pathNode = node.getFirstAncestor(OpenApi2Grammar.PATH, OpenApi3Grammar.PATH, OpenApi31Grammar.PATH);
- if (pathNode != null) {
- while (pathNode.getType() != OpenApi2Grammar.PATH && pathNode.getType() != OpenApi3Grammar.PATH && pathNode.getType() != OpenApi31Grammar.PATH) {
- pathNode = pathNode.getParent();
- }
- pathBuilder.append(((JsonNode) pathNode).key().getTokenValue());
- }
- return pathBuilder.toString();
- }
-
- private boolean shouldIncludePath(String path) {
- if (pathCheckStrategy.equals("/exclude")) {
- return !paths.contains(path);
- } else if (pathCheckStrategy.equals("/include")) {
- return paths.contains(path);
- }
- return false;
- }
-
- private Set parsePaths(String pathsStr) {
- if (!pathsStr.trim().isEmpty()) {
- return Arrays.stream(pathsStr.split(","))
- .map(String::trim)
- .collect(Collectors.toSet());
- } else {
- return new HashSet<>();
- }
- }
-
- private JsonNode resolveReference(String refValue, JsonNode root) {
- if (refValue == null || !refValue.startsWith("#/")) {
- return null;
- }
-
- String pathToReference = refValue.substring(2);
- String[] pathParts = pathToReference.split("/");
-
- JsonNode currentNode = root;
- for (String part : pathParts) {
- if (currentNode == null) {
- return null;
- }
- currentNode = currentNode.get(part);
- }
-
- if (currentNode == null) {
- } else {
- }
-
- return currentNode;
+ public OAR021ExcludeParameterCheck() {
+ super(
+ KEY,
+ MESSAGE,
+ PARAM_NAME,
+ false
+ );
}
}
\ No newline at end of file
diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR022OrderbyParameterCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR022OrderbyParameterCheck.java
index 30924966..0cbe783e 100644
--- a/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR022OrderbyParameterCheck.java
+++ b/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR022OrderbyParameterCheck.java
@@ -1,185 +1,20 @@
package apiaddicts.sonar.openapi.checks.parameters;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-import org.apiaddicts.apitools.dosonarapi.api.v2.OpenApi2Grammar;
-import org.apiaddicts.apitools.dosonarapi.api.v3.OpenApi3Grammar;
-import org.apiaddicts.apitools.dosonarapi.api.v31.OpenApi31Grammar;
-import org.apiaddicts.apitools.dosonarapi.sslr.yaml.grammar.JsonNode;
import org.sonar.check.Rule;
-import org.sonar.check.RuleProperty;
-import apiaddicts.sonar.openapi.checks.BaseCheck;
-
-import com.google.common.collect.ImmutableSet;
-import com.sonar.sslr.api.AstNode;
-import com.sonar.sslr.api.AstNodeType;
-
-import java.util.Arrays;
-import java.util.HashSet;
@Rule(key = OAR022OrderbyParameterCheck.KEY)
-public class OAR022OrderbyParameterCheck extends BaseCheck {
+public class OAR022OrderbyParameterCheck extends AbstractQueryParameterCheck {
public static final String KEY = "OAR022";
private static final String MESSAGE = "OAR022.error";
- private static final String DEFAULT_PATH = "/examples";
- private static final String PATH_STRATEGY = "/include";
private static final String PARAM_NAME = "$orderby";
- @RuleProperty(
- key = "paths",
- description = "List of explicit paths to include/exclude from this rule separated by comma",
- defaultValue = DEFAULT_PATH
- )
- private String pathsStr = DEFAULT_PATH;
-
- @RuleProperty(
- key = "pathValidationStrategy",
- description = "Path validation strategy (include/exclude)",
- defaultValue = PATH_STRATEGY
- )
- private String pathCheckStrategy = PATH_STRATEGY;
-
- @RuleProperty(
- key = "parameterName",
- description = "Name of the parameter to be checked",
- defaultValue = PARAM_NAME
- )
- private String parameterName = PARAM_NAME;
-
- private Set paths;
- private JsonNode rootNode;
-
- @Override
- public Set subscribedKinds() {
- return ImmutableSet.of(OpenApi2Grammar.OPERATION, OpenApi3Grammar.OPERATION, OpenApi31Grammar.OPERATION);
- }
-
- @Override
- protected void visitFile(JsonNode root) {
- this.rootNode = root;
- paths = parsePaths(pathsStr);
- super.visitFile(root);
- }
-
- @Override
- public void visitNode(JsonNode node) {
- if ("get".equals(node.key().getTokenValue())) {
-
- String path = getPath(node);
-
- boolean hasParameter = hasParameterInNode(node);
-
- if (shouldIncludePath(path) && !hasParameter) {
- addIssue(KEY, translate(MESSAGE, PARAM_NAME), node.key());
- }
- }
- }
-
- private boolean hasParameterInNode(JsonNode node) {
- JsonNode parametersNode = node.get("parameters");
- if (parametersNode != null) {
-
- for (JsonNode parameterNode : parametersNode.elements()) {
- if (isRefParameter(parameterNode) && hasNamedRefParameter(parameterNode)) {
- return true;
- } else if (hasDirectParameter(parameterNode)) {
- return true;
- }
- }
- }
- return false;
- }
-
- private boolean isRefParameter(JsonNode parameterNode) {
- JsonNode refNode = parameterNode.get("$ref");
- if (refNode != null) {
- return true;
- }
- return false;
- }
-
- private boolean hasNamedRefParameter(JsonNode parameterNode) {
- String refValue = parameterNode.get("$ref").getTokenValue();
- JsonNode refParameterNode = resolveReference(refValue, rootNode);
- if (refParameterNode != null) {
- JsonNode nameNode = refParameterNode.get("name");
- JsonNode inNode = refParameterNode.get("in");
- return inNode != null && "query".equals(inNode.getTokenValue()) && nameNode != null && parameterName.equals(nameNode.getTokenValue());
- }
- return false;
- }
-
- private boolean hasDirectParameter(JsonNode parameterNode) {
- JsonNode nameNode = parameterNode.get("name");
- JsonNode inNode = parameterNode.get("in");
- return inNode != null && "query".equals(inNode.getTokenValue()) && nameNode != null && parameterName.equals(nameNode.getTokenValue());
- }
-
- private String getPath(JsonNode node) {
- StringBuilder pathBuilder = new StringBuilder();
- AstNode pathNode = node.getFirstAncestor(OpenApi2Grammar.PATH, OpenApi3Grammar.PATH , OpenApi31Grammar.PATH);
- if (pathNode != null) {
- while (pathNode.getType() != OpenApi2Grammar.PATH && pathNode.getType() != OpenApi3Grammar.PATH && pathNode.getType() != OpenApi31Grammar.PATH) {
- pathNode = pathNode.getParent();
- }
- pathBuilder.append(((JsonNode) pathNode).key().getTokenValue());
- }
- return pathBuilder.toString();
- }
-
- private boolean endsWithPathParam(String path) {
- String[] segments = path.split("/");
- if (segments.length == 0) return false;
-
- String last = segments[segments.length - 1].trim();
- return last.matches("^\\{[^}]+\\}$");
- }
-
- private boolean shouldIncludePath(String path) {
- if (endsWithPathParam(path)) {
- return false;
- }
-
- if (pathCheckStrategy.equals("/exclude")) {
- return !paths.contains(path);
- } else if (pathCheckStrategy.equals("/include")) {
- return paths.contains(path);
- }
- return false;
- }
-
- private Set parsePaths(String pathsStr) {
- if (!pathsStr.trim().isEmpty()) {
- return Arrays.stream(pathsStr.split(","))
- .map(String::trim)
- .collect(Collectors.toSet());
- } else {
- return new HashSet<>();
- }
- }
-
- private JsonNode resolveReference(String refValue, JsonNode root) {
- if (refValue == null || !refValue.startsWith("#/")) {
- return null;
- }
-
- String pathToReference = refValue.substring(2);
- String[] pathParts = pathToReference.split("/");
-
- JsonNode currentNode = root;
- for (String part : pathParts) {
- if (currentNode == null) {
- return null;
- }
- currentNode = currentNode.get(part);
- }
-
- if (currentNode == null) {
- } else {
- }
-
- return currentNode;
+ public OAR022OrderbyParameterCheck() {
+ super(
+ KEY,
+ MESSAGE,
+ PARAM_NAME,
+ false
+ );
}
-}
\ No newline at end of file
+}
diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR023TotalParameterCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR023TotalParameterCheck.java
index b8226c4a..8337d644 100644
--- a/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR023TotalParameterCheck.java
+++ b/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR023TotalParameterCheck.java
@@ -1,172 +1,20 @@
package apiaddicts.sonar.openapi.checks.parameters;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-import org.apiaddicts.apitools.dosonarapi.api.v2.OpenApi2Grammar;
-import org.apiaddicts.apitools.dosonarapi.api.v3.OpenApi3Grammar;
-import org.apiaddicts.apitools.dosonarapi.api.v31.OpenApi31Grammar;
-import org.apiaddicts.apitools.dosonarapi.sslr.yaml.grammar.JsonNode;
import org.sonar.check.Rule;
-import org.sonar.check.RuleProperty;
-import apiaddicts.sonar.openapi.checks.BaseCheck;
-
-import com.google.common.collect.ImmutableSet;
-import com.sonar.sslr.api.AstNode;
-import com.sonar.sslr.api.AstNodeType;
-
-import java.util.Arrays;
-import java.util.HashSet;
@Rule(key = OAR023TotalParameterCheck.KEY)
-public class OAR023TotalParameterCheck extends BaseCheck {
+public class OAR023TotalParameterCheck extends AbstractQueryParameterCheck {
public static final String KEY = "OAR023";
private static final String MESSAGE = "OAR023.error";
- private static final String DEFAULT_PATH = "/examples";
- private static final String PATH_STRATEGY = "/include";
private static final String PARAM_NAME = "$total";
- @RuleProperty(
- key = "paths",
- description = "List of explicit paths to include/exclude from this rule separated by comma",
- defaultValue = DEFAULT_PATH
- )
- private String pathsStr = DEFAULT_PATH;
-
- @RuleProperty(
- key = "pathValidationStrategy",
- description = "Path validation strategy (include/exclude)",
- defaultValue = PATH_STRATEGY
- )
- private String pathCheckStrategy = PATH_STRATEGY;
-
- @RuleProperty(
- key = "parameterName",
- description = "Name of the parameter to be checked",
- defaultValue = PARAM_NAME
- )
- private String parameterName = PARAM_NAME;
-
- private Set paths;
- private JsonNode rootNode;
- @Override
- public Set subscribedKinds() {
- return ImmutableSet.of(OpenApi2Grammar.OPERATION, OpenApi3Grammar.OPERATION, OpenApi31Grammar.OPERATION);
- }
-
- @Override
- protected void visitFile(JsonNode root) {
- this.rootNode = root;
- paths = parsePaths(pathsStr);
- super.visitFile(root);
- }
-
- @Override
- public void visitNode(JsonNode node) {
- if ("get".equals(node.key().getTokenValue())) {
-
- String path = getPath(node);
-
- boolean hasParameter = hasParameterInNode(node);
-
- if (shouldIncludePath(path) && !hasParameter) {
- addIssue(KEY, translate(MESSAGE, PARAM_NAME), node.key());
- }
- }
- }
-
- private boolean hasParameterInNode(JsonNode node) {
- JsonNode parametersNode = node.get("parameters");
- if (parametersNode != null) {
-
- for (JsonNode parameterNode : parametersNode.elements()) {
- if (isRefParameter(parameterNode) && hasNamedRefParameter(parameterNode)) {
- return true;
- } else if (hasDirectParameter(parameterNode)) {
- return true;
- }
- }
- }
- return false;
- }
-
- private boolean isRefParameter(JsonNode parameterNode) {
- JsonNode refNode = parameterNode.get("$ref");
- if (refNode != null) {
- return true;
- }
- return false;
- }
-
- private boolean hasNamedRefParameter(JsonNode parameterNode) {
- String refValue = parameterNode.get("$ref").getTokenValue();
- JsonNode refParameterNode = resolveReference(refValue, rootNode);
- if (refParameterNode != null) {
- JsonNode nameNode = refParameterNode.get("name");
- JsonNode inNode = refParameterNode.get("in");
- return inNode != null && "query".equals(inNode.getTokenValue()) && nameNode != null && parameterName.equals(nameNode.getTokenValue());
- }
- return false;
- }
-
- private boolean hasDirectParameter(JsonNode parameterNode) {
- JsonNode nameNode = parameterNode.get("name");
- JsonNode inNode = parameterNode.get("in");
- return inNode != null && "query".equals(inNode.getTokenValue()) && nameNode != null && parameterName.equals(nameNode.getTokenValue());
- }
-
- private String getPath(JsonNode node) {
- StringBuilder pathBuilder = new StringBuilder();
- AstNode pathNode = node.getFirstAncestor(OpenApi2Grammar.PATH, OpenApi3Grammar.PATH, OpenApi31Grammar.PATH);
- if (pathNode != null) {
- while (pathNode.getType() != OpenApi2Grammar.PATH && pathNode.getType() != OpenApi3Grammar.PATH && pathNode.getType() != OpenApi31Grammar.PATH) {
- pathNode = pathNode.getParent();
- }
- pathBuilder.append(((JsonNode) pathNode).key().getTokenValue());
- }
- return pathBuilder.toString();
- }
-
- private boolean shouldIncludePath(String path) {
- if (pathCheckStrategy.equals("/exclude")) {
- return !paths.contains(path);
- } else if (pathCheckStrategy.equals("/include")) {
- return paths.contains(path);
- }
- return false;
- }
-
- private Set parsePaths(String pathsStr) {
- if (!pathsStr.trim().isEmpty()) {
- return Arrays.stream(pathsStr.split(","))
- .map(String::trim)
- .collect(Collectors.toSet());
- } else {
- return new HashSet<>();
- }
- }
-
- private JsonNode resolveReference(String refValue, JsonNode root) {
- if (refValue == null || !refValue.startsWith("#/")) {
- return null;
- }
-
- String pathToReference = refValue.substring(2);
- String[] pathParts = pathToReference.split("/");
-
- JsonNode currentNode = root;
- for (String part : pathParts) {
- if (currentNode == null) {
- return null;
- }
- currentNode = currentNode.get(part);
- }
-
- if (currentNode == null) {
- } else {
- }
-
- return currentNode;
+ public OAR023TotalParameterCheck() {
+ super(
+ KEY,
+ MESSAGE,
+ PARAM_NAME,
+ true
+ );
}
}
\ No newline at end of file
diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR024StartParameterCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR024StartParameterCheck.java
index 7da3eb0c..aedcaafa 100644
--- a/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR024StartParameterCheck.java
+++ b/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR024StartParameterCheck.java
@@ -1,173 +1,20 @@
package apiaddicts.sonar.openapi.checks.parameters;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-import org.apiaddicts.apitools.dosonarapi.api.v2.OpenApi2Grammar;
-import org.apiaddicts.apitools.dosonarapi.api.v3.OpenApi3Grammar;
-import org.apiaddicts.apitools.dosonarapi.api.v31.OpenApi31Grammar;
-import org.apiaddicts.apitools.dosonarapi.sslr.yaml.grammar.JsonNode;
import org.sonar.check.Rule;
-import org.sonar.check.RuleProperty;
-import apiaddicts.sonar.openapi.checks.BaseCheck;
-
-import com.google.common.collect.ImmutableSet;
-import com.sonar.sslr.api.AstNode;
-import com.sonar.sslr.api.AstNodeType;
-
-import java.util.Arrays;
-import java.util.HashSet;
@Rule(key = OAR024StartParameterCheck.KEY)
-public class OAR024StartParameterCheck extends BaseCheck {
+public class OAR024StartParameterCheck extends AbstractQueryParameterCheck {
public static final String KEY = "OAR024";
private static final String MESSAGE = "OAR024.error";
- private static final String DEFAULT_PATH = "/examples";
- private static final String PATH_STRATEGY = "/include";
private static final String PARAM_NAME = "$start";
- @RuleProperty(
- key = "paths",
- description = "List of explicit paths to include/exclude from this rule separated by comma",
- defaultValue = DEFAULT_PATH
- )
- private String pathsStr = DEFAULT_PATH;
-
- @RuleProperty(
- key = "pathValidationStrategy",
- description = "Path validation strategy (include/exclude)",
- defaultValue = PATH_STRATEGY
- )
- private String pathCheckStrategy = PATH_STRATEGY;
-
- @RuleProperty(
- key = "parameterName",
- description = "Name of the parameter to be checked",
- defaultValue = PARAM_NAME
- )
- private String parameterName = PARAM_NAME;
-
- private Set paths;
- private JsonNode rootNode;
-
- @Override
- public Set subscribedKinds() {
- return ImmutableSet.of(OpenApi2Grammar.OPERATION, OpenApi3Grammar.OPERATION, OpenApi31Grammar.OPERATION);
- }
-
- @Override
- protected void visitFile(JsonNode root) {
- this.rootNode = root;
- paths = parsePaths(pathsStr);
- super.visitFile(root);
- }
-
- @Override
- public void visitNode(JsonNode node) {
- if ("get".equals(node.key().getTokenValue())) {
-
- String path = getPath(node);
-
- boolean hasParameter = hasParameterInNode(node);
-
- if (shouldIncludePath(path) && !hasParameter) {
- addIssue(KEY, translate(MESSAGE, PARAM_NAME), node.key());
- }
- }
- }
-
- private boolean hasParameterInNode(JsonNode node) {
- JsonNode parametersNode = node.get("parameters");
- if (parametersNode != null) {
-
- for (JsonNode parameterNode : parametersNode.elements()) {
- if (isRefParameter(parameterNode) && hasNamedRefParameter(parameterNode)) {
- return true;
- } else if (hasDirectParameter(parameterNode)) {
- return true;
- }
- }
- }
- return false;
- }
-
- private boolean isRefParameter(JsonNode parameterNode) {
- JsonNode refNode = parameterNode.get("$ref");
- if (refNode != null) {
- return true;
- }
- return false;
- }
-
- private boolean hasNamedRefParameter(JsonNode parameterNode) {
- String refValue = parameterNode.get("$ref").getTokenValue();
- JsonNode refParameterNode = resolveReference(refValue, rootNode);
- if (refParameterNode != null) {
- JsonNode nameNode = refParameterNode.get("name");
- JsonNode inNode = refParameterNode.get("in");
- return inNode != null && "query".equals(inNode.getTokenValue()) && nameNode != null && parameterName.equals(nameNode.getTokenValue());
- }
- return false;
- }
-
- private boolean hasDirectParameter(JsonNode parameterNode) {
- JsonNode nameNode = parameterNode.get("name");
- JsonNode inNode = parameterNode.get("in");
- return inNode != null && "query".equals(inNode.getTokenValue()) && nameNode != null && parameterName.equals(nameNode.getTokenValue());
- }
-
- private String getPath(JsonNode node) {
- StringBuilder pathBuilder = new StringBuilder();
- AstNode pathNode = node.getFirstAncestor(OpenApi2Grammar.PATH, OpenApi3Grammar.PATH, OpenApi31Grammar.PATH);
- if (pathNode != null) {
- while (pathNode.getType() != OpenApi2Grammar.PATH && pathNode.getType() != OpenApi3Grammar.PATH && pathNode.getType() != OpenApi31Grammar.PATH) {
- pathNode = pathNode.getParent();
- }
- pathBuilder.append(((JsonNode) pathNode).key().getTokenValue());
- }
- return pathBuilder.toString();
- }
-
- private boolean shouldIncludePath(String path) {
- if (pathCheckStrategy.equals("/exclude")) {
- return !paths.contains(path);
- } else if (pathCheckStrategy.equals("/include")) {
- return paths.contains(path);
- }
- return false;
- }
-
- private Set parsePaths(String pathsStr) {
- if (!pathsStr.trim().isEmpty()) {
- return Arrays.stream(pathsStr.split(","))
- .map(String::trim)
- .collect(Collectors.toSet());
- } else {
- return new HashSet<>();
- }
- }
-
- private JsonNode resolveReference(String refValue, JsonNode root) {
- if (refValue == null || !refValue.startsWith("#/")) {
- return null;
- }
-
- String pathToReference = refValue.substring(2);
- String[] pathParts = pathToReference.split("/");
-
- JsonNode currentNode = root;
- for (String part : pathParts) {
- if (currentNode == null) {
- return null;
- }
- currentNode = currentNode.get(part);
- }
-
- if (currentNode == null) {
- } else {
- }
-
- return currentNode;
+ public OAR024StartParameterCheck() {
+ super(
+ KEY,
+ MESSAGE,
+ PARAM_NAME,
+ true
+ );
}
}
\ No newline at end of file
diff --git a/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR025LimitParameterCheck.java b/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR025LimitParameterCheck.java
index 828637f1..2ea5e207 100644
--- a/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR025LimitParameterCheck.java
+++ b/src/main/java/apiaddicts/sonar/openapi/checks/parameters/OAR025LimitParameterCheck.java
@@ -1,174 +1,20 @@
package apiaddicts.sonar.openapi.checks.parameters;
-import apiaddicts.sonar.openapi.checks.BaseCheck;
-import com.google.common.collect.ImmutableSet;
-import com.sonar.sslr.api.AstNode;
-import com.sonar.sslr.api.AstNodeType;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.Set;
-import java.util.stream.Collectors;
-import org.apiaddicts.apitools.dosonarapi.api.v2.OpenApi2Grammar;
-import org.apiaddicts.apitools.dosonarapi.api.v3.OpenApi3Grammar;
-import org.apiaddicts.apitools.dosonarapi.api.v31.OpenApi31Grammar;
-import org.apiaddicts.apitools.dosonarapi.sslr.yaml.grammar.JsonNode;
import org.sonar.check.Rule;
-import org.sonar.check.RuleProperty;
@Rule(key = OAR025LimitParameterCheck.KEY)
-public class OAR025LimitParameterCheck extends BaseCheck {
+public class OAR025LimitParameterCheck extends AbstractQueryParameterCheck {
public static final String KEY = "OAR025";
private static final String MESSAGE = "OAR025.error";
- private static final String DEFAULT_PATH = "/examples";
- private static final String PATH_STRATEGY = "/include";
private static final String PARAM_NAME = "$limit";
- @RuleProperty(
- key = "paths",
- description = "List of explicit paths to include/exclude from this rule separated by comma",
- defaultValue = DEFAULT_PATH
- )
- private String pathsStr = DEFAULT_PATH;
-
- @RuleProperty(
- key = "pathValidationStrategy",
- description = "Path validation strategy (include/exclude)",
- defaultValue = PATH_STRATEGY
- )
- private String pathCheckStrategy = PATH_STRATEGY;
-
- @RuleProperty(
- key = "parameterName",
- description = "Name of the parameter to be checked",
- defaultValue = PARAM_NAME
- )
- private String parameterName = PARAM_NAME;
-
- private Set paths;
- private JsonNode rootNode;
-
- @Override
- public Set subscribedKinds() {
- return ImmutableSet.of(OpenApi2Grammar.OPERATION, OpenApi3Grammar.OPERATION, OpenApi31Grammar.OPERATION);
- }
-
- @Override
- protected void visitFile(JsonNode root) {
- this.rootNode = root;
- paths = parsePaths(pathsStr);
- super.visitFile(root);
- }
-
- @Override
- public void visitNode(JsonNode node) {
- if ("get".equals(node.key().getTokenValue())) {
-
- String path = getPath(node);
-
- boolean hasParameter = hasParameterInNode(node);
-
- if (shouldIncludePath(path) && !hasParameter && !isSingleResourcePath(path)) {
- addIssue(KEY, translate(MESSAGE, PARAM_NAME), node.key());
- }
- }
- }
-
- private boolean hasParameterInNode(JsonNode node) {
- JsonNode parametersNode = node.get("parameters");
- if (parametersNode != null) {
-
- for (JsonNode parameterNode : parametersNode.elements()) {
- if (isRefParameter(parameterNode) && hasNamedRefParameter(parameterNode)) {
- return true;
- } else if (hasDirectParameter(parameterNode)) {
- return true;
- }
- }
- }
- return false;
- }
-
- private boolean isRefParameter(JsonNode parameterNode) {
- JsonNode refNode = parameterNode.get("$ref");
- if (refNode != null) {
- return true;
- }
- return false;
- }
-
- private boolean hasNamedRefParameter(JsonNode parameterNode) {
- String refValue = parameterNode.get("$ref").getTokenValue();
- JsonNode refParameterNode = resolveReference(refValue, rootNode);
- if (refParameterNode != null) {
- JsonNode nameNode = refParameterNode.get("name");
- JsonNode inNode = refParameterNode.get("in");
- return inNode != null && "query".equals(inNode.getTokenValue()) && nameNode != null && parameterName.equals(nameNode.getTokenValue());
- }
- return false;
- }
-
- private boolean hasDirectParameter(JsonNode parameterNode) {
- JsonNode nameNode = parameterNode.get("name");
- JsonNode inNode = parameterNode.get("in");
- return inNode != null && "query".equals(inNode.getTokenValue()) && nameNode != null && parameterName.equals(nameNode.getTokenValue());
- }
-
- private String getPath(JsonNode node) {
- StringBuilder pathBuilder = new StringBuilder();
- AstNode pathNode = node.getFirstAncestor(OpenApi2Grammar.PATH, OpenApi3Grammar.PATH, OpenApi31Grammar.PATH);
- if (pathNode != null) {
- while (pathNode.getType() != OpenApi2Grammar.PATH && pathNode.getType() != OpenApi3Grammar.PATH && pathNode.getType() != OpenApi31Grammar.PATH) {
- pathNode = pathNode.getParent();
- }
- pathBuilder.append(((JsonNode) pathNode).key().getTokenValue());
- }
- return pathBuilder.toString();
- }
-
- private boolean shouldIncludePath(String path) {
- if (pathCheckStrategy.equals("/exclude")) {
- return !paths.contains(path);
- } else if (pathCheckStrategy.equals("/include")) {
- return paths.contains(path);
- }
- return false;
- }
-
- private Set parsePaths(String pathsStr) {
- if (!pathsStr.trim().isEmpty()) {
- return Arrays.stream(pathsStr.split(","))
- .map(String::trim)
- .collect(Collectors.toSet());
- } else {
- return new HashSet<>();
- }
- }
-
- private JsonNode resolveReference(String refValue, JsonNode root) {
- if (refValue == null || !refValue.startsWith("#/")) {
- return null;
- }
-
- String pathToReference = refValue.substring(2);
- String[] pathParts = pathToReference.split("/");
-
- JsonNode currentNode = root;
- for (String part : pathParts) {
- if (currentNode == null) {
- return null;
- }
- currentNode = currentNode.get(part);
- }
-
- if (currentNode == null) {
- } else {
- }
-
- return currentNode;
- }
-
- private boolean isSingleResourcePath(String path) {
- return path.matches(".*/\\{[^/]+\\}$");
+ public OAR025LimitParameterCheck() {
+ super(
+ KEY,
+ MESSAGE,
+ PARAM_NAME,
+ false
+ );
}
}
\ No newline at end of file
diff --git a/src/test/java/apiaddicts/sonar/openapi/checks/operations/OAR104ResourcesByPostVerbCheckTest.java b/src/test/java/apiaddicts/sonar/openapi/checks/operations/OAR104ResourcesByPostVerbCheckTest.java
index bb1063fd..54c621d7 100644
--- a/src/test/java/apiaddicts/sonar/openapi/checks/operations/OAR104ResourcesByPostVerbCheckTest.java
+++ b/src/test/java/apiaddicts/sonar/openapi/checks/operations/OAR104ResourcesByPostVerbCheckTest.java
@@ -19,10 +19,10 @@ public void init() {
v3Path = getV3Path("operations");
}
- /*@Test
+ @Test
public void verifyInV2() {
- verifyV2("plain");
- }*/
+ verifyV2("plain.yaml");
+ }
@Test
public void verifyInV3() {
@@ -37,6 +37,6 @@ public void verifyRule() {
@Override
public void verifyParameters() {
assertNumberOfParameters(1);
- assertParameterProperties("words-to-exclude", "me", RuleParamType.STRING);
+ assertParameterProperties("words-to-exclude", "me,search", RuleParamType.STRING);
}
}
\ No newline at end of file
diff --git a/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR019SelectParameterCheckTest.java b/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR019SelectParameterCheckTest.java
index 90282011..8b1fc903 100644
--- a/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR019SelectParameterCheckTest.java
+++ b/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR019SelectParameterCheckTest.java
@@ -40,6 +40,11 @@ public void verifyInV2WithRef() {
verifyV2("with-ref");
}
+ @Test
+ public void verifyInV2PathEndingWithParam() {
+ verifyV3("with-param");
+ }
+
@Test
public void verifyInV3() {
verifyV3("plain");
@@ -80,6 +85,11 @@ public void verifyInV31WithRef() {
verifyV31("with-ref");
}
+ @Test
+ public void verifyInV3PathEndingWithParam() {
+ verifyV3("with-param");
+ }
+
@Override
public void verifyRule() {
assertRuleProperties("OAR019 - SelectParameter - the chosen parameter must be defined in this operation", RuleType.BUG, Severity.MINOR, tags("parameters"));
@@ -87,10 +97,8 @@ public void verifyRule() {
@Override
public void verifyParameters() {
- assertNumberOfParameters(3);
- assertParameterProperties("parameterName", "$select", RuleParamType.STRING);
+ assertNumberOfParameters(2);
assertParameterProperties("paths", "/examples", RuleParamType.STRING);
assertParameterProperties("pathValidationStrategy", "/include", RuleParamType.STRING);
-
}
}
\ No newline at end of file
diff --git a/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR020ExpandParameterCheckTest.java b/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR020ExpandParameterCheckTest.java
index 81134ce3..6e9c358b 100644
--- a/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR020ExpandParameterCheckTest.java
+++ b/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR020ExpandParameterCheckTest.java
@@ -39,6 +39,11 @@ public void verifyInV2WithRef() {
verifyV2("with-ref");
}
+ @Test
+ public void verifyInV2PathEndingWithParam() {
+ verifyV3("with-param");
+ }
+
@Test
public void verifyInV3() {
verifyV3("plain2");
@@ -59,6 +64,11 @@ public void verifyInV3WithRef() {
verifyV3("with-ref");
}
+ @Test
+ public void verifyInV3PathEndingWithParam() {
+ verifyV3("with-param");
+ }
+
@Override
public void verifyRule() {
assertRuleProperties("OAR020 - ExpandParameter - the chosen parameter must be defined in this operation", RuleType.BUG, Severity.MINOR, tags("parameters"));
@@ -66,9 +76,8 @@ public void verifyRule() {
@Override
public void verifyParameters() {
- assertNumberOfParameters(3);
+ assertNumberOfParameters(2);
assertParameterProperties("paths", "/examples", RuleParamType.STRING);
assertParameterProperties("pathValidationStrategy", "/include", RuleParamType.STRING);
- assertParameterProperties("parameterName", "$expand", RuleParamType.STRING);
}
}
\ No newline at end of file
diff --git a/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR021ExcludeParameterCheckTest.java b/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR021ExcludeParameterCheckTest.java
index 688a53f3..5d559cd3 100644
--- a/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR021ExcludeParameterCheckTest.java
+++ b/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR021ExcludeParameterCheckTest.java
@@ -76,9 +76,8 @@ public void verifyRule() {
@Override
public void verifyParameters() {
- assertNumberOfParameters(3);
+ assertNumberOfParameters(2);
assertParameterProperties("paths", "/examples", RuleParamType.STRING);
assertParameterProperties("pathValidationStrategy", "/include", RuleParamType.STRING);
- assertParameterProperties("parameterName", "$exclude", RuleParamType.STRING);
}
}
\ No newline at end of file
diff --git a/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR022OrderbyParameterCheckTest.java b/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR022OrderbyParameterCheckTest.java
index 40981cbe..cae16c7a 100644
--- a/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR022OrderbyParameterCheckTest.java
+++ b/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR022OrderbyParameterCheckTest.java
@@ -55,9 +55,8 @@ public void verifyRule() {
@Override
public void verifyParameters() {
- assertNumberOfParameters(3);
+ assertNumberOfParameters(2);
assertParameterProperties("paths", "/examples", RuleParamType.STRING);
assertParameterProperties("pathValidationStrategy", "/include", RuleParamType.STRING);
- assertParameterProperties("parameterName", "$orderby", RuleParamType.STRING);
}
}
\ No newline at end of file
diff --git a/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR023TotalParameterCheckTest.java b/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR023TotalParameterCheckTest.java
index 2bc638e3..7b6ab8a1 100644
--- a/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR023TotalParameterCheckTest.java
+++ b/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR023TotalParameterCheckTest.java
@@ -55,9 +55,8 @@ public void verifyRule() {
@Override
public void verifyParameters() {
- assertNumberOfParameters(3);
+ assertNumberOfParameters(2);
assertParameterProperties("paths", "/examples", RuleParamType.STRING);
assertParameterProperties("pathValidationStrategy", "/include", RuleParamType.STRING);
- assertParameterProperties("parameterName", "$total", RuleParamType.STRING);
}
}
\ No newline at end of file
diff --git a/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR024StartParameterCheckTest.java b/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR024StartParameterCheckTest.java
index 3322bec2..b978f505 100644
--- a/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR024StartParameterCheckTest.java
+++ b/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR024StartParameterCheckTest.java
@@ -55,9 +55,8 @@ public void verifyRule() {
@Override
public void verifyParameters() {
- assertNumberOfParameters(3);
+ assertNumberOfParameters(2);
assertParameterProperties("paths", "/examples", RuleParamType.STRING);
assertParameterProperties("pathValidationStrategy", "/include", RuleParamType.STRING);
- assertParameterProperties("parameterName", "$start", RuleParamType.STRING);
}
}
\ No newline at end of file
diff --git a/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR025LimitParameterCheckTest.java b/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR025LimitParameterCheckTest.java
index ad29af88..12a0cf26 100644
--- a/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR025LimitParameterCheckTest.java
+++ b/src/test/java/apiaddicts/sonar/openapi/checks/parameters/OAR025LimitParameterCheckTest.java
@@ -55,9 +55,8 @@ public void verifyRule() {
@Override
public void verifyParameters() {
- assertNumberOfParameters(3);
+ assertNumberOfParameters(2);
assertParameterProperties("paths", "/examples", RuleParamType.STRING);
assertParameterProperties("pathValidationStrategy", "/include", RuleParamType.STRING);
- assertParameterProperties("parameterName", "$limit", RuleParamType.STRING);
}
}
\ No newline at end of file
diff --git a/src/test/resources/checks/v2/operations/OAR104/plain.yaml b/src/test/resources/checks/v2/operations/OAR104/plain.yaml
index 81f57f1d..24178247 100644
--- a/src/test/resources/checks/v2/operations/OAR104/plain.yaml
+++ b/src/test/resources/checks/v2/operations/OAR104/plain.yaml
@@ -9,7 +9,7 @@ paths:
200:
description: Ok
/resources/{r_id}:
- post: # Noncompliant {{OAR104: Operation not recommended for resource path: resources/{r_id}}}
+ post:
parameters:
- name: r_id
in: path
@@ -19,12 +19,12 @@ paths:
200:
description: Ok
/resources/get:
- post:
+ post: # Noncompliant {{OAR104: Operation not recommended for resource path: resources/get}}
responses:
200:
description: Ok
/resources/delete:
- post:
+ post: # Noncompliant {{OAR104: Operation not recommended for resource path: resources/delete}}
parameters:
- name: delete
in: path
@@ -44,7 +44,12 @@ paths:
200:
description: Ok
/resources/me:
- post: # Noncompliant {{OAR104: Operation not recommended for resource path: resources/me}}
+ post:
+ responses:
+ 200:
+ description: Ok
+ /resources/search:
+ post:
responses:
200:
description: Ok
\ No newline at end of file
diff --git a/src/test/resources/checks/v2/parameters/OAR019/with-param.json b/src/test/resources/checks/v2/parameters/OAR019/with-param.json
new file mode 100644
index 00000000..799521d9
--- /dev/null
+++ b/src/test/resources/checks/v2/parameters/OAR019/with-param.json
@@ -0,0 +1,27 @@
+{
+ "swagger": "2.0",
+ "info": {
+ "title": "Swagger Petstore",
+ "version": "1.0.0"
+ },
+ "paths": {
+ "/examples/{id}": {
+ "get": {
+ "responses": {
+ "200": {
+ "description": "OK"
+ }
+ }
+ }
+ },
+ "/examples/{id}/items/{id}": {
+ "get": {
+ "responses": {
+ "200": {
+ "description": "OK"
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/test/resources/checks/v2/parameters/OAR019/with-param.yaml b/src/test/resources/checks/v2/parameters/OAR019/with-param.yaml
new file mode 100644
index 00000000..3f4a3407
--- /dev/null
+++ b/src/test/resources/checks/v2/parameters/OAR019/with-param.yaml
@@ -0,0 +1,15 @@
+swagger: "2.0"
+info:
+ title: Swagger Petstore
+ version: "1.0.0"
+paths:
+ /examples/{id}:
+ get:
+ responses:
+ 200:
+ description: OK
+ /examples/{id}/items/{id}:
+ get:
+ responses:
+ 200:
+ description: OK
diff --git a/src/test/resources/checks/v2/parameters/OAR020/with-param.json b/src/test/resources/checks/v2/parameters/OAR020/with-param.json
new file mode 100644
index 00000000..799521d9
--- /dev/null
+++ b/src/test/resources/checks/v2/parameters/OAR020/with-param.json
@@ -0,0 +1,27 @@
+{
+ "swagger": "2.0",
+ "info": {
+ "title": "Swagger Petstore",
+ "version": "1.0.0"
+ },
+ "paths": {
+ "/examples/{id}": {
+ "get": {
+ "responses": {
+ "200": {
+ "description": "OK"
+ }
+ }
+ }
+ },
+ "/examples/{id}/items/{id}": {
+ "get": {
+ "responses": {
+ "200": {
+ "description": "OK"
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/test/resources/checks/v2/parameters/OAR020/with-param.yaml b/src/test/resources/checks/v2/parameters/OAR020/with-param.yaml
new file mode 100644
index 00000000..3f4a3407
--- /dev/null
+++ b/src/test/resources/checks/v2/parameters/OAR020/with-param.yaml
@@ -0,0 +1,15 @@
+swagger: "2.0"
+info:
+ title: Swagger Petstore
+ version: "1.0.0"
+paths:
+ /examples/{id}:
+ get:
+ responses:
+ 200:
+ description: OK
+ /examples/{id}/items/{id}:
+ get:
+ responses:
+ 200:
+ description: OK
diff --git a/src/test/resources/checks/v3/operations/OAR104/plain.yaml b/src/test/resources/checks/v3/operations/OAR104/plain.yaml
index 2d5e946b..521a52ca 100644
--- a/src/test/resources/checks/v3/operations/OAR104/plain.yaml
+++ b/src/test/resources/checks/v3/operations/OAR104/plain.yaml
@@ -9,17 +9,17 @@ paths:
'200':
description: Ok
/resources/me/cars:
- post:
+ post:
responses:
'200':
description: Ok
- /resources/get:
+ /resources/get:
post: # Noncompliant {{OAR104: Operation not recommended for resource path: resources/get}}
responses:
'200':
description: Ok
- /resources/hola:
- post: # Noncompliant {{OAR104: Operation not recommended for resource path: resources/hola}}
+ /resources/hello:
+ post: # Noncompliant {{OAR104: Operation not recommended for resource path: resources/hello}}
responses:
'200':
description: Ok
@@ -28,8 +28,13 @@ paths:
responses:
'200':
description: Ok
- /resources/me:
- post:
+ /resources/me:
+ post:
+ responses:
+ '200':
+ description: Ok
+ /resources/search:
+ post:
responses:
'200':
description: Ok
\ No newline at end of file
diff --git a/src/test/resources/checks/v3/parameters/OAR019/with-param.json b/src/test/resources/checks/v3/parameters/OAR019/with-param.json
new file mode 100644
index 00000000..1097b436
--- /dev/null
+++ b/src/test/resources/checks/v3/parameters/OAR019/with-param.json
@@ -0,0 +1,27 @@
+{
+ "openapi": "3.0.0",
+ "info": {
+ "title": "Swagger Petstore",
+ "version": "1.0.0"
+ },
+ "paths": {
+ "/examples/{id}": {
+ "get": {
+ "responses": {
+ "200": {
+ "description": "OK"
+ }
+ }
+ }
+ },
+ "/examples/{id}/items/{id}": {
+ "get": {
+ "responses": {
+ "200": {
+ "description": "OK"
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/test/resources/checks/v3/parameters/OAR019/with-param.yaml b/src/test/resources/checks/v3/parameters/OAR019/with-param.yaml
new file mode 100644
index 00000000..8b012753
--- /dev/null
+++ b/src/test/resources/checks/v3/parameters/OAR019/with-param.yaml
@@ -0,0 +1,15 @@
+openapi: 3.0.0
+info:
+ title: Swagger Petstore
+ version: 1.0.0
+paths:
+ /examples/{id}:
+ get:
+ responses:
+ '200':
+ description: OK
+ /examples/{id}/items/{id}:
+ get:
+ responses:
+ '200':
+ description: OK
\ No newline at end of file
diff --git a/src/test/resources/checks/v3/parameters/OAR020/with-param.json b/src/test/resources/checks/v3/parameters/OAR020/with-param.json
new file mode 100644
index 00000000..1097b436
--- /dev/null
+++ b/src/test/resources/checks/v3/parameters/OAR020/with-param.json
@@ -0,0 +1,27 @@
+{
+ "openapi": "3.0.0",
+ "info": {
+ "title": "Swagger Petstore",
+ "version": "1.0.0"
+ },
+ "paths": {
+ "/examples/{id}": {
+ "get": {
+ "responses": {
+ "200": {
+ "description": "OK"
+ }
+ }
+ }
+ },
+ "/examples/{id}/items/{id}": {
+ "get": {
+ "responses": {
+ "200": {
+ "description": "OK"
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/test/resources/checks/v3/parameters/OAR020/with-param.yaml b/src/test/resources/checks/v3/parameters/OAR020/with-param.yaml
new file mode 100644
index 00000000..8b012753
--- /dev/null
+++ b/src/test/resources/checks/v3/parameters/OAR020/with-param.yaml
@@ -0,0 +1,15 @@
+openapi: 3.0.0
+info:
+ title: Swagger Petstore
+ version: 1.0.0
+paths:
+ /examples/{id}:
+ get:
+ responses:
+ '200':
+ description: OK
+ /examples/{id}/items/{id}:
+ get:
+ responses:
+ '200':
+ description: OK
\ No newline at end of file