NIFI-4839 Improving back-ref support so that ReferenceResolver is passed the option being resolved - Adding ResolvedReference to encapsulate the results of resolving a back-reference. - Update README.md - Added OkResult for delete commands - Added sync-flow-versions and transfer-flow-version to registry commands - Returning appropriate status code when exiting standalone mode - Adding security section to README
Signed-off-by: Pierre Villard <[email protected]> This closes #2477. Project: http://git-wip-us.apache.org/repos/asf/nifi/repo Commit: http://git-wip-us.apache.org/repos/asf/nifi/commit/5041bea7 Tree: http://git-wip-us.apache.org/repos/asf/nifi/tree/5041bea7 Diff: http://git-wip-us.apache.org/repos/asf/nifi/diff/5041bea7 Branch: refs/heads/master Commit: 5041bea773c47b0b16b0a0e713d13c16f0cd66b6 Parents: 2fd24b7 Author: Bryan Bende <[email protected]> Authored: Thu Feb 15 17:18:48 2018 -0500 Committer: Pierre Villard <[email protected]> Committed: Wed Feb 28 17:24:24 2018 +0100 ---------------------------------------------------------------------- nifi-toolkit/nifi-toolkit-cli/README.md | 124 +++++++++++++++++-- nifi-toolkit/nifi-toolkit-cli/pom.xml | 1 - .../apache/nifi/toolkit/cli/CLICompleter.java | 10 +- .../org/apache/nifi/toolkit/cli/CLIMain.java | 16 ++- .../nifi/toolkit/cli/api/ReferenceResolver.java | 5 +- .../nifi/toolkit/cli/api/ResolvedReference.java | 64 ++++++++++ .../cli/impl/command/AbstractCommand.java | 6 +- .../toolkit/cli/impl/command/CommandOption.java | 26 +++- .../cli/impl/command/CommandProcessor.java | 102 +++++++++------ .../cli/impl/command/nifi/flow/GetRootId.java | 2 +- .../cli/impl/command/nifi/pg/PGImport.java | 2 +- .../nifi/registry/CreateRegistryClient.java | 2 +- .../registry/AbstractNiFiRegistryCommand.java | 34 +++++ .../registry/NiFiRegistryCommandGroup.java | 4 + .../command/registry/bucket/CreateBucket.java | 2 +- .../command/registry/bucket/DeleteBucket.java | 10 +- .../impl/command/registry/flow/CreateFlow.java | 2 +- .../impl/command/registry/flow/DeleteFlow.java | 10 +- .../registry/flow/ImportFlowVersion.java | 2 +- .../command/registry/flow/SyncFlowVersions.java | 120 ++++++++++++++++++ .../registry/flow/TransferFlowVersion.java | 118 ++++++++++++++++++ .../cli/impl/command/session/GetVariable.java | 4 +- .../toolkit/cli/impl/result/BucketsResult.java | 9 +- .../nifi/toolkit/cli/impl/result/OkResult.java | 30 +++++ .../cli/impl/result/ProcessGroupsResult.java | 9 +- .../toolkit/cli/impl/result/StringResult.java | 10 +- .../cli/impl/result/VersionedFlowsResult.java | 11 +- .../nifi/toolkit/cli/impl/command/CommandA.java | 80 ++++++++++++ .../cli/impl/command/CommandAResult.java | 59 +++++++++ .../cli/impl/command/TestCommandProcessor.java | 83 +++++++++++++ .../impl/result/TestVersionedFlowsResult.java | 38 +++++- 31 files changed, 892 insertions(+), 103 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/nifi/blob/5041bea7/nifi-toolkit/nifi-toolkit-cli/README.md ---------------------------------------------------------------------- diff --git a/nifi-toolkit/nifi-toolkit-cli/README.md b/nifi-toolkit/nifi-toolkit-cli/README.md index 02fae89..8ca9391 100644 --- a/nifi-toolkit/nifi-toolkit-cli/README.md +++ b/nifi-toolkit/nifi-toolkit-cli/README.md @@ -22,14 +22,14 @@ Most commands will require specifying a baseUrl for the NiFi or NiFi registry in An example command to list the buckets in a NiFi Registry instance would be the following: - ./bin/cli.sh nifi-reg list-buckets -u http://localhost:18080 + ./bin/cli.sh registry list-buckets -u http://localhost:18080 In order to avoid specifying the URL (and possibly other optional arguments for TLS) on every command, -you can define a properties file containing the reptitive arguments. +you can define a properties file containing the repetitive arguments. An example properties file for a local NiFi Registry instance would look like the following: - baseUrl=https://localhost:18443 + baseUrl=http://localhost:18080 keystore= keystoreType= keystorePasswd= @@ -37,10 +37,11 @@ An example properties file for a local NiFi Registry instance would look like th truststore= truststoreType= truststorePasswd= + proxiedEntity= This properties file can then be used on a command by specifying -p <path-to-props-file> : - ./bin/cli.sh nifi-reg list-buckets -p /path/to/local-nifi-registry.properties + ./bin/cli.sh registry list-buckets -p /path/to/local-nifi-registry.properties You could then maintain a properties file for each environment you plan to interact with, such as dev, qa, prod. @@ -58,7 +59,7 @@ An example of setting the default property files would be following: This will write the above properties into the .nifi-cli.config in the user's home directory and will allow commands to be executed without specifying a URL or properties file: - ./bin/cli.sh nifi-reg list-buckets + ./bin/cli.sh registry list-buckets The above command will now use the baseUrl from *local-nifi-registry.properties*. @@ -68,6 +69,56 @@ The order of resolving an argument is the following: * A properties file argument (-p) overrides the session * The session is used when nothing else is specified +## Security Configuration + +If NiFi and NiFi Registry are secured, then commands executed from the CLI will need to make a TLS connection and +authenticate as a user with permissions to perform the desired action. + +Currently the CLI supports authenticating with a client certificate and an optional proxied-entity. A common scenario +would be running the CLI from one of the nodes where NiFi or NiFi Registry is installed, which allows the CLI to use +the same key store and trust store as the NiFi/NiFi Registry instance. + +The security configuration can be specified per-command, or in one of the properties files described in the previous section. + +The examples below are for NiFi Registry, but the same concept applies for NiFi commands. + +### Example - Secure NiFi Registry without Proxied-Entity + +Assuming we have a keystore containing the certificate for *'CN=user1, OU=NIFI'*, an example properties file would +be the following: + + baseUrl=https://localhost:18443 + keystore=/path/to/keystore.jks + keystoreType=JKS + keystorePasswd=changeme + keyPasswd=changeme + truststore=/path/to/truststore.jks + truststoreType=JKS + truststorePasswd=changeme + +In this example, commands will be executed as *'CN=user1, OU=NIFI'*. This user would need to be a user in NiFi Registry, +and commands accessing buckets would be restricted to buckets this user has access to. + +### Example - Secure NiFi Registry with Proxied-Entity + +Assuming we have access to the keystore of NiFi Registry itself, and that NiFi Registry is also configured to allow +Kerberos or LDAP authentication, an example properties file would be the following: + + baseUrl=https://localhost:18443 + keystore=/path/to/keystore.jks + keystoreType=JKS + keystorePasswd=changeme + keyPasswd=changeme + truststore=/path/to/truststore.jks + truststoreType=JKS + truststorePasswd=changeme + [email protected] + +In this example, the certificate in keystore.jks would be for the NiFi Registry server, for example *'CN=localhost, OU=NIFI'*. +This identity would need to be defined as a user in NiFi Registry and given permissions to 'Proxy'. + +*'CN=localhost, OU=NIFI'* would be proxying commands to be executed as *'[email protected]'*. + ## Interactive Usage In interactive mode the tab key can be used to perform auto-completion. @@ -75,7 +126,7 @@ In interactive mode the tab key can be used to perform auto-completion. For example, typing tab at an empty prompt should display possible commands for the first argument: #> - exit help nifi nifi-reg session + exit help nifi registry session Typing "nifi " and then a tab will show the sub-commands for NiFi: @@ -124,6 +175,65 @@ Example of json output for list-buckets: } } ] +## Back Referencing + +When using the interactive CLI, a common scenario will be using an id from a previous +result as the input to the next command. Back-referencing provides a shortcut for +referencing a result from the previous command via a positional reference. + +NOTE: Not every command produces back-references. To determine if a command +supports back-referencing, check the usage. + + #> registry list-buckets help + + Lists the buckets that the current user has access to. + + PRODUCES BACK-REFERENCES + +A common scenario for utilizing back-references would be the following: + +1) User starts by exploring the available buckets in a registry instance + + #> registry list-buckets + + # Name Id Description + - ------------ ------------------------------------ ----------- + 1 My Bucket 3c7b7467-0012-4d8f-a918-6aa42b6b9d39 (empty) + 2 Other Bucket 175fb557-43a2-4abb-871f-81a354f47bc2 (empty) + +2) User then views the flows in one of the buckets using a back-reference to the bucket id from the previous result in position 1 + + #> registry list-flows -b &1 + + Using a positional back-reference for 'My Bucket' + + # Name Id Description + - ------- ------------------------------------ ---------------- + 1 My Flow 06acb207-d2f1-447f-85ed-9b8672fe6d30 This is my flow. + +3) User then views the version of the flow using a back-reference to the flow id from the previous result in position 1 + + #> registry list-flow-versions -f &1 + + Using a positional back-reference for 'My Flow' + + Ver Date Author Message + --- -------------------------- ------------------------ ------------------------------------- + 1 Tue, Jan 23 2018 09:48 EST anonymous This is the first version of my flow. + +4) User deploys version 1 of the flow using back-references to the bucket and flow id from step 2 + + #> nifi pg-import -b &1 -f &1 -fv 1 + + Using a positional back-reference for 'My Bucket' + + Using a positional back-reference for 'My Flow' + + 9bd157d4-0161-1000-b946-c1f9b1832efd + +The reason step 4 was able to reference the results from step 2, is because the list-flow-versions +command in step 3 does not produce back-references, so the results from step 2 are still available. + ## Adding Commands To add a NiFi command, create a new class that extends AbstractNiFiCommand: @@ -151,4 +261,4 @@ Add the new command to NiFiCommandGroup: commands.add(new MyCommand()); To add a NiFi Registry command, perform the same steps, but extend from -AbstractNiFiRegistryCommand, and add the command to NiFiRegistryCommandGroup. \ No newline at end of file +AbstractNiFiRegistryCommand, and add the command to NiFiRegistryCommandGroup. http://git-wip-us.apache.org/repos/asf/nifi/blob/5041bea7/nifi-toolkit/nifi-toolkit-cli/pom.xml ---------------------------------------------------------------------- diff --git a/nifi-toolkit/nifi-toolkit-cli/pom.xml b/nifi-toolkit/nifi-toolkit-cli/pom.xml index 2f9e18c..0ffdfac 100644 --- a/nifi-toolkit/nifi-toolkit-cli/pom.xml +++ b/nifi-toolkit/nifi-toolkit-cli/pom.xml @@ -82,7 +82,6 @@ <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> - <version>2.6</version> </dependency> </dependencies> </project> http://git-wip-us.apache.org/repos/asf/nifi/blob/5041bea7/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/CLICompleter.java ---------------------------------------------------------------------- diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/CLICompleter.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/CLICompleter.java index 7889b30..1a66882 100644 --- a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/CLICompleter.java +++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/CLICompleter.java @@ -47,11 +47,11 @@ public class CLICompleter implements Completer { private static final Set<String> FILE_COMPLETION_ARGS; static { final Set<String> args = new HashSet<>(); - args.add("-" + CommandOption.PROPERTIES.getShortName()); - args.add("-" + CommandOption.INPUT_SOURCE.getShortName()); - args.add("-" + CommandOption.OUTPUT_FILE.getShortName()); - args.add("-" + CommandOption.NIFI_REG_PROPS.getShortName()); - args.add("-" + CommandOption.NIFI_PROPS.getShortName()); + for (final CommandOption option : CommandOption.values()) { + if (option.isFile()) { + args.add("-" + option.getShortName()); + } + } FILE_COMPLETION_ARGS = Collections.unmodifiableSet(args); } http://git-wip-us.apache.org/repos/asf/nifi/blob/5041bea7/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/CLIMain.java ---------------------------------------------------------------------- diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/CLIMain.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/CLIMain.java index 643fe03..200d6ea 100644 --- a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/CLIMain.java +++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/CLIMain.java @@ -67,9 +67,15 @@ public class CLIMain { if (args == null || args.length == 0) { runInteractiveCLI(); } else { - runSingleCommand(args); - System.out.println(); - System.out.flush(); + // in standalone mode we want to make sure the process exits with the correct status + try { + final int returnCode = runSingleCommand(args); + System.exit(returnCode); + } catch (Exception e) { + // shouldn't really get here, but just in case + e.printStackTrace(); + System.exit(-1); + } } } @@ -130,13 +136,13 @@ public class CLIMain { * * @param args the args passed in from the command line */ - private static void runSingleCommand(final String[] args) { + private static int runSingleCommand(final String[] args) { final Context context = createContext(System.out, false); final Map<String,Command> topLevelCommands = CommandFactory.createTopLevelCommands(context); final Map<String,CommandGroup> commandGroups = CommandFactory.createCommandGroups(context); final CommandProcessor commandProcessor = new CommandProcessor(topLevelCommands, commandGroups, context); - commandProcessor.process(args); + return commandProcessor.process(args); } private static Context createContext(final PrintStream output, final boolean isInteractive) { http://git-wip-us.apache.org/repos/asf/nifi/blob/5041bea7/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/api/ReferenceResolver.java ---------------------------------------------------------------------- diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/api/ReferenceResolver.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/api/ReferenceResolver.java index de0feb8..d739ea7 100644 --- a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/api/ReferenceResolver.java +++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/api/ReferenceResolver.java @@ -16,6 +16,8 @@ */ package org.apache.nifi.toolkit.cli.api; +import org.apache.nifi.toolkit.cli.impl.command.CommandOption; + /** * An object that is capable of resolving a positional reference to some value that corresponds with the reference. */ @@ -24,10 +26,11 @@ public interface ReferenceResolver { /** * Resolves the passed in positional reference to it's corresponding value. * + * @param option the option that the reference is being resolved for, implementers should protect against a possible null option * @param position a position in this back reference * @return the resolved value for the given position */ - String resolve(Integer position); + ResolvedReference resolve(CommandOption option, Integer position); /** * @return true if the there are no references to resolve, false otherwise http://git-wip-us.apache.org/repos/asf/nifi/blob/5041bea7/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/api/ResolvedReference.java ---------------------------------------------------------------------- diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/api/ResolvedReference.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/api/ResolvedReference.java new file mode 100644 index 0000000..b96568f --- /dev/null +++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/api/ResolvedReference.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License 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 org.apache.nifi.toolkit.cli.api; + +import org.apache.commons.lang3.Validate; +import org.apache.nifi.toolkit.cli.impl.command.CommandOption; + +/** + * Represents a resolved back-reference produced by a ReferenceResolver. + */ +public class ResolvedReference { + + private final CommandOption option; + + private final Integer position; + + private final String displayName; + + private final String resolvedValue; + + public ResolvedReference( + final CommandOption option, + final Integer position, + final String displayName, + final String resolvedValue) { + this.option = option; + this.position = position; + this.displayName = displayName; + this.resolvedValue = resolvedValue; + Validate.notNull(this.position); + Validate.notNull(this.displayName); + Validate.notNull(this.resolvedValue); + } + + public CommandOption getOption() { + return option; + } + + public Integer getPosition() { + return position; + } + + public String getDisplayName() { + return displayName; + } + + public String getResolvedValue() { + return resolvedValue; + } +} http://git-wip-us.apache.org/repos/asf/nifi/blob/5041bea7/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/AbstractCommand.java ---------------------------------------------------------------------- diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/AbstractCommand.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/AbstractCommand.java index ddf2d1a..1742243 100644 --- a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/AbstractCommand.java +++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/AbstractCommand.java @@ -124,7 +124,7 @@ public abstract class AbstractCommand<R extends Result> implements Command<R> { final PrintWriter printWriter = new PrintWriter(output); - final int width = 160; + final int width = 80; final HelpFormatter hf = new HelpFormatter(); hf.setWidth(width); @@ -174,7 +174,7 @@ public abstract class AbstractCommand<R extends Result> implements Command<R> { protected String getRequiredArg(final Properties properties, final CommandOption option) throws MissingOptionException { final String argValue = properties.getProperty(option.getLongName()); if (StringUtils.isBlank(argValue)) { - throw new MissingOptionException("Missing required option '" + option.getLongName() + "'"); + throw new MissingOptionException("Missing required option --" + option.getLongName()); } return argValue; } @@ -195,7 +195,7 @@ public abstract class AbstractCommand<R extends Result> implements Command<R> { protected Integer getRequiredIntArg(final Properties properties, final CommandOption option) throws MissingOptionException { final String argValue = properties.getProperty(option.getLongName()); if (StringUtils.isBlank(argValue)) { - throw new MissingOptionException("Missing required option '" + option.getLongName() + "'"); + throw new MissingOptionException("Missing required option --" + option.getLongName()); } try { http://git-wip-us.apache.org/repos/asf/nifi/blob/5041bea7/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/CommandOption.java ---------------------------------------------------------------------- diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/CommandOption.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/CommandOption.java index 9990b22..ad15036 100644 --- a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/CommandOption.java +++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/CommandOption.java @@ -25,13 +25,13 @@ public enum CommandOption { // General URL("u", "baseUrl", "The URL to execute the command against", true), - INPUT_SOURCE("i", "input", "A local file to read as input contents, or a public URL to fetch", true), - OUTPUT_FILE("o", "outputFile", "A file to write output to, must contain full path and filename", true), + INPUT_SOURCE("i", "input", "A local file to read as input contents, or a public URL to fetch", true, true), + OUTPUT_FILE("o", "outputFile", "A file to write output to, must contain full path and filename", true, true), PROPERTIES("p", "properties", "A properties file to load arguments from, " + - "command line values will override anything in the properties file, must contain full path to file", true), + "command line values will override anything in the properties file, must contain full path to file", true, true), - NIFI_PROPS("nifiProps", "nifiProps", "A properties file to load for NiFi config", true), - NIFI_REG_PROPS("nifiRegProps", "nifiRegProps", "A properties file to load for NiFi Registry config", true), + NIFI_PROPS("nifiProps", "nifiProps", "A properties file to load for NiFi config", true, true), + NIFI_REG_PROPS("nifiRegProps", "nifiRegProps", "A properties file to load for NiFi Registry config", true, true), // Registry - Buckets BUCKET_ID("b", "bucketIdentifier", "A bucket identifier", true), @@ -44,6 +44,11 @@ public enum CommandOption { FLOW_DESC("fd", "flowDesc", "A flow description", true), FLOW_VERSION("fv", "flowVersion", "A version of a flow", true), + // Registry - Source options for when there are two registries involved and one is a source + SRC_PROPS("sp", "sourceProps", "A properties file to load for the source", true, true), + SRC_FLOW_ID("sf", "sourceFlowIdentifier", "A flow identifier from the source registry", true), + SRC_FLOW_VERSION("sfv", "sourceFlowVersion", "A version of a flow from the source registry", true), + // NiFi - Registries REGISTRY_CLIENT_ID("rcid", "registryClientId", "The id of a registry client", true), REGISTRY_CLIENT_NAME("rcn", "registryClientName", "The name of the registry client", true), @@ -78,14 +83,21 @@ public enum CommandOption { private final String longName; private final String description; private final boolean hasArg; + private final boolean isFile; CommandOption(final String shortName, final String longName, final String description, final boolean hasArg) { + this(shortName, longName, description, hasArg, false); + } + + CommandOption(final String shortName, final String longName, final String description, final boolean hasArg, final boolean isFile) { this.shortName = shortName; this.longName = longName; this.description = description; this.hasArg = hasArg; + this.isFile = isFile; } + public String getShortName() { return shortName; } @@ -98,6 +110,10 @@ public enum CommandOption { return description; } + public boolean isFile() { + return isFile; + } + public Option createOption() { return Option.builder(shortName).longOpt(longName).desc(description).hasArg(hasArg).build(); } http://git-wip-us.apache.org/repos/asf/nifi/blob/5041bea7/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/CommandProcessor.java ---------------------------------------------------------------------- diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/CommandProcessor.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/CommandProcessor.java index c12249b..256cb14 100644 --- a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/CommandProcessor.java +++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/CommandProcessor.java @@ -22,20 +22,20 @@ import org.apache.commons.cli.DefaultParser; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; import org.apache.commons.lang3.Validate; -import org.apache.nifi.registry.client.NiFiRegistryException; import org.apache.nifi.toolkit.cli.api.Command; import org.apache.nifi.toolkit.cli.api.CommandException; import org.apache.nifi.toolkit.cli.api.CommandGroup; import org.apache.nifi.toolkit.cli.api.Context; import org.apache.nifi.toolkit.cli.api.ReferenceResolver; import org.apache.nifi.toolkit.cli.api.Referenceable; +import org.apache.nifi.toolkit.cli.api.ResolvedReference; import org.apache.nifi.toolkit.cli.api.Result; import org.apache.nifi.toolkit.cli.api.WritableResult; -import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClientException; -import java.io.IOException; import java.io.PrintStream; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicReference; @@ -117,82 +117,101 @@ public class CommandProcessor { return; } + final List<ResolvedReference> resolvedReferences = new ArrayList<>(); + for (int i=0; i < args.length; i++) { final String arg = args[i]; if (arg == null || !arg.startsWith(BACK_REF_INDICATOR)) { continue; } - if (context.isInteractive()) { - context.getOutput().println(); - } - try { + // attempt to determine which option is using the back-ref + CommandOption option = null; + if (i > 0) { + String prevArg = args[i - 1]; + if (prevArg.startsWith("--")) { + prevArg = prevArg.substring(2); + } else if (prevArg.startsWith("-")) { + prevArg = prevArg.substring(1); + } + + for (CommandOption opt : CommandOption.values()) { + if (opt.getShortName().equals(prevArg) || opt.getLongName().equals(prevArg)) { + option = opt; + break; + } + } + } + + // use the option and position to resolve the back-ref, and if it resolves then replace the arg final Integer pos = Integer.valueOf(arg.substring(1)); - final String resolvedReference = referenceResolver.resolve(pos); + final ResolvedReference resolvedReference = referenceResolver.resolve(option, pos); if (resolvedReference != null) { - args[i] = resolvedReference; + args[i] = resolvedReference.getResolvedValue(); + resolvedReferences.add(resolvedReference); } } catch (Exception e) { // skip } } + + if (context.isInteractive()) { + for (ResolvedReference resolvedRef : resolvedReferences) { + out.println(); + out.printf("Using a positional back-reference for '%s'%n", resolvedRef.getDisplayName()); + } + } } - public void process(String[] args) { + public int process(String[] args) { if (args == null || args.length == 0) { printBasicUsage(null); - return; + return -1; } if (CommandOption.HELP.getLongName().equalsIgnoreCase(args[0])) { if (args.length == 2 && "-v".equalsIgnoreCase(args[1])) { printBasicUsage(null, true); - return; + return 0; } else { printBasicUsage(null); - return; + return 0; } } final String commandStr = args[0]; if (topLevelCommands.containsKey(commandStr)) { - processTopLevelCommand(commandStr, args); + return processTopLevelCommand(commandStr, args); } else if (commandGroups.containsKey(commandStr)) { - processGroupCommand(commandStr, args); + return processGroupCommand(commandStr, args); } else { printBasicUsage("Unknown command '" + commandStr + "'"); - return; + return -1; } } - private void processTopLevelCommand(final String commandStr, final String[] args) { + private int processTopLevelCommand(final String commandStr, final String[] args) { final Command command = topLevelCommands.get(commandStr); if (command == null) { printBasicUsage("Unknown command '" + commandStr + "'"); - return; + return -1; } try { final String[] otherArgs = Arrays.copyOfRange(args, 1, args.length, String[].class); - final CommandLine commandLine = parseCli(command, otherArgs); - if (commandLine == null) { - out.println("Unable to parse command line"); - return; - } - - processCommand(otherArgs, commandLine, command); - + return processCommand(otherArgs, command); } catch (Exception e) { command.printUsage(e.getMessage()); + return -1; } } - private void processGroupCommand(final String commandGroupStr, final String[] args) { + private int processGroupCommand(final String commandGroupStr, final String[] args) { if (args.length <= 1) { printBasicUsage("No command provided to " + commandGroupStr); - return; + return -1; } final String commandStr = args[1]; @@ -205,25 +224,26 @@ public class CommandProcessor { if (command == null) { printBasicUsage("Unknown command '" + commandGroupStr + " " + commandStr + "'"); - return; + return -1; } try { final String[] otherArgs = Arrays.copyOfRange(args, 2, args.length, String[].class); - final CommandLine commandLine = parseCli(command, otherArgs); - if (commandLine == null) { - out.println("Unable to parse command line"); - return; - } - - processCommand(otherArgs, commandLine, command); - + return processCommand(otherArgs, command); } catch (Exception e) { command.printUsage(e.getMessage()); + return -1; } } - private void processCommand(final String[] args, final CommandLine commandLine, final Command command) { + // visible for testing + int processCommand(final String[] args, final Command command) throws ParseException { + final CommandLine commandLine = parseCli(command, args); + if (commandLine == null) { + out.println("Unable to parse command line"); + return -1; + } + try { if (args.length == 1 && CommandOption.HELP.getLongName().equalsIgnoreCase(args[0])) { command.printUsage(null); @@ -247,6 +267,9 @@ public class CommandProcessor { } } } + + return 0; + } catch (Exception e) { // CommandExceptions will wrap things like NiFiClientException, NiFiRegistryException, and IOException, // so for those we don't need to print the usage every time @@ -263,8 +286,9 @@ public class CommandProcessor { e.printStackTrace(out); out.println(); } + + return -1; } } - } http://git-wip-us.apache.org/repos/asf/nifi/blob/5041bea7/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/flow/GetRootId.java ---------------------------------------------------------------------- diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/flow/GetRootId.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/flow/GetRootId.java index be27f33..d5e18f9 100644 --- a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/flow/GetRootId.java +++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/flow/GetRootId.java @@ -43,7 +43,7 @@ public class GetRootId extends AbstractNiFiCommand<StringResult> { public StringResult doExecute(final NiFiClient client, final Properties properties) throws NiFiClientException, IOException { final FlowClient flowClient = client.getFlowClient(); - return new StringResult(flowClient.getRootGroupId()); + return new StringResult(flowClient.getRootGroupId(), getContext().isInteractive()); } } http://git-wip-us.apache.org/repos/asf/nifi/blob/5041bea7/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/PGImport.java ---------------------------------------------------------------------- diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/PGImport.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/PGImport.java index cb5a636..b17fba1 100644 --- a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/PGImport.java +++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/pg/PGImport.java @@ -119,7 +119,7 @@ public class PGImport extends AbstractNiFiCommand<StringResult> { final ProcessGroupClient pgClient = client.getProcessGroupClient(); final ProcessGroupEntity createdEntity = pgClient.createProcessGroup(parentPgId, pgEntity); - return new StringResult(createdEntity.getId()); + return new StringResult(createdEntity.getId(), getContext().isInteractive()); } } http://git-wip-us.apache.org/repos/asf/nifi/blob/5041bea7/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/registry/CreateRegistryClient.java ---------------------------------------------------------------------- diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/registry/CreateRegistryClient.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/registry/CreateRegistryClient.java index ddbc325..662fda3 100644 --- a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/registry/CreateRegistryClient.java +++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/registry/CreateRegistryClient.java @@ -68,6 +68,6 @@ public class CreateRegistryClient extends AbstractNiFiCommand<StringResult> { clientEntity.setRevision(getInitialRevisionDTO()); final RegistryClientEntity createdEntity = client.getControllerClient().createRegistryClient(clientEntity); - return new StringResult(createdEntity.getId()); + return new StringResult(createdEntity.getId(), getContext().isInteractive()); } } http://git-wip-us.apache.org/repos/asf/nifi/blob/5041bea7/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/AbstractNiFiRegistryCommand.java ---------------------------------------------------------------------- diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/AbstractNiFiRegistryCommand.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/AbstractNiFiRegistryCommand.java index 9a3a72a..4ed3430 100644 --- a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/AbstractNiFiRegistryCommand.java +++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/AbstractNiFiRegistryCommand.java @@ -17,19 +17,25 @@ package org.apache.nifi.toolkit.cli.impl.command.registry; import org.apache.commons.cli.ParseException; +import org.apache.commons.lang3.StringUtils; import org.apache.nifi.registry.bucket.BucketItem; +import org.apache.nifi.registry.client.FlowSnapshotClient; import org.apache.nifi.registry.client.NiFiRegistryClient; import org.apache.nifi.registry.client.NiFiRegistryException; +import org.apache.nifi.registry.flow.VersionedFlowSnapshotMetadata; import org.apache.nifi.toolkit.cli.api.ClientFactory; import org.apache.nifi.toolkit.cli.api.CommandException; import org.apache.nifi.toolkit.cli.api.Result; import org.apache.nifi.toolkit.cli.impl.command.AbstractPropertyCommand; import org.apache.nifi.toolkit.cli.impl.session.SessionVariable; +import java.io.FileInputStream; import java.io.IOException; +import java.io.InputStream; import java.util.List; import java.util.Optional; import java.util.Properties; +import java.util.stream.Collectors; /** * Base class for all NiFi Reg commands. @@ -83,4 +89,32 @@ public abstract class AbstractNiFiRegistryCommand<R extends Result> extends Abst return matchingItem.get().getBucketIdentifier(); } + protected List<Integer> getVersions(final NiFiRegistryClient client, final String bucketId, final String flowId) + throws NiFiRegistryException, IOException { + final FlowSnapshotClient srcSnapshotClient = client.getFlowSnapshotClient(); + final List<VersionedFlowSnapshotMetadata> srcVersionMetadata = srcSnapshotClient.getSnapshotMetadata(bucketId, flowId); + return srcVersionMetadata.stream().map(s -> s.getVersion()).collect(Collectors.toList()); + } + + /* + * If srcProps was specified then load the properties and create a new client for the source, + * but if it wasn't then assume the source is the same registry we already know about + */ + protected NiFiRegistryClient getSourceClient(final NiFiRegistryClient client, final String srcPropsValue) + throws IOException, org.apache.commons.cli.MissingOptionException { + final NiFiRegistryClient srcClient; + if (!StringUtils.isBlank(srcPropsValue)) { + final Properties srcProps = new Properties(); + try (final InputStream in = new FileInputStream(srcPropsValue)) { + srcProps.load(in); + } + + final ClientFactory<NiFiRegistryClient> clientFactory = getContext().getNiFiRegistryClientFactory(); + srcClient = clientFactory.createClient(srcProps); + } else { + srcClient = client; + } + return srcClient; + } + } http://git-wip-us.apache.org/repos/asf/nifi/blob/5041bea7/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/NiFiRegistryCommandGroup.java ---------------------------------------------------------------------- diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/NiFiRegistryCommandGroup.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/NiFiRegistryCommandGroup.java index be40808..6b2f059 100644 --- a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/NiFiRegistryCommandGroup.java +++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/NiFiRegistryCommandGroup.java @@ -27,6 +27,8 @@ import org.apache.nifi.toolkit.cli.impl.command.registry.flow.ExportFlowVersion; import org.apache.nifi.toolkit.cli.impl.command.registry.flow.ImportFlowVersion; import org.apache.nifi.toolkit.cli.impl.command.registry.flow.ListFlowVersions; import org.apache.nifi.toolkit.cli.impl.command.registry.flow.ListFlows; +import org.apache.nifi.toolkit.cli.impl.command.registry.flow.SyncFlowVersions; +import org.apache.nifi.toolkit.cli.impl.command.registry.flow.TransferFlowVersion; import org.apache.nifi.toolkit.cli.impl.command.registry.user.CurrentUser; import java.util.ArrayList; @@ -56,6 +58,8 @@ public class NiFiRegistryCommandGroup extends AbstractCommandGroup { commandList.add(new ListFlowVersions()); commandList.add(new ExportFlowVersion()); commandList.add(new ImportFlowVersion()); + commandList.add(new SyncFlowVersions()); + commandList.add(new TransferFlowVersion()); return new ArrayList<>(commandList); } } http://git-wip-us.apache.org/repos/asf/nifi/blob/5041bea7/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/bucket/CreateBucket.java ---------------------------------------------------------------------- diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/bucket/CreateBucket.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/bucket/CreateBucket.java index bd3260d..cabda85 100644 --- a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/bucket/CreateBucket.java +++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/bucket/CreateBucket.java @@ -62,6 +62,6 @@ public class CreateBucket extends AbstractNiFiRegistryCommand<StringResult> { final BucketClient bucketClient = client.getBucketClient(); final Bucket createdBucket = bucketClient.create(bucket); - return new StringResult(createdBucket.getIdentifier()); + return new StringResult(createdBucket.getIdentifier(), getContext().isInteractive()); } } http://git-wip-us.apache.org/repos/asf/nifi/blob/5041bea7/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/bucket/DeleteBucket.java ---------------------------------------------------------------------- diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/bucket/DeleteBucket.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/bucket/DeleteBucket.java index 754dfae..e4556e8 100644 --- a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/bucket/DeleteBucket.java +++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/bucket/DeleteBucket.java @@ -25,7 +25,7 @@ import org.apache.nifi.registry.flow.VersionedFlow; import org.apache.nifi.toolkit.cli.api.Context; import org.apache.nifi.toolkit.cli.impl.command.CommandOption; import org.apache.nifi.toolkit.cli.impl.command.registry.AbstractNiFiRegistryCommand; -import org.apache.nifi.toolkit.cli.impl.result.VoidResult; +import org.apache.nifi.toolkit.cli.impl.result.OkResult; import java.io.IOException; import java.util.List; @@ -34,10 +34,10 @@ import java.util.Properties; /** * Deletes a bucket from the given registry. */ -public class DeleteBucket extends AbstractNiFiRegistryCommand<VoidResult> { +public class DeleteBucket extends AbstractNiFiRegistryCommand<OkResult> { public DeleteBucket() { - super("delete-bucket", VoidResult.class); + super("delete-bucket", OkResult.class); } @Override @@ -52,7 +52,7 @@ public class DeleteBucket extends AbstractNiFiRegistryCommand<VoidResult> { } @Override - public VoidResult doExecute(final NiFiRegistryClient client, final Properties properties) + public OkResult doExecute(final NiFiRegistryClient client, final Properties properties) throws IOException, NiFiRegistryException, ParseException { final String bucketId = getRequiredArg(properties, CommandOption.BUCKET_ID); @@ -66,7 +66,7 @@ public class DeleteBucket extends AbstractNiFiRegistryCommand<VoidResult> { } else { final BucketClient bucketClient = client.getBucketClient(); bucketClient.delete(bucketId); - return VoidResult.getInstance(); + return new OkResult(getContext().isInteractive()); } } } http://git-wip-us.apache.org/repos/asf/nifi/blob/5041bea7/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/flow/CreateFlow.java ---------------------------------------------------------------------- diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/flow/CreateFlow.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/flow/CreateFlow.java index 075bc15..97e0c7e 100644 --- a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/flow/CreateFlow.java +++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/flow/CreateFlow.java @@ -64,6 +64,6 @@ public class CreateFlow extends AbstractNiFiRegistryCommand<StringResult> { final FlowClient flowClient = client.getFlowClient(); final VersionedFlow createdFlow = flowClient.create(flow); - return new StringResult(createdFlow.getIdentifier()); + return new StringResult(createdFlow.getIdentifier(), getContext().isInteractive()); } } http://git-wip-us.apache.org/repos/asf/nifi/blob/5041bea7/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/flow/DeleteFlow.java ---------------------------------------------------------------------- diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/flow/DeleteFlow.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/flow/DeleteFlow.java index 48257d3..f834348 100644 --- a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/flow/DeleteFlow.java +++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/flow/DeleteFlow.java @@ -25,7 +25,7 @@ import org.apache.nifi.registry.flow.VersionedFlowSnapshotMetadata; import org.apache.nifi.toolkit.cli.api.Context; import org.apache.nifi.toolkit.cli.impl.command.CommandOption; import org.apache.nifi.toolkit.cli.impl.command.registry.AbstractNiFiRegistryCommand; -import org.apache.nifi.toolkit.cli.impl.result.VoidResult; +import org.apache.nifi.toolkit.cli.impl.result.OkResult; import java.io.IOException; import java.util.List; @@ -34,10 +34,10 @@ import java.util.Properties; /** * Deletes a flow from the given registry. */ -public class DeleteFlow extends AbstractNiFiRegistryCommand<VoidResult> { +public class DeleteFlow extends AbstractNiFiRegistryCommand<OkResult> { public DeleteFlow() { - super("delete-flow", VoidResult.class); + super("delete-flow", OkResult.class); } @Override @@ -52,7 +52,7 @@ public class DeleteFlow extends AbstractNiFiRegistryCommand<VoidResult> { } @Override - public VoidResult doExecute(final NiFiRegistryClient client, final Properties properties) + public OkResult doExecute(final NiFiRegistryClient client, final Properties properties) throws IOException, NiFiRegistryException, ParseException { final String flowId = getRequiredArg(properties, CommandOption.FLOW_ID); @@ -68,7 +68,7 @@ public class DeleteFlow extends AbstractNiFiRegistryCommand<VoidResult> { } else { final FlowClient flowClient = client.getFlowClient(); flowClient.delete(bucketId, flowId); - return VoidResult.getInstance(); + return new OkResult(getContext().isInteractive()); } } } http://git-wip-us.apache.org/repos/asf/nifi/blob/5041bea7/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/flow/ImportFlowVersion.java ---------------------------------------------------------------------- diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/flow/ImportFlowVersion.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/flow/ImportFlowVersion.java index ca5384b..2b9dd63 100644 --- a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/flow/ImportFlowVersion.java +++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/flow/ImportFlowVersion.java @@ -113,7 +113,7 @@ public class ImportFlowVersion extends AbstractNiFiRegistryCommand<StringResult> final VersionedFlowSnapshot createdSnapshot = snapshotClient.create(snapshot); final VersionedFlowSnapshotMetadata createdMetadata = createdSnapshot.getSnapshotMetadata(); - return new StringResult(String.valueOf(createdMetadata.getVersion())); + return new StringResult(String.valueOf(createdMetadata.getVersion()), getContext().isInteractive()); } } http://git-wip-us.apache.org/repos/asf/nifi/blob/5041bea7/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/flow/SyncFlowVersions.java ---------------------------------------------------------------------- diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/flow/SyncFlowVersions.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/flow/SyncFlowVersions.java new file mode 100644 index 0000000..2cee5a5 --- /dev/null +++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/flow/SyncFlowVersions.java @@ -0,0 +1,120 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License 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 org.apache.nifi.toolkit.cli.impl.command.registry.flow; + +import org.apache.commons.cli.ParseException; +import org.apache.nifi.registry.client.NiFiRegistryClient; +import org.apache.nifi.registry.client.NiFiRegistryException; +import org.apache.nifi.registry.flow.VersionedFlowSnapshot; +import org.apache.nifi.registry.flow.VersionedFlowSnapshotMetadata; +import org.apache.nifi.toolkit.cli.api.Context; +import org.apache.nifi.toolkit.cli.impl.command.CommandOption; +import org.apache.nifi.toolkit.cli.impl.command.registry.AbstractNiFiRegistryCommand; +import org.apache.nifi.toolkit.cli.impl.result.OkResult; +import org.apache.nifi.toolkit.cli.impl.result.StringResult; + +import java.io.IOException; +import java.util.Collections; +import java.util.List; +import java.util.Properties; + +public class SyncFlowVersions extends AbstractNiFiRegistryCommand<StringResult> { + + public SyncFlowVersions() { + super("sync-flow-versions", StringResult.class); + } + + @Override + public String getDescription() { + return "Syncs the versions of a flow to another flow, which could be in a different bucket or registry. " + + "This command assumes the intent is to maintain the exact version history across the two flows. " + + "The list of versions from the source flow will be compared to the destination flow, and any " + + "versions not present will be added. If --" + CommandOption.SRC_PROPS.getLongName() + " is not " + + "provided then the source registry will be assumed to be the same as the destination registry."; + } + + @Override + protected void doInitialize(final Context context) { + // source properties + addOption(CommandOption.SRC_PROPS.createOption()); + + // source flow id + addOption(CommandOption.SRC_FLOW_ID.createOption()); + + // destination flow id + addOption(CommandOption.FLOW_ID.createOption()); + + // destination properties will come from standard -p or nifi.reg.props in session + } + + @Override + public StringResult doExecute(final NiFiRegistryClient client, final Properties properties) + throws IOException, NiFiRegistryException, ParseException { + + final String srcPropsValue = getArg(properties, CommandOption.SRC_PROPS); + final String srcFlowId = getRequiredArg(properties, CommandOption.SRC_FLOW_ID); + final String destFlowId = getRequiredArg(properties, CommandOption.FLOW_ID); + + final NiFiRegistryClient srcClient = getSourceClient(client, srcPropsValue); + + final String srcBucketId = getBucketId(srcClient, srcFlowId); + final String destBucketId = getBucketId(client, destFlowId); + + final List<Integer> srcVersions = getVersions(srcClient, srcBucketId, srcFlowId); + final List<Integer> destVersions = getVersions(client, destBucketId, destFlowId); + + if (destVersions.size() > srcVersions.size()) { + throw new NiFiRegistryException("Destination flow has more versions than source flow"); + } + + srcVersions.removeAll(destVersions); + + if (srcVersions.isEmpty()) { + if (getContext().isInteractive()) { + println(); + println("Source and destination already in sync"); + } + return new OkResult(getContext().isInteractive()); + } + + // the REST API returns versions in decreasing order, but we want them in increasing order + Collections.sort(srcVersions); + + for (final Integer srcVersion : srcVersions) { + final VersionedFlowSnapshot srcFlowSnapshot = srcClient.getFlowSnapshotClient().get(srcBucketId, srcFlowId, srcVersion); + srcFlowSnapshot.setFlow(null); + srcFlowSnapshot.setBucket(null); + + final VersionedFlowSnapshotMetadata destMetadata = new VersionedFlowSnapshotMetadata(); + destMetadata.setBucketIdentifier(destBucketId); + destMetadata.setFlowIdentifier(destFlowId); + destMetadata.setVersion(srcVersion); + destMetadata.setComments(srcFlowSnapshot.getSnapshotMetadata().getComments()); + + srcFlowSnapshot.setSnapshotMetadata(destMetadata); + client.getFlowSnapshotClient().create(srcFlowSnapshot); + + if (getContext().isInteractive()) { + println(); + println("Synced version " + srcVersion); + } + } + + return new OkResult(getContext().isInteractive()); + } + +} http://git-wip-us.apache.org/repos/asf/nifi/blob/5041bea7/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/flow/TransferFlowVersion.java ---------------------------------------------------------------------- diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/flow/TransferFlowVersion.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/flow/TransferFlowVersion.java new file mode 100644 index 0000000..08f0035 --- /dev/null +++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/registry/flow/TransferFlowVersion.java @@ -0,0 +1,118 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License 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 org.apache.nifi.toolkit.cli.impl.command.registry.flow; + +import org.apache.commons.cli.ParseException; +import org.apache.nifi.registry.client.NiFiRegistryClient; +import org.apache.nifi.registry.client.NiFiRegistryException; +import org.apache.nifi.registry.flow.VersionedFlowSnapshot; +import org.apache.nifi.registry.flow.VersionedFlowSnapshotMetadata; +import org.apache.nifi.toolkit.cli.api.Context; +import org.apache.nifi.toolkit.cli.impl.command.CommandOption; +import org.apache.nifi.toolkit.cli.impl.command.registry.AbstractNiFiRegistryCommand; +import org.apache.nifi.toolkit.cli.impl.result.OkResult; +import org.apache.nifi.toolkit.cli.impl.result.StringResult; + +import java.io.IOException; +import java.util.List; +import java.util.Properties; + +public class TransferFlowVersion extends AbstractNiFiRegistryCommand<StringResult> { + + public TransferFlowVersion() { + super("transfer-flow-version", StringResult.class); + } + + @Override + public String getDescription() { + return "Transfers a version of a flow directly from one flow to another, without needing to export/import. " + + "If --" + CommandOption.SRC_PROPS.getLongName() + " is not specified, the source flow is " + + "assumed to be in the same registry as the destination flow. " + + "If --" + CommandOption.SRC_FLOW_VERSION.getLongName() + " is not specified, then the latest " + + "version will be transferred."; + } + + @Override + protected void doInitialize(final Context context) { + // source properties + addOption(CommandOption.SRC_PROPS.createOption()); + + // source flow id + addOption(CommandOption.SRC_FLOW_ID.createOption()); + + // optional version of source flow, otherwise latest + addOption(CommandOption.SRC_FLOW_VERSION.createOption()); + + // destination flow id + addOption(CommandOption.FLOW_ID.createOption()); + + // destination properties will come from standard -p or nifi.reg.props in session + } + + @Override + public StringResult doExecute(final NiFiRegistryClient client, final Properties properties) + throws IOException, NiFiRegistryException, ParseException { + + final String srcPropsValue = getArg(properties, CommandOption.SRC_PROPS); + final String srcFlowId = getRequiredArg(properties, CommandOption.SRC_FLOW_ID); + final Integer srcFlowVersion = getIntArg(properties, CommandOption.SRC_FLOW_VERSION); + final String destFlowId = getRequiredArg(properties, CommandOption.FLOW_ID); + + final NiFiRegistryClient srcClient = getSourceClient(client, srcPropsValue); + + // determine the bucket ids of the source and dest flows + final String srcBucketId = getBucketId(srcClient, srcFlowId); + final String destBucketId = getBucketId(client, destFlowId); + + // get the snapshot of the source flow, either the version specified or the latest + final VersionedFlowSnapshot srcSnapshot; + if (srcFlowVersion == null) { + srcSnapshot = srcClient.getFlowSnapshotClient().getLatest(srcBucketId, srcFlowId); + } else { + srcSnapshot = srcClient.getFlowSnapshotClient().get(srcBucketId, srcFlowId, srcFlowVersion); + } + + // determine the next version number for the destination flow + final List<Integer> destVersions = getVersions(client, destBucketId, destFlowId); + final Integer destFlowVersion = destVersions.isEmpty() ? 1 : destVersions.get(0) + 1; + + // create the new metadata for the destination snapshot + final VersionedFlowSnapshotMetadata destMetadata = new VersionedFlowSnapshotMetadata(); + destMetadata.setBucketIdentifier(destBucketId); + destMetadata.setFlowIdentifier(destFlowId); + destMetadata.setVersion(destFlowVersion); + destMetadata.setComments(srcSnapshot.getSnapshotMetadata().getComments()); + + // update the source snapshot with the destination metadata + srcSnapshot.setFlow(null); + srcSnapshot.setBucket(null); + srcSnapshot.setSnapshotMetadata(destMetadata); + + // create the destination snapshot + client.getFlowSnapshotClient().create(srcSnapshot); + + if (getContext().isInteractive()) { + println(); + println("Transferred version " + srcSnapshot.getSnapshotMetadata().getVersion() + + " of source flow to version " + destFlowVersion + " of destination flow"); + } + + return new OkResult(getContext().isInteractive()); + } + + +} http://git-wip-us.apache.org/repos/asf/nifi/blob/5041bea7/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/session/GetVariable.java ---------------------------------------------------------------------- diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/session/GetVariable.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/session/GetVariable.java index 60ccb60..fb84d17 100644 --- a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/session/GetVariable.java +++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/session/GetVariable.java @@ -50,9 +50,9 @@ public class GetVariable extends AbstractCommand<StringResult> { try { final String value = session.get(args[0]); if (value == null) { - return new StringResult(""); + return new StringResult("", getContext().isInteractive()); } else { - return new StringResult(value); + return new StringResult(value, getContext().isInteractive()); } } catch (SessionException se) { throw new CommandException(se.getMessage(), se); http://git-wip-us.apache.org/repos/asf/nifi/blob/5041bea7/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/result/BucketsResult.java ---------------------------------------------------------------------- diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/result/BucketsResult.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/result/BucketsResult.java index 034889c..8fbd71f 100644 --- a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/result/BucketsResult.java +++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/result/BucketsResult.java @@ -21,7 +21,9 @@ import org.apache.nifi.registry.bucket.Bucket; import org.apache.nifi.toolkit.cli.api.Context; import org.apache.nifi.toolkit.cli.api.ReferenceResolver; import org.apache.nifi.toolkit.cli.api.Referenceable; +import org.apache.nifi.toolkit.cli.api.ResolvedReference; import org.apache.nifi.toolkit.cli.api.ResultType; +import org.apache.nifi.toolkit.cli.impl.command.CommandOption; import org.apache.nifi.toolkit.cli.impl.result.writer.DynamicTableWriter; import org.apache.nifi.toolkit.cli.impl.result.writer.Table; import org.apache.nifi.toolkit.cli.impl.result.writer.TableWriter; @@ -84,13 +86,10 @@ public class BucketsResult extends AbstractWritableResult<List<Bucket>> implemen return new ReferenceResolver() { @Override - public String resolve(final Integer position) { + public ResolvedReference resolve(final CommandOption option, final Integer position) { final Bucket bucket = backRefs.get(position); if (bucket != null) { - if (context.isInteractive()) { - context.getOutput().printf("Using a positional back-reference for '%s'%n", bucket.getName()); - } - return bucket.getIdentifier(); + return new ResolvedReference(option, position, bucket.getName(), bucket.getIdentifier()); } else { return null; } http://git-wip-us.apache.org/repos/asf/nifi/blob/5041bea7/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/result/OkResult.java ---------------------------------------------------------------------- diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/result/OkResult.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/result/OkResult.java new file mode 100644 index 0000000..582081a --- /dev/null +++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/result/OkResult.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License 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 org.apache.nifi.toolkit.cli.impl.result; + +/** + * Result for commands that want to print 'OK' for a successful command. + */ +public class OkResult extends StringResult { + + public static final String OK_VALUE = "OK"; + + public OkResult(final boolean isInteractive) { + super(OK_VALUE, isInteractive); + } + +} http://git-wip-us.apache.org/repos/asf/nifi/blob/5041bea7/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/result/ProcessGroupsResult.java ---------------------------------------------------------------------- diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/result/ProcessGroupsResult.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/result/ProcessGroupsResult.java index af0c9cd..05ac056 100644 --- a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/result/ProcessGroupsResult.java +++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/result/ProcessGroupsResult.java @@ -20,7 +20,9 @@ import org.apache.commons.lang3.Validate; import org.apache.nifi.toolkit.cli.api.Context; import org.apache.nifi.toolkit.cli.api.ReferenceResolver; import org.apache.nifi.toolkit.cli.api.Referenceable; +import org.apache.nifi.toolkit.cli.api.ResolvedReference; import org.apache.nifi.toolkit.cli.api.ResultType; +import org.apache.nifi.toolkit.cli.impl.command.CommandOption; import org.apache.nifi.toolkit.cli.impl.result.writer.DynamicTableWriter; import org.apache.nifi.toolkit.cli.impl.result.writer.Table; import org.apache.nifi.toolkit.cli.impl.result.writer.TableWriter; @@ -90,13 +92,10 @@ public class ProcessGroupsResult extends AbstractWritableResult<List<ProcessGrou return new ReferenceResolver() { @Override - public String resolve(final Integer position) { + public ResolvedReference resolve(final CommandOption option, final Integer position) { final ProcessGroupDTO pg = backRefs.get(position); if (pg != null) { - if (context.isInteractive()) { - context.getOutput().printf("Using a positional back-reference for '%s'%n", pg.getName()); - } - return pg.getId(); + return new ResolvedReference(option, position, pg.getName(), pg.getId()); } else { return null; } http://git-wip-us.apache.org/repos/asf/nifi/blob/5041bea7/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/result/StringResult.java ---------------------------------------------------------------------- diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/result/StringResult.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/result/StringResult.java index 1d82f65..e75c62d 100644 --- a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/result/StringResult.java +++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/result/StringResult.java @@ -27,9 +27,11 @@ import java.io.PrintStream; public class StringResult implements WritableResult<String> { private final String value; + private final boolean isInteractive; - public StringResult(final String value) { + public StringResult(final String value, final boolean isInteractive) { this.value = value; + this.isInteractive = isInteractive; Validate.notNull(this.value); } @@ -40,6 +42,12 @@ public class StringResult implements WritableResult<String> { @Override public void write(final PrintStream output) { + if (isInteractive) { + output.println(); + } output.println(value); + if (isInteractive) { + output.println(); + } } } http://git-wip-us.apache.org/repos/asf/nifi/blob/5041bea7/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/result/VersionedFlowsResult.java ---------------------------------------------------------------------- diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/result/VersionedFlowsResult.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/result/VersionedFlowsResult.java index 5f6cc18..678db73 100644 --- a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/result/VersionedFlowsResult.java +++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/result/VersionedFlowsResult.java @@ -21,7 +21,9 @@ import org.apache.nifi.registry.flow.VersionedFlow; import org.apache.nifi.toolkit.cli.api.Context; import org.apache.nifi.toolkit.cli.api.ReferenceResolver; import org.apache.nifi.toolkit.cli.api.Referenceable; +import org.apache.nifi.toolkit.cli.api.ResolvedReference; import org.apache.nifi.toolkit.cli.api.ResultType; +import org.apache.nifi.toolkit.cli.impl.command.CommandOption; import org.apache.nifi.toolkit.cli.impl.result.writer.DynamicTableWriter; import org.apache.nifi.toolkit.cli.impl.result.writer.Table; import org.apache.nifi.toolkit.cli.impl.result.writer.TableWriter; @@ -84,13 +86,14 @@ public class VersionedFlowsResult extends AbstractWritableResult<List<VersionedF return new ReferenceResolver() { @Override - public String resolve(final Integer position) { + public ResolvedReference resolve(final CommandOption option, final Integer position) { final VersionedFlow versionedFlow = backRefs.get(position); if (versionedFlow != null) { - if (context.isInteractive()) { - context.getOutput().printf("Using a positional backreference for '%s'%n", versionedFlow.getName()); + if (option != null && option == CommandOption.BUCKET_ID) { + return new ResolvedReference(option, position, versionedFlow.getBucketName(), versionedFlow.getBucketIdentifier()); + } else { + return new ResolvedReference(option, position, versionedFlow.getName(), versionedFlow.getIdentifier()); } - return versionedFlow.getIdentifier(); } else { return null; } http://git-wip-us.apache.org/repos/asf/nifi/blob/5041bea7/nifi-toolkit/nifi-toolkit-cli/src/test/java/org/apache/nifi/toolkit/cli/impl/command/CommandA.java ---------------------------------------------------------------------- diff --git a/nifi-toolkit/nifi-toolkit-cli/src/test/java/org/apache/nifi/toolkit/cli/impl/command/CommandA.java b/nifi-toolkit/nifi-toolkit-cli/src/test/java/org/apache/nifi/toolkit/cli/impl/command/CommandA.java new file mode 100644 index 0000000..86663d7 --- /dev/null +++ b/nifi-toolkit/nifi-toolkit-cli/src/test/java/org/apache/nifi/toolkit/cli/impl/command/CommandA.java @@ -0,0 +1,80 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License 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 org.apache.nifi.toolkit.cli.impl.command; + +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.Options; +import org.apache.nifi.toolkit.cli.api.Command; +import org.apache.nifi.toolkit.cli.api.CommandException; +import org.apache.nifi.toolkit.cli.api.Context; + +import java.util.List; + +public class CommandA implements Command<CommandAResult> { + + private final List<String> results; + + private CommandLine cli; + + public CommandA(final List<String> results) { + this.results = results; + } + + @Override + public void initialize(Context context) { + + } + + @Override + public String getName() { + return "command-a"; + } + + @Override + public String getDescription() { + return "command-a"; + } + + @Override + public Options getOptions() { + Options options = new Options(); + options.addOption(CommandOption.BUCKET_ID.createOption()); + options.addOption(CommandOption.FLOW_ID.createOption()); + return options; + } + + @Override + public void printUsage(String errorMessage) { + + } + + @Override + public CommandAResult execute(CommandLine cli) throws CommandException { + this.cli = cli; + return new CommandAResult(results); + } + + @Override + public Class<CommandAResult> getResultImplType() { + return CommandAResult.class; + } + + public CommandLine getCli() { + return this.cli; + } + +} http://git-wip-us.apache.org/repos/asf/nifi/blob/5041bea7/nifi-toolkit/nifi-toolkit-cli/src/test/java/org/apache/nifi/toolkit/cli/impl/command/CommandAResult.java ---------------------------------------------------------------------- diff --git a/nifi-toolkit/nifi-toolkit-cli/src/test/java/org/apache/nifi/toolkit/cli/impl/command/CommandAResult.java b/nifi-toolkit/nifi-toolkit-cli/src/test/java/org/apache/nifi/toolkit/cli/impl/command/CommandAResult.java new file mode 100644 index 0000000..432d93e --- /dev/null +++ b/nifi-toolkit/nifi-toolkit-cli/src/test/java/org/apache/nifi/toolkit/cli/impl/command/CommandAResult.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License 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 org.apache.nifi.toolkit.cli.impl.command; + +import org.apache.nifi.toolkit.cli.api.Context; +import org.apache.nifi.toolkit.cli.api.ReferenceResolver; +import org.apache.nifi.toolkit.cli.api.Referenceable; +import org.apache.nifi.toolkit.cli.api.ResolvedReference; +import org.apache.nifi.toolkit.cli.api.Result; + +import java.util.List; + +public class CommandAResult implements Result<List<String>>, Referenceable { + + private final List<String> results; + + public CommandAResult(final List<String> results) { + this.results = results; + } + + @Override + public List<String> getResult() { + return results; + } + + @Override + public ReferenceResolver createReferenceResolver(Context context) { + return new ReferenceResolver() { + @Override + public ResolvedReference resolve(CommandOption option, Integer position) { + if (position != null && position <= results.size()) { + return new ResolvedReference(option, position, "CommandA", results.get(position - 1)); + } else { + return null; + } + } + + @Override + public boolean isEmpty() { + return false; + } + }; + } + +} http://git-wip-us.apache.org/repos/asf/nifi/blob/5041bea7/nifi-toolkit/nifi-toolkit-cli/src/test/java/org/apache/nifi/toolkit/cli/impl/command/TestCommandProcessor.java ---------------------------------------------------------------------- diff --git a/nifi-toolkit/nifi-toolkit-cli/src/test/java/org/apache/nifi/toolkit/cli/impl/command/TestCommandProcessor.java b/nifi-toolkit/nifi-toolkit-cli/src/test/java/org/apache/nifi/toolkit/cli/impl/command/TestCommandProcessor.java new file mode 100644 index 0000000..465b3af --- /dev/null +++ b/nifi-toolkit/nifi-toolkit-cli/src/test/java/org/apache/nifi/toolkit/cli/impl/command/TestCommandProcessor.java @@ -0,0 +1,83 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License 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 org.apache.nifi.toolkit.cli.impl.command; + +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.ParseException; +import org.apache.nifi.toolkit.cli.api.Context; +import org.junit.Assert; +import org.junit.Test; +import org.mockito.Mockito; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class TestCommandProcessor { + + @Test + public void testCommandProcessor() throws ParseException { + final List<String> results = new ArrayList<>(); + results.add("foo1"); + results.add("foo2"); + + final CommandA command = new CommandA(results); + + final Context context = Mockito.mock(Context.class); + Mockito.when(context.getOutput()).thenReturn(System.out); + + // run the command once to set the previous results + final CommandProcessor processor = new CommandProcessor(Collections.emptyMap(), Collections.emptyMap(), context); + processor.processCommand(new String[] {}, command); + + // run it again and &1 should be resolved to foo1 + processor.processCommand( + new String[] { + "-" + CommandOption.BUCKET_ID.getShortName(), + "&1" + } , + command); + + final CommandLine cli1 = command.getCli(); + Assert.assertEquals("foo1", cli1.getOptionValue(CommandOption.BUCKET_ID.getShortName())); + + // run it again and &2 should be resolved to foo1 + processor.processCommand( + new String[] { + "-" + CommandOption.BUCKET_ID.getShortName(), + "&2" + }, + command); + + final CommandLine cli2 = command.getCli(); + Assert.assertEquals("foo2", cli2.getOptionValue(CommandOption.BUCKET_ID.getShortName())); + + // run it again and &1 should be resolved to foo1 + processor.processCommand( + new String[] { + "-" + CommandOption.BUCKET_ID.getShortName(), + "b1", + "-" + CommandOption.FLOW_ID.getShortName(), + "&1" + }, + command); + + final CommandLine cli3 = command.getCli(); + Assert.assertEquals("foo1", cli3.getOptionValue(CommandOption.FLOW_ID.getShortName())); + } + +} http://git-wip-us.apache.org/repos/asf/nifi/blob/5041bea7/nifi-toolkit/nifi-toolkit-cli/src/test/java/org/apache/nifi/toolkit/cli/impl/result/TestVersionedFlowsResult.java ---------------------------------------------------------------------- diff --git a/nifi-toolkit/nifi-toolkit-cli/src/test/java/org/apache/nifi/toolkit/cli/impl/result/TestVersionedFlowsResult.java b/nifi-toolkit/nifi-toolkit-cli/src/test/java/org/apache/nifi/toolkit/cli/impl/result/TestVersionedFlowsResult.java index bc0bd06..dc5abbb 100644 --- a/nifi-toolkit/nifi-toolkit-cli/src/test/java/org/apache/nifi/toolkit/cli/impl/result/TestVersionedFlowsResult.java +++ b/nifi-toolkit/nifi-toolkit-cli/src/test/java/org/apache/nifi/toolkit/cli/impl/result/TestVersionedFlowsResult.java @@ -17,10 +17,14 @@ package org.apache.nifi.toolkit.cli.impl.result; import org.apache.nifi.registry.flow.VersionedFlow; +import org.apache.nifi.toolkit.cli.api.Context; +import org.apache.nifi.toolkit.cli.api.ReferenceResolver; import org.apache.nifi.toolkit.cli.api.ResultType; +import org.apache.nifi.toolkit.cli.impl.command.CommandOption; import org.junit.Assert; import org.junit.Before; import org.junit.Test; +import org.mockito.Mockito; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -34,29 +38,34 @@ public class TestVersionedFlowsResult { private ByteArrayOutputStream outputStream; private PrintStream printStream; + private List<VersionedFlow> flows; @Before public void setup() { this.outputStream = new ByteArrayOutputStream(); this.printStream = new PrintStream(outputStream, true); - } - @Test - public void testWriteSimpleVersionedFlowsResult() throws IOException { final VersionedFlow f1 = new VersionedFlow(); f1.setName("Flow 1"); f1.setDescription("This is flow 1"); f1.setIdentifier(UUID.fromString("ea752054-22c6-4fc0-b851-967d9a3837cb").toString()); + f1.setBucketIdentifier("b1"); + f1.setBucketName("Bucket 1"); final VersionedFlow f2 = new VersionedFlow(); f2.setName("Flow 2"); f2.setDescription(null); f2.setIdentifier(UUID.fromString("ddf5f289-7502-46df-9798-4b0457c1816b").toString()); + f2.setBucketIdentifier("b2"); + f2.setBucketName("Bucket 2"); - final List<VersionedFlow> flows = new ArrayList<>(); + this.flows = new ArrayList<>(); flows.add(f1); flows.add(f2); + } + @Test + public void testWriteSimpleVersionedFlowsResult() throws IOException { final VersionedFlowsResult result = new VersionedFlowsResult(ResultType.SIMPLE, flows); result.write(printStream); @@ -73,4 +82,25 @@ public class TestVersionedFlowsResult { Assert.assertEquals(expected, resultOut); } + @Test + public void testReferenceResolver() { + final VersionedFlowsResult result = new VersionedFlowsResult(ResultType.SIMPLE, flows); + final ReferenceResolver resolver = result.createReferenceResolver(Mockito.mock(Context.class)); + + // should default to flow id when no option is specified + Assert.assertEquals("ea752054-22c6-4fc0-b851-967d9a3837cb", resolver.resolve(null, 1).getResolvedValue()); + Assert.assertEquals("ddf5f289-7502-46df-9798-4b0457c1816b", resolver.resolve(null, 2).getResolvedValue()); + + // should use flow id when flow id is specified + Assert.assertEquals("ea752054-22c6-4fc0-b851-967d9a3837cb", resolver.resolve(CommandOption.FLOW_ID, 1).getResolvedValue()); + Assert.assertEquals("ddf5f289-7502-46df-9798-4b0457c1816b", resolver.resolve(CommandOption.FLOW_ID, 2).getResolvedValue()); + + // should resolve the bucket id when bucket id option is used + Assert.assertEquals("b1", resolver.resolve(CommandOption.BUCKET_ID, 1).getResolvedValue()); + Assert.assertEquals("b2", resolver.resolve(CommandOption.BUCKET_ID, 2).getResolvedValue()); + + // should resolve to null when position doesn't exist + Assert.assertEquals(null, resolver.resolve(CommandOption.FLOW_ID, 3)); + } + }
