You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by ex...@apache.org on 2024/11/19 16:28:09 UTC

(nifi) branch main updated: NIFI-13950 Add commands to list branch, bucket, flows, versions to CLI (#9473)

This is an automated email from the ASF dual-hosted git repository.

exceptionfactory pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/nifi.git


The following commit(s) were added to refs/heads/main by this push:
     new 3508560b7a NIFI-13950 Add commands to list branch, bucket, flows, versions to CLI (#9473)
3508560b7a is described below

commit 3508560b7aae2884400e6f0b225610dc6f0c001f
Author: Pierre Villard <pi...@gmail.com>
AuthorDate: Tue Nov 19 17:28:02 2024 +0100

    NIFI-13950 Add commands to list branch, bucket, flows, versions to CLI (#9473)
    
    Signed-off-by: David Handermann <ex...@apache.org>
---
 .../toolkit/cli/impl/command/CommandOption.java    |  1 +
 .../cli/impl/command/nifi/NiFiCommandGroup.java    |  8 ++
 .../impl/command/nifi/registry/ListBranches.java   | 59 +++++++++++++
 .../impl/command/nifi/registry/ListBuckets.java    | 61 ++++++++++++++
 .../command/nifi/registry/ListFlowVersions.java    | 66 +++++++++++++++
 .../cli/impl/command/nifi/registry/ListFlows.java  | 63 ++++++++++++++
 .../impl/result/nifi/RegistryBranchesResult.java   | 77 +++++++++++++++++
 .../impl/result/nifi/RegistryBucketsResult.java    | 79 +++++++++++++++++
 .../result/nifi/RegistryFlowVersionsResult.java    | 91 ++++++++++++++++++++
 .../cli/impl/result/nifi/RegistryFlowsResult.java  | 79 +++++++++++++++++
 .../org/apache/nifi/toolkit/client/FlowClient.java | 39 +++++++++
 .../nifi/toolkit/client/impl/JerseyFlowClient.java | 98 ++++++++++++++++++++--
 12 files changed, 712 insertions(+), 9 deletions(-)

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 5bcf04d945..84da0839cf 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
@@ -47,6 +47,7 @@ public enum CommandOption {
     FLOW_NAME("fn", "flowName", "A flow name", true),
     FLOW_DESC("fd", "flowDesc", "A flow description", true),
     FLOW_VERSION("fv", "flowVersion", "A version of a flow", true),
+    FLOW_BRANCH("fb", "flowBranch", "A branch for the flow", true),
 
     FLOW_VERSION_1("fv1", "flowVersion1", "A version of a flow", true),
     FLOW_VERSION_2("fv2", "flowVersion2", "A version of a flow", true),
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/NiFiCommandGroup.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/NiFiCommandGroup.java
index e244a44333..f916f0fc5a 100644
--- a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/NiFiCommandGroup.java
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/NiFiCommandGroup.java
@@ -110,6 +110,10 @@ import org.apache.nifi.toolkit.cli.impl.command.nifi.processors.ProcessorRunOnce
 import org.apache.nifi.toolkit.cli.impl.command.nifi.processors.ProcessorStart;
 import org.apache.nifi.toolkit.cli.impl.command.nifi.registry.CreateRegistryClient;
 import org.apache.nifi.toolkit.cli.impl.command.nifi.registry.GetRegistryClientId;
+import org.apache.nifi.toolkit.cli.impl.command.nifi.registry.ListBranches;
+import org.apache.nifi.toolkit.cli.impl.command.nifi.registry.ListBuckets;
+import org.apache.nifi.toolkit.cli.impl.command.nifi.registry.ListFlowVersions;
+import org.apache.nifi.toolkit.cli.impl.command.nifi.registry.ListFlows;
 import org.apache.nifi.toolkit.cli.impl.command.nifi.registry.ListRegistryClients;
 import org.apache.nifi.toolkit.cli.impl.command.nifi.registry.UpdateRegistryClient;
 import org.apache.nifi.toolkit.cli.impl.command.nifi.reporting.DeleteReportingTask;
@@ -149,6 +153,10 @@ public class NiFiCommandGroup extends AbstractCommandGroup {
         commands.add(new CreateRegistryClient());
         commands.add(new UpdateRegistryClient());
         commands.add(new GetRegistryClientId());
+        commands.add(new ListBranches());
+        commands.add(new ListBuckets());
+        commands.add(new ListFlows());
+        commands.add(new ListFlowVersions());
         commands.add(new PGImport());
         commands.add(new PGConnect());
         commands.add(new PGStart());
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/registry/ListBranches.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/registry/ListBranches.java
new file mode 100644
index 0000000000..6d88ad5f30
--- /dev/null
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/registry/ListBranches.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.nifi.registry;
+
+import org.apache.commons.cli.MissingOptionException;
+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.nifi.AbstractNiFiCommand;
+import org.apache.nifi.toolkit.cli.impl.result.nifi.RegistryBranchesResult;
+import org.apache.nifi.toolkit.client.NiFiClient;
+import org.apache.nifi.toolkit.client.NiFiClientException;
+import org.apache.nifi.web.api.entity.FlowRegistryBranchesEntity;
+
+import java.io.IOException;
+import java.util.Properties;
+
+/**
+ * Lists the branches seen by the specified registry client.
+ */
+public class ListBranches extends AbstractNiFiCommand<RegistryBranchesResult> {
+
+    public ListBranches() {
+        super("list-branches", RegistryBranchesResult.class);
+    }
+
+    @Override
+    protected void doInitialize(Context context) {
+        super.doInitialize(context);
+        addOption(CommandOption.REGISTRY_CLIENT_ID.createOption());
+    }
+
+    @Override
+    public String getDescription() {
+        return "Returns the list of branches seen by the specified registry client.";
+    }
+
+    @Override
+    public RegistryBranchesResult doExecute(final NiFiClient client, final Properties properties)
+            throws NiFiClientException, IOException, MissingOptionException {
+        final String regClientId = getRequiredArg(properties, CommandOption.REGISTRY_CLIENT_ID);
+        final FlowRegistryBranchesEntity branches = client.getFlowClient().getFlowRegistryBranches(regClientId);
+        return new RegistryBranchesResult(getResultType(properties), branches);
+    }
+
+}
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/registry/ListBuckets.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/registry/ListBuckets.java
new file mode 100644
index 0000000000..31f9ed904d
--- /dev/null
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/registry/ListBuckets.java
@@ -0,0 +1,61 @@
+/*
+ * 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.nifi.registry;
+
+import org.apache.commons.cli.MissingOptionException;
+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.nifi.AbstractNiFiCommand;
+import org.apache.nifi.toolkit.cli.impl.result.nifi.RegistryBucketsResult;
+import org.apache.nifi.toolkit.client.NiFiClient;
+import org.apache.nifi.toolkit.client.NiFiClientException;
+import org.apache.nifi.web.api.entity.FlowRegistryBucketsEntity;
+
+import java.io.IOException;
+import java.util.Properties;
+
+/**
+ * Lists buckets for a given branch seen by a given registry client
+ */
+public class ListBuckets extends AbstractNiFiCommand<RegistryBucketsResult> {
+
+    public ListBuckets() {
+        super("list-buckets", RegistryBucketsResult.class);
+    }
+
+    @Override
+    protected void doInitialize(Context context) {
+        super.doInitialize(context);
+        addOption(CommandOption.REGISTRY_CLIENT_ID.createOption());
+        addOption(CommandOption.FLOW_BRANCH.createOption());
+    }
+
+    @Override
+    public String getDescription() {
+        return "Returns the list of branches seen by the specified registry client.";
+    }
+
+    @Override
+    public RegistryBucketsResult doExecute(final NiFiClient client, final Properties properties)
+            throws NiFiClientException, IOException, MissingOptionException {
+        final String regClientId = getRequiredArg(properties, CommandOption.REGISTRY_CLIENT_ID);
+        final String branchName = getRequiredArg(properties, CommandOption.FLOW_BRANCH);
+        final FlowRegistryBucketsEntity buckets = client.getFlowClient().getFlowRegistryBuckets(regClientId, branchName);
+        return new RegistryBucketsResult(getResultType(properties), buckets);
+    }
+
+}
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/registry/ListFlowVersions.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/registry/ListFlowVersions.java
new file mode 100644
index 0000000000..8d318eabff
--- /dev/null
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/registry/ListFlowVersions.java
@@ -0,0 +1,66 @@
+/*
+ * 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.nifi.registry;
+
+import org.apache.commons.cli.MissingOptionException;
+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.nifi.AbstractNiFiCommand;
+import org.apache.nifi.toolkit.cli.impl.result.nifi.RegistryFlowVersionsResult;
+import org.apache.nifi.toolkit.client.NiFiClient;
+import org.apache.nifi.toolkit.client.NiFiClientException;
+import org.apache.nifi.web.api.entity.VersionedFlowSnapshotMetadataSetEntity;
+
+import java.io.IOException;
+import java.util.Properties;
+
+/**
+ * Lists flow versions for a given flow in a given branch and bucket seen by a
+ * given registry client
+ */
+public class ListFlowVersions extends AbstractNiFiCommand<RegistryFlowVersionsResult> {
+
+    public ListFlowVersions() {
+        super("list-flow-versions", RegistryFlowVersionsResult.class);
+    }
+
+    @Override
+    protected void doInitialize(Context context) {
+        super.doInitialize(context);
+        addOption(CommandOption.REGISTRY_CLIENT_ID.createOption());
+        addOption(CommandOption.FLOW_BRANCH.createOption());
+        addOption(CommandOption.BUCKET_ID.createOption());
+        addOption(CommandOption.FLOW_ID.createOption());
+    }
+
+    @Override
+    public String getDescription() {
+        return "Returns the list of flow versions for a given flow in a given branch and bucket seen by the specified registry client.";
+    }
+
+    @Override
+    public RegistryFlowVersionsResult doExecute(final NiFiClient client, final Properties properties)
+            throws NiFiClientException, IOException, MissingOptionException {
+        final String regClientId = getRequiredArg(properties, CommandOption.REGISTRY_CLIENT_ID);
+        final String branchName = getRequiredArg(properties, CommandOption.FLOW_BRANCH);
+        final String bucketId = getRequiredArg(properties, CommandOption.BUCKET_ID);
+        final String flowId = getRequiredArg(properties, CommandOption.FLOW_ID);
+        final VersionedFlowSnapshotMetadataSetEntity flowVersions = client.getFlowClient().getVersions(regClientId, bucketId, flowId, branchName);
+        return new RegistryFlowVersionsResult(getResultType(properties), flowVersions);
+    }
+
+}
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/registry/ListFlows.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/registry/ListFlows.java
new file mode 100644
index 0000000000..f97f479fa7
--- /dev/null
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/registry/ListFlows.java
@@ -0,0 +1,63 @@
+/*
+ * 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.nifi.registry;
+
+import org.apache.commons.cli.MissingOptionException;
+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.nifi.AbstractNiFiCommand;
+import org.apache.nifi.toolkit.cli.impl.result.nifi.RegistryFlowsResult;
+import org.apache.nifi.toolkit.client.NiFiClient;
+import org.apache.nifi.toolkit.client.NiFiClientException;
+import org.apache.nifi.web.api.entity.VersionedFlowsEntity;
+
+import java.io.IOException;
+import java.util.Properties;
+
+/**
+ * Lists flows for a given branch and bucket seen by a given registry client
+ */
+public class ListFlows extends AbstractNiFiCommand<RegistryFlowsResult> {
+
+    public ListFlows() {
+        super("list-flows", RegistryFlowsResult.class);
+    }
+
+    @Override
+    protected void doInitialize(Context context) {
+        super.doInitialize(context);
+        addOption(CommandOption.REGISTRY_CLIENT_ID.createOption());
+        addOption(CommandOption.FLOW_BRANCH.createOption());
+        addOption(CommandOption.BUCKET_ID.createOption());
+    }
+
+    @Override
+    public String getDescription() {
+        return "Returns the list of flows in a given branch and bucket seen by the specified registry client.";
+    }
+
+    @Override
+    public RegistryFlowsResult doExecute(final NiFiClient client, final Properties properties)
+            throws NiFiClientException, IOException, MissingOptionException {
+        final String regClientId = getRequiredArg(properties, CommandOption.REGISTRY_CLIENT_ID);
+        final String branchName = getRequiredArg(properties, CommandOption.FLOW_BRANCH);
+        final String bucketId = getRequiredArg(properties, CommandOption.BUCKET_ID);
+        final VersionedFlowsEntity flows = client.getFlowClient().getFlowRegistryFlows(regClientId, branchName, bucketId);
+        return new RegistryFlowsResult(getResultType(properties), flows);
+    }
+
+}
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/result/nifi/RegistryBranchesResult.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/result/nifi/RegistryBranchesResult.java
new file mode 100644
index 0000000000..4c8cec5213
--- /dev/null
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/result/nifi/RegistryBranchesResult.java
@@ -0,0 +1,77 @@
+/*
+ * 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.nifi;
+
+import org.apache.nifi.toolkit.cli.api.ResultType;
+import org.apache.nifi.toolkit.cli.impl.result.AbstractWritableResult;
+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;
+import org.apache.nifi.web.api.dto.FlowRegistryBranchDTO;
+import org.apache.nifi.web.api.entity.FlowRegistryBranchEntity;
+import org.apache.nifi.web.api.entity.FlowRegistryBranchesEntity;
+
+import java.io.PrintStream;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Result for a FlowRegistryBranchesEntity.
+ */
+public class RegistryBranchesResult extends AbstractWritableResult<FlowRegistryBranchesEntity> {
+
+    final FlowRegistryBranchesEntity branchesEntity;
+
+    public RegistryBranchesResult(final ResultType resultType, final FlowRegistryBranchesEntity branchesEntity) {
+        super(resultType);
+        this.branchesEntity = Objects.requireNonNull(branchesEntity);
+    }
+
+    @Override
+    public FlowRegistryBranchesEntity getResult() {
+        return this.branchesEntity;
+    }
+
+    @Override
+    protected void writeSimpleResult(final PrintStream output) {
+        final Set<FlowRegistryBranchEntity> branches = branchesEntity.getBranches();
+        if (branches == null || branches.isEmpty()) {
+            return;
+        }
+
+        final List<FlowRegistryBranchDTO> branchesDTO = branches.stream()
+            .map(b -> b.getBranch())
+            .sorted(Comparator.comparing(FlowRegistryBranchDTO::getName))
+            .toList();
+
+        final Table table = new Table.Builder()
+                .column("#", 3, 3, false)
+                .column("Name", 20, 36, true)
+                .build();
+
+        for (int i = 0; i < branchesDTO.size(); i++) {
+            FlowRegistryBranchDTO branch = branchesDTO.get(i);
+            table.addRow("" + (i + 1), branch.getName());
+        }
+
+        final TableWriter tableWriter = new DynamicTableWriter();
+        tableWriter.write(table, output);
+    }
+
+}
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/result/nifi/RegistryBucketsResult.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/result/nifi/RegistryBucketsResult.java
new file mode 100644
index 0000000000..a6b5daf545
--- /dev/null
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/result/nifi/RegistryBucketsResult.java
@@ -0,0 +1,79 @@
+/*
+ * 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.nifi;
+
+import org.apache.nifi.toolkit.cli.api.ResultType;
+import org.apache.nifi.toolkit.cli.impl.result.AbstractWritableResult;
+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;
+import org.apache.nifi.web.api.dto.FlowRegistryBucketDTO;
+import org.apache.nifi.web.api.entity.FlowRegistryBucketEntity;
+import org.apache.nifi.web.api.entity.FlowRegistryBucketsEntity;
+
+import java.io.PrintStream;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Result for a FlowRegistryBucketsEntity.
+ */
+public class RegistryBucketsResult extends AbstractWritableResult<FlowRegistryBucketsEntity> {
+
+    final FlowRegistryBucketsEntity bucketsEntity;
+
+    public RegistryBucketsResult(final ResultType resultType, final FlowRegistryBucketsEntity bucketsEntity) {
+        super(resultType);
+        this.bucketsEntity = Objects.requireNonNull(bucketsEntity);
+    }
+
+    @Override
+    public FlowRegistryBucketsEntity getResult() {
+        return this.bucketsEntity;
+    }
+
+    @Override
+    protected void writeSimpleResult(final PrintStream output) {
+        final Set<FlowRegistryBucketEntity> buckets = bucketsEntity.getBuckets();
+        if (buckets == null || buckets.isEmpty()) {
+            return;
+        }
+
+        final List<FlowRegistryBucketDTO> bucketsDTO = buckets.stream()
+            .map(b -> b.getBucket())
+            .sorted(Comparator.comparing(FlowRegistryBucketDTO::getName))
+            .toList();
+
+        final Table table = new Table.Builder()
+                .column("#", 3, 3, false)
+                .column("Name", 20, 36, true)
+                .column("Id", 36, 36, false)
+                .column("Description", 11, 40, true)
+                .build();
+
+        for (int i = 0; i < bucketsDTO.size(); i++) {
+            FlowRegistryBucketDTO bucket = bucketsDTO.get(i);
+            table.addRow("" + (i + 1), bucket.getName(), bucket.getId(), bucket.getDescription() == null ? "" : bucket.getDescription());
+        }
+
+        final TableWriter tableWriter = new DynamicTableWriter();
+        tableWriter.write(table, output);
+    }
+
+}
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/result/nifi/RegistryFlowVersionsResult.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/result/nifi/RegistryFlowVersionsResult.java
new file mode 100644
index 0000000000..d47c982fb7
--- /dev/null
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/result/nifi/RegistryFlowVersionsResult.java
@@ -0,0 +1,91 @@
+/*
+ * 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.nifi;
+
+import org.apache.nifi.registry.flow.RegisteredFlowSnapshotMetadata;
+import org.apache.nifi.toolkit.cli.api.ResultType;
+import org.apache.nifi.toolkit.cli.impl.result.AbstractWritableResult;
+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;
+import org.apache.nifi.web.api.entity.VersionedFlowSnapshotMetadataEntity;
+import org.apache.nifi.web.api.entity.VersionedFlowSnapshotMetadataSetEntity;
+
+import java.io.PrintStream;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Result for a VersionedFlowSnapshotMetadataSetEntity.
+ */
+public class RegistryFlowVersionsResult extends AbstractWritableResult<VersionedFlowSnapshotMetadataSetEntity> {
+
+    final VersionedFlowSnapshotMetadataSetEntity flowVersionsEntity;
+
+    public RegistryFlowVersionsResult(final ResultType resultType, final VersionedFlowSnapshotMetadataSetEntity flowVersionsEntity) {
+        super(resultType);
+        this.flowVersionsEntity = Objects.requireNonNull(flowVersionsEntity);
+    }
+
+    @Override
+    public VersionedFlowSnapshotMetadataSetEntity getResult() {
+        return this.flowVersionsEntity;
+    }
+
+    @Override
+    protected void writeSimpleResult(final PrintStream output) {
+        final Set<VersionedFlowSnapshotMetadataEntity> flowVersions = flowVersionsEntity.getVersionedFlowSnapshotMetadataSet();
+        if (flowVersions == null || flowVersions.isEmpty()) {
+            return;
+        }
+
+        final List<RegisteredFlowSnapshotMetadata> flowVersionsMetadata = flowVersions.stream()
+            .map(fv -> fv.getVersionedFlowSnapshotMetadata())
+            .sorted(Comparator.comparingLong(RegisteredFlowSnapshotMetadata::getTimestamp))
+            .toList();
+
+        // date length, with locale specifics
+        final String datePattern = "%1$ta, %<tb %<td %<tY %<tR %<tZ";
+        final int dateLength = String.format(datePattern, new Date()).length();
+
+        final Table table = new Table.Builder()
+                .column("#", 3, 3, false)
+                .column("Version ID", 3, 50, false)
+                .column("Date", dateLength, dateLength + 10, false)
+                .column("Author", 20, 100, true)
+                .column("Message", 8, 100, true)
+                .build();
+
+        for (int i = 0; i < flowVersionsMetadata.size(); i++) {
+            RegisteredFlowSnapshotMetadata vfs = flowVersionsMetadata.get(i);
+            table.addRow(
+                    "" + (i + 1),
+                    vfs.getVersion(),
+                    String.format(datePattern, new Date(vfs.getTimestamp())),
+                    vfs.getAuthor() == null ? "" : vfs.getAuthor(),
+                    vfs.getComments() == null ? "" : vfs.getComments()
+            );
+        }
+
+        final TableWriter tableWriter = new DynamicTableWriter();
+        tableWriter.write(table, output);
+    }
+
+}
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/result/nifi/RegistryFlowsResult.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/result/nifi/RegistryFlowsResult.java
new file mode 100644
index 0000000000..484be8c276
--- /dev/null
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/result/nifi/RegistryFlowsResult.java
@@ -0,0 +1,79 @@
+/*
+ * 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.nifi;
+
+import org.apache.nifi.toolkit.cli.api.ResultType;
+import org.apache.nifi.toolkit.cli.impl.result.AbstractWritableResult;
+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;
+import org.apache.nifi.web.api.dto.VersionedFlowDTO;
+import org.apache.nifi.web.api.entity.VersionedFlowEntity;
+import org.apache.nifi.web.api.entity.VersionedFlowsEntity;
+
+import java.io.PrintStream;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Result for a VersionedFlowsEntity.
+ */
+public class RegistryFlowsResult extends AbstractWritableResult<VersionedFlowsEntity> {
+
+    final VersionedFlowsEntity flowsEntity;
+
+    public RegistryFlowsResult(final ResultType resultType, final VersionedFlowsEntity flowsEntity) {
+        super(resultType);
+        this.flowsEntity = Objects.requireNonNull(flowsEntity);
+    }
+
+    @Override
+    public VersionedFlowsEntity getResult() {
+        return this.flowsEntity;
+    }
+
+    @Override
+    protected void writeSimpleResult(final PrintStream output) {
+        final Set<VersionedFlowEntity> flows = flowsEntity.getVersionedFlows();
+        if (flows == null || flows.isEmpty()) {
+            return;
+        }
+
+        final List<VersionedFlowDTO> flowsDTO = flows.stream()
+            .map(f -> f.getVersionedFlow())
+            .sorted(Comparator.comparing(VersionedFlowDTO::getFlowName))
+            .toList();
+
+        final Table table = new Table.Builder()
+                .column("#", 3, 3, false)
+                .column("Name", 20, 36, true)
+                .column("Id", 36, 36, false)
+                .column("Description", 11, 40, true)
+                .build();
+
+        for (int i = 0; i < flowsDTO.size(); i++) {
+            VersionedFlowDTO flow = flowsDTO.get(i);
+            table.addRow("" + (i + 1), flow.getFlowName(), flow.getFlowId(), flow.getDescription() == null ? "" : flow.getDescription());
+        }
+
+        final TableWriter tableWriter = new DynamicTableWriter();
+        tableWriter.write(table, output);
+    }
+
+}
diff --git a/nifi-toolkit/nifi-toolkit-client/src/main/java/org/apache/nifi/toolkit/client/FlowClient.java b/nifi-toolkit/nifi-toolkit-client/src/main/java/org/apache/nifi/toolkit/client/FlowClient.java
index 01c9cc93f6..e483f1c91a 100644
--- a/nifi-toolkit/nifi-toolkit-client/src/main/java/org/apache/nifi/toolkit/client/FlowClient.java
+++ b/nifi-toolkit/nifi-toolkit-client/src/main/java/org/apache/nifi/toolkit/client/FlowClient.java
@@ -23,6 +23,8 @@ import org.apache.nifi.web.api.entity.ConnectionStatusEntity;
 import org.apache.nifi.web.api.entity.ControllerServiceTypesEntity;
 import org.apache.nifi.web.api.entity.ControllerServicesEntity;
 import org.apache.nifi.web.api.entity.CurrentUserEntity;
+import org.apache.nifi.web.api.entity.FlowRegistryBranchesEntity;
+import org.apache.nifi.web.api.entity.FlowRegistryBucketsEntity;
 import org.apache.nifi.web.api.entity.ParameterProvidersEntity;
 import org.apache.nifi.web.api.entity.ProcessGroupFlowEntity;
 import org.apache.nifi.web.api.entity.ProcessGroupStatusEntity;
@@ -31,6 +33,7 @@ import org.apache.nifi.web.api.entity.ReportingTaskTypesEntity;
 import org.apache.nifi.web.api.entity.ReportingTasksEntity;
 import org.apache.nifi.web.api.entity.ScheduleComponentsEntity;
 import org.apache.nifi.web.api.entity.VersionedFlowSnapshotMetadataSetEntity;
+import org.apache.nifi.web.api.entity.VersionedFlowsEntity;
 
 import java.io.IOException;
 
@@ -92,6 +95,18 @@ public interface FlowClient {
     VersionedFlowSnapshotMetadataSetEntity getVersions(String registryId, String bucketId, String flowId)
             throws NiFiClientException, IOException;
 
+    /**
+     * Gets the possible versions for the given flow in the given bucket/branch in the given registry.
+     *
+     * @param registryId the id of the registry client
+     * @param bucketId the bucket id
+     * @param flowId the flow id
+     * @param branch the branch name
+     * @return the set of snapshot metadata entities
+     */
+    VersionedFlowSnapshotMetadataSetEntity getVersions(String registryId, String bucketId, String flowId, String branch)
+            throws NiFiClientException, IOException;
+
     /**
      * Retrieves the controller services for the given group.
      *
@@ -190,4 +205,28 @@ public interface FlowClient {
      * @return the reporting task types
      */
     ReportingTaskTypesEntity getReportingTaskTypes() throws NiFiClientException, IOException;
+
+    /**
+     * Returns the list of branches for the specified registry client ID
+     * @param registryClientId Registry Client ID
+     * @return list of branches
+     */
+    FlowRegistryBranchesEntity getFlowRegistryBranches(String registryClientId) throws NiFiClientException, IOException;
+
+    /**
+     * Returns the list of buckets in a given branch for the specified registry client ID
+     * @param registryClientId Registry Client ID
+     * @param branch Name of the branch
+     * @return list of buckets
+     */
+    FlowRegistryBucketsEntity getFlowRegistryBuckets(String registryClientId, String branch) throws NiFiClientException, IOException;
+
+    /**
+     * Returns the list of flows in a given branch and bucket for the specified registry client ID
+     * @param registryClientId Registry Client ID
+     * @param branch Name of the branch
+     * @param bucket ID of the bucket
+     * @return list of flows
+     */
+    VersionedFlowsEntity getFlowRegistryFlows(String registryClientId, String branch, String bucket) throws NiFiClientException, IOException;
 }
diff --git a/nifi-toolkit/nifi-toolkit-client/src/main/java/org/apache/nifi/toolkit/client/impl/JerseyFlowClient.java b/nifi-toolkit/nifi-toolkit-client/src/main/java/org/apache/nifi/toolkit/client/impl/JerseyFlowClient.java
index f3e83b0ebe..114c4e01c2 100644
--- a/nifi-toolkit/nifi-toolkit-client/src/main/java/org/apache/nifi/toolkit/client/impl/JerseyFlowClient.java
+++ b/nifi-toolkit/nifi-toolkit-client/src/main/java/org/apache/nifi/toolkit/client/impl/JerseyFlowClient.java
@@ -35,6 +35,8 @@ import org.apache.nifi.web.api.entity.ConnectionStatusEntity;
 import org.apache.nifi.web.api.entity.ControllerServiceTypesEntity;
 import org.apache.nifi.web.api.entity.ControllerServicesEntity;
 import org.apache.nifi.web.api.entity.CurrentUserEntity;
+import org.apache.nifi.web.api.entity.FlowRegistryBranchesEntity;
+import org.apache.nifi.web.api.entity.FlowRegistryBucketsEntity;
 import org.apache.nifi.web.api.entity.ParameterProvidersEntity;
 import org.apache.nifi.web.api.entity.ProcessGroupFlowEntity;
 import org.apache.nifi.web.api.entity.ProcessGroupStatusEntity;
@@ -43,6 +45,7 @@ import org.apache.nifi.web.api.entity.ReportingTaskTypesEntity;
 import org.apache.nifi.web.api.entity.ReportingTasksEntity;
 import org.apache.nifi.web.api.entity.ScheduleComponentsEntity;
 import org.apache.nifi.web.api.entity.VersionedFlowSnapshotMetadataSetEntity;
+import org.apache.nifi.web.api.entity.VersionedFlowsEntity;
 
 import java.io.IOException;
 import java.util.ArrayList;
@@ -164,6 +167,12 @@ public class JerseyFlowClient extends AbstractJerseyClient implements FlowClient
     @Override
     public VersionedFlowSnapshotMetadataSetEntity getVersions(final String registryId, final String bucketId, final String flowId)
             throws NiFiClientException, IOException {
+        return getVersions(registryId, bucketId, flowId, null);
+    }
+
+    @Override
+    public VersionedFlowSnapshotMetadataSetEntity getVersions(final String registryId, final String bucketId, final String flowId, final String branch)
+            throws NiFiClientException, IOException {
 
         if (StringUtils.isBlank(registryId)) {
             throw new IllegalArgumentException("Registry id cannot be null");
@@ -177,15 +186,26 @@ public class JerseyFlowClient extends AbstractJerseyClient implements FlowClient
             throw new IllegalArgumentException("Flow id cannot be null");
         }
 
-        return executeAction("Error retrieving versions", () -> {
-            final WebTarget target = flowTarget
-                    .path("registries/{registry-id}/buckets/{bucket-id}/flows/{flow-id}/versions")
-                    .resolveTemplate("registry-id", registryId)
-                    .resolveTemplate("bucket-id", bucketId)
-                    .resolveTemplate("flow-id", flowId);
-
-            return getRequestBuilder(target).get(VersionedFlowSnapshotMetadataSetEntity.class);
-        });
+        if (branch == null) {
+            return executeAction("Error retrieving versions", () -> {
+                final WebTarget target = flowTarget
+                        .path("registries/{registry-id}/buckets/{bucket-id}/flows/{flow-id}/versions")
+                        .resolveTemplate("registry-id", registryId)
+                        .resolveTemplate("bucket-id", bucketId)
+                        .resolveTemplate("flow-id", flowId);
+                return getRequestBuilder(target).get(VersionedFlowSnapshotMetadataSetEntity.class);
+            });
+        } else {
+            return executeAction("Error retrieving versions", () -> {
+                final WebTarget target = flowTarget
+                        .path("registries/{registry-id}/buckets/{bucket-id}/flows/{flow-id}/versions")
+                        .resolveTemplate("registry-id", registryId)
+                        .resolveTemplate("bucket-id", bucketId)
+                        .resolveTemplate("flow-id", flowId)
+                        .queryParam("branch", branch);
+                return getRequestBuilder(target).get(VersionedFlowSnapshotMetadataSetEntity.class);
+            });
+        }
     }
 
     @Override
@@ -320,4 +340,64 @@ public class JerseyFlowClient extends AbstractJerseyClient implements FlowClient
             return getRequestBuilder(target).get(ReportingTaskTypesEntity.class);
         });
     }
+
+    @Override
+    public FlowRegistryBranchesEntity getFlowRegistryBranches(String registryClientId)
+            throws NiFiClientException, IOException {
+
+        if (StringUtils.isBlank(registryClientId)) {
+            throw new IllegalArgumentException("Registry ID cannot be null");
+        }
+
+        return executeAction("Error retrieving branches", () -> {
+            final WebTarget target = flowTarget.path("/registries/{id}/branches")
+                .resolveTemplate("id", registryClientId);
+            return getRequestBuilder(target).get(FlowRegistryBranchesEntity.class);
+        });
+    }
+
+    @Override
+    public FlowRegistryBucketsEntity getFlowRegistryBuckets(String registryClientId, String branch)
+            throws NiFiClientException, IOException {
+
+        if (StringUtils.isBlank(registryClientId)) {
+            throw new IllegalArgumentException("Registry ID cannot be null");
+        }
+
+        if (StringUtils.isBlank(branch)) {
+            throw new IllegalArgumentException("Branch name cannot be null");
+        }
+
+        return executeAction("Error retrieving buckets", () -> {
+            final WebTarget target = flowTarget.path("/registries/{id}/buckets")
+                .resolveTemplate("id", registryClientId)
+                .queryParam("branch", branch);
+            return getRequestBuilder(target).get(FlowRegistryBucketsEntity.class);
+        });
+    }
+
+    @Override
+    public VersionedFlowsEntity getFlowRegistryFlows(String registryClientId, String branch, String bucket)
+            throws NiFiClientException, IOException {
+
+        if (StringUtils.isBlank(registryClientId)) {
+            throw new IllegalArgumentException("Registry ID cannot be null");
+        }
+
+        if (StringUtils.isBlank(bucket)) {
+            throw new IllegalArgumentException("Bucket ID cannot be null");
+        }
+
+        if (StringUtils.isBlank(branch)) {
+            throw new IllegalArgumentException("Branch name cannot be null");
+        }
+
+        return executeAction("Error retrieving buckets", () -> {
+            final WebTarget target = flowTarget.path("/registries/{registry-id}/buckets/{bucket-id}/flows")
+                .resolveTemplate("registry-id", registryClientId)
+                .resolveTemplate("bucket-id", bucket)
+                .queryParam("branch", branch);
+            return getRequestBuilder(target).get(VersionedFlowsEntity.class);
+        });
+    }
 }