Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add select command #430

Merged
merged 1 commit into from
May 11, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion smithy-cli/src/main/java/software/amazon/smithy/cli/Cli.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2020 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.
Expand Down Expand Up @@ -170,6 +170,24 @@ public static void setStderr(Consumer<String> printer) {
stderr = printer;
}

/**
* Gets the stdout consumer.
*
* @return Returns the stdout consumer.
*/
public static Consumer<String> getStdout() {
return stdout;
}

/**
* Gets the stderr consumer.
*
* @return Returns the stderr consumer.
*/
public static Consumer<String> getStderr() {
return stderr;
}

/**
* Write a line of text to the configured STDOUT.
*
Expand Down
23 changes: 15 additions & 8 deletions smithy-cli/src/main/java/software/amazon/smithy/cli/Colors.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2020 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.
Expand All @@ -15,6 +15,7 @@

package software.amazon.smithy.cli;

import java.util.function.Consumer;
import software.amazon.smithy.utils.SmithyUnstableApi;

/**
Expand Down Expand Up @@ -62,11 +63,7 @@ public enum Colors {
* @param message Message to print.
*/
public void out(String message) {
if (Cli.useAnsiColors) {
Cli.stdout(format(message));
} else {
Cli.stdout(message);
}
write(Cli.getStdout(), message);
}

/**
Expand All @@ -75,10 +72,20 @@ public void out(String message) {
* @param message Message to print.
*/
public void err(String message) {
write(Cli.getStderr(), message);
}

/**
* Writes the color output to the given consumer.
*
* @param consumer Consume to invoke.
* @param message Message to write.
*/
public void write(Consumer<String> consumer, String message) {
if (Cli.useAnsiColors) {
Cli.stderr(format(message));
consumer.accept(format(message));
} else {
Cli.stderr(message);
consumer.accept(message);
}
}

Expand Down
11 changes: 5 additions & 6 deletions smithy-cli/src/main/java/software/amazon/smithy/cli/Parser.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2020 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.
Expand Down Expand Up @@ -246,18 +246,17 @@ public static final class Builder implements SmithyBuilder<Parser> {
private String positionalHelp;
private List<Argument> arguments = new ArrayList<>();

private Builder() {
@Override
public Parser build() {
// Always include --help, --debug, --stacktrace, and --no-color options; and --logging X.
// This is done during build to move them to the end. Note that this could duplicate
// arguments if a builder is reused, but that seems highly unlikely.
option(Cli.HELP, "-h", "Print this help");
option(Cli.DEBUG, "Display debug information");
option(Cli.STACKTRACE, "Display a stacktrace on error");
option(Cli.NO_COLOR, "Explicitly disable ANSI colors");
option(Cli.FORCE_COLOR, "Explicitly enables ANSI colors");
parameter(Cli.LOGGING, "Sets the log level to one of OFF, SEVERE, WARNING, INFO, FINE, ALL");
}

@Override
public Parser build() {
return new Parser(this);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2020 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.
Expand All @@ -18,6 +18,7 @@
import java.util.List;
import software.amazon.smithy.cli.commands.BuildCommand;
import software.amazon.smithy.cli.commands.DiffCommand;
import software.amazon.smithy.cli.commands.SelectCommand;
import software.amazon.smithy.cli.commands.ValidateCommand;

/**
Expand Down Expand Up @@ -86,6 +87,7 @@ public void run(String... args) {
cli.addCommand(new ValidateCommand());
cli.addCommand(new BuildCommand());
cli.addCommand(new DiffCommand());
cli.addCommand(new SelectCommand());
cli.run(args);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2020 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.
Expand Down Expand Up @@ -39,9 +39,8 @@
import software.amazon.smithy.cli.Parser;
import software.amazon.smithy.cli.SmithyCli;
import software.amazon.smithy.model.Model;
import software.amazon.smithy.model.loader.ModelAssembler;
import software.amazon.smithy.model.validation.Severity;
import software.amazon.smithy.model.validation.ValidatedResult;
import software.amazon.smithy.utils.SetUtils;
import software.amazon.smithy.utils.SmithyInternalApi;

@SmithyInternalApi
Expand Down Expand Up @@ -106,8 +105,8 @@ public void execute(Arguments arguments, ClassLoader classLoader) {

SmithyBuildConfig smithyBuildConfig = configBuilder.build();

// Build the model and fail if there are errors.
Model model = buildModel(classLoader, models, arguments);
// Build the model and fail if there are errors. Prints errors to stdout.
Model model = CommandUtils.buildModel(arguments, classLoader, SetUtils.of(Validator.Feature.STDOUT));

SmithyBuild smithyBuild = SmithyBuild.create(classLoader)
.config(smithyBuildConfig)
Expand Down Expand Up @@ -147,16 +146,6 @@ public void execute(Arguments arguments, ClassLoader classLoader) {
}
}

private Model buildModel(ClassLoader classLoader, List<String> models, Arguments arguments) {
ModelAssembler assembler = CommandUtils.createModelAssembler(classLoader);
CommandUtils.handleModelDiscovery(arguments, assembler, classLoader);
CommandUtils.handleUnknownTraitsOption(arguments, assembler);
models.forEach(assembler::addImport);
ValidatedResult<Model> result = assembler.assemble();
Validator.validate(result);
return result.getResult().orElseThrow(() -> new RuntimeException("No result; expected Validator to throw"));
}

private static final class ResultConsumer implements Consumer<ProjectionResult>, BiConsumer<String, Throwable> {
List<String> failedProjections = Collections.synchronizedList(new ArrayList<>());
AtomicInteger artifactCount = new AtomicInteger();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2020 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.
Expand All @@ -21,30 +21,45 @@
import java.nio.file.Paths;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.List;
import java.util.Set;
import java.util.logging.Logger;
import software.amazon.smithy.cli.Arguments;
import software.amazon.smithy.cli.CliError;
import software.amazon.smithy.cli.SmithyCli;
import software.amazon.smithy.model.Model;
import software.amazon.smithy.model.loader.ModelAssembler;
import software.amazon.smithy.model.validation.ValidatedResult;

final class CommandUtils {

private static final Logger LOGGER = Logger.getLogger(CommandUtils.class.getName());

private CommandUtils() {}

static void handleUnknownTraitsOption(Arguments arguments, ModelAssembler assembler) {
if (arguments.has(SmithyCli.ALLOW_UNKNOWN_TRAITS)) {
LOGGER.fine("Ignoring unknown traits");
assembler.putProperty(ModelAssembler.ALLOW_UNKNOWN_TRAITS, true);
}
static Model buildModel(Arguments arguments, ClassLoader classLoader, Set<Validator.Feature> features) {
List<String> models = arguments.positionalArguments();
ModelAssembler assembler = CommandUtils.createModelAssembler(classLoader);
CommandUtils.handleModelDiscovery(arguments, assembler, classLoader);
CommandUtils.handleUnknownTraitsOption(arguments, assembler);
models.forEach(assembler::addImport);
ValidatedResult<Model> result = assembler.assemble();
Validator.validate(result, features);
return result.getResult().orElseThrow(() -> new RuntimeException("Expected Validator to throw"));
}

static ModelAssembler createModelAssembler(ClassLoader classLoader) {
return Model.assembler(classLoader).putProperty(ModelAssembler.DISABLE_JAR_CACHE, true);
}

static void handleModelDiscovery(Arguments arguments, ModelAssembler assembler, ClassLoader baseLoader) {
private static void handleUnknownTraitsOption(Arguments arguments, ModelAssembler assembler) {
if (arguments.has(SmithyCli.ALLOW_UNKNOWN_TRAITS)) {
LOGGER.fine("Ignoring unknown traits");
assembler.putProperty(ModelAssembler.ALLOW_UNKNOWN_TRAITS, true);
}
}

private static void handleModelDiscovery(Arguments arguments, ModelAssembler assembler, ClassLoader baseLoader) {
if (arguments.has(SmithyCli.DISCOVER_CLASSPATH)) {
discoverModelsWithClasspath(arguments, assembler);
} else if (arguments.has(SmithyCli.DISCOVER)) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*
* Copyright 2020 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.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Stream;
import software.amazon.smithy.cli.Arguments;
import software.amazon.smithy.cli.Cli;
import software.amazon.smithy.cli.Command;
import software.amazon.smithy.cli.Parser;
import software.amazon.smithy.cli.SmithyCli;
import software.amazon.smithy.model.Model;
import software.amazon.smithy.model.SourceLocation;
import software.amazon.smithy.model.node.ArrayNode;
import software.amazon.smithy.model.node.Node;
import software.amazon.smithy.model.node.ObjectNode;
import software.amazon.smithy.model.selector.Selector;
import software.amazon.smithy.model.shapes.Shape;
import software.amazon.smithy.model.shapes.ShapeId;
import software.amazon.smithy.utils.IoUtils;
import software.amazon.smithy.utils.SetUtils;
import software.amazon.smithy.utils.SmithyInternalApi;

@SmithyInternalApi
public final class SelectCommand implements Command {
@Override
public String getName() {
return "select";
}

@Override
public String getSummary() {
return "Queries a model using a selector";
}

@Override
public String getHelp() {
return "This command prints the shapes in a model that match a selector. The\n"
+ "selector can be passed in using --selector or through stdin.\n\n"
+ "By default, each matching shape ID is printed to stdout on a new line.\n"
+ "Pass --vars to print out a JSON array that contains a 'shape' and 'vars'\n"
+ "property, where the 'vars' property is a map of each variable that was\n"
+ "captured when the shape was matched.";
}

@Override
public Parser getParser() {
return Parser.builder()
.parameter("--selector", "The Smithy selector to execute. Reads from STDIN when not provided.")
.option("--vars", "Include the variables that were captured when the shape was matched. Uses JSON.")
.option(SmithyCli.ALLOW_UNKNOWN_TRAITS, "Ignores unknown traits when validating models")
.option(SmithyCli.DISCOVER, "-d", "Enables model discovery, merging in models found inside of jars")
.parameter(SmithyCli.DISCOVER_CLASSPATH, "Enables model discovery using a custom classpath for models")
.positional("<MODELS>", "Path to Smithy models or directories")
.build();
}

@Override
public void execute(Arguments arguments, ClassLoader classLoader) {
// Get the selector from --selector or from STDIN/
Selector selector = arguments.has("--selector")
? Selector.parse(arguments.parameter("--selector"))
: Selector.parse(IoUtils.toUtf8String(System.in));

// Don't write the summary to STDOUT, but do write errors to STDERR.
Model model = CommandUtils.buildModel(arguments, classLoader, SetUtils.of(Validator.Feature.QUIET));

if (!arguments.has("--vars")) {
sortShapeIds(selector.select(model)).forEach(Cli::stdout);
} else {
// Show the JSON output for writing with --vars.
List<Node> result = new ArrayList<>();
selector.runner().model(model).selectMatches((shape, vars) -> {
result.add(Node.objectNodeBuilder()
.withMember("shape", Node.from(shape.getId().toString()))
.withMember("vars", collectVars(vars))
.build());
});
Cli.stdout(Node.prettyPrintJson(new ArrayNode(result, SourceLocation.NONE)));
}
}

private Stream<String> sortShapeIds(Collection<Shape> shapes) {
return shapes.stream().map(Shape::getId).map(ShapeId::toString).sorted();
}

private ObjectNode collectVars(Map<String, Set<Shape>> vars) {
ObjectNode.Builder varBuilder = Node.objectNodeBuilder();
for (Map.Entry<String, Set<Shape>> varEntry : vars.entrySet()) {
ArrayNode value = sortShapeIds(varEntry.getValue()).map(Node::from).collect(ArrayNode.collect());
varBuilder.withMember(varEntry.getKey(), value);
}
return varBuilder.build();
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2020 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.
Expand All @@ -21,9 +21,7 @@
import software.amazon.smithy.cli.Command;
import software.amazon.smithy.cli.Parser;
import software.amazon.smithy.cli.SmithyCli;
import software.amazon.smithy.model.Model;
import software.amazon.smithy.model.loader.ModelAssembler;
import software.amazon.smithy.model.validation.ValidatedResult;
import software.amazon.smithy.utils.SetUtils;
import software.amazon.smithy.utils.SmithyInternalApi;

@SmithyInternalApi
Expand Down Expand Up @@ -52,14 +50,7 @@ public Parser getParser() {
public void execute(Arguments arguments, ClassLoader classLoader) {
List<String> models = arguments.positionalArguments();
Colors.BRIGHT_WHITE.out(String.format("Validating Smithy model sources: %s", models));

ModelAssembler assembler = CommandUtils.createModelAssembler(classLoader);
CommandUtils.handleModelDiscovery(arguments, assembler, classLoader);
CommandUtils.handleUnknownTraitsOption(arguments, assembler);

models.forEach(assembler::addImport);
ValidatedResult<Model> modelResult = assembler.assemble();
Validator.validate(modelResult);
CommandUtils.buildModel(arguments, classLoader, SetUtils.of());
Colors.BRIGHT_BOLD_GREEN.out("Smithy validation complete");
}
}
Loading