diff --git a/smithy-cli/src/main/java/software/amazon/smithy/cli/commands/BuildCommand.java b/smithy-cli/src/main/java/software/amazon/smithy/cli/commands/BuildCommand.java index 25508cdfda1..0ed1e38b04d 100644 --- a/smithy-cli/src/main/java/software/amazon/smithy/cli/commands/BuildCommand.java +++ b/smithy-cli/src/main/java/software/amazon/smithy/cli/commands/BuildCommand.java @@ -72,7 +72,7 @@ public String getSummary() { public int execute(Arguments arguments, Env env) { arguments.addReceiver(new ConfigOptions()); arguments.addReceiver(new DiscoveryOptions()); - arguments.addReceiver(new SeverityOption()); + arguments.addReceiver(new ValidatorOptions()); arguments.addReceiver(new BuildOptions()); arguments.addReceiver(new Options()); diff --git a/smithy-cli/src/main/java/software/amazon/smithy/cli/commands/DiffCommand.java b/smithy-cli/src/main/java/software/amazon/smithy/cli/commands/DiffCommand.java index c0a7c10f311..5cb3db66a69 100644 --- a/smithy-cli/src/main/java/software/amazon/smithy/cli/commands/DiffCommand.java +++ b/smithy-cli/src/main/java/software/amazon/smithy/cli/commands/DiffCommand.java @@ -65,7 +65,7 @@ public String getSummary() { @Override public int execute(Arguments arguments, Env env) { arguments.addReceiver(new ConfigOptions()); - arguments.addReceiver(new SeverityOption()); + arguments.addReceiver(new ValidatorOptions()); arguments.addReceiver(new BuildOptions()); arguments.addReceiver(new Options()); arguments.getReceiver(BuildOptions.class).noPositionalArguments(true); diff --git a/smithy-cli/src/main/java/software/amazon/smithy/cli/commands/ModelBuilder.java b/smithy-cli/src/main/java/software/amazon/smithy/cli/commands/ModelBuilder.java index b9020bee8c9..778961e894c 100644 --- a/smithy-cli/src/main/java/software/amazon/smithy/cli/commands/ModelBuilder.java +++ b/smithy-cli/src/main/java/software/amazon/smithy/cli/commands/ModelBuilder.java @@ -123,7 +123,13 @@ public Model build() { StandardOptions standardOptions = arguments.getReceiver(StandardOptions.class); BuildOptions buildOptions = arguments.getReceiver(BuildOptions.class); - Severity minSeverity = resolveMinSeverity(standardOptions); + + // Resolve validator options and severity. + ValidatorOptions validatorOption = arguments.hasReceiver(ValidatorOptions.class) + ? arguments.getReceiver(ValidatorOptions.class) : new ValidatorOptions(); + Severity minSeverity = resolveMinSeverity(standardOptions, validatorOption); + validatorOption.severity(minSeverity); + ClassLoader classLoader = env.classLoader(); ColorFormatter colors = env.colors(); CliPrinter stderr = env.stderr(); @@ -176,8 +182,8 @@ public Model build() { for (ValidationEvent event : sortedEvents) { // Only log events that are >= --severity. Note that setting --quiet inherently - // configures events to need to be >= DANGER. - if (event.getSeverity().ordinal() >= minSeverity.ordinal()) { + // configures events to need to be >= DANGER. Also filter using --show-validators and --hide-validators. + if (validatorOption.isVisible(event)) { validationPrinter.println(formatter.format(event)); } } @@ -274,13 +280,11 @@ private static void discoverModelsWithClasspath(String rawClasspath, ModelAssemb } // Determine a default severity if one wasn't given, by inspecting if there is a --severity option. - private Severity resolveMinSeverity(StandardOptions standardOptions) { + private Severity resolveMinSeverity(StandardOptions standardOptions, ValidatorOptions validatorOption) { if (severity != null) { return severity; - } else if (arguments.hasReceiver(SeverityOption.class)) { - return arguments.getReceiver(SeverityOption.class).severity(standardOptions); } else { - return Severity.WARNING; + return validatorOption.severity(standardOptions); } } diff --git a/smithy-cli/src/main/java/software/amazon/smithy/cli/commands/SeverityOption.java b/smithy-cli/src/main/java/software/amazon/smithy/cli/commands/SeverityOption.java deleted file mode 100644 index febca698a33..00000000000 --- a/smithy-cli/src/main/java/software/amazon/smithy/cli/commands/SeverityOption.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -package software.amazon.smithy.cli.commands; - -import java.util.function.Consumer; -import software.amazon.smithy.cli.ArgumentReceiver; -import software.amazon.smithy.cli.CliError; -import software.amazon.smithy.cli.HelpPrinter; -import software.amazon.smithy.cli.StandardOptions; -import software.amazon.smithy.model.validation.Severity; - -/** - * Add the --severity option. - */ -final class SeverityOption implements ArgumentReceiver { - - static final String SEVERITY = "--severity"; - - private Severity severity; - - @Override - public void registerHelp(HelpPrinter printer) { - printer.param(SEVERITY, null, "SEVERITY", "Set the minimum reported validation severity (one of NOTE, " - + "WARNING [default setting], DANGER, ERROR)."); - } - - @Override - public Consumer testParameter(String name) { - if (SEVERITY.equals(name)) { - return value -> { - severity(Severity.fromString(value).orElseThrow(() -> { - return new CliError("Invalid severity level: " + value); - })); - }; - } - return null; - } - - /** - * Set the severity. - * - * @param severity Severity to set. - */ - void severity(Severity severity) { - this.severity = severity; - } - - /** - * Get the severity level, taking into account standard options that affect the default. - * - * @param options Standard options to query if no severity is explicitly set. - * @return Returns the resolved severity option. - */ - Severity severity(StandardOptions options) { - if (severity != null) { - return severity; - } else if (options.quiet()) { - return Severity.DANGER; - } else { - return Severity.WARNING; - } - } -} diff --git a/smithy-cli/src/main/java/software/amazon/smithy/cli/commands/ValidateCommand.java b/smithy-cli/src/main/java/software/amazon/smithy/cli/commands/ValidateCommand.java index 4cc3f29608b..34c7ea5ea52 100644 --- a/smithy-cli/src/main/java/software/amazon/smithy/cli/commands/ValidateCommand.java +++ b/smithy-cli/src/main/java/software/amazon/smithy/cli/commands/ValidateCommand.java @@ -46,7 +46,7 @@ public String getSummary() { public int execute(Arguments arguments, Env env) { arguments.addReceiver(new ConfigOptions()); arguments.addReceiver(new DiscoveryOptions()); - arguments.addReceiver(new SeverityOption()); + arguments.addReceiver(new ValidatorOptions()); arguments.addReceiver(new BuildOptions()); CommandAction action = HelpActionWrapper.fromCommand( diff --git a/smithy-cli/src/main/java/software/amazon/smithy/cli/commands/ValidatorOptions.java b/smithy-cli/src/main/java/software/amazon/smithy/cli/commands/ValidatorOptions.java new file mode 100644 index 00000000000..4a286905ef4 --- /dev/null +++ b/smithy-cli/src/main/java/software/amazon/smithy/cli/commands/ValidatorOptions.java @@ -0,0 +1,177 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.cli.commands; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.function.Consumer; +import software.amazon.smithy.cli.ArgumentReceiver; +import software.amazon.smithy.cli.CliError; +import software.amazon.smithy.cli.HelpPrinter; +import software.amazon.smithy.cli.StandardOptions; +import software.amazon.smithy.model.validation.Severity; +import software.amazon.smithy.model.validation.ValidationEvent; + +/** + * Add validation specific options. + */ +final class ValidatorOptions implements ArgumentReceiver { + + static final String SEVERITY = "--severity"; + static final String SHOW_VALIDATORS = "--show-validators"; + static final String HIDE_VALIDATORS = "--hide-validators"; + + private Severity severity; + private List showValidators = Collections.emptyList(); + private List hideValidators = Collections.emptyList(); + + @Override + public void registerHelp(HelpPrinter printer) { + printer.param(SEVERITY, null, "SEVERITY", "Set the minimum reported validation severity (one of NOTE, " + + "WARNING [default setting], DANGER, ERROR)."); + printer.param(SHOW_VALIDATORS, null, "VALIDATORS", "Comma-separated list of hierarchical validation event " + + "IDs to show in the output of the command, " + + "hiding the rest."); + printer.param(HIDE_VALIDATORS, null, "VALIDATORS", "Comma-separated list of hierarchical validation event " + + "IDs to hide in the output of the command, " + + "showing the rest."); + } + + @Override + public Consumer testParameter(String name) { + switch (name) { + case SEVERITY: + return value -> { + severity(Severity.fromString(value).orElseThrow(() -> { + return new CliError("Invalid severity level: " + value); + })); + }; + case SHOW_VALIDATORS: + return value -> { + if (!hideValidators.isEmpty()) { + throw new CliError(SHOW_VALIDATORS + " and " + HIDE_VALIDATORS + " are mutually exclusive"); + } + showValidators(parseIds(value)); + }; + case HIDE_VALIDATORS: + return value -> { + if (!showValidators.isEmpty()) { + throw new CliError(SHOW_VALIDATORS + " and " + HIDE_VALIDATORS + " are mutually exclusive"); + } + hideValidators(parseIds(value)); + }; + default: + return null; + } + } + + private List parseIds(String value) { + List result = new ArrayList<>(); + for (String id : value.split("\\s*,\\s*")) { + id = id.trim(); + if (id.isEmpty()) { + throw new CliError("Invalid validation event ID"); + } + result.add(id); + } + return result; + } + + /** + * Set the severity. + * + * @param severity Severity to set. + */ + void severity(Severity severity) { + this.severity = severity; + } + + /** + * Get the severity level, taking into account standard options that affect the default. + * + * @param options Standard options to query if no severity is explicitly set. + * @return Returns the resolved severity option. + */ + Severity severity(StandardOptions options) { + if (severity != null) { + return severity; + } else if (options.quiet()) { + return Severity.DANGER; + } else { + return Severity.WARNING; + } + } + + /** + * Set the list of validators to show, but hide everything else. + * + * @param validators Validators to show. + */ + void showValidators(List validators) { + this.showValidators = validators; + } + + /** + * Get the list of validators to show. + * + * @return Validator event IDs. + */ + List showValidators() { + return showValidators; + } + + /** + * Get the list of validators to hide. + * + * @return Validator event IDs. + */ + List hideValidators(String csv) { + return hideValidators; + } + + /** + * Set the list of validators to hide. + * + * @param validators Validators to hide. + */ + void hideValidators(List validators) { + this.hideValidators = validators; + } + + /** + * Check if the given validation event matches the show/hide settings. + * + *

A severity must be set before calling this method. + * + * @param event Event to check. + * @return Return true if the event can be seen. + */ + boolean isVisible(ValidationEvent event) { + if (event.getSeverity().ordinal() < severity.ordinal()) { + return false; + } + + if (!showValidators.isEmpty()) { + for (String show : showValidators) { + if (event.containsId(show)) { + return true; + } + } + return false; + } + + if (!hideValidators.isEmpty()) { + for (String hide : hideValidators) { + if (event.containsId(hide)) { + return false; + } + } + } + + return true; + } +} diff --git a/smithy-cli/src/test/java/software/amazon/smithy/cli/commands/ValidateCommandTest.java b/smithy-cli/src/test/java/software/amazon/smithy/cli/commands/ValidateCommandTest.java index b3f2fcf3274..942ead46723 100644 --- a/smithy-cli/src/test/java/software/amazon/smithy/cli/commands/ValidateCommandTest.java +++ b/smithy-cli/src/test/java/software/amazon/smithy/cli/commands/ValidateCommandTest.java @@ -18,6 +18,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; import java.net.URISyntaxException; @@ -151,4 +152,68 @@ public void validatesSeverity() { assertThat(result.code(), not(0)); assertThat(result.stderr(), containsString("Invalid severity")); } + + @Test + public void showAndHideAreExclusive() { + CliUtils.Result result = CliUtils.runSmithy("validate", "--show-validators", "X", "--hide-validators", "Y"); + + assertThat(result.code(), not(0)); + assertThat(result.stderr(), containsString("--show-validators and --hide-validators are mutually exclusive")); + } + + @Test + public void hideAndShowAreExclusive() { + CliUtils.Result result = CliUtils.runSmithy("validate", "--hide-validators", "Y", "--show-validators", "X"); + + assertThat(result.code(), not(0)); + assertThat(result.stderr(), containsString("--show-validators and --hide-validators are mutually exclusive")); + } + + @Test + public void validatesEventIdsAreNotEmpty() { + CliUtils.Result result = CliUtils.runSmithy("validate", "--show-validators", "Foo,,Bar"); + + assertThat(result.code(), not(0)); + assertThat(result.stderr(), containsString("Invalid validation event ID")); + } + + @Test + public void validatesEventIdsAreNotBlank() { + CliUtils.Result result = CliUtils.runSmithy("validate", "--show-validators", "Foo, ,Bar"); + + assertThat(result.code(), not(0)); + assertThat(result.stderr(), containsString("Invalid validation event ID")); + } + + @Test + public void validatesEventIdsAreNotLeadingEmpty() { + CliUtils.Result result = CliUtils.runSmithy("validate", "--show-validators", ",Bar"); + + assertThat(result.code(), not(0)); + assertThat(result.stderr(), containsString("Invalid validation event ID")); + } + + @Test + public void canShowEventsById() throws Exception { + Path validationEventsModel = Paths.get(getClass().getResource("validation-events.smithy").toURI()); + CliUtils.Result result = CliUtils.runSmithy("validate", "--debug", "--show-validators", "EmitWarnings", + validationEventsModel.toString()); + + assertThat(result.code(), not(0)); + assertThat(result.stdout(), containsString("EmitWarnings")); + assertThat(result.stdout(), not(containsString("EmitDangers"))); + assertThat(result.stdout(), not(containsString("HttpLabelTrait"))); + } + + @Test + public void canHideEventsById() throws Exception { + Path validationEventsModel = Paths.get(getClass().getResource("validation-events.smithy").toURI()); + CliUtils.Result result = CliUtils.runSmithy("validate", "--debug", "--hide-validators", "EmitDangers", + validationEventsModel.toString()); + + assertThat(result.code(), not(0)); + assertThat(result.stdout(), containsString("EmitWarnings")); + assertThat(result.stdout(), not(containsString("EmitDangers"))); + assertThat(result.stdout(), containsString("HttpLabelTrait")); + } }