Skip to content

Commit 26d9372

Browse files
committed
#36 Add route path exclusion parameter support
1 parent 5078dc9 commit 26d9372

File tree

11 files changed

+254
-2
lines changed

11 files changed

+254
-2
lines changed

README.md

+15
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,21 @@ Example command:
173173
$ java -jar swagger-brake.jar --old-api=/home/user/something.yaml --new-api=/home/user/something_v2.yaml --beta-api-extension-name=x-custom-beta-attributes
174174
```
175175

176+
### Excluding specific paths from the scan
177+
There might be a need to exclude specific APIs from the scan completely.
178+
179+
There's a parameter `--excluded-paths` where you can provide a list of paths you want to be excluded
180+
from the check. The paths can be separated by commas.
181+
182+
The exclusion works based on prefix-matching, so in case you'd like to exclude all paths that
183+
starts with `/auth` for example, you can pass it as a parameter.
184+
185+
Example command:
186+
187+
```bash
188+
$ java -jar swagger-brake.jar --old-api=/home/user/something.yaml --new-api=/home/user/something_v2.yaml --excluded-paths=/auth
189+
```
190+
176191
## Building
177192
The application is using Gradle as a build system and building it can be done
178193
by executing the following command:

swagger-brake-cli/src/main/java/io/redskap/swagger/brake/cli/options/CliOption.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ public enum CliOption {
2323
DEPRECATED_API_DELETION_ALLOWED("deprecated-api-deletion-allowed"),
2424

2525
API_FILENAME("api-filename"),
26-
BETA_API_EXTENSION_NAME("beta-api-extension-name");
26+
BETA_API_EXTENSION_NAME("beta-api-extension-name"),
27+
EXCLUDED_PATHS("excluded-paths");
2728

2829

2930
private final String cliOptionName;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package io.redskap.swagger.brake.cli.options.handler;
2+
3+
import static java.util.stream.Collectors.toSet;
4+
5+
import java.util.Arrays;
6+
7+
import io.redskap.swagger.brake.cli.options.CliOption;
8+
import io.redskap.swagger.brake.runner.Options;
9+
import lombok.extern.slf4j.Slf4j;
10+
import org.apache.commons.lang3.StringUtils;
11+
import org.springframework.stereotype.Component;
12+
13+
@Component
14+
@Slf4j
15+
public class ExcludedPathsHandler implements CliOptionHandler {
16+
@Override
17+
public void handle(String optionValue, Options options) {
18+
if (StringUtils.isNotBlank(optionValue)) {
19+
log.debug("Handling {} parameter with value {}", getHandledCliOption(), optionValue);
20+
String[] excludedPaths = optionValue.split(",");
21+
log.debug("Splitted values are {}", excludedPaths);
22+
options.setExcludedPaths(Arrays.stream(excludedPaths).collect(toSet()));
23+
}
24+
25+
}
26+
27+
@Override
28+
public CliOption getHandledCliOption() {
29+
return CliOption.EXCLUDED_PATHS;
30+
}
31+
32+
@Override
33+
public String getHelpMessage() {
34+
return "Specifies paths that should be excluded from the check. Multiple values can be provided by using comma.";
35+
}
36+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package io.redskap.swagger.brake.cli.options.handler;
2+
3+
import static org.assertj.core.api.Assertions.assertThat;
4+
5+
import com.google.common.base.Joiner;
6+
import io.redskap.swagger.brake.runner.Options;
7+
import org.junit.Test;
8+
9+
public class ExcludedPathsHandlerTest {
10+
private ExcludedPathsHandler underTest = new ExcludedPathsHandler();
11+
12+
@Test
13+
public void testHandleShouldNotSetValueWhenNullIsPassed() {
14+
// given
15+
Options options = new Options();
16+
// when
17+
underTest.handle(null, options);
18+
// then
19+
assertThat(options.getExcludedPaths()).isEmpty();
20+
}
21+
22+
@Test
23+
public void testHandleShouldNotSetValueWhenBlankIsPassed() {
24+
// given
25+
Options options = new Options();
26+
// when
27+
underTest.handle(" ", options);
28+
// then
29+
assertThat(options.getExcludedPaths()).isEmpty();
30+
}
31+
32+
@Test
33+
public void testHandleShouldSetSingleValue() {
34+
// given
35+
Options options = new Options();
36+
// when
37+
String path1 = "/test/path";
38+
underTest.handle(path1, options);
39+
// then
40+
assertThat(options.getExcludedPaths()).containsExactlyInAnyOrder(path1);
41+
}
42+
43+
@Test
44+
public void testHandleShouldSetMultiValue() {
45+
// given
46+
Options options = new Options();
47+
// when
48+
String path1 = "/test/path";
49+
String path2 = "/test2/path2";
50+
underTest.handle(Joiner.on(",").join(path1, path2), options);
51+
// then
52+
assertThat(options.getExcludedPaths()).containsExactlyInAnyOrder(path1, path2);
53+
}
54+
}
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
package io.redskap.swagger.brake.core;
22

3+
import java.util.Collections;
4+
import java.util.Set;
5+
36
import lombok.Data;
47

58
@Data
69
public class CheckerOptions {
710
private boolean deprecatedApiDeletionAllowed = true;
811
private String betaApiExtensionName = "x-beta-api";
12+
private Set<String> excludedPaths = Collections.emptySet();
913
}
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,35 @@
11
package io.redskap.swagger.brake.core.rule;
22

3+
import java.util.Set;
4+
5+
import io.redskap.swagger.brake.core.CheckerOptions;
6+
import io.redskap.swagger.brake.core.CheckerOptionsProvider;
37
import io.redskap.swagger.brake.core.model.Path;
8+
import io.redskap.swagger.brake.core.util.PathNormalizer;
9+
import lombok.RequiredArgsConstructor;
410
import org.springframework.stereotype.Component;
511

612
@Component
13+
@RequiredArgsConstructor
714
public class PathSkipper {
15+
private final CheckerOptionsProvider checkerOptionsProvider;
16+
817
public boolean shouldSkip(Path path) {
9-
return path.isBetaApi();
18+
return path.isBetaApi() || isPathExcluded(path);
19+
}
20+
21+
private boolean isPathExcluded(Path path) {
22+
CheckerOptions checkerOptions = checkerOptionsProvider.get();
23+
if (checkerOptions != null) {
24+
Set<String> excludedPaths = checkerOptions.getExcludedPaths();
25+
for (String excludedPath : excludedPaths) {
26+
String normalizedExcludedPath = PathNormalizer.normalizePathSlashes(excludedPath);
27+
String normalizedPath = PathNormalizer.normalizePathSlashes(path.getPath());
28+
if (normalizedPath.startsWith(normalizedExcludedPath)) {
29+
return true;
30+
}
31+
}
32+
}
33+
return false;
1034
}
1135
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package io.redskap.swagger.brake.core.util;
2+
3+
import org.apache.commons.lang3.StringUtils;
4+
5+
public abstract class PathNormalizer {
6+
public static String normalizePathSlashes(String path) {
7+
if (StringUtils.isBlank(path)) {
8+
throw new IllegalArgumentException("path cannot be blank");
9+
}
10+
String result = path;
11+
if (!result.startsWith("/")) {
12+
result = "/" + result;
13+
}
14+
if (result.endsWith("/")) {
15+
result = result.substring(0, result.length() - 1);
16+
}
17+
return result;
18+
}
19+
}

swagger-brake/src/main/java/io/redskap/swagger/brake/runner/CheckerOptionsFactory.java

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ public CheckerOptions create(Options options) {
1616
CheckerOptions checkerOptions = new CheckerOptions();
1717
checkerOptions.setDeprecatedApiDeletionAllowed(isDeprecatedApiDeletionAllowed(options));
1818
checkerOptions.setBetaApiExtensionName(getBetaApiExtensionName(checkerOptions.getBetaApiExtensionName(), options));
19+
checkerOptions.setExcludedPaths(options.getExcludedPaths());
1920
return checkerOptions;
2021
}
2122

swagger-brake/src/main/java/io/redskap/swagger/brake/runner/Options.java

+1
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,5 @@ public class Options {
2525

2626
private String apiFilename;
2727
private String betaApiExtensionName;
28+
private Set<String> excludedPaths = Collections.emptySet();
2829
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package io.redskap.swagger.brake.core.util;
2+
3+
import static org.assertj.core.api.Assertions.assertThat;
4+
5+
import org.junit.Test;
6+
7+
public class PathNormalizerTest {
8+
9+
@Test(expected = IllegalArgumentException.class)
10+
public void testNormalizePathSlashesThrowsExceptionWhenNullGiven() {
11+
// given
12+
// when
13+
PathNormalizer.normalizePathSlashes(null);
14+
// then exception thrown
15+
}
16+
17+
@Test(expected = IllegalArgumentException.class)
18+
public void testNormalizePathSlashesThrowsExceptionWhenBlankGiven() {
19+
// given
20+
// when
21+
PathNormalizer.normalizePathSlashes(" ");
22+
// then exception thrown
23+
}
24+
25+
@Test
26+
public void testNormalizePathSlashesAddsNecessaryLeadingSlashesIfMissing() {
27+
// given
28+
// when
29+
String result = PathNormalizer.normalizePathSlashes("test/path");
30+
// then
31+
assertThat(result).isEqualTo("/test/path");
32+
}
33+
34+
@Test
35+
public void testNormalizePathSlashesRemovesUnnecessaryTrailingSlashesIfAny() {
36+
// given
37+
// when
38+
String result = PathNormalizer.normalizePathSlashes("/test/path/");
39+
// then
40+
assertThat(result).isEqualTo("/test/path");
41+
}
42+
43+
@Test
44+
public void testNormalizePathSlashesRemovesAndAddsSlashesWhereNeeded() {
45+
// given
46+
// when
47+
String result = PathNormalizer.normalizePathSlashes("test/path/");
48+
// then
49+
assertThat(result).isEqualTo("/test/path");
50+
}
51+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package io.redskap.swagger.brake.integration.nobreakingchange;
2+
3+
import static org.assertj.core.api.Assertions.assertThat;
4+
5+
import java.util.Collection;
6+
7+
import com.google.common.collect.Sets;
8+
import io.redskap.swagger.brake.core.BreakingChange;
9+
import io.redskap.swagger.brake.integration.AbstractSwaggerBrakeIntTest;
10+
import io.redskap.swagger.brake.runner.Options;
11+
import org.junit.Test;
12+
import org.junit.runner.RunWith;
13+
import org.springframework.test.context.junit4.SpringRunner;
14+
15+
@RunWith(SpringRunner.class)
16+
public class ExcludedPathsIntTest extends AbstractSwaggerBrakeIntTest {
17+
@Test
18+
public void testExcludedPathsWorksCorrectlyForExactMatch() {
19+
// given
20+
String oldApiPath = "request/parameterdeleted/petstore.yaml";
21+
String newApiPath = "request/parameterdeleted/petstore_v2.yaml";
22+
Options options = new Options();
23+
options.setOldApiPath(oldApiPath);
24+
options.setNewApiPath(newApiPath);
25+
options.setExcludedPaths(Sets.newHashSet("/pet/findByStatus"));
26+
// when
27+
Collection<BreakingChange> result = execute(options);
28+
// then
29+
assertThat(result).isEmpty();
30+
}
31+
32+
@Test
33+
public void testExcludedPathsWorksCorrectlyForStartingMatch() {
34+
// given
35+
String oldApiPath = "request/parameterdeleted/petstore.yaml";
36+
String newApiPath = "request/parameterdeleted/petstore_v2.yaml";
37+
Options options = new Options();
38+
options.setOldApiPath(oldApiPath);
39+
options.setNewApiPath(newApiPath);
40+
options.setExcludedPaths(Sets.newHashSet("/pet"));
41+
// when
42+
Collection<BreakingChange> result = execute(options);
43+
// then
44+
assertThat(result).isEmpty();
45+
}
46+
}

0 commit comments

Comments
 (0)