You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by mc...@apache.org on 2016/04/29 22:32:17 UTC

[04/13] nifi git commit: NIFI-1554: - Populating component entities in the REST API to decouple key fields from the configuration DTOs. - Added initial support for components in UI when access isn't allowed. Formal styling to come later.

http://git-wip-us.apache.org/repos/asf/nifi/blob/ff98d823/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas.js
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas.js
index 175a598..83de139 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas.js
@@ -450,8 +450,8 @@ nf.Canvas = (function () {
                     d3.selectAll('g.component').classed('selected', function (d) {
                         // consider it selected if its already selected or enclosed in the bounding box
                         return d3.select(this).classed('selected') ||
-                            d.component.position.x >= selectionBoundingBox.x && (d.component.position.x + d.dimensions.width) <= (selectionBoundingBox.x + selectionBoundingBox.width) &&
-                            d.component.position.y >= selectionBoundingBox.y && (d.component.position.y + d.dimensions.height) <= (selectionBoundingBox.y + selectionBoundingBox.height);
+                            d.position.x >= selectionBoundingBox.x && (d.position.x + d.dimensions.width) <= (selectionBoundingBox.x + selectionBoundingBox.width) &&
+                            d.position.y >= selectionBoundingBox.y && (d.position.y + d.dimensions.height) <= (selectionBoundingBox.y + selectionBoundingBox.height);
                     });
 
                     // see if a connection should be selected or not
@@ -777,32 +777,31 @@ nf.Canvas = (function () {
         // load the controller
         return $.ajax({
             type: 'GET',
-            url: config.urls.api + '/process-groups/' + encodeURIComponent(processGroupId),
+            url: config.urls.api + '/flow/process-groups/' + encodeURIComponent(processGroupId),
             data: {
                 verbose: true
             },
             dataType: 'json'
-        }).done(function (processGroupResponse) {
+        }).done(function (flowResponse) {
             // set the revision
-            nf.Client.setRevision(processGroupResponse.revision);
+            nf.Client.setRevision(flowResponse.revision);
 
             // get the controller and its contents
-            var processGroup = processGroupResponse.processGroup;
+            var processGroupFlow = flowResponse.processGroupFlow;
 
             // set the group details
-            nf.Canvas.setGroupId(processGroup.id);
-            nf.Canvas.setGroupName(processGroup.name);
+            nf.Canvas.setGroupId(processGroupFlow.id);
 
             // update the breadcrumbs
             nf.ng.Bridge.call('AppCtrl.ServiceProvider.BreadcrumbsCtrl',
                 'AppCtrl.ServiceProvider.BreadcrumbsCtrl.resetBreadcrumbs');
             nf.ng.Bridge.call('AppCtrl.ServiceProvider.BreadcrumbsCtrl',
                 'AppCtrl.ServiceProvider.BreadcrumbsCtrl.generateBreadcrumbs',
-                processGroup);
+                processGroupFlow.breadcrumb);
 
             // set the parent id if applicable
-            if (nf.Common.isDefinedAndNotNull(processGroup.parent)) {
-                nf.Canvas.setParentGroupId(processGroup.parent.id);
+            if (nf.Common.isDefinedAndNotNull(processGroupFlow.parentGroupId)) {
+                nf.Canvas.setParentGroupId(processGroupFlow.parentGroupId);
             } else {
                 nf.Canvas.setParentGroupId(null);
             }
@@ -811,7 +810,7 @@ nf.Canvas = (function () {
             nf.Graph.removeAll();
 
             // refresh the graph
-            nf.Graph.add(processGroup.contents, false);
+            nf.Graph.add(processGroupFlow.flow, false);
 
             // update the toolbar
             nf.CanvasToolbar.refresh();
@@ -1253,8 +1252,8 @@ nf.Canvas = (function () {
                         return false;
                     }
 
-                    var left = d.component.position.x;
-                    var top = d.component.position.y;
+                    var left = d.position.x;
+                    var top = d.position.y;
                     var right = left + d.dimensions.width;
                     var bottom = top + d.dimensions.height;
 
@@ -1283,7 +1282,7 @@ nf.Canvas = (function () {
 
                 // marks the specific component as visible and determines if its entering or leaving visibility
                 var updateVisibility = function (d, isVisible) {
-                    var selection = d3.select('#id-' + d.component.id);
+                    var selection = d3.select('#id-' + d.id);
                     var visible = isVisible(d);
                     var wasVisible = selection.classed('visible');
 

http://git-wip-us.apache.org/repos/asf/nifi/blob/ff98d823/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-connectable.js
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-connectable.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-connectable.js
index 154c14b..a491266 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-connectable.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-connectable.js
@@ -71,7 +71,7 @@ nf.Connectable = (function () {
                         var position = d3.mouse(canvas.node());
                         canvas.insert('path', ':first-child')
                                 .datum({
-                                    'sourceId': sourceData.component.id,
+                                    'sourceId': sourceData.id,
                                     'sourceWidth': sourceData.dimensions.width,
                                     'x': position[0],
                                     'y': position[1]
@@ -122,7 +122,7 @@ nf.Connectable = (function () {
                                 var destinationData = destination.datum();
                                 
                                 // show the line preview as appropriate
-                                if (pathDatum.sourceId === destinationData.component.id) {
+                                if (pathDatum.sourceId === destinationData.id) {
                                     var x = pathDatum.x;
                                     var y = pathDatum.y;
                                     var componentOffset = pathDatum.sourceWidth / 2;
@@ -132,8 +132,8 @@ nf.Connectable = (function () {
                                 } else {
                                     // get the position on the destination perimeter
                                     var end = nf.CanvasUtils.getPerimeterPoint(pathDatum, {
-                                        'x': destinationData.component.position.x,
-                                        'y': destinationData.component.position.y,
+                                        'x': destinationData.position.x,
+                                        'y': destinationData.position.y,
                                         'width': destinationData.dimensions.width,
                                         'height': destinationData.dimensions.height
                                     });
@@ -190,7 +190,7 @@ nf.Connectable = (function () {
 
                             // create the connection
                             var destinationData = destination.datum();
-                            nf.ConnectionConfiguration.createConnection(connectorData.sourceId, destinationData.component.id);
+                            nf.ConnectionConfiguration.createConnection(connectorData.sourceId, destinationData.id);
                         }
                     });
         },

http://git-wip-us.apache.org/repos/asf/nifi/blob/ff98d823/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-connection-configuration.js
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-connection-configuration.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-connection-configuration.js
index be2cb6c..e03c60d 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-connection-configuration.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-connection-configuration.js
@@ -168,8 +168,8 @@ nf.ConnectionConfiguration = (function () {
             $('#input-port-source-name').text(inputPortData.component.name);
 
             // populate the connection source details
-            $('#connection-source-id').val(inputPortData.component.id);
-            $('#connection-source-component-id').val(inputPortData.component.id);
+            $('#connection-source-id').val(inputPortData.id);
+            $('#connection-source-component-id').val(inputPortData.id);
 
             // populate the group details
             $('#connection-source-group-id').val(nf.Canvas.getGroupId());
@@ -194,8 +194,8 @@ nf.ConnectionConfiguration = (function () {
             $('#funnel-source').show();
 
             // populate the connection source details
-            $('#connection-source-id').val(funnelData.component.id);
-            $('#connection-source-component-id').val(funnelData.component.id);
+            $('#connection-source-id').val(funnelData.id);
+            $('#connection-source-component-id').val(funnelData.id);
 
             // populate the group details
             $('#connection-source-group-id').val(nf.Canvas.getGroupId());
@@ -222,8 +222,8 @@ nf.ConnectionConfiguration = (function () {
             $('#processor-source-type').text(nf.Common.substringAfterLast(processorData.component.type, '.'));
 
             // populate the connection source details
-            $('#connection-source-id').val(processorData.component.id);
-            $('#connection-source-component-id').val(processorData.component.id);
+            $('#connection-source-id').val(processorData.id);
+            $('#connection-source-component-id').val(processorData.id);
 
             // populate the group details
             $('#connection-source-group-id').val(nf.Canvas.getGroupId());
@@ -248,13 +248,13 @@ nf.ConnectionConfiguration = (function () {
 
             $.ajax({
                 type: 'GET',
-                url: config.urls.api + '/process-groups/' + encodeURIComponent(processGroupData.component.id),
+                url: config.urls.api + '/process-groups/' + encodeURIComponent(processGroupData.id),
                 data: {
                     verbose: true
                 },
                 dataType: 'json'
             }).done(function (response) {
-                var processGroup = response.processGroup;
+                var processGroup = response.component;
                 var processGroupContents = processGroup.contents;
 
                 // only proceed if there are output ports
@@ -413,8 +413,8 @@ nf.ConnectionConfiguration = (function () {
             $('#output-port-destination-name').text(outputPortData.component.name);
 
             // populate the connection destination details
-            $('#connection-destination-id').val(outputPortData.component.id);
-            $('#connection-destination-component-id').val(outputPortData.component.id);
+            $('#connection-destination-id').val(outputPortData.id);
+            $('#connection-destination-component-id').val(outputPortData.id);
 
             // populate the group details
             $('#connection-destination-group-id').val(nf.Canvas.getGroupId());
@@ -431,8 +431,8 @@ nf.ConnectionConfiguration = (function () {
             $('#funnel-destination').show();
 
             // populate the connection destination details
-            $('#connection-destination-id').val(funnelData.component.id);
-            $('#connection-destination-component-id').val(funnelData.component.id);
+            $('#connection-destination-id').val(funnelData.id);
+            $('#connection-destination-component-id').val(funnelData.id);
 
             // populate the group details
             $('#connection-destination-group-id').val(nf.Canvas.getGroupId());
@@ -451,8 +451,8 @@ nf.ConnectionConfiguration = (function () {
             $('#processor-destination-type').text(nf.Common.substringAfterLast(processorData.component.type, '.'));
 
             // populate the connection destination details
-            $('#connection-destination-id').val(processorData.component.id);
-            $('#connection-destination-component-id').val(processorData.component.id);
+            $('#connection-destination-id').val(processorData.id);
+            $('#connection-destination-component-id').val(processorData.id);
 
             // populate the group details
             $('#connection-destination-group-id').val(nf.Canvas.getGroupId());
@@ -473,13 +473,13 @@ nf.ConnectionConfiguration = (function () {
 
             $.ajax({
                 type: 'GET',
-                url: config.urls.api + '/process-groups/' + encodeURIComponent(processGroupData.component.id),
+                url: config.urls.api + '/process-groups/' + encodeURIComponent(processGroupData.id),
                 data: {
                     verbose: true
                 },
                 dataType: 'json'
             }).done(function (response) {
-                var processGroup = response.processGroup;
+                var processGroup = response.component;
                 var processGroupContents = processGroup.contents;
 
                 // only proceed if there are output ports
@@ -628,10 +628,10 @@ nf.ConnectionConfiguration = (function () {
             $('#read-only-output-port-source').show();
 
             // populate the component information
-            $('#connection-source-component-id').val(sourceData.component.id);
+            $('#connection-source-component-id').val(sourceData.id);
 
             // populate the group details
-            $('#connection-source-group-id').val(sourceData.component.id);
+            $('#connection-source-group-id').val(sourceData.id);
             $('#connection-source-group-name').text(sourceData.component.name);
 
             // resolve the deferred
@@ -711,8 +711,8 @@ nf.ConnectionConfiguration = (function () {
         var bends = [];
         if (sourceComponentId === destinationComponentId) {
             var rightCenter = {
-                x: sourceData.component.position.x + (sourceData.dimensions.width),
-                y: sourceData.component.position.y + (sourceData.dimensions.height / 2)
+                x: sourceData.position.x + (sourceData.dimensions.width),
+                y: sourceData.position.y + (sourceData.dimensions.height / 2)
             };
 
             var xOffset = nf.Connection.config.selfLoopXOffset;
@@ -759,8 +759,8 @@ nf.ConnectionConfiguration = (function () {
                 // if we need to avoid a collision
                 if (avoidCollision === true) {
                     // determine the middle of the source/destination components
-                    var sourceMiddle = [sourceData.component.position.x + (sourceData.dimensions.width / 2), sourceData.component.position.y + (sourceData.dimensions.height / 2)];
-                    var destinationMiddle = [destinationData.component.position.x + (destinationData.dimensions.width / 2), destinationData.component.position.y + (destinationData.dimensions.height / 2)];
+                    var sourceMiddle = [sourceData.position.x + (sourceData.dimensions.width / 2), sourceData.position.y + (sourceData.dimensions.height / 2)];
+                    var destinationMiddle = [destinationData.position.x + (destinationData.dimensions.width / 2), destinationData.position.y + (destinationData.dimensions.height / 2)];
 
                     // detect if the line is more horizontal or vertical
                     var slope = ((sourceMiddle[1] - destinationMiddle[1]) / (sourceMiddle[0] - destinationMiddle[0]));
@@ -841,7 +841,7 @@ nf.ConnectionConfiguration = (function () {
         if (validateSettings()) {
             var connectionEntity = {
                 'revision': nf.Client.getRevision(),
-                'connection': {
+                'component': {
                     'name': connectionName,
                     'source': {
                         'id': sourceId,
@@ -875,7 +875,7 @@ nf.ConnectionConfiguration = (function () {
 
                 // add the connection
                 nf.Graph.add({
-                    'connections': [response.connection]
+                    'connections': [response]
                 }, true);
 
                 // reload the connections source/destination components
@@ -925,7 +925,7 @@ nf.ConnectionConfiguration = (function () {
         if (validateSettings()) {
             var connectionEntity = {
                 'revision': nf.Client.getRevision(),
-                'connection': {
+                'component': {
                     'id': connectionId,
                     'name': connectionName,
                     'destination': {
@@ -949,14 +949,12 @@ nf.ConnectionConfiguration = (function () {
                 dataType: 'json',
                 contentType: 'application/json'
             }).done(function (response) {
-                if (nf.Common.isDefinedAndNotNull(response.connection)) {
-                    var connection = response.connection;
-
+                if (nf.Common.isDefinedAndNotNull(response.component)) {
                     // update the revision
                     nf.Client.setRevision(response.revision);
 
                     // update this connection
-                    nf.Connection.set(connection);
+                    nf.Connection.set(response);
 
                     // reload the connections source/destination components
                     nf.CanvasUtils.reloadConnectionSourceAndDestination(sourceComponentId, destinationComponentId);
@@ -1226,16 +1224,16 @@ nf.ConnectionConfiguration = (function () {
          */
         showConfiguration: function (selection, destination) {
             return $.Deferred(function (deferred) {
-                var selectionData = selection.datum();
-                var connection = selectionData.component;
+                var connectionEntry = selection.datum();
+                var connection = connectionEntry.component;
 
                 // identify the source component
-                var sourceComponentId = nf.CanvasUtils.getConnectionSourceComponentId(connection);
+                var sourceComponentId = nf.CanvasUtils.getConnectionSourceComponentId(connectionEntry);
                 var source = d3.select('#id-' + sourceComponentId);
 
                 // identify the destination component
                 if (nf.Common.isUndefinedOrNull(destination)) {
-                    var destinationComponentId = nf.CanvasUtils.getConnectionDestinationComponentId(connection);
+                    var destinationComponentId = nf.CanvasUtils.getConnectionDestinationComponentId(connectionEntry);
                     destination = d3.select('#id-' + destinationComponentId);
                 }
 
@@ -1283,7 +1281,7 @@ nf.ConnectionConfiguration = (function () {
                         var destinationData = destination.datum();
 
                         // when the group ids differ, its a new destination component so we don't want to preselect any port
-                        if (connection.destination.groupId === destinationData.component.id) {
+                        if (connection.destination.groupId === destinationData.id) {
                             $('#input-port-options').combo('setSelectedOption', {
                                 value: connection.destination.id
                             });

http://git-wip-us.apache.org/repos/asf/nifi/blob/ff98d823/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-connection.js
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-connection.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-connection.js
index 8cab85e..48f7247 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-connection.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-connection.js
@@ -223,9 +223,7 @@ nf.Connection = (function () {
      * Selects the connection elements against the current connection map.
      */
     var select = function () {
-        return connectionContainer.selectAll('g.connection').data(connectionMap.values(), function (d) {
-            return d.component.id;
-        });
+        return connectionContainer.selectAll('g.connection').data(connectionMap.values());
     };
 
     var renderConnections = function (entered, selected) {
@@ -236,7 +234,7 @@ nf.Connection = (function () {
         var connection = entered.append('g')
                 .attr({
                     'id': function (d) {
-                        return 'id-' + d.component.id;
+                        return 'id-' + d.id;
                     },
                     'class': 'connection'
                 })
@@ -268,45 +266,45 @@ nf.Connection = (function () {
                 })
                 .call(nf.ContextMenu.activate);
 
-        if (nf.Common.isDFM()) {
-            // only support adding bend points when appropriate
-            selectableConnection.on('dblclick', function (d) {
-                var position = d3.mouse(this.parentNode);
-
-                // find where to put this bend point
-                var bendIndex = getNearestSegment({
-                    'x': position[0],
-                    'y': position[1]
-                }, d);
-
-                // copy the original to restore if necessary
-                var bends = d.component.bends.slice();
+        // only support adding bend points when appropriate
+        selectableConnection.filter(function (d) {
+            return d.accessPolicy.canWrite && d.accessPolicy.canRead;
+        }).on('dblclick', function (d) {
+            var position = d3.mouse(this.parentNode);
+
+            // find where to put this bend point
+            var bendIndex = getNearestSegment({
+                'x': position[0],
+                'y': position[1]
+            }, d);
+
+            // copy the original to restore if necessary
+            var bends = d.component.bends.slice();
+
+            // add it to the collection of points
+            bends.splice(bendIndex, 0, {
+                'x': position[0],
+                'y': position[1]
+            });
 
-                // add it to the collection of points
-                bends.splice(bendIndex, 0, {
-                    'x': position[0],
-                    'y': position[1]
-                });
+            var connection = {
+                id: d.id,
+                bends: bends
+            };
 
-                var connection = {
-                    id: d.component.id,
-                    bends: bends
-                };
-
-                // update the label index if necessary
-                var labelIndex = d.component.labelIndex;
-                if (bends.length === 1) {
-                    connection.labelIndex = 0;
-                } else if (bendIndex <= labelIndex) {
-                    connection.labelIndex = labelIndex + 1;
-                }
+            // update the label index if necessary
+            var labelIndex = d.component.labelIndex;
+            if (bends.length === 1) {
+                connection.labelIndex = 0;
+            } else if (bendIndex <= labelIndex) {
+                connection.labelIndex = labelIndex + 1;
+            }
 
-                // save the new state
-                save(d, connection);
+            // save the new state
+            save(d, connection);
 
-                d3.event.stopPropagation();
-            });
-        }
+            d3.event.stopPropagation();
+        });
 
         // update connection which will establish appropriate start/end points among other things
         connection.call(updateConnections, true, false);
@@ -339,9 +337,11 @@ nf.Connection = (function () {
             updated.classed('grouped', function (d) {
                 var grouped = false;
 
-                // if there are more than one selected relationship, mark this as grouped
-                if (nf.Common.isDefinedAndNotNull(d.component.selectedRelationships) && d.component.selectedRelationships.length > 1) {
-                    grouped = true;
+                if (d.accessPolicy.canRead) {
+                    // if there are more than one selected relationship, mark this as grouped
+                    if (nf.Common.isDefinedAndNotNull(d.component.selectedRelationships) && d.component.selectedRelationships.length > 1) {
+                        grouped = true;
+                    }
                 }
 
                 return grouped;
@@ -349,9 +349,11 @@ nf.Connection = (function () {
             .classed('ghost', function (d) {
                 var ghost = false;
 
-                // if the connection has a relationship that is unavailable, mark it a ghost relationship
-                if (hasUnavailableRelationship(d)) {
-                    ghost = true;
+                if (d.accessPolicy.canRead) {
+                    // if the connection has a relationship that is unavailable, mark it a ghost relationship
+                    if (hasUnavailableRelationship(d)) {
+                        ghost = true;
+                    }
                 }
 
                 return ghost;
@@ -363,7 +365,7 @@ nf.Connection = (function () {
 
             if (updatePath === true) {
                 // calculate the start and end points
-                var sourceComponentId = nf.CanvasUtils.getConnectionSourceComponentId(d.component);
+                var sourceComponentId = nf.CanvasUtils.getConnectionSourceComponentId(d);
                 var sourceData = d3.select('#id-' + sourceComponentId).datum();
                 var end;
 
@@ -373,8 +375,8 @@ nf.Connection = (function () {
                     endAnchor = d.bends[d.bends.length - 1];
                 } else {
                     endAnchor = {
-                        x: sourceData.component.position.x + (sourceData.dimensions.width / 2),
-                        y: sourceData.component.position.y + (sourceData.dimensions.height / 2)
+                        x: sourceData.position.x + (sourceData.dimensions.width / 2),
+                        y: sourceData.position.y + (sourceData.dimensions.height / 2)
                     };
                 }
 
@@ -391,8 +393,8 @@ nf.Connection = (function () {
 
                         // get the position on the new destination perimeter
                         var newEnd = nf.CanvasUtils.getPerimeterPoint(endAnchor, {
-                            'x': newDestinationData.component.position.x,
-                            'y': newDestinationData.component.position.y,
+                            'x': newDestinationData.position.x,
+                            'y': newDestinationData.position.y,
                             'width': newDestinationData.dimensions.width,
                             'height': newDestinationData.dimensions.height
                         });
@@ -402,13 +404,13 @@ nf.Connection = (function () {
                         end.y = newEnd.y;
                     }
                 } else {
-                    var destinationComponentId = nf.CanvasUtils.getConnectionDestinationComponentId(d.component);
+                    var destinationComponentId = nf.CanvasUtils.getConnectionDestinationComponentId(d);
                     var destinationData = d3.select('#id-' + destinationComponentId).datum();
 
                     // get the position on the destination perimeter
                     end = nf.CanvasUtils.getPerimeterPoint(endAnchor, {
-                        'x': destinationData.component.position.x,
-                        'y': destinationData.component.position.y,
+                        'x': destinationData.position.x,
+                        'y': destinationData.position.y,
                         'width': destinationData.dimensions.width,
                         'height': destinationData.dimensions.height
                     });
@@ -424,8 +426,8 @@ nf.Connection = (function () {
 
                 // get the position on the source perimeter
                 var start = nf.CanvasUtils.getPerimeterPoint(startAnchor, {
-                    'x': sourceData.component.position.x,
-                    'y': sourceData.component.position.y,
+                    'x': sourceData.position.x,
+                    'y': sourceData.position.y,
                     'width': sourceData.dimensions.width,
                     'height': sourceData.dimensions.height
                 });
@@ -444,9 +446,11 @@ nf.Connection = (function () {
                             'marker-end': function () {
                                 var marker = 'normal';
 
-                                // if the connection has a relationship that is unavailable, mark it a ghost relationship
-                                if (hasUnavailableRelationship(d)) {
-                                    marker = 'ghost';
+                                if (d.accessPolicy.canRead) {
+                                    // if the connection has a relationship that is unavailable, mark it a ghost relationship
+                                    if (hasUnavailableRelationship(d)) {
+                                        marker = 'ghost';
+                                    }
                                 }
 
                                 return 'url(#' + marker + ')';
@@ -471,7 +475,7 @@ nf.Connection = (function () {
                 // bends
                 // -----
 
-                if (nf.Common.isDFM()) {
+                if (d.accessPolicy.canWrite) {
                     // ------------------
                     // bends - startpoint
                     // ------------------
@@ -549,8 +553,8 @@ nf.Connection = (function () {
                                 d3.event.stopPropagation();
 
                                 // if this is a self loop prevent removing the last two bends
-                                var sourceComponentId = nf.CanvasUtils.getConnectionSourceComponentId(d.component);
-                                var destinationComponentId = nf.CanvasUtils.getConnectionDestinationComponentId(d.component);
+                                var sourceComponentId = nf.CanvasUtils.getConnectionSourceComponentId(d);
+                                var destinationComponentId = nf.CanvasUtils.getConnectionDestinationComponentId(d);
                                 if (sourceComponentId === destinationComponentId && d.component.bends.length <= 2) {
                                     nf.Dialog.showOkDialog({
                                         dialogContent: 'Looping connections must have at least two bend points.',
@@ -576,7 +580,7 @@ nf.Connection = (function () {
                                 }
 
                                 var connection = {
-                                    id: d.component.id,
+                                    id: d.id,
                                     bends: newBends
                                 };
 
@@ -640,224 +644,227 @@ nf.Connection = (function () {
 
                     var labelCount = 0;
 
-                    // -----------------------
-                    // connection label - from
-                    // -----------------------
-
-                    var connectionFrom = connectionLabelContainer.select('g.connection-from-container');
-
-                    // determine if the connection require a from label
-                    if (isGroup(d.component.source)) {
-                        // see if the connection from label is already rendered
-                        if (connectionFrom.empty()) {
-                            connectionFrom = connectionLabelContainer.append('g')
-                                    .attr({
-                                        'class': 'connection-from-container'
-                                    });
-
-                            connectionFrom.append('text')
-                                    .attr({
-                                        'class': 'connection-stats-label',
-                                        'x': 0,
-                                        'y': 10
-                                    })
-                                    .text('From');
-
-                            connectionFrom.append('text')
-                                    .attr({
-                                        'class': 'connection-stats-value connection-from',
-                                        'x': 33,
-                                        'y': 10,
-                                        'width': 130
-                                    });
-
-                            connectionFrom.append('image')
-                                    .call(nf.CanvasUtils.disableImageHref)
-                                    .attr({
-                                        'class': 'connection-from-run-status',
-                                        'width': 10,
-                                        'height': 10,
-                                        'x': 167,
-                                        'y': 1
+                    if (d.accessPolicy.canRead) {
+
+                        // -----------------------
+                        // connection label - from
+                        // -----------------------
+
+                        var connectionFrom = connectionLabelContainer.select('g.connection-from-container');
+                        
+                        // determine if the connection require a from label
+                        if (isGroup(d.component.source)) {
+                            // see if the connection from label is already rendered
+                            if (connectionFrom.empty()) {
+                                connectionFrom = connectionLabelContainer.append('g')
+                                        .attr({
+                                            'class': 'connection-from-container'
+                                        });
+    
+                                connectionFrom.append('text')
+                                        .attr({
+                                            'class': 'connection-stats-label',
+                                            'x': 0,
+                                            'y': 10
+                                        })
+                                        .text('From');
+    
+                                connectionFrom.append('text')
+                                        .attr({
+                                            'class': 'connection-stats-value connection-from',
+                                            'x': 33,
+                                            'y': 10,
+                                            'width': 130
+                                        });
+    
+                                connectionFrom.append('image')
+                                        .call(nf.CanvasUtils.disableImageHref)
+                                        .attr({
+                                            'class': 'connection-from-run-status',
+                                            'width': 10,
+                                            'height': 10,
+                                            'x': 167,
+                                            'y': 1
+                                        });
+                            }
+    
+                            // update the connection from positioning
+                            connectionFrom.attr('transform', function () {
+                                var y = 5 + (15 * labelCount++);
+                                return 'translate(5, ' + y + ')';
+                            });
+    
+                            // update the label text
+                            connectionFrom.select('text.connection-from')
+                                    .each(function () {
+                                        var connectionFromLabel = d3.select(this);
+    
+                                        // reset the label name to handle any previous state
+                                        connectionFromLabel.text(null).selectAll('title').remove();
+    
+                                        // apply ellipsis to the label as necessary
+                                        nf.CanvasUtils.ellipsis(connectionFromLabel, d.component.source.name);
+                                    }).append('title').text(function () {
+                                        return d.component.source.name;
                                     });
-                        }
-
-                        // update the connection from positioning
-                        connectionFrom.attr('transform', function () {
-                            var y = 5 + (15 * labelCount++);
-                            return 'translate(5, ' + y + ')';
-                        });
-
-                        // update the label text
-                        connectionFrom.select('text.connection-from')
-                                .each(function () {
-                                    var connectionFromLabel = d3.select(this);
-
-                                    // reset the label name to handle any previous state
-                                    connectionFromLabel.text(null).selectAll('title').remove();
-
-                                    // apply ellipsis to the label as necessary
-                                    nf.CanvasUtils.ellipsis(connectionFromLabel, d.component.source.name);
-                                }).append('title').text(function () {
-                                    return d.component.source.name;
-                                });
-
-                        // update the label run status
-                        connectionFrom.select('image.connection-from-run-status').attr('xlink:href', function () {
-                            if (d.component.source.exists === false) {
-                                return 'images/portRemoved.png';
-                            } else if (d.component.source.running === true) {
-                                return 'images/portRunning.png';
-                            } else {
-                                return 'images/portStopped.png';
+    
+                            // update the label run status
+                            connectionFrom.select('image.connection-from-run-status').attr('xlink:href', function () {
+                                if (d.component.source.exists === false) {
+                                    return 'images/portRemoved.png';
+                                } else if (d.component.source.running === true) {
+                                    return 'images/portRunning.png';
+                                } else {
+                                    return 'images/portStopped.png';
+                                }
+                            });
+                        } else {
+                            // there is no connection from, but check if the name was previous
+                            // rendered so it can be removed
+                            if (!connectionFrom.empty()) {
+                                connectionFrom.remove();
                             }
-                        });
-                    } else {
-                        // there is no connection from, but check if the name was previous
-                        // rendered so it can be removed
-                        if (!connectionFrom.empty()) {
-                            connectionFrom.remove();
                         }
-                    }
-
-                    // ---------------------
-                    // connection label - to
-                    // ---------------------
-
-                    var connectionTo = connectionLabelContainer.select('g.connection-to-container');
-
-                    // determine if the connection require a to label
-                    if (isGroup(d.component.destination)) {
-                        // see if the connection to label is already rendered
-                        if (connectionTo.empty()) {
-                            connectionTo = connectionLabelContainer.append('g')
-                                    .attr({
-                                        'class': 'connection-to-container'
-                                    });
-
-                            connectionTo.append('text')
-                                    .attr({
-                                        'class': 'connection-stats-label',
-                                        'x': 0,
-                                        'y': 10
-                                    })
-                                    .text('To');
-
-                            connectionTo.append('text')
-                                    .attr({
-                                        'class': 'connection-stats-value connection-to',
-                                        'x': 18,
-                                        'y': 10,
-                                        'width': 145
-                                    });
-
-                            connectionTo.append('image')
-                                    .call(nf.CanvasUtils.disableImageHref)
-                                    .attr({
-                                        'class': 'connection-to-run-status',
-                                        'width': 10,
-                                        'height': 10,
-                                        'x': 167,
-                                        'y': 1
+    
+                        // ---------------------
+                        // connection label - to
+                        // ---------------------
+    
+                        var connectionTo = connectionLabelContainer.select('g.connection-to-container');
+    
+                        // determine if the connection require a to label
+                        if (isGroup(d.component.destination)) {
+                            // see if the connection to label is already rendered
+                            if (connectionTo.empty()) {
+                                connectionTo = connectionLabelContainer.append('g')
+                                        .attr({
+                                            'class': 'connection-to-container'
+                                        });
+    
+                                connectionTo.append('text')
+                                        .attr({
+                                            'class': 'connection-stats-label',
+                                            'x': 0,
+                                            'y': 10
+                                        })
+                                        .text('To');
+    
+                                connectionTo.append('text')
+                                        .attr({
+                                            'class': 'connection-stats-value connection-to',
+                                            'x': 18,
+                                            'y': 10,
+                                            'width': 145
+                                        });
+    
+                                connectionTo.append('image')
+                                        .call(nf.CanvasUtils.disableImageHref)
+                                        .attr({
+                                            'class': 'connection-to-run-status',
+                                            'width': 10,
+                                            'height': 10,
+                                            'x': 167,
+                                            'y': 1
+                                        });
+                            }
+    
+                            // update the connection to positioning
+                            connectionTo.attr('transform', function () {
+                                var y = 5 + (15 * labelCount++);
+                                return 'translate(5, ' + y + ')';
+                            });
+    
+                            // update the label text
+                            connectionTo.select('text.connection-to')
+                                    .each(function (d) {
+                                        var connectionToLabel = d3.select(this);
+    
+                                        // reset the label name to handle any previous state
+                                        connectionToLabel.text(null).selectAll('title').remove();
+    
+                                        // apply ellipsis to the label as necessary
+                                        nf.CanvasUtils.ellipsis(connectionToLabel, d.component.destination.name);
+                                    }).append('title').text(function (d) {
+                                        return d.component.destination.name;
                                     });
-                        }
-
-                        // update the connection to positioning
-                        connectionTo.attr('transform', function () {
-                            var y = 5 + (15 * labelCount++);
-                            return 'translate(5, ' + y + ')';
-                        });
-
-                        // update the label text
-                        connectionTo.select('text.connection-to')
-                                .each(function (d) {
-                                    var connectionToLabel = d3.select(this);
-
-                                    // reset the label name to handle any previous state
-                                    connectionToLabel.text(null).selectAll('title').remove();
-
-                                    // apply ellipsis to the label as necessary
-                                    nf.CanvasUtils.ellipsis(connectionToLabel, d.component.destination.name);
-                                }).append('title').text(function (d) {
-                                    return d.component.destination.name;
-                                });
-
-                        // update the label run status
-                        connectionTo.select('image.connection-to-run-status').attr('xlink:href', function () {
-                            if (d.component.destination.exists === false) {
-                                return 'images/portRemoved.png';
-                            } else if (d.component.destination.running === true) {
-                                return 'images/portRunning.png';
-                            } else {
-                                return 'images/portStopped.png';
+    
+                            // update the label run status
+                            connectionTo.select('image.connection-to-run-status').attr('xlink:href', function () {
+                                if (d.component.destination.exists === false) {
+                                    return 'images/portRemoved.png';
+                                } else if (d.component.destination.running === true) {
+                                    return 'images/portRunning.png';
+                                } else {
+                                    return 'images/portStopped.png';
+                                }
+                            });
+                        } else {
+                            // there is no connection to, but check if the name was previous
+                            // rendered so it can be removed
+                            if (!connectionTo.empty()) {
+                                connectionTo.remove();
                             }
-                        });
-                    } else {
-                        // there is no connection to, but check if the name was previous
-                        // rendered so it can be removed
-                        if (!connectionTo.empty()) {
-                            connectionTo.remove();
                         }
-                    }
-
-                    // -----------------------
-                    // connection label - name
-                    // -----------------------
-
-                    // get the connection name
-                    var connectionNameValue = nf.CanvasUtils.formatConnectionName(d.component);
-                    var connectionName = connectionLabelContainer.select('g.connection-name-container');
-
-                    // is there a name to render
-                    if (!nf.Common.isBlank(connectionNameValue)) {
-                        // see if the connection name label is already rendered
-                        if (connectionName.empty()) {
-                            connectionName = connectionLabelContainer.append('g')
-                                    .attr({
-                                        'class': 'connection-name-container'
-                                    });
-
-                            connectionName.append('text')
-                                    .attr({
-                                        'class': 'connection-stats-label',
-                                        'x': 0,
-                                        'y': 10
-                                    })
-                                    .text('Name');
-
-                            connectionName.append('text')
-                                    .attr({
-                                        'class': 'connection-stats-value connection-name',
-                                        'x': 35,
-                                        'y': 10,
-                                        'width': 142
+    
+                        // -----------------------
+                        // connection label - name
+                        // -----------------------
+    
+                        // get the connection name
+                        var connectionNameValue = nf.CanvasUtils.formatConnectionName(d.component);
+                        var connectionName = connectionLabelContainer.select('g.connection-name-container');
+    
+                        // is there a name to render
+                        if (!nf.Common.isBlank(connectionNameValue)) {
+                            // see if the connection name label is already rendered
+                            if (connectionName.empty()) {
+                                connectionName = connectionLabelContainer.append('g')
+                                        .attr({
+                                            'class': 'connection-name-container'
+                                        });
+    
+                                connectionName.append('text')
+                                        .attr({
+                                            'class': 'connection-stats-label',
+                                            'x': 0,
+                                            'y': 10
+                                        })
+                                        .text('Name');
+    
+                                connectionName.append('text')
+                                        .attr({
+                                            'class': 'connection-stats-value connection-name',
+                                            'x': 35,
+                                            'y': 10,
+                                            'width': 142
+                                        });
+                            }
+    
+                            // update the connection name positioning
+                            connectionName.attr('transform', function () {
+                                var y = 5 + (15 * labelCount++);
+                                return 'translate(5, ' + y + ')';
+                            });
+    
+                            // update the connection name
+                            connectionName.select('text.connection-name')
+                                    .each(function () {
+                                        var connectionToLabel = d3.select(this);
+    
+                                        // reset the label name to handle any previous state
+                                        connectionToLabel.text(null).selectAll('title').remove();
+    
+                                        // apply ellipsis to the label as necessary
+                                        nf.CanvasUtils.ellipsis(connectionToLabel, connectionNameValue);
+                                    }).append('title').text(function () {
+                                        return connectionNameValue;
                                     });
-                        }
-
-                        // update the connection name positioning
-                        connectionName.attr('transform', function () {
-                            var y = 5 + (15 * labelCount++);
-                            return 'translate(5, ' + y + ')';
-                        });
-
-                        // update the connection name
-                        connectionName.select('text.connection-name')
-                                .each(function () {
-                                    var connectionToLabel = d3.select(this);
-
-                                    // reset the label name to handle any previous state
-                                    connectionToLabel.text(null).selectAll('title').remove();
-
-                                    // apply ellipsis to the label as necessary
-                                    nf.CanvasUtils.ellipsis(connectionToLabel, connectionNameValue);
-                                }).append('title').text(function () {
-                                    return connectionNameValue;
-                                });
-                    } else {
-                        // there is no connection name, but check if the name was previous
-                        // rendered so it can be removed
-                        if (!connectionName.empty()) {
-                            connectionName.remove();
+                        } else {
+                            // there is no connection name, but check if the name was previous
+                            // rendered so it can be removed
+                            if (!connectionName.empty()) {
+                                connectionName.remove();
+                            }
                         }
                     }
 
@@ -939,16 +946,18 @@ nf.Connection = (function () {
                                 return 5 + (15 * labelCount) + 3;
                             });
                             
-                    // determine whether or not to show the expiration icon
-                    connectionLabelContainer.select('g.expiration-icon')
-                            .classed('hidden', function () {
-                                return !isExpirationConfigured(d.component);
-                            })
-                            .select('title').text(function () {
-                                return 'Expires FlowFiles older than ' + d.component.flowFileExpiration;
-                            });
+                    if (d.accessPolicy.canRead) {
+                        // determine whether or not to show the expiration icon
+                        connectionLabelContainer.select('g.expiration-icon')
+                                .classed('hidden', function () {
+                                    return !isExpirationConfigured(d.component);
+                                })
+                                .select('title').text(function () {
+                                    return 'Expires FlowFiles older than ' + d.component.flowFileExpiration;
+                                });
+                    }
 
-                    if (nf.Common.isDFM()) {
+                    if (d.accessPolicy.canWrite) {
                         // only support dragging the label when appropriate
                         connectionLabelContainer.call(labelDrag);
                     }
@@ -1003,8 +1012,8 @@ nf.Connection = (function () {
         var revision = nf.Client.getRevision();
 
         var entity = {
-            revision: revision,
-            connection: connection
+            'revision': revision,
+            'component': connection
         };
 
         return $.ajax({
@@ -1018,7 +1027,7 @@ nf.Connection = (function () {
             nf.Client.setRevision(response.revision);
 
             // request was successful, update the entry
-            nf.Connection.set(response.connection);
+            nf.Connection.set(response);
         }).fail(function (xhr, status, error) {
             if (xhr.status === 400 || xhr.status === 404 || xhr.status === 409) {
                 nf.Dialog.showOkDialog({
@@ -1035,7 +1044,7 @@ nf.Connection = (function () {
     var removeConnections = function (removed) {
         // consider reloading source/destination of connection being removed
         removed.each(function (d) {
-            nf.CanvasUtils.reloadConnectionSourceAndDestination(d.component.source.id, d.component.destination.id);
+            nf.CanvasUtils.reloadConnectionSourceAndDestination(d.sourceId, d.destinationId);
         });
         
         // remove the connection
@@ -1099,7 +1108,7 @@ nf.Connection = (function () {
                             // only save the updated bends if necessary
                             if (different) {
                                 save(connectionData, {
-                                    id: connectionData.component.id,
+                                    id: connectionData.id,
                                     bends: bends
                                 }).fail(function () {
                                     // restore the previous bend points
@@ -1174,10 +1183,10 @@ nf.Connection = (function () {
 
                                 var connectionEntity = {
                                     'revision': nf.Client.getRevision(),
-                                    'connection': {
-                                        'id': connectionData.component.id,
+                                    'component': {
+                                        'id': connectionData.id,
                                         'destination': {
-                                            'id': destinationData.component.id,
+                                            'id': destinationData.id,
                                             'groupId': nf.Canvas.getGroupId(),
                                             'type': destinationType
                                         }
@@ -1185,10 +1194,10 @@ nf.Connection = (function () {
                                 };
 
                                 // if this is a self loop and there are less than 2 bends, add them
-                                if (connectionData.bends.length < 2 && connectionData.component.source.id === destinationData.component.id) {
+                                if (connectionData.bends.length < 2 && connectionData.component.source.id === destinationData.id) {
                                     var rightCenter = {
-                                        x: destinationData.component.position.x + (destinationData.dimensions.width),
-                                        y: destinationData.component.position.y + (destinationData.dimensions.height / 2)
+                                        x: destinationData.position.x + (destinationData.dimensions.width),
+                                        y: destinationData.position.y + (destinationData.dimensions.height / 2)
                                     };
                                     var xOffset = nf.Connection.config.selfLoopXOffset;
                                     var yOffset = nf.Connection.config.selfLoopYOffset;
@@ -1211,13 +1220,13 @@ nf.Connection = (function () {
                                     dataType: 'json',
                                     contentType: 'application/json'
                                 }).done(function (response) {
-                                    var updatedConnectionData = response.connection;
+                                    var updatedConnectionData = response.component;
 
                                     // update the revision
                                     nf.Client.setRevision(response.revision);
 
                                     // refresh to update the label
-                                    nf.Connection.set(updatedConnectionData);
+                                    nf.Connection.set(response);
                                     
                                     // reload the previous destination and the new source/destination
                                     nf.CanvasUtils.reloadConnectionSourceAndDestination(null, previousDestinationId);
@@ -1344,7 +1353,7 @@ nf.Connection = (function () {
 
                                 // save the new label index
                                 save(d, {
-                                    id: d.component.id,
+                                    id: d.id,
                                     labelIndex: d.labelIndex
                                 }).fail(function () {
                                     // restore the previous label index
@@ -1364,34 +1373,26 @@ nf.Connection = (function () {
         /**
          * Populates the graph with the specified connections.
          * 
-         * @argument {object | array} connections               The connections to add
+         * @argument {object | array} connectionEntities               The connections to add
          * @argument {boolean} selectAll                Whether or not to select the new contents
          */
-        add: function (connections, selectAll) {
+        add: function (connectionEntities, selectAll) {
             selectAll = nf.Common.isDefinedAndNotNull(selectAll) ? selectAll : false;
 
-            var add = function (connection) {
+            var add = function (connectionEntity) {
                 // add the connection
-                connectionMap.set(connection.id, {
+                connectionMap.set(connectionEntity.id, $.extend({
                     type: 'Connection',
-                    component: connection,
-                    bends: $.map(connection.bends, function (bend) {
-                        return {
-                            x: bend.x,
-                            y: bend.y
-                        };
-                    }),
-                    labelIndex: connection.labelIndex
-                });
+                }, connectionEntity));
             };
 
             // determine how to handle the specified connection
-            if ($.isArray(connections)) {
-                $.each(connections, function (_, connection) {
-                    add(connection);
+            if ($.isArray(connectionEntities)) {
+                $.each(connectionEntities, function (_, connectionEntity) {
+                    add(connectionEntity);
                 });
             } else {
-                add(connections);
+                add(connectionEntities);
             }
 
             // apply the selection and handle all new connection
@@ -1408,34 +1409,27 @@ nf.Connection = (function () {
         /**
          * Sets the value of the specified connection.
          * 
-         * @param {type} connection
+         * @param {type} connectionEntities
          */
-        set: function (connection) {
-            var set = function (conn) {
-                if (connectionMap.has(conn.id)) {
+        set: function (connectionEntities) {
+            var set = function (connectionEntity) {
+                if (connectionMap.has(connectionEntity.id)) {
                     // update the current entry
-                    var connectionEntry = connectionMap.get(conn.id);
-                    connectionEntry.component = conn;
-                    connectionEntry.bends = $.map(conn.bends, function (bend) {
-                        return {
-                            x: bend.x,
-                            y: bend.y
-                        };
-                    });
-                    connectionEntry.labelIndex = conn.labelIndex;
+                    var connectionEntry = connectionMap.get(connectionEntity.id);
+                    $.extend(connectionEntry, connectionEntity);
 
                     // update the connection in the UI
-                    d3.select('#id-' + conn.id).call(updateConnections, true, true);
+                    d3.select('#id-' + connectionEntity.id).call(updateConnections, true, true);
                 }
             };
 
             // determine how to handle the specified connection
-            if ($.isArray(connection)) {
-                $.each(connection, function (_, conn) {
-                    set(conn);
+            if ($.isArray(connectionEntities)) {
+                $.each(connectionEntities, function (_, connectionEntity) {
+                    set(connectionEntity);
                 });
             } else {
-                set(connection);
+                set(connectionEntities);
             }
         },
         
@@ -1518,7 +1512,7 @@ nf.Connection = (function () {
                     url: connection.uri,
                     dataType: 'json'
                 }).done(function (response) {
-                    nf.Connection.set(response.connection);
+                    nf.Connection.set(response);
                 });
             }
         },
@@ -1532,11 +1526,9 @@ nf.Connection = (function () {
         getComponentConnections: function (id) {
             var connections = [];
             connectionMap.forEach(function (_, entry) {
-                var connection = entry.component;
-
                 // see if this component is the source or destination of this connection
-                if (nf.CanvasUtils.getConnectionSourceComponentId(connection) === id || nf.CanvasUtils.getConnectionDestinationComponentId(connection) === id) {
-                    connections.push(connection);
+                if (nf.CanvasUtils.getConnectionSourceComponentId(entry) === id || nf.CanvasUtils.getConnectionDestinationComponentId(entry) === id) {
+                    connections.push(entry);
                 }
             });
             return connections;

http://git-wip-us.apache.org/repos/asf/nifi/blob/ff98d823/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-context-menu.js
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-context-menu.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-context-menu.js
index f841e51..90863d5 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-context-menu.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-context-menu.js
@@ -38,6 +38,9 @@ nf.ContextMenu = (function () {
         if (selection.size() !== 1) {
             return false;
         }
+        if (nf.CanvasUtils.canModify(selection) === false) {
+            return false;
+        }
 
         var isConfigurableComponent = nf.CanvasUtils.isLabel(selection) || nf.CanvasUtils.isProcessGroup(selection);
         if (!isConfigurableComponent) {
@@ -46,7 +49,7 @@ nf.ContextMenu = (function () {
             }
         }
 
-        return isConfigurableComponent && nf.Common.isDFM();
+        return isConfigurableComponent;
     };
 
     /**
@@ -59,8 +62,11 @@ nf.ContextMenu = (function () {
         if (selection.size() !== 1) {
             return false;
         }
+        if (nf.CanvasUtils.canRead(selection) === false) {
+            return false;
+        }
 
-        if (nf.Common.isDFM()) {
+        if (nf.CanvasUtils.canModify(selection)) {
             if (nf.CanvasUtils.isProcessor(selection) || nf.CanvasUtils.isInputPort(selection) || nf.CanvasUtils.isOutputPort(selection) || nf.CanvasUtils.isRemoteProcessGroup(selection) || nf.CanvasUtils.isConnection(selection)) {
                 return !nf.CanvasUtils.supportsModification(selection);
             }
@@ -77,7 +83,11 @@ nf.ContextMenu = (function () {
      * @param {selection} selection         The selection of currently selected components 
      */
     var isDeletable = function (selection) {
-        return nf.Common.isDFM() && nf.CanvasUtils.isDeletable(selection);
+        if (nf.CanvasUtils.canModify(selection) === false) {
+            return false;
+        }
+        
+        return nf.CanvasUtils.isDeletable(selection);
     };
 
     /**
@@ -86,7 +96,11 @@ nf.ContextMenu = (function () {
      * @param {selection} selection         The selection of currently selected components 
      */
     var isRunnable = function (selection) {
-        return nf.Common.isDFM() && nf.CanvasUtils.areRunnable(selection);
+        if (nf.CanvasUtils.canModify(selection) === false) {
+            return false;
+        }
+        
+        return nf.CanvasUtils.areRunnable(selection);
     };
 
     /**
@@ -95,7 +109,11 @@ nf.ContextMenu = (function () {
      * @param {selection} selection         The selection of currently selected components 
      */
     var isStoppable = function (selection) {
-        return nf.Common.isDFM() && nf.CanvasUtils.areStoppable(selection);
+        if (nf.CanvasUtils.canModify(selection) === false) {
+            return false;
+        }
+        
+        return nf.CanvasUtils.areStoppable(selection);
     };
 
     /**
@@ -141,7 +159,11 @@ nf.ContextMenu = (function () {
      * @param {selection} selection         The selection of currently selected components
      */
     var isCopyable = function (selection) {
-        return nf.Common.isDFM() && nf.CanvasUtils.isCopyable(selection);
+        if (nf.CanvasUtils.canModify(selection) === false) {
+            return false;
+        }
+        
+        return nf.CanvasUtils.isCopyable(selection);
     };
 
     /**
@@ -172,8 +194,11 @@ nf.ContextMenu = (function () {
         if (selection.size() !== 1) {
             return false;
         }
+        if (nf.CanvasUtils.canModify(selection) === false) {
+            return false;
+        }
 
-        return nf.Common.isDFM() && nf.CanvasUtils.isConnection(selection);
+        return nf.CanvasUtils.isConnection(selection);
     };
 
     /**
@@ -182,7 +207,11 @@ nf.ContextMenu = (function () {
      * @param {selection} selection          The selection
      */
     var isColorable = function (selection) {
-        return nf.Common.isDFM() && nf.CanvasUtils.isColorable(selection);
+        if (nf.CanvasUtils.canModify(selection) === false) {
+            return false;
+        }
+        
+        return nf.CanvasUtils.isColorable(selection);
     };
 
     /**
@@ -205,14 +234,16 @@ nf.ContextMenu = (function () {
      * @param {selection} selection         The selection
      */
     var hasDownstream = function (selection) {
-        // ensure the correct number of components are selected
-        if (selection.size() !== 1) {
-            return false;
-        }
-
-        return nf.CanvasUtils.isFunnel(selection) || nf.CanvasUtils.isProcessor(selection) || nf.CanvasUtils.isProcessGroup(selection) ||
-                nf.CanvasUtils.isRemoteProcessGroup(selection) || nf.CanvasUtils.isInputPort(selection) ||
-                (nf.CanvasUtils.isOutputPort(selection) && nf.Canvas.getParentGroupId() !== null);
+        // TODO
+        // // ensure the correct number of components are selected
+        // if (selection.size() !== 1) {
+        //     return false;
+        // }
+        //
+        // return nf.CanvasUtils.isFunnel(selection) || nf.CanvasUtils.isProcessor(selection) || nf.CanvasUtils.isProcessGroup(selection) ||
+        //         nf.CanvasUtils.isRemoteProcessGroup(selection) || nf.CanvasUtils.isInputPort(selection) ||
+        //         (nf.CanvasUtils.isOutputPort(selection) && nf.Canvas.getParentGroupId() !== null);
+        return false;
     };
 
     /**
@@ -221,14 +252,16 @@ nf.ContextMenu = (function () {
      * @param {selection} selection         The selection
      */
     var hasUpstream = function (selection) {
-        // ensure the correct number of components are selected
-        if (selection.size() !== 1) {
-            return false;
-        }
-
-        return nf.CanvasUtils.isFunnel(selection) || nf.CanvasUtils.isProcessor(selection) || nf.CanvasUtils.isProcessGroup(selection) ||
-                nf.CanvasUtils.isRemoteProcessGroup(selection) || nf.CanvasUtils.isOutputPort(selection) ||
-                (nf.CanvasUtils.isInputPort(selection) && nf.Canvas.getParentGroupId() !== null);
+        // TODO
+        // // ensure the correct number of components are selected
+        // if (selection.size() !== 1) {
+        //     return false;
+        // }
+        //
+        // return nf.CanvasUtils.isFunnel(selection) || nf.CanvasUtils.isProcessor(selection) || nf.CanvasUtils.isProcessGroup(selection) ||
+        //         nf.CanvasUtils.isRemoteProcessGroup(selection) || nf.CanvasUtils.isOutputPort(selection) ||
+        //         (nf.CanvasUtils.isInputPort(selection) && nf.Canvas.getParentGroupId() !== null);
+        return false;
     };
 
     /**
@@ -241,9 +274,7 @@ nf.ContextMenu = (function () {
         if (selection.size() !== 1) {
             return false;
         }
-
-        // ensure the user is DFM
-        if (nf.Common.isDFM() === false) {
+        if (nf.CanvasUtils.canModify(selection) === false) {
             return false;
         }
 
@@ -304,7 +335,11 @@ nf.ContextMenu = (function () {
      * @param {selection} selection
      */
     var canStartTransmission = function (selection) {
-        return nf.Common.isDFM() && nf.CanvasUtils.canAllStartTransmitting(selection);
+        if (nf.CanvasUtils.canModify(selection) === false) {
+            return false;
+        }
+        
+        return nf.CanvasUtils.canAllStartTransmitting(selection);
     };
 
     /**
@@ -313,7 +348,11 @@ nf.ContextMenu = (function () {
      * @param {selection} selection
      */
     var canStopTransmission = function (selection) {
-        return nf.Common.isDFM() && nf.CanvasUtils.canAllStopTransmitting(selection);
+        if (nf.CanvasUtils.canModify(selection) === false) {
+            return false;
+        }
+        
+        return nf.CanvasUtils.canAllStopTransmitting(selection);
     };
     
     /**
@@ -322,7 +361,11 @@ nf.ContextMenu = (function () {
      * @param {selection} selection
      */
     var canEmptyQueue = function (selection) {
-        return nf.Common.isDFM() && isConnection(selection);
+        if (nf.CanvasUtils.canModify(selection) === false) {
+            return false;
+        }
+        
+        return isConnection(selection);
     };
 
     /**
@@ -331,7 +374,11 @@ nf.ContextMenu = (function () {
      * @param {selection} selection
      */
     var canListQueue = function (selection) {
-        return nf.Common.isDFM() && isConnection(selection);
+        if (nf.CanvasUtils.canModify(selection) === false) {
+            return false;
+        }
+        
+        return isConnection(selection);
     };
     
     /**
@@ -340,6 +387,12 @@ nf.ContextMenu = (function () {
      * @param {type} selection
      */
     var canMoveToParent = function (selection) {
+        if (nf.CanvasUtils.canModify(selection) === false) {
+            return false;
+        }
+
+        // TODO - also check can modify in parent
+        
         return !selection.empty() && nf.CanvasUtils.isDisconnected(selection) && nf.Canvas.getParentGroupId() !== null;
     };
 

http://git-wip-us.apache.org/repos/asf/nifi/blob/ff98d823/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-draggable.js
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-draggable.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-draggable.js
index d4dd1de..ab3078f 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-draggable.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-draggable.js
@@ -43,19 +43,17 @@ nf.Draggable = (function () {
         
         var updateComponentPosition = function(d) {
             var newPosition = {
-                'x': d.component.position.x + delta.x,
-                'y': d.component.position.y + delta.y
+                'x': d.position.x + delta.x,
+                'y': d.position.y + delta.y
             };
 
             // build the entity
             var entity = {
-                'revision': nf.Client.getRevision()
-            };
-
-            // use bracket notation to dynamic get the key based on the entity type
-            entity[nf[d.type].getEntityKey(d)] = {
-                'id': d.component.id,
-                'position': newPosition
+                'revision': nf.Client.getRevision(),
+                'component': {
+                    'id': d.id,
+                    'position': newPosition
+                }
             };
 
             // update the component positioning
@@ -70,13 +68,13 @@ nf.Draggable = (function () {
                     // update the revision
                     nf.Client.setRevision(response.revision);
 
-                    // store the updated location
-                    d.component.position = newPosition;
+                    // update the component
+                    nf[d.type].set(response);
 
                     // resolve with an object so we can refresh when finished
                     deferred.resolve({
                         type: d.type,
-                        id: d.component.id
+                        id: d.id
                     });
                 }).fail(function (xhr, status, error) {
                     if (xhr.status === 400 || xhr.status === 404 || xhr.status === 409) {
@@ -95,12 +93,12 @@ nf.Draggable = (function () {
         
         var updateConnectionPosition = function(d) {
             // only update if necessary
-            if (d.component.bends.length === 0) {
+            if (d.bends.length === 0) {
                 return null;
             }
 
             // calculate the new bend points
-            var newBends = $.map(d.component.bends, function (bend) {
+            var newBends = $.map(d.bends, function (bend) {
                 return {
                     x: bend.x + delta.x,
                     y: bend.y + delta.y
@@ -108,9 +106,9 @@ nf.Draggable = (function () {
             });
 
             var entity = {
-                revision: revision,
-                connection: {
-                    id: d.component.id,
+                'revision': revision,
+                'component': {
+                    id: d.id,
                     bends: newBends
                 }
             };
@@ -127,19 +125,13 @@ nf.Draggable = (function () {
                     // update the revision
                     nf.Client.setRevision(response.revision);
 
-                    // store the updated bend points
-                    d.component.bends = response.connection.bends;
-                    d.bends = $.map(d.component.bends, function (bend) {
-                        return {
-                            x: bend.x,
-                            y: bend.y
-                        };
-                    });
+                    // update the component
+                    nf.Connection.set(response);
 
                     // resolve with an object so we can refresh when finished
                     deferred.resolve({
                         type: d.type,
-                        id: d.component.id
+                        id: d.id
                     });
                 }).fail(function (xhr, status, error) {
                     if (xhr.status === 400 || xhr.status === 404 || xhr.status === 409) {
@@ -160,14 +152,14 @@ nf.Draggable = (function () {
         d3.selectAll('g.connection.selected').each(function (d) {
             var connectionUpdate = updateConnectionPosition(d);
             if (connectionUpdate !== null) {
-                updates.set(d.component.id, connectionUpdate);
+                updates.set(d.id, connectionUpdate);
             }
         });
         
         // go through each selected component
         d3.selectAll('g.component.selected').each(function (d) {
             // consider any self looping connections
-            var connections = nf.Connection.getComponentConnections(d.component.id);
+            var connections = nf.Connection.getComponentConnections(d.id);
             $.each(connections, function(_, connection) {
                 if (!updates.has(connection.id) && nf.CanvasUtils.getConnectionSourceComponentId(connection) === nf.CanvasUtils.getConnectionDestinationComponentId(connection)) {
                     var connectionUpdate = updateConnectionPosition(nf.Connection.get(connection.id));
@@ -178,7 +170,7 @@ nf.Draggable = (function () {
             });
             
             // consider the component itself
-            updates.set(d.component.id, updateComponentPosition(d));
+            updates.set(d.id, updateComponentPosition(d));
         });
 
         // wait for all updates to complete
@@ -240,14 +232,14 @@ nf.Draggable = (function () {
                             // determine the appropriate bounding box
                             var minX = null, maxX = null, minY = null, maxY = null;
                             selection.each(function (d) {
-                                if (minX === null || d.component.position.x < minX) {
-                                    minX = d.component.position.x;
+                                if (minX === null || d.position.x < minX) {
+                                    minX = d.position.x;
                                 }
-                                if (minY === null || d.component.position.y < minY) {
-                                    minY = d.component.position.y;
+                                if (minY === null || d.position.y < minY) {
+                                    minY = d.position.y;
                                 }
-                                var componentMaxX = d.component.position.x + d.dimensions.width;
-                                var componentMaxY = d.component.position.y + d.dimensions.height;
+                                var componentMaxX = d.position.x + d.dimensions.width;
+                                var componentMaxY = d.position.y + d.dimensions.height;
                                 if (maxX === null || componentMaxX > maxX) {
                                     maxX = componentMaxX;
                                 }