Skip to content

Commit

Permalink
Add csv output format to validate command
Browse files Browse the repository at this point in the history
You can now pass `--validation-format text|csv` to the validate
command to get CSV or text output. Text continues to be the default
when not specified.
  • Loading branch information
mtdowling committed Feb 8, 2024
1 parent 9b7be40 commit 5992fb8
Show file tree
Hide file tree
Showing 4 changed files with 143 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,13 @@ public Model build() {
: new ValidatorOptions();
resolveMinSeverity(standardOptions, validatorOptions);

// Determine how to format the output, whether it's text (the default) or CSV.
// Only some commands (like validate) actually let you customize the output format, so assume a default.
ValidationEventFormatOptions formatOptions = arguments.hasReceiver(ValidationEventFormatOptions.class)
? arguments.getReceiver(ValidationEventFormatOptions.class)
: new ValidationEventFormatOptions();
ValidationEventFormatOptions.ValidationFormat validationOutputFormat = formatOptions.format();

ClassLoader classLoader = env.classLoader();
ColorFormatter colors = env.colors();
CliPrinter stderr = env.stderr();
Expand Down Expand Up @@ -180,13 +187,15 @@ public Model build() {
.titleLabel(titleLabel, titleLabelStyles)
.build();

validationOutputFormat.beginPrinting(validationPrinter);
for (ValidationEvent event : sortedEvents) {
// Only log events that are >= --severity. Note that setting --quiet inherently
// configures events to need to be >= DANGER. Also filter using --show-validators and --hide-validators.
if (validatorOptions.isVisible(event)) {
validationPrinter.println(formatter.format(event));
validationOutputFormat.print(validationPrinter, formatter, event);
}
}
validationOutputFormat.endPrinting(validationPrinter);

env.flush();
// Note: disabling validation will still show a summary of failures if the model can't be loaded.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ public int execute(Arguments arguments, Env env) {
arguments.addReceiver(new DiscoveryOptions());
arguments.addReceiver(new ValidatorOptions());
arguments.addReceiver(new BuildOptions());
arguments.addReceiver(new ValidationEventFormatOptions());

CommandAction action = HelpActionWrapper.fromCommand(
this,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/*
* 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.function.Consumer;
import software.amazon.smithy.cli.ArgumentReceiver;
import software.amazon.smithy.cli.CliError;
import software.amazon.smithy.cli.CliPrinter;
import software.amazon.smithy.cli.HelpPrinter;
import software.amazon.smithy.model.shapes.ShapeId;
import software.amazon.smithy.model.validation.ValidationEvent;
import software.amazon.smithy.model.validation.ValidationEventFormatter;

final class ValidationEventFormatOptions implements ArgumentReceiver {

private static final String VALIDATION_FORMAT = "--validation-format";

enum ValidationFormat {
TEXT {
@Override
void print(CliPrinter printer, ValidationEventFormatter formatter, ValidationEvent event) {
printer.println(formatter.format(event));
}
},

CSV {
@Override
void beginPrinting(CliPrinter printer) {
printer.println("severity,id,shape,file,message,hint,suppressionReason");
}

@Override
void print(CliPrinter printer, ValidationEventFormatter formatter, ValidationEvent event) {
printer.println(
String.format("\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\"",
event.getSeverity().toString(),
formatCsv(event.getId()),
event.getShapeId().map(ShapeId::toString).orElse(""),
formatCsv(
event.getSourceLocation().getFilename()
+ ':' + event.getSourceLocation().getLine()
+ ':' + event.getSourceLocation().getColumn()),
formatCsv(event.getMessage()),
formatCsv(event.getHint().orElse("")),
formatCsv(event.getSuppressionReason().orElse(""))));
}
};

void beginPrinting(CliPrinter printer) {}

abstract void print(CliPrinter printer, ValidationEventFormatter formatter, ValidationEvent event);

void endPrinting(CliPrinter printer) {}

private static String formatCsv(String value) {
// Replace DQUOTE with DQUOTEDQUOTE and escape newlines.
return value.replace("\"", "\"\"").replace("\n", "\\n");
}
}

private ValidationFormat format = ValidationFormat.TEXT;

@Override
public void registerHelp(HelpPrinter printer) {
printer.param(VALIDATION_FORMAT, null, "text|csv",
"Specifies the format to write validation events (text or csv). Defaults to text.");
}

@Override
public Consumer<String> testParameter(String name) {
if (name.equals("--validation-format")) {
return s -> {
switch (s) {
case "csv":
format(ValidationFormat.CSV);
break;
case "text":
format(ValidationFormat.TEXT);
break;
default:
throw new CliError("Unexpected " + VALIDATION_FORMAT + ": `" + s + "`");
}
};
}
return null;
}

void format(ValidationFormat format) {
this.format = format;
}

ValidationFormat format() {
return format;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
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;
Expand Down Expand Up @@ -216,4 +215,38 @@ public void canHideEventsById() throws Exception {
assertThat(result.stdout(), not(containsString("EmitDangers")));
assertThat(result.stdout(), containsString("HttpLabelTrait"));
}

@Test
public void canOutputCsv() throws Exception {
Path validationEventsModel = Paths.get(getClass().getResource("validation-events.smithy").toURI());
CliUtils.Result result = CliUtils.runSmithy("validate", "--validation-format", "csv",
validationEventsModel.toString());

assertThat(result.code(), not(0));
assertThat(result.stdout(), containsString("suppressionReason"));
assertThat(result.stdout(), containsString("EmitWarnings"));
assertThat(result.stdout(), containsString("EmitDangers"));
assertThat(result.stdout(), containsString("HttpLabelTrait"));
}

@Test
public void canOutputText() throws Exception {
Path validationEventsModel = Paths.get(getClass().getResource("validation-events.smithy").toURI());
CliUtils.Result result = CliUtils.runSmithy("validate", "--validation-format", "text",
validationEventsModel.toString());

assertThat(result.code(), not(0));
assertThat(result.stdout(), not(containsString("suppressionReason")));
assertThat(result.stdout(), containsString("EmitWarnings"));
assertThat(result.stdout(), containsString("EmitDangers"));
assertThat(result.stdout(), containsString("HttpLabelTrait"));
}

@Test
public void outputFormatMustBeValid() {
CliUtils.Result result = CliUtils.runSmithy("validate", "--validation-format", "HELLO");

assertThat(result.code(), not(0));
assertThat(result.stderr(), containsString("Unexpected --validation-format: `HELLO`"));
}
}

0 comments on commit 5992fb8

Please sign in to comment.