You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by fe...@apache.org on 2024/09/24 08:00:06 UTC

(nifi) branch main updated: NIFI-13358 - NiFi Registry - rework action menus for flows and bundles

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

ferdei 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 516edf5d87 NIFI-13358 - NiFi Registry - rework action menus for flows and bundles
516edf5d87 is described below

commit 516edf5d8761f1daf3a9d509d4d2a0513ea7e335
Author: Pierre Villard <pi...@gmail.com>
AuthorDate: Mon Apr 15 17:48:36 2024 +0200

    NIFI-13358 - NiFi Registry - rework action menus for flows and bundles
    
    Signed-off-by: Ferenc Erdei <er...@gmail.com>
    This closes #8924.
---
 .../nf-registry-delete-bundle-version.html         |  53 +++++++++++
 .../nf-registry-delete-bundle-version.js           |  96 +++++++++++++++++++
 .../nf-registry-download-bundle-version.html       |  53 +++++++++++
 .../nf-registry-download-bundle-version.js         |  96 +++++++++++++++++++
 .../registry/nf-registry-grid-list-viewer.html     |   4 +-
 .../src/main/webapp/nf-registry.module.js          |   8 +-
 .../src/main/webapp/services/nf-registry.api.js    |  67 ++++++++++++++
 .../main/webapp/services/nf-registry.service.js    | 103 +++++++++++++++++++--
 8 files changed, 468 insertions(+), 12 deletions(-)

diff --git a/nifi-registry/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/components/explorer/grid-list/dialogs/delete-bundle-version/nf-registry-delete-bundle-version.html b/nifi-registry/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/components/explorer/grid-list/dialogs/delete-bundle-version/nf-registry-delete-bundle-version.html
new file mode 100644
index 0000000000..52e7797b5a
--- /dev/null
+++ b/nifi-registry/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/components/explorer/grid-list/dialogs/delete-bundle-version/nf-registry-delete-bundle-version.html
@@ -0,0 +1,53 @@
+<!--
+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.
+-->
+
+<div id="nifi-registry-delete-bundle-version-dialog">
+    <div class="pad-bottom-md" fxLayout="row" fxLayoutAlign="space-between center">
+        <mat-card-title>
+            Delete Bundle Version
+        </mat-card-title>
+        <button mat-icon-button (click)="cancel()">
+            <mat-icon color="primary">close</mat-icon>
+        </button>
+    </div>
+    <div fxLayout="column" fxLayoutAlign="space-between start" class="pad-bottom-sm">
+        <div class="pad-bottom-sm label-name">
+            <label>Choose Version</label>
+        </div>
+        <div class="fill-available-width bundle-version-dropdown-field">
+            <mat-form-field appearance="fill" fxFlex>
+                <mat-select panelClass="bundle-version-dropdown-select" [(value)]="selectedVersion">
+                    <mat-option *ngFor="let snapshotMeta of droplet.snapshotMetadata" [value]="snapshotMeta.version">
+                        <span *ngIf="snapshotMeta === droplet.snapshotMetadata[0]">Latest (Version {{snapshotMeta.version}})</span>
+                        <span *ngIf="snapshotMeta != droplet.snapshotMetadata[0]">Version {{snapshotMeta.version}}</span>
+                    </mat-option>
+                </mat-select>
+            </mat-form-field>
+        </div>
+    </div>
+    <div fxLayout="row">
+        <span fxFlex></span>
+        <button (click)="cancel()" color="fds-regular" mat-raised-button
+                i18n="Cancel deletion of bundle version selection|A button to cancel the deletion of the bundle version from the registry.">
+            Cancel
+        </button>
+        <button class="push-left-sm" data-automation-id="delete-bundle-version-button" (click)="deleteVersion()"
+                color="fds-primary" mat-raised-button i18n="Delete the selected bundle version|A button to delete the bundle version from the registry.">
+            Delete
+        </button>
+    </div>
+</div>
diff --git a/nifi-registry/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/components/explorer/grid-list/dialogs/delete-bundle-version/nf-registry-delete-bundle-version.js b/nifi-registry/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/components/explorer/grid-list/dialogs/delete-bundle-version/nf-registry-delete-bundle-version.js
new file mode 100644
index 0000000000..42bdfb9a47
--- /dev/null
+++ b/nifi-registry/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/components/explorer/grid-list/dialogs/delete-bundle-version/nf-registry-delete-bundle-version.js
@@ -0,0 +1,96 @@
+/*
+ * 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.
+ */
+
+import { Component } from '@angular/core';
+import NfRegistryApi from 'services/nf-registry.api';
+import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
+import { FdsSnackBarService } from '@nifi-fds/core';
+
+/**
+ * NfRegistryDeleteBundleVersion constructor.
+ *
+ * @param nfRegistryApi         The api service.
+ * @param fdsSnackBarService    The FDS snack bar service module.
+ * @param matDialogRef          The angular material dialog ref.
+ * @param data                  The data passed into this component.
+ * @constructor
+ */
+function NfRegistryDeleteBundleVersion(nfRegistryApi, fdsSnackBarService, matDialogRef, data) {
+    // Services
+    this.snackBarService = fdsSnackBarService;
+    this.nfRegistryApi = nfRegistryApi;
+    this.dialogRef = matDialogRef;
+    // local state
+    this.keepDialogOpen = false;
+    this.protocol = location.protocol;
+    this.droplet = data.droplet;
+    this.selectedVersion = this.droplet.snapshotMetadata[0].version;
+}
+
+NfRegistryDeleteBundleVersion.prototype = {
+    constructor: NfRegistryDeleteBundleVersion,
+
+    /**
+     * Delete specified bundle version.
+     */
+    deleteVersion: function () {
+        var self = this;
+        var version = this.selectedVersion;
+
+        this.nfRegistryApi.deleteBundleVersion(this.droplet.link.href, version).subscribe(function (response) {
+            if (!response.status || response.status === 200) {
+                self.snackBarService.openCoaster({
+                    title: 'Success',
+                    message: 'Bundle version successfully deleted.',
+                    verticalPosition: 'bottom',
+                    horizontalPosition: 'right',
+                    icon: 'fa fa-check-circle-o',
+                    color: '#1EB475',
+                    duration: 3000
+                });
+
+                if (self.keepDialogOpen !== true) {
+                    self.dialogRef.close();
+                }
+            } else {
+                self.dialogRef.close();
+            }
+        });
+    },
+
+    /**
+     * Cancel the deletion of the bundle version and close dialog.
+     */
+    cancel: function () {
+        this.dialogRef.close();
+    }
+};
+
+NfRegistryDeleteBundleVersion.annotations = [
+    new Component({
+        templateUrl: './nf-registry-delete-bundle-version.html'
+    })
+];
+
+NfRegistryDeleteBundleVersion.parameters = [
+    NfRegistryApi,
+    FdsSnackBarService,
+    MatDialogRef,
+    MAT_DIALOG_DATA
+];
+
+export default NfRegistryDeleteBundleVersion;
diff --git a/nifi-registry/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/components/explorer/grid-list/dialogs/download-bundle-version/nf-registry-download-bundle-version.html b/nifi-registry/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/components/explorer/grid-list/dialogs/download-bundle-version/nf-registry-download-bundle-version.html
new file mode 100644
index 0000000000..645f068b9d
--- /dev/null
+++ b/nifi-registry/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/components/explorer/grid-list/dialogs/download-bundle-version/nf-registry-download-bundle-version.html
@@ -0,0 +1,53 @@
+<!--
+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.
+-->
+
+<div id="nifi-registry-download-bundle-version-dialog">
+    <div class="pad-bottom-md" fxLayout="row" fxLayoutAlign="space-between center">
+        <mat-card-title>
+            Download Bundle Version
+        </mat-card-title>
+        <button mat-icon-button (click)="cancel()">
+            <mat-icon color="primary">close</mat-icon>
+        </button>
+    </div>
+    <div fxLayout="column" fxLayoutAlign="space-between start" class="pad-bottom-sm">
+        <div class="pad-bottom-sm label-name">
+            <label>Choose Version</label>
+        </div>
+        <div class="fill-available-width bundle-version-dropdown-field">
+            <mat-form-field appearance="fill" fxFlex>
+                <mat-select panelClass="bundle-version-dropdown-select" [(value)]="selectedVersion">
+                    <mat-option *ngFor="let snapshotMeta of droplet.snapshotMetadata" [value]="snapshotMeta.version">
+                        <span *ngIf="snapshotMeta === droplet.snapshotMetadata[0]">Latest (Version {{snapshotMeta.version}})</span>
+                        <span *ngIf="snapshotMeta != droplet.snapshotMetadata[0]">Version {{snapshotMeta.version}}</span>
+                    </mat-option>
+                </mat-select>
+            </mat-form-field>
+        </div>
+    </div>
+    <div fxLayout="row">
+        <span fxFlex></span>
+        <button (click)="cancel()" color="fds-regular" mat-raised-button
+                i18n="Cancel the download of the bundle version|A button to cancel the download of the bundle version from the registry.">
+            Cancel
+        </button>
+        <button class="push-left-sm" data-automation-id="download-bundle-version-button" (click)="downloadVersion()"
+                color="fds-primary" mat-raised-button i18n="Download the selected bundle version|A button to download the selected bundle version from the registry.">
+            Download
+        </button>
+    </div>
+</div>
diff --git a/nifi-registry/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/components/explorer/grid-list/dialogs/download-bundle-version/nf-registry-download-bundle-version.js b/nifi-registry/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/components/explorer/grid-list/dialogs/download-bundle-version/nf-registry-download-bundle-version.js
new file mode 100644
index 0000000000..f771efe901
--- /dev/null
+++ b/nifi-registry/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/components/explorer/grid-list/dialogs/download-bundle-version/nf-registry-download-bundle-version.js
@@ -0,0 +1,96 @@
+/*
+ * 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.
+ */
+
+import { Component } from '@angular/core';
+import NfRegistryApi from 'services/nf-registry.api';
+import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
+import { FdsSnackBarService } from '@nifi-fds/core';
+
+/**
+ * NfRegistryDownloadBundleVersion constructor.
+ *
+ * @param nfRegistryApi         The api service.
+ * @param fdsSnackBarService    The FDS snack bar service module.
+ * @param matDialogRef          The angular material dialog ref.
+ * @param data                  The data passed into this component.
+ * @constructor
+ */
+function NfRegistryDownloadBundleVersion(nfRegistryApi, fdsSnackBarService, matDialogRef, data) {
+    // Services
+    this.snackBarService = fdsSnackBarService;
+    this.nfRegistryApi = nfRegistryApi;
+    this.dialogRef = matDialogRef;
+    // local state
+    this.keepDialogOpen = false;
+    this.protocol = location.protocol;
+    this.droplet = data.droplet;
+    this.selectedVersion = this.droplet.snapshotMetadata[0].version;
+}
+
+NfRegistryDownloadBundleVersion.prototype = {
+    constructor: NfRegistryDownloadBundleVersion,
+
+    /**
+     * Download the selected bundle version
+     */
+    downloadVersion: function () {
+        var self = this;
+        var version = this.selectedVersion;
+
+        this.nfRegistryApi.downloadBundleVersion(this.droplet.link.href, version, this.droplet.artifactId).subscribe(function (response) {
+            if (!response.status || response.status === 200) {
+                self.snackBarService.openCoaster({
+                    title: 'Success',
+                    message: 'Successfully downloaded the bundle version.',
+                    verticalPosition: 'bottom',
+                    horizontalPosition: 'right',
+                    icon: 'fa fa-check-circle-o',
+                    color: '#1EB475',
+                    duration: 3000
+                });
+
+                if (self.keepDialogOpen !== true) {
+                    self.dialogRef.close();
+                }
+            } else {
+                self.dialogRef.close();
+            }
+        });
+    },
+
+    /**
+     * Cancel an export of a version and close dialog.
+     */
+    cancel: function () {
+        this.dialogRef.close();
+    }
+};
+
+NfRegistryDownloadBundleVersion.annotations = [
+    new Component({
+        templateUrl: './nf-registry-download-bundle-version.html'
+    })
+];
+
+NfRegistryDownloadBundleVersion.parameters = [
+    NfRegistryApi,
+    FdsSnackBarService,
+    MatDialogRef,
+    MAT_DIALOG_DATA
+];
+
+export default NfRegistryDownloadBundleVersion;
diff --git a/nifi-registry/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/components/explorer/grid-list/registry/nf-registry-grid-list-viewer.html b/nifi-registry/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/components/explorer/grid-list/registry/nf-registry-grid-list-viewer.html
index 5f94e7c6d9..c51870dc25 100644
--- a/nifi-registry/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/components/explorer/grid-list/registry/nf-registry-grid-list-viewer.html
+++ b/nifi-registry/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/components/explorer/grid-list/registry/nf-registry-grid-list-viewer.html
@@ -74,7 +74,7 @@ limitations under the License.
                         </button>
                         <mat-menu class="fds-primary-dropdown-button-menu" #primaryButtonDropdownMenu="matMenu"
                                   [overlapTrigger]="false">
-                            <button mat-menu-item *ngFor="let action of nfRegistryService.dropletActions"
+                            <button mat-menu-item *ngFor="let action of nfRegistryService.getDropletActions(droplet)"
                                     [disabled]="action.disabled(droplet)"
                                     (click)="nfRegistryService.executeDropletAction(action, droplet)">
                                 {{action.name}}
@@ -234,4 +234,4 @@ limitations under the License.
 <div class="pad-right-xxl pad-left-xxl" *ngIf="nfRegistryService.filteredDroplets.length === 0 && nfRegistryService.buckets.length === 0 && !nfRegistryService.inProgress">
     <p class="text-center">There are no buckets to display.</p>
 </div>
-<router-outlet></router-outlet>
+<router-outlet></router-outlet>
\ No newline at end of file
diff --git a/nifi-registry/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/nf-registry.module.js b/nifi-registry/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/nf-registry.module.js
index d5e0976ef3..7cf5bdd3fc 100644
--- a/nifi-registry/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/nf-registry.module.js
+++ b/nifi-registry/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/nf-registry.module.js
@@ -55,6 +55,8 @@ import {
 import NfRegistryImportVersionedFlow from './components/explorer/grid-list/dialogs/import-versioned-flow/nf-registry-import-versioned-flow';
 import NfRegistryImportNewFlow from './components/explorer/grid-list/dialogs/import-new-flow/nf-registry-import-new-flow';
 import NfRegistryExportVersionedFlow from './components/explorer/grid-list/dialogs/export-versioned-flow/nf-registry-export-versioned-flow';
+import NfRegistryDeleteBundleVersion from './components/explorer/grid-list/dialogs/delete-bundle-version/nf-registry-delete-bundle-version';
+import NfRegistryDownloadBundleVersion from './components/explorer/grid-list/dialogs/download-bundle-version/nf-registry-download-bundle-version';
 import { NfRegistryExplorerAbout } from './components/explorer/dialogs/about/nf-registry-explorer-about';
 
 function NfRegistryModule() {
@@ -97,7 +99,9 @@ NfRegistryModule.annotations = [
             NfUserLoginComponent,
             NfRegistryExportVersionedFlow,
             NfRegistryImportVersionedFlow,
-            NfRegistryImportNewFlow
+            NfRegistryImportNewFlow,
+            NfRegistryDeleteBundleVersion,
+            NfRegistryDownloadBundleVersion
         ],
         entryComponents: [
             NfRegistryAddUser,
@@ -111,6 +115,8 @@ NfRegistryModule.annotations = [
             NfRegistryExportVersionedFlow,
             NfRegistryImportVersionedFlow,
             NfRegistryImportNewFlow,
+            NfRegistryDeleteBundleVersion,
+            NfRegistryDownloadBundleVersion,
             NfRegistryExplorerAbout
         ],
         providers: [
diff --git a/nifi-registry/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/services/nf-registry.api.js b/nifi-registry/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/services/nf-registry.api.js
index fae4a5052c..e760968ad4 100644
--- a/nifi-registry/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/services/nf-registry.api.js
+++ b/nifi-registry/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/services/nf-registry.api.js
@@ -102,6 +102,31 @@ NfRegistryApi.prototype = {
         );
     },
 
+    /**
+     * Deletes the specified bundle version from the registry.
+     *
+     * @param {string}  dropletUri      The uri of the droplet to delete.
+     * @param {number}  versionNumber   The version of the bundle to delete.
+     * @returns {*}
+     */
+    deleteBundleVersion: function (dropletUri, versionNumber) {
+        var self = this;
+        return this.http.delete('../nifi-registry-api/' + dropletUri + '/versions/' + versionNumber, headers).pipe(
+            map(function (response) {
+                return response;
+            }),
+            catchError(function (error) {
+                self.dialogService.openConfirm({
+                    title: 'Error',
+                    message: error.error,
+                    acceptButton: 'Ok',
+                    acceptButtonColor: 'fds-warn'
+                });
+                return of(error);
+            })
+        );
+    },
+
     /**
      * Retrieves the extension details for an existing snapshot the registry has stored.
      *
@@ -172,6 +197,48 @@ NfRegistryApi.prototype = {
         );
     },
 
+    /**
+     * Retrieves the specified bundle version for an existing droplet the registry has stored.
+     *
+     * @param {string}  dropletUri      The uri of the droplet to request.
+     * @param {number}  versionNumber   The version of the flow to request.
+     * @param {string}  name            The name of the bundle version to request.
+     * @returns {*}
+     */
+    downloadBundleVersion: function (dropletUri, versionNumber, name) {
+        var self = this;
+        var url = '../nifi-registry-api/' + dropletUri + '/versions/' + versionNumber + '/content';
+        var options = {
+            headers: headers,
+            observe: 'response',
+            responseType: 'arraybuffer'
+        };
+
+        return self.http.get(url, options).pipe(
+            map(function (response) {
+                var anchorElement = document.createElement('a');
+                anchorElement.href = URL.createObjectURL(new Blob([response.body], { type: 'application/binary' }));
+                anchorElement.download = name + '-' + versionNumber + '.nar';
+                anchorElement.style = 'display: none;';
+
+                document.body.appendChild(anchorElement);
+                anchorElement.click();
+                document.body.removeChild(anchorElement);
+
+                return response;
+            }),
+            catchError(function (error) {
+                self.dialogService.openConfirm({
+                    title: 'Error',
+                    message: error.error,
+                    acceptButton: 'Ok',
+                    acceptButtonColor: 'fds-warn'
+                });
+                return of(error);
+            })
+        );
+    },
+
     /**
      * Uploads a new versioned flow snapshot to the existing droplet the registry has stored.
      *
diff --git a/nifi-registry/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/services/nf-registry.service.js b/nifi-registry/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/services/nf-registry.service.js
index 0a2618733b..69a27c3eb1 100644
--- a/nifi-registry/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/services/nf-registry.service.js
+++ b/nifi-registry/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/services/nf-registry.service.js
@@ -27,6 +27,10 @@ import NfRegistryImportVersionedFlow
     from '../components/explorer/grid-list/dialogs/import-versioned-flow/nf-registry-import-versioned-flow';
 import NfRegistryImportNewFlow
     from '../components/explorer/grid-list/dialogs/import-new-flow/nf-registry-import-new-flow';
+import NfRegistryDeleteBundleVersion
+    from '../components/explorer/grid-list/dialogs/delete-bundle-version/nf-registry-delete-bundle-version';
+import NfRegistryDownloadBundleVersion
+    from '../components/explorer/grid-list/dialogs/download-bundle-version/nf-registry-download-bundle-version';
 
 /**
  * NfRegistryService constructor.
@@ -147,9 +151,9 @@ function NfRegistryService(nfRegistryApi, nfStorage, tdDataTableService, router,
             tooltip: 'Delete Policy'
         }
     ];
-    this.dropletActions = [
+    this.dropletFlowActions = [
         {
-            name: 'Import new version',
+            name: 'Import new flow version',
             icon: 'fa fa-upload',
             tooltip: 'Import new flow version',
             disabled: function (droplet) {
@@ -157,7 +161,7 @@ function NfRegistryService(nfRegistryApi, nfStorage, tdDataTableService, router,
             }
         },
         {
-            name: 'Export version',
+            name: 'Export flow version',
             icon: 'fa fa-download',
             tooltip: 'Export flow version',
             disabled: function (droplet) {
@@ -165,9 +169,35 @@ function NfRegistryService(nfRegistryApi, nfStorage, tdDataTableService, router,
             }
         },
         {
-            name: 'Delete',
+            name: 'Delete flow',
+            icon: 'fa fa-trash',
+            tooltip: 'Delete flow',
+            disabled: function (droplet) {
+                return !droplet.permissions.canDelete;
+            }
+        }
+    ];
+    this.dropletBundleActions = [
+        {
+            name: 'Download bundle version',
+            icon: 'fa fa-download',
+            tooltip: 'Download bundle version',
+            disabled: function (droplet) {
+                return !droplet.permissions.canRead;
+            }
+        },
+        {
+            name: 'Delete bundle version',
+            icon: 'fa fa-trash',
+            tooltip: 'Delete bundle version',
+            disabled: function (droplet) {
+                return !droplet.permissions.canDelete;
+            }
+        },
+        {
+            name: 'Delete bundle',
             icon: 'fa fa-trash',
-            tooltip: 'Delete',
+            tooltip: 'Delete bundle',
             disabled: function (droplet) {
                 return !droplet.permissions.canDelete;
             }
@@ -486,6 +516,40 @@ NfRegistryService.prototype = {
         });
     },
 
+    /**
+     * Opens the download bundle version dialog.
+     *
+     * @param droplet       The droplet object.
+     */
+    openDownloadBundleVersionDialog: function (droplet) {
+        this.matDialog.open(NfRegistryDownloadBundleVersion, {
+            disableClose: true,
+            width: '400px',
+            data: {
+                droplet: droplet
+            }
+        });
+    },
+
+    /**
+     * Opens the delete bundle version dialog.
+     *
+     * @param droplet       The droplet object.
+     */
+    openDeleteBundleVersionDialog: function (droplet) {
+        var self = this;
+
+        this.matDialog.open(NfRegistryDeleteBundleVersion, {
+            disableClose: true,
+            width: '400px',
+            data: {
+                droplet: droplet
+            }
+        }).afterClosed().subscribe(function () {
+            self.getDropletSnapshotMetadata(droplet);
+        });
+    },
+
     /**
      * Opens the import new flow dialog.
      *
@@ -527,6 +591,19 @@ NfRegistryService.prototype = {
         });
     },
 
+    /**
+     * Get the list of possible actions for the provided droplet.
+     *
+     * @param droplet       The droplet object the `action` will act upon.
+     */
+    getDropletActions: function (droplet) {
+        if (droplet.bundleType) {
+            return this.dropletBundleActions;
+        } else {
+            return this.dropletFlowActions;
+        }
+    },
+
     /**
      * Execute the given droplet action.
      *
@@ -535,16 +612,24 @@ NfRegistryService.prototype = {
      */
     executeDropletAction: function (action, droplet) {
         switch (action.name.toLowerCase()) {
-            case 'import new version':
+            case 'import new flow version':
                 // Opens the import versioned flow dialog
                 this.openImportVersionedFlowDialog(droplet);
                 break;
-            case 'export version':
+            case 'export flow version':
                 // Opens the export flow version dialog
                 this.openExportVersionedFlowDialog(droplet);
                 break;
-            case 'delete':
-                // Deletes the entire data flow
+            case 'download bundle version':
+                // Opens the download bundle version dialog
+                this.openDownloadBundleVersionDialog(droplet);
+                break;
+            case 'delete bundle version':
+                // Opens the delete bundle version dialog
+                this.openDeleteBundleVersionDialog(droplet);
+                break;
+            case 'delete flow':
+            case 'delete bundle':
                 this.deleteDroplet(droplet);
                 break;
             default: // do nothing